- Game-Server: eigene Tabelle statt CSV-Setting (automatische Migration), eigener Setup-Tab mit Server-Builder (Name, Typ fivem/http, Query-URL, Anzeige-Adresse); pro Server ein Live-Embed (gruen/rot, Spieler-Balken, Map, Adresse als Code-Block), edit in place; Embed wird beim Server-Loeschen mit entfernt - Team-System: web_admins mit Bereichs-Scopes (content/community/rollen/bewerbungen/ server/settings/devlogs); Owner behaelt Brand/System/API-Keys/Team exklusiv; 32 Routen-Guards auf Scopes umgestellt, sensible PUT-Felder fuer Team gestrippt; Team-Tab (Owner) + Tab-Filterung im Frontend, /api/me liefert Scopes - Bot-Karte: Profil-Beschreibung (Ueber mich) via application.edit - Fix: settings-Tabelle wird vor der Gameserver-Migration angelegt (frische DBs crashten sonst beim Start) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
18 KiB
d4rkbot — D4RKST3R // COMMUNITY BOT
Discord-Bot + Webinterface: Devlog-Tagebuch, Commit-Feed, Changelog und Community-Tools für die EcoGame-Entwicklung — alles gebrandet, alles über die Setup-Seite steuerbar.
Stack: Node.js 20+ (ESM), discord.js v14, Fastify 5, React 19 + Vite, SQLite (better-sqlite3, FTS5), Docker Multi-Stage — deploybar als Portainer-Stack.
Features
Discord
| Feature | Beschreibung |
|---|---|
| 📔 Devlog-Posts | tools/devlog.py (EcoGame-Repo) schickt Prosa + Bilder an den Bot-Endpoint → gebrandetes Embed (Datum-Titel, Author-Zeile, Commit-Zähler im Footer, Bilder-Grid, Link-Buttons) |
| 💬 Auto-Threads | Diskussions-Thread unter jedem Devlog-Post („💬 Devlog 23.07.") |
| 🔔 Rollen-Ping | Ping-Rolle beim Devlog-Post; Member abonnieren sie selbst über den 🔔-Button |
| 📦 Commit-Feed | Gitea-Push-Webhooks → Embeds in den (privaten) Commit-Kanal |
| 🚀 Release-Ankündigungen | Neues Gitea-Release → oranges Ankündigungs-Embed |
| 📊 Wochen-Rückblick | Sonntags 20:00 automatisch: Commits pro Tag als Balken, Devlogs, Top-Projekte |
🐛 /bug |
Member melden Bugs → Gitea-Issue (inkl. Screenshot-Upload); Issue geschlossen → DM an den Reporter |
| 🚨 Watchdog | Prüft eigene Dienste alle 2 min, DM-Alarm bei Ausfall + Entwarnung |
| 🧪 Playtester-Programm | /playtester-setup postet den Bewerbungs-Button; Rolle + Liste (Setup-Seite); Alpha-Keys später per DM über die API |
| ⭐ Starboard | Nachrichten mit genug ⭐-Reaktionen landen automatisch im Best-of-Kanal |
| 📸 Screenshot-Galerie | Bilder aus dem Screenshot-Kanal → öffentliche /galerie (lokal gespeichert); Historie via /galerie-backfill |
| 📬 Modmail | DM an den Bot → Thread im privaten Staff-Kanal; Antworten im Thread gehen als DM zurück |
| 🎫 Tickets | /ticket-setup postet den Button; Klick öffnet einen privaten Thread (ein offenes Ticket pro User). Schließen = Transcript per DM an den Ersteller + Kopie ins Mod-Log, Thread wird gelöscht |
| 🎭 Rollen-Menüs | Carl-Bot-Ersatz: Menüs im Webinterface bauen (Emoji, Label, Rolle, optional exklusiv), Bot postet Button-Embeds; Klick = Rolle nehmen/abgeben, Posts jederzeit editierbar |
| 🛡️ Moderation | /warn (DM + Historie), /warns, /timeout, /purge — alles im Mod-Log; dazu Join/Leave- und Nick-/Rollen-Änderungs-Logging |
| 🪪 Auto- & Sticky-Roles | Start-Rolle für neue Member; Rollen werden bei Leave gesichert und bei Rejoin wiederhergestellt |
| 📈 Level-System | XP pro Nachricht (60s-Cooldown, MEE6-Formel), /rank, Level-Up-Announce, Rollen-Belohnungen, öffentliche Bestenliste auf /level |
| 🏷️ Tags | /tag <name> (mit Autocomplete) postet gespeicherte Text-Bausteine — verwaltet im Composer-Tab |
| ⏰ Geplante Posts | Einmalig, täglich oder wöchentlich — im Composer-Tab geplant, vom Bot gepostet (bei Carl Premium) |
| 🎨 Branding | Brand-Tab mit Bot-Identity-Karte (Avatar-Vorschau, Name, Status online/idle/dnd, Aktivität), Embed-Farben, Banner-Upload, Guild-ID & Gitea-Token ohne Env-Redeploy |
| 🎟️ Alpha-Keys | Key-Pool im Community-Tab; Ein-Klick-Verteilung an alle Playtester per DM |
| 📋 Bewerbungen | Formulare (bis 5 Fragen) als Discord-Modal, Review mit ✅/❌ im Staff-Kanal, Rolle + DM bei Annahme — z. B. FiveM-Whitelist |
| 📅 Events | Neue Discord-Events werden angekündigt + öffentliche /events-Seite |
| 💬 Triggers | Auto-Antworten auf Schlüsselwörter (30s-Cooldown), verwaltet im Composer-Tab |
| ⏰ /remind | Erinnerungen per DM (/remind dauer:2h text:…) |
| 📣 Twitch/YouTube | 🔴-Live- und ▶️-Neues-Video-Announcements (YouTube ohne Key via RSS, Twitch mit App-Credentials) |
| 🔊 Temp-Voice | „Join to Create": Hub-Kanal betreten → eigener Voice-Kanal, löscht sich wenn leer |
| 📊 Server-Stats | Nachrichten/Joins/Leaves pro Tag → Aktivitäts-Chart auf der Level-Seite |
| 👋 Willkommens-Embed | Begrüßung neuer Member im Brand-Look (braucht Server-Members-Intent) |
| 📋 Mod-Log | Gelöschte/bearbeitete Nachrichten in einen privaten Log-Kanal |
| 🎮 Server-Monitor | DiscordGSM-Stil: eigener Setup-Tab, pro Server ein Live-Embed (🟢/🔴, Spieler-Balken, Map, Copy-Paste-Adresse); Typen FiveM + HTTP-Check; Spielerzahl in der Presence |
| 👥 Team-Rechte | Team-Mitglieder bekommen gezielten Web-Zugriff (Composer, Bewerbungen, Rollen, Server, …) — Brand, System, API-Keys bleiben Owner-only |
| 💡 Feature-Voting | /wunsch → Voting-Post mit 👍; Top-Wünsche öffentlich auf der Roadmap-Seite |
| 🎉 Giveaways | /giveaway (Admin): Teilnahme-Button, automatische Ziehung nach Ablauf |
| 📈 Contribution-Heatmap | GitHub-Style-Jahreskalender aus dem Commit-Archiv auf der Roadmap-Seite |
🏓 /ping, 🗄 /devlog-backfill |
Lebenszeichen · Kanal-Historie nacharchivieren (Admin) |
Webinterface (bot.d4rkst3r.de)
| Seite | Zugriff | Inhalt |
|---|---|---|
/devlogs |
öffentlich | Devlog-Archiv: Timeline, Bildergalerien, Projekt-Chips, Volltextsuche |
/roadmap |
öffentlich | Meilensteine aus Gitea mit Fortschrittsbalken |
/galerie |
öffentlich | Community-Screenshots aus dem Discord |
/level |
öffentlich | XP-Bestenliste + Server-Aktivitäts-Chart |
/events |
öffentlich | Discord-Events (Playtests, Streams …) |
/changelog |
öffentlich | Alle Releases mit Notes, Tag- und Pre-Release-Chips |
/feed.xml |
öffentlich | RSS-Feed der Devlogs |
/commits |
nur Admin | Archivierte Commits aller Repos (SHA → Gitea-Link) |
/settings |
nur Admin | Setup-Seite — siehe unten |
Login via Discord-OAuth2 (identify-Scope, signierte Session-Cookies, keine Token-Speicherung). Design: D4RKST3R-Brand (Neon-Gelb/Orange auf Schwarz, Bebas Neue + Barlow Condensed + Share Tech Mono, selbst gehostet).
Setup-Seite (/settings)
Aufgeteilt in Tabs mit Sidebar-Navigation (Status · Brand · Feeds · Community · Rollen · Support · Bewerbungen · Composer · Server · System · API · Team) — Team-Mitglieder sehen nur die Tabs ihrer Bereiche. Alles zur Laufzeit änderbar — gespeichert in SQLite, Env-Variablen sind nur Fallback, kein Redeploy nötig:
- Allgemein: Öffentliche URL, Gitea-URL
- Kanäle (Devlog / Commit / Release) als Dropdown + „Test senden"-Button je Kanal
- Devlog: Ping-Rolle, Auto-Threads, Wochen-Rückblick (+ Sofort-Test)
- Commit-Feed: an/aus, Branch-Filter, ignorierte Repos (gefiltert wird nur das Posten — archiviert wird immer)
- Community: Playtester-Rolle (+ Liste), Starboard-Kanal + ⭐-Schwellwert, Screenshot-Kanal, Voting-Kanal
- Moderation & Kontakt: Modmail-, Willkommens-, Mod-Log-Kanal
- Game-Server: Status-Kanal + Server-Liste (
Name=URL, FiveM-kompatibel) - Bug-Reports / Roadmap: Ziel-Repos
- Backups: an/aus, Upload-Kanal, „Backup jetzt"
- Watchdog: überwachte URLs
- API-Keys: erstellen (Name + Scopes), widerrufen, last-used
- Status-Panel: Bot-Account, Uptime, Devlog-/Commit-Zahlen, DB-Größe, letztes Backup
Ersteinrichtung
1. Discord-App (Developer Portal)
- New Application → Name vergeben
- General Information → Application ID =
DISCORD_CLIENT_ID - Bot → Reset Token =
DISCORD_TOKEN(wird nur einmal angezeigt!) und Message Content Intent + Server Members Intent aktivieren (Devlog-Listener bzw. Willkommens-Embeds) - OAuth2 → Client Secret =
DISCORD_CLIENT_SECRET; unter Redirects eintragen:https://bot.d4rkst3r.de/auth/callbackhttp://localhost:3080/auth/callback(lokale Entwicklung)
- OAuth2 → URL Generator: Scopes
bot+applications.commands; Permissions: Send Messages, Embed Links, Attach Files, Read Message History, Create Public Threads, Manage Roles (für den 🔔-Abo-Button) → URL öffnen, Bot einladen - In Discord: Entwicklermodus an → Server-ID =
DISCORD_GUILD_ID, eigene User-ID =ADMIN_DISCORD_ID
⚠️ Kanal-Overrides: In Kanälen, in denen @everyone nicht schreiben darf,
braucht der Bot eigene Overrides (Kanal ansehen, Nachrichten senden, Links einbetten,
Dateien anhängen, Threads erstellen). Die Bot-Rolle muss über der Ping-Rolle stehen.
2. Environment-Variablen
Vorlage: .env.example — lokal als .env (gitignored),
in Portainer als Stack-Environment-Variables.
| Variable | Pflicht | Zweck |
|---|---|---|
DISCORD_TOKEN |
✅ | Bot-Token |
DISCORD_CLIENT_ID |
✅ | Application ID |
DISCORD_CLIENT_SECRET |
✅ | OAuth2-Login |
SESSION_SECRET |
✅ | Session-Cookies signieren¹ |
ADMIN_DISCORD_ID |
✅ | Deine User-ID (Admin-Zugriff + Watchdog-DMs) |
GITEA_WEBHOOK_SECRET |
✅ | HMAC-Prüfung der Gitea-Webhooks¹ |
DEVLOG_POST_SECRET |
✅ | Secret im Devlog-Endpoint-Pfad¹ |
DISCORD_GUILD_ID |
optional | Guild-Commands sofort statt global (bis 1h) |
GITEA_API_TOKEN |
optional | Für /bug → Issues (Scope write:issue); alternativ im Brand-Tab hinterlegen |
GITEA_URL |
optional | Default https://git.d4rkst3r.de |
PUBLIC_URL |
optional | Default https://bot.d4rkst3r.de |
COMMIT_CHANNEL_ID / DEVLOG_CHANNEL_ID |
optional | Fallbacks — Kanäle kommen normal von der Setup-Seite |
TZ |
optional | Default Europe/Berlin (Wochen-Rückblick, Datumsformate) |
¹ Generieren: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
3. Routing (einmalig eingerichtet)
Cloudflare: CNAME bot → Zone, Proxied. Nginx Proxy Manager: bot.d4rkst3r.de
→ host.docker.internal:3080, Block Common Exploits, Force SSL.
Test: https://bot.d4rkst3r.de/health → {"status":"ok"}
4. Gitea-Webhook (systemweit oder pro Repo)
- Typ Gitea, URL
https://bot.d4rkst3r.de/webhooks/gitea, Content Typeapplication/json, Geheimnis =GITEA_WEBHOOK_SECRET - Trigger-Events: Push, Release, Issues (Push = Commit-Feed · Release = Ankündigungen/Changelog · Issues = Bug-Rückkanal-DMs)
5. devlog.py (EcoGame-Repo)
In tools/.devlog_webhook steht die Bot-Endpoint-URL:
https://bot.d4rkst3r.de/webhooks/devlog/<DEVLOG_POST_SECRET>
Workflow: Prosa nach tools/devlog_today.txt (+ Bilder in devlog_images/, max 4)
→ python tools/devlog.py (oder Task täglich 21:00) → Bot postet + archiviert,
Prosa-Datei und Bilder werden danach gelöscht (gepostet = erledigt).
Ohne Prosa postet das Skript nichts (Schutz des öffentlichen Kanals).
Der Endpoint versteht das Discord-Webhook-Format (JSON + Multipart) —
devlog.py kennt den Bot also gar nicht.
Deployment (Portainer)
- Stacks → Add stack → Name
ecobot(Bestands-Stack behalten! Der Name steckt im Volume-Prefix) → Build method Repository- URL
https://git.d4rkst3r.de/D4rkst3r/d4rkbot, Referencerefs/heads/main, Compose pathdocker-compose.yml - Privates Repo: Authentication mit Gitea-Token (Scope
read:repository)
- URL
- Environment-Variablen eintragen (Tabelle oben)
- Deploy the stack
Updates: Stack öffnen → Pull and redeploy („Re-pull image" aus lassen — das Image wird lokal gebaut, nicht aus einer Registry gezogen).
Daten liegen im Volume ecobot_data (/app/data): SQLite (ecobot.db, WAL)
- Devlog-Bilder (
devlog_images/— lokal gespeichert, weil Discord-CDN-Links ablaufen).
Manuell statt Portainer:
git clone git@gitea:D4rkst3r/d4rkbot.git && cd d4rkbot
cp .env.example .env # Werte eintragen
docker compose up -d --build
docker logs -f d4rkbot
Lokale Entwicklung
npm install
npm run dev # Bot + API auf :3080 (Werte aus .env)
Frontend mit Hot-Reload:
cd frontend
npm install
npm run dev # Vite auf :5173, proxied /api + /auth → :3080
Alternativ npm run build im frontend/ — der Bot liefert frontend/dist
dann selbst unter :3080 aus (so läuft es auch im Container, Multi-Stage-Build).
Commit-Feed ohne Gitea testen (signierte Fake-Testzustellung):
node tools/test-webhook.mjs
API v1 — für eigene Skripte & Dienste
Der Bot ist die zentrale Discord-Brücke der Infrastruktur: devlog.py, Platform, FiveM-Server, CI-Jobs … reden alle mit einer API statt mit zig Discord-Webhooks.
Auth: API-Keys auf der Setup-Seite erstellen (Name + Scopes, Key wird einmalig angezeigt, Widerruf jederzeit). Jeder Request:
Authorization: Bearer d4rk_<key>
| Endpoint | Scope | Body / Antwort |
|---|---|---|
POST /api/v1/message |
message |
{ channel_id, content?, embed? } → postet als Bot (Embed: title, description, color, url, image, thumbnail, footer, fields) |
PATCH /api/v1/message |
message |
{ channel_id, message_id, content?, embed? } → eigenen Bot-Post bearbeiten |
POST /api/v1/dm |
dm |
{ user_id, content } → Direktnachricht |
POST /api/v1/roles |
roles |
{ user_id, role_id, action: "add"|"remove" } → Rolle vergeben (z. B. Shop-Kauf → Kunden-Rolle) |
GET /api/v1/member/:id |
read |
Member-Info: Name, Rollen, Beitritt — für Login-/Berechtigungs-Checks |
GET /api/v1/stats |
read |
Devlog-/Commit-Zahlen, Guilds, Uptime |
Beispiel (Python):
import urllib.request, json
req = urllib.request.Request(
"https://bot.d4rkst3r.de/api/v1/message",
data=json.dumps({"channel_id": "123", "embed": {"title": "Build fertig ✅", "color": 0xF5C518}}).encode(),
headers={"Content-Type": "application/json",
"Authorization": "Bearer d4rk_...",
"User-Agent": "mein-script/1.0"})
urllib.request.urlopen(req)
HTTP-Endpoints
| Route | Auth | Zweck |
|---|---|---|
GET /health |
— | Healthcheck |
POST /webhooks/gitea |
HMAC-Signatur | Push / Release / Issues von Gitea |
POST /webhooks/devlog/:secret |
Secret im Pfad | Devlog von devlog.py (JSON/Multipart) |
GET /api/devlogs?page=&q= |
— | Archiv + FTS5-Volltextsuche |
GET /api/releases · /api/roadmap · /api/gallery · /api/wishes · /api/heatmap |
— | Changelog · Milestones · Galerie · Wunsch-Ranking · Commit-Heatmap |
GET /feed.xml |
— | RSS |
GET /devlog-assets/* · /gallery-assets/* |
— | Lokal gespeicherte Bilder |
GET /api/playtesters |
Admin | Playtester-Liste |
POST/GET /api/v1/* |
API-Key (Bearer) | Externe Skripte — siehe „API v1" oben |
GET/POST/DELETE /api/apikeys |
Admin | API-Key-Verwaltung |
GET/POST/PUT/DELETE /api/rolemenus, POST /api/rolemenus/:id/publish |
Admin | Rollen-Menüs |
GET/PUT /api/settings, POST /api/settings/test/:target |
Admin | Setup-Seite |
GET /api/commits?page= · DELETE /api/devlogs/:id |
Admin | Commit-Archiv · Devlog löschen |
GET /auth/login · /auth/callback · /auth/logout |
— | Discord-OAuth2 |
Devlogs, die in Discord gelöscht werden, verschwinden automatisch auch aus dem Archiv (MessageDelete-Sync) — zusätzlich gibt es den ✕-Button für Admins auf der Webseite.
Projektstruktur
d4rkbot/
├── src/
│ ├── index.js # Start: Bot, Webserver, Wochen-Rückblick, Watchdog
│ ├── config.js # Env-Konfiguration mit Validierung
│ ├── db.js # SQLite: Schema, Migrationen, FTS5, alle Queries
│ ├── runtime-settings.js # Effektive Settings (DB vor Env)
│ ├── gitea-api.js # Gitea-REST (Issues + Assets für /bug)
│ ├── backup.js # Nächtliche DB-Backups (gzip, Rotation, Upload)
│ ├── bot/
│ │ ├── client.js # Discord-Client, Commands, Listener, Button-Handler
│ │ ├── commit-feed.js # Push → Embed
│ │ ├── release-feed.js # Release → Ankündigungs-Embed
│ │ ├── devlog-archive.js # Nachricht → SQLite (+ Bild-Download)
│ │ ├── community.js # Starboard + Screenshot-Galerie
│ │ ├── mod-tools.js # Modmail, Willkommens-Embed, Mod-Log
│ │ ├── server-monitor.js # Game-Server-Status (FiveM-kompatibel)
│ │ ├── giveaways.js # Giveaway-Ziehung (Scheduler)
│ │ ├── weekly-recap.js # Sonntags-Zusammenfassung + Scheduler
│ │ ├── watchdog.js # URL-Checks + DM-Alarm
│ │ └── commands/ # ping, devlog-backfill, bug, playtester-setup,
│ │ # galerie-backfill, wunsch, giveaway
│ └── web/
│ ├── server.js # Fastify: Webhooks, Static, SPA-Fallback
│ ├── auth.js # Discord-OAuth2 + Session-Cookies
│ ├── api.js # REST-API + Settings + RSS
│ └── devlog-endpoint.js# Devlog-Post: Embed, Bilder, Ping, Thread, Archiv
├── frontend/ # React + Vite (D4RKST3R-Design)
│ └── src/
│ ├── App.jsx # Layout, Router, Nav, Login-Status
│ ├── markdown.jsx # Mini-Markdown (fett/kursiv/Code/Links/Listen/---/##)
│ └── pages/ # Devlogs, Roadmap, Galerie, Changelog, Commits, Settings
├── tools/test-webhook.mjs # Lokaler Commit-Feed-Test
├── .env.example # Vorlage für alle Env-Variablen
├── Dockerfile # Multi-Stage: Frontend-Build + node:22-slim Runtime
└── docker-compose.yml # Portainer-ready, Volume ecobot_data, TZ
Neue Slash-Commands: Datei in src/bot/commands/ (exportiert data + execute)
und in client.js bei commandModules eintragen.
Neue Settings: Key in db.js-Settings nutzen, Zugriff über runtime-settings.js,
UI in frontend/src/pages/Settings.jsx, GET/PUT in src/web/api.js.