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