Du hattest recht. Bei vier Anliegen ist ein Auswahlmenue der schlechtere Weg:
zwei Klicks statt einem, und die Moeglichkeiten sieht man erst nach dem
Aufklappen. Ein Knopf ist beides — sichtbar und sofort.
Umgekehrt gilt es nicht immer. Ab sechs wird die Knopfreihe zur Wand und
Discord bricht sie um; dann ist das Menue das ruhigere Bild. Also nicht
"Knoepfe statt Menue", sondern der Uebergang: bis fuenf Knoepfe, darueber
Menue. Fuenf, weil genau so viele in eine Reihe passen.
Betroffen waren nur zwei Stellen — Ticket-Anliegen und Wunsch-Bereiche. Alles
andere im Bot waren ohnehin schon Knoepfe.
Eine Sache verliert man dabei: die graue Beschreibungszeile, die ein Menue
unter jedem Eintrag anzeigt. Die steht jetzt im Text des Aufruf-Posts, und da
ist sie ehrlich gesagt besser aufgehoben — man liest sie, bevor man klickt,
statt danach.
Geprueft gegen discord.js selbst, weil Ueberlaengen und ungueltige Emojis sonst
erst beim Posten auffallen: null Eintraege (der eine alte Knopf), eins bis
fuenf (Knoepfe mit Emoji und der ID in der Kennung), sechs (zurueck zum Menue),
Murks-Emoji (faellt weg, Knopf bleibt), 200 Zeichen langer Name (auf 80
gekuerzt), und die Beschreibungszeile in allen drei Faellen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ueberall, wo ein Emoji nach Discord geht — Rollen-Menues, Ticket-Anliegen,
Wunsch-Bereiche, Portal-Kacheln — stand ein leeres Textfeld, in das man mit
Win+. hineintippen musste. Jetzt sitzt daneben ein Knopf mit einer Tafel:
sieben Gruppen, deutsche Suchbegriffe ("auto", "geld", "warnung"), ein Klick
schreibt ins Feld. Das Feld bleibt daneben stehen, denn Server-Emojis
(<:name:123>) kennt nur Discord.
Bewusst eine eigene Liste statt einer Bibliothek: die vollstaendige
Unicode-Tabelle waere ein halbes Megabyte fuer eine Handvoll Symbole, und die
Suchbegriffe duerfen deutsch sein.
## Forum-Tags
Ist der Voting-Kanal ein Forum, wird jeder Wunsch jetzt ein Forums-Beitrag
statt Nachricht-plus-Thread — und bekommt Tags fuer Bereich und Stand. Damit
laesst sich auch in Discord filtern, wofuer Tags ja da sind. Beim Wechsel des
Stands zieht das Tag mit: der alte fliegt raus, der neue kommt rein, der
Bereich bleibt stehen.
Zugeordnet wird ueber den Namen. Wer im Forum ein Tag "Fahrzeuge" anlegt,
bekommt es automatisch gesetzt; wer keines anlegt, verliert nichts. Bewusst
keine ID-Zuordnung im Panel — das waere eine zweite Liste, die man pflegen
muss und die beim ersten Umbenennen auseinanderlaeuft.
Dabei aufgefallen und mitgefixt: im Forum liegt die Startnachricht im Beitrag
selbst und nicht im Kanal. Der Status-Aktualisierer hat sie vorher im Kanal
gesucht und nicht gefunden — die Begruendung waere im Forum also nie im Post
gelandet. Und der Titel wird jetzt nur um den Stand ergaenzt statt
ueberschrieben, damit "🚗 Fahrzeuge" stehen bleibt.
Geprueft: tagsFuer gegen 20 Faelle — kein Forum, Medien-Kanal, fehlende Tags,
Gross/Kleinschreibung, Deckel bei fuenf (mehr nimmt Discord nicht), und alle
fuenf Staende einzeln. Im Browser die Emoji-Tafel an allen vier Stellen:
Suche, Escape, Klick daneben, Leeren, Uebernehmen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nach dem Vorbild von windrose.support: Bereiche zum Einsortieren, Sortierung
nach Top / Bewegung / Neu, und eine Zahl daneben, wie viel darueber geredet
wird.
Der Unterschied zu so einem Dienst ist, wo geredet wird. Zu jedem Wunsch macht
der Bot jetzt einen Thread unter dem Post auf. Der Kommentar-Zaehler auf der
Roadmap zaehlt die Beitraege darin und verlinkt hinein — es gibt also einen
Zaehler wie bei einem Ideen-Board, aber ohne ein zweites Kommentarsystem neben
Discord zu stellen. Genau das hatte ich beim letzten Mal als das Falsche
bezeichnet, und dabei bleibt es.
Eingereicht wird jetzt auch ueber einen Aufruf-Post: /wunsch-setup postet ihn,
und mit Bereichen wird daraus ein Auswahlmenue — erst wohin, dann das
Formular. Ohne Bereiche bleibt es beim einen Knopf. Derselbe Aufbau wie bei den
Ticket-Anliegen, bis hin zur Emoji-Pruefung, die ich mir dort schon geschrieben
hatte.
Das Anlegen selbst ist nach bot/wishes.js gewandert. /wunsch, der Knopf und die
Webseite gehen jetzt denselben Weg — vorher hatte die Webseite ihr eigenes
Embed zusammengebaut, das dem aus dem Befehl nur aehnlich sah und keinen Thread
bekam.
"Bewegung" sind die Stimmen der letzten sieben Tage. Die Rangliste allein
zementiert alte Wuensche: was einmal oben steht, bleibt oben, egal ob noch
jemand hinschaut. Sortiert wird auf dem Server, weil die Zeitstempel der
Stimmen im Browser gar nicht ankommen.
Bewusst nicht uebernommen: Gegenstimmen. Windrose hat sie, aber bei einer
Community dieser Groesse laden sie zum Nachtreten ein und schrecken vom
Einreichen ab — und eine Zahl, die aus zwei Richtungen kommt, sagt am Ende
weniger als eine, die nur zaehlt, wer etwas will.
Geprueft: Migration erneut durchgespielt (das Schema hat drei Spalten
dazubekommen), Routen, SQL gegen das Schema, und im Browser alle drei
Filterreihen einzeln und kombiniert, der Sortierwechsel, die Thread-Links und
die Bereichs-Pflege im Panel.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Knopf unter dem Playtester-Aufruf hat jeden aufgenommen, der ihn gedrueckt
hat — ohne Frage, ohne Pruefung. Fuer ein Programm, dessen Teilnehmer Zugang
und Alpha-Keys bekommen, ist das die falsche Tuer.
Statt einen zweiten Pruef-Ablauf danebenzustellen, laeuft die Aufnahme jetzt
ueber die Bewerbungs-Formulare, die es laengst gibt: Modal ausfuellen,
Review-Embed im Staff-Kanal, ✅ oder ❌, Rolle und Antwort-DM bei Zusage. Das
ist dieselbe Maschinerie, nur ein anderer Aufhaenger — und damit auch nur eine
Stelle, an der spaeter etwas kaputtgehen kann.
Ein Formular laesst sich als Playtester-Formular markieren ("Annahme traegt als
Playtester ein"). Wer dort angenommen wird, landet zusaetzlich in der
Playtester-Liste, damit die Schluessel-Verteilung ihn kennt. Nur eines kann es
sein — sonst wuesste /playtester-setup nicht, welches es posten soll.
/playtester-setup postet jetzt den Knopf dieses Formulars. Fehlt das Formular,
fehlen Fragen oder fehlt der Review-Kanal, sagt der Befehl das, statt einen
Aufruf zu posten, der ins Leere fuehrt.
Der alte Knopf in bereits geposteten Nachrichten nimmt niemanden mehr auf: er
verweist auf den Aufruf. Austreten bleibt Selbstbedienung — dafuer braucht es
keine Freigabe.
Im Panel steht im Playtester-Bereich, ueber welches Formular die Aufnahme
laeuft, ob es schon gepostet ist, und ein Weg dorthin. Ohne markiertes Formular
steht dort, dass gerade niemand hereinkommt — das ist sonst der Grund, warum
sich tagelang niemand bewirbt.
Geprueft: Routen, SQL gegen das Schema, und im Browser beide Zustaende des
Hinweises sowie der neue Schalter — beim Wechsel zwischen zwei Formularen
folgt er dem richtigen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher sah jedes Ticket gleich aus: ein Knopf, ein Thread, und im Thread stand
als Erstes die Frage, worum es ueberhaupt geht. Und wer sich darum kuemmert,
stand nirgends — man sah hinterher nur, wer zufaellig auf "Schliessen" geklickt
hat.
Anliegen sind Kategorien, die man in der Config anlegt: Name, Emoji, eine
Zeile Beschreibung, ein Einstiegstext und optional eine Rolle, die beim
Oeffnen erfaehrt, dass etwas anliegt. Der Ticket-Post wird dadurch vom Knopf
zum Auswahlmenue; das gewaehlte Anliegen steht im Thread-Namen und der
Einstiegstext gleich im ersten Beitrag — gute Stelle fuer "was wir wissen
muessen".
Wer keine anlegt, merkt nichts: ohne Eintrag bleibt es beim einen Knopf. Wer
nur eine Sorte Tickets hat, soll nichts einrichten muessen.
Uebernehmen ist ein Knopf im Thread, sichtbar fuers Team (wer aufraeumen darf,
darf auch uebernehmen). Danach steht im Thread, wer sich kuemmert, und der
Knopf wird zu "Freigeben". Die Bedingung "nur wenn noch niemand dran ist"
steckt im UPDATE und nicht davor — zwei gleichzeitige Klicks wuerden sonst
beide gewinnen. Beim Schliessen landen Anliegen und Bearbeiter im Transcript
und im Mod-Log-Eintrag.
Die Erstellung ist dabei aus client.js nach tickets.js gewandert. Knopf und
Menue gehen jetzt denselben Weg, und in client.js sind drei Importzeilen
weggefallen, die nur noch dort standen.
Geprueft: alsEmoji gegen 20 Eingaben (Unicode, Hautfarbe, ZWJ-Ketten,
Server-Emoji, Murks) und das fertige Panel gegen discord.js selbst — ein
ungueltiges Emoji oder ein zu langes Label laesst Discord sonst die ganze
Nachricht fallen, und das faellt erst beim Senden auf. Im Browser: Liste,
Bearbeiten-Formular und der leere Zustand.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bewusst Discords eigenes Poll-Objekt statt einer Nachbildung mit Knoepfen —
Oberflaeche, Auszaehlen und Ergebnisanzeige macht Discord dann selbst, auch
auf dem Handy. Wir bauen nur die Frage zusammen.
Bis zu vier Antworten (Discord erlaubt zehn, vier halten den Befehl
uebersichtlich), Laufzeit von einer Stunde bis einer Woche, Mehrfachauswahl
optional. Rechte: ManageMessages.
Zwei gleiche Antworten werden abgefangen. Discord stoert das nicht, aber
abstimmen kann darauf niemand sinnvoll.
Nicht zu verwechseln mit /wunsch: das sammelt Feature-Wuensche dauerhaft und
zeigt sie auf der Roadmap. /umfrage ist die schnelle Frage zwischendurch und
laeuft von selbst ab. Steht so auch als Kommentar im Befehl.
Geprueft gegen die installierte discord.js-Fassung (14.27) statt gegen die
Dokumentation: Feldnamen aus den Typdefinitionen gelesen, und die fertige
Nutzlast durch discord.js' eigene Serialisierung gejagt. Kommt als
poll_media / allow_multiselect / layout_type 1 raus, genau wie Discords API
es erwartet.
Dazu: 18 Befehle laden ohne Namenskollision, Befehl steht auf der
oeffentlichen Liste in beiden Sprachen, Zahl auf der Produktseite von 17
auf 18.
Nutzungsbedingungen als Entwurf unter docs/ — Text fuer den Seiten-Editor,
keine Rechtsberatung.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Server-Monitor: Der Footer behauptete fest "alle 2 min", obwohl das Intervall
seit den einstellbaren Werten aus monitor_interval kommt. Wer im Panel etwas
anderes setzt, bekam eine falsche Angabe. Liest jetzt den echten Wert.
Level-Aufstieg: War als einziges im Community-Bereich reiner Text, waehrend
Geburtstage, Verlosungen und Bewerbungen laengst Embeds sind. Jetzt ueber
brandEmbed mit Avatar, Gesamt-XP und Rest bis zum naechsten Level. Der Text
bleibt die Vorlage aus dem Panel — nur der Rahmen ist neu.
Dazu zeigt das Embed die Belohnungs-Rolle: entweder die gerade vergebene oder
die naechste, auf die man hinspielt. grantRewards() gibt dafuer zurueck, was
wirklich dazukam.
Ist die Vorlage im Panel leer, wirft setDescription('') — der Aufstieg waere
still ausgefallen. Faellt jetzt auf eine schlichte Zeile zurueck.
/rank: Avatar als Thumbnail, Podest-Zeichen fuer die ersten drei, Rest-XP und
naechste Belohnung als eigene Felder. Das Feld erscheint nur, wenn es
Belohnungs-Rollen gibt — sonst stuende dort ein leeres Feld.
Nebenbei zwei veraltete ephemeral: true auf flags: MessageFlags.Ephemeral
umgestellt, wie es der Rest der Befehle schon macht.
Geprueft: alle vier Embeds wirklich gebaut und ausgegeben, discord.js
validiert dabei. Das Intervall im Footer zog den auf 7 gestellten Wert.
Die negative XP-Differenz im ersten Testlauf war ein inkonsistenter
Testdatensatz — mit 4200 XP ist man Level 9, nicht 7; ueber 0..60000 XP
durchgespielt wird die Differenz nie negativ.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der fehlende Befehl zum Raid-Schutz. Drei Unterbefehle:
status ist gerade gesperrt, und wie lange noch
an jetzt sperren — fuer die Welle, die man kommen sieht
aus sofort aufheben — fuer den Fehlalarm um drei Uhr nachts
Rechte: ManageGuild, weil die Sperre genau das anfasst — Verifizierung und
Invites. Antworten sind ephemeral.
Die Sperr-Massnahmen sind in dichtmachen() herausgezogen, damit automatisch
und von Hand denselben Weg nehmen und sich nicht auseinanderentwickeln. Die
Meldung sagt jeweils dazu, wodurch sie ausgeloest wurde und wer es war.
Ist der Raid-Schutz ausgeschaltet, verweist der Befehl aufs Panel statt
stillschweigend nichts zu tun.
Dazu: der Befehl steht auf der oeffentlichen Befehls-Seite, in beiden
Sprachen, und die Zahl auf der Produktseite geht von 16 auf 17.
Geprueft: 17 Befehle laden, keine doppelten Namen, Rechte-Bitmaske 32
(ManageGuild), alle neuen Exporte vorhanden, kein Sprachschluessel fehlt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beide Befehle nahmen ihre Eingaben als Slash-Optionen entgegen. Das heißt:
eine Fehlerbeschreibung einzeilig in die Befehlszeile tippen, ohne
Umbrüche, ohne Absätze. Entsprechend dürftig fielen die Meldungen aus.
/bug öffnet jetzt ein Fenster mit vier Feldern: Titel, was ist passiert,
was war erwartet, wie kann man es nachstellen. Die letzten beiden sind
freiwillig und erzeugen im Issue nur dann eine Überschrift, wenn sie
ausgefüllt sind. /wunsch fragt die Idee und ein optionales „warum wäre das
gut?" ab — die Rückfrage macht aus einem Einzeiler einen Vorschlag, über
den man abstimmen kann.
Der Screenshot bleibt eine Option am Befehl, weil Discord in Fenstern
keine Dateien erlaubt. Er hängt damit an der Befehls-Interaktion, das
Formular kommt aber als eigene zurück — der Anhang wird deshalb kurz
zwischengeparkt und nach dem Absenden verwendet. Abgelaufene Einträge
räumt der nächste Aufruf mit weg, damit da nichts liegen bleibt.
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>
Bisher standen Begrüßung, Level-Ansage, Ticket-Texte und alle
Bestätigungs-Nachrichten fest im Code. Wer den Ton ändern wollte, musste
die Quelldateien anfassen und neu deployen — für alle außer mir also gar
nicht.
src/templates.js hält jetzt alle nach außen gehenden Texte an einer
Stelle: Standardtext, Platzhalter mit Erklärung und die Zuordnung zum
Modul. Der neue Setup-Tab „Texte" zeigt sie als Karten mit Textfeld;
die Bausteine darunter fügen sich per Klick an der Cursor-Position ein.
Der Standard bleibt im Code, die Datenbank speichert nur echte
Abweichungen. Ein leeres Feld heißt deshalb „wieder Standard" — und
angepasste Vorlagen sind an einer Marke erkennbar. Vorlagen zu
ausgeschalteten Modulen sind als solche gekennzeichnet, damit niemand
einen Text feilt, den gerade nichts sendet.
Unbekannte Platzhalter bleiben stehen statt still zu verschwinden, damit
ein Tippfehler in der Nachricht sichtbar wird.
Die Route heißt /api/texts, nicht /api/templates — letzteres sind seit je
die gespeicherten Composer-Nachrichten.
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>
- 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>
- 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>
- /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>
- 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>
- Bild-Attachments werden beim Archivieren lokal gespeichert (Discord-CDN-URLs
laufen ab) und unter /devlog-assets/ ausgeliefert; DB-Migration: images-Spalte
- Frontend: Bildergalerie im Grid (responsive nach Anzahl), Embed-Titel als
Projekt-Chip, Commit-Fußzeile abgesetzt, Monats-Trenner in der Timeline
- Backfill lädt Bilder alter Devlogs nach (Dedupe verhindert Doppel-Downloads)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- devlog.py kann per Webhook ODER Bot-Token posten — beides wird jetzt archiviert
(nur User-Chatter und der EcoBot selbst bleiben ausgeschlossen)
- /devlog-backfill meldet jetzt Webhook/Bot/User-Zählung zur Diagnose
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Webhook-Nachrichten im Devlog-Kanal werden automatisch in SQLite archiviert
(devlog.py im EcoGame-Repo bleibt unverändert)
- /devlog-backfill (Admin): scannt die komplette Kanal-Historie in 100er-Blöcken
- Dedupe über Discord-Message-ID, Embeds werden mit Titel+Beschreibung erfasst
- Neue Intents: GuildMessages + MessageContent, neue Env-Var DEVLOG_CHANNEL_ID
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- discord.js v14 Bot mit Slash-Command-Registry (Guild oder global)
- Konfiguration über .env mit Validierung (config.js)
- Dockerfile + docker-compose.yml für Portainer-Deployment
- README mit Schritt-für-Schritt-Anleitung (Discord Developer Portal)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>