From 093095e4108e648ffb54002574469ee657c671ea Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 9 Nov 2015 00:55:17 -0400 Subject: [PATCH] Added new permissions system, fixed more commands and module bugs. --- .../Discord.Net.Commands.csproj | 33 ++++++++ src/Discord.Net.Commands/Command.cs | 33 +++++--- src/Discord.Net.Commands/CommandBuilder.cs | 64 +++++++++----- src/Discord.Net.Commands/CommandMap.cs | 23 ++--- src/Discord.Net.Commands/CommandService.cs | 84 ++++++++++--------- .../CommandServiceConfig.cs | 6 +- .../Discord.Net.Commands.xproj | 2 +- .../Permissions/IPermissionChecker.cs | 7 ++ .../Levels/PermissionLevelChecker.cs | 27 ++++++ .../Levels/PermissionLevelExtensions.cs | 21 +++++ .../Levels/PermissionLevelService.cs | 23 +++++ .../Permissions/Userlist/BlacklistChecker.cs | 21 +++++ .../Userlist/BlacklistExtensions.cs | 21 +++++ .../Permissions/Userlist/BlacklistService.cs | 17 ++++ .../Permissions/Userlist/UserlistService.cs | 52 ++++++++++++ .../Permissions/Userlist/WhitelistChecker.cs | 21 +++++ .../Userlist/WhitelistExtensions.cs | 21 +++++ .../Permissions/Userlist/WhitelistService.cs | 17 ++++ .../Discord.Net.Modules.csproj | 5 +- .../Discord.Net.Modules.xproj | 2 +- src/Discord.Net.Modules/ModuleChecker.cs | 20 +++++ src/Discord.Net.Modules/ModuleManager.cs | 11 +-- src/Discord.Net.Modules/ModuleType.cs | 12 +-- 23 files changed, 443 insertions(+), 100 deletions(-) create mode 100644 src/Discord.Net.Commands/Permissions/IPermissionChecker.cs create mode 100644 src/Discord.Net.Commands/Permissions/Levels/PermissionLevelChecker.cs create mode 100644 src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs create mode 100644 src/Discord.Net.Commands/Permissions/Levels/PermissionLevelService.cs create mode 100644 src/Discord.Net.Commands/Permissions/Userlist/BlacklistChecker.cs create mode 100644 src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs create mode 100644 src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs create mode 100644 src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs create mode 100644 src/Discord.Net.Commands/Permissions/Userlist/WhitelistChecker.cs create mode 100644 src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs create mode 100644 src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs create mode 100644 src/Discord.Net.Modules/ModuleChecker.cs diff --git a/src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj b/src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj index 3a77ea6ed..b2c6076de 100644 --- a/src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands.Net45/Discord.Net.Commands.csproj @@ -61,6 +61,39 @@ CommandServiceConfig.cs + + Permissions\IPermissionChecker.cs + + + Permissions\Levels\PermissionLevelChecker.cs + + + Permissions\Levels\PermissionLevelExtensions.cs + + + Permissions\Levels\PermissionLevelService.cs + + + Permissions\Userlist\BlacklistChecker.cs + + + Permissions\Userlist\BlacklistExtensions.cs + + + Permissions\Userlist\BlacklistService.cs + + + Permissions\Userlist\UserlistService.cs + + + Permissions\Userlist\WhitelistChecker.cs + + + Permissions\Userlist\WhitelistExtensions.cs + + + Permissions\Userlist\WhitelistService.cs + diff --git a/src/Discord.Net.Commands/Command.cs b/src/Discord.Net.Commands/Command.cs index 548ef2bc9..a91251f88 100644 --- a/src/Discord.Net.Commands/Command.cs +++ b/src/Discord.Net.Commands/Command.cs @@ -1,4 +1,5 @@ -using System; +using Discord.Commands.Permissions; +using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -41,8 +42,9 @@ namespace Discord.Commands public IEnumerable Parameters => _parameters; internal CommandParameter[] _parameters; - - private Func _handler; + + private IPermissionChecker[] _checks; + private Func _runFunc; internal Command(string text) { @@ -56,7 +58,6 @@ namespace Discord.Commands { _aliases = aliases; } - internal void SetParameters(CommandParameter[] parameters) { _parameters = parameters; @@ -89,24 +90,32 @@ namespace Discord.Commands } } } - - internal void SetHandler(Func func) - { - _handler = func; - } - internal void SetHandler(Action func) + internal void SetChecks(IPermissionChecker[] checks) { - _handler = e => { func(e); return TaskHelper.CompletedTask; }; + _checks = checks; } internal bool CanRun(User user, Channel channel) { + for (int i = 0; i < _checks.Length; i++) + { + if (!_checks[i].CanRun(this, user, channel)) + return false; + } return true; } + internal void SetRunFunc(Func func) + { + _runFunc = func; + } + internal void SetRunFunc(Action func) + { + _runFunc = e => { func(e); return TaskHelper.CompletedTask; }; + } internal Task Run(CommandEventArgs args) { - var task = _handler(args); + var task = _runFunc(args); if (task != null) return task; else diff --git a/src/Discord.Net.Commands/CommandBuilder.cs b/src/Discord.Net.Commands/CommandBuilder.cs index 67f7e0c1a..6f11e3f26 100644 --- a/src/Discord.Net.Commands/CommandBuilder.cs +++ b/src/Discord.Net.Commands/CommandBuilder.cs @@ -1,4 +1,5 @@ -using System; +using Discord.Commands.Permissions; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -9,19 +10,27 @@ namespace Discord.Commands { private readonly CommandService _service; private readonly Command _command; - private List _params; - private bool _allowRequired, _isClosed; - private string _prefix; + private readonly List _params; + private readonly List _checks; + private readonly string _prefix; + private bool _allowRequiredParams, _areParamsClosed; + + public CommandService Service => _service; - internal CommandBuilder(CommandService service, Command command, string prefix, string category) + internal CommandBuilder(CommandService service, Command command, string prefix = "", string category = "", IEnumerable initialChecks = null) { _service = service; _command = command; _command.Category = category; _params = new List(); + if (initialChecks != null) + _checks = new List(initialChecks); + else + _checks = new List(); _prefix = prefix; - _allowRequired = true; - _isClosed = false; + + _allowRequiredParams = true; + _areParamsClosed = false; } public CommandBuilder Alias(params string[] aliases) @@ -35,24 +44,24 @@ namespace Discord.Commands _command.Category = category; return this; }*/ - public CommandBuilder Info(string description) + public CommandBuilder Description(string description) { _command.Description = description; return this; } public CommandBuilder Parameter(string name, ParameterType type = ParameterType.Required) { - if (_isClosed) + if (_areParamsClosed) throw new Exception($"No parameters may be added after a {nameof(ParameterType.Multiple)} or {nameof(ParameterType.Unparsed)} parameter."); - if (!_allowRequired && type == ParameterType.Required) + if (!_allowRequiredParams && type == ParameterType.Required) throw new Exception($"{nameof(ParameterType.Required)} parameters may not be added after an optional one"); _params.Add(new CommandParameter(name, type)); if (type == ParameterType.Optional) - _allowRequired = false; + _allowRequiredParams = false; if (type == ParameterType.Multiple || type == ParameterType.Unparsed) - _isClosed = true; + _areParamsClosed = true; return this; } public CommandBuilder Hide() @@ -60,20 +69,26 @@ namespace Discord.Commands _command.IsHidden = true; return this; } + public CommandBuilder AddCheck(IPermissionChecker check) + { + _checks.Add(check); + return this; + } public void Do(Func func) { - _command.SetHandler(func); + _command.SetRunFunc(func); Build(); } public void Do(Action func) { - _command.SetHandler(func); + _command.SetRunFunc(func); Build(); } private void Build() { _command.SetParameters(_params.ToArray()); + _command.SetChecks(_checks.ToArray()); _service.AddCommand(_command); } @@ -97,14 +112,21 @@ namespace Discord.Commands } public sealed class CommandGroupBuilder { - internal readonly CommandService _service; + private readonly CommandService _service; private readonly string _prefix; + private readonly List _checks; private string _category; - internal CommandGroupBuilder(CommandService service, string prefix) + public CommandService Service => _service; + + internal CommandGroupBuilder(CommandService service, string prefix, IEnumerable initialChecks = null) { _service = service; _prefix = prefix; + if (initialChecks != null) + _checks = new List(initialChecks); + else + _checks = new List(); } public CommandGroupBuilder Category(string category) @@ -112,10 +134,14 @@ namespace Discord.Commands _category = category; return this; } + public void AddCheck(IPermissionChecker check) + { + _checks.Add(check); + } public CommandGroupBuilder CreateGroup(string cmd, Action config = null) - { - config(new CommandGroupBuilder(_service, _prefix + ' ' + cmd)); + { + config(new CommandGroupBuilder(_service, CommandBuilder.AppendPrefix(_prefix, cmd), _checks)); return this; } public CommandBuilder CreateCommand() @@ -123,7 +149,7 @@ namespace Discord.Commands public CommandBuilder CreateCommand(string cmd) { var command = new Command(CommandBuilder.AppendPrefix(_prefix, cmd)); - return new CommandBuilder(_service, command, _prefix, _category); + return new CommandBuilder(_service, command, _prefix, _category, _checks); } } } diff --git a/src/Discord.Net.Commands/CommandMap.cs b/src/Discord.Net.Commands/CommandMap.cs index cceff23f9..4343cdd86 100644 --- a/src/Discord.Net.Commands/CommandMap.cs +++ b/src/Discord.Net.Commands/CommandMap.cs @@ -7,24 +7,26 @@ namespace Discord.Commands internal class CommandMap { private readonly CommandMap _parent; - private readonly string _text; + private readonly string _name, _fullName; private Command _command; private readonly Dictionary _items; private bool _isHidden; - public string Text => _text; + public string Name => _name; + public string FullName => _fullName; public bool IsHidden => _isHidden; public Command Command => _command; public IEnumerable SubGroups => _items.Values; /*public IEnumerable SubCommands => _items.Select(x => x.Value._command).Where(x => x != null); public IEnumerable SubGroups => _items.Select(x => x.Value).Where(x => x._items.Count > 0);*/ - public CommandMap(CommandMap parent, string text) + public CommandMap(CommandMap parent, string name, string fullName) { _parent = parent; - _text = text; - _items = new Dictionary(); + _name = name; + _fullName = fullName; + _items = new Dictionary(); _isHidden = true; } @@ -82,19 +84,20 @@ namespace Discord.Commands { AddCommand(0, text.Split(' '), command); } - public void AddCommand(int index, string[] parts, Command command) + private void AddCommand(int index, string[] parts, Command command) { if (!command.IsHidden && _isHidden) _isHidden = false; if (index != parts.Length) { - string nextPart = parts[index]; CommandMap nextGroup; - if (!_items.TryGetValue(nextPart, out nextGroup)) + string name = parts[index]; + string fullName = string.Join(" ", parts, 0, index + 1); + if (!_items.TryGetValue(name, out nextGroup)) { - nextGroup = new CommandMap(this, nextPart); - _items.Add(nextPart, nextGroup); + nextGroup = new CommandMap(this, name, fullName); + _items.Add(name, nextGroup); } nextGroup.AddCommand(index + 1, parts, command); } diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 24d475b79..7a3d984c7 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -9,8 +9,12 @@ namespace Discord.Commands /// A Discord.Net client with extensions for handling common bot operations like text commands. public partial class CommandService : IService { + private readonly CommandServiceConfig _config; + private readonly CommandGroupBuilder _root; private DiscordClient _client; - public CommandServiceConfig Config { get; } + + public DiscordClient Client => _client; + public CommandGroupBuilder Root => _root; //AllCommands store a flattened collection of all commands public IEnumerable AllCommands => _allCommands; @@ -23,40 +27,47 @@ namespace Discord.Commands internal IEnumerable Categories => _categories.Values; private readonly Dictionary _categories; - public CommandService(CommandServiceConfig config) { - Config = config; + _config = config; _allCommands = new List(); - _map = new CommandMap(null, null); + _map = new CommandMap(null, "", ""); _categories = new Dictionary(); - } + _root = new CommandGroupBuilder(this, "", null); + } void IService.Install(DiscordClient client) { _client = client; - Config.Lock(); + _config.Lock(); - if (Config.HelpMode != HelpMode.Disable) + if (_config.HelpMode != HelpMode.Disable) { - CreateCommand("help") + CreateCommand("help") .Parameter("command", ParameterType.Multiple) .Hide() - .Info("Returns information about commands.") - .Do(async e => + .Description("Returns information about commands.") + .Do((Func)(async e => { - Channel channel = Config.HelpMode == HelpMode.Public ? e.Channel : await client.CreatePMChannel(e.User); + Channel replyChannel = _config.HelpMode == HelpMode.Public ? e.Channel : await client.CreatePMChannel(e.User); if (e.Args.Length > 0) //Show command help { var map = _map.GetItem(string.Join(" ", e.Args)); if (map != null) - await ShowHelp(map, e.User, channel); + await ShowCommandHelp(map, e.User, e.Channel, replyChannel); else - await client.SendMessage(channel, "Unable to display help: Unknown command."); + await client.SendMessage(replyChannel, "Unable to display help: Unknown command."); } else //Show general help - await ShowHelp(e.User, channel); - }); + +/* Unmerged change from project 'Discord.Net.Commands' +Before: + await ShowHelp(e.User, e.Channel, replyChannel); +After: + await this.ShowHelp((User)e.User, e.Channel, replyChannel); +*/ + await this.ShowGeneralHelp(e.User, (Channel)e.Channel, (Channel)replyChannel); + })); } client.MessageReceived += async (s, e) => @@ -68,7 +79,7 @@ namespace Discord.Commands if (msg.Length == 0) return; //Check for command char if one is provided - var chars = Config.CommandChars; + var chars = _config.CommandChars; if (chars.Length > 0) { if (!chars.Contains(msg[0])) @@ -121,7 +132,7 @@ namespace Discord.Commands }; } - public Task ShowHelp(User user, Channel channel) + public Task ShowGeneralHelp(User user, Channel channel, Channel replyChannel = null) { StringBuilder output = new StringBuilder(); /*output.AppendLine("These are the commands you can use:"); @@ -163,7 +174,7 @@ namespace Discord.Commands else output.Append(", "); output.Append('`'); - output.Append(group.Text); + output.Append(group.Name); if (group.SubGroups.Any()) output.Append("*"); output.Append('`'); @@ -177,7 +188,7 @@ namespace Discord.Commands { output.Append("\n\n"); - var chars = Config.CommandChars; + var chars = _config.CommandChars; if (chars.Length > 0) { if (chars.Length == 1) @@ -190,20 +201,20 @@ namespace Discord.Commands output.AppendLine($"`help ` can tell you more about how to use a command."); } - return _client.SendMessage(channel, output.ToString()); + return _client.SendMessage(replyChannel ?? channel, output.ToString()); } - private Task ShowHelp(CommandMap map, User user, Channel channel) + private Task ShowCommandHelp(CommandMap map, User user, Channel channel, Channel replyChannel = null) { StringBuilder output = new StringBuilder(); Command cmd = map.Command; if (cmd != null) - ShowHelpInternal(cmd, user, channel, output); + ShowCommandHelpInternal(cmd, user, channel, output); else { output.Append('`'); - output.Append(map.Text); + output.Append(map.FullName); output.Append("`\n"); } @@ -218,21 +229,21 @@ namespace Discord.Commands else output.Append(", "); output.Append('`'); - output.Append(subCmd.Text); + output.Append(subCmd.Name); if (subCmd.SubGroups.Any()) output.Append("*"); output.Append('`'); } - return _client.SendMessage(channel, output.ToString()); + return _client.SendMessage(replyChannel ?? channel, output.ToString()); } - public Task ShowHelp(Command command, User user, Channel channel) + public Task ShowCommandHelp(Command command, User user, Channel channel, Channel replyChannel = null) { StringBuilder output = new StringBuilder(); - ShowHelpInternal(command, user, channel, output); - return _client.SendMessage(channel, output.ToString()); + ShowCommandHelpInternal(command, user, channel, output); + return _client.SendMessage(replyChannel ?? channel, output.ToString()); } - private void ShowHelpInternal(Command command, User user, Channel channel, StringBuilder output) + private void ShowCommandHelpInternal(Command command, User user, Channel channel, StringBuilder output) { output.Append('`'); output.Append(command.Text); @@ -261,17 +272,8 @@ namespace Discord.Commands output.AppendLine($"Aliases: `" + string.Join("`, `", command.Aliases) + '`'); } - public void CreateGroup(string cmd, Action config = null) - { - var builder = new CommandGroupBuilder(this, cmd); - if (config != null) - config(builder); - } - public CommandBuilder CreateCommand(string cmd) - { - var command = new Command(cmd); - return new CommandBuilder(this, command, "", ""); - } + public void CreateGroup(string cmd, Action config = null) => _root.CreateGroup(cmd, config); + public CommandBuilder CreateCommand(string cmd) => _root.CreateCommand(cmd); internal void AddCommand(Command command) { @@ -282,7 +284,7 @@ namespace Discord.Commands string categoryName = command.Category ?? ""; if (!_categories.TryGetValue(categoryName, out category)) { - category = new CommandMap(null, ""); + category = new CommandMap(null, "", ""); _categories.Add(categoryName, category); } diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 70752f7ca..6d9e51926 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -1,4 +1,5 @@ -using System; +using Discord.Commands.Permissions; +using System; namespace Discord.Commands { @@ -13,9 +14,6 @@ namespace Discord.Commands } public class CommandServiceConfig { - /*public Func PermissionResolver { get { return _permissionsResolver; } set { SetValue(ref _permissionsResolver, value); } } - private Func _permissionsResolver;*/ - public char? CommandChar { get diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.xproj b/src/Discord.Net.Commands/Discord.Net.Commands.xproj index 1c104ae22..6c0d0ca91 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.xproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.xproj @@ -7,7 +7,7 @@ 19793545-ef89-48f4-8100-3ebaad0a9141 - Discord + Discord.Commands ..\..\artifacts\obj\$(MSBuildProjectName) ..\..\artifacts\bin\$(MSBuildProjectName)\ diff --git a/src/Discord.Net.Commands/Permissions/IPermissionChecker.cs b/src/Discord.Net.Commands/Permissions/IPermissionChecker.cs new file mode 100644 index 000000000..6df6c6ea0 --- /dev/null +++ b/src/Discord.Net.Commands/Permissions/IPermissionChecker.cs @@ -0,0 +1,7 @@ +namespace Discord.Commands.Permissions +{ + public interface IPermissionChecker + { + bool CanRun(Command command, User user, Channel channel); + } +} diff --git a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelChecker.cs b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelChecker.cs new file mode 100644 index 000000000..4d5d33dd4 --- /dev/null +++ b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelChecker.cs @@ -0,0 +1,27 @@ +using System; + +namespace Discord.Commands.Permissions.Levels +{ + public class PermissionLevelChecker : IPermissionChecker + { + private readonly PermissionLevelService _service; + private readonly int _minPermissions; + + public PermissionLevelService Service => _service; + public int MinPermissions => _minPermissions; + + internal PermissionLevelChecker(DiscordClient client, int minPermissions) + { + _service = client.GetService(); + _minPermissions = minPermissions; + if (_service == null) + throw new InvalidOperationException($"{nameof(PermissionLevelService)} must be added to {nameof(DiscordClient)} before this function is called."); + } + + public bool CanRun(Command command, User user, Channel channel) + { + int permissions = _service.GetPermissionLevel(user, channel); + return permissions >= _minPermissions; + } + } +} diff --git a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs new file mode 100644 index 000000000..e2169ec7b --- /dev/null +++ b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelExtensions.cs @@ -0,0 +1,21 @@ +namespace Discord.Commands.Permissions.Levels +{ + public static class PermissionLevelExtensions + { + public static CommandBuilder MinPermissions(this CommandBuilder builder, int minPermissions) + { + builder.AddCheck(new PermissionLevelChecker(builder.Service.Client, minPermissions)); + return builder; + } + public static CommandGroupBuilder MinPermissions(this CommandGroupBuilder builder, int minPermissions) + { + builder.AddCheck(new PermissionLevelChecker(builder.Service.Client, minPermissions)); + return builder; + } + public static CommandService MinPermissions(this CommandService service, int minPermissions) + { + service.Root.AddCheck(new PermissionLevelChecker(service.Client, minPermissions)); + return service; + } + } +} diff --git a/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelService.cs b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelService.cs new file mode 100644 index 000000000..6bab13b97 --- /dev/null +++ b/src/Discord.Net.Commands/Permissions/Levels/PermissionLevelService.cs @@ -0,0 +1,23 @@ +using System; + +namespace Discord.Commands.Permissions.Levels +{ + public class PermissionLevelService : IService + { + private readonly Func _getPermissionsFunc; + + private DiscordClient _client; + public DiscordClient Client => _client; + + public PermissionLevelService(Func getPermissionsFunc) + { + _getPermissionsFunc = getPermissionsFunc; + } + + public void Install(DiscordClient client) + { + _client = client; + } + public int GetPermissionLevel(User user, Channel channel) => _getPermissionsFunc(user, channel); + } +} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistChecker.cs b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistChecker.cs new file mode 100644 index 000000000..c038af68b --- /dev/null +++ b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistChecker.cs @@ -0,0 +1,21 @@ +using System; + +namespace Discord.Commands.Permissions.Userlist +{ + public class BlacklistChecker : IPermissionChecker + { + private readonly BlacklistService _service; + + internal BlacklistChecker(DiscordClient client) + { + _service = client.GetService(); + if (_service == null) + throw new InvalidOperationException($"{nameof(BlacklistService)} must be added to {nameof(DiscordClient)} before this function is called."); + } + + public bool CanRun(Command command, User user, Channel channel) + { + return _service.CanRun(user); + } + } +} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs new file mode 100644 index 000000000..6d6ee01c3 --- /dev/null +++ b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistExtensions.cs @@ -0,0 +1,21 @@ +namespace Discord.Commands.Permissions.Userlist +{ + public static class BlacklistExtensions + { + public static CommandBuilder UseGlobalBlacklist(this CommandBuilder builder) + { + builder.AddCheck(new BlacklistChecker(builder.Service.Client)); + return builder; + } + public static CommandGroupBuilder UseGlobalBlacklist(this CommandGroupBuilder builder) + { + builder.AddCheck(new BlacklistChecker(builder.Service.Client)); + return builder; + } + public static CommandService UseGlobalBlacklist(this CommandService service) + { + service.Root.AddCheck(new BlacklistChecker(service.Client)); + return service; + } + } +} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs new file mode 100644 index 000000000..8e91ef8b6 --- /dev/null +++ b/src/Discord.Net.Commands/Permissions/Userlist/BlacklistService.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Discord.Commands.Permissions.Userlist +{ + public class BlacklistService : UserlistService + { + public BlacklistService(IEnumerable initialList = null) + : base(initialList) + { + } + + public bool CanRun(User user) + { + return !_userList.ContainsKey(user.Id); + } + } +} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs b/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs new file mode 100644 index 000000000..3c6e8bf6e --- /dev/null +++ b/src/Discord.Net.Commands/Permissions/Userlist/UserlistService.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace Discord.Commands.Permissions.Userlist +{ + public class UserlistService : IService + { + protected readonly ConcurrentDictionary _userList; + private DiscordClient _client; + + public DiscordClient Client => _client; + public IEnumerable UserIds => _userList.Select(x => x.Key); + + public UserlistService(IEnumerable initialList = null) + { + if (initialList != null) + _userList = new ConcurrentDictionary(initialList.Select(x => new KeyValuePair(x, true))); + else + _userList = new ConcurrentDictionary(); + } + + public void Add(User user) + { + if (user == null) throw new ArgumentNullException(nameof(user)); + _userList[user.Id] = true; + } + public void Add(string userId) + { + if (userId == null) throw new ArgumentNullException(nameof(userId)); + _userList[userId] = true; + } + public bool Remove(User user) + { + if (user == null) throw new ArgumentNullException(nameof(user)); + bool ignored; + return _userList.TryRemove(user.Id, out ignored); + } + public bool Remove(string userId) + { + if (userId == null) throw new ArgumentNullException(nameof(userId)); + bool ignored; + return _userList.TryRemove(userId, out ignored); + } + + public void Install(DiscordClient client) + { + _client = client; + } + } +} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistChecker.cs b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistChecker.cs new file mode 100644 index 000000000..781ea30e3 --- /dev/null +++ b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistChecker.cs @@ -0,0 +1,21 @@ +using System; + +namespace Discord.Commands.Permissions.Userlist +{ + public class WhitelistChecker : IPermissionChecker + { + private readonly WhitelistService _service; + + internal WhitelistChecker(DiscordClient client) + { + _service = client.GetService(); + if (_service == null) + throw new InvalidOperationException($"{nameof(WhitelistService)} must be added to {nameof(DiscordClient)} before this function is called."); + } + + public bool CanRun(Command command, User user, Channel channel) + { + return _service.CanRun(user); + } + } +} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs new file mode 100644 index 000000000..9076179c2 --- /dev/null +++ b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistExtensions.cs @@ -0,0 +1,21 @@ +namespace Discord.Commands.Permissions.Userlist +{ + public static class WhitelistExtensions + { + public static CommandBuilder UseGlobalWhitelist(this CommandBuilder builder) + { + builder.AddCheck(new WhitelistChecker(builder.Service.Client)); + return builder; + } + public static CommandGroupBuilder UseGlobalWhitelist(this CommandGroupBuilder builder) + { + builder.AddCheck(new WhitelistChecker(builder.Service.Client)); + return builder; + } + public static CommandService UseGlobalWhitelist(this CommandService service) + { + service.Root.AddCheck(new BlacklistChecker(service.Client)); + return service; + } + } +} diff --git a/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs new file mode 100644 index 000000000..4719ee004 --- /dev/null +++ b/src/Discord.Net.Commands/Permissions/Userlist/WhitelistService.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Discord.Commands.Permissions.Userlist +{ + public class WhitelistService : UserlistService + { + public WhitelistService(IEnumerable initialList = null) + : base(initialList) + { + } + + public bool CanRun(User user) + { + return _userList.ContainsKey(user.Id); + } + } +} diff --git a/src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj b/src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj index fd825930f..68d3404f3 100644 --- a/src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj +++ b/src/Discord.Net.Modules.Net45/Discord.Net.Modules.csproj @@ -7,7 +7,7 @@ {3091164F-66AE-4543-A63D-167C1116241D} Library Properties - Discord + Discord.Modules Discord.Net.Commands 512 v4.5 @@ -40,6 +40,9 @@ IModule.cs + + ModuleChecker.cs + ModuleExtensions.cs diff --git a/src/Discord.Net.Modules/Discord.Net.Modules.xproj b/src/Discord.Net.Modules/Discord.Net.Modules.xproj index f6db54ed1..77112cd5d 100644 --- a/src/Discord.Net.Modules/Discord.Net.Modules.xproj +++ b/src/Discord.Net.Modules/Discord.Net.Modules.xproj @@ -7,7 +7,7 @@ 01584e8a-78da-486f-9ef9-a894e435841b - Discord + Discord.Modules ..\..\artifacts\obj\$(MSBuildProjectName) ..\..\artifacts\bin\$(MSBuildProjectName)\ diff --git a/src/Discord.Net.Modules/ModuleChecker.cs b/src/Discord.Net.Modules/ModuleChecker.cs new file mode 100644 index 000000000..efa37690b --- /dev/null +++ b/src/Discord.Net.Modules/ModuleChecker.cs @@ -0,0 +1,20 @@ +using Discord.Commands; +using Discord.Commands.Permissions; + +namespace Discord.Modules +{ + public class ModuleChecker : IPermissionChecker + { + private readonly ModuleManager _manager; + + internal ModuleChecker(ModuleManager manager) + { + _manager = manager; + } + + public bool CanRun(Command command, User user, Channel channel) + { + return _manager.FilterType.HasFlag(FilterType.Unrestricted) || _manager.HasChannel(channel); + } + } +} diff --git a/src/Discord.Net.Modules/ModuleManager.cs b/src/Discord.Net.Modules/ModuleManager.cs index 2697b5bcb..43a5350fa 100644 --- a/src/Discord.Net.Modules/ModuleManager.cs +++ b/src/Discord.Net.Modules/ModuleManager.cs @@ -64,8 +64,8 @@ namespace Discord.Modules _name = name; _id = name.ToLowerInvariant(); _filterType = filterType; - _allowServerWhitelist = filterType.HasFlag(FilterType.Server); - _allowChannelWhitelist = filterType.HasFlag(FilterType.Channel); + _allowServerWhitelist = filterType.HasFlag(FilterType.ServerWhitelist); + _allowChannelWhitelist = filterType.HasFlag(FilterType.ChannelWhitelist); _allowPrivate = filterType.HasFlag(FilterType.AllowPrivate); _enabledServers = new ConcurrentDictionary(); @@ -115,6 +115,7 @@ namespace Discord.Modules commandService.CreateGroup(prefix, x => { x.Category(_name); + x.AddCheck(new ModuleChecker(this)); config(x); }); @@ -244,12 +245,12 @@ namespace Discord.Modules DisableAllChannels(); } - private bool HasServer(Server server) => + internal bool HasServer(Server server) => _allowServerWhitelist && _enabledServers.ContainsKey(server.Id); - private bool HasIndirectServer(Server server) => + internal bool HasIndirectServer(Server server) => (_allowServerWhitelist && _enabledServers.ContainsKey(server.Id)) || (_allowChannelWhitelist && _indirectServers.ContainsKey(server.Id)); - private bool HasChannel(Channel channel) + internal bool HasChannel(Channel channel) { if (channel.IsPrivate) return _allowPrivate; diff --git a/src/Discord.Net.Modules/ModuleType.cs b/src/Discord.Net.Modules/ModuleType.cs index bb0743a61..215debab6 100644 --- a/src/Discord.Net.Modules/ModuleType.cs +++ b/src/Discord.Net.Modules/ModuleType.cs @@ -5,12 +5,12 @@ namespace Discord.Modules [Flags] public enum FilterType { - /// Disables the event filter. - Disabled = 0x0, - /// Uses the server whitelist to filter events. - Server = 0x1, - /// Uses the channel whitelist to filter events. - Channel = 0x2, + /// Disables the event and command filtesr. + Unrestricted = 0x0, + /// Uses the server whitelist to filter events and commands. + ServerWhitelist = 0x1, + /// Uses the channel whitelist to filter events and commands. + ChannelWhitelist = 0x2, /// Enables this module in all private messages. AllowPrivate = 0x4 }