Tutorial

Crea un bot de Discord con discord.js v14 y comandos slash

Un bot de Discord pequeño y moderno: solo comandos slash, permisos mínimos y apagado limpio.

Tutorial3 min de lecturaActualizado

Developer Portalapp + token del bottokenTu botdiscord.jsGateway (WebSocket)interactionCreateDiscordtu servidorregister.jsPUT comandos slash1ejecútalo al cambiar comandos
Figura 1. El bot mantiene abierto un WebSocket con el gateway; los comandos slash se registran una vez por REST.
  1. Crea la aplicación y el bot

    En el Developer Portal elige New Application. Copia el Application ID de General Information. En la pestaña Bot presiona Reset Token y copia el token una sola vez; Discord no lo vuelve a mostrar.

  2. Invita el bot a tu servidor de prueba

    En OAuth2, usa el generador de URL con los scopes bot y applications.commands y solo los permisos necesarios (para este bot, Send Messages). Abre la URL y elige tu servidor de prueba.

    Activa el Modo desarrollador en Discord (Configuración, Avanzado) y haz clic derecho en el servidor para copiar su ID.

  3. Crea el proyecto

    mkdir mi-bot-discord && cd mi-bot-discord
    npm init -y
    npm pkg set type=module
    npm install discord.js
    printf 'DISCORD_TOKEN=\nCLIENT_ID=\nGUILD_ID=\n' > .env
    printf '.env\nnode_modules\n' > .gitignore

    Completa .env con el token, el Application ID y el ID del servidor.

  4. Registra los comandos slash

    register.js

    import { REST, Routes, SlashCommandBuilder } from "discord.js";
    
    const { DISCORD_TOKEN, CLIENT_ID, GUILD_ID } = process.env;
    const commands = [
      new SlashCommandBuilder().setName("ping").setDescription("Check that the bot is alive"),
      new SlashCommandBuilder().setName("echo").setDescription("Repeat a text")
        .addStringOption((o) => o.setName("text").setDescription("What to repeat").setRequired(true).setMaxLength(500)),
    ].map((c) => c.toJSON());
    
    const rest = new REST().setToken(DISCORD_TOKEN);
    // Guild commands update instantly; global commands can take longer to appear.
    const route = GUILD_ID ? Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID) : Routes.applicationCommands(CLIENT_ID);
    const data = await rest.put(route, { body: commands });
    console.log(`registered ${data.length} commands${GUILD_ID ? " in guild " + GUILD_ID : " globally"}`);
    node --env-file=.env register.js
  5. Responde los comandos

    index.js

    import { Client, Events, GatewayIntentBits, MessageFlags } from "discord.js";
    
    // Slash commands only need the Guilds intent: no privileged intents required.
    const client = new Client({ intents: [GatewayIntentBits.Guilds] });
    
    client.once(Events.ClientReady, (c) => console.log(`logged in as ${c.user.tag}`));
    
    client.on(Events.InteractionCreate, async (interaction) => {
      if (!interaction.isChatInputCommand()) return;
      try {
        if (interaction.commandName === "ping") {
          await interaction.reply(`pong (${client.ws.ping} ms)`);
        } else if (interaction.commandName === "echo") {
          const text = interaction.options.getString("text", true);
          // allowedMentions stops the bot from pinging @everyone on behalf of a user
          await interaction.reply({ content: text, allowedMentions: { parse: [] } });
        }
      } catch (err) {
        console.error("interaction failed:", err.message);
        if (!interaction.replied) await interaction.reply({ content: "Something went wrong.", flags: MessageFlags.Ephemeral }).catch(() => {});
      }
    });
    
    process.once("SIGTERM", () => client.destroy().finally(() => process.exit(0)));
    await client.login(process.env.DISCORD_TOKEN);

    Debes responder una interacción en menos de 3 segundos. Para trabajo lento llama primero a interaction.deferReply() y edita la respuesta después.

  6. Ejecuta y prueba

    node --env-file=.env index.js
    ~/mi-bot-discord$ node --env-file=.env register.jsregistered 2 commands in guild 1234567890$ node --env-file=.env index.jslogged in as MiBot#12341instantáneo en un servidor de prueba2el bot aparece en línea
    Figura 2. Salida esperada (ilustración).

    Escribe /ping en tu servidor. El bot solo está en línea mientras el proceso corre, así que el siguiente paso es desplegarlo en un hosting siempre encendido.

Solución de problemas

  • No aparecen los comandos: revisa que la invitación incluyera applications.commands y que GUILD_ID sea el servidor correcto. Los comandos globales (sin GUILD_ID) pueden tardar.
  • Used disallowed intents: pediste un intent privilegiado (Message Content, Members, Presence) que no está activado en la pestaña Bot. Los bots con comandos slash no los necesitan.
  • La interacción falló: la respuesta tardó más de 3 segundos o lanzó un error. Revisa la consola y usa deferReply para trabajo lento.

Dónde correrlo 24/7: el mejor hosting gratuito para bots de Discord, tutorial de Railway, VPS con systemd. Documentación oficial: guía de discord.js, application commands.