Construye un bot con /dado: el usuario elige el número de caras y recibe una tirada privada. El comando se registra por HTTP en un servidor de prueba; un proceso separado recibe las interacciones por Gateway y las responde.
Última revisión: 2 de octubre de 2026. Probado localmente con: Node.js 24.15.0 y discord.js 14.27.0: imports, construcción del comando y manejador con interacciones simuladas. No se conectó una aplicación real a Discord durante la revisión. Usa Node.js 24 LTS actualizado y conserva la versión de discord.js indicada.
Crea una aplicación de prueba
En el Developer Portal crea una aplicación, copia su Application ID y genera un token en Bot. En Installation habilita Guild Install y los scopes bot y applications.commands. Instálala en un servidor que administres; para este ejercicio basta permitir al bot ver el canal y enviar mensajes, sin Administrator.
Activa el modo desarrollador en tu cliente Discord y copia el ID del servidor. No confundas Application ID con Guild ID. Deja vacío Interactions Endpoint URL: este ejemplo recibe eventos por Gateway. Un endpoint HTTP de interacciones configurado cambiaría la vía de recepción.
En una carpeta nueva ejecuta:
npm init -y
npm install --save-exact [email protected]Usaremos archivos .mjs, por lo que no necesitas cambiar el campo type de package.json. Crea .env y rellena sus valores localmente:
DISCORD_TOKEN=
CLIENT_ID=
GUILD_ID=Añade .env y node_modules/ al .gitignore. Nunca publiques el token ni el archivo: quien lo obtiene puede actuar como tu bot. Si lo filtras, regenera el token en el portal y cambia el secreto del proceso. Los IDs identifican recursos; no sustituyen al token.
Define y registra el comando
Guarda commands.mjs:
import { SlashCommandBuilder } from 'discord.js';
export const commands = [
new SlashCommandBuilder()
.setName('dado')
.setDescription('Tira un dado y recibe el resultado en privado')
.addIntegerOption(option => option
.setName('caras')
.setDescription('Entre 2 y 100 caras; por defecto 6')
.setMinValue(2)
.setMaxValue(100))
.toJSON(),
];La opción es entera y opcional. Discord ofrece una interfaz adecuada para introducirla y conoce sus límites. Guarda deploy.mjs:
import { REST, Routes } from 'discord.js';
import { commands } from './commands.mjs';
const { DISCORD_TOKEN, CLIENT_ID, GUILD_ID } = process.env;
if (![DISCORD_TOKEN, CLIENT_ID, GUILD_ID].every(Boolean)) {
throw new Error('Faltan variables de entorno');
}
const rest = new REST({ version: '10' }).setToken(DISCORD_TOKEN);
await rest.put(Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID), {
body: commands,
});
console.log('Comandos registrados en el servidor de prueba');Este PUT sustituye el conjunto de comandos de esta aplicación en ese servidor. Utilízalo con una aplicación de prueba; si añades más comandos, todos deben estar en el array. No ejecutes el registro en cada conexión del bot. Vuelve a ejecutarlo cuando cambies la definición, no cuando solo cambies la lógica.

Recibe y responde
Guarda handler.mjs:
import { randomInt } from 'node:crypto';
import { MessageFlags } from 'discord.js';
export async function handle(interaction) {
if (!interaction.isChatInputCommand() || interaction.commandName !== 'dado') return;
const faces = interaction.options.getInteger('caras') ?? 6;
if (!Number.isInteger(faces) || faces < 2 || faces > 100) {
await interaction.reply({ content: 'Elige entre 2 y 100 caras.', flags: MessageFlags.Ephemeral });
return;
}
const result = randomInt(1, faces + 1);
await interaction.reply({
content: `Dado de ${faces} caras: ${result}`,
flags: MessageFlags.Ephemeral,
allowedMentions: { parse: [] },
});
}randomInt excluye su límite superior; por eso usamos faces + 1. La validación también vive en el manejador y la respuesta Ephemeral solo se muestra a quien llamó al comando. Guarda bot.mjs:
import { Client, Events, GatewayIntentBits } from 'discord.js';
import { handle } from './handler.mjs';
if (!process.env.DISCORD_TOKEN) throw new Error('Falta DISCORD_TOKEN');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.once(Events.ClientReady, ready => console.log(`Conectado: ${ready.user.tag}`));
client.on(Events.InteractionCreate, interaction => {
handle(interaction).catch(error => console.error('No se pudo responder:', error.code ?? error.name));
});
await client.login(process.env.DISCORD_TOKEN);No necesitamos MessageContent ni otros intents privilegiados: no leemos mensajes escritos en el chat. Ejecuta primero node --env-file=.env deploy.mjs y después node --env-file=.env bot.mjs. En el servidor usa /dado caras:20; el resultado debe estar entre 1 y 20. Detén el proceso con Ctrl+C.
Si el comando no aparece o no responde
Comprueba que registraste el Guild ID correcto y que instalaste la misma aplicación cuyo CLIENT_ID y token utilizas. Revisa también los permisos del usuario para utilizar comandos de aplicación en ese canal. Un token inválido se corrige regenerándolo, no activando Administrator.
Discord exige una respuesta inicial en tres segundos. Este cálculo responde directamente; si después agregas una consulta lenta, empieza con deferReply({ flags: MessageFlags.Ephemeral }) y termina con editReply(...). No llames a reply dos veces sobre la misma interacción. El token de interacción tiene una ventana de 15 minutos para las respuestas posteriores; no lo uses como mecanismo de tareas de larga duración.
Fuentes y documentación
Proyecto: discord.js, equipo discord.js y colaboradores. Página y documentación: discord.js.org. Descarga oficial: npm, versión 14.27.0. Licencia: Apache 2.0. El paquete y su README etiquetados declaran Node 18+; se eligió Node 24 LTS para este ejemplo, sin asumir que el README de desarrollo describe todas las versiones publicadas. Node.js oficial, equipo Node.js, licencia MIT y licencias de componentes. Código del ejemplo y gráficos originales de The Silencers; no se redistribuye la librería.






Comments 0
Join the conversation with your account.
Sign inLoading comments…