Files
Nexumi/docs/PHASE-TRACKING.md
TheOnlyMace bc4fae5415 Enhance tag management with cooldown feature and related updates
- Added a `cooldownSeconds` field to the `Tag` model, allowing for customizable cooldown periods between tag responses.
- Updated tag command handling to incorporate cooldown logic, preventing spam and improving user experience.
- Enhanced the WebUI to support cooldown configuration for tags, including input validation and localization for cooldown messages.
- Refactored tag-related service functions to manage cooldowns effectively, ensuring accurate tracking and enforcement.
- Improved localization for new cooldown-related messages in both English and German.
2026-07-25 16:50:46 +02:00

601 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Phase 6 WebUI-Fundament (Status: abgeschlossen, manuell bestätigt)
- OAuth/Sessions, Settings-Framework, Layout, Modul-Toggles, Access-Rules
- Login manuell bestätigt
## Phase 7 WebUI Modul-Seiten + Owner-Panel (Status: abgeschlossen, manuell bestätigt)
### Abgeschlossen
- Alle Modul-Dashboard-Seiten, Commands-Seite, Overview-Aktivität
- Owner-Panel (`/owner`: Übersicht, Guilds, Users, Flags, Presence, Team, Jobs, Changelog, Audit)
- Prisma `20260722190000_phase7_owner`, Shared Zod (`dashboard.ts`, `owner.ts`)
- BullMQ WebUI↔Bot, Presence-/Wartungsmodus-Anbindung
- Manuell bestätigt (User-Freigabe)
## Phase 8 Landing, Status, Rechtsseiten (Status: abgeschlossen, manuell bestätigt)
### Abgeschlossen
- Landing `/`, Status `/status`, Rechtsseiten, Public APIs, Core-Commands, `docs/verification.md`
- Manuell bestätigt (User-Freigabe)
## Deploy-Branch (`deploy`) Traefik + Dashboard-Domain
### Abgeschlossen (Code auf Branch `deploy`; `main` unverändert)
- Traefik-Vollstack: Compose-Netz `traefik-network` (external) + `internal`
- WebUI hinter Traefik: Host `dashboard.nexumi.de`, Entrypoint `websecure`, `certresolver=letsencrypt`
- Host-Port `3000` entfernt; nur Traefik exponiert die WebUI
- `WEBUI_URL=https://dashboard.nexumi.de` (OAuth-Callback: `/api/auth/callback`)
- Marketing-Landing archiviert unter `apps/webui/src/archived/landing-page.tsx`
- `/` zeigt Login; `/login` leitet auf `/` weiter (Query erhalten)
- **SPEC-Abweichung (bewusst):** öffentliche Landing später extern auf `nexumi.de`; Dashboard unter Subdomain
- Rechtsseiten vollständig (Impressum/Datenschutz/ToS) DE+EN; Locale per Accept-Language (Cookie hat Vorrang)
- Plesk-Landing unter `landingpage/` mit lokalen Rechtsseiten (`impressum.php`, `datenschutz.php`, `nutzungsbedingungen.php`)
### Manuell testen (Deploy)
- [ ] DNS `dashboard.nexumi.de` → Traefik-Host
- [ ] Discord OAuth Redirect: `https://dashboard.nexumi.de/api/auth/callback`
- [ ] `docker compose up -d --build` (Netz `traefik-network` muss existieren)
- [ ] TLS-Zertifikat (Resolver-Name ggf. an Traefik anpassen)
- [ ] Login `/` → Dashboard; Session-Cookie `Secure`
## Post-Phase Premium + DSGVO (Status: implementiert, manuelle Tests offen)
### Abgeschlossen (Code)
- Premium: `PremiumTierConfig`, Guild-/User-Zuweisung, Owner-UI `/owner/premium`
- Limits enforced (Tags/Feeds/Backups) in Bot + WebUI
- DSGVO: `/privacy`, `/gdpr delete confirm:true` (Löschung/Anonymisierung + `GdprDeletionLog`)
- Log-Retention: `LoggingConfig.retentionDays` + täglicher Job `retention`/`purgeExpiredLogs`
- Snipe standardmäßig aus (`GuildSettings.snipeEnabled`, Toggle in Allgemeine Einstellungen)
- Migration `20260722200000_premium_gdpr`
### Manuell testen
- [ ] Owner `/owner/premium`: Limits speichern, Guild PREMIUM zuweisen
- [ ] Free-Limit: mehr Tags/Feeds/Backups als Free-Cap → Fehler
- [ ] `/privacy` und `/gdpr delete confirm:true`
- [ ] Logging-Retention im Dashboard setzen
- [ ] Snipe: default aus; nach Aktivierung in Einstellungen nutzbar
### Bewusst offen
- Musik- und KI-Modul (nur auf Zuruf)
- Premium-Zahlungsanbindung
- Externe Marketing-Landing auf `nexumi.de`
## Post-Phase Dashboard-UI + Command-Palette + Landing-Polish (Status: implementiert)
### Abgeschlossen (Code)
- Guild-/Owner-Shell: Branding, Sticky-Header, mobile Sidebar-Sheet, Overview-Polish
- Globale Cmd/Ctrl+K-Suche: Seiten, Slash-Commands, Einstellungsfelder (Deep-Links)
- Settings-Feld-Registry + Form-Anchors; Commands-Seite liest `?q=`
- Plesk-Landing: Inter, Hero-Hierarchie, dezente Motion, Designsprache an WebUI
### Manuell testen
- [ ] Dashboard: Cmd/Ctrl+K → Seite, Command und Option navigieren (Hash-Scroll)
- [ ] Mobile: Sidebar-Sheet + Such-Trigger
- [ ] Overview und Owner-Panel Layout
- [ ] Landing Light/Dark (`prefers-color-scheme`) + CTAs
### Bewusst offen
- Musik- und KI-Modul (nur auf Zuruf)
- Premium-Zahlungsanbindung
## Post-Phase Channel-Picker überall (Status: implementiert)
### Abgeschlossen (Code)
- `DiscordChannelSelect` / `DiscordChannelMultiSelect` + `useGuildChannels`
- Alle Modul-Forms: Kanal-ID-Texteingaben durch durchsuchbare Channel-Auswahl ersetzt
- Voice-/Category-Filter für TempVoice/Stats/Tickets
## Post-Phase Role-Picker überall (Status: implementiert)
### Abgeschlossen (Code)
- `DiscordRoleSelect` / `DiscordRoleMultiSelect` + `useGuildRoles`
- Modul-Forms/Manager: Rollen-ID-Texteingaben durch durchsuchbare Role-Auswahl ersetzt (Welcome, Verification, Leveling, Logging, Birthdays, Giveaways, Tags, Tickets, Access Rules, Feeds, Scheduler, Commands)
- Selfroles: Role-Builder statt Textarea (`roleId | label | emoji`)
### Manuell testen
- [ ] Rollen in den Modul-Settings per Dropdown setzen und speichern
- [ ] Multi-Select (Autoroles, No-XP-Roles, Support-Roles, …) speichert string[] korrekt
- [ ] Access-Rules: Rolle wählen und Module zuweisen
## Post-Phase Command-Global-Rules + Channel-Picker (Status: implementiert)
### Abgeschlossen (Code)
- Globale Command Channel-Whitelist/-Blacklist in `GuildSettings` + Commands-UI
- Searchable Multi-Select für Kanäle/Rollen (statt Raw-IDs) auf der Commands-Seite
- APIs: `/commands/globals`, `/channels`, `/roles`
- Bot erzwingt globale + per-Command Channel/Role/Cooldown-Regeln in `routeCommand`
- Migration `20260722210000_command_global_channels`
### Manuell testen
- [ ] Commands: globale Whitelist/Blacklist speichern, in Discord prüfen
- [ ] Per-Command Channel/Rollen per Dropdown setzen
- [ ] Deaktivierter Command / Cooldown antwortet ephemeral
## Post-Phase Discord Embed-Builder (Status: implementiert)
### Abgeschlossen (Code)
- Wiederverwendbare `DiscordEmbedBuilder`-Komponente inkl. Live-Vorschau
- Basis: Titel, Beschreibung, Farbe
- Erweitert: Author (Name/Icon/URL), Thumbnail, großes Bild, Footer (Text/Icon), Titel-URL, Timestamp
- Welcome/Leave, Tags (EMBED) und Scheduler nutzen denselben Builder + `buildEmbedFromPayload`
- Platzhalter in Text-/URL-Feldern inkl. `{user.avatar}` / `{server.icon}` (Welcome/Tags)
### Manuell testen
- [ ] Welcome/Leave-Embed mit Author, Thumbnail, Banner und Footer speichern und in Discord prüfen
- [ ] Tag mit Antworttyp Embed (auch nur Bild/Author ohne Titel) erstellen/bearbeiten
- [ ] Geplante Nachricht mit erweitertem Embed anlegen
- [ ] Einfaches Embed (nur Titel/Beschreibung/Farbe) funktioniert weiter wie zuvor
## Post-Phase Logging Multi-Event pro Kanal (Status: implementiert)
### Abgeschlossen (Code)
- Logging-UI: pro Log-Kanal mehrere Event-Typen per Multi-Select
- Bestehende 1:1-Mappings werden beim Laden nach Kanal gruppiert; Speichern expandiert wieder auf `LogChannel`-Zeilen
- Event-Typen sind exklusiv (DB `@@unique([guildId, eventType])`) Auswahl in einer Zeile entfernt sie aus anderen
### Manuell testen
- [ ] Mehrere Events einem Kanal zuweisen und speichern
- [ ] Event von Kanal A nach Kanal B umhängen (verschwindet bei A)
- [ ] Bot schreibt Logs der gewählten Events in den richtigen Kanal
## Post-Phase Platzhalter-Hilfe (Status: implementiert)
### Abgeschlossen (Code)
- Aufklappbare `PlaceholderHelp`-Komponente mit Kopieren pro Token
- Kontextspezifische Presets (Welcome, Tags, Birthdays, Leveling, TempVoice, Stats, Feeds, Scheduler)
- Welcome-DM nutzt jetzt dieselben Platzhalter wie Welcome-Text (nicht nur `{user}`)
### Manuell testen
- [ ] Welcome: Liste aufklappen, Platzhalter kopieren, in Embed/Text einfügen
- [ ] Tags/Birthdays/Leveling/Stats/Feeds/Scheduler: nur die jeweiligen Tokens sichtbar
## Post-Phase Components V2 (Status: implementiert, manuelle Tests offen)
### Abgeschlossen (Code)
- Shared Zod: `MessageComponentsV2` + Actions, `COMPONENTS_V2` für Welcome/Tags, Leave-Typen, custom_id `cv2:{source}:{ref}:{actionId}`
- Prisma-Migration `20260722220000_components_v2`: `welcomeComponents`/`leaveType`/`leaveComponents`, `Tag.components`, `ScheduledMessage.components`, `ComponentMessageBinding`
- Bot: `buildComponentsV2Payload`, Anbindung Welcome/Leave/Tags/Scheduler, Interaction-Handler, Dashboard-Send-Queue `messages`/`dashboardMessageSend`
- Slash: `/embed builder` + `/embed components` (Quick-V2)
- WebUI: `DiscordComponentsV2Builder`, Mode-Switches Welcome/Tags/Scheduler, Dashboard-Seite `/messages`
### Manuell testen
- [ ] Welcome V2 mit Container + Thumbnail-Section + Link-Button + Toggle-Role-Button
- [ ] Tag V2 mit String-Select → ephemeral reply
- [ ] Scheduler V2 Ankündigung
- [ ] Dashboard Messages: Embed + Components V2 senden
- [ ] Slash `/embed components`
- [ ] Bestehende Embeds unverändert
- [ ] Migration anwenden (`prisma migrate deploy`) und Bot/WebUI neu bauen
### Bewusst offen
- Modal-only Components, File-Component, Premium-Buttons (nicht im MVP)
- Pixelgenaue Discord-Vorschau im Dashboard
## Post-Phase Harden (Sharding / Gates / Observability) (Status: implementiert)
### Abgeschlossen (Code)
- Confirmations (Moderation + Guildbackup) in Redis mit TTL statt Prozess-`Map`
- Command-Registrierung einmal im Manager (Redis-Lock); Recurring Jobs einmal (Primary-Shard + Lock)
- Modul-Toggles + FeatureFlags + User-/Guild-Blacklists im Bot erzwungen (`routeCommand`, Events, GuildCreate leave)
- Moderation: Hierarchy-Checks, Duration vor Ban-Confirm, Timeout-Cap 28d, warn remove guild-scoped, atomare Case-Nummern, Temp-Ban Job-Dedup + UNBAN-Case, `setDefaultMemberPermissions`
- Sentry (`@sentry/node`) in Bot + WebUI (`instrumentation.ts`); echte Prometheus-Metrics (`/metrics`)
- Interaction-Registry statt if-Kette; Unit-Tests für Duration, Confirm-Store, Module-Gates, Metrics
### Manuell testen
- [ ] Ban mit Dauer: Confirm auf anderem Shard / nach Restart innerhalb TTL
- [ ] Dashboard: Modul Moderation aus → `/ban` antwortet „Modul deaktiviert“
- [ ] Owner: User-Blacklist → Interactions blockiert; Guild-Blacklist → Bot verlässt Server
- [ ] `/metrics` mit Bearer-Token: Counters + Shard-Ping
- [ ] Temp-Ban ablaufen → Unban + Case `UNBAN`
### Bewusst offen
- Musik- und KI-Modul (nur auf Zuruf)
- Premium-Zahlungsanbindung
- P4-UX (Server-Lock, Ban deleteMessageSeconds) bewusst nachgelagert
## Post-Phase Verification Captcha-Anbieter + Alt-Erkennung (Status: implementiert)
### Abgeschlossen (Code)
- Captcha-Anbieter wählbar pro Guild: `MATH`, `RECAPTCHA_V2`, `RECAPTCHA_V3`, `HCAPTCHA`, `TURNSTILE`
- Google/hCaptcha/Turnstile Keys in `.env` (WebUI); Dashboard zeigt nur konfigurierte Anbieter
- Captcha-Seite `/verify/captcha` mit Provider-Widgets; Server-side Siteverify
- Alt-Account-Erkennung: gesalzener IP-Hash (Captcha-Flow) + Invite-Cluster; optional blockieren via `failAction`
- Prisma `VerificationRecord` + Config-Felder; Migration `20260723120000_verification_captcha_alt`
- Slash `/verify setup`: `captcha_provider`, `alt_detection`
### Manuell testen
- [ ] `.env`: `RECAPTCHA_SITE_KEY`/`SECRET` setzen → Dashboard zeigt reCAPTCHA v2/v3
- [ ] Guild Captcha-Modus + Anbieter wählen → Verify-Button → WebUI Captcha → Rolle
- [ ] Alt-Erkennung an: zweiter Account gleiche IP → Kick/Ban je nach failAction
- [ ] Migration: `prisma migrate deploy` + Bot/WebUI neu bauen
- [ ] `CAPTCHA_IP_HASH_SALT` gesetzt (min. 16 Zeichen), sonst keine IP-Alt-Signale
### Bewusst offen
- Kein Browser-Fingerprinting (DSGVO); IP nur gehasht mit Salt
- Keine manuelle „Alt freigeben“-UI (nur Flag in `VerificationRecord`)
## Post-Phase Leveling Rollen-Belohnungen UI (Status: implementiert)
### Abgeschlossen (Code)
- Dashboard Leveling: Level→Rolle-Belohnungen bearbeiten (Liste, speichern ersetzt `LevelReward`)
- Shared Schema `rewards` + `hasUniqueRewardLevels`; i18n DE/EN
- Stapel-Toggle mit Hinweis neben dem Belohnungs-Editor
### Manuell testen
- [ ] Belohnung hinzufügen (z. B. Level 5 → Rolle), speichern, Mitglied levelt → Rolle vergeben
- [ ] Stack an/aus: stapeln vs. nur aktuelle Belohnungsrolle
- [ ] Doppeltes Level → Fehlermeldung, speichern blockiert
## Post-Phase Giveaway Bugs (Timestamp + End-Status) (Status: implementiert)
### Abgeschlossen (Code)
- Embed: Endzeit als Discord-Timestamp in der **Description** (Footer rendert `<t:…>` nicht)
- `/giveaway end` wirft bei fehlender/bereits beendeter ID Fehler statt Fake-Erfolg
- Dashboard „Jetzt beenden“ prüft `endedAt` nach Job; Timeout/Fehler → kein Erfolgs-Toast
### Manuell testen
- [ ] Neues Giveaway: Endzeit als „in X Stunden“ sichtbar, nicht als Roh-`<t:…>`
- [ ] `/giveaway end` mit falscher ID → Fehlermeldung; mit korrekter ID → Reroll möglich
- [ ] Dashboard „Jetzt beenden“ → Status beendet, Discord-Nachricht aktualisiert
## Post-Phase Audit Remediation (Reliability + Security) (Status: implementiert)
### Abgeschlossen (Code)
- Scheduler: Self-Remove des aktiven Jobs entfernt; safeRemove für externe Cancels
- Channel-Picker: Fehler/leere Responses nicht cachen; Error+Reload wie Rollen
- `requirePermission`: Member- vs. Bot-Permissions getrennt; Defer auf Ban/Purge/Lock/Backup/Giveaway/Media/Translate
- Giveaway: atomares End (`updateMany`), Pause cancel/reschedule, Dashboard Pause/Delete via BullMQ
- Component-/Context-Gates: Maintenance + Modul-Toggle
- Owner-Guild-Bypass ab SUPPORT; OAuth `refresh_token` + Session-Refresh
- Queue safeRemove (Temp-Ban, Reminders, Selfroles); `addJobAndAwait` rethrowt Enqueue-Fehler
- DefaultMemberPermissions für Admin-Commands; Cooldown erst nach erfolgreichem Execute
- API: BadRequest/Upstream-Errors; Discord Roles/Channels 401/403/429/502
### Manuell testen
- [ ] One-shot Schedule sendet einmal (kein Doppel-Post nach Worker-Retry)
- [ ] Channel-Dropdown bei API-Fehler: Retry statt permanent „Keine Treffer“
- [ ] Giveaway Pause im Dashboard → Button disabled; Unpause → Timer weiter; End trotz Pause
- [ ] Modul aus → Join-Button / Ticket-Panel antwortet „Modul deaktiviert“
- [ ] VIEWER-Owner ohne Manage Guild: kein Dashboard-Guild-Zugriff; SUPPORT+: Bypass
- [ ] Session nach Token-Ablauf refresht ohne erzwungenen Re-Login (mit gültigem refresh_token)
## Post-Phase Giveaway End Fix + Role Picker (Status: implementiert)
### Abgeschlossen (Code)
- Root cause: `endGiveaway` removed its own locked BullMQ job → End-Job crashte vor DB-Update (Timer/UI/Command hingen)
- `cancelGiveawayJob` nur noch außerhalb des Workers (Command/Dashboard); locked remove wird ignoriert
- Dashboard-End nutzt eindeutige Job-IDs (`giveaway-end-now-…`); klarere 502-Fehler statt generischem 500
- Bot-Start: `recoverOverdueGiveaways` beendet überfällige aktive Giveaways
- Role-Picker: kein Cache von leeren/fehlerhaften Responses; Redis-Cache best-effort; manuelle Filter statt cmdk-Default
### Manuell testen
- [ ] Giveaway starten (kurz), warten bis Timer abläuft → Embed „Beendet“, Button weg, Gewinner gezogen
- [ ] Giveaway vorzeitig per Dashboard „Jetzt beenden“ → kein Internal Server Error
- [ ] `/giveaway list` → ID kopieren → `/giveaway end id:…` → Erfolg
- [ ] Verifizierung: Rollen-Dropdown zeigt Serverrollen (nicht nur „Keine Treffer“)
- [ ] Nach Deploy: bereits hängende Giveaways werden beim Bot-Start recovered
## Post-Phase Starboard Verbesserungen (Status: implementiert)
### Abgeschlossen (Code)
- `/starboard setup` funktioniert trotz `enabled: false` (Gate-Ausnahme)
- Kanal-Permission-Check vor Setup; `/starboard status`
- Sync: Source-Delete/Bulk-Delete, Message-Edit, RemoveAll/RemoveEmoji
- Race-sicheres Create + Recreate wenn Starboard-Msg fehlt
- Zod/UI: Emoji bis 80 Zeichen, Threshold 1100, Channel Pflicht bei enabled, NSFW-Hinweis
- Unit-Tests `emojiMatches` / `shouldIgnoreMessage`
### Manuell testen
- [ ] `/starboard setup` auf frischem Server ohne Dashboard-Toggle
- [ ] Stern ≥ Schwelle → Post; unter Schwelle → Post entfernt
- [ ] Original löschen/editieren → Starboard aktualisiert/entfernt
- [ ] Dashboard: aktivieren ohne Kanal → Fehler
## Post-Phase Welcome Embed Mentions + Autorole + Preview (Status: implementiert)
### Abgeschlossen (Code)
- Embed `{user}` in Titel/Author/Footer als Displayname (Discord resolved Mentions dort nicht)
- Beschreibung/Content behalten `<@id>`-Mentions
- Autorole: Rollen nachladen, managed/@everyone filtern, `editable`-Check, Warn-Logs bei Hierarchy/Permission
- WebUI Embed-/Components-Vorschau: echte Session-User- + Guild-Daten statt Dummy `@Alex`
### Manuell testen
- [ ] Welcome-Embed Titel `Welcome {user}` → Anzeigename, nicht `<@id>`
- [ ] Beschreibung `{user}` → klickbare Mention
- [ ] User-Autorollen gesetzt, Bot-Rolle über Autorolle, Manage Roles → Rolle bei Join
- [ ] Dashboard Welcome-Embed-Vorschau zeigt eigenen Namen/Avatar und Servername/-icon
## Post-Phase Owner Guild-Liste Meta + Invite (Status: implementiert)
### Abgeschlossen (Code)
- Owner `/owner/guilds`: Discord-Name, Icon, Member-Count, ID (Live-Meta via Bot-Token, Redis-Cache)
- Suche nach Name und ID
- Aktion „Invite erstellen“ (Systemkanal zuerst, max. 24h / 5 Uses, Clipboard + Owner-Audit)
### Manuell testen
- [ ] Serverliste zeigt Name/Logo/Mitglieder statt nur ID
- [ ] Suche nach Servername filtert
- [ ] Invite erstellen → Link im Toast/Clipboard; Bot braucht Create Instant Invite
## Post-Phase Owner Guild-Dashboard Bypass (Status: implementiert)
### Abgeschlossen (Code)
- Owner-Team (jede Owner-Rolle) kann jedes Guild-Dashboard öffnen, in dem der Bot installiert ist — ohne Discord „Server verwalten“
- `requireGuildAccess` / Layout nutzen Owner-Bypass; Guild-Meta via Bot-API wenn nicht in OAuth-Liste
- Owner `/owner/guilds`: Button „Dashboard öffnen“; Hinweisbanner im Guild-Dashboard bei Bypass
### Manuell testen
- [ ] Owner-UI → Server → Dashboard öffnen (Server, auf dem man kein Member/Manager ist)
- [ ] Welcome/Settings speichern funktioniert; Banner „Owner-Zugriff“ sichtbar
- [ ] Nicht-Owner ohne Manage Guild → weiterhin kein Zugriff
## Post-Phase Owner Presence WebUI Polish (Status: implementiert)
### Abgeschlossen (Code)
- PresenceForm: Live-Vorschau, i18n-Enums, Zeichenzähler, Switch+Confirm für Wartung, Hinweise
- Sofort-Apply: WebUI enqueued `presenceRefresh` nach Save
- Wartungsmodus: Discord DND + Wartungstext (keine Rotation)
- VIEWER read-only auf `/owner/presence` (Save nur ADMIN+)
### Manuell testen
- [ ] Status/Text speichern → Discord-Präsenz innerhalb weniger Sekunden
- [ ] Rotierende Zeilen → Vorschau erste Zeile; Job rotiert weiter
- [ ] Wartung an → Confirm, Discord zeigt Wartungstext, kein Rotating
- [ ] VIEWER sieht Formular, kann nicht speichern
## Post-Phase Owner About /about (Status: implementiert)
### Abgeschlossen (Code)
- Core-Command `/about` (neben SPEC-`/info`); Help-Katalog + Locales DE/EN
- Prisma `BotAboutConfig` Singleton + Migration `20260724120000_bot_about`
- Owner `/owner/about`: TEXT / Embed / Components V2 (Builder), Redis-Cache, Audit
- Components-V2-Source `a` für Button-Interaktionen aus `/about`
### Manuell testen
- [ ] Migration deploy + Bot/WebUI neu bauen
- [ ] Owner About Embed speichern → `/about` zeigt Embed
- [ ] About deaktivieren → Deaktiviert-Hinweis
- [ ] `/info` unverändert technisch
## Post-Phase Automod/Moderation Dashboard Ausbau (Status: implementiert)
### Abgeschlossen (Code)
- Automod: alle 10 Filterregeln im Dashboard (Aktion, Schwellen, Link-WL/BL, Wörter/Regex, Ausnahmen)
- Lockdown an/aus enqueued `activateLockdown` / `deactivateLockdown` an den Bot
- Anti-Raid-Aktion `VERIFY` (Unverified-Rolle) zusätzlich zu `LOCKDOWN`
- Moderation: Warn-Tabelle + Case edit/delete im Dashboard
- `/lock` `/unlock` Option `server` für guild-weiten Lock
- Shared: `AutomodRuleDashboardSchema`, `CaseUpdateDashboardSchema`, `AntiRaidAction` inkl. VERIFY
### Manuell testen
- [ ] Automod: WORD_FILTER Wörter setzen, EXTERNAL_LINK Whitelist, Regel speichern → Filter greift
- [ ] Lockdown-Toggle aus → Kanäle wieder beschreibbar
- [ ] Anti-Raid VERIFY: Unverified-Rolle in Verification konfiguriert, Join-Welle → Rolle
- [ ] Cases: Grund editieren / löschen; Warnings entfernen
- [ ] `/lock server:true` und `/unlock server:true`
## Post-Phase Selfroles WebUI → Discord Sync (Status: implementiert)
### Abgeschlossen (Code)
- Root cause: Dashboard speicherte Selfrole-Panels nur in Postgres, ohne BullMQ-Job an den Bot
- Neue Queue `selfroles` mit Jobs `selfRolePanelCreate` / `Update` / `Delete`
- WebUI wartet (bounded) auf Bot-Bestätigung; Panel hat danach `messageId`
- Update zieht fehlende Discord-Nachricht nach (auch für Alt-Panels ohne `messageId`)
- Roles-JSON `{ entries }` wird im Dashboard korrekt gelesen
### Manuell testen
- [ ] WebUI: Panel erstellen → Embed/Buttons erscheinen im gewählten Channel
- [ ] Panel editieren (Titel/Rollen) → Discord-Nachricht aktualisiert sich
- [ ] Channel wechseln → alte Nachricht weg, neue im Zielkanal
- [ ] Panel löschen → Discord-Nachricht entfernt
- [ ] Alt-Panel ohne Message speichern → Nachricht wird nachgezogen
## Post-Phase Selfroles Role-Builder (Status: implementiert)
### Abgeschlossen (Code)
- Textarea `roleId | label | emoji` durch zeilenbasierten Builder ersetzt
- `DiscordRoleSelect` + Label/Beschreibung-Felder + `DiscordEmojiSelect` (Server-CDN + Unicode)
- API `GET /api/guilds/[guildId]/emojis` inkl. Redis-Cache; Shared `DiscordEmojiOption`
- Reaktions-Modus: Emoji je Rolle vor Save Pflicht
- Emoji-Picker Discord-ähnlich: 9er-Grid, Kategorie-Sidebar, Vollsatz via `@emoji-mart/data`, Hautfarben, Suche
### Manuell testen
- [ ] Panel mit Role-Picker, Label, Server-Emoji und Unicode speichern → Discord-Nachricht korrekt
- [ ] Dropdown: optionale Beschreibung erscheint in den Select-Optionen
- [ ] REACTIONS ohne Emoji → Toast, kein Save
- [ ] Bestehende Panels mit Custom-Emoji-Strings laden und editieren
- [ ] Emoji-Picker: Kategorien scrollen, Suche, Hautfarbe, Server-Grid
## Nächster geplanter Schritt
- Deploy auf Traefik-Server manuell verifizieren; danach ggf. `deploy``main` mergen (nur auf Freigabe).
## Post-Phase Verification Panel aus WebUI (Status: implementiert)
### Abgeschlossen (Code)
- Root cause: Dashboard speicherte nur `VerificationConfig`, ohne Panel in Discord zu posten
- Neuer BullMQ-Job `verificationPanelSync` (Queue `verification`); WebUI wartet bounded auf Bot-Bestätigung
- `syncVerificationPanel` shared für `/verify panel` und Dashboard (Edit bei gleichem Kanal, sonst neu posten)
- i18n DE/EN: Hinweis in Modul-Beschreibung + Fehlertexte bei Panel-Sync-Fail
### Manuell testen
- [ ] WebUI: Verifizierung aktivieren, Kanal + Rolle setzen, Speichern → Panel erscheint im Kanal
- [ ] Speichern mit geändertem Modus → bestehendes Panel wird aktualisiert
- [ ] Kanal wechseln und speichern → altes Panel weg, neues im Zielkanal
- [ ] Bot ohne Send-Permission → Fehlermeldung; Config bleibt gespeichert, erneutes Speichern nach Fix
## Post-Phase Ticket Panel aus WebUI + Kategorie-Emoji-Picker (Status: implementiert)
### Abgeschlossen (Code)
- Root cause: Dashboard speicherte Ticket-Config/Kategorien ohne Panel in Discord
- Prisma `TicketConfig.panelChannelId` / `panelMessageId`; Migration `20260725160000_ticket_panel`
- BullMQ-Job `ticketPanelSync` (Queue `tickets`); Sync auch bei Kategorie create/update/delete
- `syncTicketPanel` shared für `/ticket panel_create` und Dashboard; Custom-Emoji-Parsing für Buttons/Select
- WebUI: Panel-Kanal-Picker; `DiscordEmojiSelect` in Kategorie-Erstellung und -Bearbeitung
- Emoji-Zod max 80 (Custom-Emoji-Format)
### Manuell testen
- [ ] WebUI: Kategorie(n) anlegen, Panel-Kanal setzen, Speichern → Panel mit Buttons im Kanal
- [ ] Kategorie mit Emoji-Picker (Unicode + Server-Emoji) speichern → Button zeigt Emoji
- [ ] Kategorie ändern/löschen → Panel-Buttons aktualisieren sich
- [ ] Panel-Kanal wechseln → altes Panel weg, neues im Zielkanal
## Post-Phase Ticket Support-Ping + Control-Panel (Status: implementiert)
### Abgeschlossen (Code)
- Support-Rollen: Member-Cache nachladen (`guild.members.fetch`), bis 50 Staff in Private Threads adden
- Ticket-Eröffnung pingt Support-Rollen (`allowedMentions.roles`)
- Control-Panel im Ticket: Claim, Close, User-Select Add/Remove (wie TempVoice-Pattern)
- Opener darf schließen; Claim/Add/Remove nur Support-Staff
- Prioritäts-Dropdown im Discord-Control-Panel (`ticket:ctrl:priority`)
- Robustheit: Unknown Channel (10003) beim Close nur noch debug; Unknown Member (10007) bei Add/Remove/Interactions abgefangen; Ticket-Commands nutzen `ephemeral()` flags
### Manuell testen
- [ ] Ticket öffnen (Thread) → Support-Rolle wird gepingt, Staff erscheinen im Thread
- [ ] Ticket öffnen (Channel) → Support-Rolle gepingt, Rolle hat Kanal-Zugriff
- [ ] Control-Panel: Claim / Close / User hinzufügen / entfernen
- [ ] Opener kann schließen; Nicht-Staff kann nicht claimen
## Post-Phase Offene Tickets im WebUI (Status: implementiert)
### Abgeschlossen (Code)
- Liste offener Tickets (`OPEN`/`CLAIMED`) auf der Tickets-Seite
- Aktionen: In Discord öffnen, Übernehmen, Schließen (optionaler Grund), Priorität setzen
- BullMQ-Job `ticketDashboardAction`; Shared Zod `TicketDashboard` / `TicketDashboardAction`
- API `GET …/tickets/open`, `POST …/tickets/open/[ticketId]/action`
### Manuell testen
- [ ] Ticket öffnen → erscheint in der WebUI-Liste
- [ ] Claim / Priority aus Dashboard → Nachricht im Ticket-Kanal
- [ ] Close aus Dashboard → Ticket verschwindet aus der Liste, Kanal/Thread wird geschlossen
- [ ] Refresh-Button aktualisiert die Liste
## Post-Phase Ticket Support-Rollen Speichern (Bugfix)
### Abgeschlossen (Code)
- Root cause: Kategorie-Save warf Fehler wenn Panel-Sync scheiterte → UI „Speichern fehlgeschlagen“, obwohl `supportRoleIds` schon in Postgres lagen
- `maybeSyncTicketPanel` fängt Sync-Fehler ab; Kategorie-CRUD bleibt erfolgreich
- Nach Speichern: Response inkl. `supportRoleIds` in die UI übernommen
- Zod: `supportRoleIds` jetzt `SnowflakeSchema[]`
### Manuell testen
- [ ] Kategorie: Support-Rolle wählen → Speichern → Erfolgstoast
- [ ] Seite neu laden → Rolle ist noch ausgewählt
- [ ] Neues Ticket → Support-Rolle wird gepingt / in Thread aufgenommen
## Post-Phase Ban/Kick-Reason Logging + Verification Fail Event (Status: implementiert)
### Abgeschlossen (Code)
- Root cause Ban/Kick-Reason im Log: Discord sendet Reasons nicht über Gateway (`ban.reason` war immer leer)
- `MEMBER_BAN` / Kick-`MEMBER_LEAVE` lesen Reason + Moderator jetzt aus dem Audit-Log
- Ephemere Success-Antworten für Ban/Kick/Timeout zeigen den Grund mit
- Neuer Log-Event-Typ `VERIFICATION_FAIL` (Shared + WebUI-Picker DE/EN)
- Alt-Check / Account-zu-jung schreiben ins Logging (`logVerificationFail`); auch Flag-only ohne Block
### Manuell testen
- [ ] `/kick` mit Reason → Log-Kanal (MEMBER_LEAVE) zeigt Reason + Moderator
- [ ] `/ban` mit Reason → Log-Kanal (MEMBER_BAN) zeigt Reason + Moderator; Bot braucht „Audit-Log anzeigen“
- [ ] Logging: `VERIFICATION_FAIL` einem Kanal zuordnen
- [ ] Alt-Check mit Block → User sieht Fehler-Ephemeral; Log-Kanal bekommt Embed
- [ ] Alt-Check nur Flag (ohne Block) → User wird verifiziert, Log trotzdem
## Post-Phase Tag Auto-Responder Cooldown (Status: implementiert)
### Abgeschlossen (Code)
- `Tag.cooldownSeconds` (Default 15, 0 = aus) inkl. Migration `20260725170000_tag_cooldown`
- Auto-Responder: kanalbezogener Redis-Cooldown, bei aktivem Cooldown stille Ignorierung (kein Spam)
- `/tag run`: nutzerbezogener Cooldown mit ephemeral Hinweis
- Dashboard Tags: Cooldown-Feld (DE/EN), Shared Zod + WebUI Persistenz
### Manuell testen
- [ ] Tag mit Trigger-Wort: zweimal schnell tippen → nur erste Antwort
- [ ] Cooldown im Dashboard auf 0 setzen → Bot antwortet wieder sofort
- [ ] `/tag run` während Cooldown → ephemeral mit Restzeit
## Post-Phase Server-Stats WebUI Refresh (Bugfix)
### Abgeschlossen (Code)
- Root cause: Dashboard speicherte nur `StatsConfig`, ohne Discord-Kanal-Rename; Cron erst alle 10 Min. Online-Count ohne `withCounts` oft 0 (kein GuildPresences-Intent)
- BullMQ-Job `statsRefreshGuild` (Queue `stats`); WebUI triggert nach Speichern bei enabled + mind. einem Kanal
- Guild-Fetch mit `withCounts: true` für `approximatePresenceCount` / Member-Approx
- `/stats setup|refresh` refresht nur den aktuellen Guild (nicht alle)
### Manuell testen
- [ ] WebUI: Stats aktivieren, Voice-Kanäle setzen, Speichern → Kanalnamen zeigen `{count}`-Werte innerhalb weniger Sekunden
- [ ] Toggle aus → Speichern → keine weiteren Renames (Cron überspringt disabled)
- [ ] Online-Kanal zeigt sinnvolle Zahl (nicht dauerhaft 0)
- [ ] Bot ohne „Kanäle verwalten“ → Config gespeichert; Bot-Log warnt; nach Permission-Fix erneut speichern