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