@@ -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) | |||