📌 Responders
Os responders permitem lidar com interações dinâmicas no Discord, como botões, selects e modais, usando customId com parâmetros.
🚀 Como funciona
- 🎯 O matching é feito pelo
customId. - 🧠
createResponder(...)registra automaticamente a rota. - 🧩 Suporta parâmetros dinâmicos (
:param). - 🔎
runrecebeinteraction+params. - ⚙️ Suporte a
lifetimeecache.
Responders não são carregados automaticamente.
Você precisa importar o arquivo em algum ponto do bot.
Se o responder for criado por um comando/evento, esse comando/evento deve estar dentro dos diretórios configurados em paths.commands ou paths.events.
🔗 Fluxo com comando
- O comando cria um componente com
customId. - O responder escuta esse mesmo
customId.
🧪 Exemplo
- JavaScript
- TypeScript
const { createCommand, createResponder, CommandType, ResponderType } = require('nexocord');
const { ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
// responder
createResponder({
customId: "user/button",
type: ResponderType.Button,
async run(interaction) {
await interaction.reply("Botão clicado!");
},
});
// comando
module.exports = createCommand({
// ...
async run(interaction) {
const row = new ActionRowBuilder().addComponents(
new ButtonBuilder()
.setCustomId("user/button")
.setLabel("Clicar")
.setStyle(ButtonStyle.Success)
);
// ...
},
});
import { createCommand, createResponder, CommandType, ResponderType } from 'nexocord';
import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
// responder
createResponder({
customId: "user/button",
type: ResponderType.Button,
async run(interaction) {
await interaction.reply("Botão clicado!");
},
});
// comando
export default createCommand({
...
async run(interaction) {
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setCustomId("user/button")
.setLabel('Clicar')
.setStyle(ButtonStyle.Success),
);
...
},
});
Declare o responder fora do run para evitar múltiplos registros.
📋 Campos do responder
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
customId | string | Sim | Rota da interação |
type | ResponderType | ResponderType[] | Sim | Tipo(s) de interação |
run | function | Sim | Função executada |
parse | ZodTypeAny | (params) => any | Não | Validação |
lifetime | 'once' | 'temporary' | Não | Controle de uso |
expire | number | Não* | Expiração (ms) |
cache | 'cached' | 'guild' | Não | Contexto |
Se usar temporary, expire é obrigatório.
🎯 Tipos de responder
- Button
- Modal
- Selects
type: ResponderType.Button
type: ResponderType.Modal
type: ResponderType.SelectString,
type: ResponderType.SelectUser,
type: ResponderType.SelectRole,
type: ResponderType.SelectChannel,
type: ResponderType.SelectMentionable,
Também é possível registrar o mesmo responder para mais de um tipo:
type: [ResponderType.Button, ResponderType.SelectString]
Nesse caso, o interaction vira uma união dos dois tipos e você deve fazer guarda de tipo no run:
createResponder({
customId: 'responder',
type: [ResponderType.Button, ResponderType.SelectString],
async run(interaction) {
if (interaction.isButton()) {
await interaction.reply(`Botão clicado`);
return;
}
if (interaction.isStringSelectMenu()) {
await interaction.reply(`Select usado: ${interaction.values.join(', ')}`);
}
},
});
🧱 Estrutura da função run
A função run recebe dois parâmetros:
run(interaction, params)
- interaction → interação do Discord tipada automaticamente
- params → parâmetros extraídos do
customId
🧩 Parâmetros na rota
Parâmetros são definidos usando : dentro do customId.
Exemplo:
customId: 'responder/:id'
Também é possível ter múltiplos parâmetros:
customId: 'responder/:id/:name'
No run, os valores chegam em params:
async run(interaction, params) {
// params.id, params.name...
}
Destructuring de parâmetros
Você também pode usar destructuring diretamente nos parâmetros:
async run(interaction, { id, name }) {
// id, name..
}
🧠 Parse de parâmetros
O parse permite transformar ou validar os parâmetros antes de executar o responder.
Ele pode ser:
- um schema Zod
- uma função manual
parse: schema.parse
// ou
parse: (params) => ({ id: Number(params.id) })
Se nenhum parse for definido, os parâmetros serão strings.
⏱️ Lifetime e expiração
Os responders podem ter controle de uso usando lifetime.
lifetime: 'once'
Permite usar o responder apenas uma vez.
lifetime: 'once',
lifetime: 'temporary'
Permite usar o responder por um tempo limitado.
lifetime: 'temporary',
expire: 15_000
🔐 Cache e contexto
Use cache para limitar onde a interação pode rodar:
cache: 'guild'→ exige servidorcache: 'cached'→ exige servidor em cache
Quando a regra não é atendida, o bot responde com mensagem efêmera automaticamente.
✅ Boas práticas
- 🧱 Use padrões de
customId(grupo/:id/acao) - 🧪 Valide parâmetros com
parse - ⏳ Use
temporarypara ações sensíveis - 🔁 Use
onceem confirmações únicas - 🧼 Separe lógicas pesadas
❌ Erros comuns
- Declarar
temporarysemexpire(gera erro no registro) - Não importar o arquivo de responder em nenhum ponto do boot
- Misturar
customIdsem padrão, dificultando manutenção - Ignorar validação de
paramsem fluxos críticos