A classe de definição do comando
Uma definição de slash command é uma classe decorada com @SlashCommand que descreve o que o Discord deve mostrar no seletor de comandos. A classe em si não contém lógica de execução. A função dela é guardar metadados que o SlashRegistrar lê no boot para montar o payload JSON enviado para a API REST do Discord. As opções são declaradas como decorators de método em um método build(), e os subcomandos são declarados em métodos nomeados. A mesma classe pode ser importada por handlers, autocomplete handlers e testes sem puxar dependências de serviço.
O decorator @SlashCommand aceita um objeto SlashCommandConfig com as propriedades abaixo. Todo comando precisa informar pelo menos name e description. O restante controla permissões padrão, disponibilidade em DM, marcação NSFW, registro específico por guild e localização.
import { SlashCommand, SlashOption } from '@spraxium/common';
import { PermissionFlagsBits } from 'discord.js';
@SlashCommand({
name: 'ban',
description: 'Ban a member from the server.',
defaultMemberPermissions: PermissionFlagsBits.BanMembers,
})
export class BanCommand {
@SlashOption.User('target', {
description: 'The member to ban',
required: true,
})
@SlashOption.String('reason', {
description: 'Reason for the ban',
required: false,
maxLength: 512,
})
@SlashOption.Integer('days', {
description: 'Number of days of messages to delete',
required: false,
min: 0,
max: 7,
})
build() {}
}Tipos de opção
O Spraxium oferece nove decorators de tipo de opção dentro do namespace SlashOption, um para cada tipo de opção de application command do Discord. Cada decorator é aplicado ao método build() da classe de comando, ou a um método decorado com @SlashSubcommand quando a opção pertence a um subcomando. Você empilha as opções no mesmo método, e a ordem dos decorators define a ordem de exibição no cliente do Discord, com o decorator mais alto aparecendo por último no seletor.
Todo decorator de opção recebe dois argumentos posicionais: o name da opção, usado no registro com o Discord e no matching de @SlashOpt, e um objeto config. Esse objeto estende SlashBaseOptionConfig com campos específicos de cada tipo.
Configuração base das opções
Os nove tipos de opção compartilham uma interface de configuração em comum. O campo description é a única propriedade obrigatória. O restante tem defaults sensatos, então você só precisa informar o que realmente importa para o seu caso.
| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
| description | string | (obrigatório) | Descrição mostrada ao usuário no seletor de opções do Discord. Aceita de 1 a 100 caracteres. |
| required | boolean | false | Define se o usuário precisa obrigatoriamente informar um valor. Quando está em false, o handler recebe null se a opção for omitida. |
| i18n | SlashI18nKeys | undefined | Chaves de localização para o nome e a descrição da opção, resolvidas pelo módulo de i18n. |
Restrições de opções string
O decorator @SlashOption.String suporta quatro propriedades extras além da configuração base. Elas refletem diretamente as restrições que a API do Discord oferece para opções string e permitem controlar comprimento do texto, choices fixos e autocomplete.
import { SlashCommand, SlashOption } from '@spraxium/common';
@SlashCommand({
name: 'echo',
description: 'Repeat a message back to the channel.',
})
export class EchoCommand {
@SlashOption.String('message', {
description: 'Text to echo',
required: true,
minLength: 1,
maxLength: 2000,
})
build() {}
}| Propriedade | Tipo | Descrição |
|---|---|---|
| minLength | number | Comprimento mínimo aceito para o texto informado. |
| maxLength | number | Comprimento máximo aceito para o texto informado. |
| choices | Lista de choices | Conjunto fixo de valores exibido como dropdown. |
| autocomplete | boolean | Ativa sugestões dinâmicas à medida que o usuário digita. |
Quando minLength ou maxLength está definido, o Discord valida o tamanho do texto no cliente antes de enviar a interação. Quando choices é informado, o usuário escolhe um valor em um dropdown em vez de digitar livremente. Quando autocomplete está em true, o Discord chama seu autocomplete handler enquanto o usuário digita, o que permite montar sugestões em tempo real a partir do seu código.
choices e autocomplete são mutuamente exclusivos
O Discord não permite combinar choices estáticos e autocomplete na mesma opção. Quando autocomplete está ativo, os choices estáticos são ignorados e apenas o autocomplete handler é consultado. Use choices para listas pequenas e fechadas, e autocomplete quando os valores forem dinâmicos, grandes ou dependerem de dados externos.
Restrições de opções numéricas
Tanto @SlashOption.Integer quanto @SlashOption.Number suportam as propriedades min e max, que definem a faixa de valores aceita. O Discord aplica essas restrições no cliente, então o handler já recebe um valor dentro do intervalo esperado, ou null quando a opção não é obrigatória e foi omitida. A variante Integer também suporta choices e autocomplete, enquanto a variante Number suporta apenas autocomplete.
import { SlashCommand, SlashOption } from '@spraxium/common';
@SlashCommand({ name: 'roll', description: 'Roll a dice.' })
export class RollCommand {
@SlashOption.Integer('sides', {
description: 'Number of sides on the dice',
required: true,
choices: [
{ name: '6-sided', value: 6 },
{ name: '12-sided', value: 12 },
{ name: '20-sided', value: 20 },
],
})
build() {}
}Filtragem por tipo de canal
O decorator @SlashOption.Channel aceita um array channelTypes que restringe quais tipos de canal o usuário pode selecionar. Os valores correspondem aos membros do enum ChannelType do Discord. Quando o array é informado, o cliente do Discord só mostra no seletor os canais compatíveis com os tipos especificados.
import { SlashCommand, SlashOption } from '@spraxium/common';
import { ChannelType } from 'discord.js';
@SlashCommand({
name: 'setup',
description: 'Configure the bot for this server.',
})
export class SetupCommand {
@SlashOption.Channel('log-channel', {
description: 'Channel for bot logs',
required: true,
channelTypes: [ChannelType.GuildText, ChannelType.GuildAnnouncement],
})
@SlashOption.Channel('voice-channel', {
description: 'Default voice channel',
required: false,
channelTypes: [ChannelType.GuildVoice, ChannelType.GuildStageVoice],
})
build() {}
}| ChannelType | Valor numérico | Descrição |
|---|---|---|
| GuildText | 0 | Canal de texto padrão dentro de uma guild. |
| GuildVoice | 2 | Canal de voz dentro de uma guild. |
| GuildCategory | 4 | Categoria que agrupa outros canais. |
| GuildAnnouncement | 5 | Canal de anúncios dentro de uma guild. |
| AnnouncementThread | 10 | Thread criada dentro de um canal de anúncios. |
| PublicThread | 11 | Thread pública aberta para todos os membros do canal. |
| PrivateThread | 12 | Thread privada visível apenas para membros convidados. |
| GuildStageVoice | 13 | Canal de palco para eventos de áudio moderados. |
| GuildForum | 15 | Canal de fórum em que cada postagem cria uma thread. |
| GuildMedia | 16 | Canal de mídia para publicações com foco em conteúdo visual. |
Localização com chaves de i18n
Tanto o decorator @SlashCommand quanto cada @SlashOption aceitam uma propriedade opcional i18n do tipo SlashI18nKeys. Esse objeto contém duas chaves string, name e description, que o módulo de i18n resolve para textos localizados durante o registro do comando. As localizações resolvidas são enviadas ao Discord no payload do comando, então o usuário enxerga nomes e descrições no idioma configurado no cliente dele.
import { SlashCommand, SlashOption } from '@spraxium/common';
@SlashCommand({
name: 'locale',
description: 'Change your preferred language.',
i18n: {
name: 'commands.locale.name',
description: 'commands.locale.description',
},
})
export class LocaleCommand {
@SlashOption.String('language', {
description: 'The language to use.',
required: true,
i18n: {
name: 'commands.locale.options.language.name',
description: 'commands.locale.options.language.description',
},
choices: [
{ name: 'English (US)', value: 'en-US' },
{ name: 'Português (Brasil)', value: 'pt-BR' },
],
})
build() {}
}As chaves de i18n seguem o mesmo padrão de resolução usado no restante do framework. Elas apontam para entradas nos arquivos JSON de locale e suportam todos os idiomas aceitos pelo Discord. Se um usuário com o cliente em português do Brasil abrir o seletor de comandos, ele verá automaticamente o nome e a descrição traduzidos do comando e das opções.
Checklist de design para definições de comando
Mantenha a definição livre de lógica de negócio
A classe do comando deve conter apenas decorators de metadados e um método build() vazio, ou métodos nomeados de subcomando. Não importe serviços, repositórios nem dependências de runtime para um arquivo de definição. Isso permite reutilizar a classe em handlers, autocomplete handlers e testes sem criar cadeias de dependência desnecessárias.
Use o tipo de opção mais específico disponível
Prefira @SlashOption.User em vez de @SlashOption.String para IDs de usuário, @SlashOption.Channel em vez de @SlashOption.String para referências de canal e @SlashOption.Integer em vez de @SlashOption.Number para números inteiros. Tipos específicos dão uma UI melhor no Discord, como seletores de usuário e dropdowns de canal, e ainda entregam objetos já resolvidos no handler.
Restrinja canais com channelTypes
Quando um comando espera um tipo específico de canal, sempre informe o array channelTypes. Isso evita que o usuário selecione um canal inválido, e o próprio cliente do Discord bloqueia a escolha antes de a interação chegar ao bot.
Prefira choices em conjuntos pequenos
Arrays estáticos de choices aparecem como dropdown e não exigem código de handler adicional. Use-os quando o conjunto de valores válidos for pequeno e fixo, normalmente abaixo de 25 entradas. Use autocomplete quando os valores forem dinâmicos, vierem do banco ou excederem esse limite.
Defina permissões já na definição do comando
Use defaultMemberPermissions para indicar o mínimo de permissões exigido para o comando aparecer no seletor. Isso funciona como um filtro de UI do Discord. Combine com @UseGuards no handler para ter a validação do lado do servidor, que não pode ser burlada.