mirror of
https://github.com/arthur-pbty/flint.git
synced 2026-08-01 20:29:03 +02:00
feat: ajouter la gestion du cooldown par commande et mise à jour des traductions
This commit is contained in:
@@ -4,6 +4,7 @@ Professional command framework template for Discord.js `14.26.2` with:
|
||||
|
||||
- Single command object schema
|
||||
- Minimal command authoring (`name`, `category`, `execute`)
|
||||
- Optional per-user command cooldown (`cooldown` in seconds)
|
||||
- Shared execution logic for prefix and slash
|
||||
- External JSON i18n
|
||||
- Automatic prefix and slash localizations from locale files
|
||||
@@ -69,13 +70,13 @@ An example with two bot services is available in `docker-compose.multi-bot.examp
|
||||
- `src/framework/commands/defineCommand.ts`: default command completion
|
||||
- `src/framework/commands/registry.ts`: trigger/name mapping generated from locales
|
||||
- `src/framework/commands/argParser.ts`: prefix/slash args parsing from schema
|
||||
- `src/framework/execution/CommandExecutor.ts`: unified pipeline (permissions/execute)
|
||||
- `src/framework/execution/CommandExecutor.ts`: unified pipeline (permissions/cooldown/execute)
|
||||
- `src/framework/handlers/prefixHandler.ts`: prefix entrypoint
|
||||
- `src/framework/handlers/slashHandler.ts`: slash entrypoint
|
||||
- `src/framework/presence/presenceStore.ts`: PostgreSQL presence storage
|
||||
- `src/framework/presence/presenceTypes.ts`: shared presence types/validation
|
||||
- `locales/*.json`: external i18n dictionaries
|
||||
- `src/commands/*`: business commands only (`execute`)
|
||||
- `src/commands/*`: business commands only (`execute`, optional `cooldown`)
|
||||
|
||||
## Included Commands
|
||||
|
||||
|
||||
+2
-1
@@ -11,7 +11,8 @@
|
||||
"permissions": {
|
||||
"user": "You are missing permissions: {{permissions}}"
|
||||
},
|
||||
"execution": "An unexpected error happened while running this command."
|
||||
"execution": "An unexpected error happened while running this command.",
|
||||
"cooldown": "Please wait {{seconds}}s before using this command again."
|
||||
},
|
||||
"categories": {
|
||||
"fun": "Fun",
|
||||
|
||||
+2
-1
@@ -11,7 +11,8 @@
|
||||
"permissions": {
|
||||
"user": "Te faltan permisos: {{permissions}}"
|
||||
},
|
||||
"execution": "Ocurrio un error inesperado al ejecutar este comando."
|
||||
"execution": "Ocurrio un error inesperado al ejecutar este comando.",
|
||||
"cooldown": "Espera {{seconds}}s antes de volver a usar este comando."
|
||||
},
|
||||
"categories": {
|
||||
"fun": "Diversion",
|
||||
|
||||
+2
-1
@@ -11,7 +11,8 @@
|
||||
"permissions": {
|
||||
"user": "Tu n as pas les permissions: {{permissions}}"
|
||||
},
|
||||
"execution": "Une erreur inattendue est survenue pendant l execution."
|
||||
"execution": "Une erreur inattendue est survenue pendant l execution.",
|
||||
"cooldown": "Patiente {{seconds}}s avant de reutiliser cette commande."
|
||||
},
|
||||
"categories": {
|
||||
"fun": "Fun",
|
||||
|
||||
@@ -6,6 +6,7 @@ export const pingCommand = defineCommand({
|
||||
name: "ping",
|
||||
category: "utility",
|
||||
},
|
||||
cooldown: 5,
|
||||
examples: [
|
||||
{
|
||||
descriptionKey: "examples.basic",
|
||||
|
||||
@@ -18,14 +18,30 @@ const assertRequiredArgsBeforeOptional = (input: BotCommandInput): void => {
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeCooldown = (input: BotCommandInput): number | undefined => {
|
||||
if (input.cooldown === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!Number.isFinite(input.cooldown) || input.cooldown <= 0) {
|
||||
throw new Error(
|
||||
`Invalid cooldown for command "${input.meta.name}": expected a positive number of seconds, received "${input.cooldown}".`,
|
||||
);
|
||||
}
|
||||
|
||||
return input.cooldown;
|
||||
};
|
||||
|
||||
export const defineCommand = (input: BotCommandInput): BotCommand => {
|
||||
assertRequiredArgsBeforeOptional(input);
|
||||
const cooldown = normalizeCooldown(input);
|
||||
|
||||
return {
|
||||
meta: input.meta,
|
||||
args: [...(input.args ?? [])],
|
||||
permissions: input.permissions ?? [],
|
||||
examples: input.examples ?? [],
|
||||
...(cooldown !== undefined ? { cooldown } : {}),
|
||||
execute: input.execute,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
import type { BotCommand, CommandExecutionContext } from "../types/command.js";
|
||||
|
||||
export class CommandExecutor {
|
||||
private readonly cooldowns = new Map<string, number>();
|
||||
|
||||
public async run(command: BotCommand, ctx: CommandExecutionContext): Promise<void> {
|
||||
const missingUserPermissions = this.getMissingPermissions(command.permissions, this.memberPermissions(ctx));
|
||||
if (missingUserPermissions.length > 0) {
|
||||
@@ -15,6 +17,12 @@ export class CommandExecutor {
|
||||
return;
|
||||
}
|
||||
|
||||
const remainingCooldownSeconds = this.consumeCooldown(command, ctx.user.id);
|
||||
if (remainingCooldownSeconds > 0) {
|
||||
await ctx.reply(ctx.t("errors.cooldown", { seconds: remainingCooldownSeconds }));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await command.execute(ctx);
|
||||
} catch (error) {
|
||||
@@ -73,4 +81,25 @@ export class CommandExecutor {
|
||||
.replace(/_/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
private consumeCooldown(command: BotCommand, userId: string): number {
|
||||
if (command.cooldown === undefined || command.cooldown <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const key = this.cooldownKey(command.meta.name, userId);
|
||||
const now = Date.now();
|
||||
const expiresAt = this.cooldowns.get(key);
|
||||
|
||||
if (expiresAt !== undefined && expiresAt > now) {
|
||||
return Math.ceil((expiresAt - now) / 1000);
|
||||
}
|
||||
|
||||
this.cooldowns.set(key, now + command.cooldown * 1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private cooldownKey(commandName: string, userId: string): string {
|
||||
return `${commandName}:${userId}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ export interface BotCommandInput {
|
||||
args?: CommandArgument[];
|
||||
permissions?: PermissionResolvable[];
|
||||
examples?: CommandExample[];
|
||||
cooldown?: number;
|
||||
execute: (ctx: CommandExecutionContext) => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -106,5 +107,6 @@ export interface BotCommand {
|
||||
args: CommandArgument[];
|
||||
permissions: PermissionResolvable[];
|
||||
examples: CommandExample[];
|
||||
cooldown?: number;
|
||||
execute: (ctx: CommandExecutionContext) => Promise<void>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user