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
@Options
si se utiliza@AutoLoad
. Seyfert cargará automáticamente los subcomandos de la carpeta.