Implement command cooldown management and enhance command execution flow

- 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.
This commit is contained in:
TheOnlyMace
2026-07-25 15:37:56 +02:00
parent 57cdf847f5
commit 8513848440
42 changed files with 856 additions and 160 deletions

View File

@@ -1,14 +1,49 @@
import { type ChatInputCommandInteraction } from 'discord.js';
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,
permission: bigint,
locale: Locale
memberPermission: bigint,
locale: Locale,
options?: RequirePermissionOptions
): Promise<boolean> {
const member = interaction.memberPermissions;
if (!member?.has(permission)) {
if (!member?.has(memberPermission)) {
await interaction.reply({
content: t(locale, 'generic.noPermission'),
ephemeral: true
@@ -16,16 +51,36 @@ export async function requirePermission(
return false;
}
if (!interaction.guild?.members.me?.permissions.has(permission)) {
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}',
permission.toString()
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;
}