From 4d1b0a956d34ab48da0cfe46add0fe49dcba9c51 Mon Sep 17 00:00:00 2001 From: D4rkst3r Date: Thu, 23 Jul 2026 13:43:27 +0200 Subject: [PATCH] Ticket-System, Web-Composer und Nachrichten-Bearbeitung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- README.md | 2 + frontend/src/pages/Settings.jsx | 74 ++++++++++++++++++++++++++++ frontend/src/style.css | 14 ++++++ src/bot/client.js | 84 ++++++++++++++++++++++++++++++-- src/bot/commands/ticket-setup.js | 42 ++++++++++++++++ src/db.js | 20 ++++++++ src/runtime-settings.js | 5 ++ src/web/api-v1.js | 25 ++++++++++ src/web/api.js | 48 +++++++++++++++++- 9 files changed, 310 insertions(+), 4 deletions(-) create mode 100644 src/bot/commands/ticket-setup.js diff --git a/README.md b/README.md index 38dbea9..a2ccd7d 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ SQLite (better-sqlite3, FTS5), Docker Multi-Stage — deploybar als Portainer-St | ⭐ **Starboard** | Nachrichten mit genug ⭐-Reaktionen landen automatisch im Best-of-Kanal | | 📸 **Screenshot-Galerie** | Bilder aus dem Screenshot-Kanal → öffentliche `/galerie` (lokal gespeichert); Historie via `/galerie-backfill` | | 📬 **Modmail** | DM an den Bot → Thread im privaten Staff-Kanal; Antworten im Thread gehen als DM zurück | +| 🎫 **Tickets** | `/ticket-setup` postet den Button; Klick öffnet einen privaten Thread (ein offenes Ticket pro User, Schließen-Button) | | 👋 **Willkommens-Embed** | Begrüßung neuer Member im Brand-Look (braucht Server-Members-Intent) | | 📋 **Mod-Log** | Gelöschte/bearbeitete Nachrichten in einen privaten Log-Kanal | | 🎮 **Server-Monitor** | FiveM-kompatible Game-Server (/dynamic.json): Live-Status-Embed + Spielerzahl in der Bot-Presence | @@ -202,6 +203,7 @@ Authorization: Bearer d4rk_ | Endpoint | Scope | Body / Antwort | |---|---|---| | `POST /api/v1/message` | `message` | `{ channel_id, content?, embed? }` → postet als Bot (Embed: title, description, color, url, image, thumbnail, footer, fields) | +| `PATCH /api/v1/message` | `message` | `{ channel_id, message_id, content?, embed? }` → eigenen Bot-Post bearbeiten | | `POST /api/v1/dm` | `dm` | `{ user_id, content }` → Direktnachricht | | `POST /api/v1/roles` | `roles` | `{ user_id, role_id, action: "add"\|"remove" }` → Rolle vergeben (z. B. Shop-Kauf → Kunden-Rolle) | | `GET /api/v1/member/:id` | `read` | Member-Info: Name, Rollen, Beitritt — für Login-/Berechtigungs-Checks | diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx index 54893f8..b80a618 100644 --- a/frontend/src/pages/Settings.jsx +++ b/frontend/src/pages/Settings.jsx @@ -28,6 +28,8 @@ export default function Settings({ me }) { const [feedback, setFeedback] = useState(null); // Community const [playtesters, setPlaytesters] = useState([]); + // Composer + const [composer, setComposer] = useState({ channel: '', title: '', text: '', messageId: '' }); // API-Keys const [keys, setKeys] = useState([]); const [newKeyName, setNewKeyName] = useState(''); @@ -80,6 +82,25 @@ export default function Settings({ me }) { } } + async function sendCompose() { + if (!composer.channel || !composer.text.trim()) { + flash('✗ Kanal und Text nötig'); + return; + } + try { + const res = await apiPost('/api/compose', { + channel_id: composer.channel, + message_id: composer.messageId.trim() || undefined, + embed_title: composer.title.trim() || undefined, + content: composer.text, + }); + flash(res.edited ? '✓ Aktualisiert' : `✓ Gesendet (ID ${res.message_id})`); + if (!res.edited) setComposer({ ...composer, messageId: res.message_id }); + } catch { + flash('✗ Senden fehlgeschlagen'); + } + } + async function sendTest(target) { try { await apiPost(`/api/settings/test/${target}`); @@ -400,6 +421,59 @@ export default function Settings({ me }) { {channelOptions} +
+ + +
+ + + {/* Composer */} +
+

// Post verfassen

+
+ + +
+
+ + setComposer({ ...composer, title: e.target.value })} + /> +
+
+ +