# AI Agent Guide: PixelpΓΆbel Discord Bot This document is intended for AI agents to understand and recreate the `pixelpΓΆbel` Discord bot. ## πŸ— Architecture Overview The bot uses a **Modular Command & Event Loading** pattern with **ESM (ECMAScript Modules)** and **TypeScript**, emphasizing strict typing and performance. ### Key Design Patterns: 1. **Extended Client Pattern:** Extend `discord.js` `Client` class to hold global state and a typed `DB` instance. 2. **Dynamic Discovery:** `index.ts` uses `readdirSync` and dynamic `import()` to register commands/events. 3. **Automatic Command Deployment:** Commands are automatically registered with Discord on startup via `Deployer.deployCommands()`, unless disabled via `AUTO_DEPLOY=false`. 4. **Interface-Driven Commands:** All commands implement the `Command` interface. 5. **Optimized SQLite Database:** Persistent storage with `better-sqlite3`, utilizing **WAL (Write-Ahead Logging)**, **Foreign Key support**, and **surgical In-Memory Caching** (clearing specific guild data on updates). 6. **Twitch Monitoring:** Periodic API polling using **Batch-Requests** (100 channels per request) with Stream ID tracking and **database transactions** for status updates. 7. **TwitchMonitor IRC:** TMI.js-based IRC chat monitoring for mod events, using **reusable WebhookClient instances** for efficiency. 8. **Reminder System:** Periodic reminder checking with `target_time` tracking. 9. **Auto-Response System:** Trigger word detection for automatic replies (cached). 10. **Welcome/Goodbye System:** Guild member add/remove events with customizable messages. 11. **Logging System:** Configurable event logging (messages, roles, moderation, etc.). 12. **Role Selection System:** Self-service role assignment via select menus with support for exclusive categories and max-role limits. ## πŸ“ Project Structure ``` pixelpoebel/ β”œβ”€β”€ src/ β”‚ β”œβ”€β”€ index.ts # Entry point, loads commands/events/auto-deploy β”‚ β”œβ”€β”€ deploy-commands.ts # Manual deploy script for Discord β”‚ β”œβ”€β”€ commands/ β”‚ β”‚ └── utility/ # All commands grouped here β”‚ β”œβ”€β”€ events/ β”‚ β”‚ β”œβ”€β”€ ... # All Discord event handlers β”‚ └── structures/ β”‚ β”œβ”€β”€ ExtendedClient.ts # Typed client with DB property β”‚ β”œβ”€β”€ Command.ts β”‚ β”œβ”€β”€ Database.ts # Database with Transaction & Surgical Caching β”‚ β”œβ”€β”€ TwitchManager.ts # Batch Polling & Transaction logic β”‚ β”œβ”€β”€ TwitchMonitor.ts # IRC Monitoring & Webhook reuse β”‚ β”œβ”€β”€ TwitchCache.ts # IRC Message FIFO Cache β”‚ β”œβ”€β”€ ReminderManager.ts β”‚ └── Deployer.ts # Centralized deployment logic β”œβ”€β”€ data/ # SQLite database (volume mounted) β”œβ”€β”€ package.json β”œβ”€β”€ tsconfig.json β”œβ”€β”€ Dockerfile β”œβ”€β”€ docker-compose.yml β”œβ”€β”€ .env.example └── .gitattributes ``` ## πŸ“ Implementation Steps ### 1. Database with Surgical Caching The database uses WAL mode and foreign keys. Caching is surgical (clears specific guild entries when possible). ```typescript // src/structures/Database.ts export class DB { private static settingsCache = new Map(); static init() { db.pragma('journal_mode = WAL'); db.pragma('foreign_keys = ON'); // ... Table creation ... } static run(query: string, ...params: any[]) { const result = db.prepare(query).run(...params); if (query.toLowerCase().includes('guild_settings')) { const guildId = params.find(p => typeof p === 'string' && /^\d{17,20}$/.test(p)); if (guildId) this.settingsCache.delete(guildId); else this.settingsCache.clear(); } return result; } static transaction(fn: () => T): T { return db.transaction(fn)(); } } ``` ### 2. Twitch Batch Polling with Transactions Updates are collected and saved in a single transaction to reduce disk I/O. ```typescript // src/structures/TwitchManager.ts static async checkStreams(client: ExtendedClient) { // ... fetch streams ... const updates = []; // collect updates here if (updates.length > 0) { client.DB.transaction(() => { for (const update of updates) { // ... client.DB.run update ... } }); } } ``` ### 3. Webhook Reuse in TwitchMonitor Avoid recreating `WebhookClient` instances for every message to improve performance. ```typescript // src/structures/TwitchMonitor.ts private async sendWebhook(webhookUrl: string, data: any, monitorKey?: string) { let webhook = monitorKey ? this.webhookClients.get(monitorKey) : new WebhookClient({ url: webhookUrl }); await webhook.send({ embeds: [embed] }); } ``` ### 4. Automatic Deployment Commands are loaded into an array and deployed during the `index.ts` startup sequence. ```typescript // src/index.ts const commandData = []; // ... while loading commands ... commandData.push(command.data.toJSON()); if (process.env.AUTO_DEPLOY !== 'false') { await Deployer.deployCommands(process.env.CLIENT_ID, process.env.DISCORD_TOKEN, commandData); } ``` ## πŸš€ Performance Benchmarks (approx.) | Metric | Old (Individual) | New (Batch/Cached/Transaction) | |--------|------------------|-------------------------------| | API Calls (500 channels) | 500 | 5 | | DB Access (Polling) | Individual writes | Single Transaction | | Webhook Overhead | New Connection | Connection Reuse | | Notification Delay | ~5-7 min | ~2 min | ## ⚠️ Critical Constraints 1. **Transaction Safety:** Never call async functions (like `fetch` or `channel.send`) inside a synchronous `db.transaction()`. 2. **Foreign Keys:** Always enable `foreign_keys = ON` to maintain data integrity. 3. **Type Safety:** Use `ExtendedClient` instead of `any` for the client instance. 4. **Webhook Reuse:** Map `WebhookClient` instances to their monitor keys to prevent leaks. 5. **Partial Message Handling:** In `messageDelete` and `messageUpdate` events, always check for null `author` property to avoid runtime crashes on uncached partial messages. 6. **Centralized Settings Cache:** Always use `client.DB.getSettings(guildId)` instead of raw SQLite select queries for `guild_settings` to leverage central caching and automatic default generation. 7. **Category Role Selection Limits:** Do not count general guild roles when checking role selection limits; filter the user's role list using only the specific role IDs registered under that category.