Initial: Pixelpöbel Discord Bot
Some checks failed
Auto Build and Push Docker Image / build (push) Failing after 8s

- Modular Discord Bot mit TypeScript und discord.js
- Grouped Commands (/admin, /owner subcommands)
- SQLite Datenbank mit Persistenz
- Twitch Monitoring mit Stream-ID Tracking
- Konfigurierbares Warn-System (Auto-Kick/Mute/Ban)
- Mute mit verschiedenen Zeiteinheiten (sec, min, hour, day, week, month, year, perm)
- Auto-Deploy beim Start (Option 3)
- Docker Support

Features:
- /ping, /help, /twitch (online, add, remove, list)
- /admin (kick, warn, unwarn, mute, unmute, ban, unban, config)
- /owner (deploy, stats, servers)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-22 16:29:30 +01:00
commit 40be1e181c
26 changed files with 3042 additions and 0 deletions

293
AGENTS-BOT.md Normal file
View File

@@ -0,0 +1,293 @@
# 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. **Grouped Commands:** Admin/Owner 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
│ │ ├── admin.ts # All admin subcommands
│ │ └── owner.ts # All owner subcommands
│ ├── events/
│ │ ├── ready.ts
│ │ └── interactionCreate.ts
│ └── structures/
│ ├── ExtendedClient.ts
│ ├── Command.ts
│ ├── Database.ts
│ ├── TwitchManager.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)
);
```
### 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;
}
```
**Deployer - Auto-Deploy (Option 3):**
```typescript
export class Deployer {
static async deployIfMissing(clientId: string, token: string) {
const rest = new REST().setToken(token);
try {
const data = await rest.get(Routes.applicationCommands(clientId));
if (data.length > 0) {
console.log('[DEPLOYER] Commands already registered. Skipping deployment.');
return data.length;
}
} catch (error) {
// Commands don't exist yet
}
return this.deploy(clientId, token);
}
}
```
### 4. Command Examples
**Grouped Command (admin):**
```typescript
data: new SlashCommandBuilder()
.setName('admin')
.setDescription('Admin-Commands')
.addSubcommand(sub => sub.setName('kick').setDescription('Kickt einen Nutzer')
.addUserOption(opt => opt.setName('target').setDescription('Nutzer').setRequired(true))
.addStringOption(opt => opt.setName('reason').setDescription('Grund')))
.addSubcommand(sub => sub.setName('warn').setDescription('Verwarnt einen Nutzer')
.addUserOption(opt => opt.setName('target').setDescription('Nutzer').setRequired(true))
.addStringOption(opt => opt.setName('reason').setDescription('Grund').setRequired(true)))
// ... more subcommands
```
### 5. 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);
}
```
### 6. 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(...);
}
```
### 7. 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`.
## 📋 Command Summary
| Command | Subcommands | Permission |
|---------|-------------|------------|
| `/ping` | | Public |
| `/help` | | Public |
| `/twitch` | online, add, remove, list | Public/Admin |
| `/admin` | kick, warn, unwarn, mute, unmute, ban, unban, config | Admin |
| `/owner` | deploy, stats, servers | Owner |