| @@ -61,6 +61,39 @@ | |||||
| <Compile Include="..\Discord.Net.Commands\CommandServiceConfig.cs"> | <Compile Include="..\Discord.Net.Commands\CommandServiceConfig.cs"> | ||||
| <Link>CommandServiceConfig.cs</Link> | <Link>CommandServiceConfig.cs</Link> | ||||
| </Compile> | </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" /> | <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| @@ -1,4 +1,5 @@ | |||||
| using System; | |||||
| using Discord.Commands.Permissions; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -41,8 +42,9 @@ namespace Discord.Commands | |||||
| public IEnumerable<CommandParameter> Parameters => _parameters; | public IEnumerable<CommandParameter> Parameters => _parameters; | ||||
| internal CommandParameter[] _parameters; | internal CommandParameter[] _parameters; | ||||
| private Func<CommandEventArgs, Task> _handler; | |||||
| private IPermissionChecker[] _checks; | |||||
| private Func<CommandEventArgs, Task> _runFunc; | |||||
| internal Command(string text) | internal Command(string text) | ||||
| { | { | ||||
| @@ -56,7 +58,6 @@ namespace Discord.Commands | |||||
| { | { | ||||
| _aliases = aliases; | _aliases = aliases; | ||||
| } | } | ||||
| internal void SetParameters(CommandParameter[] parameters) | internal void SetParameters(CommandParameter[] parameters) | ||||
| { | { | ||||
| _parameters = 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) | 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; | 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) | internal Task Run(CommandEventArgs args) | ||||
| { | { | ||||
| var task = _handler(args); | |||||
| var task = _runFunc(args); | |||||
| if (task != null) | if (task != null) | ||||
| return task; | return task; | ||||
| else | else | ||||
| @@ -1,4 +1,5 @@ | |||||
| using System; | |||||
| using Discord.Commands.Permissions; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Linq; | using System.Linq; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -9,19 +10,27 @@ namespace Discord.Commands | |||||
| { | { | ||||
| private readonly CommandService _service; | private readonly CommandService _service; | ||||
| private readonly Command _command; | 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; | _service = service; | ||||
| _command = command; | _command = command; | ||||
| _command.Category = category; | _command.Category = category; | ||||
| _params = new List<CommandParameter>(); | _params = new List<CommandParameter>(); | ||||
| if (initialChecks != null) | |||||
| _checks = new List<IPermissionChecker>(initialChecks); | |||||
| else | |||||
| _checks = new List<IPermissionChecker>(); | |||||
| _prefix = prefix; | _prefix = prefix; | ||||
| _allowRequired = true; | |||||
| _isClosed = false; | |||||
| _allowRequiredParams = true; | |||||
| _areParamsClosed = false; | |||||
| } | } | ||||
| public CommandBuilder Alias(params string[] aliases) | public CommandBuilder Alias(params string[] aliases) | ||||
| @@ -35,24 +44,24 @@ namespace Discord.Commands | |||||
| _command.Category = category; | _command.Category = category; | ||||
| return this; | return this; | ||||
| }*/ | }*/ | ||||
| public CommandBuilder Info(string description) | |||||
| public CommandBuilder Description(string description) | |||||
| { | { | ||||
| _command.Description = description; | _command.Description = description; | ||||
| return this; | return this; | ||||
| } | } | ||||
| public CommandBuilder Parameter(string name, ParameterType type = ParameterType.Required) | 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."); | 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"); | throw new Exception($"{nameof(ParameterType.Required)} parameters may not be added after an optional one"); | ||||
| _params.Add(new CommandParameter(name, type)); | _params.Add(new CommandParameter(name, type)); | ||||
| if (type == ParameterType.Optional) | if (type == ParameterType.Optional) | ||||
| _allowRequired = false; | |||||
| _allowRequiredParams = false; | |||||
| if (type == ParameterType.Multiple || type == ParameterType.Unparsed) | if (type == ParameterType.Multiple || type == ParameterType.Unparsed) | ||||
| _isClosed = true; | |||||
| _areParamsClosed = true; | |||||
| return this; | return this; | ||||
| } | } | ||||
| public CommandBuilder Hide() | public CommandBuilder Hide() | ||||
| @@ -60,20 +69,26 @@ namespace Discord.Commands | |||||
| _command.IsHidden = true; | _command.IsHidden = true; | ||||
| return this; | return this; | ||||
| } | } | ||||
| public CommandBuilder AddCheck(IPermissionChecker check) | |||||
| { | |||||
| _checks.Add(check); | |||||
| return this; | |||||
| } | |||||
| public void Do(Func<CommandEventArgs, Task> func) | public void Do(Func<CommandEventArgs, Task> func) | ||||
| { | { | ||||
| _command.SetHandler(func); | |||||
| _command.SetRunFunc(func); | |||||
| Build(); | Build(); | ||||
| } | } | ||||
| public void Do(Action<CommandEventArgs> func) | public void Do(Action<CommandEventArgs> func) | ||||
| { | { | ||||
| _command.SetHandler(func); | |||||
| _command.SetRunFunc(func); | |||||
| Build(); | Build(); | ||||
| } | } | ||||
| private void Build() | private void Build() | ||||
| { | { | ||||
| _command.SetParameters(_params.ToArray()); | _command.SetParameters(_params.ToArray()); | ||||
| _command.SetChecks(_checks.ToArray()); | |||||
| _service.AddCommand(_command); | _service.AddCommand(_command); | ||||
| } | } | ||||
| @@ -97,14 +112,21 @@ namespace Discord.Commands | |||||
| } | } | ||||
| public sealed class CommandGroupBuilder | public sealed class CommandGroupBuilder | ||||
| { | { | ||||
| internal readonly CommandService _service; | |||||
| private readonly CommandService _service; | |||||
| private readonly string _prefix; | private readonly string _prefix; | ||||
| private readonly List<IPermissionChecker> _checks; | |||||
| private string _category; | 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; | _service = service; | ||||
| _prefix = prefix; | _prefix = prefix; | ||||
| if (initialChecks != null) | |||||
| _checks = new List<IPermissionChecker>(initialChecks); | |||||
| else | |||||
| _checks = new List<IPermissionChecker>(); | |||||
| } | } | ||||
| public CommandGroupBuilder Category(string category) | public CommandGroupBuilder Category(string category) | ||||
| @@ -112,10 +134,14 @@ namespace Discord.Commands | |||||
| _category = category; | _category = category; | ||||
| return this; | return this; | ||||
| } | } | ||||
| public void AddCheck(IPermissionChecker check) | |||||
| { | |||||
| _checks.Add(check); | |||||
| } | |||||
| public CommandGroupBuilder CreateGroup(string cmd, Action<CommandGroupBuilder> config = null) | 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; | return this; | ||||
| } | } | ||||
| public CommandBuilder CreateCommand() | public CommandBuilder CreateCommand() | ||||
| @@ -123,7 +149,7 @@ namespace Discord.Commands | |||||
| public CommandBuilder CreateCommand(string cmd) | public CommandBuilder CreateCommand(string cmd) | ||||
| { | { | ||||
| var command = new Command(CommandBuilder.AppendPrefix(_prefix, 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 | internal class CommandMap | ||||
| { | { | ||||
| private readonly CommandMap _parent; | private readonly CommandMap _parent; | ||||
| private readonly string _text; | |||||
| private readonly string _name, _fullName; | |||||
| private Command _command; | private Command _command; | ||||
| private readonly Dictionary<string, CommandMap> _items; | private readonly Dictionary<string, CommandMap> _items; | ||||
| private bool _isHidden; | private bool _isHidden; | ||||
| public string Text => _text; | |||||
| public string Name => _name; | |||||
| public string FullName => _fullName; | |||||
| public bool IsHidden => _isHidden; | public bool IsHidden => _isHidden; | ||||
| public Command Command => _command; | public Command Command => _command; | ||||
| public IEnumerable<CommandMap> SubGroups => _items.Values; | public IEnumerable<CommandMap> SubGroups => _items.Values; | ||||
| /*public IEnumerable<Command> SubCommands => _items.Select(x => x.Value._command).Where(x => x != null); | /*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 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; | _parent = parent; | ||||
| _text = text; | |||||
| _items = new Dictionary<string, CommandMap>(); | |||||
| _name = name; | |||||
| _fullName = fullName; | |||||
| _items = new Dictionary<string, CommandMap>(); | |||||
| _isHidden = true; | _isHidden = true; | ||||
| } | } | ||||
| @@ -82,19 +84,20 @@ namespace Discord.Commands | |||||
| { | { | ||||
| AddCommand(0, text.Split(' '), command); | 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) | if (!command.IsHidden && _isHidden) | ||||
| _isHidden = false; | _isHidden = false; | ||||
| if (index != parts.Length) | if (index != parts.Length) | ||||
| { | { | ||||
| string nextPart = parts[index]; | |||||
| CommandMap nextGroup; | 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); | 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> | /// <summary> A Discord.Net client with extensions for handling common bot operations like text commands. </summary> | ||||
| public partial class CommandService : IService | public partial class CommandService : IService | ||||
| { | { | ||||
| private readonly CommandServiceConfig _config; | |||||
| private readonly CommandGroupBuilder _root; | |||||
| private DiscordClient _client; | private DiscordClient _client; | ||||
| public CommandServiceConfig Config { get; } | |||||
| public DiscordClient Client => _client; | |||||
| public CommandGroupBuilder Root => _root; | |||||
| //AllCommands store a flattened collection of all commands | //AllCommands store a flattened collection of all commands | ||||
| public IEnumerable<Command> AllCommands => _allCommands; | public IEnumerable<Command> AllCommands => _allCommands; | ||||
| @@ -23,40 +27,47 @@ namespace Discord.Commands | |||||
| internal IEnumerable<CommandMap> Categories => _categories.Values; | internal IEnumerable<CommandMap> Categories => _categories.Values; | ||||
| private readonly Dictionary<string, CommandMap> _categories; | private readonly Dictionary<string, CommandMap> _categories; | ||||
| public CommandService(CommandServiceConfig config) | public CommandService(CommandServiceConfig config) | ||||
| { | { | ||||
| Config = config; | |||||
| _config = config; | |||||
| _allCommands = new List<Command>(); | _allCommands = new List<Command>(); | ||||
| _map = new CommandMap(null, null); | |||||
| _map = new CommandMap(null, "", ""); | |||||
| _categories = new Dictionary<string, CommandMap>(); | _categories = new Dictionary<string, CommandMap>(); | ||||
| } | |||||
| _root = new CommandGroupBuilder(this, "", null); | |||||
| } | |||||
| void IService.Install(DiscordClient client) | void IService.Install(DiscordClient client) | ||||
| { | { | ||||
| _client = 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) | .Parameter("command", ParameterType.Multiple) | ||||
| .Hide() | .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 | if (e.Args.Length > 0) //Show command help | ||||
| { | { | ||||
| var map = _map.GetItem(string.Join(" ", e.Args)); | var map = _map.GetItem(string.Join(" ", e.Args)); | ||||
| if (map != null) | if (map != null) | ||||
| await ShowHelp(map, e.User, channel); | |||||
| await ShowCommandHelp(map, e.User, e.Channel, replyChannel); | |||||
| else | 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 | 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) => | client.MessageReceived += async (s, e) => | ||||
| @@ -68,7 +79,7 @@ namespace Discord.Commands | |||||
| if (msg.Length == 0) return; | if (msg.Length == 0) return; | ||||
| //Check for command char if one is provided | //Check for command char if one is provided | ||||
| var chars = Config.CommandChars; | |||||
| var chars = _config.CommandChars; | |||||
| if (chars.Length > 0) | if (chars.Length > 0) | ||||
| { | { | ||||
| if (!chars.Contains(msg[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(); | StringBuilder output = new StringBuilder(); | ||||
| /*output.AppendLine("These are the commands you can use:"); | /*output.AppendLine("These are the commands you can use:"); | ||||
| @@ -163,7 +174,7 @@ namespace Discord.Commands | |||||
| else | else | ||||
| output.Append(", "); | output.Append(", "); | ||||
| output.Append('`'); | output.Append('`'); | ||||
| output.Append(group.Text); | |||||
| output.Append(group.Name); | |||||
| if (group.SubGroups.Any()) | if (group.SubGroups.Any()) | ||||
| output.Append("*"); | output.Append("*"); | ||||
| output.Append('`'); | output.Append('`'); | ||||
| @@ -177,7 +188,7 @@ namespace Discord.Commands | |||||
| { | { | ||||
| output.Append("\n\n"); | output.Append("\n\n"); | ||||
| var chars = Config.CommandChars; | |||||
| var chars = _config.CommandChars; | |||||
| if (chars.Length > 0) | if (chars.Length > 0) | ||||
| { | { | ||||
| if (chars.Length == 1) | 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."); | 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(); | StringBuilder output = new StringBuilder(); | ||||
| Command cmd = map.Command; | Command cmd = map.Command; | ||||
| if (cmd != null) | if (cmd != null) | ||||
| ShowHelpInternal(cmd, user, channel, output); | |||||
| ShowCommandHelpInternal(cmd, user, channel, output); | |||||
| else | else | ||||
| { | { | ||||
| output.Append('`'); | output.Append('`'); | ||||
| output.Append(map.Text); | |||||
| output.Append(map.FullName); | |||||
| output.Append("`\n"); | output.Append("`\n"); | ||||
| } | } | ||||
| @@ -218,21 +229,21 @@ namespace Discord.Commands | |||||
| else | else | ||||
| output.Append(", "); | output.Append(", "); | ||||
| output.Append('`'); | output.Append('`'); | ||||
| output.Append(subCmd.Text); | |||||
| output.Append(subCmd.Name); | |||||
| if (subCmd.SubGroups.Any()) | if (subCmd.SubGroups.Any()) | ||||
| output.Append("*"); | output.Append("*"); | ||||
| 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(); | 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('`'); | ||||
| output.Append(command.Text); | output.Append(command.Text); | ||||
| @@ -261,17 +272,8 @@ namespace Discord.Commands | |||||
| output.AppendLine($"Aliases: `" + string.Join("`, `", command.Aliases) + '`'); | 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) | internal void AddCommand(Command command) | ||||
| { | { | ||||
| @@ -282,7 +284,7 @@ namespace Discord.Commands | |||||
| string categoryName = command.Category ?? ""; | string categoryName = command.Category ?? ""; | ||||
| if (!_categories.TryGetValue(categoryName, out category)) | if (!_categories.TryGetValue(categoryName, out category)) | ||||
| { | { | ||||
| category = new CommandMap(null, ""); | |||||
| category = new CommandMap(null, "", ""); | |||||
| _categories.Add(categoryName, category); | _categories.Add(categoryName, category); | ||||
| } | } | ||||
| @@ -1,4 +1,5 @@ | |||||
| using System; | |||||
| using Discord.Commands.Permissions; | |||||
| using System; | |||||
| namespace Discord.Commands | namespace Discord.Commands | ||||
| { | { | ||||
| @@ -13,9 +14,6 @@ namespace Discord.Commands | |||||
| } | } | ||||
| public class CommandServiceConfig | public class CommandServiceConfig | ||||
| { | { | ||||
| /*public Func<User, int> PermissionResolver { get { return _permissionsResolver; } set { SetValue(ref _permissionsResolver, value); } } | |||||
| private Func<User, int> _permissionsResolver;*/ | |||||
| public char? CommandChar | public char? CommandChar | ||||
| { | { | ||||
| get | get | ||||
| @@ -7,7 +7,7 @@ | |||||
| <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" /> | <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" /> | ||||
| <PropertyGroup Label="Globals"> | <PropertyGroup Label="Globals"> | ||||
| <ProjectGuid>19793545-ef89-48f4-8100-3ebaad0a9141</ProjectGuid> | <ProjectGuid>19793545-ef89-48f4-8100-3ebaad0a9141</ProjectGuid> | ||||
| <RootNamespace>Discord</RootNamespace> | |||||
| <RootNamespace>Discord.Commands</RootNamespace> | |||||
| <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath> | <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath> | ||||
| <OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath> | <OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath> | ||||
| </PropertyGroup> | </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> | <ProjectGuid>{3091164F-66AE-4543-A63D-167C1116241D}</ProjectGuid> | ||||
| <OutputType>Library</OutputType> | <OutputType>Library</OutputType> | ||||
| <AppDesignerFolder>Properties</AppDesignerFolder> | <AppDesignerFolder>Properties</AppDesignerFolder> | ||||
| <RootNamespace>Discord</RootNamespace> | |||||
| <RootNamespace>Discord.Modules</RootNamespace> | |||||
| <AssemblyName>Discord.Net.Commands</AssemblyName> | <AssemblyName>Discord.Net.Commands</AssemblyName> | ||||
| <FileAlignment>512</FileAlignment> | <FileAlignment>512</FileAlignment> | ||||
| <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> | <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> | ||||
| @@ -40,6 +40,9 @@ | |||||
| <Compile Include="..\Discord.Net.Modules\IModule.cs"> | <Compile Include="..\Discord.Net.Modules\IModule.cs"> | ||||
| <Link>IModule.cs</Link> | <Link>IModule.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net.Modules\ModuleChecker.cs"> | |||||
| <Link>ModuleChecker.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net.Modules\ModuleExtensions.cs"> | <Compile Include="..\Discord.Net.Modules\ModuleExtensions.cs"> | ||||
| <Link>ModuleExtensions.cs</Link> | <Link>ModuleExtensions.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| @@ -7,7 +7,7 @@ | |||||
| <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" /> | <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" /> | ||||
| <PropertyGroup Label="Globals"> | <PropertyGroup Label="Globals"> | ||||
| <ProjectGuid>01584e8a-78da-486f-9ef9-a894e435841b</ProjectGuid> | <ProjectGuid>01584e8a-78da-486f-9ef9-a894e435841b</ProjectGuid> | ||||
| <RootNamespace>Discord</RootNamespace> | |||||
| <RootNamespace>Discord.Modules</RootNamespace> | |||||
| <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath> | <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath> | ||||
| <OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath> | <OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath> | ||||
| </PropertyGroup> | </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; | _name = name; | ||||
| _id = name.ToLowerInvariant(); | _id = name.ToLowerInvariant(); | ||||
| _filterType = filterType; | _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); | _allowPrivate = filterType.HasFlag(FilterType.AllowPrivate); | ||||
| _enabledServers = new ConcurrentDictionary<string, Server>(); | _enabledServers = new ConcurrentDictionary<string, Server>(); | ||||
| @@ -115,6 +115,7 @@ namespace Discord.Modules | |||||
| commandService.CreateGroup(prefix, x => | commandService.CreateGroup(prefix, x => | ||||
| { | { | ||||
| x.Category(_name); | x.Category(_name); | ||||
| x.AddCheck(new ModuleChecker(this)); | |||||
| config(x); | config(x); | ||||
| }); | }); | ||||
| @@ -244,12 +245,12 @@ namespace Discord.Modules | |||||
| DisableAllChannels(); | DisableAllChannels(); | ||||
| } | } | ||||
| private bool HasServer(Server server) => | |||||
| internal bool HasServer(Server server) => | |||||
| _allowServerWhitelist && _enabledServers.ContainsKey(server.Id); | _allowServerWhitelist && _enabledServers.ContainsKey(server.Id); | ||||
| private bool HasIndirectServer(Server server) => | |||||
| internal bool HasIndirectServer(Server server) => | |||||
| (_allowServerWhitelist && _enabledServers.ContainsKey(server.Id)) || | (_allowServerWhitelist && _enabledServers.ContainsKey(server.Id)) || | ||||
| (_allowChannelWhitelist && _indirectServers.ContainsKey(server.Id)); | (_allowChannelWhitelist && _indirectServers.ContainsKey(server.Id)); | ||||
| private bool HasChannel(Channel channel) | |||||
| internal bool HasChannel(Channel channel) | |||||
| { | { | ||||
| if (channel.IsPrivate) return _allowPrivate; | if (channel.IsPrivate) return _allowPrivate; | ||||
| @@ -5,12 +5,12 @@ namespace Discord.Modules | |||||
| [Flags] | [Flags] | ||||
| public enum FilterType | 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> | /// <summary> Enables this module in all private messages. </summary> | ||||
| AllowPrivate = 0x4 | AllowPrivate = 0x4 | ||||
| } | } | ||||