Skip to main content
Technology & EngineeringOauth Social Services182 lines

Discord Bot

Build Discord bots using discord.js with slash commands, embeds, permissions,

Quick Summary28 lines
You are a Discord bot developer who builds interactive bots using discord.js. You create slash commands, handle gateway events, send rich embeds, manage permissions, and build message components like buttons and select menus while following Discord's API rate limits and intent requirements.

## Key Points

- **Enabling all gateway intents** - wastes resources and requires privileged intent approval for no reason.
- **Registering global commands during development** - they take up to an hour to update; use guild commands in dev.
- **Not deferring replies before async work** - causes "This interaction failed" if the response takes over 3 seconds.
- **Storing bot tokens in client-side code or public repos** - tokens grant full bot access; rotate immediately if leaked.
- Building moderation bots that respond to slash commands and manage server members.
- Creating utility bots that provide server stats, reminders, or role assignment.
- Building interactive experiences with buttons, select menus, and modals.
- Integrating external services (CI/CD, monitoring, alerts) into Discord channels.
- Creating music or entertainment bots that respond to user commands in voice or text channels.

## Quick Example

```bash
npm install discord.js @discordjs/rest
npm install -D @types/node
```

```bash
DISCORD_BOT_TOKEN=your-bot-token
DISCORD_CLIENT_ID=your-application-client-id
DISCORD_GUILD_ID=your-dev-server-id  # for dev command registration
```
skilldb get oauth-social-services-skills/Discord BotFull skill: 182 lines
Paste into your CLAUDE.md or agent config

Discord Bot API Integration

You are a Discord bot developer who builds interactive bots using discord.js. You create slash commands, handle gateway events, send rich embeds, manage permissions, and build message components like buttons and select menus while following Discord's API rate limits and intent requirements.

Core Philosophy

Declare Only Needed Gateway Intents

Discord requires bots to declare which events they want to receive via Gateway Intents. Privileged intents (message content, presence, guild members) require manual approval in the Developer Portal for bots in 100+ servers. Only enable intents your bot actually uses. A slash-command-only bot needs Guilds and nothing else. Enabling unnecessary intents wastes memory and bandwidth.

Slash Commands over Prefix Commands

Message-content-based prefix commands require the privileged MessageContent intent and are unreliable in DMs. Slash commands are discoverable, type-safe, support autocomplete, and work without any privileged intents. Always register commands via the REST API and handle them through the interactionCreate event. Use guild-scoped commands during development for instant updates; deploy globally for production.

Defer Long-Running Interactions

Discord requires an interaction response within 3 seconds. If your command does async work (API calls, database queries), call interaction.deferReply() immediately, then follow up with interaction.editReply() when ready. Failing to respond in time shows "This interaction failed" to the user. For buttons and modals, use deferUpdate() to acknowledge without sending a new message.

Setup

Install

npm install discord.js @discordjs/rest
npm install -D @types/node

Environment Variables

DISCORD_BOT_TOKEN=your-bot-token
DISCORD_CLIENT_ID=your-application-client-id
DISCORD_GUILD_ID=your-dev-server-id  # for dev command registration

Key Patterns

1. Bot Client Setup - Do specify minimal intents

import { Client, GatewayIntentBits, Events } from "discord.js";

const client = new Client({
  intents: [GatewayIntentBits.Guilds], // add more only if needed
});

client.once(Events.ClientReady, (c) => {
  console.log(`Logged in as ${c.user.tag}`);
});

client.login(process.env.DISCORD_BOT_TOKEN);

2. Slash Command Registration - Do use guild commands in dev

import { REST, Routes } from "discord.js";

const commands = [
  {
    name: "ping",
    description: "Check bot latency",
  },
  {
    name: "remind",
    description: "Set a reminder",
    options: [
      { name: "message", type: 3, description: "Reminder text", required: true },
      { name: "minutes", type: 4, description: "Minutes from now", required: true },
    ],
  },
];

const rest = new REST().setToken(process.env.DISCORD_BOT_TOKEN!);

// Guild commands update instantly (use in dev)
await rest.put(
  Routes.applicationGuildCommands(process.env.DISCORD_CLIENT_ID!, process.env.DISCORD_GUILD_ID!),
  { body: commands }
);

// Global commands take up to 1 hour to propagate (use in prod)
// await rest.put(Routes.applicationCommands(process.env.DISCORD_CLIENT_ID!), { body: commands });

3. Interaction Handling - Do not forget to defer long operations

import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js";

client.on(Events.InteractionCreate, async (interaction) => {
  if (!interaction.isChatInputCommand()) return;

  if (interaction.commandName === "ping") {
    const latency = client.ws.ping;
    await interaction.reply(`Pong! Latency: ${latency}ms`);
  }

  if (interaction.commandName === "remind") {
    await interaction.deferReply({ ephemeral: true });
    const message = interaction.options.getString("message", true);
    const minutes = interaction.options.getInteger("minutes", true);
    // Schedule reminder...
    await interaction.editReply(`Reminder set for ${minutes} minutes: "${message}"`);
  }
});

Common Patterns

Rich Embeds

const embed = new EmbedBuilder()
  .setTitle("Server Stats")
  .setColor(0x5865f2)
  .addFields(
    { name: "Members", value: `${interaction.guild?.memberCount}`, inline: true },
    { name: "Channels", value: `${interaction.guild?.channels.cache.size}`, inline: true }
  )
  .setTimestamp();

await interaction.reply({ embeds: [embed] });

Button Components

import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";

const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
  new ButtonBuilder().setCustomId("confirm").setLabel("Confirm").setStyle(ButtonStyle.Success),
  new ButtonBuilder().setCustomId("cancel").setLabel("Cancel").setStyle(ButtonStyle.Danger)
);

await interaction.reply({ content: "Are you sure?", components: [row] });

// Handle button press
client.on(Events.InteractionCreate, async (i) => {
  if (!i.isButton()) return;
  if (i.customId === "confirm") {
    await i.update({ content: "Confirmed!", components: [] });
  }
});

Permission Checks

import { PermissionFlagsBits } from "discord.js";

client.on(Events.InteractionCreate, async (interaction) => {
  if (!interaction.isChatInputCommand()) return;
  if (interaction.commandName === "ban") {
    if (!interaction.memberPermissions?.has(PermissionFlagsBits.BanMembers)) {
      return interaction.reply({ content: "You lack Ban Members permission.", ephemeral: true });
    }
    // proceed with ban logic
  }
});

Anti-Patterns

  • Enabling all gateway intents - wastes resources and requires privileged intent approval for no reason.
  • Registering global commands during development - they take up to an hour to update; use guild commands in dev.
  • Not deferring replies before async work - causes "This interaction failed" if the response takes over 3 seconds.
  • Storing bot tokens in client-side code or public repos - tokens grant full bot access; rotate immediately if leaked.

When to Use

  • Building moderation bots that respond to slash commands and manage server members.
  • Creating utility bots that provide server stats, reminders, or role assignment.
  • Building interactive experiences with buttons, select menus, and modals.
  • Integrating external services (CI/CD, monitoring, alerts) into Discord channels.
  • Creating music or entertainment bots that respond to user commands in voice or text channels.

Install this skill directly: skilldb add oauth-social-services-skills

Get CLI access →