Creando tu primer comando
La entrada principal de cualquier bot de Discord son los comandos, en Seyfert, puedes crear y administrar comandos de forma sencilla.
Seyfert se encargará de cargar y ejecutar todos los comandos que hayas creado dentro de la carpeta commands
(especificada también en la configuración).
Para empezar, vamos a crear el siguiente archivo y utilizar el comando ping
como ejemplo.
Todos los comandos deben usar el decorador @Declare
para la información y una clase que extiende a Command
:
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, type 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: 'ping', description: string
description: 'Mostrar la latencia con Discord'})export default class class PingCommand
PingCommand extends class Command
Command { async PingCommand.run(ctx: CommandContext): Promise<void>
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) { // Latencia media entre las conexiones existentes const const ping: number
ping = ctx: CommandContext<{}, never>
ctx.CommandContext<{}, never>.client: UsingClient
client.Client<true>.gateway: ShardManager
gateway.ShardManager.latency: number
latency;
await 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: `La latencia es \`${const ping: number
ping}\`` }); }}
Antes de poder ejecutar comandos nuevos necesitas registrarlos en Discord. Para ello, puedes utilizar el siguiente código:
import { class Client<Ready extends boolean = boolean>
Client } from 'seyfert';
const const client: Client<boolean>
client = new new Client<boolean>(options?: ClientOptions): Client<boolean>
Client();
const client: Client<boolean>
client.Client<boolean>.start(options?: Omit<DeepPartial<StartOptions>, "httpConnection">, execute?: boolean): Promise<void>
start() .Promise<void>.then<void, never>(onfulfilled?: ((value: void) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>
Attaches callbacks for the resolution and/or rejection of the Promise.
then(() => const client: Client<boolean>
client.BaseClient.uploadCommands({ applicationId, cachePath }?: { applicationId?: string; cachePath?: string;}): Promise<void>
uploadCommands({ cachePath?: string
cachePath: './commands.json' }));
Con esto puedes ejecutar tu bot y probar el comando /ping
en Discord. ¡Deberías ver la latencia de tu bot!
Usando opciones
¿Todo simple, verdad? Sin embargo, los comandos no siempre se limitan a responder lo mismo cada que lo ejecutas, hay veces que necesitamos interpretar lo que realmente necesita el usuario. Aquí es donde entran las options
.
Vamos a configurar este comando para que su respuesta sea invisible para el resto de usuarios (mensaje efímero — ephemeral).
Para esto utilizaremos el decorador @Options
y crearemos una opción hide
de tipo booleano:
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 createBooleanOption<R extends boolean, T extends SeyfertBooleanOption<R> = SeyfertBooleanOption<R>>(data: T): T & { readonly type: ApplicationCommandOptionType.Boolean;}
createBooleanOption, type class CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>interface CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>
CommandContext} from 'seyfert';import { MessageFlags } from 'seyfert/lib/types';
const const options: { hide: { description: string; } & { readonly type: ApplicationCommandOptionType.Boolean; };}
options = { hide: { description: string;} & { readonly type: ApplicationCommandOptionType.Boolean;}
hide: createBooleanOption<boolean, { description: string;}>(data: { description: string;}): { description: string;} & { readonly type: ApplicationCommandOptionType.Boolean;}
createBooleanOption({ description: string
description: "Ocultar la respuesta del comando", }),};
@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: 'ping', description: string
description: 'Mostrar la latencia con Discord'})@function Options(options: (new () => SubCommand)[] | OptionsRecord): <T extends { new (...args: any[]): object;}>(target: T) => { new (...args: any[]): { options: SubCommand[] | CommandOption[]; };} & T
Options(const options: { hide: { description: string; } & { readonly type: ApplicationCommandOptionType.Boolean; };}
options)export default class class PingCommand
PingCommand extends class Command
Command {
async PingCommand.run(ctx: CommandContext<typeof options>): Promise<void>
run(ctx: CommandContext<{ hide: { description: string; } & { readonly type: ApplicationCommandOptionType.Boolean; };}, 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: { hide: { description: string; } & { readonly type: ApplicationCommandOptionType.Boolean; };}
options>) { const const flags: MessageFlags.Ephemeral | undefined
flags = ctx: CommandContext<{ hide: { description: string; } & { readonly type: ApplicationCommandOptionType.Boolean; };}, never>
ctx.CommandContext<{ hide: { description: string; } & { readonly type: ApplicationCommandOptionType.Boolean; }; }, never>.options: ContextOptions<{ hide: { description: string; } & { readonly type: ApplicationCommandOptionType.Boolean; };}>
options.hide?: boolean | undefined
hide ? MessageFlags.function (enum member) MessageFlags.Ephemeral = 64
This message is only visible to the user who invoked the Interaction
Ephemeral : var undefined
undefined;
// Latencia media entre las conexiones existentes const const ping: number
ping = ctx: CommandContext<{ hide: { description: string; } & { readonly type: ApplicationCommandOptionType.Boolean; };}, never>
ctx.CommandContext<{ hide: { description: string; } & { readonly type: ApplicationCommandOptionType.Boolean; }; }, never>.client: UsingClient
client.Client<true>.gateway: ShardManager
gateway.ShardManager.latency: number
latency;
await ctx: CommandContext<{ hide: { description: string; } & { readonly type: ApplicationCommandOptionType.Boolean; };}, never>
ctx.CommandContext<{ hide: { description: string; } & { readonly type: ApplicationCommandOptionType.Boolean; }; }, never>.write<false>(body: InteractionCreateBodyRequest, withResponse?: false | undefined): Promise<void | WebhookMessage>
write({ content?: string | undefined
The message contents (up to 2000 characters)
content: `La latencia es \`${const ping: number
ping}\``, flags?: MessageFlags | undefined
Message flags combined as a bitfield
flags, }); }}
Al CommandContext
se le añade el genérico para inferir el tipo de las opciones y poder acceder a ellas.
Más información sobre las opciones de los comandos aquí.
La estructura de tu proyecto debería verse así:
Directorysrc
Directorycommands
- ping.ts
- index.ts
- package.json
- seyfert.config.mjs
- tsconfig.json