Files
abdulive/docs/02-twitch-integration.md
T
ZeasyAlexandClaude Opus 5 d07c18441b S1: Einrichtungs-Assistent, Twitch-Auth, Datenbank und Chat-Aufnahme
Zugangsdaten kommen jetzt aus der App statt aus einer .env: ein
Einrichtungs-Assistent auf http://localhost:4792/setup nimmt Client-ID,
Secret und OBS-Passwort entgegen, der OAuth-Rueckruf laeuft auf denselben
Server. Gespeichert wird verschlueselt (AES-256-GCM) in einem Volume,
bewusst nicht in der Datenbank - die wird auf die Windows-Seite gesichert.
Siehe ADR-0005.

Technikbasis vereinfacht: Node 24 bringt node:sqlite, WebSocket, fetch und
die Ausfuehrung von TypeScript mit. Das Projekt hat damit null
Laufzeitabhaengigkeiten und keinen Build-Schritt; pnpm-Workspaces und die
Paketmanifeste sind entfallen, Importe laufen ueber die imports-Map der
Wurzel-package.json. erasableSyntaxOnly verhindert Syntax, die das Type
Stripping stillschweigend entfernen wuerde. Siehe Nachtrag 2 in ADR-0001.

Enthalten:
- packages/core: Config, Layout, Logger mit Secret-Maskierung, Retry, YAML
- packages/db: Migrationen, Repositories, alle SQL-Anweisungen an einem Ort
- packages/twitch: Secret-Speicher, OAuth, Helix mit Token-Bucket, IRC
- apps/ingest: Daemon, Einrichtungsseite, Spike-Erkennung
- 20 Tests (IRC-Parser, Spike-Detektor, Datenbankschicht)

Der Chat verbindet sich anonym, solange nichts hinterlegt ist - damit laesst
sich die Datenaufnahme pruefen, bevor die Twitch-Apps existieren.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VTGjd92LsDDWF3frd9FTB
2026-09-09 09:56:55 +02:00

6.0 KiB
Raw Blame History

Twitch-Integration

Zwei Identitäten

Es werden zwei getrennte Twitch-Apps/Tokens gebraucht:

  1. Broadcaster-Token (Konto abdulive_) — für alles, was den eigenen Kanal betrifft: Clips erstellen, Titel ändern, Raids starten, EventSub abonnieren, Analytics lesen.
  2. Bot-Token (eigenes Bot-Konto, z. B. abdulive_bot) — für Chat-Nachrichten. Trennung, damit Bot-Nachrichten als Bot erkennbar sind und der Broadcaster- Token nicht im Chat-Client hängt.

Client-IDs, Client-Secrets und beide Tokens liegen verschlüsselt in /db/secrets.json im Docker-Volume und werden über Refresh Tokens automatisch erneuert (packages/twitch/secrets.ts). Eingegeben werden sie im Einrichtungs-Assistenten unter http://localhost:4792/setup, nicht in einer .env und nie im Repo — siehe ADR-0005.

Scopes

Scope Wofür Token
chat:read IRC lesen Bot
chat:edit IRC schreiben Bot
user:write:chat Chat über Helix senden (Alternative zu IRC) Bot
moderator:read:chatters Zuschauerliste im Chat Broadcaster
clips:edit Clips erstellen Broadcaster
channel:manage:broadcast Titel/Kategorie ändern, Marker setzen Broadcaster
channel:read:subscriptions Sub-Events Broadcaster
moderator:read:followers Follow-Events (EventSub) Broadcaster
channel:manage:raids Raid starten/abbrechen Broadcaster
channel:read:redemptions Channel-Point-Einlösungen Broadcaster
channel:manage:redemptions eigene Punkte-Rewards verwalten Broadcaster
channel:manage:polls Umfragen Broadcaster
analytics:read:games Game-Analytics-Report Broadcaster
bits:read Bits-Events Broadcaster

Der Autorisierungs-Flow läuft über den Einrichtungs-Assistenten: docker compose up -d, dann http://localhost:4792/setup öffnen und je einmal für Broadcaster- und Bot-Konto auf „Mit Twitch verbinden" klicken. Die Redirect-URI, die in beiden Twitch-Apps hinterlegt sein muss, lautet http://localhost:4792/callback.

EventSub (WebSocket)

Abonniert werden beim Start:

Subscription Version Nutzen für
stream.online 1 Broadcaster, Analytics, Recap
stream.offline 1 Recap, VOD-Kapitel
channel.update 2 VOD-Kapitel (Kategorie/Titel), Analytics
channel.follow 2 Community, Recap
channel.subscribe / .message / .gift 1 Recap, Overlay
channel.cheer 1 Recap, Overlay
channel.raid (to + from) 1 Raid-Tool
channel.channel_points_custom_reward_redemption.add 1 Loyalty, Overlay
channel.poll.begin/progress/end 1 Overlay
channel.chat.message 1 optional, siehe unten
channel.chat.notification 1 Community (Erstzuschauer-Marker)

Chat: IRC oder EventSub? Beides funktioniert. Wir lesen den Chat über IRC (irc-ws.chat.twitch.tv:443), weil die Tags dort (first-msg, returning-chatter, emotes, badges, msg-id) reichhaltiger und stabiler sind als im EventSub-Payload, und weil Rate Limits beim Senden dort klar dokumentiert sind. EventSub übernimmt alles Nicht-Chat.

Verbindungsregeln: Eine WS-Session hält max. 300 Subscriptions. Twitch sendet alle ~10 s ein Keepalive; bleibt es aus, neu verbinden. session_reconnect liefert eine neue URL — dort verbinden, dann erst die alte Verbindung schließen. Nach jedem Reconnect: Lücke per Helix-Polling füllen.

Helix — die relevanten Endpunkte

Endpunkt Tool
GET /helix/streams?user_id= Ingest (Viewer-Sampling, alle 60 s)
GET /helix/streams?game_id=&first=100 Raid-Scout
GET /helix/channels / PATCH /helix/channels Loyalty (Titel-Shop), VOD-Kapitel
POST /helix/clips Clipper
GET /helix/clips?id= Clipper (Metadaten + Thumbnail-URL)
GET /helix/videos?user_id=&type=archive VOD-Kapitel
POST /helix/streams/markers VOD-Kapitel, Clipper
GET /helix/chat/chatters Community (Anwesenheit, alle 5 min)
GET /helix/channels/followers Recap, Community
POST /helix/raids Raid-Tool
GET /helix/users?login= überall (Login → ID)

Rate Limits: 800 Punkte/Minute pro Client-ID (Standard: 1 Punkt pro Request). Der Helix-Client in packages/twitch führt einen Token-Bucket und drosselt selbst; Ratelimit-Remaining aus dem Response-Header wird ausgewertet. Bei 429 → Backoff nach Ratelimit-Reset.

Clips: die Fallstricke

Das ist der Teil, an dem die Clip-Pipeline scheitert, wenn man ihn nicht kennt:

  1. POST /helix/clips schneidet rückwirkend ~30 s ab dem Aufrufzeitpunkt (minus Stream-Delay). Man kann keinen Zeitbereich wählen.
  2. Der Clip ist nicht sofort verfügbar. Nach dem POST bekommt man id und edit_url; die Verarbeitung dauert typischerweise 5–20 s. Danach liefert GET /helix/clips?id= erst Metadaten.
  3. Die MP4-URL steht nirgends in der API. Sie wird aus thumbnail_url abgeleitet: alles ab -preview- abschneiden, .mp4 anhängen.
    https://clips-media-assets2.twitch.tv/AT-cm%7C123-preview-480x272.jpg
    → https://clips-media-assets2.twitch.tv/AT-cm%7C123.mp4
    
    Das ist undokumentiert und kann sich ändern — deshalb im Code an genau einer Stelle (packages/twitch/clips.ts) gekapselt und mit Fallback versehen.
  4. Der Clip enthält das fertig komposite Bild (1920×1080), nicht die Einzelquellen. Facecam und Gameplay lassen sich nur per Crop trennen, und nur wenn das OBS-Layout fest ist.
  5. Bitrate/Qualität entspricht dem Transcode, nicht dem Original.

Deshalb: OBS-Replay-Buffer ist der bessere Primärweg. Details in docs/projects/clip-shorts.md.

OBS-WebSocket

obs-websocket v5 auf ws://127.0.0.1:4455. Genutzt für:

  • CurrentProgramSceneChanged → VOD-Kapitel
  • SaveReplayBuffer → Clipper
  • TriggerHotkeyByName, SetInputSettings → Chat-Steuerung, Loyalty-Shop
  • GetSourceScreenshot → Go-Live-Broadcaster (Screenshot für den Post)