| @@ -61,6 +61,39 @@ | |||
| <Compile Include="..\Discord.Net.Commands\CommandServiceConfig.cs"> | |||
| <Link>CommandServiceConfig.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Commands\Permissions\IPermissionChecker.cs"> | |||
| <Link>Permissions\IPermissionChecker.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Commands\Permissions\Levels\PermissionLevelChecker.cs"> | |||
| <Link>Permissions\Levels\PermissionLevelChecker.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Commands\Permissions\Levels\PermissionLevelExtensions.cs"> | |||
| <Link>Permissions\Levels\PermissionLevelExtensions.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Commands\Permissions\Levels\PermissionLevelService.cs"> | |||
| <Link>Permissions\Levels\PermissionLevelService.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Commands\Permissions\Userlist\BlacklistChecker.cs"> | |||
| <Link>Permissions\Userlist\BlacklistChecker.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Commands\Permissions\Userlist\BlacklistExtensions.cs"> | |||
| <Link>Permissions\Userlist\BlacklistExtensions.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Commands\Permissions\Userlist\BlacklistService.cs"> | |||
| <Link>Permissions\Userlist\BlacklistService.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Commands\Permissions\Userlist\UserlistService.cs"> | |||
| <Link>Permissions\Userlist\UserlistService.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Commands\Permissions\Userlist\WhitelistChecker.cs"> | |||
| <Link>Permissions\Userlist\WhitelistChecker.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Commands\Permissions\Userlist\WhitelistExtensions.cs"> | |||
| <Link>Permissions\Userlist\WhitelistExtensions.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Commands\Permissions\Userlist\WhitelistService.cs"> | |||
| <Link>Permissions\Userlist\WhitelistService.cs</Link> | |||
| </Compile> | |||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| @@ -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<CommandParameter> Parameters => _parameters; | |||
| internal CommandParameter[] _parameters; | |||
| private Func<CommandEventArgs, Task> _handler; | |||
| private IPermissionChecker[] _checks; | |||
| private Func<CommandEventArgs, Task> _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<CommandEventArgs, Task> func) | |||
| { | |||
| _handler = func; | |||
| } | |||
| internal void SetHandler(Action<CommandEventArgs> 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<CommandEventArgs, Task> func) | |||
| { | |||
| _runFunc = func; | |||
| } | |||
| internal void SetRunFunc(Action<CommandEventArgs> 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 | |||
| @@ -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<CommandParameter> _params; | |||
| private bool _allowRequired, _isClosed; | |||
| private string _prefix; | |||
| private readonly List<CommandParameter> _params; | |||
| private readonly List<IPermissionChecker> _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<IPermissionChecker> initialChecks = null) | |||
| { | |||
| _service = service; | |||
| _command = command; | |||
| _command.Category = category; | |||
| _params = new List<CommandParameter>(); | |||
| if (initialChecks != null) | |||
| _checks = new List<IPermissionChecker>(initialChecks); | |||
| else | |||
| _checks = new List<IPermissionChecker>(); | |||
| _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<CommandEventArgs, Task> func) | |||
| { | |||
| _command.SetHandler(func); | |||
| _command.SetRunFunc(func); | |||
| Build(); | |||
| } | |||
| public void Do(Action<CommandEventArgs> 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<IPermissionChecker> _checks; | |||
| private string _category; | |||
| internal CommandGroupBuilder(CommandService service, string prefix) | |||
| public CommandService Service => _service; | |||
| internal CommandGroupBuilder(CommandService service, string prefix, IEnumerable<IPermissionChecker> initialChecks = null) | |||
| { | |||
| _service = service; | |||
| _prefix = prefix; | |||
| if (initialChecks != null) | |||
| _checks = new List<IPermissionChecker>(initialChecks); | |||
| else | |||
| _checks = new List<IPermissionChecker>(); | |||
| } | |||
| 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<CommandGroupBuilder> 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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<string, CommandMap> _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<CommandMap> SubGroups => _items.Values; | |||
| /*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 CommandMap(CommandMap parent, string text) | |||
| public CommandMap(CommandMap parent, string name, string fullName) | |||
| { | |||
| _parent = parent; | |||
| _text = text; | |||
| _items = new Dictionary<string, CommandMap>(); | |||
| _name = name; | |||
| _fullName = fullName; | |||
| _items = new Dictionary<string, CommandMap>(); | |||
| _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); | |||
| } | |||
| @@ -9,8 +9,12 @@ namespace Discord.Commands | |||
| /// <summary> A Discord.Net client with extensions for handling common bot operations like text commands. </summary> | |||
| 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<Command> AllCommands => _allCommands; | |||
| @@ -23,40 +27,47 @@ namespace Discord.Commands | |||
| internal IEnumerable<CommandMap> Categories => _categories.Values; | |||
| private readonly Dictionary<string, CommandMap> _categories; | |||
| public CommandService(CommandServiceConfig config) | |||
| { | |||
| Config = config; | |||
| _config = config; | |||
| _allCommands = new List<Command>(); | |||
| _map = new CommandMap(null, null); | |||
| _map = new CommandMap(null, "", ""); | |||
| _categories = new Dictionary<string, CommandMap>(); | |||
| } | |||
| _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<CommandEventArgs, Task>)(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 <command>` 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<CommandGroupBuilder> 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<CommandGroupBuilder> 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); | |||
| } | |||
| @@ -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<User, int> PermissionResolver { get { return _permissionsResolver; } set { SetValue(ref _permissionsResolver, value); } } | |||
| private Func<User, int> _permissionsResolver;*/ | |||
| public char? CommandChar | |||
| { | |||
| get | |||
| @@ -7,7 +7,7 @@ | |||
| <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" /> | |||
| <PropertyGroup Label="Globals"> | |||
| <ProjectGuid>19793545-ef89-48f4-8100-3ebaad0a9141</ProjectGuid> | |||
| <RootNamespace>Discord</RootNamespace> | |||
| <RootNamespace>Discord.Commands</RootNamespace> | |||
| <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath> | |||
| <OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath> | |||
| </PropertyGroup> | |||
| @@ -0,0 +1,7 @@ | |||
| namespace Discord.Commands.Permissions | |||
| { | |||
| public interface IPermissionChecker | |||
| { | |||
| bool CanRun(Command command, User user, Channel channel); | |||
| } | |||
| } | |||
| @@ -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<PermissionLevelService>(); | |||
| _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; | |||
| } | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| using System; | |||
| namespace Discord.Commands.Permissions.Levels | |||
| { | |||
| public class PermissionLevelService : IService | |||
| { | |||
| private readonly Func<User, Channel, int> _getPermissionsFunc; | |||
| private DiscordClient _client; | |||
| public DiscordClient Client => _client; | |||
| public PermissionLevelService(Func<User, Channel, int> getPermissionsFunc) | |||
| { | |||
| _getPermissionsFunc = getPermissionsFunc; | |||
| } | |||
| public void Install(DiscordClient client) | |||
| { | |||
| _client = client; | |||
| } | |||
| public int GetPermissionLevel(User user, Channel channel) => _getPermissionsFunc(user, channel); | |||
| } | |||
| } | |||
| @@ -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<BlacklistService>(); | |||
| 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| using System.Collections.Generic; | |||
| namespace Discord.Commands.Permissions.Userlist | |||
| { | |||
| public class BlacklistService : UserlistService | |||
| { | |||
| public BlacklistService(IEnumerable<string> initialList = null) | |||
| : base(initialList) | |||
| { | |||
| } | |||
| public bool CanRun(User user) | |||
| { | |||
| return !_userList.ContainsKey(user.Id); | |||
| } | |||
| } | |||
| } | |||
| @@ -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<string, bool> _userList; | |||
| private DiscordClient _client; | |||
| public DiscordClient Client => _client; | |||
| public IEnumerable<string> UserIds => _userList.Select(x => x.Key); | |||
| public UserlistService(IEnumerable<string> initialList = null) | |||
| { | |||
| if (initialList != null) | |||
| _userList = new ConcurrentDictionary<string, bool>(initialList.Select(x => new KeyValuePair<string, bool>(x, true))); | |||
| else | |||
| _userList = new ConcurrentDictionary<string, bool>(); | |||
| } | |||
| 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; | |||
| } | |||
| } | |||
| } | |||
| @@ -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<WhitelistService>(); | |||
| 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| using System.Collections.Generic; | |||
| namespace Discord.Commands.Permissions.Userlist | |||
| { | |||
| public class WhitelistService : UserlistService | |||
| { | |||
| public WhitelistService(IEnumerable<string> initialList = null) | |||
| : base(initialList) | |||
| { | |||
| } | |||
| public bool CanRun(User user) | |||
| { | |||
| return _userList.ContainsKey(user.Id); | |||
| } | |||
| } | |||
| } | |||
| @@ -7,7 +7,7 @@ | |||
| <ProjectGuid>{3091164F-66AE-4543-A63D-167C1116241D}</ProjectGuid> | |||
| <OutputType>Library</OutputType> | |||
| <AppDesignerFolder>Properties</AppDesignerFolder> | |||
| <RootNamespace>Discord</RootNamespace> | |||
| <RootNamespace>Discord.Modules</RootNamespace> | |||
| <AssemblyName>Discord.Net.Commands</AssemblyName> | |||
| <FileAlignment>512</FileAlignment> | |||
| <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> | |||
| @@ -40,6 +40,9 @@ | |||
| <Compile Include="..\Discord.Net.Modules\IModule.cs"> | |||
| <Link>IModule.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Modules\ModuleChecker.cs"> | |||
| <Link>ModuleChecker.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net.Modules\ModuleExtensions.cs"> | |||
| <Link>ModuleExtensions.cs</Link> | |||
| </Compile> | |||
| @@ -7,7 +7,7 @@ | |||
| <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" /> | |||
| <PropertyGroup Label="Globals"> | |||
| <ProjectGuid>01584e8a-78da-486f-9ef9-a894e435841b</ProjectGuid> | |||
| <RootNamespace>Discord</RootNamespace> | |||
| <RootNamespace>Discord.Modules</RootNamespace> | |||
| <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath> | |||
| <OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath> | |||
| </PropertyGroup> | |||
| @@ -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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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<string, Server>(); | |||
| @@ -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; | |||
| @@ -5,12 +5,12 @@ namespace Discord.Modules | |||
| [Flags] | |||
| public enum FilterType | |||
| { | |||
| /// <summary> Disables the event filter. </summary> | |||
| Disabled = 0x0, | |||
| /// <summary> Uses the server whitelist to filter events. </summary> | |||
| Server = 0x1, | |||
| /// <summary> Uses the channel whitelist to filter events. </summary> | |||
| Channel = 0x2, | |||
| /// <summary> Disables the event and command filtesr. </summary> | |||
| Unrestricted = 0x0, | |||
| /// <summary> Uses the server whitelist to filter events and commands. </summary> | |||
| ServerWhitelist = 0x1, | |||
| /// <summary> Uses the channel whitelist to filter events and commands. </summary> | |||
| ChannelWhitelist = 0x2, | |||
| /// <summary> Enables this module in all private messages. </summary> | |||
| AllowPrivate = 0x4 | |||
| } | |||