Ein browserbasiertes Twitch-Chat- und Moderations-Tool. StreamDesk verbindet sich per OAuth sicher mit deinem Twitch-Account, zeigt den Chat mehrerer Channels gleichzeitig in Tabs an und bietet Moderatoren direkten Zugriff auf Timeouts, Bans, Nachrichten-Löschung, Umfragen, Predictions und detaillierte User-Infos – alles in einem schlanken Web-Interface ohne zusätzliche Installation auf Zuschauerseite.
🐙 by Epyx
- Sicherer Login ausschließlich über Twitch-OAuth (Authorization Code Flow) – es werden niemals Zugangsdaten manuell eingegeben oder gespeichert
- Multi-Channel-Chat mit Tabs, Lese-Badges für ungelesene Nachrichten und pro-Channel-Verlauf
- Live-Moderation: Timeout (inkl. individueller Dauer), Ban/Unban, Nachrichten löschen – direkt aus dem Chat oder dem User-Info-Panel
- User-Info-Panel mit Account-Alter, Follow-Dauer, Abo-Status, Badges, Ban-/Timeout-Status, letzten Nachrichten des Users und einem direkten Link zum Twitch-Profil
- Live-Rollen-Erkennung: Mods/VIPs/Broadcaster werden farblich unterschieden, Beförderungen/ Degradierungen (Mod) kommen in Echtzeit an
- Event-Log für Ankündigungen, Subs/Resubs/Gift-Subs, Cheers, Raids, Bans/Timeouts, gelöschte Nachrichten sowie Join/Part-Events – inkl. persistenter Historie pro Channel
- Umfragen & Predictions: erstellen, live mitverfolgen und verwalten (Broadcaster/Mod)
- Emote-Unterstützung: native Twitch-Emotes sowie FFZ, BetterTTV und 7TV
- Wortfilter pro Channel zur farblichen Hervorhebung bestimmter Begriffe
- Bot-Erkennung bekannter Chat-Bots (z.B. Nightbot, StreamElements) in der User-Liste
- Lokale Persistenz: Nachrichten, Events und User-Listen bleiben nach einem Reload erhalten (localStorage), inkl. Speicher-Anzeige und manueller Aufräum-Funktion
- Automatischer Reconnect von WebSocket und Twitch-IRC-Verbindung mit exponentiellem Backoff
- XSS-sichere Darstellung: sämtlicher Nutzer-generierter Text (Chat, Ankündigungen, Sub-/Cheer-Nachrichten, gelöschte Nachrichten) läuft durch dieselbe escaping- und Link-/Emote-Erkennungs-Pipeline
| Bereich | Technologie |
|---|---|
| Backend | Node.js, Express, ws, tmi.js |
| Frontend | Vanilla HTML/CSS/JavaScript (keine Frameworks, keine Build-Tools) |
| APIs | Twitch Helix API, Twitch IRC (via tmi.js), FrankerFaceZ, BetterTTV, 7TV |
Es gibt bewusst keine Frontend-Frameworks, Bundler oder Build-Schritte – das Projekt lässt sich direkt ausführen und ist leicht nachvollziehbar.
project/
├── server.js # Einstiegspunkt: Express/WebSocket-Server, verdrahtet alle Module
├── src/ # Backend-Module
│ ├── config.js # Lädt/prüft Umgebungsvariablen (.env)
│ ├── logger.js # Strukturiertes, zeitgestempeltes Server-Logging
│ ├── helpers.js # Kleine geteilte Helfer (sendToClient, Emote-Parsing, Farben)
│ ├── helixClient.js # Generischer Wrapper um die Twitch-Helix-API (inkl. Pagination)
│ ├── twitchServices.js # Bot-Liste, Badges, Chatters/Mods/VIPs, Polls/Predictions, Emotes
│ ├── userRoster.js # Aufbau/Pflege der Mod-/VIP-/User-Listen, Timeouts, Sub-Tiers
│ ├── clientState.js # Zustands-Objekt pro WebSocket-Verbindung
│ ├── tmiEvents.js # Alle tmi.js-Event-Handler (Nachrichten, Bans, Subs, Raids, ...)
│ ├── oauth.js # OAuth-Flow, Login, /callback-Route, State-Persistenz
│ └── wsHandlers.js # WebSocket-Nachrichten-Handler (Channel, Mod-Aktionen, Polls, ...)
└── public/ # Frontend (statisch ausgeliefert)
├── index.html # Markup
├── css/
│ └── style.css # Gesamtes Styling
└── js/
├── state.js # Globaler State, DOM-Referenzen, Storage-Helfer
├── websocket.js # WebSocket-Verbindung, Reconnect, Server-Nachrichten-Routing
├── channels.js # Channel-/Tab-Verwaltung
├── chat.js # Chat-Rendering, Emote-/Link-Pipeline
├── sidebar.js # User-Liste, Event-Log, Poll-/Prediction-Anzeige
├── userPanel.js # User-Info-Panel, Mod-Aktionen
└── main.js # Event-Wiring & Bootstrap
- Node.js ≥ 18
- Ein Twitch-Account
- Eine registrierte Twitch-Anwendung (siehe unten)
git clone https://github.com/Epyxx/StreamDesk.git
cd StreamDesk
npm install- Öffne die Twitch Developer Console und klicke auf Register Your Application.
- Vergib einen Namen (z.B.
StreamDesk – Lokale Instanz). - Trage als OAuth Redirect URL ein:
http://localhost:3000/callback - Kategorie:
Application Integration(oder eine passende Kategorie deiner Wahl). - Nach dem Erstellen erhältst du eine Client-ID; über New Secret generierst du ein Client-Secret.
Kopiere .env.example zu .env und trage deine Zugangsdaten ein:
cp .env.example .envTWITCH_CLIENT_ID=deine_client_id
TWITCH_CLIENT_SECRET=dein_client_secret
TWITCH_REDIRECT_URI=http://localhost:3000/callback
PORT=3000
⚠️ Die.env-Datei enthält geheime Zugangsdaten und darf niemals committet werden (sie ist bereits über.gitignoreausgeschlossen).
Wird StreamDesk über einen Reverse Proxy erreichbar gemacht (z.B. https://example.com/streamdesk
statt http://localhost:3000), muss TWITCH_REDIRECT_URI exakt auf die öffentlich
erreichbare Callback-URL zeigen:
TWITCH_REDIRECT_URI=https://example.com/streamdesk/callbackDiese URL muss zusätzlich identisch als OAuth Redirect URL in der
Twitch Developer Console hinterlegt sein – Twitch leitet
den Browser nach dem Login exakt an die dort registrierte (und hier konfigurierte) Adresse
weiter. Bleibt TWITCH_REDIRECT_URI auf http://localhost:3000/callback stehen, während die App
öffentlich unter einer anderen Adresse läuft, landet der Login-Redirect fälschlicherweise auf
localhost statt auf der öffentlichen URL.
Der Reverse Proxy muss außerdem WebSocket-Upgrades (nicht nur normale HTTP-Requests) für denselben Pfad an den Node-Prozess durchreichen, da die Live-Verbindung (Chat, Events, ...) über WebSocket läuft.
npm startFür Entwicklung mit automatischem Neustart bei Dateiänderungen:
npm run devAnschließend die Anwendung unter http://localhost:3000 öffnen und über „Mit Twitch anmelden" einloggen.
📦 Docker Hub: hub.docker.com/r/epyx/streamdesk
StreamDesk ist zustandslos auf Server-Seite (der eigentliche Chat-/Event-Verlauf liegt im
localStorage des Browsers) – ein Container benötigt daher kein Volume für persistente Daten,
lediglich die drei Twitch-Umgebungsvariablen.
docker run -d \
--name streamdesk \
--restart unless-stopped \
-p 3000:3000 \
-e TWITCH_CLIENT_ID=deine_client_id \
-e TWITCH_CLIENT_SECRET=dein_client_secret \
-e TWITCH_REDIRECT_URI=https://example.com/streamdesk/callback \
epyx/streamdesk:latestOder mit einer .env-Datei statt einzelner -e-Flags:
docker run -d --name streamdesk --restart unless-stopped -p 3000:3000 --env-file .env epyx/streamdesk:latestgit clone https://github.com/Epyxx/StreamDesk.git
cd StreamDesk
docker build -t streamdesk .
docker run -d --name streamdesk --restart unless-stopped -p 3000:3000 --env-file .env streamdeskdocker compose up -d --build(docker-compose.yml liegt bereits im Repo – baut standardmäßig lokal aus dem Quellcode; die
image:-Zeile darin kann alternativ auskommentiert werden, um stattdessen direkt
epyx/streamdesk:latest von Docker Hub zu verwenden.)
Läuft der Container – wie im Abschnitt oben beschrieben – hinter einem Reverse Proxy unter einem
Unterverzeichnis, muss dieser sowohl normale HTTP-Requests als auch WebSocket-Upgrades an
http://<container-host>:3000/ weiterleiten. Zwei Beispiele:
Apache (≥ 2.4.47, benötigt a2enmod proxy proxy_http proxy_wstunnel headers):
<Location /streamdesk/>
ProxyPass http://127.0.0.1:3000/ upgrade=websocket
ProxyPassReverse http://127.0.0.1:3000/
</Location>nginx:
location /streamdesk/ {
proxy_pass http://127.0.0.1:3000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}In beiden Fällen TWITCH_REDIRECT_URI (siehe oben) exakt auf die öffentliche
.../streamdesk/callback-URL setzen und identisch in der Twitch Developer Console hinterlegen.
.github/workflows/docker-publish.yml baut das Image bei jedem Push auf main sowie bei
Git-Tags (v*) automatisch für linux/amd64 und linux/arm64 und veröffentlicht es als
epyx/streamdesk auf Docker Hub. Bei Pushes auf main wird zusätzlich die Kurzbeschreibung und
die ausführliche Overview-Seite des Docker-Hub-Repositories aus docker/README.md
aktuell gehalten. Dafür müssen einmalig zwei Secrets im Repository hinterlegt werden
(Settings → Secrets and variables → Actions → New repository secret):
| Secret | Wert |
|---|---|
DOCKERHUB_USERNAME |
Docker-Hub-Benutzername (epyx) |
DOCKERHUB_TOKEN |
Docker-Hub Access Token (nicht das Account-Passwort) – erstellbar unter hub.docker.com/settings/security |
| Scope | Zweck |
|---|---|
chat:read, chat:edit |
Chat lesen und schreiben |
channel:moderate, moderation:read |
Timeout/Ban/Nachrichten löschen |
channel:read:subscriptions |
Abo-Informationen im User-Info-Panel |
moderator:read:followers |
Follow-Dauer im User-Info-Panel |
moderator:read:chatters |
Aktuelle Chatter-Liste (Mods/VIPs/User) |
channel:read:vips |
VIP-Liste (nur als Broadcaster nutzbar, siehe unten) |
channel:read:polls, channel:manage:polls |
Umfragen anzeigen/erstellen/beenden |
channel:read:predictions, channel:manage:predictions |
Predictions anzeigen/verwalten |
Diese Punkte sind keine Bugs, sondern Grenzen der öffentlichen Twitch-API, mit denen das Tool bewusst und transparent umgeht:
moderation/moderatorsundchannels/vipslassen sich laut Twitch ausschließlich mit dem Token des Broadcasters abfragen – selbst ein regulärer Moderator bekommt hier eine Berechtigungs-Absage. In fremden Channels ohne eigene Mod-Rechte fällt StreamDesk daher auf die IRC-NAMES-Liste und zuletzt gesehene Chat-Badges zurück.- Für VIP-Beförderungen gibt es kein Echtzeit-IRC-Signal (anders als bei Mods, die per
MODE +o/-osofort erkannt werden) – ein neuer VIP-Status wird erst mit der nächsten Chat-Nachricht des Users oder dem nächsten periodischen Roster-Abgleich sichtbar. - Es existiert keine öffentliche Twitch-API, über die Zuschauer bei Umfragen/Predictions abstimmen können – StreamDesk bietet daher bewusst nur Anzeige und Verwaltung, keine Abstimm-Funktion.
- Login ausschließlich über den offiziellen Twitch-OAuth-Flow – StreamDesk sieht oder speichert niemals dein Passwort.
- Sämtlicher nutzergenerierter Text (Chatnachrichten, Ankündigungen, Sub-/Cheer-Nachrichten, gelöschte Nachrichten) wird clientseitig konsequent escaped, bevor er ins DOM eingefügt wird; Links und Emotes werden über eine dedizierte, token-basierte Pipeline erkannt – nie über direkte HTML-Interpolation von Nutzereingaben.
- Zugangsdaten (
TWITCH_CLIENT_SECRETetc.) werden ausschließlich serverseitig über Umgebungsvariablen verwaltet und niemals an den Client übertragen.
Dieses Projekt steht unter der MIT-Lizenz.
- tmi.js – Twitch-IRC-Client
- FrankerFaceZ, BetterTTV, 7TV – Drittanbieter-Emotes
- Entwickelt von Epyx