AI Skill Report Card

Developing Discord Bots

A-82·Jun 15, 2026·Source: Web
15 / 15

Modern Slash Command (Python)

Python
import discord from discord.ext import commands import asyncio import logging # Production-ready bot setup logging.basicConfig(level=logging.INFO) class Bot(commands.Bot): def __init__(self): intents = discord.Intents.default() intents.message_content = True super().__init__(command_prefix='!', intents=intents) async def on_ready(self): logging.info(f'{self.user} has connected to Discord!') try: synced = await self.tree.sync() logging.info(f'Synced {len(synced)} commands') except Exception as e: logging.error(f'Failed to sync commands: {e}') bot = Bot() @bot.tree.command(name='hello', description='Greet a user') async def hello(interaction: discord.Interaction, user: discord.Member = None): try: target = user or interaction.user await interaction.response.send_message(f'Hello {target.display_name}!') except Exception as e: logging.error(f'Hello command error: {e}') await interaction.followup.send('Something went wrong!', ephemeral=True)

Modern Slash Command (JavaScript)

JavaScript
const { Client, GatewayIntentBits, SlashCommandBuilder } = require('discord.js'); const client = new Client({ intents: [GatewayIntentBits.Guilds] }); client.once('ready', async () => { console.log(`${client.user.tag} has connected to Discord!`); const commands = [ new SlashCommandBuilder() .setName('hello') .setDescription('Greet a user') .addUserOption(option => option.setName('user') .setDescription('User to greet') .setRequired(false) ) ]; try { await client.application.commands.set(commands); console.log('Commands synced successfully'); } catch (error) { console.error('Failed to sync commands:', error); } }); client.on('interactionCreate', async interaction => { if (!interaction.isChatInputCommand()) return; if (interaction.commandName === 'hello') { try { const user = interaction.options.getUser('user') || interaction.user; await interaction.reply(`Hello ${user.displayName}!`); } catch (error) { console.error('Hello command error:', error); await interaction.reply({ content: 'Something went wrong!', ephemeral: true }); } } });
Recommendation
Complete the JavaScript database example that cuts off mid-sentence - the skill appears unfinished
13 / 15

Bot Development Process

Progress:

  • Architecture Planning - Define bot type, features, and scaling requirements
  • Environment Setup - Configure development and production environments
  • Core Bot Structure - Implement base bot class with proper error handling
  • Command System - Build modular command structure with validation
  • Database Integration - Set up persistent data storage with connection pooling
  • External API Integration - Implement HTTP clients with retry logic
  • Permission System - Configure role-based access control
  • Interactive Components - Add buttons, select menus, and state management
  • Production Deployment - Deploy with monitoring and stability measures
  • Testing & Monitoring - Implement logging, error tracking, and performance monitoring

1. Bot Architecture & Code Organization

Modular Cog Structure (Python)

Python
# bot.py - Main bot file import discord from discord.ext import commands import asyncio import os from pathlib import Path class ProductionBot(commands.Bot): def __init__(self): intents = discord.Intents.default() intents.message_content = True super().__init__( command_prefix=commands.when_mentioned_or('!'), intents=intents, help_command=None ) async def setup_hook(self): """Load all cogs on startup""" cog_path = Path('./cogs') for file in cog_path.glob('*.py'): if file.name != '__init__.py': try: await self.load_extension(f'cogs.{file.stem}') print(f'Loaded {file.stem}') except Exception as e: print(f'Failed to load {file.stem}: {e}') async def on_ready(self): print(f'{self.user} is ready!') await self.tree.sync() # cogs/moderation.py - Modular functionality class ModerationCog(commands.Cog): def __init__(self, bot): self.bot = bot @discord.app_commands.command(name='kick') @discord.app_commands.describe(user='User to kick', reason='Reason for kick') async def kick_user(self, interaction: discord.Interaction, user: discord.Member, reason: str = None): # Check permissions if not interaction.user.guild_permissions.kick_members: return await interaction.response.send_message( 'You lack kick permissions!', ephemeral=True ) try: await user.kick(reason=reason) await interaction.response.send_message( f'Kicked {user.mention}. Reason: {reason or "No reason provided"}' ) # Log the action log_channel = discord.utils.get(interaction.guild.channels, name='mod-logs') if log_channel: embed = discord.Embed( title='User Kicked', color=discord.Color.orange(), timestamp=interaction.created_at ) embed.add_field(name='User', value=user.mention, inline=True) embed.add_field(name='Moderator', value=interaction.user.mention, inline=True) embed.add_field(name='Reason', value=reason or 'No reason', inline=False) await log_channel.send(embed=embed) except discord.Forbidden: await interaction.response.send_message( 'I lack permissions to kick this user!', ephemeral=True ) except Exception as e: await interaction.response.send_message( f'Error kicking user: {e}', ephemeral=True ) async def setup(bot): await bot.add_cog(ModerationCog(bot))

Modular Structure (JavaScript)

JavaScript
// bot.js - Main bot file const { Client, GatewayIntentBits, Collection } = require('discord.js'); const fs = require('fs'); const path = require('path'); class ProductionBot extends Client { constructor() { super({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent ] }); this.commands = new Collection(); this.loadCommands(); } loadCommands() { const commandsPath = path.join(__dirname, 'commands'); const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js')); for (const file of commandFiles) { try { const command = require(path.join(commandsPath, file)); this.commands.set(command.data.name, command); console.log(`Loaded command: ${command.data.name}`); } catch (error) { console.error(`Failed to load ${file}:`, error); } } } } const bot = new ProductionBot(); bot.on('interactionCreate', async interaction => { if (!interaction.isChatInputCommand()) return; const command = bot.commands.get(interaction.commandName); if (!command) return; try { await command.execute(interaction); } catch (error) { console.error('Command execution error:', error); const reply = { content: 'There was an error executing this command!', ephemeral: true }; if (interaction.replied || interaction.deferred) { await interaction.followup.send(reply); } else { await interaction.reply(reply); } } }); // commands/moderation.js - Command module const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js'); module.exports = { data: new SlashCommandBuilder() .setName('kick') .setDescription('Kick a user from the server') .addUserOption(option => option.setName('user') .setDescription('User to kick') .setRequired(true)) .addStringOption(option => option.setName('reason') .setDescription('Reason for the kick') .setRequired(false)) .setDefaultMemberPermissions(PermissionFlagsBits.KickMembers), async execute(interaction) { const user = interaction.options.getMember('user'); const reason = interaction.options.getString('reason') || 'No reason provided'; if (!interaction.member.permissions.has(PermissionFlagsBits.KickMembers)) { return await interaction.reply({ content: 'You lack kick permissions!', ephemeral: true }); } try { await user.kick(reason); await interaction.reply(`Kicked ${user.user.tag}. Reason: ${reason}`); // Log action const logChannel = interaction.guild.channels.cache.find(ch => ch.name === 'mod-logs'); if (logChannel) { const embed = { title: 'User Kicked', color: 0xFFA500, timestamp: new Date().toISOString(), fields: [ { name: 'User', value: `${user.user.tag} (${user.id})`, inline: true }, { name: 'Moderator', value: interaction.user.tag, inline: true }, { name: 'Reason', value: reason, inline: false } ] }; await logChannel.send({ embeds: [embed] }); } } catch (error) { console.error('Kick error:', error); await interaction.reply({ content: 'Failed to kick user. Check my permissions.', ephemeral: true }); } } };

2. Database Integration

PostgreSQL with Connection Pooling (Python)

Python
import asyncpg import asyncio from contextlib import asynccontextmanager import os class DatabaseManager: def __init__(self): self.pool = None async def create_pool(self): """Create connection pool on startup""" self.pool = await asyncpg.create_pool( host=os.getenv('DB_HOST', 'localhost'), port=os.getenv('DB_PORT', 5432), user=os.getenv('DB_USER'), password=os.getenv('DB_PASSWORD'), database=os.getenv('DB_NAME'), min_size=10, max_size=20, command_timeout=60 ) # Initialize tables async with self.pool.acquire() as conn: await conn.execute(""" CREATE TABLE IF NOT EXISTS user_data ( user_id BIGINT PRIMARY KEY, guild_id BIGINT, xp INTEGER DEFAULT 0, last_message TIMESTAMP DEFAULT NOW(), created_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_user_guild ON user_data(user_id, guild_id); """) @asynccontextmanager async def acquire(self): """Context manager for database connections""" async with self.pool.acquire() as conn: try: yield conn except Exception as e: await conn.execute('ROLLBACK') raise async def add_xp(self, user_id: int, guild_id: int, xp: int): """Add XP to user with upsert pattern""" async with self.acquire() as conn: await conn.execute(""" INSERT INTO user_data (user_id, guild_id, xp, last_message) VALUES ($1, $2, $3, NOW()) ON CONFLICT (user_id) DO UPDATE SET xp = user_data.xp + $3, last_message = NOW() """, user_id, guild_id, xp) async def get_user_xp(self, user_id: int): """Get user XP with error handling""" try: async with self.acquire() as conn: row = await conn.fetchrow( "SELECT xp FROM user_data WHERE user_id = $1", user_id ) return row['xp'] if row else 0 except Exception as e: logging.error(f'Database error getting XP for {user_id}: {e}') return 0 # Usage in cog class LevelingCog(commands.Cog): def __init__(self, bot): self.bot = bot self.db = DatabaseManager() async def cog_load(self): await self.db.create_pool() @commands.Cog.listener() async def on_message(self, message): if message.author.bot: return # Add XP (rate limited to prevent spam) await self.db.add_xp(message.author.id, message.guild.id, 5)

SQLite with Error Recovery (JavaScript)

JavaScript
const Database = require('better-sqlite3'); const path = require('path'); class DatabaseManager { constructor() { this.db = null; this.init(); } init() { try { this.db = new Database(path.join(__dirname, 'data', 'bot.db')); this.db.pragma('journal_mode = WAL'); this.db.pragma('foreign_keys = ON'); // Initialize tables this.db.exec(` CREATE TABLE IF NOT EXISTS user_data ( user_id TEXT PRIMARY KEY, guild_id TEXT, xp INTEGER DEFAULT 0, last_message DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_user_guild ON user_data(user_id, guild_id); `); // Prepare statements for better performance this.statements = { addXp: this.db.prepare(` INSERT INTO user_data (user_id, guild_id, xp, last_message) VALUES (?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(user_id) DO UPDATE SET xp = xp + excluded.xp, last_message = CURRENT_TIMESTAMP `), getUserXp: this.db.prepare(` SELECT xp FROM user_data WHERE user_id = ? `), getLeaderboard: this.db.prepare(` SELECT user_id, xp FROM user_data WHERE guild_id = ? ORDER BY xp DESC LIMIT ? `) }; console.log('Database initialized successfully');
Recommendation
Add specific deployment examples (Docker, Heroku, VPS setup) and monitoring tools (Winston, Sentry) in the completeness section
0
Grade A-AI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
15/15
Workflow
13/15
Examples
17/20
Completeness
10/20
Format
15/15
Conciseness
12/15