From fdecfe6bd4adcd0fc1d01948cf996ac0c8039eac Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 25 Nov 2016 17:44:37 -0500 Subject: [PATCH 1/4] Add RequireOwner Precondition This precondition will require that the invoker of the command is the owner of the bot. --- .../Preconditions/RequireOwnerAttribute.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs new file mode 100644 index 000000000..e03e3971b --- /dev/null +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Discord; + +namespace Discord.Commands +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] + public class RequireOwnerAttribute : PreconditionAttribute + { + private IApplication application; + + public override async Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) + { + if (application == null) + application = await context.Client.GetApplicationInfoAsync(); + if (context.User.Id == application.Owner.Id) return PreconditionResult.FromSuccess(); + return PreconditionResult.FromError("Command can only be run by the owner of the bot"); + } + } +} From ea148db08b2c2fa643167a4d0703113ee04a44ab Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 25 Nov 2016 17:48:08 -0500 Subject: [PATCH 2/4] Add RequireBotPermission, rename RequirePermission This is a breaking change. Adds a precondition that requires the bot has a specified permission (Resolves #211). Renames RequirePermission to RequireUserPermission. --- .../RequireBotPermissionAttribute.cs | 54 +++++++++++++++++++ ...e.cs => RequireUserPermissionAttribute.cs} | 6 +-- 2 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs rename src/Discord.Net.Commands/Attributes/Preconditions/{RequirePermissionAttribute.cs => RequireUserPermissionAttribute.cs} (89%) diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs new file mode 100644 index 000000000..d3c598591 --- /dev/null +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Discord.Commands +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] + public class RequireBotPermissionAttribute : PreconditionAttribute + { + public GuildPermission? GuildPermission { get; } + public ChannelPermission? ChannelPermission { get; } + + public RequireBotPermissionAttribute(GuildPermission permission) + { + GuildPermission = permission; + ChannelPermission = null; + } + public RequireBotPermissionAttribute(ChannelPermission permission) + { + ChannelPermission = permission; + GuildPermission = null; + } + + public override async Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) + { + var guildUser = await context.Guild.GetCurrentUserAsync(); + + if (GuildPermission.HasValue) + { + if (guildUser == null) + return PreconditionResult.FromError("Command must be used in a guild channel"); + if (!guildUser.GuildPermissions.Has(GuildPermission.Value)) + return PreconditionResult.FromError($"Command requires guild permission {GuildPermission.Value}"); + } + + if (ChannelPermission.HasValue) + { + var guildChannel = context.Channel as IGuildChannel; + + ChannelPermissions perms; + if (guildChannel != null) + perms = guildUser.GetPermissions(guildChannel); + else + perms = ChannelPermissions.All(guildChannel); + + if (!perms.Has(ChannelPermission.Value)) + return PreconditionResult.FromError($"Command requires channel permission {ChannelPermission.Value}"); + } + + return PreconditionResult.FromSuccess(); + } + } +} diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequirePermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs similarity index 89% rename from src/Discord.Net.Commands/Attributes/Preconditions/RequirePermissionAttribute.cs rename to src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs index 26aeac5ec..2e19b61cf 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequirePermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs @@ -4,17 +4,17 @@ using System.Threading.Tasks; namespace Discord.Commands { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] - public class RequirePermissionAttribute : PreconditionAttribute + public class RequireUserPermissionAttribute : PreconditionAttribute { public GuildPermission? GuildPermission { get; } public ChannelPermission? ChannelPermission { get; } - public RequirePermissionAttribute(GuildPermission permission) + public RequireUserPermissionAttribute(GuildPermission permission) { GuildPermission = permission; ChannelPermission = null; } - public RequirePermissionAttribute(ChannelPermission permission) + public RequireUserPermissionAttribute(ChannelPermission permission) { ChannelPermission = permission; GuildPermission = null; From defc8f1c4eeee8302ec30549a021abfa5649beb0 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 25 Nov 2016 18:09:18 -0500 Subject: [PATCH 3/4] Add docstrings to preconditions. --- .../RequireBotPermissionAttribute.cs | 21 ++++++++++++++++++ .../Preconditions/RequireContextAttribute.cs | 16 ++++++++++++++ .../Preconditions/RequireOwnerAttribute.cs | 4 ++++ .../RequireUserPermissionAttribute.cs | 22 +++++++++++++++++++ 4 files changed, 63 insertions(+) diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs index d3c598591..914978192 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs @@ -5,17 +5,38 @@ using System.Threading.Tasks; namespace Discord.Commands { + /// + /// This attribute requires that the bot has a speicifed permission in the channel a command is invoked in. + /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class RequireBotPermissionAttribute : PreconditionAttribute { public GuildPermission? GuildPermission { get; } public ChannelPermission? ChannelPermission { get; } + /// + /// Require that the bot account has a specified GuildPermission + /// + /// This precondition will always fail if the command is being invoked in a private channel. + /// The GuildPermission that the bot must have. Multiple permissions can be specified by ORing or ANDing the permissions together. public RequireBotPermissionAttribute(GuildPermission permission) { GuildPermission = permission; ChannelPermission = null; } + /// + /// Require that the bot account has a specified ChannelPermission. + /// + /// The ChannelPermission that the bot must have. Multiple permissions can be specified by ORing or ANDing the permissions together. + /// + /// + /// [Command("permission")] + /// [RequireBotPermission(ChannelPermission.ManageMessages)] + /// public async Task Purge() + /// { + /// } + /// + /// public RequireBotPermissionAttribute(ChannelPermission permission) { ChannelPermission = permission; diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs index 1cd32e72e..beadbbc89 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs @@ -11,11 +11,27 @@ namespace Discord.Commands Group = 0x04 } + /// + /// Require that the command be invoked in a specified context. + /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class RequireContextAttribute : PreconditionAttribute { public ContextType Contexts { get; } + /// + /// Require that the command be invoked in a specified context. + /// + /// The type of context the command can be invoked in. Multiple contexts can be speicifed by ORing the contexts together. + /// + /// + /// [Command("private_only")] + /// [RequireContext(ContextType.DM | ContextType.Group)] + /// public async Task PrivateOnly() + /// { + /// } + /// + /// public RequireContextAttribute(ContextType contexts) { Contexts = contexts; diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs index e03e3971b..beb8dfdd0 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs @@ -6,6 +6,10 @@ using Discord; namespace Discord.Commands { + /// + /// Require that the command is invoked by the owner of the bot. + /// + /// This precondition will only work if the bot is a bot account. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class RequireOwnerAttribute : PreconditionAttribute { diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs index 2e19b61cf..17cf234aa 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs @@ -3,17 +3,39 @@ using System.Threading.Tasks; namespace Discord.Commands { + /// + /// This attribute requires that the user invoking the command has a specified permission. + /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] public class RequireUserPermissionAttribute : PreconditionAttribute { public GuildPermission? GuildPermission { get; } public ChannelPermission? ChannelPermission { get; } + /// + /// Require that the user invoking the command has a specified GuildPermission + /// + /// This precondition will always fail if the command is being invoked in a private channel. + /// The GuildPermission that the user must have. Multiple permissions can be specified by ORing or ANDing the permissions together. public RequireUserPermissionAttribute(GuildPermission permission) { GuildPermission = permission; ChannelPermission = null; } + /// + /// Require that the user invoking the command has a specified ChannelPermission. + /// + /// The ChannelPermission that the user must have. Multiple permissions can be specified by ORing or ANDing the permissions together. + /// + /// + /// [Command("permission")] + /// [RequireUserPermission(ChannelPermission.ReadMessageHistory & ChannelPermission.ReadMessages)] + /// public async Task HasPermission() + /// { + /// await ReplyAsync("You can read messages and the message history!"); + /// } + /// + /// public RequireUserPermissionAttribute(ChannelPermission permission) { ChannelPermission = permission; From 1ed4f703bf61b9ab8af7c3d64d266154265fc236 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 26 Nov 2016 14:59:20 -0500 Subject: [PATCH 4/4] Cache the current application on DiscordSocketClient --- .../Attributes/Preconditions/RequireOwnerAttribute.cs | 5 +---- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 7 +++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs index beb8dfdd0..8992cd115 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs @@ -13,12 +13,9 @@ namespace Discord.Commands [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class RequireOwnerAttribute : PreconditionAttribute { - private IApplication application; - public override async Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) { - if (application == null) - application = await context.Client.GetApplicationInfoAsync(); + var application = await context.Client.GetApplicationInfoAsync(); if (context.User.Id == application.Owner.Id) return PreconditionResult.FromSuccess(); return PreconditionResult.FromError("Command can only be run by the owner of the bot"); } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 3eb4158d1..8c6a37f3c 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -38,6 +38,7 @@ namespace Discord.WebSocket private int _nextAudioId; private bool _canReconnect; private DateTimeOffset? _statusSince; + private RestApplication _application; /// Gets the shard of of this client. public int ShardId { get; } @@ -333,8 +334,10 @@ namespace Discord.WebSocket } /// - public Task GetApplicationInfoAsync() - => ClientHelper.GetApplicationInfoAsync(this); + public async Task GetApplicationInfoAsync() + { + return _application ?? (_application = await ClientHelper.GetApplicationInfoAsync(this)); + } /// public SocketGuild GetGuild(ulong id)