public static async Task <ICommandResult> Help(DiscordMessageContext context, [DisplayName("page | command"), HelpText("Specifies page number or gets help for this specific command")] string command = null, [HelpText("Whether or not to show parameter descriptions")] Verbosity verbosity = Verbosity.Default) { const int pageSize = 5; string commandPrefix = CommandTools.GetCommandPrefix(context, context.Channel); var builder = new EmbedBuilder { Title = "Help", Color = new Color(33, 150, 243) }; bool paginate = int.TryParse(command, out int page); if (paginate) { command = null; } else if (command == null) { page = 1; paginate = true; } if (verbosity == Verbosity.Default) { verbosity = command != null ? Verbosity.Verbose : Verbosity.Standard; } var commands = command == null ? commandMethods.Where(method => method.GetCustomAttribute <CommandScopeAttribute>() ?.ChannelTypes.Contains(context.ChannelType) ?? true) .Where(method => method.GetCustomAttribute <PermissionsAttribute>()?.GetPermissionError(context) == null) .ToList() : GetCommands(command.StartsWith(commandPrefix) ? command.Substring(commandPrefix.Length) : command) .ToList(); if (commands.Count == 0 || commands[0] == null) { return(new ErrorResult($"The requested command {commandPrefix}{command} was not found")); } List <EmbedFieldBuilder> fields = new List <EmbedFieldBuilder>(); foreach (MethodInfo method in commands) { var title = new StringBuilder(); title.Append("`"); title.Append(string.Join("|", method.GetCustomAttribute <CommandAttribute>().Names.Select(name => $"{commandPrefix}{name}"))); title.Append(" "); title.Append(string.Join(" ", method.GetParameters() .Skip(1) .Select(param => param.IsOptional ? $"[{param.GetCustomAttribute<DisplayNameAttribute>()?.Name ?? param.Name}{(param.IsDefined(typeof(JoinRemainingParametersAttribute)) ? "..." : "")}{(param.DefaultValue == null ? "" : $" = {param.DefaultValue}")}]"
public static async Task <ICommandResult> Run(string commandMessage, ICommandContext context, string prefix, bool awaitResult) { var args = commandMessage.Trim().Substring(prefix.Length).Split(' '); string commandName = args[0]; args = CommandTools.ParseArguments(string.Join(" ", args.Skip(1))); //MethodInfo command = GetCommand(commandName, args.Length); var commands = GetCommands(commandName); await DiscordBot.Log(context.LogMessage(commandName)); List <object> values = null; ICommandResult error = null; MethodInfo commandToRun = null; foreach (var command in commands) { var parameters = command.GetParameters(); var requiredParameters = parameters.Where(param => !param.IsOptional).ToArray(); Type contextType = context.GetType(); if (!parameters[0].ParameterType.IsAssignableFrom(contextType)) { string message = $"That command is not valid in the context {context.GetType().Name}"; error = error ?? new ErrorResult(message, "Invalid Context"); continue; } if (args.Length < requiredParameters.Length - 1) { string message = $"The syntax of the command was incorrect. The following parameters are required: `{string.Join("`, `", requiredParameters.Select(param => param.GetCustomAttribute<DisplayNameAttribute>()?.Name ?? param.Name).Skip(args.Length + 1))}`\nUse `{prefix}help {commandName}` for command info"; error = error ?? new ErrorResult(message, "Syntax Error"); continue; } if (context is DiscordMessageContext discordContext) { if (!(command.GetCustomAttribute <CommandScopeAttribute>()?.ChannelTypes.Contains(discordContext.ChannelType) ?? true)) { string message = $"The command `{prefix}{commandName}` is not valid in the scope {discordContext.ChannelType}"; error = error ?? new ErrorResult(message, "Scope Error"); continue; } string permissionError = command.GetCustomAttribute <PermissionsAttribute>()?.GetPermissionError(discordContext); if (permissionError != null) { error = error ?? new ErrorResult(permissionError, "Permission Error"); continue; } } values = new List <object> { context }; bool err = false; for (int i = 1; i < parameters.Length; i++) { if (parameters[i].IsOptional && i > args.Length) { values.Add(Type.Missing); continue; } if (i == parameters.Length - 1 && parameters[i].IsDefined(typeof(JoinRemainingParametersAttribute))) { if (parameters[i].ParameterType == typeof(string)) { values.Add(string.Join(" ", args.Skip(i - 1))); } else if (parameters[i].ParameterType == typeof(string[])) { values.Add(args.Skip(i - 1).ToArray()); } else { object converted = ConvertToType(parameters[i].ParameterType, string.Join(" ", args.Skip(i - 1)), context); if (converted == null) { error = error ?? ThrowTypeError(context, string.Join(" ", args.Skip(i - 1)), parameters[i]); err = true; break; } values.Add(converted); } break; } var result = ConvertToType(parameters[i].ParameterType, args[i - 1], context); if (result == null) { error = error ?? ThrowTypeError(context, args[i - 1], parameters[i]); err = true; break; } values.Add(result); } if (err) { continue; } error = null; commandToRun = command; break; } if (args.Length > 0 && context is DiscordMessageContext d && args[0] == "help" && commandName != "help") { var embed = new EmbedBuilder() .WithDescription($"Did you mean `{prefix}help {commandName}`?") .WithColor(new Color(0x2AC2E9)) .WithAuthor(author => { author .WithName("Help") .WithIconUrl("https://www.shareicon.net/data/128x128/2016/08/18/809295_info_512x512.png"); }); await d.Reply("", embed : embed.Build()); } if (error != null) { await context.ReplyError(error.Message, ((ErrorResult)error).Title); return(error); } else { if (awaitResult) { return(await RunCommand(context, commandToRun, values.ToArray())); } #pragma warning disable 4014 RunCommand(context, commandToRun, values.ToArray()); #pragma warning restore 4014 return(new SuccessResult()); } }