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.")); } }