diff --git a/src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj b/src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj
index 1de1757f1..3a77ea6ed 100644
--- a/src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj
+++ b/src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj
@@ -7,7 +7,7 @@
{1B5603B4-6F8F-4289-B945-7BAAE523D740}
Library
Properties
- Discord
+ Discord.Commands
Discord.Net.Commands
512
v4.5
@@ -43,17 +43,23 @@
CommandBuilder.cs
+
+ CommandExtensions.cs
+
CommandMap.cs
CommandParser.cs
-
- CommandsPlugin.cs
+
+ CommandService.cs
+
+
+ CommandService.Events.cs
-
- CommandsPlugin.Events.cs
+
+ CommandServiceConfig.cs
diff --git a/src/Discord.Net.Commands/CommandBuilder.cs b/src/Discord.Net.Commands/CommandBuilder.cs
index dd8035dc7..99a8ae97c 100644
--- a/src/Discord.Net.Commands/CommandBuilder.cs
+++ b/src/Discord.Net.Commands/CommandBuilder.cs
@@ -7,15 +7,15 @@ namespace Discord.Commands
{
public sealed class CommandBuilder
{
- private readonly CommandsPlugin _plugin;
+ private readonly CommandService _service;
private readonly Command _command;
private List _params;
private bool _allowRequired, _isClosed;
private string _prefix;
- public CommandBuilder(CommandsPlugin plugin, Command command, string prefix)
+ public CommandBuilder(CommandService service, Command command, string prefix)
{
- _plugin = plugin;
+ _service = service;
_command = command;
_params = new List();
_prefix = prefix;
@@ -75,8 +75,8 @@ namespace Discord.Commands
{
_command.SetParameters(_params.ToArray());
foreach (var alias in _command.Aliases)
- _plugin.Map.AddCommand(alias, _command);
- _plugin.AddCommand(_command);
+ _service.Map.AddCommand(alias, _command);
+ _service.AddCommand(_command);
}
internal static string AppendPrefix(string prefix, string cmd)
@@ -99,13 +99,13 @@ namespace Discord.Commands
}
public sealed class CommandGroupBuilder
{
- internal readonly CommandsPlugin _plugin;
+ internal readonly CommandService _service;
private readonly string _prefix;
private int _defaultMinPermissions;
- internal CommandGroupBuilder(CommandsPlugin plugin, string prefix, int defaultMinPermissions)
+ internal CommandGroupBuilder(CommandService service, string prefix, int defaultMinPermissions)
{
- _plugin = plugin;
+ _service = service;
_prefix = prefix;
_defaultMinPermissions = defaultMinPermissions;
}
@@ -117,7 +117,7 @@ namespace Discord.Commands
public CommandGroupBuilder CreateCommandGroup(string cmd, Action config = null)
{
- config(new CommandGroupBuilder(_plugin, _prefix + ' ' + cmd, _defaultMinPermissions));
+ config(new CommandGroupBuilder(_service, _prefix + ' ' + cmd, _defaultMinPermissions));
return this;
}
public CommandBuilder CreateCommand()
@@ -126,7 +126,7 @@ namespace Discord.Commands
{
var command = new Command(CommandBuilder.AppendPrefix(_prefix, cmd));
command.MinPermissions = _defaultMinPermissions;
- return new CommandBuilder(_plugin, command, _prefix);
+ return new CommandBuilder(_service, command, _prefix);
}
}
}
diff --git a/src/Discord.Net.Commands/CommandExtensions.cs b/src/Discord.Net.Commands/CommandExtensions.cs
new file mode 100644
index 000000000..e6d7a0ee3
--- /dev/null
+++ b/src/Discord.Net.Commands/CommandExtensions.cs
@@ -0,0 +1,8 @@
+namespace Discord.Commands
+{
+ public static class CommandExtensions
+ {
+ public static CommandService Commands(this DiscordClient client)
+ => client.GetService();
+ }
+}
diff --git a/src/Discord.Net.Commands/CommandsPlugin.Events.cs b/src/Discord.Net.Commands/CommandService.Events.cs
similarity index 97%
rename from src/Discord.Net.Commands/CommandsPlugin.Events.cs
rename to src/Discord.Net.Commands/CommandService.Events.cs
index da31fa6f7..92a3d429c 100644
--- a/src/Discord.Net.Commands/CommandsPlugin.Events.cs
+++ b/src/Discord.Net.Commands/CommandService.Events.cs
@@ -36,7 +36,7 @@ namespace Discord.Commands
}
}
- public partial class CommandsPlugin
+ public partial class CommandService
{
public event EventHandler RanCommand;
private void RaiseRanCommand(CommandEventArgs args)
diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs
new file mode 100644
index 000000000..efc84d7b2
--- /dev/null
+++ b/src/Discord.Net.Commands/CommandService.cs
@@ -0,0 +1,184 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord.Commands
+{
+ /// A Discord.Net client with extensions for handling common bot operations like text commands.
+ public partial class CommandService : IService
+ {
+ private DiscordClient _client;
+
+ CommandServiceConfig Config { get; }
+ public IEnumerable Commands => _commands;
+ private readonly List _commands;
+
+ internal CommandMap Map => _map;
+ private readonly CommandMap _map;
+
+ public CommandService(CommandServiceConfig config)
+ {
+ Config = config;
+ _commands = new List();
+ _map = new CommandMap(null);
+ }
+
+ void IService.Install(DiscordClient client)
+ {
+ _client = client;
+ Config.Lock();
+
+ if (Config.HelpMode != HelpMode.Disable)
+ {
+ CreateCommand("help")
+ .Parameter("command", ParameterType.Multiple)
+ .Hide()
+ .Info("Returns information about commands.")
+ .Do(async e =>
+ {
+ Channel channel = Config.HelpMode == HelpMode.Public ? e.Channel : await client.CreatePMChannel(e.User);
+ if (e.Args.Length > 0) //Show command help
+ {
+ var cmd = _map.GetCommand(string.Join(" ", e.Args));
+ if (cmd != null)
+ await ShowHelp(cmd, e.User, channel);
+ else
+ await client.SendMessage(channel, "Unable to display help: unknown command.");
+ }
+ else //Show general help
+ await ShowHelp(e.User, channel);
+ });
+ }
+
+ client.MessageReceived += async (s, e) =>
+ {
+ if (_commands.Count == 0) return;
+ if (e.Message.IsAuthor) return;
+
+ string msg = e.Message.Text;
+ if (msg.Length == 0) return;
+
+ //Check for command char if one is provided
+ var chars = Config.CommandChars;
+ if (chars.Length > 0)
+ {
+ if (!chars.Contains(msg[0]))
+ return;
+ msg = msg.Substring(1);
+ }
+
+ //Parse command
+ Command command;
+ int argPos;
+ CommandParser.ParseCommand(msg, _map, out command, out argPos);
+ if (command == null)
+ {
+ CommandEventArgs errorArgs = new CommandEventArgs(e.Message, null, null, null);
+ RaiseCommandError(CommandErrorType.UnknownCommand, errorArgs);
+ return;
+ }
+ else
+ {
+ int userPermissions = Config.PermissionResolver?.Invoke(e.Message.User) ?? 0;
+
+ //Parse arguments
+ string[] args;
+ var error = CommandParser.ParseArgs(msg, argPos, command, out args);
+ if (error != null)
+ {
+ var errorArgs = new CommandEventArgs(e.Message, command, userPermissions, null);
+ RaiseCommandError(error.Value, errorArgs);
+ return;
+ }
+
+ var eventArgs = new CommandEventArgs(e.Message, command, userPermissions, args);
+
+ // Check permissions
+ if (userPermissions < command.MinPermissions)
+ {
+ 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);
+ }
+ }
+ };
+ }
+
+ public Task ShowHelp(User user, Channel channel)
+ {
+ int permissions = Config.PermissionResolver(user);
+
+ StringBuilder output = new StringBuilder();
+ output.AppendLine("These are the commands you can use:");
+ output.Append(string.Join(", ", _commands
+ .Where(x => permissions >= x.MinPermissions && !x.IsHidden)
+ .Select(x => '`' + x.Text + '`')));
+
+ var chars = Config.CommandChars;
+ if (chars.Length > 0)
+ {
+ if (chars.Length == 1)
+ output.AppendLine($"\nYou can use `{chars[0]}` to call a command.");
+ else
+ output.AppendLine($"\nYou can use `{string.Join(" ", chars.Take(chars.Length - 1))}` or `{chars.Last()}` to call a command.");
+ }
+
+ output.AppendLine("`help ` can tell you more about how to use a command.");
+
+ return _client.SendMessage(channel, output.ToString());
+ }
+
+ public Task ShowHelp(Command command, User user, Channel channel)
+ {
+ StringBuilder output = new StringBuilder();
+
+ output.Append($"`{command.Text}`");
+
+ if (command.MinArgs != null && command.MaxArgs != null)
+ {
+ if (command.MinArgs == command.MaxArgs)
+ {
+ if (command.MaxArgs != 0)
+ output.Append($" {command.MinArgs.ToString()} Args");
+ }
+ else
+ output.Append($" {command.MinArgs.ToString()} - {command.MaxArgs.ToString()} Args");
+ }
+ else if (command.MinArgs != null && command.MaxArgs == null)
+ output.Append($" ≥{command.MinArgs.ToString()} Args");
+ else if (command.MinArgs == null && command.MaxArgs != null)
+ output.Append($" ≤{command.MaxArgs.ToString()} Args");
+
+ output.Append($": {command.Description ?? "No description set for this command."}");
+
+ return _client.SendMessage(channel, output.ToString());
+ }
+
+ public void CreateCommandGroup(string cmd, Action config = null)
+ => config(new CommandGroupBuilder(this, cmd, 0));
+ public CommandBuilder CreateCommand(string cmd)
+ {
+ var command = new Command(cmd);
+ _commands.Add(command);
+ return new CommandBuilder(this, command, "");
+ }
+
+ internal void AddCommand(Command command)
+ {
+ _commands.Add(command);
+ _map.AddCommand(command.Text, command);
+ }
+ }
+}
diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs
new file mode 100644
index 000000000..f2903d861
--- /dev/null
+++ b/src/Discord.Net.Commands/CommandServiceConfig.cs
@@ -0,0 +1,49 @@
+using System;
+
+namespace Discord.Commands
+{
+ public enum HelpMode
+ {
+ /// Disable the automatic help command.
+ Disable,
+ /// Use the automatic help command and respond in the channel the command is used.
+ Public,
+ /// Use the automatic help command and respond in a private message.
+ Private
+ }
+ public class CommandServiceConfig
+ {
+ public Func PermissionResolver { get { return _permissionsResolver; } set { SetValue(ref _permissionsResolver, value); } }
+ private Func _permissionsResolver;
+
+ public char? CommandChar
+ {
+ get
+ {
+ return _commandChars.Length > 0 ? _commandChars[0] : (char?)null;
+ }
+ set
+ {
+ if (value != null)
+ CommandChars = new char[] { value.Value };
+ else
+ CommandChars = new char[0];
+ }
+ }
+ public char[] CommandChars { get { return _commandChars; } set { SetValue(ref _commandChars, value); } }
+ private char[] _commandChars = new char[] { '!' };
+
+ public HelpMode HelpMode { get { return _helpMode; } set { SetValue(ref _helpMode, value); } }
+ private HelpMode _helpMode = HelpMode.Disable;
+
+ //Lock
+ protected bool _isLocked;
+ internal void Lock() { _isLocked = true; }
+ protected void SetValue(ref T storage, T value)
+ {
+ if (_isLocked)
+ throw new InvalidOperationException("Unable to modify a discord client's configuration after it has been created.");
+ storage = value;
+ }
+ }
+}
diff --git a/src/Discord.Net.Commands/CommandsPlugin.cs b/src/Discord.Net.Commands/CommandsPlugin.cs
deleted file mode 100644
index e77210e5f..000000000
--- a/src/Discord.Net.Commands/CommandsPlugin.cs
+++ /dev/null
@@ -1,211 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Discord.Commands
-{
- /// A Discord.Net client with extensions for handling common bot operations like text commands.
- public partial class CommandsPlugin
- {
- private readonly DiscordClient _client;
- private readonly Func _getPermissions;
-
- public IEnumerable Commands => _commands;
- private readonly List _commands;
-
- internal CommandMap Map => _map;
- private readonly CommandMap _map;
-
- public char? ComamndChar
- {
- get { return _commandChars.Length > 0 ? _commandChars[0] : (char?)null; }
- set { _commandChars = value != null ? new char[] { value.Value } : new char[0]; }
- }
- public IEnumerable CommandChars { get { return _commandChars; } set { _commandChars = value.ToArray(); } }
- private char[] _commandChars;
-
- public bool RequireCommandCharInPublic { get; set; }
- public bool RequireCommandCharInPrivate { get; set; }
- public bool HelpInPublic { get; set; }
-
- public CommandsPlugin(DiscordClient client, Func getPermissions = null, bool builtInHelp = false)
- {
- _client = client;
- _getPermissions = getPermissions;
-
- _commands = new List();
- _map = new CommandMap(null);
-
- _commandChars = new char[] { '!' };
- RequireCommandCharInPublic = true;
- RequireCommandCharInPrivate = true;
- HelpInPublic = true;
-
- if (builtInHelp)
- {
- CreateCommand("help")
- .Parameter("command", ParameterType.Optional)
- .Hide()
- .Info("Returns information about commands.")
- .Do(async e =>
- {
- if (e.Command.Text != "help")
- await Reply(e, CommandDetails(e.Command));
- else
- {
- if (e.Args == null)
- {
- int permissions = getPermissions(e.User);
-
- StringBuilder output = new StringBuilder();
- output.AppendLine("These are the commands you can use:");
- output.Append("`");
- output.Append(string.Join(", ", _commands.Select(x => permissions >= x.MinPermissions && !x.IsHidden)));
- output.Append("`");
-
- if (_commandChars.Length > 0)
- {
- if (_commandChars.Length == 1)
- output.AppendLine($"\nYou can use `{_commandChars[0]}` to call a command.");
- else
- output.AppendLine($"\nYou can use `{string.Join(" ", CommandChars.Take(_commandChars.Length - 1))}` and `{_commandChars.Last()}` to call a command.");
- }
-
- output.AppendLine("`help ` can tell you more about how to use a command.");
-
- await Reply(e, output.ToString());
- }
- else
- {
- var cmd = _map.GetCommand(e.Args[0]);
- if (cmd != null)
- await Reply(e, CommandDetails(cmd));
- else
- await Reply(e, $"`{e.Args[0]}` is not a valid command.");
- }
- }
- });
-
- }
-
- client.MessageReceived += async (s, e) =>
- {
- if (_commands.Count == 0) return;
- if (e.Message.IsAuthor) return;
-
- string msg = e.Message.Text;
- if (msg.Length == 0) return;
-
- //Check for command char if one is provided
- if (_commandChars.Length > 0)
- {
- 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.
- }
-
- //Parse command
- Command command;
- int argPos;
- CommandParser.ParseCommand(msg, _map, out command, out argPos);
- if (command == null)
- {
- CommandEventArgs errorArgs = new CommandEventArgs(e.Message, null, null, null);
- RaiseCommandError(CommandErrorType.UnknownCommand, errorArgs);
- return;
- }
- else
- {
- int userPermissions = _getPermissions != null ? _getPermissions(e.Message.User) : 0;
-
- //Parse arguments
- string[] args;
- var error = CommandParser.ParseArgs(msg, argPos, command, out args);
- if (error != null)
- {
- var errorArgs = new CommandEventArgs(e.Message, command, userPermissions, null);
- RaiseCommandError(error.Value, errorArgs);
- return;
- }
-
- var eventArgs = new CommandEventArgs(e.Message, command, userPermissions, args);
-
- // Check permissions
- if (userPermissions < command.MinPermissions)
- {
- 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);
- }
- }
- };
- }
-
- private string CommandDetails(Command command)
- {
- StringBuilder output = new StringBuilder();
-
- output.Append($"`{command.Text}`");
-
- if (command.MinArgs != null && command.MaxArgs != null)
- {
- if (command.MinArgs == command.MaxArgs)
- {
- if (command.MaxArgs != 0)
- output.Append($" {command.MinArgs.ToString()} Args");
- }
- else
- output.Append($" {command.MinArgs.ToString()} - {command.MaxArgs.ToString()} Args");
- }
- else if (command.MinArgs != null && command.MaxArgs == null)
- output.Append($" ≥{command.MinArgs.ToString()} Args");
- else if (command.MinArgs == null && command.MaxArgs != null)
- output.Append($" ≤{command.MaxArgs.ToString()} Args");
-
- output.Append($": {command.Description ?? "No description set for this command."}");
-
- 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 config = null)
- => config(new CommandGroupBuilder(this, cmd, 0));
- public CommandBuilder CreateCommand(string cmd)
- {
- var command = new Command(cmd);
- _commands.Add(command);
- return new CommandBuilder(null, command, "");
- }
-
- internal void AddCommand(Command command)
- {
- _commands.Add(command);
- _map.AddCommand(command.Text, command);
- }
- }
-}
diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj
index 0b3717e57..ae77302da 100644
--- a/src/Discord.Net.Net45/Discord.Net.csproj
+++ b/src/Discord.Net.Net45/Discord.Net.csproj
@@ -232,6 +232,9 @@
HttpException.cs
+
+ IService.cs
+
Models\Channel.cs
diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs
index 48c03e372..f00048c20 100644
--- a/src/Discord.Net/DiscordClient.cs
+++ b/src/Discord.Net/DiscordClient.cs
@@ -17,6 +17,7 @@ namespace Discord
private readonly JsonSerializer _serializer;
private readonly ConcurrentQueue _pendingMessages;
private readonly ConcurrentDictionary _voiceClients;
+ private readonly Dictionary _services;
private bool _sentInitialLog;
private uint _nextVoiceClientId;
private UserStatus _status;
@@ -46,6 +47,7 @@ namespace Discord
_roles = new Roles(this, cacheLock);
_servers = new Servers(this, cacheLock);
_globalUsers = new GlobalUsers(this, cacheLock);
+ _services = new Dictionary();
_status = UserStatus.Online;
@@ -168,7 +170,6 @@ namespace Discord
_serializer.MissingMemberHandling = MissingMemberHandling.Error;
#endif
}
-
internal override VoiceWebSocket CreateVoiceSocket()
{
var socket = base.CreateVoiceSocket();
@@ -216,7 +217,6 @@ namespace Discord
await Connect(token).ConfigureAwait(false);
return token;
}
-
/// Connects to the Discord server with the provided token.
public async Task Connect(string token)
{
@@ -273,6 +273,22 @@ namespace Discord
_currentUser = null;
}
+ public void AddService(T obj)
+ where T : class, IService
+ {
+ _services.Add(typeof(T), obj);
+ obj.Install(this);
+ }
+ public T GetService()
+ where T : class, IService
+ {
+ IService service;
+ if (_services.TryGetValue(typeof(T), out service))
+ return service as T;
+ else
+ return null;
+ }
+
protected override IEnumerable GetTasks()
{
if (Config.UseMessageQueue)
diff --git a/src/Discord.Net/IService.cs b/src/Discord.Net/IService.cs
new file mode 100644
index 000000000..cf60f70d1
--- /dev/null
+++ b/src/Discord.Net/IService.cs
@@ -0,0 +1,7 @@
+namespace Discord
+{
+ public interface IService
+ {
+ void Install(DiscordClient client);
+ }
+}