Files
d4rkbot/frontend/src/pages/BotCommands.jsx
T
D4rkst3randClaude Opus 5 89f6dfcd29
Deploy / check (push) Canceled after 0s
Deploy / deploy (push) Canceled after 0s
Radio: Webradio im Sprachkanal, 44 Sender zum Durchschalten
/radio holt den Bot in den Sprachkanal und postet eine Tafel: Auswahlmenue
mit allen Sendern, Stopp, und ein Embed mit dem laufenden Titel, das sich
alle 30 Sekunden selbst nachfuehrt.

Der Titel kommt aus dem Icecast-Strom selbst (Icy-MetaData). Bewusst so und
nicht ueber eine Sender-API: das funktioniert bei jedem Strom, den jemand
eintraegt, und nicht nur bei den drei, die ich kenne. Leere Titel-Bloecke
heissen "unveraendert", deshalb werden bis zu drei gelesen.

Die Tonkette braucht keine Opus-Bibliothek: ffmpeg holt das MP3 und gibt
direkt Ogg/Opus aus, prism-media packt nur noch aus. Gemessen: 600 Pakete in
fuenf Sekunden. Sonst muesste @discordjs/opus mit ins Image, und das will
gebaut werden. Neu im Image ist nur ffmpeg.

44 RauteMusik-Sender sind ab Werk eingetragen — dieselben, die im LS25 und
ETS als Bordradio laufen. Die Liste ist nicht abgeschrieben, sondern
gemessen: jede Adresse einzeln angefragt, der Name kommt aus icy-name.
Doppelgaenger sind raus (deutschrap-charts und wackenradio liefern denselben
Strom wie deutschrap und metal). Eingetragen wird einmal, solange die Liste
leer ist — wer loescht, behaelt es geloescht.

Ein Auswahlmenue fasst nur 25 Eintraege. Bei 44 Sendern haette ein Deckel
die letzten 19 stillschweigend verschluckt; stattdessen mehrere Menues,
benannt nach ihrem ersten und letzten Sender.

Der Bot geht raus, wenn der Kanal leer ist, und kommt nach einem Neustart in
den Kanal zurueck — sonst beendet jeder Deploy die Musik endgueltig.

Kein YouTube. Genau daran sind Groovy und Rythm gestorben, und was davon
uebrig ist, ist ein Dauerlauf gegen kaputte Extraktoren.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 11:40:15 +02:00

74 lines
3.0 KiB
React

// Übersicht aller Slash-Befehle des Bots.
import { useEffect } from 'react';
import { IconLock } from '../icons.jsx';
import { useT } from '../i18n.jsx';
// Befehlsname bleibt wie er ist — nur die Erklärung wird übersetzt.
const GROUPS = [
['commands.g.everyone', false, [
['/rank', 'commands.rank'],
['/wunsch idee:…', 'commands.wunsch'],
['/bug titel:… beschreibung:…', 'commands.bug'],
['/geburtstag setzen tag:… monat:…', 'commands.bdaySet'],
['/geburtstag entfernen', 'commands.bdayDel'],
['/remind dauer:… text:…', 'commands.remind'],
['/tag name:…', 'commands.cmdTag'],
['/ping', 'commands.ping'],
['/umfrage frage:… a:… b:…', 'commands.umfrage'],
['/radio', 'commands.radio'],
]],
['commands.g.mod', true, [
['/warn user:… grund:…', 'commands.warn'],
['/warns user:…', 'commands.warns'],
['/timeout user:… dauer:…', 'commands.timeout'],
['/purge anzahl:…', 'commands.purge'],
]],
['commands.g.setup', true, [
['/raid status | an | aus', 'commands.raid'],
['/giveaway preis:… dauer:… gewinner:…', 'commands.giveaway'],
['/ticket-setup', 'commands.ticketSetup'],
['/playtester-setup', 'commands.playtesterSetup'],
['/devlog-backfill', 'commands.devlogBackfill'],
['/galerie-backfill', 'commands.galerieBackfill'],
]],
];
export default function BotCommands() {
const { t } = useT();
useEffect(() => { window.scrollTo(0, 0); }, []);
return (
<>
<section className="page-header">
<div className="page-bg-text">BEFEHLE</div>
<div className="page-header-grid" aria-hidden="true" />
<div className="page-header-content">
<div className="page-tag">{t('commands.tag')}</div>
<h1 className="page-title">{t('commands.title')}</h1>
<p className="page-subtitle">{t('commands.subtitle')}</p>
</div>
</section>
<section className="content">
{GROUPS.map(([groupKey, restricted, commands]) => (
<div className="roadmap-group" key={groupKey}>
<h2 className="settings-title">
// {t(groupKey)}
{restricted && <IconLock size={13} />}
</h2>
<div className="cmd-list">
{commands.map(([name, descKey]) => (
<div className="cmd-row" key={name}>
<code className="cmd-name">{name}</code>
<span className="cmd-desc">{t(descKey)}</span>
</div>
))}
</div>
</div>
))}
<p className="notice">{t('commands.note')}</p>
</section>
</>
);
}