Specialized skill for building production-ready Discord bots. Covers Discord.js (JavaScript) and Pycord (Python), gateway intents, slash commands, interactive components, rate limiting, and sharding.
Add this skill
npx mdskills install sickn33/discord-bot-architectComprehensive Discord bot development guide with modern patterns and strong security awareness
1---2name: discord-bot-architect3description: "Specialized skill for building production-ready Discord bots. Covers Discord.js (JavaScript) and Pycord (Python), gateway intents, slash commands, interactive components, rate limiting, and sharding."4source: vibeship-spawner-skills (Apache 2.0)5---67# Discord Bot Architect89## Patterns1011### Discord.js v14 Foundation1213Modern Discord bot setup with Discord.js v14 and slash commands1415**When to use**: ['Building Discord bots with JavaScript/TypeScript', 'Need full gateway connection with events', 'Building bots with complex interactions']1617```javascript18```javascript19// src/index.js20const { Client, Collection, GatewayIntentBits, Events } = require('discord.js');21const fs = require('node:fs');22const path = require('node:path');23require('dotenv').config();2425// Create client with minimal required intents26const client = new Client({27 intents: [28 GatewayIntentBits.Guilds,29 // Add only what you need:30 // GatewayIntentBits.GuildMessages,31 // GatewayIntentBits.MessageContent, // PRIVILEGED - avoid if possible32 ]33});3435// Load commands36client.commands = new Collection();37const commandsPath = path.join(__dirname, 'commands');38const commandFiles = fs.readdirSync(commandsPath).filter(f => f.endsWith('.js'));3940for (const file of commandFiles) {41 const filePath = path.join(commandsPath, file);42 const command = require(filePath);43 if ('data' in command && 'execute' in command) {44 client.commands.set(command.data.name, command);45 }46}4748// Load events49const eventsPath = path.join(__dirname, 'events');50const eventFiles = fs.readdirSync(eventsPath).filter(f => f.endsWith('.js'));5152for (const file of eventFiles) {53 const filePath = path.join(eventsPath, file);54 const event = require(filePath);55 if (event.once) {56 client.once(event.name, (...args) => event.execute(...args));57 } else {58 client.on(event.name, (...args) => event.execute(...args));59 }60}6162client.login(process.env.DISCORD_TOKEN);63```6465```javascript66// src/commands/ping.js67const { SlashCommandBuilder } = require('discord.js');6869module.exports = {70 data: new SlashCommandBuilder()71 .setName('ping')72 .setDescription('Replies with Pong!'),7374 async execute(interaction) {75 const sent = await interaction.reply({76 content: 'Pinging...',77 fetchReply: true78 });7980 const latency = sent.createdTimestamp - interaction.createdTimestamp;81 await interaction.editReply(`Pong! Latency: ${latency}ms`);82 }83};84```8586```javascript87// src/events/interactionCreate.js88const { Events } = require('discord.js');8990module.exports = {91 name: Event92```9394### Pycord Bot Foundation9596Discord bot with Pycord (Python) and application commands9798**When to use**: ['Building Discord bots with Python', 'Prefer async/await patterns', 'Need good slash command support']99100```python101```python102# main.py103import os104import discord105from discord.ext import commands106from dotenv import load_dotenv107108load_dotenv()109110# Configure intents - only enable what you need111intents = discord.Intents.default()112# intents.message_content = True # PRIVILEGED - avoid if possible113# intents.members = True # PRIVILEGED114115bot = commands.Bot(116 command_prefix="!", # Legacy, prefer slash commands117 intents=intents118)119120@bot.event121async def on_ready():122 print(f"Logged in as {bot.user}")123 # Sync commands (do this carefully - see sharp edges)124 # await bot.sync_commands()125126# Slash command127@bot.slash_command(name="ping", description="Check bot latency")128async def ping(ctx: discord.ApplicationContext):129 latency = round(bot.latency * 1000)130 await ctx.respond(f"Pong! Latency: {latency}ms")131132# Slash command with options133@bot.slash_command(name="greet", description="Greet a user")134async def greet(135 ctx: discord.ApplicationContext,136 user: discord.Option(discord.Member, "User to greet"),137 message: discord.Option(str, "Custom message", required=False)138):139 msg = message or "Hello!"140 await ctx.respond(f"{user.mention}, {msg}")141142# Load cogs143for filename in os.listdir("./cogs"):144 if filename.endswith(".py"):145 bot.load_extension(f"cogs.{filename[:-3]}")146147bot.run(os.environ["DISCORD_TOKEN"])148```149150```python151# cogs/general.py152import discord153from discord.ext import commands154155class General(commands.Cog):156 def __init__(self, bot):157 self.bot = bot158159 @commands.slash_command(name="info", description="Bot information")160 async def info(self, ctx: discord.ApplicationContext):161 embed = discord.Embed(162 title="Bot Info",163 description="A helpful Discord bot",164 color=discord.Color.blue()165 )166 embed.add_field(name="Servers", value=len(self.bot.guilds))167 embed.add_field(name="Latency", value=f"{round(self.bot.latency * 1000)}ms")168 await ctx.respond(embed=embed)169170 @commands.Cog.171```172173### Interactive Components Pattern174175Using buttons, select menus, and modals for rich UX176177**When to use**: ['Need interactive user interfaces', 'Collecting user input beyond slash command options', 'Building menus, confirmations, or forms']178179```python180```javascript181// Discord.js - Buttons and Select Menus182const {183 SlashCommandBuilder,184 ActionRowBuilder,185 ButtonBuilder,186 ButtonStyle,187 StringSelectMenuBuilder,188 ModalBuilder,189 TextInputBuilder,190 TextInputStyle191} = require('discord.js');192193module.exports = {194 data: new SlashCommandBuilder()195 .setName('menu')196 .setDescription('Shows an interactive menu'),197198 async execute(interaction) {199 // Button row200 const buttonRow = new ActionRowBuilder()201 .addComponents(202 new ButtonBuilder()203 .setCustomId('confirm')204 .setLabel('Confirm')205 .setStyle(ButtonStyle.Primary),206 new ButtonBuilder()207 .setCustomId('cancel')208 .setLabel('Cancel')209 .setStyle(ButtonStyle.Danger),210 new ButtonBuilder()211 .setLabel('Documentation')212 .setURL('https://discord.js.org')213 .setStyle(ButtonStyle.Link) // Link buttons don't emit events214 );215216 // Select menu row (one per row, takes all 5 slots)217 const selectRow = new ActionRowBuilder()218 .addComponents(219 new StringSelectMenuBuilder()220 .setCustomId('select-role')221 .setPlaceholder('Select a role')222 .setMinValues(1)223 .setMaxValues(3)224 .addOptions([225 { label: 'Developer', value: 'dev', emoji: '๐ป' },226 { label: 'Designer', value: 'design', emoji: '๐จ' },227 { label: 'Community', value: 'community', emoji: '๐' }228 ])229 );230231 await interaction.reply({232 content: 'Choose an option:',233 components: [buttonRow, selectRow]234 });235236 // Collect responses237 const collector = interaction.channel.createMessageComponentCollector({238 filter: i => i.user.id === interaction.user.id,239 time: 60_000 // 60 seconds timeout240 });241242 collector.on('collect', async i => {243 if (i.customId === 'confirm') {244 await i.update({ content: 'Confirmed!', components: [] });245 collector.stop();246 } else if (i.custo247```248249## Anti-Patterns250251### โ Message Content for Commands252253**Why bad**: Message Content Intent is privileged and deprecated for bot commands.254Slash commands are the intended approach.255256### โ Syncing Commands on Every Start257258**Why bad**: Command registration is rate limited. Global commands take up to 1 hour259to propagate. Syncing on every start wastes API calls and can hit limits.260261### โ Blocking the Event Loop262263**Why bad**: Discord gateway requires regular heartbeats. Blocking operations264cause missed heartbeats and disconnections.265266## โ ๏ธ Sharp Edges267268| Issue | Severity | Solution |269|-------|----------|----------|270| Issue | critical | ## Acknowledge immediately, process later |271| Issue | critical | ## Step 1: Enable in Developer Portal |272| Issue | high | ## Use a separate deploy script (not on startup) |273| Issue | critical | ## Never hardcode tokens |274| Issue | high | ## Generate correct invite URL |275| Issue | medium | ## Development: Use guild commands |276| Issue | medium | ## Never block the event loop |277| Issue | medium | ## Show modal immediately |278
Full transparency โ inspect the skill content before installing.