discord-bot-architect

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Discord Bot Architect

Discord机器人架构师

Patterns

设计模式

Discord.js v14 Foundation

Discord.js v14基础架构

Modern Discord bot setup with Discord.js v14 and slash commands
When to use: ['Building Discord bots with JavaScript/TypeScript', 'Need full gateway connection with events', 'Building bots with complex interactions']
javascript
```javascript
// src/index.js
const { Client, Collection, GatewayIntentBits, Events } = require('discord.js');
const fs = require('node:fs');
const path = require('node:path');
require('dotenv').config();

// Create client with minimal required intents
const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    // Add only what you need:
    // GatewayIntentBits.GuildMessages,
    // GatewayIntentBits.MessageContent,  // PRIVILEGED - avoid if possible
  ]
});

// Load commands
client.commands = new Collection();
const commandsPath = path.join(__dirname, 'commands');
const commandFiles = fs.readdirSync(commandsPath).filter(f => f.endsWith('.js'));

for (const file of commandFiles) {
  const filePath = path.join(commandsPath, file);
  const command = require(filePath);
  if ('data' in command && 'execute' in command) {
    client.commands.set(command.data.name, command);
  }
}

// Load events
const eventsPath = path.join(__dirname, 'events');
const eventFiles = fs.readdirSync(eventsPath).filter(f => f.endsWith('.js'));

for (const file of eventFiles) {
  const filePath = path.join(eventsPath, file);
  const event = require(filePath);
  if (event.once) {
    client.once(event.name, (...args) => event.execute(...args));
  } else {
    client.on(event.name, (...args) => event.execute(...args));
  }
}

client.login(process.env.DISCORD_TOKEN);
javascript
// src/commands/ping.js
const { SlashCommandBuilder } = require('discord.js');

module.exports = {
  data: new SlashCommandBuilder()
    .setName('ping')
    .setDescription('Replies with Pong!'),

  async execute(interaction) {
    const sent = await interaction.reply({
      content: 'Pinging...',
      fetchReply: true
    });

    const latency = sent.createdTimestamp - interaction.createdTimestamp;
    await interaction.editReply(`Pong! Latency: ${latency}ms`);
  }
};
javascript
// src/events/interactionCreate.js
const { Events } = require('discord.js');

module.exports = {
  name: Event
基于Discord.js v14和斜杠命令的现代Discord机器人搭建方案
适用场景:['使用JavaScript/TypeScript构建Discord机器人', '需要带事件的完整网关连接', '构建具有复杂交互的机器人']
javascript
// src/index.js
const { Client, Collection, GatewayIntentBits, Events } = require('discord.js');
const fs = require('node:fs');
const path = require('node:path');
require('dotenv').config();

// Create client with minimal required intents
const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    // Add only what you need:
    // GatewayIntentBits.GuildMessages,
    // GatewayIntentBits.MessageContent,  // PRIVILEGED - avoid if possible
  ]
});

// Load commands
client.commands = new Collection();
const commandsPath = path.join(__dirname, 'commands');
const commandFiles = fs.readdirSync(commandsPath).filter(f => f.endsWith('.js'));

for (const file of commandFiles) {
  const filePath = path.join(commandsPath, file);
  const command = require(filePath);
  if ('data' in command && 'execute' in command) {
    client.commands.set(command.data.name, command);
  }
}

// Load events
const eventsPath = path.join(__dirname, 'events');
const eventFiles = fs.readdirSync(eventsPath).filter(f => f.endsWith('.js'));

for (const file of eventFiles) {
  const filePath = path.join(eventsPath, file);
  const event = require(filePath);
  if (event.once) {
    client.once(event.name, (...args) => event.execute(...args));
  } else {
    client.on(event.name, (...args) => event.execute(...args));
  }
}

client.login(process.env.DISCORD_TOKEN);
javascript
// src/commands/ping.js
const { SlashCommandBuilder } = require('discord.js');

module.exports = {
  data: new SlashCommandBuilder()
    .setName('ping')
    .setDescription('Replies with Pong!'),

  async execute(interaction) {
    const sent = await interaction.reply({
      content: 'Pinging...',
      fetchReply: true
    });

    const latency = sent.createdTimestamp - interaction.createdTimestamp;
    await interaction.editReply(`Pong! Latency: ${latency}ms`);
  }
};
javascript
// src/events/interactionCreate.js
const { Events } = require('discord.js');

module.exports = {
  name: Event

Pycord Bot Foundation

Pycord机器人基础架构

Discord bot with Pycord (Python) and application commands
When to use: ['Building Discord bots with Python', 'Prefer async/await patterns', 'Need good slash command support']
python
```python
基于Pycord(Python)和应用命令的Discord机器人方案
适用场景:['使用Python构建Discord机器人', '偏好async/await模式', '需要完善的斜杠命令支持']
python
undefined

main.py

main.py

import os import discord from discord.ext import commands from dotenv import load_dotenv
load_dotenv()
import os import discord from discord.ext import commands from dotenv import load_dotenv
load_dotenv()

Configure intents - only enable what you need

Configure intents - only enable what you need

intents = discord.Intents.default()
intents = discord.Intents.default()

intents.message_content = True # PRIVILEGED - avoid if possible

intents.message_content = True # PRIVILEGED - avoid if possible

intents.members = True # PRIVILEGED

intents.members = True # PRIVILEGED

bot = commands.Bot( command_prefix="!", # Legacy, prefer slash commands intents=intents )
@bot.event async def on_ready(): print(f"Logged in as {bot.user}") # Sync commands (do this carefully - see sharp edges) # await bot.sync_commands()
bot = commands.Bot( command_prefix="!", # Legacy, prefer slash commands intents=intents )
@bot.event async def on_ready(): print(f"Logged in as {bot.user}") # Sync commands (do this carefully - see sharp edges) # await bot.sync_commands()

Slash command

Slash command

@bot.slash_command(name="ping", description="Check bot latency") async def ping(ctx: discord.ApplicationContext): latency = round(bot.latency * 1000) await ctx.respond(f"Pong! Latency: {latency}ms")
@bot.slash_command(name="ping", description="Check bot latency") async def ping(ctx: discord.ApplicationContext): latency = round(bot.latency * 1000) await ctx.respond(f"Pong! Latency: {latency}ms")

Slash command with options

Slash command with options

@bot.slash_command(name="greet", description="Greet a user") async def greet( ctx: discord.ApplicationContext, user: discord.Option(discord.Member, "User to greet"), message: discord.Option(str, "Custom message", required=False) ): msg = message or "Hello!" await ctx.respond(f"{user.mention}, {msg}")
@bot.slash_command(name="greet", description="Greet a user") async def greet( ctx: discord.ApplicationContext, user: discord.Option(discord.Member, "User to greet"), message: discord.Option(str, "Custom message", required=False) ): msg = message or "Hello!" await ctx.respond(f"{user.mention}, {msg}")

Load cogs

Load cogs

for filename in os.listdir("./cogs"): if filename.endswith(".py"): bot.load_extension(f"cogs.{filename[:-3]}")
bot.run(os.environ["DISCORD_TOKEN"])

```python
for filename in os.listdir("./cogs"): if filename.endswith(".py"): bot.load_extension(f"cogs.{filename[:-3]}")
bot.run(os.environ["DISCORD_TOKEN"])

```python

cogs/general.py

cogs/general.py

import discord from discord.ext import commands
class General(commands.Cog): def init(self, bot): self.bot = bot
@commands.slash_command(name="info", description="Bot information")
async def info(self, ctx: discord.ApplicationContext):
    embed = discord.Embed(
        title="Bot Info",
        description="A helpful Discord bot",
        color=discord.Color.blue()
    )
    embed.add_field(name="Servers", value=len(self.bot.guilds))
    embed.add_field(name="Latency", value=f"{round(self.bot.latency * 1000)}ms")
    await ctx.respond(embed=embed)

@commands.Cog.
undefined
import discord from discord.ext import commands
class General(commands.Cog): def init(self, bot): self.bot = bot
@commands.slash_command(name="info", description="Bot information")
async def info(self, ctx: discord.ApplicationContext):
    embed = discord.Embed(
        title="Bot Info",
        description="A helpful Discord bot",
        color=discord.Color.blue()
    )
    embed.add_field(name="Servers", value=len(self.bot.guilds))
    embed.add_field(name="Latency", value=f"{round(self.bot.latency * 1000)}ms")
    await ctx.respond(embed=embed)

@commands.Cog.
undefined

Interactive Components Pattern

交互组件模式

Using buttons, select menus, and modals for rich UX
When to use: ['Need interactive user interfaces', 'Collecting user input beyond slash command options', 'Building menus, confirmations, or forms']
python
```javascript
// Discord.js - Buttons and Select Menus
const {
  SlashCommandBuilder,
  ActionRowBuilder,
  ButtonBuilder,
  ButtonStyle,
  StringSelectMenuBuilder,
  ModalBuilder,
  TextInputBuilder,
  TextInputStyle
} = require('discord.js');

module.exports = {
  data: new SlashCommandBuilder()
    .setName('menu')
    .setDescription('Shows an interactive menu'),

  async execute(interaction) {
    // Button row
    const buttonRow = new ActionRowBuilder()
      .addComponents(
        new ButtonBuilder()
          .setCustomId('confirm')
          .setLabel('Confirm')
          .setStyle(ButtonStyle.Primary),
        new ButtonBuilder()
          .setCustomId('cancel')
          .setLabel('Cancel')
          .setStyle(ButtonStyle.Danger),
        new ButtonBuilder()
          .setLabel('Documentation')
          .setURL('https://discord.js.org')
          .setStyle(ButtonStyle.Link)  // Link buttons don't emit events
      );

    // Select menu row (one per row, takes all 5 slots)
    const selectRow = new ActionRowBuilder()
      .addComponents(
        new StringSelectMenuBuilder()
          .setCustomId('select-role')
          .setPlaceholder('Select a role')
          .setMinValues(1)
          .setMaxValues(3)
          .addOptions([
            { label: 'Developer', value: 'dev', emoji: '💻' },
            { label: 'Designer', value: 'design', emoji: '🎨' },
            { label: 'Community', value: 'community', emoji: '🎉' }
          ])
      );

    await interaction.reply({
      content: 'Choose an option:',
      components: [buttonRow, selectRow]
    });

    // Collect responses
    const collector = interaction.channel.createMessageComponentCollector({
      filter: i => i.user.id === interaction.user.id,
      time: 60_000  // 60 seconds timeout
    });

    collector.on('collect', async i => {
      if (i.customId === 'confirm') {
        await i.update({ content: 'Confirmed!', components: [] });
        collector.stop();
      } else if (i.custo
使用按钮、选择菜单和模态框实现丰富的用户体验
适用场景:['需要交互式用户界面', '收集斜杠命令选项之外的用户输入', '构建菜单、确认框或表单']
javascript
// Discord.js - Buttons and Select Menus
const {
  SlashCommandBuilder,
  ActionRowBuilder,
  ButtonBuilder,
  ButtonStyle,
  StringSelectMenuBuilder,
  ModalBuilder,
  TextInputBuilder,
  TextInputStyle
} = require('discord.js');

module.exports = {
  data: new SlashCommandBuilder()
    .setName('menu')
    .setDescription('Shows an interactive menu'),

  async execute(interaction) {
    // Button row
    const buttonRow = new ActionRowBuilder()
      .addComponents(
        new ButtonBuilder()
          .setCustomId('confirm')
          .setLabel('Confirm')
          .setStyle(ButtonStyle.Primary),
        new ButtonBuilder()
          .setCustomId('cancel')
          .setLabel('Cancel')
          .setStyle(ButtonStyle.Danger),
        new ButtonBuilder()
          .setLabel('Documentation')
          .setURL('https://discord.js.org')
          .setStyle(ButtonStyle.Link)  // Link buttons don't emit events
      );

    // Select menu row (one per row, takes all 5 slots)
    const selectRow = new ActionRowBuilder()
      .addComponents(
        new StringSelectMenuBuilder()
          .setCustomId('select-role')
          .setPlaceholder('Select a role')
          .setMinValues(1)
          .setMaxValues(3)
          .addOptions([
            { label: 'Developer', value: 'dev', emoji: '💻' },
            { label: 'Designer', value: 'design', emoji: '🎨' },
            { label: 'Community', value: 'community', emoji: '🎉' }
          ])
      );

    await interaction.reply({
      content: 'Choose an option:',
      components: [buttonRow, selectRow]
    });

    // Collect responses
    const collector = interaction.channel.createMessageComponentCollector({
      filter: i => i.user.id === interaction.user.id,
      time: 60_000  // 60 seconds timeout
    });

    collector.on('collect', async i => {
      if (i.customId === 'confirm') {
        await i.update({ content: 'Confirmed!', components: [] });
        collector.stop();
      } else if (i.custo

Anti-Patterns

反模式

❌ Message Content for Commands

❌ 使用消息内容触发命令

Why bad: Message Content Intent is privileged and deprecated for bot commands. Slash commands are the intended approach.
问题所在:消息内容意图是特权权限,且已被弃用作为机器人命令的触发方式。斜杠命令是官方推荐的方案。

❌ Syncing Commands on Every Start

❌ 每次启动时同步命令

Why bad: Command registration is rate limited. Global commands take up to 1 hour to propagate. Syncing on every start wastes API calls and can hit limits.
问题所在:命令注册存在速率限制。全局命令最长需要1小时才能完成同步。每次启动都同步会浪费API调用次数,甚至触发限制。

❌ Blocking the Event Loop

❌ 阻塞事件循环

Why bad: Discord gateway requires regular heartbeats. Blocking operations cause missed heartbeats and disconnections.
问题所在:Discord网关需要定期发送心跳包。阻塞操作会导致心跳包丢失,进而引发断开连接。

⚠️ Sharp Edges

⚠️ 潜在陷阱

IssueSeveritySolution
Issuecritical## Acknowledge immediately, process later
Issuecritical## Step 1: Enable in Developer Portal
Issuehigh## Use a separate deploy script (not on startup)
Issuecritical## Never hardcode tokens
Issuehigh## Generate correct invite URL
Issuemedium## Development: Use guild commands
Issuemedium## Never block the event loop
Issuemedium## Show modal immediately
问题严重程度解决方案
问题严重## 立即确认,延后处理
问题严重## 步骤1:在开发者门户中启用
问题## 使用独立的部署脚本(而非启动时执行)
问题严重## 切勿硬编码令牌
问题## 生成正确的邀请链接
问题## 开发阶段:使用服务器专属命令
问题## 切勿阻塞事件循环
问题## 立即显示模态框