diff --git a/locales/en.json b/locales/en.json index 6241eb0..f6f4213 100644 --- a/locales/en.json +++ b/locales/en.json @@ -5,7 +5,8 @@ "invalidInt": "Argument must be an integer. Received: {{value}}", "invalidNumber": "Argument must be a number. Received: {{value}}", "invalidBoolean": "Argument must be a boolean value. Received: {{value}}", - "invalidUser": "Could not resolve a user from {{value}}" + "invalidUser": "Could not resolve a user from {{value}}", + "tooMany": "Too many arguments detected ({{count}}): {{extras}}. Usage: {{usage}}" }, "permissions": { "user": "You are missing permissions: {{permissions}}" diff --git a/locales/es.json b/locales/es.json index d4412b7..c9f7a5f 100644 --- a/locales/es.json +++ b/locales/es.json @@ -5,7 +5,8 @@ "invalidInt": "El argumento debe ser un entero. Recibido: {{value}}", "invalidNumber": "El argumento debe ser un numero. Recibido: {{value}}", "invalidBoolean": "El argumento debe ser un valor booleano. Recibido: {{value}}", - "invalidUser": "No se pudo resolver un usuario desde {{value}}" + "invalidUser": "No se pudo resolver un usuario desde {{value}}", + "tooMany": "Se detectaron demasiados argumentos ({{count}}): {{extras}}. Uso: {{usage}}" }, "permissions": { "user": "Te faltan permisos: {{permissions}}" diff --git a/locales/fr.json b/locales/fr.json index a49c4d3..da49567 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -5,7 +5,8 @@ "invalidInt": "L argument doit etre un entier. Recu: {{value}}", "invalidNumber": "L argument doit etre un nombre. Recu: {{value}}", "invalidBoolean": "L argument doit etre un booleen. Recu: {{value}}", - "invalidUser": "Impossible de trouver un utilisateur depuis {{value}}" + "invalidUser": "Impossible de trouver un utilisateur depuis {{value}}", + "tooMany": "Trop d arguments ({{count}}) detectes: {{extras}}. Usage: {{usage}}" }, "permissions": { "user": "Tu n as pas les permissions: {{permissions}}" diff --git a/src/commands/utility/advanced.ts b/src/commands/utility/advanced.ts index fe25166..f82cc3b 100644 --- a/src/commands/utility/advanced.ts +++ b/src/commands/utility/advanced.ts @@ -43,6 +43,12 @@ export const advancedCommand = defineCommand({ required: true, descriptionKey: "args.count", }, + { + name: "user", + type: "user", + required: true, + descriptionKey: "args.user", + }, { name: "ratio", type: "number", @@ -55,12 +61,6 @@ export const advancedCommand = defineCommand({ required: false, descriptionKey: "args.enabled", }, - { - name: "user", - type: "user", - required: true, - descriptionKey: "args.user", - }, { name: "channel", type: "channel", @@ -76,7 +76,7 @@ export const advancedCommand = defineCommand({ ], examples: [ { - args: '"hello world" 5 1.5 true @Arthur #general @Moderators', + args: '"hello world" 5 @Arthur 1.5 true #general @Moderators', descriptionKey: "examples.full", }, { diff --git a/src/framework/commands/argParser.ts b/src/framework/commands/argParser.ts index 7b9cd2d..6699177 100644 --- a/src/framework/commands/argParser.ts +++ b/src/framework/commands/argParser.ts @@ -59,6 +59,16 @@ export const parsePrefixArgs = async ( errors.push(parsed.error); } + if (tokens.length > 0) { + errors.push({ + key: "errors.args.tooMany", + vars: { + count: tokens.length, + extras: tokens.join(" "), + }, + }); + } + return { values, errors }; }; diff --git a/src/framework/commands/defineCommand.ts b/src/framework/commands/defineCommand.ts index d1c4bd8..b95fea2 100644 --- a/src/framework/commands/defineCommand.ts +++ b/src/framework/commands/defineCommand.ts @@ -1,18 +1,29 @@ import type { BotCommand, BotCommandInput } from "../types/command.js"; -export const defineCommand = (input: BotCommandInput): BotCommand => { - // Discord requires all required slash options before any optional ones. - const normalizedArgs = [...(input.args ?? [])].sort((a, b) => { - if (a.required === b.required) { - return 0; +const assertRequiredArgsBeforeOptional = (input: BotCommandInput): void => { + const args = input.args ?? []; + let firstOptionalArgName: string | null = null; + + for (const arg of args) { + if (!arg.required) { + firstOptionalArgName ??= arg.name; + continue; } - return a.required ? -1 : 1; - }); + if (firstOptionalArgName) { + throw new Error( + `Invalid argument order for command "${input.meta.name}": required argument "${arg.name}" cannot appear after optional argument "${firstOptionalArgName}". Declare all required arguments before optional ones.`, + ); + } + } +}; + +export const defineCommand = (input: BotCommandInput): BotCommand => { + assertRequiredArgsBeforeOptional(input); return { meta: input.meta, - args: normalizedArgs, + args: [...(input.args ?? [])], permissions: input.permissions ?? [], examples: input.examples ?? [], execute: input.execute,