Files
pixelpoebel/AGENTS-BOT.md
coding d546035bb7
Some checks failed
Auto Build and Push Docker Image / build (push) Failing after 14s
Optimize database, implement automatic command deployment, and improve project-wide type safety
2026-04-29 19:50:50 +02:00

5.9 KiB

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).

// src/structures/Database.ts
export class DB {
    private static settingsCache = new Map<string, any>();
    
    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<T>(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.

// 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.

// 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.

// 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.