| @@ -43,6 +43,9 @@ | |||
| <Compile Include="..\Discord.Net.Commands\CommandBuilder.cs"> | |||
| <Link>CommandBuilder.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Commands\CommandMap.cs"> | |||
| <Link>CommandMap.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Commands\CommandParser.cs"> | |||
| <Link>CommandParser.cs</Link> | |||
| </Compile> | |||
| @@ -1,23 +1,102 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Threading.Tasks; | |||
| namespace Discord.Commands | |||
| { | |||
| public sealed class CommandParameter | |||
| { | |||
| public string Name { get; } | |||
| public bool IsOptional { get; } | |||
| public bool IsCatchAll { get; } | |||
| public CommandParameter(string name, bool isOptional, bool isCatchAll) | |||
| { | |||
| Name = name; | |||
| IsOptional = isOptional; | |||
| IsCatchAll = isCatchAll; | |||
| } | |||
| } | |||
| public sealed class Command | |||
| { | |||
| public string Text { get; } | |||
| public int? MinArgs { get; internal set; } | |||
| public int? MaxArgs { get; internal set; } | |||
| public int? MinArgs { get; private set; } | |||
| public int? MaxArgs { get; private set; } | |||
| public int MinPerms { get; internal set; } | |||
| public bool IsHidden { get; internal set; } | |||
| public string Description { get; internal set; } | |||
| internal Func<CommandEventArgs, Task> Handler; | |||
| public IEnumerable<string> Aliases => _aliases; | |||
| private string[] _aliases; | |||
| public IEnumerable<CommandParameter> Parameters => _parameters; | |||
| private CommandParameter[] _parameters; | |||
| private Func<CommandEventArgs, Task> _handler; | |||
| internal Command(string text) | |||
| { | |||
| Text = text; | |||
| IsHidden = false; // Set false by default to avoid null error | |||
| Description = "No description set for this command."; | |||
| IsHidden = false; | |||
| _aliases = new string[0]; | |||
| _parameters = new CommandParameter[0]; | |||
| } | |||
| internal void SetAliases(string[] aliases) | |||
| { | |||
| _aliases = aliases; | |||
| } | |||
| internal void SetParameters(CommandParameter[] parameters) | |||
| { | |||
| _parameters = parameters; | |||
| if (parameters != null) | |||
| { | |||
| if (parameters.Length == 0) | |||
| { | |||
| MinArgs = 0; | |||
| MaxArgs = 0; | |||
| } | |||
| else | |||
| { | |||
| if (parameters[parameters.Length - 1].IsCatchAll) | |||
| MaxArgs = null; | |||
| else | |||
| MaxArgs = parameters.Length; | |||
| int? optionalStart = null; | |||
| for (int i = parameters.Length - 1; i >= 0; i--) | |||
| { | |||
| if (parameters[i].IsOptional) | |||
| optionalStart = i; | |||
| else | |||
| break; | |||
| } | |||
| if (optionalStart == null) | |||
| MinArgs = MaxArgs; | |||
| else | |||
| MinArgs = optionalStart.Value; | |||
| } | |||
| } | |||
| } | |||
| internal void SetHandler(Func<CommandEventArgs, Task> func) | |||
| { | |||
| _handler = func; | |||
| } | |||
| internal void SetHandler(Action<CommandEventArgs> func) | |||
| { | |||
| _handler = e => { func(e); return TaskHelper.CompletedTask; }; | |||
| } | |||
| internal Task Run(CommandEventArgs args) | |||
| { | |||
| var task = _handler(args); | |||
| if (task != null) | |||
| return task; | |||
| else | |||
| return TaskHelper.CompletedTask; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,50 +1,55 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Threading.Tasks; | |||
| namespace Discord.Commands | |||
| { | |||
| public sealed class CommandBuilder | |||
| { | |||
| private readonly CommandsPlugin _plugin; | |||
| private readonly Command _command; | |||
| public CommandBuilder(Command command) | |||
| private List<CommandParameter> _params; | |||
| private bool _hasOptional, _hasCatchAll; | |||
| private string _prefix; | |||
| public CommandBuilder(CommandsPlugin plugin, Command command, string prefix) | |||
| { | |||
| _plugin = plugin; | |||
| _command = command; | |||
| } | |||
| public CommandBuilder ArgsEqual(int argCount) | |||
| { | |||
| _command.MinArgs = argCount; | |||
| _command.MaxArgs = argCount; | |||
| return this; | |||
| } | |||
| public CommandBuilder ArgsAtLeast(int minArgCount) | |||
| { | |||
| _command.MinArgs = minArgCount; | |||
| _command.MaxArgs = null; | |||
| return this; | |||
| } | |||
| public CommandBuilder ArgsAtMost(int maxArgCount) | |||
| _params = new List<CommandParameter>(); | |||
| _prefix = prefix; | |||
| } | |||
| public CommandBuilder Alias(params string[] aliases) | |||
| { | |||
| _command.MinArgs = null; | |||
| _command.MaxArgs = maxArgCount; | |||
| aliases = aliases.Select(x => AppendPrefix(_prefix, x)).ToArray(); | |||
| _command.SetAliases(aliases); | |||
| return this; | |||
| } | |||
| public CommandBuilder ArgsBetween(int minArgCount, int maxArgCount) | |||
| public CommandBuilder Info(string description) | |||
| { | |||
| _command.MinArgs = minArgCount; | |||
| _command.MaxArgs = maxArgCount; | |||
| _command.Description = description; | |||
| return this; | |||
| } | |||
| public CommandBuilder NoArgs() | |||
| public CommandBuilder Parameter(string name, bool isOptional = false, bool isCatchAll = false) | |||
| { | |||
| _command.MinArgs = 0; | |||
| _command.MaxArgs = 0; | |||
| if (_hasCatchAll) | |||
| throw new Exception("No parameters may be added after the catch-all"); | |||
| if (_hasOptional && isOptional) | |||
| throw new Exception("Non-optional parameters may not be added after an optional one"); | |||
| _params.Add(new CommandParameter(name, isOptional, isCatchAll)); | |||
| if (isOptional) | |||
| _hasOptional = true; | |||
| if (isCatchAll) | |||
| _hasCatchAll = true; | |||
| return this; | |||
| } | |||
| public CommandBuilder AnyArgs() | |||
| public CommandBuilder IsHidden() | |||
| { | |||
| _command.MinArgs = null; | |||
| _command.MaxArgs = null; | |||
| _command.IsHidden = true; | |||
| return this; | |||
| } | |||
| @@ -54,32 +59,45 @@ namespace Discord.Commands | |||
| return this; | |||
| } | |||
| public CommandBuilder Desc(string desc) | |||
| { | |||
| _command.Description = desc; | |||
| return this; | |||
| } | |||
| public CommandBuilder IsHidden() | |||
| { | |||
| _command.IsHidden = true; | |||
| return this; | |||
| } | |||
| public CommandBuilder Do(Func<CommandEventArgs, Task> func) | |||
| public void Do(Func<CommandEventArgs, Task> func) | |||
| { | |||
| _command.Handler = func; | |||
| return this; | |||
| _command.SetHandler(func); | |||
| Build(); | |||
| } | |||
| public CommandBuilder Do(Action<CommandEventArgs> func) | |||
| public void Do(Action<CommandEventArgs> func) | |||
| { | |||
| _command.Handler = e => { func(e); return TaskHelper.CompletedTask; }; | |||
| return this; | |||
| _command.SetHandler(func); | |||
| Build(); | |||
| } | |||
| private void Build() | |||
| { | |||
| _command.SetParameters(_params.ToArray()); | |||
| foreach (var alias in _command.Aliases) | |||
| _plugin.Map.AddCommand(alias, _command); | |||
| _plugin.AddCommand(_command); | |||
| } | |||
| internal static string AppendPrefix(string prefix, string cmd) | |||
| { | |||
| if (cmd != "") | |||
| { | |||
| if (prefix != "") | |||
| return prefix + ' ' + cmd; | |||
| else | |||
| return cmd; | |||
| } | |||
| else | |||
| { | |||
| if (prefix != "") | |||
| return prefix; | |||
| else | |||
| throw new ArgumentOutOfRangeException(nameof(cmd)); | |||
| } | |||
| } | |||
| } | |||
| public sealed class CommandGroupBuilder | |||
| { | |||
| private readonly CommandsPlugin _plugin; | |||
| internal readonly CommandsPlugin _plugin; | |||
| private readonly string _prefix; | |||
| private int _defaultMinPermissions; | |||
| @@ -104,25 +122,9 @@ namespace Discord.Commands | |||
| => CreateCommand(""); | |||
| public CommandBuilder CreateCommand(string cmd) | |||
| { | |||
| string text; | |||
| if (cmd != "") | |||
| { | |||
| if (_prefix != "") | |||
| text = _prefix + ' ' + cmd; | |||
| else | |||
| text = cmd; | |||
| } | |||
| else | |||
| { | |||
| if (_prefix != "") | |||
| text = _prefix; | |||
| else | |||
| throw new ArgumentOutOfRangeException(nameof(cmd)); | |||
| } | |||
| var command = new Command(text); | |||
| var command = new Command(CommandBuilder.AppendPrefix(_prefix, cmd)); | |||
| command.MinPerms = _defaultMinPermissions; | |||
| _plugin.AddCommand(command); | |||
| return new CommandBuilder(command); | |||
| return new CommandBuilder(_plugin, command, _prefix); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,84 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| namespace Discord.Commands | |||
| { | |||
| internal class CommandMap | |||
| { | |||
| private CommandMap _parent; | |||
| private Command _command; | |||
| private readonly Dictionary<string, CommandMap> _subCommands; | |||
| public CommandMap(CommandMap parent) | |||
| { | |||
| _parent = parent; | |||
| _subCommands = new Dictionary<string, CommandMap>(); | |||
| } | |||
| public CommandMap GetMap(string text) | |||
| { | |||
| CommandMap map; | |||
| if (_subCommands.TryGetValue(text, out map)) | |||
| return map; | |||
| else | |||
| return null; | |||
| } | |||
| public Command GetCommand() | |||
| { | |||
| if (_command != null) | |||
| return _command; | |||
| else if (_parent != null) | |||
| return _parent.GetCommand(); | |||
| else | |||
| return null; | |||
| } | |||
| public Command GetCommand(string text) | |||
| { | |||
| return GetCommand(0, text.Split(' ')); | |||
| } | |||
| public Command GetCommand(int index, string[] parts) | |||
| { | |||
| if (index != parts.Length) | |||
| { | |||
| string nextPart = parts[index]; | |||
| CommandMap nextGroup; | |||
| if (_subCommands.TryGetValue(nextPart, out nextGroup)) | |||
| { | |||
| var cmd = nextGroup.GetCommand(index + 1, parts); | |||
| if (cmd != null) | |||
| return cmd; | |||
| } | |||
| } | |||
| if (_command != null) | |||
| return _command; | |||
| return null; | |||
| } | |||
| public void AddCommand(string text, Command command) | |||
| { | |||
| AddCommand(0, text.Split(' '), command); | |||
| } | |||
| public void AddCommand(int index, string[] parts, Command command) | |||
| { | |||
| if (index != parts.Length) | |||
| { | |||
| string nextPart = parts[index]; | |||
| CommandMap nextGroup; | |||
| if (!_subCommands.TryGetValue(nextPart, out nextGroup)) | |||
| { | |||
| nextGroup = new CommandMap(this); | |||
| _subCommands.Add(nextPart, nextGroup); | |||
| } | |||
| nextGroup.AddCommand(index + 1, parts, command); | |||
| } | |||
| else | |||
| { | |||
| if (_command != null) | |||
| throw new InvalidOperationException("A command has already been added with this path."); | |||
| _command = command; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -15,37 +15,69 @@ namespace Discord.Commands | |||
| } | |||
| //TODO: Check support for escaping | |||
| public static class CommandParser | |||
| internal static class CommandParser | |||
| { | |||
| private enum CommandParserPart | |||
| { | |||
| None, | |||
| CommandName, | |||
| Parameter, | |||
| QuotedParameter, | |||
| DoubleQuotedParameter | |||
| } | |||
| public static bool Parse(string input, out string command, out CommandPart[] args) | |||
| public static bool ParseCommand(string input, CommandMap map, out Command command, out int endPos) | |||
| { | |||
| return Parse(input, out command, out args, true); | |||
| } | |||
| public static bool ParseArgs(string input, out CommandPart[] args) | |||
| { | |||
| string ignored; | |||
| return Parse(input, out ignored, out args, false); | |||
| int startPosition = 0; | |||
| int endPosition = 0; | |||
| int inputLength = input.Length; | |||
| bool isEscaped = false; | |||
| command = null; | |||
| endPos = 0; | |||
| if (input == "") | |||
| return false; | |||
| while (endPosition < inputLength) | |||
| { | |||
| char currentChar = input[endPosition++]; | |||
| if (isEscaped) | |||
| isEscaped = false; | |||
| else if (currentChar == '\\') | |||
| isEscaped = true; | |||
| if ((!isEscaped && currentChar == ' ') || endPosition >= inputLength) | |||
| { | |||
| int length = (currentChar == ' ' ? endPosition - 1 : endPosition) - startPosition; | |||
| string temp = input.Substring(startPosition, length); | |||
| if (temp == "") | |||
| startPosition = endPosition; | |||
| else | |||
| { | |||
| var newMap = map.GetMap(temp); | |||
| if (newMap != null) | |||
| { | |||
| map = newMap; | |||
| endPos = endPosition; | |||
| } | |||
| else | |||
| break; | |||
| startPosition = endPosition; | |||
| } | |||
| } | |||
| } | |||
| command = map.GetCommand(); //Work our way backwards to find a command that matches our input | |||
| return command != null; | |||
| } | |||
| private static bool Parse(string input, out string command, out CommandPart[] args, bool parseCommand) | |||
| public static bool ParseArgs(string input, int startPos, Command command, out CommandPart[] args) | |||
| { | |||
| CommandParserPart currentPart = parseCommand ? CommandParserPart.CommandName : CommandParserPart.None; | |||
| int startPosition = 0; | |||
| int endPosition = 0; | |||
| CommandParserPart currentPart = CommandParserPart.None; | |||
| int startPosition = startPos; | |||
| int endPosition = startPos; | |||
| int inputLength = input.Length; | |||
| bool isEscaped = false; | |||
| List<CommandPart> argList = new List<CommandPart>(); | |||
| command = null; | |||
| args = null; | |||
| if (input == "") | |||
| @@ -61,21 +93,6 @@ namespace Discord.Commands | |||
| switch (currentPart) | |||
| { | |||
| case CommandParserPart.CommandName: | |||
| if ((!isEscaped && currentChar == ' ') || endPosition >= inputLength) | |||
| { | |||
| int length = (currentChar == ' ' ? endPosition - 1 : endPosition) - startPosition; | |||
| string temp = input.Substring(startPosition, length); | |||
| if (temp == "") | |||
| startPosition = endPosition; | |||
| else | |||
| { | |||
| currentPart = CommandParserPart.None; | |||
| command = temp; | |||
| startPosition = endPosition; | |||
| } | |||
| } | |||
| break; | |||
| case CommandParserPart.None: | |||
| if ((!isEscaped && currentChar == '\"')) | |||
| { | |||
| @@ -126,9 +143,6 @@ namespace Discord.Commands | |||
| } | |||
| } | |||
| if (parseCommand && (command == null || command == "")) | |||
| return false; | |||
| args = argList.ToArray(); | |||
| return true; | |||
| } | |||
| @@ -22,7 +22,7 @@ namespace Discord.Commands | |||
| } | |||
| } | |||
| public enum CommandErrorType { Exception, UnknownCommand, BadPermissions, BadArgCount } | |||
| public enum CommandErrorType { Exception, UnknownCommand, BadPermissions, BadArgCount, InvalidInput } | |||
| public class CommandErrorEventArgs : CommandEventArgs | |||
| { | |||
| public CommandErrorType ErrorType { get; } | |||
| @@ -10,29 +10,30 @@ namespace Discord.Commands | |||
| public partial class CommandsPlugin | |||
| { | |||
| private readonly DiscordClient _client; | |||
| private Func<User, int> _getPermissions; | |||
| private Dictionary<string, Command> _commands; | |||
| private readonly Func<User, int> _getPermissions; | |||
| public Dictionary<string, Command> Commands => _commands; | |||
| public IEnumerable<Command> Commands => _commands; | |||
| private readonly List<Command> _commands; | |||
| internal CommandMap Map => _map; | |||
| private readonly CommandMap _map; | |||
| public char CommandChar { get { return CommandChars[0]; } set { CommandChars = new List<char> { value }; } } // This could possibly be removed entirely. Not sure. | |||
| public List<char> CommandChars { get; set; } | |||
| public bool UseCommandChar { get; set; } | |||
| public IEnumerable<char> CommandChars { get { return _commandChars; } set { _commandChars = value.ToArray(); } } | |||
| private char[] _commandChars; | |||
| public bool RequireCommandCharInPublic { get; set; } | |||
| public bool RequireCommandCharInPrivate { get; set; } | |||
| public bool HelpInPublic { get; set; } | |||
| public CommandsPlugin(DiscordClient client, Func<User, int> getPermissions = null, bool builtInHelp = false) | |||
| { | |||
| _client = client; // Wait why is this even set | |||
| _client = client; | |||
| _getPermissions = getPermissions; | |||
| _commands = new Dictionary<string, Command>(); | |||
| _commands = new List<Command>(); | |||
| _map = new CommandMap(null); | |||
| CommandChar = '!'; // Kept around to keep from possibly throwing an error. Might not be necessary. | |||
| CommandChars = new List<char> { '!', '?', '/' }; | |||
| UseCommandChar = true; | |||
| _commandChars = new char[] { '!' }; | |||
| RequireCommandCharInPublic = true; | |||
| RequireCommandCharInPrivate = true; | |||
| HelpInPublic = true; | |||
| @@ -40,9 +41,9 @@ namespace Discord.Commands | |||
| if (builtInHelp) | |||
| { | |||
| CreateCommand("help") | |||
| .ArgsBetween(0, 1) | |||
| .Parameter("command", isOptional: true) | |||
| .IsHidden() | |||
| .Desc("Returns information about commands.") | |||
| .Info("Returns information about commands.") | |||
| .Do(async e => | |||
| { | |||
| if (e.Command.Text != "help") | |||
| @@ -50,29 +51,18 @@ namespace Discord.Commands | |||
| else | |||
| { | |||
| if (e.Args == null) | |||
| { | |||
| StringBuilder output = new StringBuilder(); | |||
| bool first = true; | |||
| { | |||
| int permissions = getPermissions(e.User); | |||
| StringBuilder output = new StringBuilder(); | |||
| output.AppendLine("These are the commands you can use:"); | |||
| output.Append("`"); | |||
| int permissions = getPermissions(e.User); | |||
| foreach (KeyValuePair<string, Command> k in _commands) | |||
| { | |||
| if (permissions >= k.Value.MinPerms && !k.Value.IsHidden) | |||
| if (first) | |||
| { | |||
| output.Append(k.Key); | |||
| first = false; | |||
| } | |||
| else | |||
| output.Append($", {k.Key}"); | |||
| } | |||
| output.Append(string.Join(", ", _commands.Select(x => permissions >= x.MinPerms && !x.IsHidden))); | |||
| output.Append("`"); | |||
| if (CommandChars.Count == 1) | |||
| output.AppendLine($"{Environment.NewLine}You can use `{CommandChars[0]}` to call a command."); | |||
| if (_commandChars.Length == 1) | |||
| output.AppendLine($"\nYou can use `{_commandChars[0]}` to call a command."); | |||
| else | |||
| output.AppendLine($"{Environment.NewLine}You can use `{String.Join(" ", CommandChars.Take(CommandChars.Count - 1))}` and `{CommandChars.Last()}` to call a command."); | |||
| output.AppendLine($"\nYou can use `{string.Join(" ", CommandChars.Take(_commandChars.Length - 1))}` and `{_commandChars.Last()}` to call a command."); | |||
| output.AppendLine("`help <command>` can tell you more about how to use a command."); | |||
| @@ -80,8 +70,9 @@ namespace Discord.Commands | |||
| } | |||
| else | |||
| { | |||
| if (_commands.ContainsKey(e.Args[0])) | |||
| await Reply(e, CommandDetails(_commands[e.Args[0]])); | |||
| var cmd = _map.GetCommand(e.Args[0]); | |||
| if (cmd != null) | |||
| await Reply(e, CommandDetails(cmd)); | |||
| else | |||
| await Reply(e, $"`{e.Args[0]}` is not a valid command."); | |||
| } | |||
| @@ -99,7 +90,7 @@ namespace Discord.Commands | |||
| string msg = e.Message.Text; | |||
| if (msg.Length == 0) return; | |||
| if (UseCommandChar) | |||
| if (_commandChars.Length > 0) | |||
| { | |||
| bool isPrivate = e.Message.Channel.IsPrivate; | |||
| bool hasCommandChar = CommandChars.Contains(msg[0]); | |||
| @@ -112,44 +103,41 @@ namespace Discord.Commands | |||
| return; // Same, but public. | |||
| } | |||
| string cmd; | |||
| CommandPart[] args; | |||
| if (!CommandParser.Parse(msg, out cmd, out args)) | |||
| return; | |||
| if (_commands.ContainsKey(cmd)) | |||
| { | |||
| Command comm = _commands[cmd]; | |||
| //Clean args | |||
| //Parse command | |||
| Command command; | |||
| int argPos; | |||
| CommandParser.ParseCommand(msg, _map, out command, out argPos); | |||
| if (command == null) | |||
| { | |||
| CommandEventArgs errorArgs = new CommandEventArgs(e.Message, null, null, null); | |||
| RaiseCommandError(CommandErrorType.UnknownCommand, errorArgs); | |||
| return; | |||
| } | |||
| else | |||
| { | |||
| //Parse arguments | |||
| CommandPart[] args; | |||
| if (!CommandParser.ParseArgs(msg, argPos, command, out args)) | |||
| { | |||
| CommandEventArgs errorArgs = new CommandEventArgs(e.Message, null, null, null); | |||
| RaiseCommandError(CommandErrorType.InvalidInput, errorArgs); | |||
| return; | |||
| } | |||
| int argCount = args.Length; | |||
| string[] newArgs = null; | |||
| if (comm.MaxArgs != null && argCount > 0) | |||
| { | |||
| newArgs = new string[(int)comm.MaxArgs]; | |||
| for (int j = 0; j < newArgs.Length; j++) | |||
| newArgs[j] = args[j].Value; | |||
| } | |||
| else if (comm.MaxArgs == null && comm.MinArgs == null) | |||
| { | |||
| newArgs = new string[argCount]; | |||
| for (int j = 0; j < newArgs.Length; j++) | |||
| newArgs[j] = args[j].Value; | |||
| } | |||
| //Get information for the rest of the steps | |||
| int userPermissions = _getPermissions != null ? _getPermissions(e.Message.User) : 0; | |||
| var eventArgs = new CommandEventArgs(e.Message, comm, userPermissions, newArgs); | |||
| var eventArgs = new CommandEventArgs(e.Message, command, userPermissions, args.Select(x => x.Value).ToArray()); | |||
| // Check permissions | |||
| if (userPermissions < comm.MinPerms) | |||
| if (userPermissions < command.MinPerms) | |||
| { | |||
| RaiseCommandError(CommandErrorType.BadPermissions, eventArgs); | |||
| return; | |||
| } | |||
| //Check arg count | |||
| if (argCount < comm.MinArgs) | |||
| if (argCount < command.MinArgs) | |||
| { | |||
| RaiseCommandError(CommandErrorType.BadArgCount, eventArgs); | |||
| return; | |||
| @@ -159,21 +147,13 @@ namespace Discord.Commands | |||
| try | |||
| { | |||
| RaiseRanCommand(eventArgs); | |||
| var task = comm.Handler(eventArgs); | |||
| if (task != null) | |||
| await task.ConfigureAwait(false); | |||
| } | |||
| catch (Exception ex) | |||
| { | |||
| RaiseCommandError(CommandErrorType.Exception, eventArgs, ex); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| CommandEventArgs eventArgs = new CommandEventArgs(e.Message, null, null, null); | |||
| RaiseCommandError(CommandErrorType.UnknownCommand, eventArgs); | |||
| return; | |||
| } | |||
| await command.Run(eventArgs).ConfigureAwait(false); | |||
| } | |||
| catch (Exception ex) | |||
| { | |||
| RaiseCommandError(CommandErrorType.Exception, eventArgs, ex); | |||
| } | |||
| } | |||
| }; | |||
| } | |||
| @@ -198,7 +178,7 @@ namespace Discord.Commands | |||
| else if (command.MinArgs == null && command.MaxArgs != null) | |||
| output.Append($" ≤{command.MaxArgs.ToString()} Args"); | |||
| output.Append($": {command.Description}"); | |||
| output.Append($": {command.Description ?? "No description set for this command."}"); | |||
| return output.ToString(); | |||
| } | |||
| @@ -216,13 +196,14 @@ namespace Discord.Commands | |||
| public CommandBuilder CreateCommand(string cmd) | |||
| { | |||
| var command = new Command(cmd); | |||
| _commands.Add(cmd, command); | |||
| return new CommandBuilder(command); | |||
| _commands.Add(command); | |||
| return new CommandBuilder(null, command, ""); | |||
| } | |||
| internal void AddCommand(Command command) | |||
| { | |||
| _commands.Add(command.Text, command); | |||
| _commands.Add(command); | |||
| _map.AddCommand(command.Text, command); | |||
| } | |||
| } | |||
| } | |||