- FTS5-Volltextindex mit Trigger-Sync und einmaligem Backfill bestehender Devlogs - /api/devlogs?q= — Präfix-Suche, injection-sicher (Phrasen-Quoting) - Suchfeld mit Entprellung und Treffer-Zähler auf der Devlog-Seite - /feed.xml — RSS 2.0 der letzten 20 Devlogs (escaped, deutsche Titel) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
EcoBot
Discord-Bot + Webinterface für die EcoGame-Community.
Features (Roadmap):
- ✅ Bot online +
/pingSlash-Command - ✅ Commit-Feed: Gitea-Push-Webhooks → Discord-Embeds + SQLite-Archiv
- ✅ Devlog-Archiv: Devlog-Kanal live archivieren +
/devlog-backfillfür die Historie - ✅ Webinterface: Devlog-Archiv (öffentlich) + Commit-Feed (Discord-Login, nur Admin)
Stack: Node.js 20+, discord.js v14, Fastify, React + Vite, SQLite (better-sqlite3), Docker
Setup: Discord-App anlegen (einmalig)
1. App erstellen
- Öffne das Discord Developer Portal
- New Application → Name:
EcoBot→ Create - Unter General Information die Application ID kopieren
→ das ist
DISCORD_CLIENT_ID
2. Bot-Token holen
- Linke Seitenleiste → Bot
- Reset Token → Token kopieren → das ist
DISCORD_TOKEN⚠️ Der Token wird nur einmal angezeigt — direkt in die.enveintragen! - Auf der Bot-Seite weiter unten: Message Content Intent aktivieren (brauchen wir ab Feature 3 zum Lesen der Devlog-Nachrichten)
3. Bot auf den Server einladen
- Linke Seitenleiste → OAuth2 → URL Generator
- Scopes ankreuzen:
botundapplications.commands - Bot Permissions ankreuzen:
- Send Messages
- Embed Links
- Read Message History
- Generierte URL unten kopieren, im Browser öffnen, deinen Server auswählen → Autorisieren
4. Server-ID holen (für sofortige Slash-Commands)
- In Discord: Einstellungen → Erweitert → Entwicklermodus aktivieren
- Rechtsklick auf deinen Server → Server-ID kopieren
→ das ist
DISCORD_GUILD_ID
5. .env anlegen
cp .env.example .env
Dann die Werte eintragen. Die .env ist in .gitignore und landet nie im Repo.
Setup: Commit-Feed (Gitea → Discord)
1. Neue .env-Werte
| Variable | Woher |
|---|---|
COMMIT_CHANNEL_ID |
Rechtsklick auf den Ziel-Kanal in Discord → Kanal-ID kopieren |
GITEA_WEBHOOK_SECRET |
Generieren: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" |
2. Öffentlicher Host im Nginx Proxy Manager (bot.d4rkst3r.de)
Cloudflare (DNS):
- Cloudflare-Dashboard → Zone
d4rkst3r.de→ DNS - Neuen Record anlegen: Typ CNAME, Name
bot, Ziel wie bei den bestehenden Hosts (z. B.d4rkst3r.de), Proxy-Status Proxied (orange Wolke)
Nginx Proxy Manager:
- NPM öffnen → Hosts → Proxy Hosts → Add Proxy Host
- Tab Details:
- Domain Names:
bot.d4rkst3r.de - Scheme:
http - Forward Hostname / IP:
host.docker.internal(NPM läuft im Container, der Bot published Port 3080 auf dem Windows-Host) - Forward Port:
3080 - ✅ Block Common Exploits
- Domain Names:
- Tab SSL:
- SSL Certificate: Request a new SSL Certificate (Let's Encrypt) — oder das vorhandene Wildcard-Zertifikat auswählen
- ✅ Force SSL
- Save
Test: https://bot.d4rkst3r.de/health im Browser → {"status":"ok"}
3. Webhook in Gitea eintragen
- Gitea → EcoGame-Repo → Einstellungen → Webhooks → Webhook hinzufügen → Gitea
- Ziel-URL:
https://bot.d4rkst3r.de/webhooks/gitea - HTTP-Methode:
POST, POST Content Type:application/json - Geheimnis: exakt der Wert aus
GITEA_WEBHOOK_SECRET - Trigger: Push-Events, Branch-Filter leer (= alle)
- Speichern → auf den Webhook klicken → Testzustellung senden → im Discord-Kanal erscheint ein Embed 🎉
Den alten direkten Discord-Webhook im Repo danach löschen, sonst gibt's Doppel-Posts.
Bereits gepostete Commits werden per SHA dedupliziert — ein erneuter Push derselben Commits erzeugt keine doppelten DB-Einträge.
Setup: Devlog (Bot postet + archiviert)
Der Bot ist das Tagebuch: tools/devlog.py (EcoGame-Repo) schickt sein Devlog an
den Bot-Endpoint (Discord-Webhook-kompatibel), der Bot postet ein gebrandetes
Embed (Gelb, „D4RKST3R // DEVLOG"-Footer, bis zu 4 Bilder als Grid) in den
Devlog-Kanal und archiviert Text + Bilder in SQLite.
.env/ Portainer-Stack:DEVLOG_CHANNEL_ID= Kanal-ID des Devlog-KanalsDEVLOG_POST_SECRET= generieren:node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
- Im EcoGame-Repo in
tools/.devlog_webhookstatt der Discord-URL eintragen:https://bot.d4rkst3r.de/webhooks/devlog/<DEVLOG_POST_SECRET>devlog.pyselbst bleibt unverändert — der Endpoint versteht das Discord-Webhook-Format (JSON + Multipart mit Bildern). - Den alten Discord-Webhook im Devlog-Kanal löschen (wird nicht mehr gebraucht)
Zusätzlich liest der Bot den Kanal live mit und archiviert auch fremde
Webhook-/Bot-Posts (dafür: Message Content Intent im Developer Portal aktiv).
/devlog-backfill (Admin) archiviert die Kanal-Historie nach.
Dedupe läuft über die Discord-Message-ID — alles beliebig wiederholbar.
Bilder werden lokal gespeichert (data/devlog_images/), weil Discord-CDN-Links ablaufen.
Setup-Seite im Webinterface (Admin)
Unter /settings (Nav: „Setup", nur als Admin sichtbar) lassen sich zur Laufzeit ändern — gespeichert in SQLite, Env-Variablen sind nur noch der Fallback, Änderungen greifen sofort ohne Redeploy:
- Devlog-Kanal und Commit-Kanal als Dropdown (alle Textkanäle, die der Bot sieht) — mit Test senden-Button pro Kanal
- Commit-Feed an/aus (aus = Commits werden weiter archiviert, nur nicht gepostet)
- Branch-Filter (kommagetrennt, leer = alle) — gefilterte Branches werden archiviert, aber nicht gepostet
- Ignorierte Repos (kommagetrennt, z. B.
D4rkst3r/ecobot) — komplett übersprungen - Status-Panel: Bot-Account, Uptime, Anzahl Devlogs/Commits, DB-Größe
COMMIT_CHANNEL_ID und DEVLOG_CHANNEL_ID sind damit optional geworden.
Setup: Webinterface
Zugriffsmodell: Devlog-Archiv ist öffentlich (wie der Discord-Kanal), die Commit-Seite erfordert Discord-Login und ist auf deine Discord-ID beschränkt.
1. OAuth2 im Developer Portal konfigurieren
- Developer Portal → deine App → OAuth2
- Client Secret kopieren (ggf. „Reset Secret") →
DISCORD_CLIENT_SECRET - Unter Redirects BEIDE URLs eintragen:
https://bot.d4rkst3r.de/auth/callback(Produktion)http://localhost:3080/auth/callback(lokale Entwicklung)
2. Neue .env-Werte
| Variable | Woher |
|---|---|
DISCORD_CLIENT_SECRET |
Developer Portal → OAuth2 (Schritt 1) |
SESSION_SECRET |
Generieren: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" |
ADMIN_DISCORD_ID |
Discord: Rechtsklick auf deinen Namen → Benutzer-ID kopieren |
PUBLIC_URL |
Produktion: https://bot.d4rkst3r.de · lokal: http://localhost:3080 |
3. Frontend lokal entwickeln
cd frontend
npm install
npm run dev # Vite-Dev-Server auf :5173, proxied /api + /auth zum Bot auf :3080
Für den „Produktions-Look" lokal: npm run build im frontend/-Ordner —
der Bot liefert frontend/dist dann selbst unter http://localhost:3080 aus.
Im Docker-Image wird das Frontend automatisch mitgebaut (Multi-Stage).
Lokal starten (Entwicklung)
npm install
npm run dev
Erwartete Ausgabe:
[bot] Eingeloggt als EcoBot#1234
[bot] 1 Slash-Command(s) registriert (Guild)
Dann in Discord: /ping → Bot antwortet mit Latenz. 🎉
Commit-Feed lokal testen (ohne Gitea — simuliert eine signierte Testzustellung):
node tools/test-webhook.mjs
→ Embed erscheint im Commit-Kanal, Commit landet in der lokalen SQLite (data/ecobot.db).
Deployment (Docker / Portainer)
Variante A: Portainer-Stack aus Git (empfohlen)
- Portainer → Stacks → Add stack → Name:
ecobot - Build method: Repository
- Repository URL:
https://git.d4rkst3r.de/D4rkst3r/ecobot - Repository reference:
refs/heads/main - Compose path:
docker-compose.yml - Falls das Repo privat ist: Authentication aktivieren —
Username:
D4rkst3r, Password: ein Gitea-Token (Gitea → Einstellungen → Anwendungen → Token erzeugen, Scoperead:repository)
- Repository URL:
- Unter Environment variables → Advanced mode die Werte aus der lokalen
.enveintragen (DISCORD_TOKEN,DISCORD_CLIENT_ID,DISCORD_GUILD_ID,COMMIT_CHANNEL_ID,GITEA_WEBHOOK_SECRET) - Deploy the stack
Updates später: Stack öffnen → Pull and redeploy (holt den neuesten Stand von main).
Variante B: Manuell auf dem Server
git clone git@gitea:D4rkst3r/ecobot.git
cd ecobot
cp .env.example .env # Werte eintragen
docker compose up -d --build
Logs prüfen:
docker logs -f ecobot
Projektstruktur
ecobot/
├── src/
│ ├── index.js # Einstiegspunkt (Bot + Webserver)
│ ├── config.js # Env-Konfiguration mit Validierung
│ ├── db.js # SQLite (better-sqlite3), Schema + Queries
│ ├── bot/
│ │ ├── client.js # Discord-Client, Command-Registry, Interactions, Devlog-Listener
│ │ ├── commit-feed.js # Push → Discord-Embed
│ │ ├── devlog-archive.js # Webhook-Nachricht → SQLite
│ │ └── commands/
│ │ ├── ping.js # /ping — Lebenszeichen
│ │ └── devlog-backfill.js # /devlog-backfill — Historie archivieren (Admin)
│ └── web/
│ ├── server.js # Fastify: Webhook, Static-Serving, SPA-Fallback
│ ├── auth.js # Discord-OAuth2-Flow + Session-Cookies
│ └── api.js # REST-API: /api/me, /api/devlogs, /api/commits
├── frontend/ # React + Vite (Devlogs öffentlich, Commits admin-only)
│ └── src/
│ ├── App.jsx # Layout, Router, Login-Status
│ ├── markdown.jsx # Mini-Markdown-Renderer für Devlog-Prosa
│ └── pages/ # Devlogs.jsx, Commits.jsx
├── .env.example # Vorlage für Secrets (nach .env kopieren)
├── Dockerfile # Multi-Stage: Frontend-Build + Runtime
├── docker-compose.yml
└── README.md
Neue Slash-Commands: Datei in src/bot/commands/ anlegen (exportiert data + execute)
und in src/bot/client.js bei commandModules eintragen.