Example #1
0
        private async Task HandleCommand(SocketUserMessage message, CommandRegistrationFindResult findResult)
        {
            var correlationId = Guid.NewGuid();
            var logger        = _logger.WithCommandScope(message, correlationId, findResult.Registration, findResult.Usage);

            var stopwatch    = Stopwatch.StartNew();
            var gatewayDelay = DateTimeOffset.UtcNow - message.Timestamp;

            var counter = Interlocked.Increment(ref _commandCounter);
            var guild   = (message.Channel as IGuildChannel)?.Guild;

            // Don't log command content for non-guild channels, since these commands are usually meant to be private
            if (guild != null)
            {
                logger.LogInformation("Command {CommandCounter} {MessageContent} with {MessageAttachmentCount} attachments", counter, message.Content, message.Attachments.Count);
            }
            else
            {
                logger.LogInformation("Command {CommandCounter} {MessageContentRedacted} with {MessageAttachmentCount} attachments", counter, findResult.Prefix + findResult.Usage.InvokeUsage, message.Attachments.Count);
            }

            // Check if the channel type is valid for this command
            if (!IsValidCommandSource(message.Channel, findResult.Registration))
            {
                logger.LogInformation("Command {CommandCounter} rejected in {TotalElapsed:F3}s (g: {GatewayElapsed:F3}s) due to invalid channel type", counter, stopwatch.Elapsed.TotalSeconds, gatewayDelay.TotalSeconds);

                if (message.Channel is ITextChannel && findResult.Registration.Flags.HasFlag(CommandFlags.DirectMessageOnly))
                {
                    await _communicator.CommandReplyDirectMessageOnly(message.Channel, findResult.Registration);
                }

                return;
            }

            // Check owner
            if (findResult.Registration.Flags.HasFlag(CommandFlags.OwnerOnly) && !_ownerIDs.Contains(message.Author.Id))
            {
                logger.LogInformation("Command {CommandCounter} rejected in {TotalElapsed:F3}s (g: {GatewayElapsed:F3}s) due to the user not being an owner", counter, stopwatch.Elapsed.TotalSeconds, gatewayDelay.TotalSeconds);
                await _communicator.CommandReplyNotOwner(message.Channel, findResult.Registration);

                return;
            }

            // Check guild permisssions
            if (message.Channel is IGuildChannel guildChannel)
            {
                // User
                var guildUser          = message.Author as IGuildUser;
                var missingPermissions = findResult.Registration.UserPermissions.Where(x => !guildUser.GuildPermissions.Has(x));
                if (missingPermissions.Any())
                {
                    logger.LogInformation("Command {CommandCounter} rejected in {TotalElapsed:F3}s (g: {GatewayElapsed:F3}s) due to missing user permissions", counter, stopwatch.Elapsed.TotalSeconds, gatewayDelay.TotalSeconds);
                    await _communicator.CommandReplyMissingPermissions(message.Channel, findResult.Registration, missingPermissions);

                    return;
                }

                // Bot
                var selfUser = await guild.GetCurrentUserAsync();

                var missingBotPermissions = findResult.Registration.BotPermissions.Where(x => !selfUser.GuildPermissions.Has(x));
                if (missingBotPermissions.Any())
                {
                    logger.LogInformation("Command {CommandCounter} rejected in {TotalElapsed:F3}s (g: {GatewayElapsed:F3}s) due to missing bot permissions", counter, stopwatch.Elapsed.TotalSeconds, gatewayDelay.TotalSeconds);
                    await _communicator.CommandReplyMissingBotPermissions(message.Channel, findResult.Registration, missingBotPermissions);

                    return;
                }
            }

            var verificationElapsed = stopwatch.Elapsed;

            // Create command
            var parseResult = await _commandParser.Parse(message, findResult.Registration, findResult.Usage, findResult.Prefix);

            if (parseResult.Type != CommandParseResultType.Success)
            {
                string explanation = "";
                switch (parseResult.Type)
                {
                case CommandParseResultType.NotEnoughParameters: explanation = Properties.Resources.Command_NotEnoughParameters; break;

                case CommandParseResultType.TooManyParameters: explanation = Properties.Resources.Command_TooManyParameters; break;

                case CommandParseResultType.InvalidParameterFormat: explanation = string.Format(Properties.Resources.Command_InvalidParameterFormat, ((InvalidParameterCommandParseResult)parseResult).InvalidPosition); break;
                }

                logger.LogInformation("Command {CommandCounter} rejected in {TotalElapsed:F3}s (g: {GatewayElapsed:F3}s) due to incorrect parameters", counter, stopwatch.Elapsed.TotalSeconds, gatewayDelay.TotalSeconds);
                await _communicator.CommandReplyIncorrectParameters(message.Channel, findResult.Registration, explanation, findResult.Prefix);

                return;
            }

            var command        = new Command((SuccessCommandParseResult)parseResult, _communicator);
            var parsingElapsed = stopwatch.Elapsed;

            // Execute
            if (findResult.Registration.Flags.HasFlag(CommandFlags.Synchronous))
            {
                await ExecuteCommandAsync(counter, correlationId, logger, findResult, command, stopwatch, verificationElapsed, parsingElapsed, gatewayDelay);
            }
            else
            {
                TaskHelper.FireForget(() => ExecuteCommandAsync(counter, correlationId, logger, findResult, command, stopwatch, verificationElapsed, parsingElapsed, gatewayDelay),
                                      x => logger.LogError(x, "Uncaught exception while handling command."));
            }
        }