- Introduced `applyCommandCooldown` function to manage per-command cooldowns after execution, improving command rate limiting. - Updated command execution flow to include cooldown application, ensuring users are informed of cooldown status. - Enhanced error handling in interaction replies, allowing for more graceful error management and user feedback. - Improved the handling of interaction responses across various commands, ensuring consistent user experience. - Added permission checks for new commands, ensuring only authorized users can execute specific actions.
87 lines
2.9 KiB
TypeScript
87 lines
2.9 KiB
TypeScript
import { PermissionFlagsBits, type ChatInputCommandInteraction } from 'discord.js';
|
|
import { t } from '@nexumi/shared';
|
|
import type { Locale } from '@nexumi/shared';
|
|
|
|
const PERMISSION_LABELS: Partial<Record<string, string>> = {
|
|
[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<boolean> {
|
|
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;
|
|
}
|