import { PermissionFlagsBits, type ChatInputCommandInteraction } from 'discord.js'; import { t } from '@nexumi/shared'; import type { Locale } from '@nexumi/shared'; const PERMISSION_LABELS: Partial> = { [PermissionFlagsBits.Administrator.toString()]: 'Administrator', [PermissionFlagsBits.ManageGuild.toString()]: 'Manage Server', [PermissionFlagsBits.ManageChannels.toString()]: 'Manage Channels', [PermissionFlagsBits.ManageRoles.toString()]: 'Manage Roles', [PermissionFlagsBits.ManageMessages.toString()]: 'Manage Messages', [PermissionFlagsBits.ManageNicknames.toString()]: 'Manage Nicknames', [PermissionFlagsBits.ManageGuildExpressions.toString()]: 'Manage Expressions', [PermissionFlagsBits.KickMembers.toString()]: 'Kick Members', [PermissionFlagsBits.BanMembers.toString()]: 'Ban Members', [PermissionFlagsBits.ModerateMembers.toString()]: 'Timeout Members', [PermissionFlagsBits.ViewChannel.toString()]: 'View Channel', [PermissionFlagsBits.SendMessages.toString()]: 'Send Messages', [PermissionFlagsBits.EmbedLinks.toString()]: 'Embed Links', [PermissionFlagsBits.AttachFiles.toString()]: 'Attach Files', [PermissionFlagsBits.ReadMessageHistory.toString()]: 'Read Message History' }; function permissionLabel(permission: bigint): string { return PERMISSION_LABELS[permission.toString()] ?? permission.toString(); } export type RequirePermissionOptions = { /** * Discord permissions the bot itself must hold. Omit for dashboard-style * member gates (e.g. ManageGuild) where the bot does not need the same bit. */ botPermissions?: bigint | readonly bigint[]; }; /** * Ensures the invoking member has `memberPermission`. Optionally also checks * that the bot has one or more `botPermissions` (moderation actions). */ export async function requirePermission( interaction: ChatInputCommandInteraction, memberPermission: bigint, locale: Locale, options?: RequirePermissionOptions ): Promise { const member = interaction.memberPermissions; if (!member?.has(memberPermission)) { await interaction.reply({ content: t(locale, 'generic.noPermission'), ephemeral: true }); return false; } const botBits = options?.botPermissions; if (botBits === undefined) { return true; } const required = Array.isArray(botBits) ? botBits : [botBits]; const me = interaction.guild?.members.me; if (!me) { await interaction.reply({ content: t(locale, 'generic.botMissingPermission').replace( '{permission}', required.map(permissionLabel).join(', ') ), ephemeral: true }); return false; } for (const bit of required) { if (!me.permissions.has(bit)) { await interaction.reply({ content: t(locale, 'generic.botMissingPermission').replace( '{permission}', permissionLabel(bit) ), ephemeral: true }); return false; } } return true; }