Files
pixelpoebel/AGENTS-BOT.md
sarah 390297936d
All checks were successful
Auto Build and Push Docker Image / build (push) Successful in 28s
fix: make trigger list public and ephemeral, fix rolesetup types, and improve bot safety
- Make /trigger list and help public subcommands with ephemeral replies
- Restrict /trigger add and remove to moderators/admins
- Use Unicode-aware regex and strip URLs before trigger word matching
- Ensure guild_settings exist before UPDATE queries in welcome and log commands
- Fix rolesetup edit neue_rolle type to addRoleOption and prevent label truncation
- Reset select menu components after selection so users can re-click same options
- Validate Twitch streamers via Helix Users API and prevent interaction timeouts with deferReply
- Connect TwitchMonitor automatically on startup and when first channel is added
- Add moderation role-hierarchy checks to admin commands and enforce timeout limits
- Disable DM execution on server-bound commands to prevent SQLite null constraint errors
- Update README.md and AGENTS-BOT.md with new constraints and command documentation
2026-11-01 12:34:54 +01:00

9.8 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.) via centralized EventLogger.
  12. Role Selection System: Self-service role assignment via select menus with support for exclusive categories and max-role limits.
  13. Cat & CatGIF System: TheCatAPI integration for random cat images and GIFs (CatManager).
  14. Centralized Event Logger: Kapsel event logs into EventLogger.sendLog() to eliminate duplicated logging logic across event files.
  15. Stopwatch System: Persistent count-up timer tracking stored in SQLite (stopwatches table) calculating elapsed time dynamically on query.

📁 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, Indexes & Surgical Caching
│       ├── EventLogger.ts    # Centralized event logging helper
│       ├── CatManager.ts     # TheCatAPI HTTP request helper
│       ├── 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, foreign keys, and performance indexes for frequent lookups. 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 & indexes ...
    }

    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.
  5. Partial Message Handling: In messageDelete and messageUpdate events, always handle partial messages safely (e.g. message.author or message.content may be missing for uncached 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.
  8. Event Logger Centralization: Use EventLogger.sendLog(client, guildId, event, embed) for logging server events instead of inline/duplicated helper functions.
  9. Discord Timeout Limit: Discord API limits timeouts to maximum 28 days (2419200000 ms). Never pass null to member.timeout(durationMs) when muting.
  10. Role Menu Error Handling: Always wrap member.roles.add and member.roles.remove in try/catch blocks in interaction listeners to handle permission/hierarchy errors gracefully.
  11. Select Menu Emoji Handling: Never pass an empty string '' to setEmoji(). Only call setEmoji() when a valid, non-empty emoji string is provided.
  12. Team Application Owner Support: When checking bot owner status, check process.env.BOT_OWNER_ID or test if client.application.owner is a Team object with owner.members.has(userId).
  13. Stopwatch Duplicate Prevention & Custom Start Time: When creating stopwatches, perform a case-insensitive name uniqueness check per user/guild (LOWER(name)). Validate custom start times (startzeit) to ensure they do not lie in the future.
  14. Public vs. Mod Subcommands & Ephemeral Output: For commands offering both public query options and mod-only actions (e.g. /trigger, /timer, /twitch), do not set defaultMemberPermissions on the root SlashCommandBuilder if subcommands like list or help should be public. Check isAdmin inside mutating subcommands (add, remove), and deliver list/help subcommands with ephemeral: true.
  15. Server-Bound Commands & DM Isolation: Commands relying on guild context (guildId, roles, settings) must enforce .setDMPermission(false) and validate if (!interaction.guildId) return; to prevent SQLite null constraint errors in direct messages.
  16. Guild Settings Provisioning: Before issuing raw SQL updates on guild_settings, call client.DB.getSettings(guildId) to ensure the guild settings record exists in SQLite.
  17. Unicode-Safe Trigger Matching & URL Filtering: In messageCreate, strip URLs before matching auto-responses and use Unicode property escapes ((?<![\p{L}\p{N}_])...(?![\p{L}\p{N}_]) with iu) to handle umlauts and avoid URL false positives.
  18. Role Option Select Menus & Typing: Subcommand options taking a Discord role to edit must use .addRoleOption(). After role select interactions, re-render/reset components so users can click the same option again to toggle or remove it.
  19. Twitch Helix User Validation & Interaction Deferral: Commands validating Twitch streamers must use interaction.deferReply() to avoid 3-second interaction timeouts and validate user accounts via the Helix Users API (/helix/users).