| @@ -9,14 +9,14 @@ namespace Discord.Commands | |||||
| private readonly CommandMap _parent; | private readonly CommandMap _parent; | ||||
| private readonly string _name, _fullName; | private readonly string _name, _fullName; | ||||
| private Command _command; | |||||
| private readonly List<Command> _commands; | |||||
| private readonly Dictionary<string, CommandMap> _items; | private readonly Dictionary<string, CommandMap> _items; | ||||
| private bool _isHidden; | private bool _isHidden; | ||||
| public string Name => _name; | public string Name => _name; | ||||
| public string FullName => _fullName; | public string FullName => _fullName; | ||||
| public bool IsHidden => _isHidden; | public bool IsHidden => _isHidden; | ||||
| public Command Command => _command; | |||||
| public IEnumerable<Command> Commands => _commands; | |||||
| public IEnumerable<CommandMap> SubGroups => _items.Values; | public IEnumerable<CommandMap> SubGroups => _items.Values; | ||||
| /*public IEnumerable<Command> SubCommands => _items.Select(x => x.Value._command).Where(x => x != null); | /*public IEnumerable<Command> SubCommands => _items.Select(x => x.Value._command).Where(x => x != null); | ||||
| public IEnumerable<CommandMap> SubGroups => _items.Select(x => x.Value).Where(x => x._items.Count > 0);*/ | public IEnumerable<CommandMap> SubGroups => _items.Select(x => x.Value).Where(x => x._items.Count > 0);*/ | ||||
| @@ -27,6 +27,7 @@ namespace Discord.Commands | |||||
| _name = name; | _name = name; | ||||
| _fullName = fullName; | _fullName = fullName; | ||||
| _items = new Dictionary<string, CommandMap>(); | _items = new Dictionary<string, CommandMap>(); | ||||
| _commands = new List<Command>(); | |||||
| _isHidden = true; | _isHidden = true; | ||||
| } | } | ||||
| @@ -48,20 +49,20 @@ namespace Discord.Commands | |||||
| return this; | return this; | ||||
| } | } | ||||
| public Command GetCommand() | |||||
| public IEnumerable<Command> GetCommands() | |||||
| { | { | ||||
| if (_command != null) | |||||
| return _command; | |||||
| if (_commands.Count > 0) | |||||
| return _commands; | |||||
| else if (_parent != null) | else if (_parent != null) | ||||
| return _parent.GetCommand(); | |||||
| return _parent.GetCommands(); | |||||
| else | else | ||||
| return null; | return null; | ||||
| } | } | ||||
| public Command GetCommand(string text) | |||||
| public IEnumerable<Command> GetCommands(string text) | |||||
| { | { | ||||
| return GetCommand(0, text.Split(' ')); | |||||
| return GetCommands(0, text.Split(' ')); | |||||
| } | } | ||||
| public Command GetCommand(int index, string[] parts) | |||||
| public IEnumerable<Command> GetCommands(int index, string[] parts) | |||||
| { | { | ||||
| if (index != parts.Length) | if (index != parts.Length) | ||||
| { | { | ||||
| @@ -69,14 +70,14 @@ namespace Discord.Commands | |||||
| CommandMap nextGroup; | CommandMap nextGroup; | ||||
| if (_items.TryGetValue(nextPart, out nextGroup)) | if (_items.TryGetValue(nextPart, out nextGroup)) | ||||
| { | { | ||||
| var cmd = nextGroup.GetCommand(index + 1, parts); | |||||
| var cmd = nextGroup.GetCommands(index + 1, parts); | |||||
| if (cmd != null) | if (cmd != null) | ||||
| return cmd; | return cmd; | ||||
| } | } | ||||
| } | } | ||||
| if (_command != null) | |||||
| return _command; | |||||
| if (_commands != null) | |||||
| return _commands; | |||||
| return null; | return null; | ||||
| } | } | ||||
| @@ -102,21 +103,26 @@ namespace Discord.Commands | |||||
| nextGroup.AddCommand(index + 1, parts, command); | nextGroup.AddCommand(index + 1, parts, command); | ||||
| } | } | ||||
| else | else | ||||
| { | |||||
| if (_command != null) | |||||
| throw new InvalidOperationException("A command has already been added with this path."); | |||||
| _command = command; | |||||
| } | |||||
| _commands.Add(command); | |||||
| } | } | ||||
| public bool CanRun(User user, Channel channel) | public bool CanRun(User user, Channel channel) | ||||
| { | { | ||||
| if (_command != null && _command.CanRun(user, channel)) | |||||
| return true; | |||||
| foreach (var item in _items) | |||||
| if (_commands.Count > 0) | |||||
| { | |||||
| foreach (var cmd in _commands) | |||||
| { | |||||
| if (cmd.CanRun(user, channel)) | |||||
| return true; | |||||
| } | |||||
| } | |||||
| if (_items.Count > 0) | |||||
| { | { | ||||
| if (item.Value.CanRun(user, channel)) | |||||
| return true; | |||||
| foreach (var item in _items) | |||||
| { | |||||
| if (item.Value.CanRun(user, channel)) | |||||
| return true; | |||||
| } | |||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -12,13 +12,13 @@ namespace Discord.Commands | |||||
| DoubleQuotedParameter | DoubleQuotedParameter | ||||
| } | } | ||||
| public static bool ParseCommand(string input, CommandMap map, out Command command, out int endPos) | |||||
| public static bool ParseCommand(string input, CommandMap map, out IEnumerable<Command> commands, out int endPos) | |||||
| { | { | ||||
| int startPosition = 0; | int startPosition = 0; | ||||
| int endPosition = 0; | int endPosition = 0; | ||||
| int inputLength = input.Length; | int inputLength = input.Length; | ||||
| bool isEscaped = false; | bool isEscaped = false; | ||||
| command = null; | |||||
| commands = null; | |||||
| endPos = 0; | endPos = 0; | ||||
| if (input == "") | if (input == "") | ||||
| @@ -52,8 +52,8 @@ namespace Discord.Commands | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| command = map.GetCommand(); //Work our way backwards to find a command that matches our input | |||||
| return command != null; | |||||
| commands = map.GetCommands(); //Work our way backwards to find a command that matches our input | |||||
| return commands != null; | |||||
| } | } | ||||
| //TODO: Check support for escaping | //TODO: Check support for escaping | ||||
| @@ -78,6 +78,8 @@ namespace Discord.Commands | |||||
| { | { | ||||
| if (startPosition == endPosition && (parameter == null || parameter.Type != ParameterType.Multiple)) //Is first char of a new arg | if (startPosition == endPosition && (parameter == null || parameter.Type != ParameterType.Multiple)) //Is first char of a new arg | ||||
| { | { | ||||
| if (argList.Count >= expectedArgs.Length) | |||||
| return CommandErrorType.BadArgCount; //Too many args | |||||
| parameter = expectedArgs[argList.Count]; | parameter = expectedArgs[argList.Count]; | ||||
| if (parameter.Type == ParameterType.Unparsed) | if (parameter.Type == ParameterType.Unparsed) | ||||
| { | { | ||||
| @@ -144,6 +146,7 @@ namespace Discord.Commands | |||||
| } | } | ||||
| } | } | ||||
| //Too few args | |||||
| for (int i = argList.Count; i < expectedArgs.Length; i++) | for (int i = argList.Count; i < expectedArgs.Length; i++) | ||||
| { | { | ||||
| var param = expectedArgs[i]; | var param = expectedArgs[i]; | ||||
| @@ -158,11 +161,11 @@ namespace Discord.Commands | |||||
| } | } | ||||
| } | } | ||||
| if (argList.Count > expectedArgs.Length) | |||||
| /*if (argList.Count > expectedArgs.Length) | |||||
| { | { | ||||
| if (expectedArgs.Length == 0 || expectedArgs[expectedArgs.Length - 1].Type != ParameterType.Multiple) | if (expectedArgs.Length == 0 || expectedArgs[expectedArgs.Length - 1].Type != ParameterType.Multiple) | ||||
| return CommandErrorType.BadArgCount; | return CommandErrorType.BadArgCount; | ||||
| } | |||||
| }*/ | |||||
| args = argList.ToArray(); | args = argList.ToArray(); | ||||
| return null; | return null; | ||||
| @@ -88,10 +88,10 @@ After: | |||||
| } | } | ||||
| //Parse command | //Parse command | ||||
| Command command; | |||||
| IEnumerable<Command> commands; | |||||
| int argPos; | int argPos; | ||||
| CommandParser.ParseCommand(msg, _map, out command, out argPos); | |||||
| if (command == null) | |||||
| CommandParser.ParseCommand(msg, _map, out commands, out argPos); | |||||
| if (commands == null) | |||||
| { | { | ||||
| CommandEventArgs errorArgs = new CommandEventArgs(e.Message, null, null); | CommandEventArgs errorArgs = new CommandEventArgs(e.Message, null, null); | ||||
| RaiseCommandError(CommandErrorType.UnknownCommand, errorArgs); | RaiseCommandError(CommandErrorType.UnknownCommand, errorArgs); | ||||
| @@ -99,35 +99,46 @@ After: | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| //Parse arguments | |||||
| string[] args; | |||||
| var error = CommandParser.ParseArgs(msg, argPos, command, out args); | |||||
| if (error != null) | |||||
| foreach (var command in commands) | |||||
| { | { | ||||
| var errorArgs = new CommandEventArgs(e.Message, command, null); | |||||
| RaiseCommandError(error.Value, errorArgs); | |||||
| return; | |||||
| } | |||||
| var eventArgs = new CommandEventArgs(e.Message, command, args); | |||||
| //Parse arguments | |||||
| string[] args; | |||||
| var error = CommandParser.ParseArgs(msg, argPos, command, out args); | |||||
| if (error != null) | |||||
| { | |||||
| if (error == CommandErrorType.BadArgCount) | |||||
| continue; | |||||
| else | |||||
| { | |||||
| var errorArgs = new CommandEventArgs(e.Message, command, null); | |||||
| RaiseCommandError(error.Value, errorArgs); | |||||
| return; | |||||
| } | |||||
| } | |||||
| // Check permissions | |||||
| if (!command.CanRun(eventArgs.User, eventArgs.Channel)) | |||||
| { | |||||
| RaiseCommandError(CommandErrorType.BadPermissions, eventArgs); | |||||
| return; | |||||
| } | |||||
| var eventArgs = new CommandEventArgs(e.Message, command, args); | |||||
| // Run the command | |||||
| try | |||||
| { | |||||
| RaiseRanCommand(eventArgs); | |||||
| await command.Run(eventArgs).ConfigureAwait(false); | |||||
| } | |||||
| catch (Exception ex) | |||||
| { | |||||
| RaiseCommandError(CommandErrorType.Exception, eventArgs, ex); | |||||
| // Check permissions | |||||
| if (!command.CanRun(eventArgs.User, eventArgs.Channel)) | |||||
| { | |||||
| RaiseCommandError(CommandErrorType.BadPermissions, eventArgs); | |||||
| return; | |||||
| } | |||||
| // Run the command | |||||
| try | |||||
| { | |||||
| RaiseRanCommand(eventArgs); | |||||
| await command.Run(eventArgs).ConfigureAwait(false); | |||||
| } | |||||
| catch (Exception ex) | |||||
| { | |||||
| RaiseCommandError(CommandErrorType.Exception, eventArgs, ex); | |||||
| } | |||||
| return; | |||||
| } | } | ||||
| var errorArgs2 = new CommandEventArgs(e.Message, null, null); | |||||
| RaiseCommandError(CommandErrorType.BadArgCount, errorArgs2); | |||||
| } | } | ||||
| }; | }; | ||||
| } | } | ||||
| @@ -208,9 +219,19 @@ After: | |||||
| { | { | ||||
| StringBuilder output = new StringBuilder(); | StringBuilder output = new StringBuilder(); | ||||
| Command cmd = map.Command; | |||||
| if (cmd != null) | |||||
| ShowCommandHelpInternal(cmd, user, channel, output); | |||||
| IEnumerable<Command> cmds = map.Commands; | |||||
| bool isFirstCmd = true; | |||||
| if (cmds != null) | |||||
| { | |||||
| foreach (var cmd in cmds) | |||||
| { | |||||
| if (isFirstCmd) | |||||
| isFirstCmd = false; | |||||
| /*else | |||||
| output.AppendLine();*/ | |||||
| ShowCommandHelpInternal(cmd, user, channel, output); | |||||
| } | |||||
| } | |||||
| else | else | ||||
| { | { | ||||
| output.Append('`'); | output.Append('`'); | ||||
| @@ -218,12 +239,12 @@ After: | |||||
| output.Append("`\n"); | output.Append("`\n"); | ||||
| } | } | ||||
| bool isFirst = true; | |||||
| bool isFirstSubCmd = true; | |||||
| foreach (var subCmd in map.SubGroups.Where(x => x.CanRun(user, channel) && !x.IsHidden)) | foreach (var subCmd in map.SubGroups.Where(x => x.CanRun(user, channel) && !x.IsHidden)) | ||||
| { | { | ||||
| if (isFirst) | |||||
| if (isFirstSubCmd) | |||||
| { | { | ||||
| isFirst = false; | |||||
| isFirstSubCmd = false; | |||||
| output.AppendLine("Sub Commands: "); | output.AppendLine("Sub Commands: "); | ||||
| } | } | ||||
| else | else | ||||
| @@ -235,7 +256,7 @@ After: | |||||
| output.Append('`'); | output.Append('`'); | ||||
| } | } | ||||
| if (isFirst) | |||||
| if (isFirstCmd && isFirstSubCmd) //Had no commands and no subcommands | |||||
| { | { | ||||
| output.Clear(); | output.Clear(); | ||||
| output.AppendLine("You do not have permission to access this command."); | output.AppendLine("You do not have permission to access this command."); | ||||
| @@ -246,17 +267,14 @@ After: | |||||
| public Task ShowCommandHelp(Command command, User user, Channel channel, Channel replyChannel = null) | public Task ShowCommandHelp(Command command, User user, Channel channel, Channel replyChannel = null) | ||||
| { | { | ||||
| StringBuilder output = new StringBuilder(); | StringBuilder output = new StringBuilder(); | ||||
| ShowCommandHelpInternal(command, user, channel, output); | |||||
| if (!command.CanRun(user, channel)) | |||||
| output.AppendLine("You do not have permission to access this command."); | |||||
| else | |||||
| ShowCommandHelpInternal(command, user, channel, output); | |||||
| return _client.SendMessage(replyChannel ?? channel, output.ToString()); | return _client.SendMessage(replyChannel ?? channel, output.ToString()); | ||||
| } | } | ||||
| private void ShowCommandHelpInternal(Command command, User user, Channel channel, StringBuilder output) | private void ShowCommandHelpInternal(Command command, User user, Channel channel, StringBuilder output) | ||||
| { | { | ||||
| if (!command.CanRun(user, channel)) | |||||
| { | |||||
| output.AppendLine("You do not have permission to access this command."); | |||||
| return; | |||||
| } | |||||
| output.Append('`'); | output.Append('`'); | ||||
| output.Append(command.Text); | output.Append(command.Text); | ||||
| foreach (var param in command.Parameters) | foreach (var param in command.Parameters) | ||||