+ );
+}
diff --git a/frontend/src/icons.jsx b/frontend/src/icons.jsx
index 51c543e..6662865 100644
--- a/frontend/src/icons.jsx
+++ b/frontend/src/icons.jsx
@@ -43,6 +43,7 @@ export const IconBrand = make(Palette);
export const IconFeeds = make(Megaphone);
export const IconCommunity = make(Users);
export const IconRoles = make(Shield);
+export const IconShield = make(Shield);
export const IconSupport = make(Ticket);
export const IconApplications = make(FileText);
export const IconComposer = make(Pencil);
diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx
index 988887a..11a6edb 100644
--- a/frontend/src/main.jsx
+++ b/frontend/src/main.jsx
@@ -2,6 +2,8 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App.jsx';
+import BotApp from './BotApp.jsx';
+import { IS_BOT } from './site.js';
import '@fontsource/bebas-neue';
import '@fontsource/barlow-condensed/300.css';
import '@fontsource/barlow-condensed/400.css';
@@ -9,10 +11,12 @@ import '@fontsource/barlow-condensed/600.css';
import '@fontsource/share-tech-mono';
import './style.css';
+// Zwei Gesichter, eine Anwendung: Der Server sagt über data-site am ,
+// ob gerade die Produktseite oder der Community-Hub gefragt ist.
ReactDOM.createRoot(document.getElementById('root')).render(
-
+ {IS_BOT ? : }
);
diff --git a/frontend/src/pages/BotCommands.jsx b/frontend/src/pages/BotCommands.jsx
new file mode 100644
index 0000000..3885a9b
--- /dev/null
+++ b/frontend/src/pages/BotCommands.jsx
@@ -0,0 +1,74 @@
+// Übersicht aller Slash-Befehle des Bots.
+import { useEffect } from 'react';
+import { IconLock } from '../icons.jsx';
+
+const GROUPS = [
+ ['Für alle', [
+ ['/rank', 'Zeigt deinen Rang, deine XP und den Fortschritt zum nächsten Level.'],
+ ['/wunsch idee:…', 'Reicht einen Feature-Wunsch ein — landet im Voting-Kanal und auf der Roadmap.'],
+ ['/bug titel:… beschreibung:…', 'Meldet einen Fehler als Issue im Repository. Wird er geschlossen, bekommst du eine Nachricht.'],
+ ['/geburtstag setzen tag:… monat:…', 'Trägt deinen Geburtstag ein. Der Bot gratuliert morgens und vergibt eine Rolle für den Tag.'],
+ ['/geburtstag entfernen', 'Trägt ihn wieder aus.'],
+ ['/remind dauer:… text:…', 'Erinnert dich per Direktnachricht — etwa „2h" oder „30m".'],
+ ['/tag name:…', 'Ruft einen gespeicherten Textbaustein ab, mit Vorschlägen beim Tippen.'],
+ ['/ping', 'Lebenszeichen des Bots.'],
+ ]],
+ ['Moderation', [
+ ['/warn user:… grund:…', 'Verwarnt jemanden, schickt eine Nachricht und schreibt es ins Protokoll.'],
+ ['/warns user:…', 'Zeigt die Verwarnungs-Historie.'],
+ ['/timeout user:… dauer:…', 'Setzt eine Auszeit.'],
+ ['/purge anzahl:…', 'Löscht mehrere Nachrichten auf einmal.'],
+ ]],
+ ['Einrichtung', [
+ ['/giveaway preis:… dauer:… gewinner:…', 'Startet eine Verlosung mit Teilnahme-Knopf und automatischer Ziehung.'],
+ ['/ticket-setup', 'Postet den Knopf, über den Support-Tickets als private Threads entstehen.'],
+ ['/playtester-setup', 'Postet den Bewerbungs-Knopf für das Playtester-Programm.'],
+ ['/devlog-backfill', 'Holt ältere Devlog-Nachrichten nachträglich ins Archiv.'],
+ ['/galerie-backfill', 'Dasselbe für den Screenshot-Kanal.'],
+ ]],
+];
+
+export default function BotCommands() {
+ useEffect(() => { window.scrollTo(0, 0); }, []);
+
+ return (
+ <>
+
+
BEFEHLE
+
+
+
// Referenz
+
Befehle
+
+ Alle Slash-Befehle im Überblick. Vieles läuft zusätzlich über Knöpfe
+ direkt in den Nachrichten.
+
+ Moderations- und Einrichtungs-Befehle sind Mitgliedern mit den passenden
+ Discord-Rechten vorbehalten.
+
+
+ >
+ );
+}
diff --git a/frontend/src/pages/BotLanding.jsx b/frontend/src/pages/BotLanding.jsx
new file mode 100644
index 0000000..3039493
--- /dev/null
+++ b/frontend/src/pages/BotLanding.jsx
@@ -0,0 +1,119 @@
+// Produktseite des Bots: was er kann, wie er aussieht, wo es weitergeht.
+// Bewusst ohne „Einladen"-Button — der Bot ist auf einen Server ausgelegt.
+import { useEffect, useState } from 'react';
+import { Link } from 'react-router-dom';
+import { apiGet } from '../api.js';
+import {
+ IconDevlog, IconLevel, IconRoles, IconServer, IconSupport, IconGiveaway,
+ IconBirthday, IconShield, IconMagic, IconKey, IconArrowRight, IconBot,
+} from '../icons.jsx';
+
+const HIGHLIGHTS = [
+ [IconDevlog, 'Devlogs & Feeds', 'Entwicklungs-Berichte landen als gebrandetes Embed im Discord und werden gleichzeitig durchsuchbar archiviert. Commits und Releases aus Gitea genauso.'],
+ [IconServer, 'Server-Monitor', 'Über 300 Spiele per gamedig, dazu FiveM und einfache Erreichbarkeits-Checks. Ein Live-Embed pro Server, Alarm bei Ausfall, Spielerzahl in der Bot-Präsenz.'],
+ [IconLevel, 'Level & Aktivität', 'XP fürs Mitreden mit Rollen-Belohnungen, öffentliche Bestenliste und Aktivitäts-Statistik des Servers.'],
+ [IconRoles, 'Rollen-Menüs', 'Selbstbedienung per Knopfdruck — im Discord und im Browser. Farben pro Knopf, wahlweise exklusiv.'],
+ [IconSupport, 'Support & Kontakt', 'Tickets als private Threads mit Gesprächsprotokoll, dazu Modmail: eine Direktnachricht an den Bot landet beim Team.'],
+ [IconShield, 'Moderation', 'Verwarnungen mit Verlauf, Auszeiten, Aufräumen — alles im Mod-Log, dazu Protokoll für Beitritte, Namens- und Rollenwechsel.'],
+ [IconGiveaway, 'Verlosungen', 'Teilnahme per Knopf, automatische Ziehung, Neuauslosung wenn sich niemand meldet.'],
+ [IconBirthday, 'Geburtstage', 'Wer mag, trägt seinen Tag ein — der Bot gratuliert morgens und vergibt eine Rolle für den Tag.'],
+ [IconMagic, 'Willkommens-Karten', 'Neue Mitglieder bekommen ein gerendertes Bild mit Avatar und Mitglieds-Nummer.'],
+];
+
+export default function BotLanding() {
+ const [status, setStatus] = useState(null);
+ const [hub, setHub] = useState(null);
+
+ useEffect(() => {
+ apiGet('/api/legal').then((d) => setHub(d)).catch(() => {});
+ apiGet('/api/serverstats').then(setStatus).catch(() => {});
+ window.scrollTo(0, 0);
+ }, []);
+
+ return (
+ <>
+
+
BOT
+
+
+
// Selbst gebaut, keine Paywall
+
+ Ein Discord-Bot, der alles macht
+
+
+ Devlogs, Level, Moderation, Server-Monitoring, Verlosungen, Tickets —
+ was sonst drei Bots mit Abo-Modell erledigen, läuft hier in einem,
+ mit eigenem Webinterface.
+
+
+
+ Alle Funktionen
+
+
+ Befehle ansehen
+
+
+
+
+
+
+
+
+ 16
+ Slash-Befehle
+
+
+ 300+
+ Spiele im Monitor
+
+
+ 0 €
+ Kosten, keine Premium-Stufen
+
+
+
+
// Was er kann
+
+ {HIGHLIGHTS.map(([Icon, title, text]) => (
+
+
+ {title}
+ {text}
+
+ ))}
+
+
+
+
// Für Entwickler
+
+
+
+ API & Single Sign-On
+
+ Eigene Skripte posten über die API v1 mit Bearer-Schlüsseln und Bereichs-Rechten.
+ Andere Dienste nutzen den Discord-Login des Bots mit und bekommen die Rollen
+ gleich mitgeliefert.
+
+
+
+
+ Läuft überall
+
+ Node und SQLite in einem Container, ein Volume für die Daten.
+ Nächtliche Sicherungen inklusive — von der Datenbank und allen Repositories.
+
+
+
+
+
+ {hub?.publicUrl && (
+
+ Der Bot läuft für die D4RKST3R-Community
+ {status?.members ? ` mit ${status.members} Mitgliedern` : ''} —
+ {' '}zum Community-Hub.
+
+ Community-Hub und Bot-Produktseite auf getrennte Adressen legen. Solange
+ beide Felder leer sind, läuft alles unverändert auf der öffentlichen URL.
+ Sind sie gesetzt, leitet der Server falsch aufgerufene Seiten automatisch
+ auf die richtige Adresse um.
+