Revamped CommandsPlugin
This uses a dictionary for the commands list, if a command has a max args set it'll only get that amount, will call the UnkownCommand event, and now has a built in help command that can be optionally enabled. CommandChar is now a list, but a single character can still be used. Externally, not much should have changed, but commands can be hidden from the help command and a description can be set. There's probably more that I've forgotten about.
This commit is contained in:
@@ -30,6 +30,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Modules", "src\
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Net.Modules", "src\Discord.Net.Modules.Net45\Discord.Net.Modules.csproj", "{3091164F-66AE-4543-A63D-167C1116241D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestingProject", "TestingProject\TestingProject.csproj", "{6CD8116D-6749-4174-81EB-C4EB4B1F185B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -79,6 +81,12 @@ Global
|
||||
{3091164F-66AE-4543-A63D-167C1116241D}.FullDebug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3091164F-66AE-4543-A63D-167C1116241D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3091164F-66AE-4543-A63D-167C1116241D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6CD8116D-6749-4174-81EB-C4EB4B1F185B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6CD8116D-6749-4174-81EB-C4EB4B1F185B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6CD8116D-6749-4174-81EB-C4EB4B1F185B}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6CD8116D-6749-4174-81EB-C4EB4B1F185B}.FullDebug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6CD8116D-6749-4174-81EB-C4EB4B1F185B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6CD8116D-6749-4174-81EB-C4EB4B1F185B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -93,5 +101,6 @@ Global
|
||||
{1B5603B4-6F8F-4289-B945-7BAAE523D740} = {DF03D4E8-38F6-4FE1-BC52-E38124BE8AFD}
|
||||
{01584E8A-78DA-486F-9EF9-A894E435841B} = {EA68EBE2-51C8-4440-9EF7-D633C90A5D35}
|
||||
{3091164F-66AE-4543-A63D-167C1116241D} = {DF03D4E8-38F6-4FE1-BC52-E38124BE8AFD}
|
||||
{6CD8116D-6749-4174-81EB-C4EB4B1F185B} = {6317A2E6-8E36-4C3E-949B-3F10EC888AB9}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -9,13 +9,15 @@ namespace Discord.Commands
|
||||
public int? MinArgs { get; internal set; }
|
||||
public int? MaxArgs { get; internal set; }
|
||||
public int MinPerms { get; internal set; }
|
||||
internal readonly string[] Parts;
|
||||
public bool IsHidden { get; internal set; }
|
||||
public string Description { get; internal set; }
|
||||
internal Func<CommandEventArgs, Task> Handler;
|
||||
|
||||
internal Command(string text)
|
||||
{
|
||||
Text = text;
|
||||
Parts = text.ToLowerInvariant().Split(' ');
|
||||
IsHidden = false; // Set false by default to avoid null error
|
||||
Description = "No description set for this command.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,18 @@ namespace Discord.Commands
|
||||
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)
|
||||
{
|
||||
_command.Handler = func;
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
namespace Discord.Commands
|
||||
{
|
||||
public class PermissionException : Exception { public PermissionException() : base("User does not have permission to run this command.") { } }
|
||||
public class ArgumentException : Exception { public ArgumentException() : base("This command requires more arguments.") { } }
|
||||
public class CommandEventArgs
|
||||
{
|
||||
public Message Message { get; }
|
||||
public Command Command { get; }
|
||||
public string MessageText { get; }
|
||||
public string CommandText { get; }
|
||||
public string ArgText { get; }
|
||||
public int? Permissions { get; }
|
||||
@@ -16,10 +18,11 @@ namespace Discord.Commands
|
||||
public Channel Channel => Message.Channel;
|
||||
public Server Server => Message.Channel.Server;
|
||||
|
||||
public CommandEventArgs(Message message, Command command, string commandText, string argText, int? permissions, string[] args)
|
||||
public CommandEventArgs(Message message, Command command, string messageText, string commandText, string argText, int? permissions, string[] args)
|
||||
{
|
||||
Message = message;
|
||||
Command = command;
|
||||
MessageText = messageText;
|
||||
CommandText = commandText;
|
||||
ArgText = argText;
|
||||
Permissions = permissions;
|
||||
@@ -31,7 +34,7 @@ namespace Discord.Commands
|
||||
public Exception Exception { get; }
|
||||
|
||||
public CommandErrorEventArgs(CommandEventArgs baseArgs, Exception ex)
|
||||
: base(baseArgs.Message, baseArgs.Command, baseArgs.CommandText, baseArgs.ArgText, baseArgs.Permissions, baseArgs.Args)
|
||||
: base(baseArgs.Message, baseArgs.Command, baseArgs.MessageText, baseArgs.CommandText, baseArgs.ArgText, baseArgs.Permissions, baseArgs.Args)
|
||||
{
|
||||
Exception = ex;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
@@ -7,132 +10,234 @@ namespace Discord.Commands
|
||||
public partial class CommandsPlugin
|
||||
{
|
||||
private readonly DiscordClient _client;
|
||||
private List<Command> _commands;
|
||||
private Func<User, int> _getPermissions;
|
||||
|
||||
public IEnumerable<Command> Commands => _commands;
|
||||
private Dictionary<string, Command> _commands;
|
||||
|
||||
public Dictionary<string, Command> Commands => _commands;
|
||||
|
||||
public char CommandChar { get; set; }
|
||||
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 bool RequireCommandCharInPublic { get; set; }
|
||||
public bool RequireCommandCharInPrivate { get; set; }
|
||||
public bool HelpInPublic { get; set; }
|
||||
|
||||
public CommandsPlugin(DiscordClient client, Func<User, int> getPermissions = null)
|
||||
{
|
||||
_client = client;
|
||||
_getPermissions = getPermissions;
|
||||
_commands = new List<Command>();
|
||||
public CommandsPlugin(DiscordClient client, Func<User, int> getPermissions = null, bool builtInHelp = false)
|
||||
{
|
||||
_client = client; // Wait why is this even set
|
||||
_getPermissions = getPermissions;
|
||||
|
||||
_commands = new Dictionary<string, Command>();
|
||||
|
||||
CommandChar = '/';
|
||||
UseCommandChar = false;
|
||||
RequireCommandCharInPublic = true;
|
||||
RequireCommandCharInPrivate = true;
|
||||
CommandChar = '!'; // Kept around to keep from possibly throwing an error. Might not be necessary.
|
||||
CommandChars = new List<char> { '!', '?', '/' };
|
||||
UseCommandChar = true;
|
||||
RequireCommandCharInPublic = true;
|
||||
RequireCommandCharInPrivate = true;
|
||||
HelpInPublic = true;
|
||||
|
||||
client.MessageReceived += async (s, e) =>
|
||||
{
|
||||
//If commands aren't being used, don't bother processing them
|
||||
if (_commands.Count == 0)
|
||||
return;
|
||||
if (builtInHelp)
|
||||
{
|
||||
CreateCommand("help")
|
||||
.ArgsBetween(0, 1)
|
||||
.IsHidden()
|
||||
.Desc("Returns information about commands.")
|
||||
.Do(async e =>
|
||||
{
|
||||
if (e.Command.Text != "help")
|
||||
{
|
||||
await Reply(e, CommandDetails(e.Command));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (e.Args == null)
|
||||
{
|
||||
StringBuilder output = new StringBuilder();
|
||||
bool first = true;
|
||||
output.AppendLine("These are the commands you can use:");
|
||||
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("`");
|
||||
|
||||
//Ignore messages from ourselves
|
||||
if (e.Message.User == client.CurrentUser)
|
||||
return;
|
||||
if (CommandChars.Count == 1)
|
||||
output.AppendLine($"{Environment.NewLine}You can use `{CommandChars[0]}` to call a command.");
|
||||
else
|
||||
output.AppendLine($"{Environment.NewLine}You can use `{String.Join(" ", CommandChars.Take(CommandChars.Count - 1))}` and `{CommandChars.Last()}` to call a command.");
|
||||
|
||||
//Check for the command character
|
||||
string msg = e.Message.Text;
|
||||
if (UseCommandChar)
|
||||
{
|
||||
if (msg.Length == 0)
|
||||
return;
|
||||
bool isPrivate = e.Message.Channel.IsPrivate;
|
||||
bool hasCommandChar = msg[0] == CommandChar;
|
||||
if (hasCommandChar)
|
||||
msg = msg.Substring(1);
|
||||
if (!isPrivate && RequireCommandCharInPublic && !hasCommandChar)
|
||||
return;
|
||||
if (isPrivate && RequireCommandCharInPrivate && !hasCommandChar)
|
||||
return;
|
||||
}
|
||||
output.AppendLine("`help <command>` can tell you more about how to use a command.");
|
||||
|
||||
CommandPart[] args;
|
||||
if (!CommandParser.ParseArgs(msg, out args))
|
||||
return;
|
||||
await Reply(e, output.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_commands.ContainsKey(e.Args[0]))
|
||||
await Reply(e, CommandDetails(_commands[e.Args[0]]));
|
||||
else
|
||||
await Reply(e, $"`{e.Args[0]}` is not a valid command.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (int i = 0; i < _commands.Count; i++)
|
||||
{
|
||||
Command cmd = _commands[i];
|
||||
}
|
||||
|
||||
//Check Command Parts
|
||||
if (args.Length < cmd.Parts.Length)
|
||||
continue;
|
||||
client.MessageReceived += async (s, e) =>
|
||||
{
|
||||
// This will need to be changed once a built in help command is made
|
||||
if (_commands.Count == 0)
|
||||
return;
|
||||
|
||||
bool isValid = true;
|
||||
for (int j = 0; j < cmd.Parts.Length; j++)
|
||||
{
|
||||
if (!string.Equals(args[j].Value, cmd.Parts[j], StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isValid)
|
||||
continue;
|
||||
if (e.Message.IsAuthor)
|
||||
return;
|
||||
|
||||
//Check Arg Count
|
||||
int argCount = args.Length - cmd.Parts.Length;
|
||||
if (argCount < cmd.MinArgs || argCount > cmd.MaxArgs)
|
||||
continue;
|
||||
string msg = e.Message.Text;
|
||||
|
||||
//Clean Args
|
||||
string[] newArgs = new string[argCount];
|
||||
for (int j = 0; j < newArgs.Length; j++)
|
||||
newArgs[j] = args[j + cmd.Parts.Length].Value;
|
||||
if (msg.Length == 0)
|
||||
return;
|
||||
|
||||
//Get ArgText
|
||||
string argText;
|
||||
if (argCount == 0)
|
||||
argText = "";
|
||||
else
|
||||
argText = msg.Substring(args[cmd.Parts.Length].Index);
|
||||
if (UseCommandChar)
|
||||
{
|
||||
bool isPrivate = e.Message.Channel.IsPrivate;
|
||||
bool hasCommandChar = CommandChars.Contains(msg[0]);
|
||||
if (hasCommandChar)
|
||||
msg = msg.Substring(1);
|
||||
|
||||
//Check Permissions
|
||||
int permissions = _getPermissions != null ? _getPermissions(e.Message.User) : 0;
|
||||
var eventArgs = new CommandEventArgs(e.Message, cmd, msg, argText, permissions, newArgs);
|
||||
if (permissions < cmd.MinPerms)
|
||||
{
|
||||
RaiseCommandError(eventArgs, new PermissionException());
|
||||
return;
|
||||
}
|
||||
if (isPrivate && RequireCommandCharInPrivate && !hasCommandChar)
|
||||
return; // If private, and command char is required, and it doesn't have it, ignore it.
|
||||
if (!isPrivate && RequireCommandCharInPublic && !hasCommandChar)
|
||||
return; // Same, but public.
|
||||
}
|
||||
|
||||
//Run Command
|
||||
string cmd;
|
||||
CommandPart[] args;
|
||||
if (!CommandParser.Parse(msg, out cmd, out args))
|
||||
return;
|
||||
|
||||
if (_commands.ContainsKey(cmd))
|
||||
{
|
||||
Command comm = _commands[cmd];
|
||||
|
||||
//Get ArgText
|
||||
int argCount = args.Length;
|
||||
string argText;
|
||||
if (argCount == 0)
|
||||
argText = "";
|
||||
else
|
||||
argText = msg.Substring(args[0].Index);
|
||||
|
||||
//Clean Args
|
||||
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;
|
||||
}
|
||||
|
||||
// Check permissions here
|
||||
int permissions = _getPermissions != null ? _getPermissions(e.Message.User) : 0;
|
||||
var eventArgs = new CommandEventArgs(e.Message, comm, msg, cmd, argText, permissions, newArgs);
|
||||
if (permissions < comm.MinPerms)
|
||||
{
|
||||
RaiseCommandError(eventArgs, new PermissionException());
|
||||
return;
|
||||
}
|
||||
|
||||
//Check Arg Count
|
||||
if (argCount < comm.MinArgs)
|
||||
{
|
||||
RaiseCommandError(eventArgs, new ArgumentException());
|
||||
if (builtInHelp)
|
||||
await _commands["help"].Handler(eventArgs);
|
||||
return;
|
||||
}
|
||||
|
||||
// Actually run the command here
|
||||
RaiseRanCommand(eventArgs);
|
||||
try
|
||||
{
|
||||
var task = cmd.Handler(eventArgs);
|
||||
if (task != null)
|
||||
await task.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RaiseCommandError(eventArgs, ex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
try
|
||||
{
|
||||
var task = comm.Handler(eventArgs);
|
||||
if (task != null)
|
||||
await task.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RaiseCommandError(eventArgs, ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CommandEventArgs eventArgs = new CommandEventArgs(e.Message, null, msg, cmd, null, null, null);
|
||||
RaiseUnknownCommand(eventArgs);
|
||||
if (builtInHelp)
|
||||
await Reply(eventArgs, $"Command `cmd` does not exist.");
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
internal string CommandDetails(Command comm)
|
||||
{
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
output.Append($"`{comm.Text}`");
|
||||
|
||||
if (comm.MinArgs != null && comm.MaxArgs != null)
|
||||
{
|
||||
if (comm.MinArgs == comm.MaxArgs)
|
||||
{
|
||||
if (comm.MaxArgs != 0)
|
||||
output.Append($" {comm.MinArgs.ToString()} Args");
|
||||
}
|
||||
else
|
||||
output.Append($" {comm.MinArgs.ToString()} - {comm.MaxArgs.ToString()} Args");
|
||||
}
|
||||
else if (comm.MinArgs != null && comm.MaxArgs == null)
|
||||
{
|
||||
output.Append($" ≥{comm.MinArgs.ToString()} Args");
|
||||
}
|
||||
else if (comm.MinArgs == null && comm.MaxArgs != null)
|
||||
{
|
||||
output.Append($" ≤{comm.MaxArgs.ToString()} Args");
|
||||
}
|
||||
|
||||
output.Append($": {comm.Description}");
|
||||
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
internal async Task Reply(CommandEventArgs e, string message)
|
||||
{
|
||||
if (HelpInPublic)
|
||||
await _client.SendMessage(e.Channel, message);
|
||||
else
|
||||
await _client.SendPrivateMessage(e.User, message);
|
||||
}
|
||||
|
||||
public void CreateCommandGroup(string cmd, Action<CommandGroupBuilder> config = null)
|
||||
=> config(new CommandGroupBuilder(this, cmd, 0));
|
||||
public CommandBuilder CreateCommand(string cmd)
|
||||
{
|
||||
var command = new Command(cmd);
|
||||
_commands.Add(command);
|
||||
_commands.Add(cmd, command);
|
||||
return new CommandBuilder(command);
|
||||
}
|
||||
|
||||
internal void AddCommand(Command command)
|
||||
{
|
||||
_commands.Add(command);
|
||||
_commands.Add(command.Text, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user