private Task CommandExecutionFailedAsync(CommandExecutionFailedEventArgs e) { if (e.Result.CommandExecutionStep == CommandExecutionStep.Command && e.Result.Exception is ContextTypeMismatchException contextTypeMismatchException) { var message = "A command context type mismatch occurred while attempting to execute {0}. " + "The module expected {1}, but got {2}."; var args = new List <object>(5) { e.Result.Command.Name, contextTypeMismatchException.ExpectedType, contextTypeMismatchException.ActualType }; // If the expected type is a DiscordGuildCommandContext, the actual type is a DiscordCommandContext, and the module doesn't have guild restrictions. if (typeof(DiscordGuildCommandContext).IsAssignableFrom(contextTypeMismatchException.ExpectedType) && typeof(DiscordCommandContext).IsAssignableFrom(contextTypeMismatchException.ActualType) && !CommandUtilities.EnumerateAllChecks(e.Result.Command.Module).Any(x => x is RequireGuildAttribute)) { message += " Did you forget to decorate the module with {3}?"; args.Add(nameof(RequireGuildAttribute)); } // If the expected type is a custom made context. if (contextTypeMismatchException.ExpectedType != typeof(DiscordGuildCommandContext) && contextTypeMismatchException.ExpectedType != typeof(DiscordCommandContext)) { message += " If you have not overridden {4}, you must do so and have it return the given context type. " + "Otherwise ensure it returns the correct context types."; args.Add(nameof(CreateCommandContext)); } Logger.LogError(message, args.ToArray()); } else { if (e.Result.Exception is OperationCanceledException && StoppingToken.IsCancellationRequested) { // Means the bot is stopping and any exceptions caused by cancellation we can ignore. return(Task.CompletedTask); } Logger.LogError(e.Result.Exception, e.Result.FailureReason); } if (e.Context is not DiscordCommandContext context) { return(Task.CompletedTask); } _ = InternalHandleFailedResultAsync(context, e.Result); return(Task.CompletedTask); }
public Task HelpAsync([Remainder, Description("Command or module.")] string command) { if (string.IsNullOrWhiteSpace(command)) { return(HelpAsync()); } var matchingCommands = _commands.FindCommands(command); if (matchingCommands.Count == 0) { string typoFix = command.Levenshtein(_commands); if (!string.IsNullOrWhiteSpace(typoFix)) { matchingCommands = _commands.FindCommands(typoFix); } } LocalEmbedBuilder embed; if (matchingCommands.Count == 0) //Could be a module. { var matchingModule = _commands.TopLevelModules.FirstOrDefault(x => x.Name.Equals(command, StringComparison.OrdinalIgnoreCase)); if (matchingModule is null) { matchingModule = _commands.TopLevelModules.FirstOrDefault(x => x.Name.Equals(command.Levenshtein(_commands), StringComparison.OrdinalIgnoreCase)); } var modules = _commands.GetAllModules(); if (matchingModule is null) //Look for nested modules { matchingModule = modules.FirstOrDefault(x => x.FullAliases.Any(y => y.Equals(command, StringComparison.OrdinalIgnoreCase))); } if (matchingModule is null) //Look for nested modules with levenshtein { matchingModule = modules.FirstOrDefault(x => x.FullAliases.Any(y => y.Equals(command.Levenshtein(modules.Select(z => z.FullAliases.FirstOrDefault()).ToList()), StringComparison.OrdinalIgnoreCase))); } if (matchingModule is null) //Look for submodule but without complete module path { matchingModule = modules.FirstOrDefault(x => x.Name.Equals(command, StringComparison.OrdinalIgnoreCase)); } if (matchingModule is null) //Look for submodule but without complete module path with levenshtein { matchingModule = modules.FirstOrDefault(x => x.Name.Equals(command.Levenshtein(modules.Select(z => z.Name).ToList()), StringComparison.OrdinalIgnoreCase)); } if (matchingModule is null) //Remove last input { var cmdArgs = command.Split(' ').ToList(); cmdArgs.RemoveAt(cmdArgs.Count - 1); return(HelpAsync(string.Join(' ', cmdArgs))); } embed = new LocalEmbedBuilder { Title = $"Help - {matchingModule.Name} Module", Description = "This embed contains the list of every command in this module.", Footer = new LocalEmbedFooterBuilder { Text = $"{matchingModule.Commands.Count} commands available in this context." } }; if (matchingModule.Submodules.Count > 0) { embed.AddField("Modules", string.Join(", ", matchingModule.Submodules.DistinctBy(x => x.Name).Select(x => $"`{x.Name}`"))); } if (matchingModule.Commands.Count > 0) { embed.AddField("Commands", string.Join(", ", matchingModule.Commands.DistinctBy(x => x.Name).Select(x => $"`{x.Name}`"))); } var moduleChecks = CommandUtilities.EnumerateAllChecks(matchingModule).Cast <AbfCheckBaseAttribute>().ToArray(); if (moduleChecks.Length > 0) { embed.AddField("Requirements", string.Join("\n", moduleChecks.Select(x => $"`- {x.Name}{x.Details}`"))); } return(ReplyAsync(embed: embed.Build())); } embed = new LocalEmbedBuilder { Title = "Help" }; var builder = new StringBuilder(); foreach (var cmd in matchingCommands.Where(c => c.Command.Attributes.All(a => !(a is HiddenAttribute)))) { builder.AppendLine($"**{cmd.Command.Description ?? "Undocumented."}**"); builder.AppendLine(($"`{Context.Prefix}{cmd.Command.Name} {string.Join(" ", cmd.Command.Parameters.Select(GetUsage(cmd.Command)))}`".ToLowerInvariant()).TrimEnd()); foreach (var param in cmd.Command.Parameters) { builder.AppendLine($"`[{param.Name}]`: {param.Description ?? "Undocumented."}"); } builder.AppendLine($"\n**Example:** `{cmd.Command.Remarks ?? "No example provided."}`\n"); } embed.AddField("Usages", builder.ToString()); var defaultCmd = matchingCommands.First().Command; var checks = CommandUtilities.EnumerateAllChecks(defaultCmd.Module).Cast <AbfCheckBaseAttribute>().ToArray(); if (checks.Length > 0) { embed.AddField("Module Requirements", string.Join("\n", checks.Select(x => $"- `{x.Name}{x.Details}`"))); } if (defaultCmd.Checks.Count > 0) { embed.AddField("Command Requirements", string.Join("\n", defaultCmd.Checks.Cast <AbfCheckBaseAttribute>().Select(x => $"- `{x.Name}{x.Details}`"))); } return(ReplyAsync(embed: embed.Build())); }
public static async ValueTask <EmbedBuilder> CreateCommandEmbedAsync(Command command, VolteContext ctx) { var embed = ctx.CreateEmbedBuilder() .WithTitle(command.Name) .WithDescription(command.Description ?? "No description provided."); var checks = CommandUtilities.EnumerateAllChecks(command).ToList(); async Task AddSubcommandsFieldAsync() { embed.AddField("Subcommands", (await command.Module.Commands.WhereAccessibleAsync(ctx) .Where(x => !x.Attributes.Any(a => a is DummyCommandAttribute)).ToListAsync()) .Select(x => FormatCommandShort(x, false)) .Join(", ")); } if (command.Attributes.Any(x => x is DummyCommandAttribute)) { await AddSubcommandsFieldAsync(); return(!checks.IsEmpty() ? embed.AddField("Checks", (await Task.WhenAll(checks.Select(check => FormatCheckAsync(check, ctx)))).Join("\n")) : embed); } if (command.Remarks != null) { embed.AppendDescription($" {command.Remarks}"); } if (!command.FullAliases.IsEmpty()) { embed.AddField("Aliases", command.FullAliases.Select(x => Format.Code(x)).Join(", "), true); } if (!command.Parameters.IsEmpty()) { embed.AddField("Parameters", command.Parameters.Select(FormatParameter).Join("\n")); } if (command.CustomArgumentParserType is null) { embed.AddField("Usage", FormatUsage(ctx, command)); } if (command.Attributes.Any(x => x is ShowPlaceholdersInHelpAttribute)) { embed.AddField("Placeholders", WelcomeOptions.ValidPlaceholders .Select(x => $"{Format.Code($"{{{x.Key}}}")}: {Format.Italics(x.Value)}") .Join("\n")); } if (command.Attributes.Any(x => x is ShowTimeFormatInHelpAttribute)) { embed.AddField("Example Valid Time", $"{Format.Code("4d3h2m1s")}: {Format.Italics("4 days, 3 hours, 2 minutes and one second.")}"); } if (command.Attributes.Any(x => x is ShowSubcommandsInHelpOverrideAttribute)) { await AddSubcommandsFieldAsync(); } if (command.Attributes.AnyGet(x => x is ShowUnixArgumentsInHelpAttribute, out var unixAttr) && unixAttr is ShowUnixArgumentsInHelpAttribute attr) {