// Funktionsliste der Produktseite. Die Einträge kommen aus /api/features und // damit aus demselben Register wie die Module im Panel — die Seite kann also // nicht veralten, wenn eine Funktion dazukommt. import { useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; import { apiGet } from '../api.js'; import { IconMessage, IconTeam, IconServer, IconApi, IconPages, IconBrand, IconBot, IconArrowRight, } from '../icons.jsx'; import { moduleIcon } from '../module-icons.jsx'; // Was kein Discord-Modul ist, sondern zum Drumherum gehört const EXTRAS = [ [IconBrand, 'Webinterface im eigenen Look', 'Jede Funktion wird im Browser eingestellt — keine Konfigurationsdatei, kein Neustart. ' + 'Farben, Bot-Avatar und Banner gehören dazu, und andere Dienste können denselben Look mitbenutzen.'], [IconMessage, 'Texte selbst schreiben', 'Begrüßung, Level-Ansage, Ticket-Texte — alles, was der Bot nach außen schreibt, ' + 'lässt sich im Panel ändern, mit Platzhaltern für Name, Zahl und Link.'], [IconApi, 'API & Single Sign-On', 'Eigene Skripte posten über die API v1 mit Bearer-Schlüsseln und Bereichs-Rechten. ' + 'Andere Dienste nutzen den Discord-Login mit und bekommen die Rollen gleich mitgeliefert.'], [IconPages, 'Eigene Seiten', 'Regeln, Über uns, FAQ — in Markdown geschrieben, mit einem Klick veröffentlicht ' + 'und automatisch im Menü.'], [IconTeam, 'Rechte fürs Team', 'Team-Mitglieder bekommen gezielten Zugriff auf einzelne Bereiche. ' + 'Wer was geändert hat, steht im Protokoll.'], [IconServer, '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.'], ]; export default function BotFeatures() { const [data, setData] = useState(null); useEffect(() => { apiGet('/api/features').then(setData).catch(() => setData({ groups: [], features: [] })); window.scrollTo(0, 0); }, []); const total = data ? data.features.length + EXTRAS.length : null; return ( <>
FEATURES
{!data &&

Lade …

} {data?.groups.map((group) => { const items = data.features.filter((f) => f.group === group.id); if (items.length === 0) return null; return (

// {group.label}

{items.map((f) => { const Icon = moduleIcon(f.id); return (
{f.name}

{f.desc}

); })}
); })} {data && (

// Drumherum

{EXTRAS.map(([Icon, name, desc]) => (
{name}

{desc}

))}
)} {data && (

Gebaut, weil drei Bots mit je eigenem Abo für eine Community weder übersichtlich noch günstig sind. Der Quelltext liegt offen, der Betrieb kostet einen Container.

Befehle ansehen Zur Übersicht
)}
); }