| @@ -43,6 +43,9 @@ | |||||
| <Compile Include="..\Discord.Net.Commands\CommandBuilder.cs"> | <Compile Include="..\Discord.Net.Commands\CommandBuilder.cs"> | ||||
| <Link>CommandBuilder.cs</Link> | <Link>CommandBuilder.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net.Commands\CommandMap.cs"> | |||||
| <Link>CommandMap.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net.Commands\CommandParser.cs"> | <Compile Include="..\Discord.Net.Commands\CommandParser.cs"> | ||||
| <Link>CommandParser.cs</Link> | <Link>CommandParser.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| @@ -1,23 +1,102 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| namespace Discord.Commands | 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 sealed class Command | ||||
| { | { | ||||
| public string Text { get; } | 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 int MinPerms { get; internal set; } | ||||
| public bool IsHidden { get; internal set; } | public bool IsHidden { get; internal set; } | ||||
| public string Description { 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) | internal Command(string text) | ||||
| { | { | ||||
| Text = 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; | ||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| { | { | ||||
| public sealed class CommandBuilder | public sealed class CommandBuilder | ||||
| { | { | ||||
| private readonly CommandsPlugin _plugin; | |||||
| private readonly Command _command; | 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; | _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; | 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; | 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; | return this; | ||||
| } | } | ||||
| public CommandBuilder AnyArgs() | |||||
| public CommandBuilder IsHidden() | |||||
| { | { | ||||
| _command.MinArgs = null; | |||||
| _command.MaxArgs = null; | |||||
| _command.IsHidden = true; | |||||
| return this; | return this; | ||||
| } | } | ||||
| @@ -54,32 +59,45 @@ namespace Discord.Commands | |||||
| return this; | 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 | public sealed class CommandGroupBuilder | ||||
| { | { | ||||
| private readonly CommandsPlugin _plugin; | |||||
| internal readonly CommandsPlugin _plugin; | |||||
| private readonly string _prefix; | private readonly string _prefix; | ||||
| private int _defaultMinPermissions; | private int _defaultMinPermissions; | ||||
| @@ -104,25 +122,9 @@ namespace Discord.Commands | |||||
| => CreateCommand(""); | => CreateCommand(""); | ||||
| public CommandBuilder CreateCommand(string cmd) | 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; | 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 | //TODO: Check support for escaping | ||||
| public static class CommandParser | |||||
| internal static class CommandParser | |||||
| { | { | ||||
| private enum CommandParserPart | private enum CommandParserPart | ||||
| { | { | ||||
| None, | None, | ||||
| CommandName, | |||||
| Parameter, | Parameter, | ||||
| QuotedParameter, | QuotedParameter, | ||||
| DoubleQuotedParameter | 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; | int inputLength = input.Length; | ||||
| bool isEscaped = false; | bool isEscaped = false; | ||||
| List<CommandPart> argList = new List<CommandPart>(); | List<CommandPart> argList = new List<CommandPart>(); | ||||
| command = null; | |||||
| args = null; | args = null; | ||||
| if (input == "") | if (input == "") | ||||
| @@ -61,21 +93,6 @@ namespace Discord.Commands | |||||
| switch (currentPart) | 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: | case CommandParserPart.None: | ||||
| if ((!isEscaped && currentChar == '\"')) | if ((!isEscaped && currentChar == '\"')) | ||||
| { | { | ||||
| @@ -126,9 +143,6 @@ namespace Discord.Commands | |||||
| } | } | ||||
| } | } | ||||
| if (parseCommand && (command == null || command == "")) | |||||
| return false; | |||||
| args = argList.ToArray(); | args = argList.ToArray(); | ||||
| return true; | 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 class CommandErrorEventArgs : CommandEventArgs | ||||
| { | { | ||||
| public CommandErrorType ErrorType { get; } | public CommandErrorType ErrorType { get; } | ||||
| @@ -10,29 +10,30 @@ namespace Discord.Commands | |||||
| public partial class CommandsPlugin | public partial class CommandsPlugin | ||||
| { | { | ||||
| private readonly DiscordClient _client; | 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 RequireCommandCharInPublic { get; set; } | ||||
| public bool RequireCommandCharInPrivate { get; set; } | public bool RequireCommandCharInPrivate { get; set; } | ||||
| public bool HelpInPublic { get; set; } | public bool HelpInPublic { get; set; } | ||||
| public CommandsPlugin(DiscordClient client, Func<User, int> getPermissions = null, bool builtInHelp = false) | public CommandsPlugin(DiscordClient client, Func<User, int> getPermissions = null, bool builtInHelp = false) | ||||
| { | { | ||||
| _client = client; // Wait why is this even set | |||||
| _client = client; | |||||
| _getPermissions = getPermissions; | _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; | RequireCommandCharInPublic = true; | ||||
| RequireCommandCharInPrivate = true; | RequireCommandCharInPrivate = true; | ||||
| HelpInPublic = true; | HelpInPublic = true; | ||||
| @@ -40,9 +41,9 @@ namespace Discord.Commands | |||||
| if (builtInHelp) | if (builtInHelp) | ||||
| { | { | ||||
| CreateCommand("help") | CreateCommand("help") | ||||
| .ArgsBetween(0, 1) | |||||
| .Parameter("command", isOptional: true) | |||||
| .IsHidden() | .IsHidden() | ||||
| .Desc("Returns information about commands.") | |||||
| .Info("Returns information about commands.") | |||||
| .Do(async e => | .Do(async e => | ||||
| { | { | ||||
| if (e.Command.Text != "help") | if (e.Command.Text != "help") | ||||
| @@ -50,29 +51,18 @@ namespace Discord.Commands | |||||
| else | else | ||||
| { | { | ||||
| if (e.Args == null) | 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.AppendLine("These are the commands you can use:"); | ||||
| output.Append("`"); | 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("`"); | 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 | 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."); | output.AppendLine("`help <command>` can tell you more about how to use a command."); | ||||
| @@ -80,8 +70,9 @@ namespace Discord.Commands | |||||
| } | } | ||||
| else | 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 | else | ||||
| await Reply(e, $"`{e.Args[0]}` is not a valid command."); | await Reply(e, $"`{e.Args[0]}` is not a valid command."); | ||||
| } | } | ||||
| @@ -99,7 +90,7 @@ namespace Discord.Commands | |||||
| string msg = e.Message.Text; | string msg = e.Message.Text; | ||||
| if (msg.Length == 0) return; | if (msg.Length == 0) return; | ||||
| if (UseCommandChar) | |||||
| if (_commandChars.Length > 0) | |||||
| { | { | ||||
| bool isPrivate = e.Message.Channel.IsPrivate; | bool isPrivate = e.Message.Channel.IsPrivate; | ||||
| bool hasCommandChar = CommandChars.Contains(msg[0]); | bool hasCommandChar = CommandChars.Contains(msg[0]); | ||||
| @@ -112,44 +103,41 @@ namespace Discord.Commands | |||||
| return; // Same, but public. | 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; | 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; | 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 | // Check permissions | ||||
| if (userPermissions < comm.MinPerms) | |||||
| if (userPermissions < command.MinPerms) | |||||
| { | { | ||||
| RaiseCommandError(CommandErrorType.BadPermissions, eventArgs); | RaiseCommandError(CommandErrorType.BadPermissions, eventArgs); | ||||
| return; | return; | ||||
| } | } | ||||
| //Check arg count | //Check arg count | ||||
| if (argCount < comm.MinArgs) | |||||
| if (argCount < command.MinArgs) | |||||
| { | { | ||||
| RaiseCommandError(CommandErrorType.BadArgCount, eventArgs); | RaiseCommandError(CommandErrorType.BadArgCount, eventArgs); | ||||
| return; | return; | ||||
| @@ -159,21 +147,13 @@ namespace Discord.Commands | |||||
| try | try | ||||
| { | { | ||||
| RaiseRanCommand(eventArgs); | 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) | else if (command.MinArgs == null && command.MaxArgs != null) | ||||
| output.Append($" ≤{command.MaxArgs.ToString()} Args"); | output.Append($" ≤{command.MaxArgs.ToString()} Args"); | ||||
| output.Append($": {command.Description}"); | |||||
| output.Append($": {command.Description ?? "No description set for this command."}"); | |||||
| return output.ToString(); | return output.ToString(); | ||||
| } | } | ||||
| @@ -216,13 +196,14 @@ namespace Discord.Commands | |||||
| public CommandBuilder CreateCommand(string cmd) | public CommandBuilder CreateCommand(string cmd) | ||||
| { | { | ||||
| var command = new Command(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) | internal void AddCommand(Command command) | ||||
| { | { | ||||
| _commands.Add(command.Text, command); | |||||
| _commands.Add(command); | |||||
| _map.AddCommand(command.Text, command); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||