[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-8f60bfe3-0d51-4943-bb39-6a55770b3c03":3,"$fPsa5knZJ1r4ndhE-cdi5pWQtluMP9Xs1OmSaCAVaCrc":43},{"id":4,"title":5,"description":6,"categoryId":7,"moduleId":8,"tags":9,"prompt":10,"icon":11,"source":12,"sourceUrl":13,"authorId":14,"authorName":15,"isPublic":16,"stars":17,"runs":18,"createdAt":19,"updatedAt":19,"module":20,"category":27,"packages":34},"8f60bfe3-0d51-4943-bb39-6a55770b3c03","discord-bot-architect","构建生产就绪的Discord机器人的专业技能。","cat_life_career","mod_other","sickn33,other","---\nname: discord-bot-architect\ndescription: Specialized skill for building production-ready Discord bots.\n  Covers Discord.js (JavaScript) and Pycord (Python), gateway intents, slash\n  commands, interactive components, rate limiting, and sharding.\nrisk: unknown\nsource: vibeship-spawner-skills (Apache 2.0)\ndate_added: 2026-02-27\n---\n\n# Discord Bot Architect\n\nSpecialized skill for building production-ready Discord bots.\nCovers Discord.js (JavaScript) and Pycord (Python), gateway intents,\nslash commands, interactive components, rate limiting, and sharding.\n\n## Principles\n\n- Slash commands over message parsing (Message Content Intent deprecated)\n- Acknowledge interactions within 3 seconds, always\n- Request only required intents (minimize privileged intents)\n- Handle rate limits gracefully with exponential backoff\n- Plan for sharding from the start (required at 2500+ guilds)\n- Use components (buttons, selects, modals) for rich UX\n- Test with guild commands first, deploy global when ready\n\n## Patterns\n\n### Discord.js v14 Foundation\n\nModern Discord bot setup with Discord.js v14 and slash commands\n\n**When to use**: Building Discord bots with JavaScript\u002FTypeScript,Need full gateway connection with events,Building bots with complex interactions\n\n```javascript\n\u002F\u002F src\u002Findex.js\nconst { Client, Collection, GatewayIntentBits, Events } = require('discord.js');\nconst fs = require('node:fs');\nconst path = require('node:path');\nrequire('dotenv').config();\n\n\u002F\u002F Create client with minimal required intents\nconst client = new Client({\n  intents: [\n    GatewayIntentBits.Guilds,\n    \u002F\u002F Add only what you need:\n    \u002F\u002F GatewayIntentBits.GuildMessages,\n    \u002F\u002F GatewayIntentBits.MessageContent,  \u002F\u002F PRIVILEGED - avoid if possible\n  ]\n});\n\n\u002F\u002F Load commands\nclient.commands = new Collection();\nconst commandsPath = path.join(__dirname, 'commands');\nconst commandFiles = fs.readdirSync(commandsPath).filter(f => f.endsWith('.js'));\n\nfor (const file of commandFiles) {\n  const filePath = path.join(commandsPath, file);\n  const command = require(filePath);\n  if ('data' in command && 'execute' in command) {\n    client.commands.set(command.data.name, command);\n  }\n}\n\n\u002F\u002F Load events\nconst eventsPath = path.join(__dirname, 'events');\nconst eventFiles = fs.readdirSync(eventsPath).filter(f => f.endsWith('.js'));\n\nfor (const file of eventFiles) {\n  const filePath = path.join(eventsPath, file);\n  const event = require(filePath);\n  if (event.once) {\n    client.once(event.name, (...args) => event.execute(...args));\n  } else {\n    client.on(event.name, (...args) => event.execute(...args));\n  }\n}\n\nclient.login(process.env.DISCORD_TOKEN);\n```\n\n```javascript\n\u002F\u002F src\u002Fcommands\u002Fping.js\nconst { SlashCommandBuilder } = require('discord.js');\n\nmodule.exports = {\n  data: new SlashCommandBuilder()\n    .setName('ping')\n    .setDescription('Replies with Pong!'),\n\n  async execute(interaction) {\n    const sent = await interaction.reply({\n      content: 'Pinging...',\n      fetchReply: true\n    });\n\n    const latency = sent.createdTimestamp - interaction.createdTimestamp;\n    await interaction.editReply(`Pong! Latency: ${latency}ms`);\n  }\n};\n```\n\n```javascript\n\u002F\u002F src\u002Fevents\u002FinteractionCreate.js\nconst { Events } = require('discord.js');\n\nmodule.exports = {\n  name: Events.InteractionCreate,\n  async execute(interaction) {\n    if (!interaction.isChatInputCommand()) return;\n\n    const command = interaction.client.commands.get(interaction.commandName);\n    if (!command) {\n      console.error(`No command matching ${interaction.commandName}`);\n      return;\n    }\n\n    try {\n      await command.execute(interaction);\n    } catch (error) {\n      console.error(error);\n      const reply = {\n        content: 'There was an error executing this command!',\n        ephemeral: true\n      };\n\n      if (interaction.replied || interaction.deferred) {\n        await interaction.followUp(reply);\n      } else {\n        await interaction.reply(reply);\n      }\n    }\n  }\n};\n```\n\n```javascript\n\u002F\u002F src\u002Fdeploy-commands.js\nconst { REST, Routes } = require('discord.js');\nconst fs = require('node:fs');\nconst path = require('node:path');\nrequire('dotenv').config();\n\nconst commands = [];\nconst commandsPath = path.join(__dirname, 'commands');\nconst commandFiles = fs.readdirSync(commandsPath).filter(f => f.endsWith('.js'));\n\nfor (const file of commandFiles) {\n  const command = require(path.join(commandsPath, file));\n  commands.push(command.data.toJSON());\n}\n\nconst rest = new REST().setToken(process.env.DISCORD_TOKEN);\n\n(async () => {\n  try {\n    console.log(`Refreshing ${commands.length} commands...`);\n\n    \u002F\u002F Guild commands (instant, for testing)\n    \u002F\u002F const data = await rest.put(\n    \u002F\u002F   Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID),\n    \u002F\u002F   { body: commands }\n    \u002F\u002F );\n\n    \u002F\u002F Global commands (can take up to 1 hour to propagate)\n    const data = await rest.put(\n      Routes.applicationCommands(process.env.CLIENT_ID),\n      { body: commands }\n    );\n\n    console.log(`Successfully registered ${data.length} commands`);\n  } catch (error) {\n    console.error(error);\n  }\n})();\n```\n\n### Structure\n\ndiscord-bot\u002F\n├── src\u002F\n│   ├── index.js           # Main entry point\n│   ├── deploy-commands.js # Command registration script\n│   ├── commands\u002F          # Slash command handlers\n│   │   └── ping.js\n│   └── events\u002F            # Event handlers\n│       ├── ready.js\n│       └── interactionCreate.js\n├── .env\n└── package.json\n\n### Pycord Bot Foundation\n\nDiscord bot with Pycord (Python) and application commands\n\n**When to use**: Building Discord bots with Python,Prefer async\u002Fawait patterns,Need good slash command support\n\n```python\n# main.py\nimport os\nimport discord\nfrom discord.ext import commands\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\n# Configure intents - only enable what you need\nintents = discord.Intents.default()\n# intents.message_content = True  # PRIVILEGED - avoid if possible\n# intents.members = True          # PRIVILEGED\n\nbot = commands.Bot(\n    command_prefix=\"!\",  # Legacy, prefer slash commands\n    intents=intents\n)\n\n@bot.event\nasync def on_ready():\n    print(f\"Logged in as {bot.user}\")\n    # Sync commands (do this carefully - see sharp edges)\n    # await bot.sync_commands()\n\n# Slash command\n@bot.slash_command(name=\"ping\", description=\"Check bot latency\")\nasync def ping(ctx: discord.ApplicationContext):\n    latency = round(bot.latency * 1000)\n    await ctx.respond(f\"Pong! Latency: {latency}ms\")\n\n# Slash command with options\n@bot.slash_command(name=\"greet\", description=\"Greet a user\")\nasync def greet(\n    ctx: discord.ApplicationContext,\n    user: discord.Option(discord.Member, \"User to greet\"),\n    message: discord.Option(str, \"Custom message\", required=False)\n):\n    msg = message or \"Hello!\"\n    await ctx.respond(f\"{user.mention}, {msg}\")\n\n# Load cogs\nfor filename in os.listdir(\".\u002Fcogs\"):\n    if filename.endswith(\".py\"):\n        bot.load_extension(f\"cogs.{filename[:-3]}\")\n\nbot.run(os.environ[\"DISCORD_TOKEN\"])\n```\n\n```python\n# cogs\u002Fgeneral.py\nimport discord\nfrom discord.ext import commands\n\nclass General(commands.Cog):\n    def __init__(self, bot):\n        self.bot = bot\n\n    @commands.slash_command(name=\"info\", description=\"Bot information\")\n    async def info(self, ctx: discord.ApplicationContext):\n        embed = discord.Embed(\n            title=\"Bot Info\",\n            description=\"A helpful Discord bot\",\n            color=discord.Color.blue()\n        )\n        embed.add_field(name=\"Servers\", value=len(self.bot.guilds))\n        embed.add_field(name=\"Latency\", value=f\"{round(self.bot.latency * 1000)}ms\")\n        await ctx.respond(embed=embed)\n\n    @commands.Cog.listener()\n    async def on_member_join(self, member: discord.Member):\n        # Requires Members intent (PRIVILEGED)\n        channel = member.guild.system_channel\n        if channel:\n            await channel.send(f\"Welcome {member.mention}!\")\n\ndef setup(bot):\n    bot.add_cog(General(bot))\n```\n\n### Structure\n\ndiscord-bot\u002F\n├── main.py           # Main bot file\n├── cogs\u002F             # Command groups\n│   └── general.py\n├── .env\n└── requirements.txt\n\n### Interactive Components Pattern\n\nUsing buttons, select menus, and modals for rich UX\n\n**When to use**: Need interactive user interfaces,Collecting user input beyond slash command options,Building menus, confirmations, or forms\n\n```javascript\n\u002F\u002F Discord.js - Buttons and Select Menus\nconst {\n  SlashCommandBuilder,\n  ActionRowBuilder,\n  ButtonBuilder,\n  ButtonStyle,\n  StringSelectMenuBuilder,\n  ModalBuilder,\n  TextInputBuilder,\n  TextInputStyle\n} = require('discord.js');\n\nmodule.exports = {\n  data: new SlashCommandBuilder()\n    .setName('menu')\n    .setDescription('Shows an interactive menu'),\n\n  async execute(interaction) {\n    \u002F\u002F Button row\n    const buttonRow = new ActionRowBuilder()\n      .addComponents(\n        new ButtonBuilder()\n          .setCustomId('confirm')\n          .setLabel('Confirm')\n          .setStyle(ButtonStyle.Primary),\n        new ButtonBuilder()\n          .setCustomId('cancel')\n          .setLabel('Cancel')\n          .setStyle(ButtonStyle.Danger),\n        new ButtonBuilder()\n          .setLabel('Documentation')\n          .setURL('https:\u002F\u002Fdiscord.js.org')\n          .setStyle(ButtonStyle.Link)  \u002F\u002F Link buttons don't emit events\n      );\n\n    \u002F\u002F Select menu row (one per row, takes all 5 slots)\n    const selectRow = new ActionRowBuilder()\n      .addComponents(\n        new StringSelectMenuBuilder()\n          .setCustomId('select-role')\n          .setPlaceholder('Select a role')\n          .setMinValues(1)\n          .setMaxValues(3)\n          .addOptions([\n            { label: 'Developer', value: 'dev', emoji: '💻' },\n            { label: 'Designer', value: 'design', emoji: '🎨' },\n            { label: 'Community', value: 'community', emoji: '🎉' }\n          ])\n      );\n\n    await interaction.reply({\n      content: 'Choose an option:',\n      components: [buttonRow, selectRow]\n    });\n\n    \u002F\u002F Collect responses\n    const collector = interaction.channel.createMessageComponentCollector({\n      filter: i => i.user.id === interaction.user.id,\n      time: 60_000  \u002F\u002F 60 seconds timeout\n    });\n\n    collector.on('collect', async i => {\n      if (i.customId === 'confirm') {\n        await i.update({ content: 'Confirmed!', components: [] });\n        collector.stop();\n      } else if (i.customId === 'cancel') {\n        await i.update({ content: 'Cancelled', components: [] });\n        collector.stop();\n      } else if (i.customId === 'select-role') {\n        await i.update({ content: `You selected: ${i.values.join(', ')}` });\n      }\n    });\n  }\n};\n```\n\n```javascript\n\u002F\u002F Modals (forms)\nmodule.exports = {\n  data: new SlashCommandBuilder()\n    .setName('feedback')\n    .setDescription('Submit feedback'),\n\n  async execute(interaction) {\n    const modal = new ModalBuilder()\n      .setCustomId('feedback-modal')\n      .setTitle('Submit Feedback');\n\n    const titleInput = new TextInputBuilder()\n      .setCustomId('feedback-title')\n      .setLabel('Title')\n      .setStyle(TextInputStyle.Short)\n      .setRequired(true)\n      .setMaxLength(100);\n\n    const bodyInput = new TextInputBuilder()\n      .setCustomId('feedback-body')\n      .setLabel('Your feedback')\n      .setStyle(TextInputStyle.Paragraph)\n      .setRequired(true)\n      .setMaxLength(1000)\n      .setPlaceholder('Describe your feedback...');\n\n    modal.addComponents(\n      new ActionRowBuilder().addComponents(titleInput),\n      new ActionRowBuilder().addComponents(bodyInput)\n    );\n\n    \u002F\u002F Show modal - MUST be first response\n    await interaction.showModal(modal);\n  }\n};\n\n\u002F\u002F Handle modal submission in interactionCreate\nif (interaction.isModalSubmit()) {\n  if (interaction.customId === 'feedback-modal') {\n    const title = interaction.fields.getTextInputValue('feedback-title');\n    const body = interaction.fields.getTextInputValue('feedback-body');\n\n    await interaction.reply({\n      content: `Thanks for your feedback!\\n**${title}**\\n${body}`,\n      ephemeral: true\n    });\n  }\n}\n```\n\n```python\n# Pycord - Buttons and Views\nimport discord\n\nclass ConfirmView(discord.ui.View):\n    def __init__(self):\n        super().__init__(timeout=60)\n        self.value = None\n\n    @discord.ui.button(label=\"Confirm\", style=discord.ButtonStyle.green)\n    async def confirm(self, button, interaction):\n        self.value = True\n        await interaction.response.edit_message(content=\"Confirmed!\", view=None)\n        self.stop()\n\n    @discord.ui.button(label=\"Cancel\", style=discord.ButtonStyle.red)\n    async def cancel(self, button, interaction):\n        self.value = False\n        await interaction.response.edit_message(content=\"Cancelled\", view=None)\n        self.stop()\n\n@bot.slash_command(name=\"confirm\")\nasync def confirm_cmd(ctx: discord.ApplicationContext):\n    view = ConfirmView()\n    await ctx.respond(\"Are you sure?\", view=view)\n\n    await view.wait()  # Wait for user interaction\n    if view.value is None:\n        await ctx.followup.send(\"Timed out\")\n\n# Select Menu\nclass RoleSelect(discord.ui.Select):\n    def __init__(self):\n        options = [\n            discord.SelectOption(label=\"Developer\", value=\"dev\", emoji=\"💻\"),\n            discord.SelectOption(label=\"Designer\", value=\"design\", emoji=\"🎨\"),\n        ]\n        super().__init__(\n            placeholder=\"Select roles...\",\n            min_values=1,\n            max_values=2,\n            options=options\n        )\n\n    async def callback(self, interaction):\n        await interaction.response.send_message(\n            f\"You selected: {', '.join(self.values)}\",\n            ephemeral=True\n        )\n\nclass RoleView(discord.ui.View):\n    def __init__(self):\n        super().__init__()\n        self.add_item(RoleSelect())\n\n# Modal\nclass FeedbackModal(discord.ui.Modal):\n    def __init__(self):\n        super().__init__(title=\"Submit Feedback\")\n\n        self.add_item(discord.ui.InputText(\n            label=\"Title\",\n            style=discord.InputTextStyle.short,\n            required=True,\n            max_length=100\n        ))\n        self.add_item(discord.ui.InputText(\n            label=\"Feedback\",\n            style=discord.InputTextStyle.long,\n            required=True,\n            max_length=1000\n        ))\n\n    async def callback(self, interaction):\n        title = self.children[0].value\n        body = self.children[1].value\n        await interaction.response.send_message(\n            f\"Thanks!\\n**{title}**\\n{body}\",\n            ephemeral=True\n        )\n\n@bot.slash_command(name=\"feedback\")\nasync def feedback(ctx: discord.ApplicationContext):\n    await ctx.send_modal(FeedbackModal())\n```\n\n### Limits\n\n- 5 ActionRows per message\u002Fmodal\n- 5 buttons per ActionRow\n- 1 select menu per ActionRow (takes all 5 slots)\n- 5 select menus max per message\n- 25 options per select menu\n- Modal must be first response (cannot defer first)\n\n### Deferred Response Pattern\n\nHandle slow operations without timing out\n\n**When to use**: Operation takes more than 3 seconds,Database queries, API calls, LLM responses,File processing or generation\n\n```javascript\n\u002F\u002F Discord.js - Deferred response\nmodule.exports = {\n  data: new SlashCommandBuilder()\n    .setName('slow-task')\n    .setDescription('Performs a slow operation'),\n\n  async execute(interaction) {\n    \u002F\u002F Defer immediately - you have 3 seconds!\n    await interaction.deferReply();\n    \u002F\u002F For ephemeral: await interaction.deferReply({ ephemeral: true });\n\n    try {\n      \u002F\u002F Now you have 15 minutes to complete\n      const result = await slowDatabaseQuery();\n      const aiResponse = await callOpenAI(result);\n\n      \u002F\u002F Edit the deferred reply\n      await interaction.editReply({\n        content: `Result: ${aiResponse}`,\n        embeds: [resultEmbed]\n      });\n    } catch (error) {\n      await interaction.editReply({\n        content: 'An error occurred while processing your request.'\n      });\n    }\n  }\n};\n\n\u002F\u002F For components (buttons, select menus)\ncollector.on('collect', async i => {\n  await i.deferUpdate();  \u002F\u002F Acknowledge without visual change\n  \u002F\u002F Or: await i.deferReply({ ephemeral: true });\n\n  const result = await slowOperation();\n  await i.editReply({ content: result });\n});\n```\n\n```python\n# Pycord - Deferred response\n@bot.slash_command(name=\"slow-task\")\nasync def slow_task(ctx: discord.ApplicationContext):\n    # Defer immediately\n    await ctx.defer()\n    # For ephemeral: await ctx.defer(ephemeral=True)\n\n    try:\n        result = await slow_database_query()\n        ai_response = await call_openai(result)\n\n        await ctx.followup.send(f\"Result: {ai_response}\")\n    except Exception as e:\n        await ctx.followup.send(\"An error occurred\")\n```\n\n### Timing\n\n- Initial_response: 3 seconds\n- Deferred_followup: 15 minutes\n- Ephemeral_note: Can only be set on initial response, not changed later\n\n### Embed Builder Pattern\n\nRich embedded messages for professional-looking content\n\n**When to use**: Displaying formatted information,Status updates, help menus, logs,Data with structure (fields, images)\n\n```javascript\nconst { EmbedBuilder, Colors } = require('discord.js');\n\n\u002F\u002F Basic embed\nconst embed = new EmbedBuilder()\n  .setColor(Colors.Blue)\n  .setTitle('Bot Status')\n  .setURL('https:\u002F\u002Fexample.com')\n  .setAuthor({\n    name: 'Bot Name',\n    iconURL: client.user.displayAvatarURL()\n  })\n  .setDescription('Current status and statistics')\n  .addFields(\n    { name: 'Servers', value: `${client.guilds.cache.size}`, inline: true },\n    { name: 'Users', value: `${client.users.cache.size}`, inline: true },\n    { name: 'Uptime', value: formatUptime(), inline: true }\n  )\n  .setThumbnail(client.user.displayAvatarURL())\n  .setImage('https:\u002F\u002Fexample.com\u002Fbanner.png')\n  .setTimestamp()\n  .setFooter({\n    text: 'Requested by User',\n    iconURL: interaction.user.displayAvatarURL()\n  });\n\nawait interaction.reply({ embeds: [embed] });\n\n\u002F\u002F Multiple embeds (max 10)\nawait interaction.reply({ embeds: [embed1, embed2, embed3] });\n```\n\n```python\n# Pycord\nembed = discord.Embed(\n    title=\"Bot Status\",\n    description=\"Current status and statistics\",\n    color=discord.Color.blue(),\n    url=\"https:\u002F\u002Fexample.com\"\n)\nembed.set_author(\n    name=\"Bot Name\",\n    icon_url=bot.user.display_avatar.url\n)\nembed.add_field(name=\"Servers\", value=len(bot.guilds), inline=True)\nembed.add_field(name=\"Users\", value=len(bot.users), inline=True)\nembed.set_thumbnail(url=bot.user.display_avatar.url)\nembed.set_image(url=\"https:\u002F\u002Fexample.com\u002Fbanner.png\")\nembed.set_footer(text=\"Requested by User\", icon_url=ctx.author.display_avatar.url)\nembed.timestamp = discord.utils.utcnow()\n\nawait ctx.respond(embed=embed)\n```\n\n### Limits\n\n- 10 embeds per message\n- 6000 characters total across all embeds\n- 256 characters for title\n- 4096 characters for description\n- 25 fields per embed\n- 256 characters per field name\n- 1024 characters per field value\n\n### Rate Limit Handling Pattern\n\nGracefully handle Discord API rate limits\n\n**When to use**: High-volume operations,Bulk messaging or role assignments,Any repeated API calls\n\n```javascript\n\u002F\u002F Discord.js handles rate limits automatically, but for custom handling:\nconst { REST } = require('discord.js');\n\nconst rest = new REST({ version: '10' })\n  .setToken(process.env.DISCORD_TOKEN);\n\nrest.on('rateLimited', (info) => {\n  console.log(`Rate limited! Retry after ${info.retryAfter}ms`);\n  console.log(`Route: ${info.route}`);\n  console.log(`Global: ${info.global}`);\n});\n\n\u002F\u002F Queue pattern for bulk operations\nclass RateLimitQueue {\n  constructor() {\n    this.queue = [];\n    this.processing = false;\n    this.requestsPerSecond = 40; \u002F\u002F Safe margin below 50\n  }\n\n  async add(operation) {\n    return new Promise((resolve, reject) => {\n      this.queue.push({ operation, resolve, reject });\n      this.process();\n    });\n  }\n\n  async process() {\n    if (this.processing || this.queue.length === 0) return;\n    this.processing = true;\n\n    while (this.queue.length > 0) {\n      const { operation, resolve, reject } = this.queue.shift();\n\n      try {\n        const result = await operation();\n        resolve(result);\n      } catch (error) {\n        reject(error);\n      }\n\n      \u002F\u002F Throttle: ~40 requests per second\n      await new Promise(r => setTimeout(r, 1000 \u002F this.requestsPerSecond));\n    }\n\n    this.processing = false;\n  }\n}\n\nconst queue = new RateLimitQueue();\n\n\u002F\u002F Usage: Send 200 messages without hitting rate limits\nfor (const user of users) {\n  await queue.add(() => user.send('Welcome!'));\n}\n```\n\n```python\n# Pycord\u002Fdiscord.py handles rate limits automatically\n# For custom handling:\nimport asyncio\nfrom collections import deque\n\nclass RateLimitQueue:\n    def __init__(self, requests_per_second=40):\n        self.queue = deque()\n        self.processing = False\n        self.delay = 1 \u002F requests_per_second\n\n    async def add(self, coro):\n        future = asyncio.Future()\n        self.queue.append((coro, future))\n        if not self.processing:\n            asyncio.create_task(self._process())\n        return await future\n\n    async def _process(self):\n        self.processing = True\n        while self.queue:\n            coro, future = self.queue.popleft()\n            try:\n                result = await coro\n                future.set_result(result)\n            except Exception as e:\n                future.set_exception(e)\n            await asyncio.sleep(self.delay)\n        self.processing = False\n\nqueue = RateLimitQueue()\n\n# Usage\nfor member in guild.members:\n    await queue.add(member.send(\"Welcome!\"))\n```\n\n### Rate_limits\n\n- Global: 50 requests per second\n- Gateway: 120 requests per 60 seconds\n- Specific: Messages to same channel: 5\u002F5s, Bulk delete: 1\u002F1s, Guild member requests: varies by guild size\n\n### Sharding Pattern\n\nScale bots to 2500+ servers with sharding\n\n**When to use**: Bot approaching 2500 guilds (required),Want horizontal scaling,Memory optimization for large bots\n\n```javascript\n\u002F\u002F Discord.js Sharding Manager\n\u002F\u002F shard.js (main entry)\nconst { ShardingManager } = require('discord.js');\n\nconst manager = new ShardingManager('.\u002Fbot.js', {\n  token: process.env.DISCORD_TOKEN,\n  totalShards: 'auto',  \u002F\u002F Discord determines optimal count\n  \u002F\u002F Or specify: totalShards: 4\n});\n\nmanager.on('shardCreate', shard => {\n  console.log(`Launched shard ${shard.id}`);\n\n  shard.on('ready', () => {\n    console.log(`Shard ${shard.id} ready`);\n  });\n\n  shard.on('disconnect', () => {\n    console.log(`Shard ${shard.id} disconnected`);\n  });\n});\n\nmanager.spawn();\n\n\u002F\u002F bot.js - Modified for sharding\nconst { Client } = require('discord.js');\n\nconst client = new Client({ intents: [...] });\n\n\u002F\u002F Get shard info\nclient.on('ready', () => {\n  console.log(`Shard ${client.shard.ids[0]} ready with ${client.guilds.cache.size} guilds`);\n});\n\n\u002F\u002F Cross-shard data\nasync function getTotalGuilds() {\n  const results = await client.shard.fetchClientValues('guilds.cache.size');\n  return results.reduce((acc, count) => acc + count, 0);\n}\n\n\u002F\u002F Broadcast to all shards\nasync function broadcastMessage(channelId, message) {\n  await client.shard.broadcastEval(\n    (c, { channelId, message }) => {\n      const channel = c.channels.cache.get(channelId);\n      if (channel) channel.send(message);\n    },\n    { context: { channelId, message } }\n  );\n}\n```\n\n```python\n# Pycord - AutoShardedBot\nimport discord\nfrom discord.ext import commands\n\n# Automatically handles sharding\nbot = commands.AutoShardedBot(\n    command_prefix=\"!\",\n    intents=discord.Intents.default(),\n    shard_count=None  # Auto-determine\n)\n\n@bot.event\nasync def on_ready():\n    print(f\"Logged in on {len(bot.shards)} shards\")\n    for shard_id, shard in bot.shards.items():\n        print(f\"Shard {shard_id}: {shard.latency * 1000:.2f}ms\")\n\n@bot.event\nasync def on_shard_ready(shard_id):\n    print(f\"Shard {shard_id} is ready\")\n\n# Get guilds per shard\nfor shard_id, guilds in bot.guilds_by_shard().items():\n    print(f\"Shard {shard_id}: {len(guilds)} guilds\")\n```\n\n### Scaling_guide\n\n- 1-2500 guilds: No sharding required\n- 2500+ guilds: Sharding required by Discord\n- Recommended: ~1000 guilds per shard\n- Memory: Each shard runs in separate process\n\n## Sharp Edges\n\n### Interaction Timeout (3 Second Rule)\n\nSeverity: CRITICAL\n\nSituation: Handling slash commands, buttons, select menus, or modals\n\nSymptoms:\nUser sees \"This interaction failed\" or \"The application did not respond.\"\nCommand works locally but fails in production.\nSlow operations never complete.\n\nWhy this breaks:\nDiscord requires ALL interactions to be acknowledged within 3 seconds:\n- Slash commands\n- Button clicks\n- Select menu selections\n- Context menu commands\n\nIf you do ANY slow operation (database, API, file I\u002FO) before responding,\nyou'll miss the window. Discord shows an error even if your bot processes\nthe request correctly afterward.\n\nAfter acknowledgment, you have 15 minutes for follow-up responses.\n\nRecommended fix:\n\n## Acknowledge immediately, process later\n\n```javascript\n\u002F\u002F Discord.js - Defer for slow operations\nmodule.exports = {\n  async execute(interaction) {\n    \u002F\u002F DEFER IMMEDIATELY - before any slow operation\n    await interaction.deferReply();\n    \u002F\u002F For ephemeral: await interaction.deferReply({ ephemeral: true });\n\n    \u002F\u002F Now you have 15 minutes\n    const result = await slowDatabaseQuery();\n    const aiResponse = await callLLM(result);\n\n    \u002F\u002F Edit the deferred reply\n    await interaction.editReply(`Result: ${aiResponse}`);\n  }\n};\n```\n\n```python\n# Pycord\n@bot.slash_command()\nasync def slow_command(ctx):\n    await ctx.defer()  # Acknowledge immediately\n    # await ctx.defer(ephemeral=True)  # For private response\n\n    result = await slow_operation()\n    await ctx.followup.send(f\"Result: {result}\")\n```\n\n## For components (buttons, menus)\n\n```javascript\n\u002F\u002F If you're updating the message\nawait interaction.deferUpdate();\n\n\u002F\u002F If you're sending a new response\nawait interaction.deferReply({ ephemeral: true });\n```\n\n### Missing Privileged Intent Configuration\n\nSeverity: CRITICAL\n\nSituation: Bot needs member data, presences, or message content\n\nSymptoms:\nMembers intent: member lists empty, on_member_join doesn't fire\nPresences intent: statuses always unknown\u002Foffline\nMessage content intent: message.content is empty string\n\nWhy this breaks:\nDiscord has 3 privileged intents that require manual enablement:\n1. **GUILD_MEMBERS** - Member join\u002Fleave, member lists\n2. **GUILD_PRESENCES** - Online status, activities\n3. **MESSAGE_CONTENT** - Read message text (deprecated for commands)\n\nThese must be:\n1. Enabled in Discord Developer Portal > Bot > Privileged Gateway Intents\n2. Requested in your bot code\n\nAt 100+ servers, you need Discord verification to keep using them.\n\nRecommended fix:\n\n## Step 1: Enable in Developer Portal\n\n```\n1. Go to https:\u002F\u002Fdiscord.com\u002Fdevelopers\u002Fapplications\n2. Select your application\n3. Go to Bot section\n4. Scroll to Privileged Gateway Intents\n5. Toggle ON the intents you need\n```\n\n## Step 2: Request in code\n\n```javascript\n\u002F\u002F Discord.js\nconst { Client, GatewayIntentBits } = require('discord.js');\n\nconst client = new Client({\n  intents: [\n    GatewayIntentBits.Guilds,\n    GatewayIntentBits.GuildMembers,       \u002F\u002F PRIVILEGED\n    \u002F\u002F GatewayIntentBits.GuildPresences,  \u002F\u002F PRIVILEGED\n    \u002F\u002F GatewayIntentBits.MessageContent,  \u002F\u002F PRIVILEGED - avoid!\n  ]\n});\n```\n\n```python\n# Pycord\nintents = discord.Intents.default()\nintents.members = True       # PRIVILEGED\n# intents.presences = True   # PRIVILEGED\n# intents.message_content = True  # PRIVILEGED - avoid!\n\nbot = commands.Bot(intents=intents)\n```\n\n## Avoid Message Content Intent if possible\n\nUse slash commands, buttons, and modals instead of message parsing.\nThese don't require the Message Content intent.\n\n### Command Registration Rate Limited\n\nSeverity: HIGH\n\nSituation: Registering slash commands\n\nSymptoms:\nCommands not appearing. 429 errors when deploying.\n\"You are being rate limited\" messages.\nCommands appear for some guilds but not others.\n\nWhy this breaks:\nCommand registration is rate limited:\n- Global commands: 200 creates\u002Fday, updates take up to 1 hour to propagate\n- Guild commands: 200 creates\u002Fday per guild, instant update\n\nCommon mistakes:\n- Registering commands on every bot startup\n- Registering in every guild separately\n- Making changes in a loop without delays\n\nRecommended fix:\n\n## Use a separate deploy script (not on startup)\n\n```javascript\n\u002F\u002F deploy-commands.js - Run manually, not on bot start\nconst { REST, Routes } = require('discord.js');\n\nconst rest = new REST().setToken(process.env.DISCORD_TOKEN);\n\nasync function deploy() {\n  \u002F\u002F For development: Guild commands (instant)\n  if (process.env.GUILD_ID) {\n    await rest.put(\n      Routes.applicationGuildCommands(\n        process.env.CLIENT_ID,\n        process.env.GUILD_ID\n      ),\n      { body: commands }\n    );\n    console.log('Guild commands deployed instantly');\n  }\n\n  \u002F\u002F For production: Global commands (up to 1 hour)\n  else {\n    await rest.put(\n      Routes.applicationCommands(process.env.CLIENT_ID),\n      { body: commands }\n    );\n    console.log('Global commands deployed (may take up to 1 hour)');\n  }\n}\n\ndeploy();\n```\n\n```python\n# Pycord - Don't sync on every startup\n@bot.event\nasync def on_ready():\n    # DON'T DO THIS:\n    # await bot.sync_commands()\n\n    print(f\"Ready! Commands should already be registered.\")\n\n# Instead, sync manually or use a flag\nif __name__ == \"__main__\":\n    if \"--sync\" in sys.argv:\n        # Only sync when explicitly requested\n        bot.sync_commands_on_start = True\n    bot.run(token)\n```\n\n## Testing workflow\n\n1. Use guild commands during development (instant updates)\n2. Only deploy global commands when ready for production\n3. Run deploy script manually, not on every restart\n\n### Bot Token Exposed\n\nSeverity: CRITICAL\n\nSituation: Storing or sharing bot token\n\nSymptoms:\nUnauthorized actions from your bot.\nBot joins random servers.\nBot sends spam or malicious content.\n\"Invalid token\" after Discord invalidates it.\n\nWhy this breaks:\nYour bot token provides FULL control over your bot. Attackers can:\n- Send messages as your bot\n- Join servers, create invites\n- Access all data your bot can access\n- Potentially take over servers where bot has admin\n\nDiscord actively scans GitHub for exposed tokens and invalidates them.\nCommon exposure points:\n- Committed to Git\n- Shared in Discord itself\n- In client-side code\n- In public screenshots\n\nRecommended fix:\n\n## Never hardcode tokens\n\n```javascript\n\u002F\u002F BAD - never do this\nconst token = 'MTIzNDU2Nzg5MDEyMzQ1Njc4.ABCDEF.xyz...';\n\n\u002F\u002F GOOD - environment variables\nrequire('dotenv').config();\nclient.login(process.env.DISCORD_TOKEN);\n```\n\n## Use .gitignore\n\n```\n# .gitignore\n.env\n.env.local\nconfig.json\n```\n\n## If token is exposed\n\n1. Go to Developer Portal immediately\n2. Regenerate the token\n3. Update all deployments\n4. Review bot activity for unauthorized actions\n5. Check git history and force push to remove if needed\n\n## Use environment variables properly\n\n```bash\n# .env (never commit)\nDISCORD_TOKEN=your_token_here\nCLIENT_ID=your_client_id\n```\n\n```javascript\n\u002F\u002F Load with dotenv\nrequire('dotenv').config();\nconst token = process.env.DISCORD_TOKEN;\n```\n\n### Bot Missing applications.commands Scope\n\nSeverity: HIGH\n\nSituation: Slash commands not appearing for users\n\nSymptoms:\nBot is in server but slash commands don't show up.\nTyping \u002F shows no commands from your bot.\nCommands worked in development server but not others.\n\nWhy this breaks:\nDiscord has two important OAuth scopes:\n- `bot` - Traditional bot permissions (messages, reactions, etc.)\n- `applications.commands` - Slash command permissions\n\nMany bots were invited with only the `bot` scope before slash commands\nexisted. They need to be re-invited with both scopes.\n\nRecommended fix:\n\n## Generate correct invite URL\n\n```\nhttps:\u002F\u002Fdiscord.com\u002Fapi\u002Foauth2\u002Fauthorize\n  ?client_id=YOUR_CLIENT_ID\n  &permissions=0\n  &scope=bot%20applications.commands\n```\n\n## In Discord Developer Portal\n\n1. Go to OAuth2 > URL Generator\n2. Select BOTH:\n   - `bot`\n   - `applications.commands`\n3. Select required bot permissions\n4. Use generated URL\n\n## Re-invite without kicking\n\nUsers can use the new invite URL even if bot is already in server.\nThis adds the new scope without removing the bot.\n\n```javascript\n\u002F\u002F Generate invite URL in code\nconst inviteUrl = client.generateInvite({\n  scopes: ['bot', 'applications.commands'],\n  permissions: [\n    'SendMessages',\n    'EmbedLinks',\n    \u002F\u002F Add other needed permissions\n  ]\n});\n```\n\n### Global Commands Not Appearing Immediately\n\nSeverity: MEDIUM\n\nSituation: Deploying global slash commands\n\nSymptoms:\nCommands don't appear after deployment.\nGuild commands work but global commands don't.\nCommands appear after an hour.\n\nWhy this breaks:\nGlobal commands can take up to 1 hour to propagate to all Discord servers.\nThis is by design for Discord's caching and CDN.\n\nGuild commands are instant but only work in that specific guild.\n\nRecommended fix:\n\n## Development: Use guild commands\n\n```javascript\n\u002F\u002F Instant updates for testing\nawait rest.put(\n  Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID),\n  { body: commands }\n);\n```\n\n## Production: Deploy global commands during off-peak\n\n```javascript\n\u002F\u002F Takes up to 1 hour to propagate\nawait rest.put(\n  Routes.applicationCommands(CLIENT_ID),\n  { body: commands }\n);\n```\n\n## Workflow\n\n1. Develop and test with guild commands (instant)\n2. When ready, deploy global commands\n3. Wait up to 1 hour for propagation\n4. Don't deploy global commands frequently\n\n### Frequent Gateway Disconnections\n\nSeverity: MEDIUM\n\nSituation: Bot randomly goes offline or misses events\n\nSymptoms:\nBot shows as offline intermittently.\nEvents are missed (member joins, messages).\nReconnection messages in logs.\n\nWhy this breaks:\nDiscord gateway requires regular heartbeats. Issues:\n- Blocking operations prevent heartbeat\n- Network instability\n- Memory pressure causing GC pauses\n- Too many guilds without sharding (2500+ requires sharding)\n\nRecommended fix:\n\n## Never block the event loop\n\n```javascript\n\u002F\u002F BAD - blocks event loop\nconst data = fs.readFileSync('file.json');\n\n\u002F\u002F GOOD - async\nconst data = await fs.promises.readFile('file.json');\n```\n\n## Handle reconnections gracefully\n\n```javascript\nclient.on('shardResume', (id, replayedEvents) => {\n  console.log(`Shard ${id} resumed, replayed ${replayedEvents} events`);\n});\n\nclient.on('shardDisconnect', (event, id) => {\n  console.log(`Shard ${id} disconnected`);\n});\n\nclient.on('shardReconnecting', (id) => {\n  console.log(`Shard ${id} reconnecting...`);\n});\n```\n\n## Implement sharding at scale\n\n```javascript\n\u002F\u002F Required at 2500+ guilds\nconst manager = new ShardingManager('.\u002Fbot.js', {\n  token: process.env.DISCORD_TOKEN,\n  totalShards: 'auto'\n});\nmanager.spawn();\n```\n\n### Modal Must Be First Response\n\nSeverity: MEDIUM\n\nSituation: Showing a modal from a slash command or button\n\nSymptoms:\n\"Interaction has already been acknowledged\" error.\nModal doesn't appear.\nWorks sometimes but not others.\n\nWhy this breaks:\nModals have a special requirement: showing a modal MUST be the first\nresponse to an interaction. You cannot:\n- defer() then showModal()\n- reply() then showModal()\n- Think for more than 3 seconds then showModal()\n\nRecommended fix:\n\n## Show modal immediately\n\n```javascript\n\u002F\u002F CORRECT - modal is first response\nasync execute(interaction) {\n  const modal = new ModalBuilder()\n    .setCustomId('my-modal')\n    .setTitle('Input Form');\n\n  \u002F\u002F Show immediately - no defer, no reply first\n  await interaction.showModal(modal);\n}\n```\n\n```javascript\n\u002F\u002F WRONG - deferred first\nasync execute(interaction) {\n  await interaction.deferReply();  \u002F\u002F CAN'T DO THIS\n  await interaction.showModal(modal);  \u002F\u002F Will fail\n}\n```\n\n## If you need to check something first\n\n```javascript\nasync execute(interaction) {\n  \u002F\u002F Quick sync check is OK (under 3 seconds)\n  if (!hasPermission(interaction.user.id)) {\n    return interaction.reply({\n      content: 'No permission',\n      ephemeral: true\n    });\n  }\n\n  \u002F\u002F Show modal (still first interaction response for this path)\n  await interaction.showModal(modal);\n}\n```\n\n## Validation Checks\n\n### Hardcoded Discord Token\n\nSeverity: ERROR\n\nDiscord tokens must never be hardcoded\n\nMessage: Hardcoded Discord token detected. Use environment variables.\n\n### Token Variable Assignment\n\nSeverity: ERROR\n\nTokens should come from environment, not strings\n\nMessage: Token assigned from string literal. Use environment variable.\n\n### Token in Client-Side Code\n\nSeverity: ERROR\n\nNever expose Discord tokens to browsers\n\nMessage: Discord credentials exposed client-side. Only use server-side.\n\n### Slow Operation Without Defer\n\nSeverity: WARNING\n\nSlow operations should be deferred to avoid timeout\n\nMessage: Slow operation without defer. Interaction may timeout.\n\n### Interaction Without Error Handling\n\nSeverity: WARNING\n\nInteractions should have try\u002Fcatch for graceful errors\n\nMessage: Interaction without error handling. Add try\u002Fcatch.\n\n### Using Message Content Intent\n\nSeverity: WARNING\n\nMessage Content is privileged, prefer slash commands\n\nMessage: Using Message Content intent. Consider slash commands instead.\n\n### Requesting All Intents\n\nSeverity: WARNING\n\nOnly request intents you actually need\n\nMessage: Requesting all intents. Only enable what you need.\n\n### Syncing Commands on Ready Event\n\nSeverity: WARNING\n\nDon't sync commands on every bot startup\n\nMessage: Syncing commands on startup. Use separate deploy script.\n\n### Registering Commands in Loop\n\nSeverity: WARNING\n\nUse bulk registration, not individual calls\n\nMessage: Registering commands in loop. Use bulk registration.\n\n### No Rate Limit Handling\n\nSeverity: INFO\n\nConsider handling rate limits for bulk operations\n\nMessage: Bulk operation without rate limit handling.\n\n## Collaboration\n\n### Delegation Triggers\n\n- user needs AI-powered Discord bot -> llm-architect (Integrate LLM for conversational Discord bot)\n- user needs Slack integration too -> slack-bot-builder (Cross-platform bot architecture)\n- user needs voice features -> voice-agents (Discord voice channel integration)\n- user needs database for bot data -> postgres-wizard (Store user data, server configs, moderation logs)\n- user needs workflow automation -> workflow-automation (Discord events trigger workflows)\n- user needs high availability -> devops (Sharding, scaling, monitoring for large bots)\n- user needs payment integration -> stripe-specialist (Premium bot features, subscription management)\n\n## When to Use\nUse this skill when the request clearly matches the capabilities and patterns described above.\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.\n","","imported","https:\u002F\u002Fgithub.com\u002Fsickn33\u002Fantigravity-awesome-skills","user_system_seed","SkillOPIC",true,110,316,"2026-05-16 13:15:25",{"id":8,"name":21,"slug":22,"icon":23,"description":24,"sort":25,"createdAt":26},"其他","other","mdi-page-next-outline","其他类型Skill",5,"2026-05-16 12:53:40",{"id":7,"name":28,"slug":29,"icon":30,"description":31,"moduleId":8,"sort":32,"skillCount":33,"createdAt":26},"职场发展","career","mdi-briefcase-outline","面试准备、简历优化、职业规划",4,575,[35],{"id":36,"skillId":4,"version":37,"fileName":38,"fileSize":39,"filePath":40,"fileHash":41,"manifest":42,"createdAt":19},"8b0fcb61-e74b-4ff1-a7dd-2eb46fe0f0eb","1.0.0","discord-bot-architect.zip",12035,"uploads\u002Fskills\u002F8f60bfe3-0d51-4943-bb39-6a55770b3c03\u002Fdiscord-bot-architect.zip","f6e66e37f1716e7b3db4f8223ddd2ec24a5815d8d3431df71662b12dd915409c","[{\"path\":\"SKILL.md\",\"isDirectory\":false,\"size\":38750}]",{"code":44,"message":45,"data":46},200,"success",{"items":47,"stats":48,"page":51},[],{"averageRating":49,"totalRatings":49,"ratingCounts":50},0,[49,49,49,49,49],{"limit":52,"offset":49,"hasMore":53,"nextOffset":52,"ratedOnly":16},15,false]