Guía 03

Cómo crear un bot de Discord: aplicaciones, intents, comandos slash e interacciones

Los bots modernos de Discord giran en torno a las interacciones: comandos slash, botones, menús de selección y modales. Si aciertas con el modelo de permisos, lo demás es sencillo.

Guía4 min de lecturaActualizado

Crear la aplicación

  1. Entra en el Developer Portal y crea una aplicación.
  2. En Bot, restablece y copia el token. Guárdalo en un gestor de secretos. Desactiva Public Bot mientras desarrollas si no quieres que otros lo instalen.
  3. Anota el Application ID y la Public Key en General Information. La clave pública sirve para verificar las interacciones HTTP.

Enlace de instalación, scopes y permisos

Los bots entran en los servidores mediante una URL OAuth2. Normalmente necesitas los scopes bot y applications.commands. El valor permissions es un campo de bits: pide solo lo que usan tus funciones.

https://discord.com/oauth2/authorize?client_id=APP_ID&scope=bot%20applications.commands&permissions=2147485696

Ese valor de ejemplo (2048 + 2147483648) concede solo Enviar mensajes y Usar comandos de aplicación. Evita Administrator: un token comprometido con permisos de administrador puede arrasar un servidor. Los dueños de servidores confían más cuando la pantalla de instalación pide poco.

Intents del Gateway

Si tu bot mantiene una conexión WebSocket con el Gateway, se suscribe a grupos de eventos llamados intents. Tres son privilegiados y deben activarse en el portal:

  • GUILD_MEMBERS: altas, bajas y cambios de miembros.
  • GUILD_PRESENCES: estado en línea y actividades.
  • MESSAGE_CONTENT: el texto de mensajes que no mencionan al bot.

La mayoría de bots nuevos no necesita MESSAGE_CONTENT: los comandos slash entregan sus opciones como datos estructurados. Cuando un bot está en 100 o más servidores debe verificarse, y los intents privilegiados requieren aprobación con justificación.

Comandos slash con discord.js

import { Client, GatewayIntentBits, REST, Routes, SlashCommandBuilder, MessageFlags } from "discord.js";

const commands = [
  new SlashCommandBuilder().setName("ping").setDescription("Comprueba que el bot está vivo"),
  new SlashCommandBuilder().setName("remind").setDescription("Crea un recordatorio")
    .addIntegerOption(o => o.setName("minutes").setDescription("En cuántos minutos").setRequired(true).setMinValue(1).setMaxValue(1440))
    .addStringOption(o => o.setName("text").setDescription("Qué recordarte").setRequired(true).setMaxLength(200)),
].map(c => c.toJSON());

// Registro único (paso de despliegue). Los comandos de servidor se actualizan al momento; los globales pueden tardar.
await new REST().setToken(process.env.DISCORD_TOKEN)
  .put(Routes.applicationGuildCommands(process.env.APP_ID, process.env.DEV_GUILD_ID), { body: commands });

const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.on("interactionCreate", async (i) => {
  if (!i.isChatInputCommand()) return;
  if (i.commandName === "ping") return i.reply({ content: `Pong: ${client.ws.ping} ms`, flags: MessageFlags.Ephemeral });
  if (i.commandName === "remind") {
    await i.deferReply({ flags: MessageFlags.Ephemeral });       // gana tiempo más allá de 3 s
    await scheduleReminder(i.user.id, i.options.getInteger("minutes"), i.options.getString("text"));
    await i.editReply("Guardado. Te escribiré por mensaje directo.");
  }
});
client.login(process.env.DISCORD_TOKEN);

La regla de los 3 segundos

Cada interacción debe recibir una respuesta inicial en 3 segundos, o el usuario verá "La aplicación no ha respondido". Si el trabajo tarda más, difiere (deferReply o tipo de respuesta 5) y luego edita o envía mensajes de seguimiento. El token de la interacción sigue siendo válido 15 minutos.

Las respuestas efímeras solo las ve quien ejecutó el comando: úsalas para errores, confirmaciones y todo lo personal.

Componentes y modales

Los botones y menús llevan un custom_id (hasta 100 caracteres) para enrutar. Codifica una acción corta y un ID, nunca lo uses como autorización y vuelve a comprobar permisos en cada clic. Los modales recogen texto y son ideales para reportes o formularios.

Interacciones HTTP en lugar del Gateway

Si tu bot solo usa comandos y componentes, no necesitas una conexión persistente. Configura una Interactions Endpoint URL en el portal y Discord te enviará cada interacción por POST. Funciona muy bien en plataformas serverless como Cloudflare Workers.

Debes verificar cada petición con la firma Ed25519 de X-Signature-Ed25519 sobre X-Signature-Timestamp + cuerpoCrudo usando la clave pública de tu aplicación, y responder a type: 1 (PING) con {"type":1}. Discord envía firmas inválidas a propósito para ponerte a prueba.

import { verifyKey } from "discord-interactions";

const ok = await verifyKey(rawBody, req.headers.get("X-Signature-Ed25519"),
                           req.headers.get("X-Signature-Timestamp"), env.DISCORD_PUBLIC_KEY);
if (!ok) return new Response("firma no válida", { status: 401 });
const interaction = JSON.parse(rawBody);
if (interaction.type === 1) return Response.json({ type: 1 });

Embeds y webhooks

Los embeds son tarjetas enriquecidas con título, descripción, campos, imágenes y color. Diseña uno con el constructor de embeds de Discord. Para avisos unidireccionales (resultados de CI, monitorización) un webhook de canal es más simple que un bot: no necesita token ni Gateway, solo la URL del webhook, que debes mantener en secreto.

Configura siempre allowed_mentions para que un texto del usuario no pueda mencionar a @everyone.

Límites de tasa

Discord devuelve cabeceras de límite en cada respuesta REST (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset-After, X-RateLimit-Bucket) y 429 con retry_after cuando superas un bucket. También hay un límite global de unas 50 peticiones por segundo por bot. Las bibliotecas principales encolan y respetan esto automáticamente; si llamas a la API directamente, hazlo tú. Ignorar repetidamente los 429 puede provocar un bloqueo temporal de IP.

Lo esencial de las políticas

Sigue la Developer Policy: no guardes el contenido de mensajes más de lo necesario, no vendas ni compartas datos, indica en el portal URLs de política de privacidad y términos, y no crees self-bots (automatizar una cuenta de usuario va contra los términos).

Comprueba lo aprendido

1. ¿Cuánto tiempo tienes para la respuesta inicial a una interacción?
2. ¿Qué intent es privilegiado?