| @@ -9,14 +9,14 @@ namespace Discord.Commands | |||
| private readonly CommandMap _parent; | |||
| private readonly string _name, _fullName; | |||
| private Command _command; | |||
| private readonly List<Command> _commands; | |||
| private readonly Dictionary<string, CommandMap> _items; | |||
| private bool _isHidden; | |||
| public string Name => _name; | |||
| public string FullName => _fullName; | |||
| public bool IsHidden => _isHidden; | |||
| public Command Command => _command; | |||
| public IEnumerable<Command> Commands => _commands; | |||
| public IEnumerable<CommandMap> SubGroups => _items.Values; | |||
| /*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);*/ | |||
| @@ -27,6 +27,7 @@ namespace Discord.Commands | |||
| _name = name; | |||
| _fullName = fullName; | |||
| _items = new Dictionary<string, CommandMap>(); | |||
| _commands = new List<Command>(); | |||
| _isHidden = true; | |||
| } | |||
| @@ -48,20 +49,20 @@ namespace Discord.Commands | |||
| return this; | |||
| } | |||
| public Command GetCommand() | |||
| public IEnumerable<Command> GetCommands() | |||
| { | |||
| if (_command != null) | |||
| return _command; | |||
| if (_commands.Count > 0) | |||
| return _commands; | |||
| else if (_parent != null) | |||
| return _parent.GetCommand(); | |||
| return _parent.GetCommands(); | |||
| else | |||
| 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) | |||
| { | |||
| @@ -69,14 +70,14 @@ namespace Discord.Commands | |||
| CommandMap nextGroup; | |||
| if (_items.TryGetValue(nextPart, out nextGroup)) | |||
| { | |||
| var cmd = nextGroup.GetCommand(index + 1, parts); | |||
| var cmd = nextGroup.GetCommands(index + 1, parts); | |||
| if (cmd != null) | |||
| return cmd; | |||
| } | |||
| } | |||
| if (_command != null) | |||
| return _command; | |||
| if (_commands != null) | |||
| return _commands; | |||
| return null; | |||
| } | |||
| @@ -102,21 +103,26 @@ namespace Discord.Commands | |||
| nextGroup.AddCommand(index + 1, parts, command); | |||
| } | |||
| 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) | |||
| { | |||
| 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; | |||
| } | |||
| @@ -12,13 +12,13 @@ namespace Discord.Commands | |||
| 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 endPosition = 0; | |||
| int inputLength = input.Length; | |||
| bool isEscaped = false; | |||
| command = null; | |||
| commands = null; | |||
| endPos = 0; | |||
| 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 | |||
| @@ -78,6 +78,8 @@ namespace Discord.Commands | |||
| { | |||
| 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]; | |||
| if (parameter.Type == ParameterType.Unparsed) | |||
| { | |||
| @@ -144,6 +146,7 @@ namespace Discord.Commands | |||
| } | |||
| } | |||
| //Too few args | |||
| for (int i = argList.Count; i < expectedArgs.Length; 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) | |||
| return CommandErrorType.BadArgCount; | |||
| } | |||
| }*/ | |||
| args = argList.ToArray(); | |||
| return null; | |||
| @@ -88,10 +88,10 @@ After: | |||
| } | |||
| //Parse command | |||
| Command command; | |||
| IEnumerable<Command> commands; | |||
| 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); | |||
| RaiseCommandError(CommandErrorType.UnknownCommand, errorArgs); | |||
| @@ -99,35 +99,46 @@ After: | |||
| } | |||
| 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(); | |||
| 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 | |||
| { | |||
| output.Append('`'); | |||
| @@ -218,12 +239,12 @@ After: | |||
| output.Append("`\n"); | |||
| } | |||
| bool isFirst = true; | |||
| bool isFirstSubCmd = true; | |||
| 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: "); | |||
| } | |||
| else | |||
| @@ -235,7 +256,7 @@ After: | |||
| output.Append('`'); | |||
| } | |||
| if (isFirst) | |||
| if (isFirstCmd && isFirstSubCmd) //Had no commands and no subcommands | |||
| { | |||
| output.Clear(); | |||
| 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) | |||
| { | |||
| 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()); | |||
| } | |||
| 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(command.Text); | |||
| foreach (var param in command.Parameters) | |||