You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Discord.NET 开发Bot无法向指定及数据库用户发送DM私信问题

核心问题说明

你的代码存在4个致命错误,导致无法正常发送私信:

  1. 自定义重写了Discord.NET内置的IUser接口,这个自定义接口没有对接Discord API的实现逻辑,完全无效,根本无法用来创建私信通道。
  2. 没有正确获取有效IUser实例,IUser是Discord API返回的实体对象,不能自己构造,必须通过客户端实例从缓存/REST接口拉取。
  3. 没有等待客户端完全就绪就执行操作,StartAsync调用完成不代表客户端已经加载完所有缓存、完成网关握手,这时候调用接口会返回空或者抛异常。
  4. 异常捕获逻辑覆盖不全,CreateDMChannelAsync本身也会抛出403异常(用户关闭私信、无共同服务器),你原来的写法只捕获了发消息的异常,创建通道失败还是会导致程序崩溃。
正确实现方案

前置准备

  • 删掉你自己写的那段IUser接口定义,全程使用Discord.NET官方库提供的Discord.IUser类型,不要重写任何库内置的实体接口。
  • 数据库只需要存储每个Discord用户的ulong类型ID即可,不需要存储用户名、鉴别号这类可变信息,需要的时候通过ID拉取最新数据。

代码修正

首先修正机器人启动逻辑,添加就绪事件监听,配置客户端缓存策略:

public async Task RunBotAsync()
{
    // 配置客户端,开启用户全量缓存保证能拉到所有共通服务器用户
    var socketConfig = new DiscordSocketConfig()
    {
        AlwaysDownloadUsers = true,
        MessageCacheSize = 100
    };
    _client = new DiscordSocketClient(socketConfig);
    _commands = new CommandService();

    _services = new ServiceCollection()
        .AddSingleton(_client)
        .AddSingleton(_commands)
        .BuildServiceProvider();

    // 注册客户端就绪事件,等所有资源加载完成再执行业务逻辑
    _client.Ready += SendBatchDmToAllUsers;

    // 注意:实际部署时不要把Token硬编码在代码里,用配置文件存储
    string botToken = "你的机器人Token";
    await _client.LoginAsync(TokenType.Bot, botToken);
    await _client.StartAsync();
    await Task.Delay(-1);
}

实现批量发私信的就绪回调方法:

private async Task SendBatchDmToAllUsers()
{
    // 这里替换成你从数据库读取所有用户ID的逻辑
    // 例:List<ulong> allStoredUserIds = await _dbContext.DiscordUsers.Select(u => u.UserId).ToListAsync();
    List<ulong> allStoredUserIds = new List<ulong>(){ 1234567890UL, 0987654321UL };

    foreach (var userId in allStoredUserIds)
    {
        await SendSingleDm(userId);
        // 加3秒间隔避免触发Discord速率限制,批量发私信必须加延迟,否则会被接口限流
        await Task.Delay(3000);
    }
}

修正单条私信发送方法,补全异常处理:

public async Task SendSingleDm(ulong targetUserId)
{
    try
    {
        // 通过客户端拉取有效用户实例,这是唯一合法获取IUser对象的方式
        IUser targetUser = await _client.GetUserAsync(targetUserId);
        if (targetUser == null)
        {
            Console.WriteLine($"用户ID:{targetUserId} 不存在或与机器人无共同服务器,跳过发送");
            return;
        }

        // 创建私信通道
        var dmChannel = await targetUser.CreateDMChannelAsync();
        // 发送自定义内容
        await dmChannel.SendMessageAsync("你要推送的私信内容");
        Console.WriteLine($"已成功向 {targetUser.Username}#{targetUser.Discriminator} 发送私信");
    }
    catch (Discord.Net.HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden)
    {
        Console.WriteLine($"用户ID:{targetUserId} 关闭了陌生人私信权限,无法发送");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"给用户{targetUserId}发信失败,错误信息:{ex.Message}");
    }
}
注意事项
  • 所有Discord实体(IUser、IGuild、IMessage等)都不要自己实现接口、不要手动new构造,只能通过客户端提供的方法从API/缓存获取,自己构造的对象没有和Discord网关对接的逻辑,完全无法使用。
  • 批量发私信的间隔不要低于2秒,Discord对私信接口的速率限制比普通频道消息更严格,触发限流后轻则短时间无法发消息,重则机器人会被封禁。
  • 如果用户退出了所有和机器人共同的服务器,GetUserAsync会返回null,直接跳过即可,这种情况没有任何办法给对方发私信。
  • 机器人必须和目标用户存在至少一个共同服务器,且对方没有关闭“允许服务器成员发送私信”的开关,否则无法成功发送私信。

内容的提问来源于stack exchange,提问作者Marcio silva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 15:45:42