Discord机器人开发架构师Skill discord-bot-architect

这个技能专注于构建生产就绪的Discord机器人,覆盖Discord.js(JavaScript)和Pycord(Python)开发框架,包括网关意图配置、斜杠命令实现、交互组件(如按钮和选择菜单)的使用,以及处理速率限制和分片技术。帮助开发者掌握高性能、可靠的Discord机器人设计与部署,避免常见错误如滥用消息内容意图和阻塞事件循环。关键词:Discord机器人、Discord.js、Pycord、斜杠命令、交互组件、机器人架构、速率限制、分片。

架构设计 0 次安装 0 次浏览 更新于 3/21/2026

名称: discord-bot-architect 描述: “专注于构建生产就绪的 Discord 机器人的专业技能。涵盖 Discord.js(JavaScript)和 Pycord(Python),网关意图,斜杠命令,交互组件,速率限制,和分片。” 来源: vibeship-spawner-skills (Apache 2.0)

Discord 机器人架构师

模式

Discord.js v14 基础

使用 Discord.js v14 和斜杠命令的现代 Discord 机器人设置

使用时机: [‘构建使用 JavaScript/TypeScript 的 Discord 机器人’, ‘需要完整网关连接和事件’, ‘构建具有复杂交互的机器人’]

// src/index.js
const { Client, Collection, GatewayIntentBits, Events } = require('discord.js');
const fs = require('node:fs');
const path = require('node:path');
require('dotenv').config();

// 创建客户端,仅启用所需意图
const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    // 只添加需要的:
    // GatewayIntentBits.GuildMessages,
    // GatewayIntentBits.MessageContent,  // 特权意图 - 尽可能避免
  ]
});

// 加载命令
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);
  }
}

// 加载事件
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);
// src/commands/ping.js
const { SlashCommandBuilder } = require('discord.js');

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

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

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

module.exports = {
  name: Events.InteractionCreate,
  async execute(interaction) {
    if (!interaction.isCommand()) return;
    const command = interaction.client.commands.get(interaction.commandName);
    if (!command) {
      console.error(`未找到命令: ${interaction.commandName}`);
      return;
    }
    try {
      await command.execute(interaction);
    } catch (error) {
      console.error(error);
      await interaction.reply({ content: '执行命令时出错', ephemeral: true });
    }
  }
};

Pycord 机器人基础

使用 Pycord(Python)和应用程序命令的 Discord 机器人

使用时机: [‘构建使用 Python 的 Discord 机器人’, ‘偏好异步/等待模式’, ‘需要良好的斜杠命令支持’]

# main.py
import os
import discord
from discord.ext import commands
from dotenv import load_dotenv

load_dotenv()

# 配置意图 - 仅启用需要的
intents = discord.Intents.default()
# intents.message_content = True  # 特权意图 - 尽可能避免
# intents.members = True          # 特权意图

bot = commands.Bot(
    command_prefix="!",  # 传统命令,推荐使用斜杠命令
    intents=intents
)

@bot.event
async def on_ready():
    print(f"登录为 {bot.user}")
    # 同步命令(小心使用 - 见注意事项)
    # await bot.sync_commands()

# 斜杠命令
@bot.slash_command(name="ping", description="检查机器人延迟")
async def ping(ctx: discord.ApplicationContext):
    latency = round(bot.latency * 1000)
    await ctx.respond(f"Pong! 延迟: {latency}ms")

# 带选项的斜杠命令
@bot.slash_command(name="greet", description="问候用户")
async def greet(
    ctx: discord.ApplicationContext,
    user: discord.Option(discord.Member, "要问候的用户"),
    message: discord.Option(str, "自定义消息", required=False)
):
    msg = message or "你好!"
    await ctx.respond(f"{user.mention}, {msg}")

# 加载扩展
for filename in os.listdir("./cogs"):
    if filename.endswith(".py"):
        bot.load_extension(f"cogs.{filename[:-3]}")

bot.run(os.environ["DISCORD_TOKEN"])
# 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="机器人信息")
    async def info(self, ctx: discord.ApplicationContext):
        embed = discord.Embed(
            title="机器人信息",
            description="一个有用的 Discord 机器人",
            color=discord.Color.blue()
        )
        embed.add_field(name="服务器数量", value=len(self.bot.guilds))
        embed.add_field(name="延迟", value=f"{round(self.bot.latency * 1000)}ms")
        await ctx.respond(embed=embed)

    @commands.Cog.listener()
    async def on_ready(self):
        print("General cog loaded")

def setup(bot):
    bot.add_cog(General(bot))

交互组件模式

使用按钮、选择菜单和模态对话框实现丰富的用户体验

使用时机: [‘需要交互式用户界面’, ‘收集用户输入超越斜杠命令选项’, ‘构建菜单、确认或表单’]

// Discord.js - 按钮和选择菜单
const {
  SlashCommandBuilder,
  ActionRowBuilder,
  ButtonBuilder,
  ButtonStyle,
  StringSelectMenuBuilder,
  ModalBuilder,
  TextInputBuilder,
  TextInputStyle
} = require('discord.js');

module.exports = {
  data: new SlashCommandBuilder()
    .setName('menu')
    .setDescription('显示交互式菜单'),

  async execute(interaction) {
    // 按钮行
    const buttonRow = new ActionRowBuilder()
      .addComponents(
        new ButtonBuilder()
          .setCustomId('confirm')
          .setLabel('确认')
          .setStyle(ButtonStyle.Primary),
        new ButtonBuilder()
          .setCustomId('cancel')
          .setLabel('取消')
          .setStyle(ButtonStyle.Danger),
        new ButtonBuilder()
          .setLabel('文档')
          .setURL('https://discord.js.org')
          .setStyle(ButtonStyle.Link)  // 链接按钮不触发事件
      );

    // 选择菜单行(每行一个,占用全部5个槽位)
    const selectRow = new ActionRowBuilder()
      .addComponents(
        new StringSelectMenuBuilder()
          .setCustomId('select-role')
          .setPlaceholder('选择一个角色')
          .setMinValues(1)
          .setMaxValues(3)
          .addOptions([
            { label: '开发者', value: 'dev', emoji: '💻' },
            { label: '设计师', value: 'design', emoji: '🎨' },
            { label: '社区', value: 'community', emoji: '🎉' }
          ])
      );

    await interaction.reply({
      content: '选择一个选项:',
      components: [buttonRow, selectRow]
    });

    // 收集响应
    const collector = interaction.channel.createMessageComponentCollector({
      filter: i => i.user.id === interaction.user.id,
      time: 60_000  // 60秒超时
    });

    collector.on('collect', async i => {
      if (i.customId === 'confirm') {
        await i.update({ content: '已确认!', components: [] });
        collector.stop();
      } else if (i.customId === 'cancel') {
        await i.update({ content: '已取消', components: [] });
        collector.stop();
      } else if (i.customId === 'select-role') {
        const roles = i.values.join(', ');
        await i.update({ content: `你选择了: ${roles}`, components: [] });
      }
    });

    collector.on('end', collected => {
      console.log(`收集到 ${collected.size} 个交互`);
    });
  }
};

反模式

❌ 使用消息内容进行命令

为什么不好: 消息内容意图是特权性的,并且对于机器人命令已弃用。斜杠命令是推荐的方法。

❌ 每次启动都同步命令

为什么不好: 命令注册有速率限制。全局命令传播可能需要长达1小时。每次启动同步浪费 API 调用,并可能触及限制。

❌ 阻塞事件循环

为什么不好: Discord 网关需要定期心跳。阻塞操作会导致错过心跳和断开连接。

⚠️ 注意事项

问题 严重性 解决方案
未启用特权意图 关键 ## 立即在开发者门户启用
速率限制 ## 使用单独的部署脚本(不要在启动时)
硬编码令牌 关键 ## 永远不要硬编码令牌
邀请 URL 错误 ## 生成正确的邀请 URL
开发时全局命令 中等 ## 开发时使用公会命令
阻塞事件循环 中等 ## 永远不要阻塞事件循环
模态对话框延迟 中等 ## 立即显示模态对话框