Der ganze Unterschied ist einer: ein Sender endet nie, ein Titel schon.
Also verzweigt genau eine Abspielfunktion und genau ein Idle-Melder nach
der Art der Quelle, statt dass ein zweiter Pfad daneben entsteht -- zwei
Pfade, die dasselbe tun sollen, driften auseinander.
Beim Sender heisst Idle "die Leitung ist weg" (neu aufbauen), bei YouTube
"fertig, der naechste bitte". Danach: Warteschlange, sonst der Sender von
vorher, sonst raus. Das Radio ist die Grundstellung, YouTube die
Unterbrechung.
Bremse gegen das Durchrutschen: ein Eintrag ohne Ton geht sofort wieder
auf Idle. Ohne Zaehlung waere eine Liste mit einem kaputten Eintrag in
Sekundenbruchteilen leer, und niemand haette gesehen warum. Nach drei
Fehlschlaegen in Folge wird angehalten und der Grund gemeldet. Von Hand
Uebersprungenes zaehlt nicht mit.
Zwei Fehler nebenbei, beide vorher schon da:
Der Idle-Melder hielt `kanal` aus dem Abschluss des ERSTEN Aufrufs fest.
Nach einem Kanalwechsel haette der Wiederanlauf in den alten Kanal
gespielt. Er liest den Kanal jetzt frisch aus `laufend`.
`radioLautstaerke()` gab bei unbelegter Einstellung 10 zurueck statt der
50, die der Kommentar zwei Zeilen darueber als Vorgabe begruendet:
`Number(null)` ist 0 und damit endlich, die Pruefung auf "nicht endlich"
hat den unbelegten Fall nie erwischt und auf die Untergrenze geklemmt.
Eine frische Installation startete also sehr leise. Gemessen gegen eine
leere Datenbank, mit Gegenproben fuer leeren String, Unsinn, 0 und 999.
Das Fundament, noch ohne Anbindung ans Radio.
yt-dlp holt selbst und schiebt rohe Bytes durch ein Rohr an ffmpeg. Der
bequemere Weg waere `-g` und die fertige Adresse -- der ist eine Falle:
googlevideo-Adressen laufen ab und haengen an der IP, die sie geholt hat.
Das schlaegt nach zehn Minuten zu und sieht dann aus wie ein Netzproblem.
Beim Stroemen nach stdout ist die Vorgabe von yt-dlp *mit* Bild; ohne ein
ausdrueckliches `-f bestaudio/best` laedt ein Tonstrom das ganze Video.
Nachgelesen im README der Fassung 2026.08.19.
stderr wird aufgehoben statt weggeworfen. Genau hier entsteht sonst der
Fehler, den niemand deuten kann: YouTube antwortet "Sign in to confirm",
yt-dlp bricht ab, der Bot sitzt still im Kanal. Die letzte ERROR-Zeile
geht ins Panel.
Gemessen am 28.08.2026 gegen echtes YouTube (yt-dlp 2026.08.19, von hier
aus, NICHT auf dem Zielhost): Einzelvideo, Suche und flache Playlist
liefern die erwarteten Felder, eine kaputte Video-ID meldet "Video
unavailable" und wird als Fehler durchgereicht statt als leere Liste.
Die Warteschlange steht in der Datenbank, nicht nur im Speicher -- ein
Deploy soll die Musik unterbrechen, nicht die Wuensche von fuenf Leuten
wegwerfen. Die YouTube-Tafel bekommt eine eigene Tabelle, weil
`radio_state` beim Stoppen geloescht wird und die Tafel das ueberleben
soll.
Nebenbei: der ffmpeg-Kommentar im Dockerfile beschrieb noch die alte
Ogg/Opus-Kette. Seit der Lautstaerkeregelung laeuft dort PCM.
Entscheidung des Betreibers am 19.08.2026: das d4rk_gameserver-Panel
baut und stellt seine Status-Embeds ab sofort selbst in den Discord.
Damit hat diese Anbindung keinen Zweck mehr.
WARUM SIE UEBERHAUPT WEG SOLL, und nicht bloss ungenutzt liegenbleibt:
sie war die zweite Meinung ueber denselben Zustand. `zustandText()` gab
`null` zurueck, wenn ein Server LIEF UND BEREIT war -- also im
Normalfall -- und der Monitor fiel dann auf seine gamedig-Abfrage
zurueck. Erreichte die den Server nicht, stand im Kanal "Offline",
waehrend dasselbe Embed RAM und CPU aus dem Panel anzeigte. Ein
Widerspruch, den man von keiner der beiden Seiten aufloesen konnte.
ENTFERNT:
src/panel.js die Anbindung
src/panel-abgleich.js das Anlegen/Wegraeumen von Eintraegen
tools/panel-pruefen.mjs die zugehoerigen Proben
tools/panel-abgleich-pruefen.mjs
panelUrl/panelZeichen in runtime-settings.js
panel_url/panel_zeichen als Einstellung und im Formular
Servertyp `panel` er fragte nicht selbst, sondern las ab
WAS DER MONITOR DAMIT VERLIERT, und es gehoert dazugesagt: die
Schonfrist beim Hochfahren. Ein startendes Modpack antwortet minutenlang
nicht -- am ATM10 gemessen 306 Sekunden -- und meldet dadurch wieder
einen Ausfall, wo vorher "startet gerade" stand. Die
Zwei-Fehlschlaege-Schwelle federt das teilweise ab, nicht ganz.
Fuer Server im Panel ist das kein Verlust: dort postet das Panel und
kennt den Unterschied. Fuer FiveM und LS25 hat es ihn nie gegeben.
Geprueft: alle 281 relativen Importe loesen auf, die geaenderten Dateien
sind syntaktisch in Ordnung, das Frontend baut. Der Bot selbst ist NICHT
neu gestartet worden -- das ist eine Entscheidung des Betreibers.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Betreiber bekam "fuer alles einen Alert". Zu Recht: sechs verschiedene
Meldungen liefen in denselben Kanal, und dahinter stand ein einziger
Modulschalter -- entweder alles oder nichts.
geloescht / bearbeitet / Nick+Rollen / beigetreten / gegangen / AutoMod
Zwei davon waren der eigentliche Laerm, und beide sind gar keine
Moderationsvorgaenge:
1. Jede selbst zurueckgenommene Nachricht wurde gemeldet. Wer seinen eigenen
Tippfehler loescht, ist kein Vorfall.
2. Jede Rollenaenderung, die der Bot selbst ausgeloest hat -- Rollenmenue-Klick,
Level-Aufstieg, Sticky-Restore nach Wiedereintritt, Playtester-Knopf. Der
Bot hat sich selbst bei der Arbeit zugesehen und darueber Protokoll gefuehrt.
Beides wird jetzt gefiltert. Erkannt ueber Discords Audit-Log und nicht ueber
Merker an den vier Stellen, die Rollen vergeben -- so ist auch die fuenfte
erfasst, die jemand spaeter baut.
Dafuer musste werHatGeloescht() zuerst repariert werden. Sie gab fuer VIER
verschiedene Lagen dasselbe `null` zurueck: kein Server, kein Recht aufs
Audit-Log, Abfrage fehlgeschlagen, und "der Verfasser war es selbst". Zum
Anzeigen reichte das ("dann eben ohne Angabe"). Als Filter waere es die
schlimmste Sorte Fehler gewesen: fehlt dem Bot das Recht, saehe "selbst
geloescht" genauso aus wie "konnte nicht nachsehen" -- und das Protokoll
haette stillschweigend alles verschluckt, ohne dass es jemandem auffaellt.
Jetzt drei unterscheidbare Antworten: { wer } / { selbst } / { unklar, Grund }.
Verschwiegen wird nur, was sicher selbst geloescht wurde. Unklares wird
gemeldet, und der Grund steht im Embed, damit niemand die Zeile fuer "von
einem Mod geloescht" haelt. Fehlt das Recht, sagt das Log es einmal und dann
nicht wieder -- sonst waere die Meldung selbst wieder Rauschen.
Dazu vier Einzelschalter im Panel, weil "alles oder nichts" die Ursache war.
Vorgabe an: ein Protokoll, das nach einem Update stillschweigend aufhoert zu
protokollieren, waere die schlechtere Ueberraschung. Gelesen wird mit
!== '0', geschrieben werden sie einmalig als '1' -- das Panel liest
Feld-Schalter naemlich mit === '1', und ein Formular, das das Gegenteil
dessen zeigt, was gilt, ist schlimmer als gar keins.
Aufgepasst bei den Schaltern: an drei der Handler haengt mehr als nur die
Meldung. Begruessung und Auto-Rollen beim Beitritt, das Sichern der Rollen
beim Austritt, und das Nachfuehren des Nachrichten-Gedaechtnisses beim
Bearbeiten laufen weiter, auch wenn niemand die Meldung sehen will. Sonst
haette ein Protokoll-Schalter das Verhalten des Servers geaendert.
Geprueft mit gestellten Objekten, zwoelf Faelle: kein Server, kein Recht,
Abfrage wirft, kein Eintrag, Eintrag vom Autor selbst, Eintrag zu alt, Mod
war es -- und fuer die Rollen: Bot war es, Mensch war es, kein Eintrag, kein
Recht, Abfrage wirft. Im Zweifel wird immer gemeldet. Dazu die Schalter gegen
die Panel-Anzeige: beide sagen dasselbe.
Ungeprueft: der Lauf gegen ein echtes Audit-Log. Ob der Bot das Recht
"Audit-Log ansehen" hier ueberhaupt hat, steht nach dem Ausrollen im Log.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Anlass ist eine Beobachtung aus dem Betrieb: der Bot kam mit voller
Aussteuerung in den Kanal, und wer schon drin sass, ist erschrocken. Dass
sich jeder den Bot fuer sich leiser drehen kann, hilft erst *nachdem* es zu
laut war. Also liegt die Vorgabe jetzt bei 50 %, und lauter machen kostet
zwei Knopfdruecke.
Dafuer musste die Tonkette umgebaut werden. Bisher lief Opus unveraendert
durch (ffmpeg -> Ogg/Opus -> Discord). Das war eine Stufe sparsamer, aber an
fertigen Opus-Paketen laesst sich die Lautstaerke nicht drehen -- dafuer muss
man an die Abtastwerte. Jetzt: ffmpeg gibt PCM aus, die Sprachbibliothek
regelt und kodiert.
Der alte Einwand im Dateikopf ("sonst muesste @discordjs/opus mit ins Image,
und das will gebaut werden") stimmt weiterhin -- fuer @discordjs/opus.
Nachgemessen am 14.08.2026 in node:22-slim:
@discordjs/opus npm i bricht ab, kein Prebuild fuer node-v127/glibc-2.36,
node-gyp will bauen, im Image fehlen die Werkzeuge
opusscript reines JavaScript, installiert in 740 ms, kein Compiler
0,3 % eines Kerns fuers Kodieren eines Dauerstroms
Der Umweg ueber Opus-Dekodieren entfaellt dabei ganz, ffmpeg liefert ja schon
Abtastwerte. Deshalb 0,3 % und nicht 2,9 %, was Dekodieren und Kodieren
zusammen gekostet haetten.
Die Kette ist mit echtem ffmpeg gegen einen Sinuston geprueft: 200
Opus-Pakete fuer 4,0 s Ton, kein Versatz, und die Lautstaerke laesst sich
mitten im Strom aendern. Genau darum geht der Weg ueber PCM und nicht ueber
ffmpegs volume-Filter -- der haette bei jedem Klick einen Neustart des
Prozesses gebraucht, also eine hoerbare Luecke.
Am Panel zwei Knoepfe in der bestehenden Reihe, Schritte von 10, Bereich 10
bis 200. Am Anschlag graut der Knopf aus, statt sich druecken zu lassen und
nichts zu tun. Die Rueckmeldung ist die neu gezeichnete Tafel mit dem neuen
Prozentwert.
Der Wert steht in den Einstellungen, nicht in radio_state: der Merker dort
wird beim Stoppen geloescht, die Lautstaerke soll das ueberleben.
Dazu der Kanal-Status: was laeuft, steht jetzt auch in der Statuszeile des
Sprachkanals und wird mit dem Titel nachgefuehrt.
ACHTUNG, das ist ein UNDOKUMENTIERTER Endpunkt.
PUT /channels/{id}/voice-status -- Discord hat ihn nie in die API-Doku
aufgenommen, die Pull Requests dazu liegen seit 2023 offen, und discord.js
hat die Umsetzung als "not planned" geschlossen. Roher Aufruf ueber
client.rest also, und er kann ohne Ankuendigung verschwinden.
Deshalb ist er als Beiwerk gebaut: faellt er aus, sagt er einmal warum und
schweigt dann -- ein Fehler alle 30 Sekunden waere kein Hinweis mehr,
sondern Rauschen. Das Radio laeuft in jedem Fall weiter. Der naechste
Sender-Start versucht es erneut, falls das Recht inzwischen erteilt wurde.
Das Recht heisst "Kanalstatus festlegen" und sitzt auf Bit 48. discord.js
kennt es nicht einmal als Konstante, deshalb steht die Zahl im Quelltext.
Ungeprueft, weil dafuer ein echter Sprachkanal noetig ist: ob das Recht dem
Bot hier tatsaechlich erteilt ist. Faellt es aus, steht der Grund im Log.
Beim Stoppen wird die Zeile geleert -- sonst behauptet sie stundenlang, es
liefe ein Lied, das laengst vorbei ist.
Nebenbei: prism-media wurde direkt importiert, stand aber nie in der
package.json. Der Import ist mit dem Ogg-Auspacker weggefallen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gamedig FRAGT einen Server. Ein startendes Modpack antwortet nicht, und
nach zwei Fehlversuchen stand im Alarm-Kanal eine Ausfallmeldung, obwohl
niemand etwas kaputtgemacht hat -- am ATM10 des Betreibers gemessen jedes
Mal 306 Sekunden Fehlalarm.
Das d4rk_gameserver-Panel SIEHT den Container, statt ihn zu fragen. Neu
ist deshalb `src/panel.js`: Zustand, Fertig-Merkmal, RAM und CPU kommen
als Zusatzauskunft dazu, einmal je Durchlauf abgerufen und 20 Sekunden
zwischengespeichert.
Was das im Alarm-Kanal aendert:
startet gerade kein Alarm, aber nur innerhalb einer Gnadenfrist von
15 Minuten. Ohne diese Grenze fraesse die Anbindung
genau den Alarm, fuer den es sie gibt -- `bereit`
wird nie von allein wahr, ein haengender Start bliebe
sonst fuer immer stumm.
im Panel gestoppt kein Alarm (Code 0/143/137)
abgestuerzt ALARM, mit Code im Embed
OOM-Kill ALARM, und beim Namen genannt statt als Absturz
getarnt -- bei Modpacks die haeufigste Ursache
Container weg ALARM
Der Zaehler wird beim Unterdruecken NICHT zurueckgesetzt: ein bereits
gemeldeter Ausfall bleibt gemeldet, sonst verschluckt ein Stopp im Panel
die spaetere "wieder online"-Entwarnung und im Kanal bliebe ein Alarm
ohne Aufloesung stehen.
ES IST DURCHGEHEND OPTIONAL. Adresse und Zeichen stehen im Webinterface
unter "Server" und NICHT in der .env; ohne Eintrag -- und ebenso, wenn
das Panel nicht antwortet -- verhaelt sich der Monitor exakt wie vorher.
Das Zeichen darf nur lesen, damit auch ein verlorenes niemandem einen
Server stoppen kann.
`tools/panel-pruefen.mjs` prueft die Logik ohne Discord und ohne
Datenbank; deshalb laedt panel.js die Einstellungen erst beim Aufruf.
Zwei Fehler, die zusammen für Ratlosigkeit gesorgt haben.
In der Liste stand „Fehlermeldungen — Gitea-Token", obwohl das Token über
GITEA_API_TOKEN längst gesetzt war und /bug lief. Grund: die
Voraussetzungs-Prüfung las die Einstellung roh statt den wirksamen Wert,
sah also nur in die Datenbank und nicht in die Umgebung. Betraf außerdem
Devlog-Kanal, Commit-Kanal und Guild-ID. runtime-settings hat dafür jetzt
effectiveSetting(), das beides zusammenführt.
Und wer dann auf „Einrichten" drückte, landete im System-Bereich — dort
gibt es gar kein Token-Feld, das steht bei Brand. Das Modul zeigte auf den
falschen Bereich.
Jetzt springt „Einrichten" auf das konkrete Feld und hebt es kurz hervor,
statt nur den Bereich zu wechseln. Der Bereich kommt dabei aus dem
Einstellungs-Register, das ohnehin weiß, wo jedes Feld liegt — damit kann
die Zuordnung am Modul nicht mehr auseinanderlaufen. Dieselbe Logik hängt
an „Einstellungen →" auf den Modul-Karten.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Karte war komplett fest verdrahtet — Farben, der Schriftzug
„WILLKOMMEN", die Zeile „Member #N", das Hintergrund-Raster. Wer etwas
ändern wollte, musste das SVG im Code anfassen.
Jetzt steht das Aussehen unter Config → Support: vier Farben, die beiden
Textzeilen, ein optionales Hintergrundbild per Adresse, der Markenname im
Hintergrund an/aus, und die Karte selbst abschaltbar (dann bleibt das
Text-Embed mit Avatar als Vorschaubild).
Daneben eine Vorschau, die beim Tippen mitrendert — sie geht über
/api/welcome-preview.png und bekommt die Werte als Query, damit man sieht,
was man einstellt, bevor gespeichert wird. Gespeichert wird davon nichts.
Die Akzentfarben greifen auf die Markenfarben zurück, solange nichts
Eigenes gesetzt ist. Wer also die Marke umfärbt, hat die Karte gleich mit.
Beim Hintergrundbild ist Vorsicht eingebaut: nur http(s), Zeitlimit,
Größenbegrenzung, und ein nicht erreichbares Bild lässt die Karte ohne
Bild rendern statt sie ausfallen zu lassen. Über einem Bild liegt ein
Schleier, sonst säuft der Text ab.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Seite hieß Setup und listete 15 Bereiche flach untereinander. Wer
nicht wusste, dass Geburtstage unter „Community" stecken und Willkommens-
Texte unter „Support", hat geklickt bis er es fand.
Die Seitenleiste gruppiert jetzt nach Überblick, Auftritt, Inhalte,
Community, Technik und Zugang. Darüber steht ein Suchfeld, das nicht nur
Beschriftungen durchsucht, sondern auch Stichwörter je Bereich — „geburtstag"
führt zu Community, „ticket" zu Support. Enter springt zum ersten Treffer.
Der aktive Bereich steht in der Adresse (/settings#texte). Damit überlebt er
das Neuladen, und man kann jemandem einen Link auf genau die Stelle schicken.
Der Status-Bereich war die leerste Seite im ganzen Panel: sechs Zahlen. Er ist
jetzt der Einstieg und beantwortet die Frage, die man beim Öffnen wirklich hat
— was läuft noch nicht? Eingeschaltete Module, denen ein Kanal oder eine Rolle
fehlt, stehen dort mit einem Knopf, der direkt an die richtige Stelle springt.
Ist alles eingerichtet, sagt die Seite genau das.
Umbenannt in „Config", weil „Setup" nach einmaliger Einrichtung klingt — die
Seite ist aber der Ort, an dem man dauerhaft alles einstellt.
Schmale Bildschirme: die Leiste wird zur umbrechenden Zeile ohne
Gruppen-Überschriften, das Suchfeld nimmt die volle Breite.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Eine Anwendung, zwei Gesichter — der Server erkennt an der Domain, welche
Seite gefragt ist, und schreibt das als data-site an den <body>. Das
Frontend rendert daraufhin entweder den Community-Hub wie bisher oder die
neue Produktseite.
- Bot-Produktseite (BotApp): Landing mit Feature-Übersicht, Befehls-
referenz, Dashboard unter /dashboard, Orange als Leitfarbe
- Weiterleitungen: Community-Routen auf der Bot-Domain und umgekehrt
werden dauerhaft (301) auf die richtige Adresse geschickt — geteilte
Devlog-Permalinks laufen also nicht ins Leere. Rechtstexte bleiben auf
beiden erreichbar.
- Open-Graph-Tags je Domain, inklusive eigener Vorschau für frei
angelegte Seiten (Entwürfe bekommen bewusst keine)
- Login: neue Einstellung für die Cookie-Domain, damit die Anmeldung auf
beiden Seiten gilt; nach dem Discord-Login landet man wieder auf der
Seite, von der man gestartet ist (vorher immer auf der Hauptadresse)
- Alles greift erst, wenn beide Adressen im Setup eingetragen sind —
bis dahin verhält sich die Anwendung unverändert
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- /impressum (§ 5 DDG) und /datenschutz als öffentliche Seiten im
Brand-Look; Betreiber-Angaben (Name, Anschrift, E-Mail, Zusatz)
liegen als Settings und sind bewusst Owner-only — Team-Mitglieder
mit settings-Scope kommen an die private Anschrift nicht heran
- Die Datenschutzerklärung beschreibt die tatsächliche Verarbeitung
dieser Instanz: Server-Logs, Session-Cookies (einwilligungsfrei nach
§ 25 Abs. 2 TDDDG, kein Tracking), Discord-Login, Level, Wünsche,
Geburtstage, Playtester/Alpha-Keys, Bewerbungen, Tickets/Modmail,
Erinnerungen, Moderation und Audit-Log
- DSGVO Art. 17: DELETE /api/profile löscht XP, Geburtstag,
Playtester-Eintrag, Abstimmungen, Erinnerungen, Rollen-Sicherung,
Giveaway-Teilnahmen und gibt den Alpha-Key wieder frei; Button im
Profil unter „Meine Daten", Löschung landet im Audit-Log
- Footer verlinkt beide Rechtstexte, neues GET /api/legal (öffentlich)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 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 <noreply@anthropic.com>
Der Discord-Login ist jetzt für alle Member nützlich:
- /profil: Rang + XP-Fortschritt, Discord-Rollen als Chips, Playtester-
Badge, eigener Alpha-Key mit Kopier-Button
- Rollen-Selfservice: veröffentlichte Rollen-Menüs direkt im Browser
togglen (gleiche Exklusiv-Logik wie die Discord-Buttons; nur Rollen
aus aktiven Menüs erlaubt)
- Wunsch-Voting im Web: auf /roadmap Wünsche einreichen (postet wie
/wunsch in den Voting-Kanal) und upvoten (wish_votes-Tabelle,
ein Vote pro Member, unabhängig von Discord-👍)
- Member-Gate: Login nur für Mitglieder der konfigurierten Guild;
Abgewiesene sehen einen Banner mit Discord-Invite-Link (neue
Settings member_gate_enabled + discord_invite_url im System-Tab)
- Membership-Check mit 5-Minuten-Cache, Owner immer erlaubt
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Alpha-Keys: Pool im Community-Tab, Ein-Klick-Verteilung per DM an alle Playtester
(Key bleibt frei, wenn die DM geblockt wird)
- Bewerbungs-Formulare: Builder im neuen Bewerbungen-Tab (bis 5 Fragen),
Button → Discord-Modal → Review-Embed mit ✅/❌, Rolle + DM bei Entscheidung
- Events: GuildScheduledEventCreate → Announce-Embed; öffentliche /events-Seite
aus den Discord-Events (5-min-Cache)
- Server-Stats: activity_daily (Nachrichten/Joins/Leaves) → Balken-Chart auf /level
- Triggers (Auto-Antworten, 30s-Cooldown), /remind (DM-Scheduler),
Twitch-Live (Helix, App-Creds write-only) + YouTube-RSS-Announcements,
Temp-Voice (Join to Create, Cleanup bei Leerstand + Start)
- Brand-Tab: MEE6-Style Bot-Identity-Karte (Avatar-Vorschau mit Status-Dot,
Bot-Name via setUsername, Presence online/idle/dnd, Aktivität)
- Neue Intents: GuildVoiceStates, GuildScheduledEvents; alles smoke-getestet
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Alle Embeds nutzen jetzt brandName/brandColor/brandColor2/brandFooter aus den
Settings (Codemod über 20 Module) — Farbe & Name serverweit per Klick änderbar
- Bot-Status (Presence): Typ (Spielt/Schaut/Hört/Status) + Text, sofort angewendet;
Server-Monitor nutzt die Presence nur noch, wenn kein eigener Status gesetzt ist
- Avatar-/Banner-Upload direkt aufs Bot-Profil (Base64, 8-MB-Limit,
Discord-Rate-Limit sauber gemeldet)
- Env-Verlagerung: GITEA_API_TOKEN (write-only Setting) und DISCORD_GUILD_ID
jetzt auch über den Brand-Tab pflegbar — weniger Redeploys
- Rollen-Menüs: Button-Farbe pro Eintrag wählbar (Grau/Blau/Grün/Rot)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Tickets: /ticket-setup postet 🎫-Button; Klick erstellt privaten Thread
(ein offenes Ticket pro User), Schließen-Button sperrt + archiviert
- Composer auf der Setup-Seite: Nachricht/Embed als Bot in beliebigen Kanal
senden oder per Message-ID bearbeiten (nur eigene Bot-Posts)
- API v1: PATCH /api/v1/message — Skripte können ihre Bot-Posts aktualisieren
- Setting ticket_channel_id; README ergänzt
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- /wunsch: Voting-Embed mit 👍, Reaction-Tracking (add/remove) in SQLite,
Top-20-Rangliste öffentlich auf der Roadmap-Seite
- /giveaway (Admin): Preis/Dauer/Gewinnerzahl, 🎉-Teilnahme-Button (toggle),
Minuten-Scheduler zieht Gewinner, schließt das Embed ab und pingt sie
- Contribution-Heatmap: 52-Wochen-Grid in Brand-Gelb aus dem Commit-Archiv
(/api/heatmap) auf der Roadmap-Seite
- Setting voting_channel_id auf der Setup-Seite
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Modmail: DMs an den Bot landen als Threads im privaten Staff-Kanal,
Thread-Antworten gehen als DM zurück (Zustell-Feedback per Reaktion)
- Willkommens-Embed für neue Member (GuildMemberAdd; Server-Members-Intent nötig)
- Mod-Log: gelöschte/bearbeitete User-Nachrichten in privaten Log-Kanal
- Server-Monitor: FiveM-kompatible Endpoints (/dynamic.json) alle 2 min,
persistentes Status-Embed (edit in place) + Spielerzahl als Bot-Presence
- Neue Intents: DirectMessages, GuildMembers; Partials.Channel
- Setup-Seite: Sektionen // Moderation & Kontakt und // Game-Server
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Playtester: /playtester-setup postet Bewerbungs-Embed mit 🧪-Button;
Toggle vergibt Rolle + DB-Eintrag; Liste + Rollen-Auswahl auf der Setup-Seite
- Starboard: ab konfigurierbarem ⭐-Schwellwert Repost als Embed in den
Best-of-Kanal (Dedupe, kein Selbst-Boarding); neue Intents/Partials für Reaktionen
- Galerie: Bilder aus dem Screenshot-Kanal lokal archiviert (live + /galerie-backfill,
Delete-Sync, Bot reagiert mit ⭐) → öffentliche /galerie-Seite mit Hover-Grid
- Setup-Seite: Sektion // Community (Rolle, Starboard-Kanal + Schwellwert, Screenshot-Kanal)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- GET /api/roadmap (public): Milestones des Setting roadmap_repo, 5-min-Cache,
letzter guter Stand als Fallback bei Gitea-Ausfall
- /roadmap: Gruppen In Arbeit / Geplant / Fertig, Fortschrittsbalken im Brand-Look,
Ziel-Monat, Markdown-Beschreibungen; Nav-Link öffentlich
- Setup-Seite: Roadmap-Repo konfigurierbar
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- /bug (alle Member): erstellt Gitea-Issue inkl. Screenshot-Upload als Asset;
bug_reports-Tabelle für den Rückkanal — Issue geschlossen (issues-Webhook)
→ DM an den Reporter
- Auto-Thread '💬 Devlog <Datum>' unter jedem Devlog-Post (Setting, Default an)
- Watchdog: prüft Setting watchdog_urls alle 2 min, DM an Admin nach 2 Fails
in Folge + Entwarnung mit Downtime-Dauer
- Setup-Seite: Bug-Repo, Threads-Toggle, Watchdog-URLs; Env GITEA_API_TOKEN/GITEA_URL
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Setting devlog_ping_role_id (Rollen-Dropdown auf der Setup-Seite, leer = aus)
- Devlog-Post erwähnt die Rolle (allowedMentions gescoped) und trägt einen
🔔-Button, über den Member die Rolle selbst an-/abmelden (ephemere Bestätigung)
- API liefert vergebbare Rollen (ohne @everyone/managed), PUT validiert
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Gitea-Release-Webhook: Releases werden archiviert (releases-Tabelle) und neue
'published'-Releases als oranges 🚀-Embed mit Buttons angekündigt
- Release-Kanal auf der Setup-Seite wählbar (leer = Feature aus) + Test-Button
- /changelog: öffentliche Seite mit Tag-Chips, Pre-Release-Badge, Release-Notes
- /api/releases öffentlich; Updates/Redeliveries posten nicht doppelt
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Settings-Tabelle in SQLite; Env-Variablen nur noch Fallback, Änderungen greifen sofort
- /settings (Admin): Devlog-/Commit-Kanal als Dropdown aus allen sichtbaren Textkanälen,
je mit Test-senden-Button
- Commit-Feed-Regeln: an/aus, Branch-Filter, ignorierte Repos (archiviert wird immer,
gefiltert wird nur das Posten; ignorierte Repos komplett übersprungen)
- Status-Panel: Bot-Tag, Uptime, Devlog-/Commit-Zahlen, DB-Größe
- API: GET/PUT /api/settings, POST /api/settings/test/:target (alles Admin-only)
- COMMIT_CHANNEL_ID/DEVLOG_CHANNEL_ID in config optional
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>