mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-19 04:38:17 +00:00
fix(handlers/webhook): handle slash commands limitations (#618)
* add more errors * add the validation functions * spread
This commit is contained in:
+83
-36
@@ -9,6 +9,9 @@ import {
|
|||||||
ExecuteWebhookOptions,
|
ExecuteWebhookOptions,
|
||||||
MessageCreateOptions,
|
MessageCreateOptions,
|
||||||
SlashCommand,
|
SlashCommand,
|
||||||
|
SlashCommandOption,
|
||||||
|
SlashCommandOptionChoice,
|
||||||
|
SlashCommandOptionType,
|
||||||
SlashCommandResponseOptions,
|
SlashCommandResponseOptions,
|
||||||
UpsertSlashCommandOptions,
|
UpsertSlashCommandOptions,
|
||||||
UpsertSlashCommandsOptions,
|
UpsertSlashCommandsOptions,
|
||||||
@@ -270,6 +273,82 @@ export async function deleteWebhookMessage(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateSlashOptionChoices(
|
||||||
|
choices: SlashCommandOptionChoice[],
|
||||||
|
optionType: SlashCommandOptionType,
|
||||||
|
) {
|
||||||
|
for (const choice of choices) {
|
||||||
|
if ([...choice.name].length < 1 || [...choice.name].length > 100) {
|
||||||
|
throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(optionType === SlashCommandOptionType.STRING &&
|
||||||
|
(typeof choice.value !== "string" || choice.value.length < 1 ||
|
||||||
|
choice.value.length > 100)) ||
|
||||||
|
(optionType === SlashCommandOptionType.INTEGER &&
|
||||||
|
typeof choice.value !== "number")
|
||||||
|
) {
|
||||||
|
throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSlashOptions(options: SlashCommandOption[]) {
|
||||||
|
for (const option of options) {
|
||||||
|
if (
|
||||||
|
(option.choices?.length && option.choices.length > 25) ||
|
||||||
|
option.type !== SlashCommandOptionType.STRING &&
|
||||||
|
option.type !== SlashCommandOptionType.INTEGER
|
||||||
|
) {
|
||||||
|
throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
([...option.name].length < 1 || [...option.name].length > 32) ||
|
||||||
|
([...option.description].length < 1 ||
|
||||||
|
[...option.description].length > 100)
|
||||||
|
) {
|
||||||
|
throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.choices) {
|
||||||
|
validateSlashOptionChoices(option.choices, option.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSlashCommands(
|
||||||
|
commands: UpsertSlashCommandOptions[],
|
||||||
|
create = false,
|
||||||
|
) {
|
||||||
|
for (const command of commands) {
|
||||||
|
if (
|
||||||
|
(command.name && !SLASH_COMMANDS_NAME_REGEX.test(command.name)) ||
|
||||||
|
(create && !command.name)
|
||||||
|
) {
|
||||||
|
throw new Error(Errors.INVALID_SLASH_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(command.description &&
|
||||||
|
([...command.description].length < 1 ||
|
||||||
|
[...command.description].length > 100)) ||
|
||||||
|
(create && !command.description)
|
||||||
|
) {
|
||||||
|
throw new Error(Errors.INVALID_SLASH_DESCRIPTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.options?.length) {
|
||||||
|
if (command.options.length > 25) {
|
||||||
|
throw new Error(Errors.INVALID_SLASH_OPTIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
validateSlashOptions(command.options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* There are two kinds of Slash Commands: global commands and guild commands. Global commands are available for every guild that adds your app; guild commands are specific to the guild you specify when making them. Command names are unique per application within each scope (global and guild). That means:
|
* There are two kinds of Slash Commands: global commands and guild commands. Global commands are available for every guild that adds your app; guild commands are specific to the guild you specify when making them. Command names are unique per application within each scope (global and guild). That means:
|
||||||
*
|
*
|
||||||
@@ -282,15 +361,7 @@ export async function deleteWebhookMessage(
|
|||||||
* Guild commands update **instantly**. We recommend you use guild commands for quick testing, and global commands when they're ready for public use.
|
* Guild commands update **instantly**. We recommend you use guild commands for quick testing, and global commands when they're ready for public use.
|
||||||
*/
|
*/
|
||||||
export async function createSlashCommand(options: CreateSlashCommandOptions) {
|
export async function createSlashCommand(options: CreateSlashCommandOptions) {
|
||||||
if (!SLASH_COMMANDS_NAME_REGEX.test(options.name)) {
|
validateSlashCommands([options], true);
|
||||||
throw new Error(Errors.INVALID_SLASH_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
[...options.description].length < 1 || [...options.description].length > 100
|
|
||||||
) {
|
|
||||||
throw new Error(Errors.INVALID_SLASH_DESCRIPTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await RequestManager.post(
|
const result = await RequestManager.post(
|
||||||
options.guildID
|
options.guildID
|
||||||
@@ -335,17 +406,7 @@ export async function upsertSlashCommand(
|
|||||||
options: UpsertSlashCommandOptions,
|
options: UpsertSlashCommandOptions,
|
||||||
guildID?: string,
|
guildID?: string,
|
||||||
) {
|
) {
|
||||||
if (options.name && !SLASH_COMMANDS_NAME_REGEX.test(options.name)) {
|
validateSlashCommands([options]);
|
||||||
throw new Error(Errors.INVALID_SLASH_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
options.description &&
|
|
||||||
([...options.description].length < 1 ||
|
|
||||||
[...options.description].length > 100)
|
|
||||||
) {
|
|
||||||
throw new Error(Errors.INVALID_SLASH_DESCRIPTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await RequestManager.patch(
|
const result = await RequestManager.patch(
|
||||||
guildID
|
guildID
|
||||||
@@ -370,27 +431,13 @@ export async function upsertSlashCommands(
|
|||||||
options: UpsertSlashCommandsOptions[],
|
options: UpsertSlashCommandsOptions[],
|
||||||
guildID?: string,
|
guildID?: string,
|
||||||
) {
|
) {
|
||||||
const data = options.map((option) => {
|
validateSlashCommands(options);
|
||||||
if (option.name && !SLASH_COMMANDS_NAME_REGEX.test(option.name)) {
|
|
||||||
throw new Error(Errors.INVALID_SLASH_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
option.description &&
|
|
||||||
([...option.description].length < 1 ||
|
|
||||||
[...option.description].length > 100)
|
|
||||||
) {
|
|
||||||
throw new Error(Errors.INVALID_SLASH_DESCRIPTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
return option;
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await RequestManager.put(
|
const result = await RequestManager.put(
|
||||||
guildID
|
guildID
|
||||||
? endpoints.COMMANDS_GUILD(applicationID, guildID)
|
? endpoints.COMMANDS_GUILD(applicationID, guildID)
|
||||||
: endpoints.COMMANDS(applicationID),
|
: endpoints.COMMANDS(applicationID),
|
||||||
data,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export enum Errors {
|
|||||||
// Interaction Errors
|
// Interaction Errors
|
||||||
INVALID_SLASH_DESCRIPTION = "INVALID_SLASH_DESCRIPTION",
|
INVALID_SLASH_DESCRIPTION = "INVALID_SLASH_DESCRIPTION",
|
||||||
INVALID_SLASH_NAME = "INVALID_SLASH_NAME",
|
INVALID_SLASH_NAME = "INVALID_SLASH_NAME",
|
||||||
|
INVALID_SLASH_OPTIONS = "INVALID_SLASH_OPTIONS",
|
||||||
|
INVALID_SLASH_OPTIONS_CHOICES = "INVALID_SLASH_OPTIONS_CHOICES",
|
||||||
// Webhook Errors
|
// Webhook Errors
|
||||||
INVALID_WEBHOOK_NAME = "INVALID_WEBHOOK_NAME",
|
INVALID_WEBHOOK_NAME = "INVALID_WEBHOOK_NAME",
|
||||||
INVALID_WEBHOOK_OPTIONS = "INVALID_WEBHOOK_OPTIONS",
|
INVALID_WEBHOOK_OPTIONS = "INVALID_WEBHOOK_OPTIONS",
|
||||||
|
|||||||
Reference in New Issue
Block a user