From 1a5cba875d349a983d53a0881558d7798cd23de3 Mon Sep 17 00:00:00 2001 From: zobweyt <98274273+zobweyt@users.noreply.github.com> Date: Sun, 12 May 2024 01:27:15 +0300 Subject: [PATCH] [Feature] Parameter precondition attribute for simplifying performing hierarchical operations within a guild (#2906) * Support interaction framework and update bundled preconditions docs * Support text commands and update bundled preconditions docs * Fix example * Move hierarchy util to `PermissionUtils` * Refactoring --- docs/guides/int_framework/preconditions.md | 1 + docs/guides/text_commands/preconditions.md | 1 + .../DoHierarchyCheckAttribute.cs | 49 +++++++++++++++++++ src/Discord.Net.Core/Utils/PermissionUtils.cs | 31 ++++++++++++ .../DoHierarchyCheckAttribute.cs | 48 ++++++++++++++++++ 5 files changed, 130 insertions(+) create mode 100644 src/Discord.Net.Commands/Attributes/Preconditions/DoHierarchyCheckAttribute.cs create mode 100644 src/Discord.Net.Core/Utils/PermissionUtils.cs create mode 100644 src/Discord.Net.Interactions/Attributes/Preconditions/DoHierarchyCheckAttribute.cs diff --git a/docs/guides/int_framework/preconditions.md b/docs/guides/int_framework/preconditions.md index bfa7168e..9573ff61 100644 --- a/docs/guides/int_framework/preconditions.md +++ b/docs/guides/int_framework/preconditions.md @@ -30,6 +30,7 @@ to use. * @Discord.Interactions.RequireNsfwAttribute * @Discord.Interactions.RequireRoleAttribute * @Discord.Interactions.RequireTeamAttribute +* @Discord.Interactions.DoHierarchyCheckAttribute ## Using Preconditions diff --git a/docs/guides/text_commands/preconditions.md b/docs/guides/text_commands/preconditions.md index 4be7ca2b..86ddccbb 100644 --- a/docs/guides/text_commands/preconditions.md +++ b/docs/guides/text_commands/preconditions.md @@ -29,6 +29,7 @@ to use. * @Discord.Commands.RequireBotPermissionAttribute * @Discord.Commands.RequireUserPermissionAttribute * @Discord.Commands.RequireNsfwAttribute +* @Discord.Commands.DoHierarchyCheckAttribute ## Using Preconditions diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/DoHierarchyCheckAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/DoHierarchyCheckAttribute.cs new file mode 100644 index 00000000..615bfea6 --- /dev/null +++ b/src/Discord.Net.Commands/Attributes/Preconditions/DoHierarchyCheckAttribute.cs @@ -0,0 +1,49 @@ +using System; +using System.Threading.Tasks; + +namespace Discord.Commands +{ + /// + /// Ensures that command parameters are passed within a correct hierarchical context. + /// + /// + /// Useful for performing hierarchical operations within a guild, such as managing roles or users. + /// + /// This supports , , and parameter types. + /// + /// + /// + /// Thrown when the parameter type is not supported by this precondition attribute. + /// + /// + /// + public class DoHierarchyCheckAttribute : ParameterPreconditionAttribute + { + /// + /// Gets or sets the error message displayed when the command is used outside of a guild. + /// + public string NotAGuildErrorMessage { get; set; } = "This command cannot be used outside of a guild."; + + /// + /// Gets the error message to be returned if execution context doesn't pass the precondition check. + /// + public string ErrorMessage { get; set; } = "You cannot target anyone who is higher or equal in the hierarchy to you or the bot."; + + /// + /// + /// Thrown when the parameter type is not supported by this precondition attribute. + /// + public override async Task CheckPermissionsAsync(ICommandContext context, ParameterInfo parameterInfo, object value, IServiceProvider services) + { + if (context.User is not IGuildUser guildUser) + return PreconditionResult.FromError(NotAGuildErrorMessage); + + var hieararchy = PermissionUtils.GetHieararchy(value); + if (hieararchy >= guildUser.Hierarchy || + hieararchy >= (await context.Guild.GetCurrentUserAsync().ConfigureAwait(false)).Hierarchy) + return PreconditionResult.FromError(ErrorMessage); + + return PreconditionResult.FromSuccess(); + } + } +} diff --git a/src/Discord.Net.Core/Utils/PermissionUtils.cs b/src/Discord.Net.Core/Utils/PermissionUtils.cs new file mode 100644 index 00000000..edd159d0 --- /dev/null +++ b/src/Discord.Net.Core/Utils/PermissionUtils.cs @@ -0,0 +1,31 @@ +using System; + +namespace Discord +{ + /// + /// Provides a series of helper methods for permissions. + /// + public static class PermissionUtils + { + /// + /// Determines the hierarchy of a target object based on its type. + /// + /// + /// The target object: , , or . + /// + /// + /// An integer representing the hierarchy of the target. + /// + /// + /// Thrown when the parameter type is not supported by this precondition attribute. + /// + public static int GetHieararchy(object target) => target switch + { + // The order of cases here is important to determine the correct hierarchy value. + IRole role => role.Position, + IGuildUser guildUser => guildUser.Hierarchy, + IUser => int.MinValue, + _ => throw new ArgumentOutOfRangeException(nameof(target), "Cannot determine hierarchy for the provided target.") + }; + } +} diff --git a/src/Discord.Net.Interactions/Attributes/Preconditions/DoHierarchyCheckAttribute.cs b/src/Discord.Net.Interactions/Attributes/Preconditions/DoHierarchyCheckAttribute.cs new file mode 100644 index 00000000..180bc393 --- /dev/null +++ b/src/Discord.Net.Interactions/Attributes/Preconditions/DoHierarchyCheckAttribute.cs @@ -0,0 +1,48 @@ +using System; +using System.Threading.Tasks; + +namespace Discord.Interactions +{ + /// + /// Ensures that command parameters are passed within a correct hierarchical context. + /// + /// + /// Useful for performing hierarchical operations within a guild, such as managing roles or users. + /// + /// This supports , , and parameter types. + /// + /// + /// + /// Thrown when the parameter type is not supported by this precondition attribute. + /// + /// + /// + /// + public class DoHierarchyCheckAttribute : ParameterPreconditionAttribute + { + /// + /// Gets or sets the error message displayed when the command is used outside of a guild. + /// + public string NotAGuildErrorMessage { get; set; } = "This command cannot be used outside of a guild."; + + /// + public override string ErrorMessage => "You cannot target anyone who is higher or equal in the hierarchy to you or the bot."; + + /// + /// + /// Thrown when the parameter type is not supported by this precondition attribute. + /// + public override async Task CheckRequirementsAsync(IInteractionContext context, IParameterInfo parameterInfo, object value, IServiceProvider services) + { + if (context.User is not IGuildUser guildUser) + return PreconditionResult.FromError(NotAGuildErrorMessage); + + var hieararchy = PermissionUtils.GetHieararchy(value); + if (hieararchy >= guildUser.Hierarchy || + hieararchy >= (await context.Guild.GetCurrentUserAsync().ConfigureAwait(false)).Hierarchy) + return PreconditionResult.FromError(ErrorMessage); + + return PreconditionResult.FromSuccess(); + } + } +}