From 676fc45bb1ccf195bfb61644c7dd4ebf2ce22f3f Mon Sep 17 00:00:00 2001 From: D4rkst3r Date: Thu, 30 Jul 2026 22:43:15 +0200 Subject: [PATCH] Willkommens-Karten, Geburtstage, Devlog-Permalinks mit OG-Vorschau, Auto-Publish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Willkommens-Karten: gerendertes PNG (SVG → sharp) mit Avatar im Neon-Ring, Willkommens-Schriftzug und Member-Nummer; Fallback aufs bisherige Embed wenn das Rendering scheitert; Dockerfile installiert fonts-dejavu-core für die Text-Darstellung - Geburtstags-System: /geburtstag setzen|entfernen (birthdays-Tabelle), tägliche Runde ab 09:00 Europe/Berlin (Doppel-Post-Schutz über last_birthday_run), Gratulations-Embed + Tages-Rolle (wird am nächsten Morgen wieder abgeräumt); Kanal + Rolle im Community-Tab - Devlog-Permalinks: /devlogs/:id als eigene Seite (GET /api/devlogs/:id), Link-Symbol an jeder Karte, Link-kopieren-Button; der Server injiziert Open-Graph-Tags ins SPA-HTML — Devlog-Links zeigen Titel, Anriss und Bild, alle anderen Seiten bekommen Default-Tags (auch die Startseite) - Auto-Publish: maybeCrosspost() veröffentlicht Devlog-, Release-, Composer- und geplante Posts automatisch in Ankündigungs-Kanälen - brandEmbed: leere Avatar-URL crasht nicht mehr die Footer-Validierung Co-Authored-By: Claude Fable 5 --- Dockerfile | 4 + README.md | 7 +- frontend/src/App.jsx | 2 + frontend/src/pages/DevlogDetail.jsx | 94 +++++ frontend/src/pages/Devlogs.jsx | 3 +- frontend/src/pages/Settings.jsx | 18 + frontend/src/style.css | 7 + package-lock.json | 610 +++++++++++++++++++++++++++- package.json | 3 +- src/bot/birthdays.js | 69 ++++ src/bot/client.js | 3 +- src/bot/commands/geburtstag.js | 43 ++ src/bot/mod-tools.js | 21 +- src/bot/release-feed.js | 4 +- src/bot/welcome-card.js | 70 ++++ src/db.js | 24 ++ src/embeds.js | 12 +- src/index.js | 2 + src/runtime-settings.js | 10 + src/web/api.js | 21 +- src/web/devlog-endpoint.js | 2 + src/web/scheduled-posts.js | 4 +- src/web/server.js | 68 +++- 23 files changed, 1080 insertions(+), 21 deletions(-) create mode 100644 frontend/src/pages/DevlogDetail.jsx create mode 100644 src/bot/birthdays.js create mode 100644 src/bot/commands/geburtstag.js create mode 100644 src/bot/welcome-card.js diff --git a/Dockerfile b/Dockerfile index 2aee55c..466dc49 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,10 @@ RUN npm run build # Stage 2: Runtime (slim statt alpine: better-sqlite3 liefert glibc-Prebuilds) FROM node:22-slim +# Fonts für die Willkommens-Karten (sharp/librsvg braucht installierte Schriften) +RUN apt-get update && apt-get install -y --no-install-recommends fonts-dejavu-core \ + && rm -rf /var/lib/apt/lists/* + WORKDIR /app # Erst nur Manifest kopieren → Docker-Layer-Cache für npm ci diff --git a/README.md b/README.md index 64e0431..7529c2e 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,10 @@ SQLite (better-sqlite3, FTS5), Docker Multi-Stage — deploybar als Portainer-St | 📣 **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 mit Control-Panel (Umbenennen, Sperren, Limit, Löschen — nur für den Besitzer), 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) | +| 👋 **Willkommens-Karten** | Begrüßung neuer Member als gerendertes Bild (Avatar mit Neon-Ring, Member-Nummer, Brand-Look); Fallback aufs Text-Embed | +| 🎂 **Geburtstage** | `/geburtstag` zum Eintragen; morgens ab 09:00 Gratulation im Kanal + Tages-Rolle (Community-Tab) | +| 📢 **Auto-Publish** | Devlog-/Release-/Composer-Posts in Ankündigungs-Kanälen werden automatisch veröffentlicht (Follower bekommen sie) | +| 🔗 **Link-Vorschau** | Jedes Devlog hat einen Permalink (`/devlogs/:id`); geteilte Links zeigen überall gebrandete Open-Graph-Vorschau mit Bild | | 📋 **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 mit %, Map, Spieler-Liste, klickbarer Connect-Link, Ping); **300+ Spiele via gamedig** (Minecraft, Rust, CS2, Valheim, ARK …) + FiveM + HTTP-Check; Down/Up-Alerts (🚨 nach 2 Fehlversuchen, ✅ mit Downtime bei Recovery) in eigenen Alert-Kanal; Spielerzahl in der Presence | | 👥 **Team-Rechte** | Team-Mitglieder bekommen gezielten Web-Zugriff (Composer, Bewerbungen, Rollen, Server, …) — Brand, System, API-Keys bleiben Owner-only; **Audit-Log** im Team-Tab zeigt dem Owner die letzten Aktionen | @@ -55,7 +58,7 @@ SQLite (better-sqlite3, FTS5), Docker Multi-Stage — deploybar als Portainer-St ### Webinterface (`bot.d4rkst3r.de`) | Seite | Zugriff | Inhalt | |---|---|---| -| `/devlogs` | öffentlich | Devlog-Archiv: Timeline, Bildergalerien, Projekt-Chips, **Volltextsuche** | +| `/devlogs` | öffentlich | Devlog-Archiv: Timeline, Bildergalerien, Projekt-Chips, **Volltextsuche**; `/devlogs/:id` als teilbarer Permalink | | `/roadmap` | öffentlich | Meilensteine aus Gitea mit Fortschrittsbalken | | `/galerie` | öffentlich | Community-Screenshots aus dem Discord | | `/level` | öffentlich | XP-Bestenliste + Server-Aktivitäts-Chart | diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 9e17c64..1825d30 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -12,6 +12,7 @@ import Settings from './pages/Settings.jsx'; import Profil from './pages/Profil.jsx'; import Server from './pages/Server.jsx'; import Home from './pages/Home.jsx'; +import DevlogDetail from './pages/DevlogDetail.jsx'; const COMMUNITY_LINKS = [ ['/galerie', 'Galerie'], @@ -187,6 +188,7 @@ export default function App() { } /> } /> + } /> } /> } /> } /> diff --git a/frontend/src/pages/DevlogDetail.jsx b/frontend/src/pages/DevlogDetail.jsx new file mode 100644 index 0000000..cda6dfd --- /dev/null +++ b/frontend/src/pages/DevlogDetail.jsx @@ -0,0 +1,94 @@ +// Permalink-Seite für ein einzelnes Devlog (/devlogs/:id) — teilbar mit +// Open-Graph-Vorschau (injiziert der Server ins HTML). +import { useEffect, useState } from 'react'; +import { Link, useParams } from 'react-router-dom'; +import { apiGet } from '../api.js'; +import { Markdown } from '../markdown.jsx'; +import Lightbox from '../components/Lightbox.jsx'; +import { SkeletonCards } from '../components/Skeleton.jsx'; +import { splitContent } from './Devlogs.jsx'; + +const dateFmt = new Intl.DateTimeFormat('de-DE', { + weekday: 'long', day: '2-digit', month: 'long', year: 'numeric', +}); + +export default function DevlogDetail() { + const { id } = useParams(); + const [item, setItem] = useState(null); + const [error, setError] = useState(false); + const [lightbox, setLightbox] = useState(null); + const [copied, setCopied] = useState(false); + + useEffect(() => { + setItem(null); + setError(false); + apiGet(`/api/devlogs/${id}`).then((d) => setItem(d.item)).catch(() => setError(true)); + window.scrollTo(0, 0); + }, [id]); + + async function copyLink() { + try { + await navigator.clipboard.writeText(window.location.href); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + } catch { /* Browser ohne Clipboard-Rechte */ } + } + + const parts = item ? splitContent(item.content) : null; + + return ( + <> +
+
DEVLOG
+
+ +
+ {error && ( +

+ Devlog nicht gefunden — zurück zum Archiv. +

+ )} + {!error && !item && } + + {item && ( + <> + + +
+ ← Alle Devlogs + +
+ + {lightbox !== null && ( + setLightbox(null)} /> + )} + + )} +
+ + ); +} diff --git a/frontend/src/pages/Devlogs.jsx b/frontend/src/pages/Devlogs.jsx index 75d3a74..cff47ee 100644 --- a/frontend/src/pages/Devlogs.jsx +++ b/frontend/src/pages/Devlogs.jsx @@ -24,7 +24,7 @@ function relative(iso) { * - Embed-Titel („Devlog — Projekt") → Projekt-Chip statt Fließtext * - Abschließende Kursiv-Zeile („*X Commits heute …*") → abgesetzte Fußzeile */ -function splitContent(raw) { +export function splitContent(raw) { const blocks = raw.split(/\n{2,}/); let project = null; let footer = null; @@ -135,6 +135,7 @@ export default function Devlogs({ me }) { {project && {project}} {relative(d.posted_at)} + 🔗 {me?.admin && (