private async Task OnCommandExecutedAsync(Optional <CommandInfo> optionalCommand, ICommandContext context, IResult result) { // We have access to the information of the command executed, // the context of the command, and the result returned from the // execution in this event. // command is unspecified when there was a search failure (command not found) if (!optionalCommand.IsSpecified) { await _logService.LogAsync(new LogMessage(LogSeverity.Info, "Command", $"Unknown command: \"{context.Message.Content}\", sent by {context.User} in {context.Display()}")); return; } var command = optionalCommand.Value; if (command.Module.Name != Constants.DevelopmentModuleName) { // Update the command stats lock (_cmdStatsLock) { var stats = DatabaseConfig.CommandStats; if (stats.ContainsKey(command.Name)) { stats[command.Name]++; } else { stats.Add(command.Name, 1); } DatabaseConfig.Update(x => x.CommandStats = stats); } } // the command was successful, we don't care about this result, unless we want to log that a command succeeded. if (result.IsSuccess) { return; } double ignoreTime = Constants.DefaultIgnoreTime; switch (result.Error) { //case CommandError.UnknownCommand: // await SendEmbedAsync(context.Message, string.Format(LocalizationService.Locate("CommandNotFound", context.Message), GetPrefix(context.Channel))); // break; case CommandError.BadArgCount: case CommandError.ParseFailed: string language = GuildUtils.GetLanguage(context.Channel); string prefix = GuildUtils.GetPrefix(context.Channel); await SendEmbedAsync(context.Message, command.ToHelpEmbed(language, prefix), _services); break; case CommandError.UnmetPrecondition when command.Module.Name != Constants.DevelopmentModuleName: ChannelPermissions permissions; if (context.Guild == null) { permissions = ChannelPermissions.All(context.Channel); } else { var guildUser = await context.Guild.GetCurrentUserAsync().ConfigureAwait(false); permissions = guildUser.GetPermissions((IGuildChannel)context.Channel); } if (!permissions.Has(Constants.MinimumRequiredPermissions)) { var builder = new EmbedBuilder() .WithDescription($"\u26a0 {result.ErrorReason}") .WithColor(FergunClient.Config.EmbedColor); try { await context.User.SendMessageAsync(embed : builder.Build()); } catch (HttpException e) when(e.DiscordCode == 50007) { await _logService.LogAsync(new LogMessage(LogSeverity.Warning, "Command", "Unable to send a DM about the minimum required permissions to the user.")); } } else { if (result.ErrorReason.StartsWith("(Cooldown)", StringComparison.OrdinalIgnoreCase)) { ignoreTime = Constants.CooldownIgnoreTime; } await SendEmbedAsync(context.Message, $"\u26a0 {GuildUtils.Locate(result.ErrorReason, context.Channel)}", _services); } break; case CommandError.ObjectNotFound: // reason: The error reason (User not found., Role not found., etc) string reason = result.ErrorReason; // Delete the last char (.) reason = reason.Substring(0, result.ErrorReason.Length - 1); // Convert to title case (User Not Found) reason = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(reason); // Remove spaces (UserNotFound) reason = reason.Replace(" ", string.Empty, StringComparison.OrdinalIgnoreCase); // Locate the string to the current language of the guild reason = GuildUtils.Locate(reason, context.Channel); await SendEmbedAsync(context.Message, $"\u26a0 {reason}", _services); break; case CommandError.MultipleMatches: await SendEmbedAsync(context.Message, $"\u26a0 {GuildUtils.Locate("MultipleMatches", context.Channel)}", _services); break; case CommandError.Unsuccessful: await SendEmbedAsync(context.Message, $"\u26a0 {result.ErrorReason}".Truncate(EmbedBuilder.MaxDescriptionLength), _services); break; case CommandError.Exception when result is ExecuteResult execResult: var exception = execResult.Exception; if (exception is HttpException httpException && httpException.HttpCode >= HttpStatusCode.InternalServerError) { await Task.Delay(2000); var builder = new EmbedBuilder() .WithTitle(GuildUtils.Locate("DiscordServerError", context.Channel)) .WithDescription($"\u26a0 {GuildUtils.Locate("DiscordServerErrorInfo", context.Channel)}") .AddField(GuildUtils.Locate("ErrorDetails", context.Channel), Format.Code($"Code: {(int)httpException.HttpCode}, Reason: {httpException.Reason}", "md")) .WithColor(FergunClient.Config.EmbedColor); try { await SendEmbedAsync(context.Message, builder.Build(), _services); } catch (HttpException) { } break; } var builder2 = new EmbedBuilder() .WithTitle($"\u274c {GuildUtils.Locate("FailedExecution", context.Channel)} {Format.Code(command.Name)}") .AddField(GuildUtils.Locate("ErrorType", context.Channel), Format.Code(exception.GetType().Name, "cs")) .AddField(GuildUtils.Locate("ErrorMessage", context.Channel), Format.Code(exception.Message, "cs")) .WithColor(FergunClient.Config.EmbedColor); var owner = (await context.Client.GetApplicationInfoAsync()).Owner; if (context.User.Id != owner.Id) { builder2.WithFooter(GuildUtils.Locate("ErrorSentToOwner", context.Channel)); } await SendEmbedAsync(context.Message, builder2.Build(), _services); if (context.User.Id == owner.Id) { break; } // if the user that executed the command isn't the bot owner, send the full stack trace to the errors channel var channel = await context.Client.GetChannelAsync(FergunClient.Config.LogChannel); if (!(channel is IMessageChannel messageChannel)) { await _logService.LogAsync(new LogMessage(LogSeverity.Warning, "Command", $"Invalid log channel Id ({FergunClient.Config.LogChannel}). Not possible to send the embed with the error info.")); break; } var builder3 = new EmbedBuilder() .WithTitle($"\u274c Failed to execute {Format.Code(command.Name)} in {context.Display()}".Truncate(EmbedBuilder.MaxTitleLength)) .AddField(GuildUtils.Locate("ErrorType", messageChannel), Format.Code(exception.GetType().Name, "cs")) .AddField(GuildUtils.Locate("ErrorMessage", messageChannel), Format.Code(exception.ToString().Truncate(EmbedFieldBuilder.MaxFieldValueLength - 10), "cs")) .AddField("Jump url", context.Message.GetJumpUrl()) .AddField("Command", context.Message.Content.Truncate(EmbedFieldBuilder.MaxFieldValueLength)) .WithColor(FergunClient.Config.EmbedColor); try { await messageChannel.SendMessageAsync(embed : builder3.Build()); } catch (HttpException e) { await _logService.LogAsync(new LogMessage(LogSeverity.Warning, "Command", "Error while sending the embed in the log channel", e)); } break; } _ = IgnoreUserAsync(context.User.Id, TimeSpan.FromSeconds(ignoreTime)); await _logService.LogAsync(new LogMessage(LogSeverity.Info, "Command", $"Failed to execute \"{command.Name}\" for {context.User} in {context.Display()}, with error type: {result.Error} and reason: {result.ErrorReason}")); }