# 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 | | 👋 **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** | FiveM-kompatible Game-Server (/dynamic.json): Live-Status-Embed + Spielerzahl in der Bot-Presence | | 🏓 `/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 | | `/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`) Alles zur Laufzeit änderbar — gespeichert in SQLite, Env-Variablen sind nur Fallback, kein Redeploy nötig: - **Kanäle** (Devlog / Commit / Release) als Dropdown + „Test senden"-Button je Kanal - **Devlog:** Ping-Rolle, Auto-Threads an/aus, Wochen-Rückblick an/aus (+ Sofort-Test) - **Commit-Feed:** an/aus, Branch-Filter, ignorierte Repos (gefiltert wird nur das Posten — archiviert wird immer; ignorierte Repos komplett übersprungen) - **Bug-Reports:** Ziel-Repo für `/bug` - **Watchdog:** überwachte URLs - **Status-Panel:** Bot-Account, Uptime, Devlog-/Commit-Zahlen, DB-Größe --- ## Ersteinrichtung ### 1. Discord-App ([Developer Portal](https://discord.com/developers/applications)) 1. **New Application** → Name vergeben 2. **General Information** → Application ID = `DISCORD_CLIENT_ID` 3. **Bot** → Reset Token = `DISCORD_TOKEN` (wird nur einmal angezeigt!) und **Message Content Intent** + **Server Members Intent** aktivieren (Devlog-Listener bzw. Willkommens-Embeds) 4. **OAuth2** → Client Secret = `DISCORD_CLIENT_SECRET`; unter **Redirects** eintragen: - `https://bot.d4rkst3r.de/auth/callback` - `http://localhost:3080/auth/callback` (lokale Entwicklung) 5. **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 6. 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](.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`); ohne Token ist `/bug` aus | | `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 Type `application/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/ ``` 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) 1. **Stacks → Add stack** → Name `ecobot` (Bestands-Stack behalten! Der Name steckt im Volume-Prefix) → Build method **Repository** - URL `https://git.d4rkst3r.de/D4rkst3r/d4rkbot`, Reference `refs/heads/main`, Compose path `docker-compose.yml` - Privates Repo: Authentication mit Gitea-Token (Scope `read:repository`) 2. Environment-Variablen eintragen (Tabelle oben) 3. **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_ ``` | 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) | | `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): ```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?page=` | — | Changelog | | `GET /feed.xml` | — | RSS | | `GET /devlog-assets/*` | — | Lokal gespeicherte Devlog-Bilder | | `POST/GET /api/v1/*` | API-Key (Bearer) | Externe Skripte — siehe „API v1" oben | | `GET/POST/DELETE /api/apikeys` | Admin | API-Key-Verwaltung | | `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) │ ├── bot/ │ │ ├── client.js # Discord-Client, Commands, Listener (Devlog, Delete, Buttons) │ │ ├── commit-feed.js # Push → Embed │ │ ├── release-feed.js # Release → Ankündigungs-Embed │ │ ├── devlog-archive.js # Nachricht → SQLite (+ Bild-Download) │ │ ├── weekly-recap.js # Sonntags-Zusammenfassung + Scheduler │ │ ├── watchdog.js # URL-Checks + DM-Alarm │ │ └── commands/ # ping, devlog-backfill, bug │ └── 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, 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`.