From f742eff0268eba28f228db5b7e1a3fe690a450e0 Mon Sep 17 00:00:00 2001 From: Peter-MJ-Parker <34216187+Peter-MJ-Parker@users.noreply.github.com> Date: Sun, 7 Jul 2024 21:07:31 -0500 Subject: [PATCH 1/2] feat: Create subcommandPermCheck plugin This plugin was requested to be uploaded for basic sub command permission handling. Only available for slash commands and will fault out for other command types. --- plugins/subcommandPermCheck.ts | 110 +++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 plugins/subcommandPermCheck.ts diff --git a/plugins/subcommandPermCheck.ts b/plugins/subcommandPermCheck.ts new file mode 100644 index 0000000..4aad830 --- /dev/null +++ b/plugins/subcommandPermCheck.ts @@ -0,0 +1,110 @@ +//@ts-nocheck +/** + * @plugin + * Inspired by the plugin "requirePermission" created by Benzo-Fury & needhamgary, this plugin will set permissions for specific subcommands without manually writing it into the code. + * + * @author @Peter-MJ-Parker [<@371759410009341952>] + * @version 1.0 + * @example + * ```ts + * import { subcommandPermCheck } from "../plugins/subcommandPerms"; + * import { commandModule, CommandType } from "@sern/handler"; + * import { PermissionFlagBits } from "discord.js"; + * export default commandModule({ + * type: CommandType.Slash, + * plugins: [ + * subcommandPermCheck( + * [ + * { name: "string", perms: [PermissionFlagBits.Administrator] }, + * { name: "number", perms: [PermissionFlagBits.SendMessages, PermissionFlagBits.UseVAD] } + * ], + * all: true, //all = Require the member to have all perms stated or at least one? + * "OPTIONAL - respond to user with this message or default." + * ) + * ], + * execute: ({ interaction }) => { + * //your code here + * } + * }) + * ``` + * @end + */ + +/** Marked TODO's will be reconfigured with release of sern v4! **/ +import { type GuildMember, type PermissionResolvable, type TextChannel } from 'discord.js'; +import { type CommandType, CommandControlPlugin, controller } from '@sern/handler'; + +export const permsToString = (...perms: PermissionResolvable[]) => { + return new PermissionsBitField(perms) + .toArray() + .map((perm) => `\`${perm}\``) + .join(", "); +}; + +export function subcommandPermCheck(opts: Options) { + return CommandControlPlugin(async (ctx, [, args]) => { + if (!ctx.isSlash()) { + throw new Error('You provided a Text command.', { cause: "The plugin 'subcommandPermCheck' is meant for Slash commands only!" }); + //TODO: return state to command rather than error + return controller.stop(); + } + + const { interaction } = ctx; + if (interaction.guild === null) { + await interaction.reply({ + content: "This sub command cannot be used in DM's!", + ephemeral: true + }); + return controller.stop(); + } + const member = interaction.member as GuildMember; + const subcommands = opts.list; + let sub = args.getSubcommand(); + + /** WILL BE REWRITTEN WHEN SERN V4 IS RELEASED!!! **/ + for (const { name, perms } of subcommands) { + if (name !== sub) { + throw new Error("You provided a subcommand name which doesn't exist in given command.", { + cause: `${name} not found on command: ${interaction.commandName}.` + }); + //TODO: return state to command rather than error + return controller.stop(); + } else { + const each = permsToString(perms); + const { needAllPerms } = opts; + const memberPermissions = member.permissionsIn(interaction.channel as TextChannel); + if (needAllPerms === true) { + if (!memberPermissions.has(perms)) { + await interaction.reply({ + content: + opts.response ?? + `You are required to have all of the following permissions to run this subcommand in this channel:\n${each}`, + ephemeral: true + }); + //TODO: return state to command + return controller.stop(); + } + } else { + if (!memberPermissions.any(perms)) { + await interaction.reply({ + content: + opts.response ?? + `You are required to have at least one of the following permissions to run this subcommand in this channel:\n${each}`, + ephemeral: true + }); + //TODO: return state to command + return controller.stop(); + } + } + } + } + //TODO: return state to command + return controller.next(); + }); +} + +interface Options { + list: { name: string; perms: PermissionResolvable[] }[]; + needAllPerms: boolean; + response?: string; +} From 34af2ab405c018a8fe596c6cc1038e6365ddfc8d Mon Sep 17 00:00:00 2001 From: Peter-MJ-Parker <34216187+Peter-MJ-Parker@users.noreply.github.com> Date: Tue, 9 Jul 2024 21:57:20 -0500 Subject: [PATCH 2/2] edit: resolve conflicts Removed deconstructed interaction Made errors more understandable Corrected the example to use a parameter as the first object and input correct properties. --- plugins/subcommandPermCheck.ts | 35 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/plugins/subcommandPermCheck.ts b/plugins/subcommandPermCheck.ts index 4aad830..2556efc 100644 --- a/plugins/subcommandPermCheck.ts +++ b/plugins/subcommandPermCheck.ts @@ -7,20 +7,20 @@ * @version 1.0 * @example * ```ts - * import { subcommandPermCheck } from "../plugins/subcommandPerms"; + * import { subcommandPermCheck } from "../plugins/subcommandPerms.js"; * import { commandModule, CommandType } from "@sern/handler"; * import { PermissionFlagBits } from "discord.js"; * export default commandModule({ * type: CommandType.Slash, * plugins: [ - * subcommandPermCheck( - * [ + * subcommandPermCheck({ + * list: [ * { name: "string", perms: [PermissionFlagBits.Administrator] }, * { name: "number", perms: [PermissionFlagBits.SendMessages, PermissionFlagBits.UseVAD] } * ], - * all: true, //all = Require the member to have all perms stated or at least one? - * "OPTIONAL - respond to user with this message or default." - * ) + * needAllPerms: true, //all = Require the member to have all perms stated or at least one? + * //response: "OPTIONAL - respond to user with this message or default." + * }) * ], * execute: ({ interaction }) => { * //your code here @@ -31,7 +31,7 @@ */ /** Marked TODO's will be reconfigured with release of sern v4! **/ -import { type GuildMember, type PermissionResolvable, type TextChannel } from 'discord.js'; +import type { GuildMember, PermissionResolvable, TextChannel } from 'discord.js'; import { type CommandType, CommandControlPlugin, controller } from '@sern/handler'; export const permsToString = (...perms: PermissionResolvable[]) => { @@ -42,40 +42,39 @@ export const permsToString = (...perms: PermissionResolvable[]) => { }; export function subcommandPermCheck(opts: Options) { - return CommandControlPlugin(async (ctx, [, args]) => { + return CommandControlPlugin(async (ctx) => { if (!ctx.isSlash()) { - throw new Error('You provided a Text command.', { cause: "The plugin 'subcommandPermCheck' is meant for Slash commands only!" }); + throw new Error('You did not provide a slash command.', { cause: "The plugin 'subcommandPermCheck' is meant for Slash commands only!" }); //TODO: return state to command rather than error return controller.stop(); } - const { interaction } = ctx; - if (interaction.guild === null) { - await interaction.reply({ + if (ctx.guild === null) { + await ctx.reply({ content: "This sub command cannot be used in DM's!", ephemeral: true }); return controller.stop(); } - const member = interaction.member as GuildMember; + const member = ctx.member as GuildMember; const subcommands = opts.list; - let sub = args.getSubcommand(); + let sub = ctx.options.getSubcommand(); /** WILL BE REWRITTEN WHEN SERN V4 IS RELEASED!!! **/ for (const { name, perms } of subcommands) { if (name !== sub) { throw new Error("You provided a subcommand name which doesn't exist in given command.", { - cause: `${name} not found on command: ${interaction.commandName}.` + cause: `subcommand: \`${name}\` not found on command: ${ctx.interaction.commandName}.` }); //TODO: return state to command rather than error return controller.stop(); } else { const each = permsToString(perms); const { needAllPerms } = opts; - const memberPermissions = member.permissionsIn(interaction.channel as TextChannel); + const memberPermissions = member.permissionsIn(ctx.channel as TextChannel); if (needAllPerms === true) { if (!memberPermissions.has(perms)) { - await interaction.reply({ + await ctx.reply({ content: opts.response ?? `You are required to have all of the following permissions to run this subcommand in this channel:\n${each}`, @@ -86,7 +85,7 @@ export function subcommandPermCheck(opts: Options) { } } else { if (!memberPermissions.any(perms)) { - await interaction.reply({ + await ctx.reply({ content: opts.response ?? `You are required to have at least one of the following permissions to run this subcommand in this channel:\n${each}`,