Files
pixelpoebel/AGENTS-BOT.md
sarah 5c6188ea13
All checks were successful
Auto Build and Push Docker Image / build (push) Successful in 6s
Add: Timer and Trigger modules with help subcommands
- Add /timer command: add, remove, list, listall, help
- Add /trigger command: add, remove, list, help
- Add /admin help and /owner help subcommands
- Add /twitch help subcommand
- Add ReminderManager for timer notifications
- Add reminders table to database
- Update README.md and AGENTS-BOT.md documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 18:53:33 +01:00

333 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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**.
### Key Design Patterns:
1. **Extended Client Pattern:** Extend `discord.js` `Client` class to hold global state.
2. **Dynamic Discovery:** `index.ts` uses `readdirSync` and dynamic `import()` to register commands/events.
3. **Interface-Driven Commands:** All commands implement `Command` interface.
4. **SQLite Database:** Persistent data storage with `better-sqlite3`.
5. **Twitch Monitoring:** Periodic API polling with Stream ID tracking.
6. **Reminder System:** Periodic reminder checking with target_time tracking.
7. **Auto-Response System:** Trigger word detection for automatic replies.
8. **Grouped Commands:** Admin/Owner/Trigger/Timer commands grouped under subcommands.
## 📁 Project Structure
```
pixelpoebel/
├── src/
│ ├── index.ts # Entry point, loads commands/events
│ ├── deploy-commands.ts # Deploy script for Discord
│ ├── commands/
│ │ └── utility/ # All commands grouped here
│ │ ├── ping.ts
│ │ ├── help.ts
│ │ ├── twitch.ts
│ │ ├── trigger.ts # Auto-responses
│ │ ├── timer.ts # Reminders
│ │ ├── admin.ts # All admin subcommands
│ │ └── owner.ts # All owner subcommands
│ ├── events/
│ │ ├── ready.ts
│ │ └── interactionCreate.ts
│ └── structures/
│ ├── ExtendedClient.ts
│ ├── Command.ts
│ ├── Database.ts
│ ├── TwitchManager.ts
│ ├── ReminderManager.ts
│ └── Deployer.ts
├── data/ # SQLite database (volume mounted)
├── package.json
├── tsconfig.json
├── Dockerfile
├── docker-compose.yml
└── .env.example
```
## 📝 Implementation Steps
### 1. Project Setup
```json
{
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "nodemon --watch 'src/**/*.ts' --exec 'node --loader ts-node/esm' src/index.ts",
"deploy": "node --loader ts-node/esm src/deploy-commands.ts"
}
}
```
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true
}
}
```
### 2. Database Structure
```sql
-- guild_settings
CREATE TABLE guild_settings (
guild_id TEXT PRIMARY KEY,
prefix TEXT DEFAULT '!',
mod_log_channel TEXT,
welcome_channel TEXT,
warn_threshold INTEGER DEFAULT 3,
warn_action TEXT DEFAULT 'ban',
warn_mute_duration INTEGER DEFAULT 1800
);
-- mod_logs
CREATE TABLE mod_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id TEXT,
user_id TEXT,
moderator_id TEXT,
action TEXT,
reason TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- command_stats
CREATE TABLE command_stats (
command_name TEXT PRIMARY KEY,
uses INTEGER DEFAULT 0
);
-- dm_users
CREATE TABLE dm_users (
user_id TEXT PRIMARY KEY,
username TEXT,
last_seen DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- twitch_monitors
CREATE TABLE twitch_monitors (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id TEXT,
channel_name TEXT,
discord_channel_id TEXT,
custom_message TEXT,
last_status TEXT DEFAULT 'offline',
last_stream_id TEXT,
UNIQUE(guild_id, channel_name)
);
-- auto_responses
CREATE TABLE auto_responses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id TEXT,
trigger_word TEXT,
response_text TEXT,
UNIQUE(guild_id, trigger_word)
);
-- reminders
CREATE TABLE reminders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id TEXT,
channel_id TEXT,
user_id TEXT,
reminder_text TEXT,
target_time DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
```
### 3. Key Components
**ExtendedClient.ts:**
```typescript
import { Client, Collection, GatewayIntentBits } from 'discord.js';
import { Command } from './Command.js';
export class ExtendedClient extends Client {
public commands: Collection<string, Command> = new Collection();
constructor() {
super({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildMembers,
],
});
}
}
```
**Command Interface:**
```typescript
export interface Command {
data: any;
category: 'Owner' | 'Admin' | 'Public';
execute: (interaction: any, client: any) => Promise<void> | void;
cooldown?: number;
}
```
### 4. Timer System
**Time Parsing:**
```typescript
// Duration parsing: "30s", "2h", "1d30m", "3wochen"
function parseDuration(input: string): number | null {
const unitMs: Record<string, number> = {
's': 1000, 'sec': 1000, 'sek': 1000, 'sekunden': 1000,
'm': 60000, 'min': 60000, 'minuten': 60000,
'h': 3600000, 'hour': 3600000, 'std': 3600000, 'stunden': 3600000,
'd': 86400000, 'day': 86400000, 't': 86400000, 'tage': 86400000,
'w': 604800000, 'week': 604800000, 'wochen': 604800000,
'mo': 2592000000, 'month': 2592000000, 'monat': 2592000000, 'monate': 2592000000,
'y': 31536000000, 'year': 31536000000, 'j': 31536000000, 'jahr': 31536000000, 'jahre': 31536000000
};
// Parse and calculate total milliseconds
}
// Date parsing: "25.12.2024 14:30", "morgen 15:00"
function parseDateTime(input: string): Date | null {
// German date format, ISO format, or relative "tomorrow"
}
```
**Reminder Polling:**
```typescript
export class ReminderManager {
static async checkReminders(client: any) {
const now = new Date().toISOString();
const dueReminders = DB.all('SELECT * FROM reminders WHERE target_time <= ?', now);
for (const reminder of dueReminders) {
const guild = client.guilds.cache.get(reminder.guild_id);
const channel = guild.channels.cache.get(reminder.channel_id);
// Send notification and delete reminder
}
}
static startPolling(client: any) {
setInterval(() => this.checkReminders(client), 30 * 1000);
}
}
```
### 5. Auto-Response (Trigger) System
```typescript
// Add trigger (admin only)
DB.run('INSERT INTO auto_responses (guild_id, trigger_word, response_text) VALUES (?, ?, ?)',
guildId, triggerWord.toLowerCase(), responseText);
// List triggers (public)
DB.all('SELECT * FROM auto_responses WHERE guild_id = ? ORDER BY trigger_word ASC', guildId);
// Remove trigger (admin only)
DB.run('DELETE FROM auto_responses WHERE guild_id = ? AND trigger_word = ?', guildId, triggerWord);
```
### 6. Twitch Monitoring with Stream ID
```typescript
// Stream ID tracking prevents duplicate notifications
const isNewStream = monitor.last_stream_id !== stream.id;
const wasOffline = monitor.last_status === 'offline';
if (wasOffline || (monitor.last_status === 'online' && isNewStream)) {
// Send notification
DB.run('UPDATE twitch_monitors SET last_status = "online", last_stream_id = ? WHERE id = ?',
stream.id, monitor.id);
}
```
### 7. Configurable Warn System
**Database Schema:**
- `warn_threshold`: Number of warns before action
- `warn_action`: 'none', 'kick', 'mute', 'ban'
- `warn_mute_duration`: Duration in seconds for mute
**Auto-Action Logic:**
```typescript
const warnCount = countResult?.count ?? 0;
const warnThreshold = settings?.warn_threshold ?? 3;
const warnAction = settings?.warn_action ?? 'ban';
if (warnCount >= warnThreshold) {
if (warnAction === 'kick') await target.kick(...);
else if (warnAction === 'mute') await target.timeout(duration, ...);
else if (warnAction === 'ban') await target.ban(...);
}
```
### 8. Mute with Multiple Time Units
```typescript
const unitSeconds: Record<string, number> = {
sec: 1,
min: 60,
hour: 60 * 60,
day: 60 * 60 * 24,
week: 60 * 60 * 24 * 7,
month: 60 * 60 * 24 * 30,
year: 60 * 60 * 24 * 365
};
if (unit === 'perm') {
durationMs = null; // Permanent
} else {
durationMs = duration * unitSeconds[unit!] * 1000;
}
```
## 🚀 Build & Deploy
```bash
# Build
npm run build
# Deploy Commands
npm run deploy
# Run
npm start
# Docker
docker-compose up -d --build
```
## ⚠️ Critical Constraints
1. **Environment Variables:** Always use `process.env`, never hardcode.
2. **Type Safety:** Use `any` for command `data` and `execute` parameters (compatibility).
3. **Interaction States:** Check `interaction.replied` before replying.
4. **Twitch API:** Max 100 channels per request, batch properly.
5. **Deploy:** Commands NOT auto-registered on start use `/owner deploy`.
6. **Ephemeral Replies:** Use `flags: [MessageFlags.Ephemeral]` instead of deprecated `ephemeral: true`.
## 📋 Command Summary
| Command | Subcommands | Permission |
|---------|-------------|------------|
| `/ping` | | Public |
| `/help` | | Public |
| `/twitch` | online, add, remove, list, help | Public/Admin |
| `/trigger` | add, remove, list, help | Public/Admin |
| `/timer` | add, remove, list, listall, help | Public/Admin |
| `/admin` | kick, warn, unwarn, mute, unmute, ban, unban, config, help | Admin |
| `/owner` | deploy, stats, servers, help | Owner |