Introducción a comandos
La entrada principal de cualquier bot de Discord son los comandos. En Seyfert, estos se definen utilizando decoradores de TypeScript, lo que facilita definir sus propiedades, opciones, middlewares y subcomandos. Esta guía te ayudará a comprender cómo funcionan los decoradores relacionados con comandos en Seyfert.
Declarando un comando
Todos los comandos en Seyfert son basados en clases y cada una de estas clases son extendidas de la clase base Command.
Además de esto, el nombre y la descripción son propiedades obligatorias en cada comando. A continuación, una lista de las posibles propiedades que puedes utilizar junto al decorador @Declare:
import { function Declare(declare: CommandDeclareOptions): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { name: string; nsfw: boolean | undefined; props: ExtraProps | undefined; contexts: InteractionContextType[]; integrationTypes: ApplicationIntegrationType[]; defaultMemberPermissions: bigint | undefined; botPermissions: bigint | undefined; description: string; type: ApplicationCommandType; guildId?: string[]; ignore?: IgnoreCommand; aliases?: string[]; handler?: EntryPointCommandHandlerType; };} & T
Declare, class Command
Command, enum IgnoreCommand
IgnoreCommand } from 'seyfert';
@function Declare(declare: CommandDeclareOptions): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { name: string; nsfw: boolean | undefined; props: ExtraProps | undefined; contexts: InteractionContextType[]; integrationTypes: ApplicationIntegrationType[]; defaultMemberPermissions: bigint | undefined; botPermissions: bigint | undefined; description: string; type: ApplicationCommandType; guildId?: string[]; ignore?: IgnoreCommand; aliases?: string[]; handler?: EntryPointCommandHandlerType; };} & T
Declare({ name: string
name: 'tu-comando', description: string
description: 'Una descripción para este comando', // Propiedades para pasar en el comando como metadata props: {}
props: {},
// Lista de permisos que necesita el miembro defaultMemberPermissions: "Administrator"[]
defaultMemberPermissions: ['Administrator'],
// Lista de permisos que necesita el bot botPermissions: "ManageGuild"[]
botPermissions: ['ManageGuild'],
// Lista de IDs de servidores para registrar el comando guildId: string[]
guildId: ['100000'],
// Determina si el comando es NSFW nsfw: false
nsfw: false,
// Lista de nombres alternos para el comando (en comandos de texto) aliases: string[]
aliases: ['un-comando'],
// Identifica los tipos de instalaciones que soporta el comando, // por defecto solo en el servidor integrationTypes: ("GuildInstall" | "UserInstall")[]
integrationTypes: ['GuildInstall', 'UserInstall'],
// Determina dónde se puede usar un comando contexts: ("Guild" | "BotDM" | "PrivateChannel")[]
contexts: ['BotDM', 'Guild', 'PrivateChannel'],
// Establece si ignorar la ejecución del comando en slash // o en su versión mensaje de texto ignore: IgnoreCommand.Slash
ignore: enum IgnoreCommand
IgnoreCommand. function (enum member) IgnoreCommand.Slash = 0
Slash,MessageSlash
// Establece el tipo de comando: /// type: ApplicationCommandType.User})class class MiComando
MiComando extends class Command
Command {}Añadiendo opciones
Algo esencial es que los comandos se ajusten a las opciones ingresadas por el usuario. Para ello, utilizamos el decorador @Options, que necesita un objeto con todas las opciones del comando:
import { function Options(options: (new () => SubCommand)[] | OptionsRecord): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { options: SubCommand[] | CommandOption[]; };} & T
Options, class Command
Command, function createStringOption<R extends boolean, C extends SeyfertChoice<string>[] = SeyfertChoice<string>[], VC = never>(data: SeyfertStringOption<C, R, VC>): { readonly type: ApplicationCommandOptionType.String; readonly required?: R | undefined; readonly choices?: C | undefined; readonly value?: ValueCallback<ApplicationCommandOptionType.String, C, VC> | undefined; readonly description: string; readonly description_localizations?: APIApplicationCommandBasicOption["description_localizations"]; readonly name_localizations?: APIApplicationCommandBasicOption["name_localizations"]; readonly locales?: { name?: FlatObjectKeys<DefaultLocale>; description?: FlatObjectKeys<DefaultLocale>; }; readonly autocomplete?: AutocompleteCallback; readonly onAutocompleteError?: OnAutocompleteErrorCallback; readonly min_length?: number; readonly max_length?: number;}
createStringOption, type class CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>interface CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>
CommandContext,} from 'seyfert';
const const options: { message: { readonly type: ApplicationCommandOptionType.String; readonly required?: boolean | undefined; ... 9 more ...; readonly max_length?: number; };}
options = { message: { readonly type: ApplicationCommandOptionType.String; readonly required?: boolean | undefined; readonly choices?: SeyfertChoice<...>[] | undefined; ... 8 more ...; readonly max_length?: number;}
message: createStringOption<boolean, SeyfertChoice<string>[], never>(data: SeyfertStringOption<SeyfertChoice<string>[], boolean, never>): { readonly type: ApplicationCommandOptionType.String; ... 10 more ...; readonly max_length?: number;}
createStringOption({ SeyfertBaseChoiceableOption<T extends keyof ReturnOptionsTypes, C = T extends ChoiceableTypes ? SeyfertChoice<ChoiceableValues[T]>[] : never, R = boolean, VC = never>.description: string
description: 'Mi opción de texto', }),};
@function Options(options: (new () => SubCommand)[] | OptionsRecord): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { options: SubCommand[] | CommandOption[]; };} & T
Options(const options: { message: { readonly type: ApplicationCommandOptionType.String; readonly required?: boolean | undefined; ... 9 more ...; readonly max_length?: number; };}
options)class class MiComando
MiComando extends class Command
Command { MiComando.run(ctx: CommandContext<typeof options>): void
run(ctx: CommandContext<{ message: { readonly type: ApplicationCommandOptionType.String; readonly required?: boolean | undefined; ... 9 more ...; readonly max_length?: number; };}, never>
ctx: class CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>interface CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>
CommandContext<typeof const options: { message: { readonly type: ApplicationCommandOptionType.String; readonly required?: boolean | undefined; ... 9 more ...; readonly max_length?: number; };}
options>) { ctx: CommandContext<{ message: { readonly type: ApplicationCommandOptionType.String; readonly required?: boolean | undefined; ... 9 more ...; readonly max_length?: number; };}, never>
ctx.CommandContext<{ message: { readonly type: ApplicationCommandOptionType.String; readonly required?: boolean | undefined; ... 9 more ...; readonly max_length?: number; }; }, never>.options: ContextOptions<{ message: { readonly type: ApplicationCommandOptionType.String; readonly required?: boolean | undefined; ... 9 more ...; readonly max_length?: number; };}>
options.message;message?: string | undefined
}}Todas los tipos y posibles opciones para cada comando se pueden encontrar en su respectiva sección.
Utilizando middlewares
Seyfert cuenta con un sistema avanzado de middlewares, completamente tipado y de gran potencia. Este sistema permite procesar una lista de middlewares, que son funciones diseñadas para ejecutarse previamente a la ejecución de un comando.
Recuerda que debes haber hecho uso del declare module para que funcione correctamente:
import { class Command
Command, function Middlewares(cbs: readonly (keyof RegisteredMiddlewares)[]): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { middlewares: readonly never[]; };} & T
Middlewares } from 'seyfert';
@function Middlewares(cbs: readonly (keyof RegisteredMiddlewares)[]): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { middlewares: readonly never[]; };} & T
Middlewares(['mi-middleware', 'siguiente-middleware'])class class MiComando
MiComando extends class Command
Command {}Los casos de uso para los middlewares y cómo empezar a usarlos están en la sección de middlewares.
Idiomas
Con el sistema de idiomas avanzado de Seyfert, puedes automáticamente traducir todo el contenido de un comando utilizando el decorador @LocalesT.
Recuerda que para el funcionamiento correcto de este, debes haber usado el declare module correctamente.
import { class Command
Command, function LocalesT(name?: FlatObjectKeys<DefaultLocale>, description?: FlatObjectKeys<DefaultLocale>): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { __t: { name: undefined; description: undefined; }; };} & T
LocalesT } from 'seyfert';
@function LocalesT(name?: FlatObjectKeys<DefaultLocale>, description?: FlatObjectKeys<DefaultLocale>): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { __t: { name: undefined; description: undefined; }; };} & T
LocalesT('mi-comando .nombre', 'mi-comando.description')mi-comando.nombremi-comando.descriptionclass class MiComando
MiComando extends class Command
Command {}Subcomandos
Basándonos en la estructura de Discord, los subcomandos de tipo slash pueden pertenecer a grupos o no. Es importante tener en cuenta que solo se pueden ejecutar los subcomandos directamente, sin incluir los grupos o el comando principal.
Para añadir subcomandos, se puede utilizar el decorador @Options en un comando principal, especificando los subcomandos disponibles con la clase SubCommand. Para mantener una buena organización, se recomienda usar una estructura de carpetas que permita gestionar los subcomandos de manera ordenada:
import { class Command
Command, function Declare(declare: CommandDeclareOptions): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { name: string; nsfw: boolean | undefined; props: ExtraProps | undefined; contexts: InteractionContextType[]; integrationTypes: ApplicationIntegrationType[]; defaultMemberPermissions: bigint | undefined; botPermissions: bigint | undefined; description: string; type: ApplicationCommandType; guildId?: string[]; ignore?: IgnoreCommand; aliases?: string[]; handler?: EntryPointCommandHandlerType; };} & T
Declare, function Options(options: (new () => SubCommand)[] | OptionsRecord): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { options: SubCommand[] | CommandOption[]; };} & T
Options } from 'seyfert';import import MiSubComando
MiSubComando from './sub';
@function Declare(declare: CommandDeclareOptions): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { name: string; nsfw: boolean | undefined; props: ExtraProps | undefined; contexts: InteractionContextType[]; integrationTypes: ApplicationIntegrationType[]; defaultMemberPermissions: bigint | undefined; botPermissions: bigint | undefined; description: string; type: ApplicationCommandType; guildId?: string[]; ignore?: IgnoreCommand; aliases?: string[]; handler?: EntryPointCommandHandlerType; };} & T
Declare({ name: string
name: 'padre', description: string
description: 'Mi comando principal',})@function Options(options: (new () => SubCommand)[] | OptionsRecord): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { options: SubCommand[] | CommandOption[]; };} & T
Options([import MiSubComando
MiSubComando])export default class class PadreCommand
PadreCommand extends class Command
Command { }import { class SubCommand
SubCommand, function Declare(declare: CommandDeclareOptions): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { name: string; nsfw: boolean | undefined; props: ExtraProps | undefined; contexts: InteractionContextType[]; integrationTypes: ApplicationIntegrationType[]; defaultMemberPermissions: bigint | undefined; botPermissions: bigint | undefined; description: string; type: ApplicationCommandType; guildId?: string[]; ignore?: IgnoreCommand; aliases?: string[]; handler?: EntryPointCommandHandlerType; };} & T
Declare, class CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>interface CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>
CommandContext } from 'seyfert';
@function Declare(declare: CommandDeclareOptions): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { name: string; nsfw: boolean | undefined; props: ExtraProps | undefined; contexts: InteractionContextType[]; integrationTypes: ApplicationIntegrationType[]; defaultMemberPermissions: bigint | undefined; botPermissions: bigint | undefined; description: string; type: ApplicationCommandType; guildId?: string[]; ignore?: IgnoreCommand; aliases?: string[]; handler?: EntryPointCommandHandlerType; };} & T
Declare({ name: string
name: 'subcomando', description: string
description: 'Uno de mi subcomandos',})export default class class MiSubCommand
MiSubCommand extends class SubCommand
SubCommand { MiSubCommand.run(ctx: CommandContext): Promise<void | WebhookMessage>
run(ctx: CommandContext<{}, never>
ctx: class CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>interface CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>
CommandContext) { return ctx: CommandContext<{}, never>
ctx.CommandContext<{}, never>.write<false>(body: InteractionCreateBodyRequest, withResponse?: false | undefined): Promise<void | WebhookMessage>
write({ content?: string | undefined
The message contents (up to 2000 characters)
content: '¡Hola desde el subcomando!', }); }}Grupos de subcomandos
Si queremos una estructura más dividida, podemos crear grupos para los subcomandos. Para ello, se utiliza el decorador @Group y @Groups en el comando principal.
import { class Command
Command, function Declare(declare: CommandDeclareOptions): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { name: string; nsfw: boolean | undefined; props: ExtraProps | undefined; contexts: InteractionContextType[]; integrationTypes: ApplicationIntegrationType[]; defaultMemberPermissions: bigint | undefined; botPermissions: bigint | undefined; description: string; type: ApplicationCommandType; guildId?: string[]; ignore?: IgnoreCommand; aliases?: string[]; handler?: EntryPointCommandHandlerType; };} & T
Declare, function Options(options: (new () => SubCommand)[] | OptionsRecord): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { options: SubCommand[] | CommandOption[]; };} & T
Options, function Groups(groups: Record<string, { name?: [language: LocaleString, value: string][]; description?: [language: LocaleString, value: string][]; defaultDescription: string; aliases?: string[];}>): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { groups: Record<string, { name?: [language: LocaleString, value: string][]; description?: [language: LocaleString, value: string][]; defaultDescription: string; aliases?: string[]; }>; groupsAliases: Record<string, string>; };} & T
Groups } from 'seyfert';import import MiSubComando
MiSubComando from './sub';
@function Declare(declare: CommandDeclareOptions): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { name: string; nsfw: boolean | undefined; props: ExtraProps | undefined; contexts: InteractionContextType[]; integrationTypes: ApplicationIntegrationType[]; defaultMemberPermissions: bigint | undefined; botPermissions: bigint | undefined; description: string; type: ApplicationCommandType; guildId?: string[]; ignore?: IgnoreCommand; aliases?: string[]; handler?: EntryPointCommandHandlerType; };} & T
Declare({ name: string
name: 'padre', description: string
description: 'Mi comando principal',})@function Options(options: (new () => SubCommand)[] | OptionsRecord): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { options: SubCommand[] | CommandOption[]; };} & T
Options([import MiSubComando
MiSubComando])@function Groups(groups: Record<string, { name?: [language: LocaleString, value: string][]; description?: [language: LocaleString, value: string][]; defaultDescription: string; aliases?: string[];}>): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { groups: Record<string, { name?: [language: LocaleString, value: string][]; description?: [language: LocaleString, value: string][]; defaultDescription: string; aliases?: string[]; }>; groupsAliases: Record<string, string>; };} & T
Groups({ 'mi-grupo': { defaultDescription: string
defaultDescription: 'Un grupo de subcomandos', }})export default class class PadreCommand
PadreCommand extends class Command
Command { }import { class SubCommand
SubCommand, function Declare(declare: CommandDeclareOptions): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { name: string; nsfw: boolean | undefined; props: ExtraProps | undefined; contexts: InteractionContextType[]; integrationTypes: ApplicationIntegrationType[]; defaultMemberPermissions: bigint | undefined; botPermissions: bigint | undefined; description: string; type: ApplicationCommandType; guildId?: string[]; ignore?: IgnoreCommand; aliases?: string[]; handler?: EntryPointCommandHandlerType; };} & T
Declare, class CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>interface CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>
CommandContext, function Group(groupName: string): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { group: string; };} & T
Group } from 'seyfert';
@function Declare(declare: CommandDeclareOptions): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { name: string; nsfw: boolean | undefined; props: ExtraProps | undefined; contexts: InteractionContextType[]; integrationTypes: ApplicationIntegrationType[]; defaultMemberPermissions: bigint | undefined; botPermissions: bigint | undefined; description: string; type: ApplicationCommandType; guildId?: string[]; ignore?: IgnoreCommand; aliases?: string[]; handler?: EntryPointCommandHandlerType; };} & T
Declare({ name: string
name: 'subcomando', description: string
description: 'Uno de mi subcomandos dentro del grupo mi-grupo',})@function Group(groupName: string): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { group: string; };} & T
Group('mi-grupo')export default class class GrupoMiSubCommand
GrupoMiSubCommand extends class SubCommand
SubCommand { GrupoMiSubCommand.run(ctx: CommandContext): Promise<void | WebhookMessage>
run(ctx: CommandContext<{}, never>
ctx: class CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>interface CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>
CommandContext) { return ctx: CommandContext<{}, never>
ctx.CommandContext<{}, never>.write<false>(body: InteractionCreateBodyRequest, withResponse?: false | undefined): Promise<void | WebhookMessage>
write({ content?: string | undefined
The message contents (up to 2000 characters)
content: '¡Hola desde el subcomando!', }); }}Carga automática de subcomandos
Para ahorrarnos el hecho de importar cada comando manualmente, Seyfert nos permite cargar automáticamente los comandos de una carpeta. Para ello, se utiliza el decorador @AutoLoad en el comando principal.
import { class Command
Command, function Declare(declare: CommandDeclareOptions): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { name: string; nsfw: boolean | undefined; props: ExtraProps | undefined; contexts: InteractionContextType[]; integrationTypes: ApplicationIntegrationType[]; defaultMemberPermissions: bigint | undefined; botPermissions: bigint | undefined; description: string; type: ApplicationCommandType; guildId?: string[]; ignore?: IgnoreCommand; aliases?: string[]; handler?: EntryPointCommandHandlerType; };} & T
Declare, function AutoLoad(): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { __autoload: boolean; };} & T
AutoLoad } from 'seyfert';
@function Declare(declare: CommandDeclareOptions): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { name: string; nsfw: boolean | undefined; props: ExtraProps | undefined; contexts: InteractionContextType[]; integrationTypes: ApplicationIntegrationType[]; defaultMemberPermissions: bigint | undefined; botPermissions: bigint | undefined; description: string; type: ApplicationCommandType; guildId?: string[]; ignore?: IgnoreCommand; aliases?: string[]; handler?: EntryPointCommandHandlerType; };} & T
Declare({ name: string
name: 'padre', description: string
description: 'Mi comando principal',})@function AutoLoad(): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { __autoload: boolean; };} & T
AutoLoad()export default class class PadreCommand
PadreCommand extends class Command
Command { }No es necesario utilizar el decorador
@Optionssi se utiliza@AutoLoad. Seyfert cargará automáticamente los subcomandos de la carpeta.