Browse Source

Merge c6f871235f into 50d8d76e73

pull/7/merge
Googie2149 9 years ago
parent
commit
e9ead8fbcb
5 changed files with 273 additions and 111 deletions
  1. +9
    -0
      Discord.Net.sln
  2. +4
    -2
      src/Discord.Net.Commands/Command.cs
  3. +12
    -0
      src/Discord.Net.Commands/CommandBuilder.cs
  4. +5
    -2
      src/Discord.Net.Commands/CommandsPlugin.Events.cs
  5. +243
    -107
      src/Discord.Net.Commands/CommandsPlugin.cs

+ 9
- 0
Discord.Net.sln View File

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

+ 4
- 2
src/Discord.Net.Commands/Command.cs View File

@@ -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.";
}
}
}

+ 12
- 0
src/Discord.Net.Commands/CommandBuilder.cs View File

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


+ 5
- 2
src/Discord.Net.Commands/CommandsPlugin.Events.cs View File

@@ -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;
}


+ 243
- 107
src/Discord.Net.Commands/CommandsPlugin.cs View File

@@ -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,265 @@ 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>();

CommandChar = '/';
UseCommandChar = false;
RequireCommandCharInPublic = true;
RequireCommandCharInPrivate = true;

client.MessageReceived += async (s, e) =>
{
//If commands aren't being used, don't bother processing them
if (_commands.Count == 0)
return;

//Ignore messages from ourselves
if (e.Message.User == client.CurrentUser)
return;

//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;
}

CommandPart[] args;
if (!CommandParser.ParseArgs(msg, out args))
return;

for (int i = 0; i < _commands.Count; i++)
{
Command cmd = _commands[i];

//Check Command Parts
if (args.Length < cmd.Parts.Length)
continue;

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;

//Check Arg Count
int argCount = args.Length - cmd.Parts.Length;
if (argCount < cmd.MinArgs || argCount > cmd.MaxArgs)
continue;

//Clean Args
string[] newArgs = new string[argCount];
for (int j = 0; j < newArgs.Length; j++)
newArgs[j] = args[j + cmd.Parts.Length].Value;

//Get ArgText
string argText;
if (argCount == 0)
argText = "";
else
argText = msg.Substring(args[cmd.Parts.Length].Index);

//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;
}

//Run 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 = '!'; // 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;

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("`");

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.");

output.AppendLine("`help <command>` can tell you more about how to use a command.");

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.");
}
}
});

}

client.MessageReceived += async (s, e) =>
{
// This will need to be changed once a built in help command is made
if (_commands.Count == 0)
return;

if (e.Message.IsAuthor)
return;

string msg = e.Message.Text;

if (msg.Length == 0)
return;

if (UseCommandChar)
{
bool isPrivate = e.Message.Channel.IsPrivate;
bool hasCommandChar = CommandChars.Contains(msg[0]);
if (hasCommandChar)
msg = msg.Substring(1);

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.
}

string cmd;
CommandPart[] args;
if (!CommandParser.Parse(msg, out cmd, out args))
return;

while (!_commands.ContainsKey(cmd))
{
if (args.Length != 0)
{
cmd = $"{cmd} {args[0].Value}";
CommandPart[] tempArgs = new CommandPart[args.Length - 1];
if (args.Length - 1 > 0)
{
for (int i = 0; i < args.Length - 1; i++)
{
tempArgs[i] = args[i + 1];
}
}
args = tempArgs;
}
else
break;
}

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;
}
else if (comm.MaxArgs == null && comm.MinArgs == null)
{
newArgs = new string[argCount];
for (int j = 0; j < newArgs.Length; j++)
newArgs[j] = args[j].Value;
}
else if (comm.MaxArgs == null && comm.MinArgs != null)
{
newArgs = new string[argCount];
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, $"The 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);
}
}
}

Loading…
Cancel
Save