mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-15 19:08:17 +00:00
feat(helpers,types)!: slash command localization (#2129)
* feat(helpers,types): slash command localization Discord has documented the slash command localization feature now. This adds the full functionality for those to Discordeno. Additionally a `Locales` has also been added to allow for better typing. Reference: https://github.com/discord/discord-api-docs/pull/4653 * suggestion * better locales type * f * fix serializing * stupid direct pushes * b
This commit is contained in:
@@ -69,6 +69,7 @@ import { transformStageInstance } from "./transformers/stageInstance.ts";
|
||||
import { StickerPack, transformSticker, transformStickerPack } from "./transformers/sticker.ts";
|
||||
import { GetGatewayBot, transformGatewayBot } from "./transformers/gatewayBot.ts";
|
||||
import {
|
||||
DiscordApplicationCommandOptionChoice,
|
||||
DiscordEmoji,
|
||||
DiscordGatewayPayload,
|
||||
DiscordInteractionDataOption,
|
||||
@@ -127,6 +128,10 @@ import { VoiceRegions } from "./transformers/voiceRegion.ts";
|
||||
import { GuildWidget } from "./transformers/widget.ts";
|
||||
import { StageInstance } from "./transformers/stageInstance.ts";
|
||||
import { Sticker } from "./transformers/sticker.ts";
|
||||
import {
|
||||
ApplicationCommandOptionChoice,
|
||||
transformApplicationCommandOptionChoice,
|
||||
} from "./transformers/applicationCommandOptionChoice.ts";
|
||||
import { transformEmbedToDiscordEmbed } from "./transformers/reverse/embed.ts";
|
||||
import { transformComponentToDiscordComponent } from "./transformers/reverse/component.ts";
|
||||
|
||||
@@ -421,6 +426,10 @@ export interface Transformers {
|
||||
stageInstance: (bot: Bot, payload: DiscordStageInstance) => StageInstance;
|
||||
sticker: (bot: Bot, payload: DiscordSticker) => Sticker;
|
||||
stickerPack: (bot: Bot, payload: DiscordStickerPack) => StickerPack;
|
||||
applicationCommandOptionChoice: (
|
||||
bot: Bot,
|
||||
payload: DiscordApplicationCommandOptionChoice,
|
||||
) => ApplicationCommandOptionChoice;
|
||||
template: (bot: Bot, payload: DiscordTemplate) => Template;
|
||||
}
|
||||
|
||||
@@ -467,6 +476,7 @@ export function createTransformers(options: Partial<Transformers>) {
|
||||
sticker: options.sticker || transformSticker,
|
||||
stickerPack: options.stickerPack || transformStickerPack,
|
||||
gatewayBot: options.gatewayBot || transformGatewayBot,
|
||||
applicationCommandOptionChoice: options.applicationCommandOptionChoice || transformApplicationCommandOptionChoice,
|
||||
template: options.template || transformTemplate,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Bot } from "../../../bot.ts";
|
||||
import { ApplicationCommandOption, ApplicationCommandTypes } from "../../../mod.ts";
|
||||
import { ApplicationCommandOption, ApplicationCommandTypes, Localization } from "../../../mod.ts";
|
||||
import { DiscordApplicationCommand, DiscordApplicationCommandOption } from "../../../types/discord.ts";
|
||||
|
||||
/**
|
||||
@@ -24,9 +24,13 @@ export async function createApplicationCommand(
|
||||
guildId
|
||||
? bot.constants.endpoints.COMMANDS_GUILD(bot.applicationId, guildId)
|
||||
: bot.constants.endpoints.COMMANDS(bot.applicationId),
|
||||
isContextApplicationCommand(options) ? { name: options.name, type: options.type } : {
|
||||
isContextApplicationCommand(options)
|
||||
? { name: options.name, name_localizations: options.nameLocalizations, type: options.type }
|
||||
: {
|
||||
name: options.name,
|
||||
name_localizations: options.nameLocalizations,
|
||||
description: options.description,
|
||||
description_localizations: options.descriptionLocalizations,
|
||||
type: options.type,
|
||||
options: options.options ? makeOptionsForCommand(options.options) : undefined,
|
||||
},
|
||||
@@ -39,9 +43,15 @@ export function makeOptionsForCommand(options: ApplicationCommandOption[]): Disc
|
||||
return options.map((option) => ({
|
||||
type: option.type,
|
||||
name: option.name,
|
||||
name_localizations: option.nameLocalizations,
|
||||
description: option.description,
|
||||
description_localizations: option.descriptionLocalizations,
|
||||
required: option.required,
|
||||
choices: option.choices,
|
||||
choices: option.choices?.map((choice) => ({
|
||||
name: choice.name,
|
||||
name_localizations: choice.nameLocalizations,
|
||||
value: choice.value,
|
||||
})),
|
||||
options: option.options ? makeOptionsForCommand(option.options) : undefined,
|
||||
channel_types: option.channelTypes,
|
||||
autocomplete: option.autocomplete,
|
||||
@@ -50,12 +60,16 @@ export function makeOptionsForCommand(options: ApplicationCommandOption[]): Disc
|
||||
}));
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/interactions/slash-commands#create-global-application-command-json-params */
|
||||
/** https://discord.com/developers/docs/interactions/application-commands#endpoints-json-params */
|
||||
export interface CreateApplicationCommand {
|
||||
/** 1-31 character name matching lowercase `^[\w-]{1,32}$` */
|
||||
name: string;
|
||||
/** Localization object for the `name` field. Values follow the same restrictions as `name` */
|
||||
nameLocalizations?: Localization;
|
||||
/** 1-100 character description */
|
||||
description: string;
|
||||
/** Localization object for the `description` field. Values follow the same restrictions as `description` */
|
||||
descriptionLocalizations?: Localization;
|
||||
/** The type of the command */
|
||||
type?: ApplicationCommandTypes;
|
||||
/** The parameters for the command */
|
||||
@@ -64,10 +78,12 @@ export interface CreateApplicationCommand {
|
||||
defaultPermission?: boolean;
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/interactions/slash-commands#create-global-application-command-json-params */
|
||||
/** https://discord.com/developers/docs/interactions/application-commands#endpoints-json-params */
|
||||
export interface CreateContextApplicationCommand {
|
||||
/** 1-31 character name matching lowercase `^[\w-]{1,32}$` */
|
||||
name: string;
|
||||
/** Localization object for the `name` field. Values follow the same restrictions as `name` */
|
||||
nameLocalizations?: Localization;
|
||||
/** The type of the command */
|
||||
type: ApplicationCommandTypes.Message | ApplicationCommandTypes.User;
|
||||
}
|
||||
|
||||
@@ -2,14 +2,30 @@ import type { Bot } from "../../../bot.ts";
|
||||
import { DiscordApplicationCommand } from "../../../types/discord.ts";
|
||||
|
||||
/** Fetches the global command for the given Id. If a guildId is provided, the guild command will be fetched. */
|
||||
export async function getApplicationCommand(bot: Bot, commandId: bigint, guildId?: bigint) {
|
||||
export async function getApplicationCommand(bot: Bot, commandId: bigint, options?: GetApplicationCommand) {
|
||||
let url = `${
|
||||
options?.guildId
|
||||
? bot.constants.endpoints.COMMANDS_GUILD_ID(bot.applicationId, options.guildId, commandId)
|
||||
: bot.constants.endpoints.COMMANDS_ID(bot.applicationId, commandId)
|
||||
}?`;
|
||||
|
||||
if (options?.withLocalizations !== undefined) {
|
||||
url += `with_localizations=${options.withLocalizations}`;
|
||||
}
|
||||
|
||||
const result = await bot.rest.runMethod<DiscordApplicationCommand>(
|
||||
bot.rest,
|
||||
"get",
|
||||
guildId
|
||||
? bot.constants.endpoints.COMMANDS_GUILD_ID(bot.applicationId, guildId, commandId)
|
||||
: bot.constants.endpoints.COMMANDS_ID(bot.applicationId, commandId),
|
||||
url,
|
||||
);
|
||||
|
||||
return bot.transformers.applicationCommand(bot, result);
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/interactions/application-commands#endpoints-query-string-params */
|
||||
export interface GetApplicationCommand {
|
||||
/** Guild ID of the guild in which the command is available if it is a guild-specific command */
|
||||
guildId?: bigint;
|
||||
/** Whether to include full localization object (`name_localizations` and `description_localizations`) in the returned objects, instead of the `name_localized` and `description_localized` fields. Default false */
|
||||
withLocalizations?: boolean;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ export function transformApplicationCommand(bot: Bot, payload: DiscordApplicatio
|
||||
applicationId: bot.transformers.snowflake(payload.application_id),
|
||||
guildId: payload.guild_id ? bot.transformers.snowflake(payload.guild_id) : undefined,
|
||||
name: payload.name,
|
||||
nameLocalizations: payload.name_localizations,
|
||||
description: payload.description,
|
||||
descriptionLocalizations: payload.description_localizations,
|
||||
defaultPermission: payload.default_permission ?? false,
|
||||
type: payload.type,
|
||||
version: payload.version,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Bot } from "../bot.ts";
|
||||
import { DiscordApplicationCommandOption, DiscordApplicationCommandOptionChoice } from "../types/discord.ts";
|
||||
import { ApplicationCommandOptionTypes, ChannelTypes } from "../types/shared.ts";
|
||||
import { ApplicationCommandOptionTypes, ChannelTypes, Localization } from "../types/shared.ts";
|
||||
import { Optionalize } from "../types/shared.ts";
|
||||
import { ApplicationCommandOptionChoice } from "./applicationCommandOptionChoice.ts";
|
||||
|
||||
export function transformApplicationCommandOption(
|
||||
bot: Bot,
|
||||
@@ -10,9 +11,11 @@ export function transformApplicationCommandOption(
|
||||
return {
|
||||
type: payload.type,
|
||||
name: payload.name,
|
||||
nameLocalizations: payload.name_localizations,
|
||||
description: payload.description,
|
||||
descriptionLocalizations: payload.description_localizations,
|
||||
required: payload.required ?? false,
|
||||
choices: payload.choices,
|
||||
choices: payload.choices?.map((choice) => bot.transformers.applicationCommandOptionChoice(bot, choice)),
|
||||
autocomplete: payload.autocomplete,
|
||||
channelTypes: payload.channel_types,
|
||||
minValue: payload.min_value,
|
||||
@@ -29,12 +32,16 @@ export interface ApplicationCommandOption {
|
||||
type: ApplicationCommandOptionTypes;
|
||||
/** 1-32 character name matching lowercase `^[\w-]{1,32}$` */
|
||||
name: string;
|
||||
/** Localization object for the `name` field. Values follow the same restrictions as `name` */
|
||||
nameLocalizations?: Localization;
|
||||
/** 1-100 character description */
|
||||
description: string;
|
||||
/** Localization object for the `description` field. Values follow the same restrictions as `description` */
|
||||
descriptionLocalizations?: Localization;
|
||||
/** If the parameter is required or optional--default `false` */
|
||||
required: boolean;
|
||||
/** Choices for `string` and `int` types for the user to pick from */
|
||||
choices?: DiscordApplicationCommandOptionChoice[];
|
||||
choices?: ApplicationCommandOptionChoice[];
|
||||
/** If the option is a subcommand or subcommand group type, this nested options will be the parameters */
|
||||
options?: ApplicationCommandOption[];
|
||||
/** if autocomplete interactions are enabled for this `String`, `Integer`, or `Number` type option */
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Bot } from "../bot.ts";
|
||||
import { DiscordApplicationCommandOptionChoice } from "../types/discord.ts";
|
||||
import { Camelize, Localization, Optionalize } from "../types/shared.ts";
|
||||
|
||||
export function transformApplicationCommandOptionChoice(bot: Bot, payload: DiscordApplicationCommandOptionChoice) {
|
||||
const applicationCommandChoice = {
|
||||
name: payload.name,
|
||||
nameLocalizations: payload.name_localizations,
|
||||
value: payload.value,
|
||||
};
|
||||
|
||||
return applicationCommandChoice as Optionalize<typeof applicationCommandChoice>;
|
||||
}
|
||||
|
||||
export interface ApplicationCommandOptionChoice extends ReturnType<typeof transformApplicationCommandOptionChoice> {}
|
||||
+16
-4
@@ -16,6 +16,8 @@ import {
|
||||
GuildNsfwLevel,
|
||||
IntegrationExpireBehaviors,
|
||||
InteractionTypes,
|
||||
Locales,
|
||||
Localization,
|
||||
MessageActivityTypes,
|
||||
MessageComponentTypes,
|
||||
MessageTypes,
|
||||
@@ -1633,7 +1635,7 @@ export interface DiscordInviteStageInstance {
|
||||
topic: string;
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/interactions/slash-commands#applicationcommand */
|
||||
/** https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-structure */
|
||||
export interface DiscordApplicationCommand {
|
||||
/** Unique id of the command */
|
||||
id: string;
|
||||
@@ -1643,8 +1645,12 @@ export interface DiscordApplicationCommand {
|
||||
guild_id?: string;
|
||||
/** 1-32 character name matching */
|
||||
name: string;
|
||||
/** Localization object for the `name` field. Values follow the same restrictions as `name` */
|
||||
name_localizations?: Localization;
|
||||
/** 1-100 character description */
|
||||
description?: string;
|
||||
description: string;
|
||||
/** Localization object for the `description` field. Values follow the same restrictions as `description` */
|
||||
description_localizations?: Localization;
|
||||
/** The parameters for the command */
|
||||
options?: DiscordApplicationCommandOption[];
|
||||
/** Whether the command is enbaled by default when the app is added to a guild */
|
||||
@@ -1655,14 +1661,18 @@ export interface DiscordApplicationCommand {
|
||||
version: string;
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoption */
|
||||
/** https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure */
|
||||
export interface DiscordApplicationCommandOption {
|
||||
/** Value of Application Command Option Type */
|
||||
type: ApplicationCommandOptionTypes;
|
||||
/** 1-32 character name matching lowercase `^[\w-]{1,32}$` */
|
||||
name: string;
|
||||
/** Localization object for the `name` field. Values follow the same restrictions as `name` */
|
||||
name_localizations?: Localization;
|
||||
/** 1-100 character description */
|
||||
description: string;
|
||||
/** Localization object for the `description` field. Values follow the same restrictions as `description` */
|
||||
description_localizations?: Localization;
|
||||
/** If the parameter is required or optional--default `false` */
|
||||
required?: boolean;
|
||||
/** Choices for `string` and `int` types for the user to pick from */
|
||||
@@ -1679,10 +1689,12 @@ export interface DiscordApplicationCommandOption {
|
||||
max_value?: number;
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoptionchoice */
|
||||
/** https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-choice-structure */
|
||||
export interface DiscordApplicationCommandOptionChoice {
|
||||
/** 1-100 character choice name */
|
||||
name: string;
|
||||
/** Localization object for the `name` field. Values follow the same restrictions as `name` */
|
||||
name_localizations?: Localization;
|
||||
/** Value of the choice, up to 100 characters if string */
|
||||
value: string | number;
|
||||
}
|
||||
|
||||
@@ -1240,6 +1240,41 @@ export enum Errors {
|
||||
YOU_CAN_NOT_DM_THE_BOT_ITSELF = "YOU_CAN_NOT_DM_THE_BOT_ITSELF",
|
||||
}
|
||||
|
||||
export enum Locales {
|
||||
Danish = "da",
|
||||
German = "de",
|
||||
EnglishUk = "en-GB",
|
||||
EnglishUs = "en-US",
|
||||
Spanish = "es-ES",
|
||||
French = "fr",
|
||||
Croatian = "hr",
|
||||
Italian = "it",
|
||||
Lithuanian = "lt",
|
||||
Hungarian = "hu",
|
||||
Dutch = "nl",
|
||||
Norwegian = "no",
|
||||
Polish = "pl",
|
||||
PortugueseBrazilian = "pt-BR",
|
||||
RomanianRomania = "ro",
|
||||
Finnish = "fi",
|
||||
Swedish = "sv-SE",
|
||||
Vietnamese = "vi",
|
||||
Turkish = "tr",
|
||||
Czech = "cs",
|
||||
Greek = "el",
|
||||
Bulgarian = "bg",
|
||||
Russian = "ru",
|
||||
Ukrainian = "uk",
|
||||
Hindi = "hi",
|
||||
Thai = "th",
|
||||
ChineseChina = "zh-CN",
|
||||
Japanese = "ja",
|
||||
ChineseTaiwan = "zh-TW",
|
||||
Korean = "ko",
|
||||
}
|
||||
|
||||
export type Localization = Partial<Record<Locales, string>>;
|
||||
|
||||
// UTILS
|
||||
|
||||
export type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U];
|
||||
|
||||
Reference in New Issue
Block a user