Discord.net 宣布新用户加入时出现问题

Discord.net Problem with announcing new users joining

我使用 C# 和 Discord.net 制作了我自己的 Discord 机器人,但是在宣布新用户时有些东西不起作用。 当有人加入时,它会发送消息,一切都很好,但紧接着,机器人崩溃并出现错误

    System.NullReferenceException: 'Object reference not set to an instance of an object.'

    var context = new SocketCommandContext(_client, message);

关于原因有什么想法吗?

完整代码如下:

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.Extensions.DependencyInjection;
using System.Net.Sockets;
using System.Reflection;
using System.Threading;
using System.IO;
using Discord.Rest;

namespace DiscordBot2
{
    class Program
    {
        static void Main()
        {
            Thread t = new Thread(new ThreadStart(input));
            t.Start();
            new Program().RunBotAsync().GetAwaiter().GetResult();
        }


        private DiscordSocketClient _client;
        private CommandService _commands;
        private IServiceProvider _services;

        public async Task RunBotAsync()
        {
            _client = new DiscordSocketClient();
            _commands = new CommandService();

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

            string token = "lol you wish";
            _client.Log += _client_Log;

            await HandleNewUser();

            await RegisterCommandsAsync();

            await _client.LoginAsync(TokenType.Bot, token);

            await _client.StartAsync();

            await _client.SetGameAsync("with the flock!");
                
            await _client.SetStatusAsync(UserStatus.Online);

            await Task.Delay(-1);
        }

        private Task _client_Log(LogMessage arg)
        {
            Console.WriteLine(arg);
            return Task.CompletedTask;
        }

        public async Task RegisterCommandsAsync()
        {
            _client.MessageReceived += HandleCommandAsync; //Handle incoming commands
            await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
        }

        public async Task HandleNewUser()
        {
            _client.UserJoined += AnnounceJoinedUser;
            await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
        }

        public async Task AnnounceJoinedUser(SocketGuildUser user)
        {
            var channel = _client.GetChannel(743578569476931626) as SocketTextChannel; // Gets the channel to send the message in
            await channel.SendMessageAsync($"Welcome {user.Mention} to {channel.Guild.Name}"); //Welcomes the new user
            return;
        }

        private async Task HandleCommandAsync(SocketMessage arg)
        {
            var message = arg as SocketUserMessage;
            var context = new SocketCommandContext(_client, message);
            //Prevent looping
            if (message.Author.IsBot) return;

            //Check for prefix
            int argPos = 0;
            await BadWordsWarn(message);
            if (message.HasStringPrefix("c!", ref argPos))
            {
                //Execute command
                var result = await _commands.ExecuteAsync(context, argPos, _services);
                //If it failed, log the error
                if (!result.IsSuccess)
                { 
                    Console.WriteLine(@"'" + message.Content + @"' has thrown an error: " + result.ErrorReason);
                    //Handle errors in commands
                    result = await _commands.ExecuteAsync(context, argPos, _services);
                    if (result.ErrorReason != null)
                        switch (result.ErrorReason)
                        {
                            //Not enough paramaters
                            case "The input text has too few parameters.":
                                await context.Channel.SendMessageAsync(
                                    "This command lacks parameters. Check the command description for more details.");
                                break;
                            //Bad command
                            case "Unknown command.":
                                await context.Channel.SendMessageAsync(
                                    "I don't understand this command. :frowning: You can type **c!list** to see the list of avaliable commands.");
                                break;
                            //Some other shenanigans
                            default:
                                await context.Channel.SendMessageAsync(
                                    $"{result.ErrorReason}");
                                break;
                        }
                }
            }
        }
        private async Task BadWordsWarn(SocketMessage message)
        {
            string[] badWords = File.ReadAllLines("Banned.txt");

            if (badWords.Any(word => message.Content.IndexOf(word, 0, message.Content.Length, StringComparison.OrdinalIgnoreCase) >= 0))
            {
                var m = (RestUserMessage)await message.Channel.GetMessageAsync(message.Id);
                await m.DeleteAsync();
            }
        }

我不确定 Main() 中变量 input 的来源,但我启动机器人的方式如下:

static void Main() => new Program().RunBotAsync().GetAwaiter().GetResult();

您只需调用 AddModulesAsync() 一次。您在 RegisterCommandsAsync()HandleNewUser() 中调用了它。多次调用它可能会导致问题,因此我将其移至 RunBotAsync() 方法并将其从其他两个方法中删除。我还将行 _client.MessageReceived += HandleCommandAsync;_client.UserJoined += AnnounceJoinedUser; 移动到 RunBotAsync() 方法中,因为我留下了两个单独的方法,每个 运行 一行代码,只需要被调用一次,我觉得不需要额外的方法。 RegisterCommandsAsync()HandleNewUser()方法可以删除。

public async Task RunBotAsync()
{
    _client = new DiscordSocketClient();
    _commands = new CommandService();

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

    await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services); // This should only be called once. You were calling it in RegisterCommandsAsync() and RegisterCommandsAsync()

    string token = "lol you wish";
    _client.Log += _client_Log;

    _client.UserJoined += AnnounceJoinedUser;

    _client.MessageReceived += HandleCommandAsync; //Handle incoming commands

    await _client.LoginAsync(TokenType.Bot, token);

    await _client.StartAsync();

    await _client.SetGameAsync("with the flock!");

    await _client.SetStatusAsync(UserStatus.Online);

    await Task.Delay(-1);
}

您的 HandleCommandAsync() 方法中的行 var message = arg as SocketUserMessage; 可能导致 null,这看起来像是问题的原因。为了处理这个问题,我将该行替换为 if (arg is SocketUserMessage message) 并将所有代码包装在其中。这样您就可以在执行其余代码之前检查 null 。或者,您可以保留原始行并在其下方添加 if (message == null) return;

您还使用 _commands.ExecuteAsync 执行您的命令,如果它们失败,您将再次执行它们,然后再向 Discord 频道发送错误响应。如果一个命令第一次失败,那么它第二次也可能失败,所以我不确定你为什么要再次执行它们。我已经评论了第二行,所以它现在只执行一次。

private async Task HandleCommandAsync(SocketMessage arg)
{
    if (arg is SocketUserMessage message)
    {
        var context = new SocketCommandContext(_client, message);
        //Prevent looping
        if (message.Author.IsBot) return;

        //Check for prefix
        int argPos = 0;
        await BadWordsWarn(message);
        if (message.HasStringPrefix("c!", ref argPos))
        {
            //Execute command
            var result = await _commands.ExecuteAsync(context, argPos, _services);
            //If it failed, log the error
            if (!result.IsSuccess)
            {
                Console.WriteLine(@"'" + message.Content + @"' has thrown an error: " + result.ErrorReason);
                //Handle errors in commands
                //result = await _commands.ExecuteAsync(context, argPos, _services); // Why are you executing the command again after it failed?
                if (result.ErrorReason != null)
                    switch (result.ErrorReason)
                    {
                        //Not enough paramaters
                        case "The input text has too few parameters.":
                            await context.Channel.SendMessageAsync(
                                "This command lacks parameters. Check the command description for more details.");
                            break;
                        //Bad command
                        case "Unknown command.":
                            await context.Channel.SendMessageAsync(
                                "I don't understand this command. :frowning: You can type **c!list** to see the list of avaliable commands.");
                            break;
                        //Some other shenanigans
                        default:
                            await context.Channel.SendMessageAsync(
                                $"{result.ErrorReason}");
                            break;
                    }
            }
        }
    }
}

这个 YouTube 视频帮助我开始使用 Discord.Net。也许它也会对您有所帮助:https://www.youtube.com/watch?v=ccsf5Rcu9mM