BetterStream: Commands, Variablen und Actions

Geschenk-Sub-Stufen einrichten

Ab Desktop 0.8.45 kannst du unter Alerts → Geschenk eigene Designs für Geschenkpakete einstellen, beispielsweise ab 5, ab 10 und ab 25 Subs.

1. Geschenk auswählen. Das Standard-Design gilt, wenn keine aktive Stufe zur verschenkten Anzahl passt.

2. Unter Neue Stufe ab die Mindestmenge eingeben und Stufe hinzufügen anklicken. Neue Stufen übernehmen zunächst das gespeicherte Standard-Design.

3. Die Stufe bei Design bearbeiten auswählen. Titel, Nachricht, Farben, Bild/GIF, Sound, Lautstärke und die weiteren Einstellungen im normalen Alert-Editor anpassen.

4. Stufe ab … Subs speichern anklicken. Mit Stufe aktiv lässt sich eine Stufe deaktivieren; auch diese Änderung speichern. Es sind bis zu 20 Stufen mit jeweils 1–10.000 Subs als Mindestmenge möglich.

5. Bei Geschenk-Subs für den Test eine Anzahl eingeben und Gespeicherte Stufen testen anklicken. Dieser Test wählt genau wie ein Live-Ereignis das gespeicherte Design und sendet es an die verbundene OBS-Alertbox. Es werden keine Subs gekauft, keine Statistik erhöht und kein Subathon verlängert. Testalert in OBS prüft dagegen das aktuell bearbeitete Design einschließlich ungespeicherter Änderungen.

Es wird immer eine Stufe verwendet: die höchste aktive Mindestmenge, die kleiner oder gleich der Paketgröße ist. Bei Stufen ab 5, 10 und 25 nutzen 12 Subs die 10er-Stufe, 25 Subs die 25er-Stufe und 4 Subs das Standard-Design. Ist die 10er-Stufe deaktiviert, verwenden 12 Subs die 5er-Stufe. Die Anzahl bezieht sich auf das aktuelle Geschenkpaket, nicht auf die insgesamt jemals verschenkten Subs und nicht auf Twitch-Abo-Tier 1/2/3.

In Titel und Nachricht stehen {name} für den Schenkenden und {amount} für die tatsächliche Anzahl, beispielsweise Danke {name} für {amount} Geschenk-Subs!. Anonyme Pakete verwenden den Namen Anonym. BetterStream empfängt Geschenkpakete über den verbundenen Twitch-Chat-Bot; dieser muss verbunden sein. Die gekennzeichneten Empfänger-Meldungen eines Community-Geschenkpakets erzeugen keine zusätzlichen Alerts und erhöhen die Zähler nicht nochmals. Einzelgeschenke funktionieren weiterhin mit der Anzahl 1.

Stufe entfernen löscht nur diese Stufe; das Standard-Design und andere Stufen bleiben bestehen. Die bestehende OBS-Alertbox-URL kann weiterverwendet werden.

Eigene Custom-Alerts pro Stufe

Ab 0.8.46 hat jede Geschenk-Sub-Stufe auch eigene HTML-, CSS-, JS-, Fields- und Data-Einstellungen. Unter Alerts → Geschenk → Design bearbeiten die gewünschte Stufe auswählen, den Bereich Custom Alert öffnen und den Code in die fünf Reiter eintragen. Mit Custom Alert speichern wird ausschließlich der Code der ausgewählten Stufe gespeichert. Das Standard-Design wird separat bearbeitet und gespeichert.

Neue Stufen kopieren zunächst den gespeicherten Custom-Alert des Standards. Bereits vorhandene Stufen ohne eigenen Custom-Code verwenden zunächst den Standard; nach dem ersten eigenen Speichern sind ihre Quellen unabhängig. Leeres HTML mit Custom Alert speichern übernehmen, um für diese Stufe wieder das normale Design zu verwenden. Fields und Data benötigen gültiges JSON; fehlerhafte Eingaben überschreiben die gespeicherten Quellen nicht.

Gespeicherte Stufen testen wählt den gespeicherten Code anhand der eingegebenen Geschenk-Anzahl. Testalert in OBS sendet dagegen die aktuell bearbeiteten Quellen inklusive ungespeicherter Änderungen. Die HTML/CSS-Vorschau im Editor führt JavaScript nicht aus; für dessen Prüfung den OBS-Test verwenden.

Custom-JavaScript bekommt root als Wurzelelement und data mit den zusammengeführten Data-/Fields-Werten sowie den Ereigniswerten. Beispielsweise enthalten data.name den Schenkenden und data.amount die tatsächliche Geschenk-Anzahl. Bei gleichen Schlüsseln überschreiben Fields die Data-Werte und die Ereigniswerte beide. HTML kann diese Werte mit {{name}} und {{amount}} anzeigen.

<div class="gift">Danke {{name}} für {{amount}} Subs!</div>
root.querySelector('.gift').dataset.giftCount = String(data.amount);

Sound Alerts einrichten

Ab Desktop 0.8.44 kannst du unter Alerts → Sound Alerts Twitch-Kanalpunkte-Belohnungen eigene Sounds zuordnen.

1. Twitch verbinden. Falls noch keine Belohnung existiert, diese unter Commands → Channel Points erstellen.

2. Unter Alerts → Sound Alerts die Twitch-Belohnung auswählen. BetterStream speichert ihre Reward-ID; eine Umbenennung unterbricht die Zuordnung nicht.

3. Einen der fünf mitgelieferten Sounds Ping, Erfolg, Level Up, Alarm oder Fehler übernehmen oder eine eigene MP3-, WAV-, OGG- oder M4A-Datei wählen.

4. Lautstärke und maximale Wiedergabezeit einstellen, mit Anhören lokal prüfen und Sound-Alert hinzufügen anklicken.

5. OBS-Alertbox kopieren anklicken und die Adresse in OBS als Browserquelle hinzufügen. Dort Audio über OBS steuern aktivieren und die Audioausgabe im OBS-Mixer einstellen.

Bestehende Einträge lassen sich bearbeiten, deaktivieren, ersetzen und entfernen. Änderungen an Belohnung, Lautstärke, Wiedergabezeit oder Aktivierung mit Änderungen speichern übernehmen. Das Ersetzen der Audiodatei speichert unmittelbar. 0 % Lautstärke wird unterstützt. In OBS testen sendet die aktuellen Einstellungen an die verbundene Alertbox, ohne Twitch-Kanalpunkte einzulösen; der Test funktioniert auch bei einem deaktivierten Eintrag. Eine gemeldete Verbindung bestätigt den Empfangskanal, nicht die hörbare Audioausgabe von OBS.

Die Desktop-App und die OBS-Browserquelle müssen laufen. Mehrere Sound-Alerts werden nacheinander abgespielt; normale Alert-Sounds verwenden weiterhin ihren eigenen Player. Die maximale Wiedergabezeit beträgt pro Eintrag 1–30 Sekunden. Die Warteschlange nimmt bis zu 50 wartende Sounds zusätzlich zum laufenden Sound auf; weitere werden mit einem Hinweis im Browserlog verworfen. Der Redemption-Schutz verhindert, dass eine doppelt zugestellte Einlösung denselben Sound erneut startet.

Audiodateien dürfen maximal 10 MB pro Datei groß sein; insgesamt sind 50 Einträge und 32 MB Audiodaten erlaubt. Die fünf Standardsounds wurden für BetterStream erstellt. Dateien und Einstellungen bleiben lokal auf dem PC und werden getrennt nach BetterStream-Konto gespeichert. Vorhandene Sound-Rewards werden beim ersten Zugriff dem aktiven Konto zugeordnet; die ursprüngliche Datei bleibt erhalten. Eine kopierte Alertbox-Adresse enthält einen privaten Zugangsschlüssel und sollte nicht öffentlich geteilt werden.

In drei Schritten

1. Twitch verbinden und den Chat-Bot einschalten.

2. Commands → Neuer Command öffnen. Einen Namen wie !hallo, die Berechtigung und einen Cooldown wählen.

3. Eine Chat-Antwort, JavaScript oder eine Action-Liste eintragen, speichern und aktivieren.

JavaScript ist die unterstützte Skriptsprache. Java ist eine andere Sprache. Code hier ohne HTML-Tags und ohne zusätzliche Funktionshülle einfügen. Für OBS-Actions muss OBS verbunden sein. Ohne laufende Desktop-App werden lokale Commands und Community-Funktionen nicht ausgeführt.

JavaScript: verfügbare Daten

NameTypBedeutung und Beispiel
userstringTwitch-Login des Absenders, z. B. froggly
displayNamestringAnzeigename des Absenders
userIdstringTwitch-ID; für dauerhafte Nutzerzuordnung verwenden
argsstring[]Wörter nach dem Command; !hallo Welt ergibt ["Welt"]
commandstringKonfigurierter Command-Name, z. B. !hallo
rawstringGesamte Chat-Nachricht einschließlich Command
channelstringTwitch-Kanal mit #
contextobjectEreignisdaten: user, command, channel, stream, platform, vars; bei Automationen zusätzlich event

args[0] kann fehlen. Zahlen immer mit Number() umwandeln und mit Number.isFinite() prüfen. Das globale user ist ein Text; context.user ist ein Objekt mit id, name, displayName, roles.

JavaScript: verfügbare Funktionen

AufrufWirkung
await say("Hallo!")Sendet sofort maximal 500 Zeichen in den aktuellen Chat
return "Hallo!"Sendet den Rückgabewert nach dem Skript; maximal drei Antworten
return ["A", "B"]Sendet mehrere Antworten; maximal drei
console.log("Text")Wird ebenfalls als Chat-Antwort ausgegeben
await points.get(user)Liest Loyalty-Punkte; gibt eine Zahl zurück
await points.add(user, 10, displayName)Schreibt dem angegebenen Nutzer Punkte gut
await points.remove(user, 10)Entfernt Punkte über die Loyalty-API
await points.transfer(user, target, 10, targetName)Überträgt Punkte; Quellkonto ist ausdrücklich angegeben
state.get("key", fallback)Liest lokale, pro BetterStream-Konto persistente Daten
state.set("key", value)Speichert JSON-kompatible Daten lokal
state.delete("key")Entfernt gespeicherte Daten
await sleep(500)Wartet; pro Aufruf maximal 2 Sekunden
schedule(id, delayMs, async data => { ... }, data)Speichert eine Aufgabe für später; maximal 7 Tage Verzögerung

Math, JSON, Number, String, Boolean, Array, Object, Date, parseInt und parseFloat sind verfügbar. fetch, require, process, DOM und CPH sind hier keine unterstützten APIs. HTTP und OBS über die Action-Liste verwenden.

Skripte haben eine Laufzeit von maximal drei Sekunden und synchrone Verarbeitung von maximal einer Sekunde. Zeitüberschreitung ist keine Rückabwicklung bereits versendeter Nachrichten oder Punkteänderungen. Der Testmodus zeigt den Ablauf; er führt JavaScript, C#, HTTP und externe Actions nicht live aus. Lokale Skripte nur von vertrauenswürdigen Quellen verwenden; Node-VM ist keine Sicherheitsgrenze für fremden Code.

Punkteänderungen haben echte Wirkung. Commands, die beliebigen Konten Punkte geben, auf Broadcaster oder Moderator beschränken. Cooldowns schützen vor wiederholtem Auslösen. Gemeinsame state-Daten nicht als Bank für parallele Punkteoperationen verwenden; dafür die Loyalty-API nutzen.

Beispiel: personalisierte Antwort

const target = args[0] || displayName;
return `Hallo ${target}!`;

Beispiel: eigener Zähler

const key = `hello:${userId}`;
const count = Number(state.get(key, 0)) + 1;
state.set(key, count);
return `${displayName}, dein Hallo Nummer ${count}!`;

Beispiel: verzögerte Erinnerung

const result = schedule(`reminder:${userId}`, 60000, async data => {
  await say(`@${data.name} Deine Minute ist um!`);
}, { name: user });
return result.scheduled ? "Erinnerung gespeichert." : "Deine Erinnerung läuft bereits.";

Der geplante Handler bekommt nur data, say, points, state, sleep und die Standardfunktionen. Er kann keine Variablen aus dem ursprünglichen Skript übernehmen. Geplante Aufgaben bleiben auf diesem PC gespeichert. Sie laufen bei geöffneter App, auch nach einem Neustart; Fehler können in den lokalen Protokollen erscheinen. schedule verhindert parallele Aufgaben mit derselben ID, maximal 250 gespeicherte Aufgaben. Handler dürfen maximal 8 Sekunden laufen.

Textvariablen in Antworten und Actions

Textfelder nutzen geschweifte Klammern. JavaScript benutzt seine eigenen Variablen und ${...} innerhalb eines Template-Strings.

PlatzhalterInhalt
{user} / {username} / {user.name}Twitch-Login; bei Automation kann user.name der Anzeigename sein
{user.id} / {user.displayName}Twitch-ID / Anzeigename
{target} / {targetUser}Erstes Argument hinter dem Command
{args} / {command.args}Alle Argumente als Text mit Leerzeichen
{command.args.0}Erstes Argument
{command.name} / {command.raw} / {command.argCount}Name / gesamte Nachricht / Anzahl der Argumente
{channel.name} / {channel.id}Kanalname / Kanal-ID im aktuellen Kontext
{stream.viewerCount} / {stream.game} / {stream.uptime}Zuschauer / Kategorie / Laufzeit in Sekunden; fehlende Daten ergeben leeren Text
{random}Neue Zufallszahl von 1 bis 100 bei jeder Ersetzung
{vars.name}Wert einer set_variable-Action während dieses Ablaufs
{variable.name} / {var.name}Benannte persistente Automation-Variable
{clip.url}Nach erfolgreicher create_clip-Action in deren Nachricht
{game.bet} / {game.payout} / {game.balance}Ergebnis einer Loyalty-Spiel-Action
{event.type} / {event.message} / {event.viewers}Nur bei Automationen: Art / Text / Zuschauer eines Ereignisses
{event.amount} / {event.months} / {event.recipient}Nur bei Automationen: Betrag bzw. Bits / Monate / Empfänger

Unbekannte Platzhalter werden leer. Ereignisdaten entstehen nur bei einem passenden Trigger. Eine Chat-Nachricht liefert keine Follow-Dauer und kein Sub-Ereignis.

Erweiterte Commands: Action-Liste

Jede Action ist ein Objekt mit type und config. Reihenfolge entspricht der Ausführung. enabled:false überspringt einen Schritt. Beispiel:

[
  {"type":"send_message","config":{"message":"Hallo {user.displayName}!"}},
  {"type":"delay","config":{"ms":500}},
  {"type":"change_scene","config":{"sceneName":"Gameplay"}}
]
typeWichtige config-FelderWirkung
send_message, reply, announcementmessageNachricht; Reply setzt @user, Announcement braucht Twitch-Rechte
delayms oder secondsWartet maximal 30 Sekunden
conditionleft, right, operator: equals, contains, not_equalsBricht bei nicht erfüllter Bedingung ab
set_variablename, valueSetzt temporären Wert in vars
randomoptions: ArraySpeichert ausgewählten Wert in {vars.random}
countername, deltaErhöht temporären Zähler in vars
stop_actionkeineBeendet den Ablauf
javascript_codecodeJavaScript mit den oben beschriebenen APIs
csharp_codecodeLokal freigegebener C#-Code; kein Ersatz für CPHInline
create_clipmessageTwitch-Clip und Antwort; Twitch-Berechtigung erforderlich
change_scenesceneNameWechsel der OBS-Szene
show_source, hide_source, toggle_sourcesceneName, sourceNameSichtbarkeit einer OBS-Quelle
source_visibilitysceneName, sourceName, state: visible, hidden, toggleSichtbarkeit gezielt ändern
set_muteinputName, muted: booleanOBS-Audio stummschalten
set_volumeinputName, volumeOBS-Audiolautstärke
screenshotsourceName, path, formatSpeichert OBS-Screenshot auf dem OBS-PC
replay_savekeineSpeichert laufenden OBS-Replay-Puffer
start_stream, stop_stream, start_recording, stop_recordingkeineSteuert OBS; nur für vertrauenswürdige Commands freigeben
show_overlay, play_alerttitle, message, alertTypeLokaler BetterStream-Alert
notification, tts, play_soundmessage und je nach Action Medien-/LautstärkefelderDesktop-Meldung, Sprache oder Sound
http_requesturl, method, headers, bodyHTTPS oder http://localhost:PORT/; GET ohne Body, sonst JSON

Spezielle Spiele, Spotify, Timer und Variablen über ihre jeweiligen fertigen Editoren konfigurieren: Dort sind die benötigten Felder vollständig abgebildet.

Automationen: WHEN → IF → THEN

WHEN ist das auslösende Ereignis, etwa Chat, Command, Follow, Kanalpunkte, Raid, Abo, Stream online/offline, Timer abgelaufen oder Variable geändert. IF schränkt den Ablauf ein, etwa Zuschauerzahl, Rollen, Variablenvergleich oder Uhrzeit. THEN führt ein gespeichertes Event mit Actions aus. Optional ELSE für nicht erfüllte Bedingungen.

Die visuellen Actions verwenden teils andere Namen als die Command-Liste: send_chat_message → send_message, send_announcement → announcement, change_obs_scene → change_scene, show_obs_source → show_source, hide_obs_source → hide_source, show_overlay_alert → show_overlay, text_to_speech → tts, desktop_notification → notification. Timer und persistente Variablen im visuellen Editor anlegen und über ihre Namen auswählen.

Nicht jedes von Twitch angebotene Ereignis wird von dieser App abonniert. Die aktuelle Laufzeit liefert Chat, Follow, Abo/Resub/Gift, Bits, Raid, Kanalpunkte und Streamstatus. Einen weiteren Trigger erst live prüfen, bevor ein Ablauf davon abhängig gemacht wird.

Widgets

Widgets → Dynamic Voting: Frage, 2–10 Antworten und Dauer von 10–3600 Sekunden festlegen. Mit Widget-URL kopieren die lokale OBS-Browserquelle übernehmen. Standard: !vote 1. Pro Twitch-ID zählt eine Stimme; bei Gleichstand werden alle Gewinner angezeigt. Farbe und Command ändern, Vorlagen speichern/laden/löschen und Abstimmungen vorzeitig beenden. Das Ergebnis bleibt bis zur nächsten Abstimmung sichtbar.

Social-Media-Rotator: Widget erstellen und im Overlay-Studio die Einträge in Data bearbeiten. Jeder Eintrag enthält label und value. intervalSeconds steuert die Wechselzeit (mindestens drei Sekunden); accent in Fields die Farbe. Keine automatisch vorgegebenen Anbieter oder Werbetexte.

Nachrichten-Highlight: Command aktivieren, Widget erstellen und die Browserquelle in OBS einfügen. Streamer und Moderatoren blenden mit !highlight Text eine Nachricht ein. durationSeconds in Data steuert die Sichtbarkeit, accent in Fields die Farbe. Nachrichten werden als Text dargestellt.

Kanalpunkte-Belohnungen

Commands → Channel Points → BetterStream-Belohnungen enthält First Redeemer und Daily Check-in. Eine vorhandene Belohnung aus der Liste wählen oder mit Name, Punkten, Beschreibung und Farbe direkt auf Twitch erstellen. Twitch bei fehlenden Berechtigungen erneut verbinden. Twitch bietet Kanalpunkte für berechtigte Kanäle an.

Fortschritt und Widget-Konfiguration bleiben lokal pro BetterStream-Konto. Kein automatischer Abgleich zwischen PCs. BetterStream muss beim Einlösen laufen und Twitch-Ereignisse empfangen.

Import-Strings direkt übernehmen

Commands → Import: vollständigen String einfügen, Vorschau prüfen und privat übernehmen. BetterStream liest das SBAE-Format sowie dessen JSON-Daten. Es ist keine zweite Bot-App oder WebSocket-Verbindung erforderlich. Importieren startet keinen enthaltenen Auto-Run-Code.

Die Vorschau unterscheidet ausführbare Actions und Schritte mit Anpassungsbedarf. Chat-Nachrichten, feste Verzögerungen, Argumente, einfache globale Variablen und unterstützte C#-Aufrufe werden lokal verarbeitet. Die Reihenfolge folgt den exportierten Indexwerten. Kommentare und deaktivierte Schritte werden nicht ausgeführt. Unbekannte Schritte werden erhalten, aber die betroffene Action ist nicht ausführbar: Ein unvollständiger Ablauf wird nicht stillschweigend gestartet.

Enthaltener C#-Quellcode läuft nach lokaler Freigabe über die BetterStream-Kompatibilitätsschicht. Quellcode ansehen öffnet den vollständigen Code, Import-Einstellungen die exportierten Argumentwerte. Erst anschließend Code freigeben und Import aktivieren. Code bleibt auf diesem PC und wird nicht auf die Website hochgeladen. C# ist lokaler Programmcode mit Dateizugriff; eine Freigabe gilt für den genauen Quellcode.

Unterstützte C#-Aufrufe: TryGetArg, GetGlobalVar, SetGlobalVar, SendMessage, Wait, Between, ObsSetGdiText, ObsSetImageSourceFile, ObsShowFilter, ObsHideFilter, PlaySound, EnableAction, DisableAction, EnableCommand, DisableCommand, EnableReward, DisableReward, AddUserToGroup, RemoveUserFromGroup, TwitchAddVip, TwitchRemoveVip. OBS verwendet Verbindung 0; Quellen und Filter müssen existieren. Sound- und Bilddateien müssen auf dem jeweiligen PC verfügbar sein. VIP-Verwaltung und Belohnungsänderungen benötigen die passenden Twitch-Rechte. Fremde DLLs und weitere APIs werden nicht automatisch ersetzt.

Auslöser jeder Action prüfen: Manuell, Chat-Command, Kanalpunkte oder Jede Chatnachricht. Exportierte Commands und Reward-Verknüpfungen werden vorbelegt; weitere Trigger müssen zugeordnet werden. Deaktivierte Actions bleiben deaktiviert und können vom importierten Ablauf aktiviert werden. Die Daten bleiben lokal, inklusive Originalexport und nicht unterstützter Schritte. Erneutes Importieren desselben Strings legt keine Kopie an. Zwischen unterschiedlichen Importversionen erfolgt keine automatische Datenmigration.

Twitch erlaubt das Bearbeiten einer Belohnung normalerweise nur der App, die sie erstellt hat. Erstelle bei Bedarf eine neue Belohnung in BetterStream und wähle sie als Auslöser. Aktivieren und Deaktivieren der alten Reward-ID wird im importierten Ablauf dann auf die neu gewählte Belohnung umgeleitet.

Die Kompatibilitätsschicht ersetzt unterstützte APIs, korrigiert aber keine Fehler im eigenen Skript. Imports mit abweichenden Variablennamen, fehlenden Medien oder externen DLLs müssen angepasst werden. Maximal 120 Sekunden pro C#-Aufruf und 30 Sekunden pro Wait.

Profil, Discord und Tarif

Kopiervorlage für KI

Erstelle einen BetterStream-JavaScript-Command für: [Wunsch]. Nutze ausschließlich user, userId, displayName, args, command, raw, channel, context, say, points, state, schedule und sleep aus diesem Leitfaden. Code wird bereits in einer async-Funktion ausgeführt. Kein CPH, require, fetch oder DOM. Prüfe fehlende Argumente und ungültige Zahlen. Maximal drei Sekunden Laufzeit; spätere Aktionen mit schedule und ausschließlich serialisierbaren data planen. Keine Punkteänderung ohne explizite Anforderung. Empfiehl passende Berechtigungen und Cooldowns. Gib fertigen Code und kurz die vom Nutzer anpassbaren Werte an.

Event Bridge: Commands mit eigenen Widgets verbinden

Ab Desktop 0.8.42 können JavaScript-Commands, JavaScript-Actions und geplante JavaScript-Aufgaben Live-Ereignisse an eigene Widgets senden. Widgets im Overlay-Studio und deren OBS-Browserquellen verwenden dieselbe API. BetterStream muss laufen und angemeldet sein. Es ist kein zusätzliches Programm erforderlich.

KontextAPIAufgabe
JavaScript-Command / JavaScript-Action / geplante Aufgabeawait events.emit(name, payload, options)Live-Signal senden
Custom-Widget JavaScriptevents.on(name, handler)Signale empfangen; gibt eine Abmeldefunktion zurück
Custom-Widget JavaScriptevents.once(name, handler)Genau das nächste passende Signal empfangen
Custom-Widget JavaScriptevents.off(name, handler)Alle Anmeldungen dieser Funktion für diesen Namen entfernen
Command / Action / geplante Aufgabeawait shared.set(key, value)Gemeinsamen JSON-State speichern und Leser benachrichtigen
Command / Action / geplante Aufgabeawait shared.get(key)Aktuellen State oder null lesen
Custom-Widget JavaScriptawait shared.get(key)Aktuellen State lesen; bei fehlendem Wert oder Verbindungsfehler null
Custom-Widget JavaScriptshared.on(key, handler)Aktuellen Wert und spätere Änderungen empfangen; gibt Abmeldefunktion zurück
Custom-Widget JavaScriptshared.off(key, handler)Diese State-Anmeldung entfernen

Widgets erhalten ausschließlich die Empfangs- und Lese-API: kein events.emit, kein shared.set, keine Command-Ausführung und keine neuen Punkte-, OBS- oder Dateirechte. Browser-JavaScript im Widget hat weiterhin die normalen Browserfunktionen. Speichere keine Tokens oder andere Geheimnisse in Fields, Data, Event-Payloads oder gemeinsamem State: Widgets dürfen diese Daten lesen.

Live-Signal senden

Im JavaScript-Feld eines Commands, ohne zusätzliche Funktionshülle:

const result = await events.emit('custom.win', {
  user: displayName,
  amount: 100,
  message: 'Treffer!'
});
if (!result.ok) return `Widget-Signal fehlgeschlagen: ${result.error}`;

Im JavaScript-Tab eines eigenen Widgets:

const stop = events.on('custom.win', (payload, meta) => {
  document.querySelector('#message').textContent =
    `${payload.user}: ${payload.message} (${payload.amount})`;
});
// Bei Bedarf später: stop();

Dazu im HTML-Tab: <div id="message"></div>. textContent stellt Nutzereingaben als Text dar. Der Handler bekommt eine eigene JSON-Kopie und als zweites Argument {name}. Fehler eines Handlers werden in der Widget-Konsole angezeigt; weitere Handler und Widgets empfangen weiterhin. Einmalige Anmeldung: events.once('custom.win', handler). Exakten Handler entfernen: events.off('custom.win', handler).

Events werden nicht gespeichert, nachgeladen oder wiederholt. Ist ein Widget beim Senden geschlossen oder getrennt, verpasst es das Signal. Verbindungen werden automatisch wiederhergestellt. Normale OBS-Widgetseiten erneuern nach einem App-Neustart ihre Verbindung für dasselbe Konto; die Studio-Vorschau bei Bedarf über Vorschau aktualisieren neu laden. Ein Kontowechsel trennt vorhandene Leser; Widgetseiten für das neue Konto neu laden.

Namen, Payload und Empfänger

EinstellungRegel
Event-Name / State-Key1–128 Zeichen; beginnt mit Buchstabe oder Zahl; danach Buchstaben, Zahlen, _, ., :, -; Groß-/Kleinschreibung zählt
Eigene NamenFrei wählbar, z. B. custom.win, boss.damage, poll.update, loyalty.change
Namespace-Anmeldungevents.on('custom.*', handler) empfängt Namen mit custom.; nur .* am Ende ist erlaubt
PayloadJSON-Objekt; keine Funktionen, undefined, Zyklen, Getter, BigInt oder nicht endlichen Zahlen
GrößeMaximal 64 KiB JSON je Payload oder State-Wert, maximal 16 Verschachtelungsebenen und 10000 Werte/Arrayplätze
Ohne optionsAlle verbundenen Custom-Widgetseiten und Studio-Vorschauen mit passender Anmeldung
{target:'overlay'}Alle passenden Custom-Widget-Empfänger; keine zusätzliche Overlay-Gruppen-ID
{target:'widget', widgetId:'a826bddbeb9f'}Nur Empfänger dieser Widget-ID, einschließlich gleichzeitig geöffneter Vorschau und OBS-Quelle

Die Widget-ID ist der letzte Teil der kopierten OBS-Adresse, z. B. /widget/a826bddbeb9f. In Beispielen verwendete IDs durch die eigene ID ersetzen. Fremde Optionsfelder oder ungültige Ziele werden abgelehnt.

await events.emit('boss.damage', { damage: 25, user: displayName }, {
  target: 'widget',
  widgetId: 'a826bddbeb9f'
});

Erfolg: {ok:true, delivered:2}. delivered zählt passende aktive Browser-Verbindungen, an die das Signal zur Übertragung übergeben wurde. Es zählt keine Handler und bestätigt keine abgeschlossene Darstellung. Zwei Browserquellen derselben Widget-ID zählen als zwei Empfänger. Ohne Empfänger ist {ok:true, delivered:0} gültig. Ungültige Daten liefern {ok:false, delivered:0, error:'...'} und beenden den Command nicht automatisch. Beispiele: invalid_event_name, invalid_payload, payload_too_large, invalid_target, not_signed_in.

Maximal 128 gleichzeitige Widget-Verbindungen und je Verbindung 128 unterschiedliche Event-Namen sowie 128 State-Keys. Übermäßig viele Nachrichten oder zu langsame Empfänger werden getrennt. Live-Signale verwenden den lokalen Desktop-Server und erzeugen keine zusätzlichen Website-/Vercel-Aufrufe.

Shared State: Zustand nach einem Reload wiederherstellen

Fields sind vom Nutzer einstellbare Optionen, z. B. Farbe und Schrift. Data ist die gespeicherte Konfiguration dieses Widgets. Events sind kurzlebige Signale. Shared State ist ein aktueller, gemeinsam lesbarer Zustand, z. B. Boss-Lebenspunkte.

Command (für Änderungen an gemeinsamen Spieldaten passende Berechtigung wählen):

const previous = await shared.get('boss');
const hp = Math.max(0, Number(previous?.hp ?? 100) - 10);
const saved = await shared.set('boss', { hp, maximum: 100 });
if (!saved.ok) return `Speichern fehlgeschlagen: ${saved.error}`;
await events.emit('boss.damage', { damage: 10, hp, user: displayName });

Widget-JavaScript und HTML <div id="boss"></div>:

shared.on('boss', (value, meta) => {
  document.querySelector('#boss').textContent = value
    ? `Boss: ${value.hp} / ${value.maximum}`
    : 'Noch kein Boss gestartet';
  // meta.initial ist beim ersten aktuellen Wert true.
});

shared.set liefert {ok:true, delivered:n} oder {ok:false, delivered:0, error}. Gültige Werte sind JSON-Objekte, Arrays, Texte, endliche Zahlen, Booleans und null. Pro Konto maximal 256 Keys und zusammen 1 MiB JSON. State wird lokal atomar gespeichert und beim nächsten Start wieder geladen; kein Abgleich mit anderen PCs. Fehlender Key ergibt null. shared.get im Widget wartet höchstens drei Sekunden. shared.on stellt beim Verbinden den aktuellen Wert und anschließend Änderungen bereit. State hat keine Historie und ist kein Transaktionssystem: parallele Lese-/Schreibfolgen können sich überschreiben. Loyalty-Punkte weiterhin ausschließlich über die dokumentierte points-API ändern.

Bereits vorhandene Systemereignisse

Systemereignisse entstehen aus den Daten, die BetterStream bereits empfängt. Eine Anmeldung aktiviert keine neue Twitch-Verbindung oder Berechtigung. Fehlt die Quelle, entsteht kein Ereignis. Alle Payloads enthalten type; zusätzliche Felder sind abhängig vom tatsächlich vorhandenen Ereignis. Kein Token, privater Import-Code oder Alert-JavaScript wird übertragen.

EventQuelle und mögliche Payload-Felder
twitch.chat.messageEmpfangener Twitch-Chat: user, userId, message, color, badges, emotes
twitch.followFollow-Alert: name, title, message
twitch.subAbo, Resub oder Gift: name, months, amount, recipient, title, message; type unterscheidet die Art
twitch.raidRaid-Alert: name, viewers, message
twitch.cheerBits-Alert: name, amount, message
twitch.reward.redeemedEmpfangene Kanalpunkte-Einlösung: userId, username, rewardId, rewardTitle
stream.alertJeder dargestellte Alert: name, title, message, amount, months, viewers, recipient, test; Test-Alerts enthalten test:true
stream.donationDonation-Alert: name, amount, message
stream.viewers, stream.subs, stream.subathonBereits vorhandene Viewer-/Abo-/Subathon-Updates; nur verfügbare Felder wie viewers, current, live, status, time
command.executedErfolgreich ausgeführter konfigurierter Command: command, user, userId, channel, status:'success'
points.changedErfolgreiche Punkteänderung über die Desktop-Loyalty-API: operation, user, amount, balance; Übertragung kann senderBalance, target enthalten, Spiele game, bet, payout, result. Keine Garantie für Änderungen außerhalb des Desktop-Clients
poll.updatedBetterStream Dynamic Voting: poll mit title, options (label, votes), active, endsAt, optional winners; ohne Abstimmung poll:null
poll.endedBeendete Abstimmung: title, winners
community.checkinDaily Check-in: userId, user, day, total, streak
community.firstFirst Redeemer: userId, user, rewardId, streamId, redemptionId
// Im Widget-JavaScript:
events.on('twitch.chat.message', payload => {
  document.querySelector('#message').textContent = payload.message || '';
});
events.on('stream.alert', payload => {
  if (payload.test) return;
  // Eigene Darstellung mit vorhandenen Payload-Feldern aktualisieren.
});

Keine C#-API für die Event Bridge: diese Funktionen stehen in den oben beschriebenen JavaScript-Kontexten bereit. Die allgemeine Bridge eignet sich für eigene Spiele, Boss-Anzeigen, Abstimmungen, Trefferanzeigen und Fortschrittsleisten. Geschäftslogik und Punkteänderungen gehören in Commands; das Widget stellt die empfangenen Daten dar.

KI-Vorlage für Command und Widget

Erstelle einen BetterStream-JavaScript-Command und ein Custom-Widget für: [Wunsch]. Nutze die dokumentierten Command-Variablen und await events.emit(name,payload,options). Das Widget verwendet events.on/once/off; für Zustand nach Reload shared.on/get, im Command await shared.set/get. Fields sind Einstellungen, Data ist Widget-Konfiguration. Verwende JSON-Objekte für Events und textContent für Nutzertexte. Es gibt kein events.emit und kein shared.set im Widget. Erkläre anpassbare Werte, Widget-ID, Berechtigung und Cooldown. Keine Punkteänderung ohne ausdrückliche Anforderung. Kein zusätzlicher Server, keine externe Bot-App, kein CPH, require oder fetch im Command. Liefere Command-Code sowie getrennt HTML, CSS, JavaScript, Fields und Data für das Widget.

Twitch: Channel Point Reward Redeemed

Ab Desktop 0.8.43 startet eine eingelöste benutzerdefinierte Twitch-Kanalpunkte-Belohnung beliebige BetterStream-Actions oder JavaScript. Die Funktion ist allgemein und lässt sich beispielsweise für Sounds, Overlays, Boss-Angriffe, Spiele oder Chat-Antworten verwenden.

Automation einrichten

1. Twitch im Bereich Integrationen verbinden. BetterStream muss laufen und angemeldet sein.

2. Unter Automations → Events ein Event anlegen, Aktion hinzufügen → Utility → JavaScript ausführen wählen und den Code eintragen.

3. Unter Automations → Trigger einen Trigger anlegen und Auslöser hinzufügen → Twitch → Channel Point Reward Redeemed wählen.

4. Bestimmte Belohnung wählen und die Twitch-Belohnung aus der Liste auswählen, oder ausdrücklich Alle Kanalpunkte-Belohnungen auswählen.

5. Unter DANN das erstellte Event auswählen, aktivieren und speichern. Für mehrere Einlösungen Nacheinander und Cooldown 0 verwenden. Gleichzeitig erlaubt parallele Actions; gemeinsame Lese-/Schreibfolgen sind dabei keine Transaktionen.

Die Zuordnung einer bestimmten Belohnung verwendet ihre Reward-ID. Eine Umbenennung ändert diese ID nicht. Wird eine Belohnung gelöscht und neu erstellt, muss die neue Belohnung gewählt werden. Bereits vorhandene Kanalpunkte-Trigger ohne Reward-ID empfangen weiterhin alle benutzerdefinierten Belohnungen. Automatische Twitch-Standardbelohnungen verwenden andere EventSub-Typen und sind in dieser Version nicht enthalten.

Twitch-Berechtigungen und Wiederverbindung

BetterStream verwendet den vorhandenen Twitch-User-Token des verbundenen Broadcasters und EventSub channel.channel_points_custom_reward_redemption.add, Version 1. Twitch verlangt channel:read:redemptions oder channel:manage:redemptions. Beide Scopes sind bereits Bestandteil des BetterStream-Twitch-Verbindungsablaufs. Bei einer älteren Anmeldung ohne diese Rechte Twitch erneut verbinden und die Kanalpunkte-Berechtigungen erlauben. Ein App-Token reicht für diese WebSocket-Subscription nicht aus.

Nach erfolgreicher Anmeldung beziehungsweise App-Neustart wird die Verbindung neu hergestellt. Bei einem Twitch-Reconnect bleibt die alte Verbindung bis zur Welcome-Nachricht der neuen offen; die Subscription wird übertragen. Bei einem Verbindungsabbruch wird erneut verbunden und abonniert. Fehlende Berechtigungen oder widerrufene Anmeldung werden protokolliert und benötigen einen Reconnect über Integrationen. Events während einer vollständigen Unterbrechung werden von Twitch nicht nachgeliefert.

Offizielle Referenzen: Twitch Redemption Subscription und Twitch WebSocket-Verbindungen.

Variablen im Redemption-JavaScript

VariableTypBedeutung
userstringTwitch-Login des einlösenden Zuschauers
displayNamestringTwitch-Anzeigename
userIdstringTwitch-ID des Zuschauers
rewardIdstringStabile Reward-ID
rewardTitlestringReward-Titel zum Zeitpunkt der Einlösung
rewardCostnumberVon Twitch mitgelieferte Kanalpunkte-Kosten
redemptionIdstringEindeutige ID dieser Einlösung
userInputstringTexteingabe des Zuschauers; ohne Eingabe leerer Text
redeemedAtstringTwitch-Zeitpunkt in RFC3339 / UTC
redemptionobjectid, userId, userName (Login), displayName, rewardId, rewardTitle, rewardCost, userInput, redeemedAt

Die bestehenden JavaScript-Funktionen bleiben verfügbar: await say(...), await points.get/add/remove/transfer(...), state.get/set/delete(...), await events.emit(...), await shared.get/set(...), schedule(...) und await sleep(...). Die üblichen Laufzeitgrenzen gelten. JavaScript läuft bereits in einer async-Funktion. Ein zurückgegebener Text wird als Chat-Antwort gesendet.

context.redemption enthält dieselben strukturierten Daten; context.event enthält zusätzlich rewardId, rewardTitle, rewardCost, redemptionId, userInput, redeemedAt. In Text-Actions sind daher beispielsweise {redemption.displayName}, {redemption.rewardTitle} und {redemption.userInput} einsetzbar. Bei geplanten Aufgaben benötigte Redemption-Daten ausdrücklich als serialisierbares data mitgeben; die ursprünglichen lokalen Variablen werden nicht automatisch in späteren Aufgaben wiederhergestellt.

Beispiel: Reward löst ein vorhandenes Widget aus

Als JavaScript-Action des zugeordneten Events eintragen. Die Widget-ID durch die eigene ID ersetzen:

const result = await events.emit('slots.spin', {
  player: displayName,
  rewardId,
  rewardCost,
  redemptionId,
  reels: ['🐸', '🍺', '🐸'],
  payout: 500,
  jackpot: false
}, {
  target: 'widget',
  widgetId: 'a826bddbeb9f'
});
if (!result.ok) throw new Error(`Widget-Event fehlgeschlagen: ${result.error}`);

Dieses Beispiel sendet Anzeigedaten. Es schreibt keine Loyalty-Punkte gut und enthält keine vollständige Spiellogik. Das vorhandene Widget muss events.on('slots.spin', handler) verwenden und verbunden sein. delivered:0 bedeutet, dass gerade kein passender Empfänger aktiv ist; Live-Events werden nicht gespeichert.

Unabhängig von einer zugeordneten Automation veröffentlicht BetterStream die Einlösung einmal über twitch.reward.redeemed. Die Payload enthält type:'channel_points', userId, user (Login), login, username / displayName, rewardId, rewardTitle, rewardCost, redemptionId, userInput, redeemedAt und, falls vorhanden, status. Widgets können darauf direkt mit events.on('twitch.reward.redeemed', handler) reagieren. Für Punkteänderungen oder weitere Actions eine Automation verwenden.

Doppelte Zustellungen und Fehler

BetterStream speichert die Redemption-ID lokal je BetterStream-Konto und Twitch-Broadcaster vor dem Start der Actions. Dieselbe ID wird danach auch bei Reconnect, wiederholter Zustellung oder App-Neustart nicht erneut verarbeitet. Verschiedene Redemption-IDs werden unabhängig verarbeitet. Dieser Schutz umfasst auch den direkten Event-Bridge-Empfang sowie bestehende Sound- und Community-Belohnungen.

Die Verarbeitung ist höchstens einmal: Ein Absturz nach dem Speichern der ID kann einen Ablauf unterbrechen; BetterStream startet ihn nicht automatisch erneut. Actions sind keine gemeinsame Transaktion. Bereits gesendete Nachrichten oder Punkteänderungen werden bei späteren Fehlern nicht rückgängig gemacht. Bei einem nicht schreibbaren oder beschädigten Deduplizierungsjournal werden Einlösungen nicht ausgeführt und ein Speicherfehler protokolliert. Journaldateien nicht löschen; ohne sie entfällt der Neustart-Schutz. Kein Deduplizierungsabgleich zwischen mehreren PCs.

Das lokale Log %APPDATA%\betterstream-desktop\twitch-redemptions.log enthält Empfang, Redemption-ID, Reward-ID/-Titel, Nutzer, gefundene oder fehlende Trigger, JavaScript-Start, Erfolg/Fehlerart sowie Subscription- und Verbindungsfehler. OAuth-Tokens und die optionale Texteingabe werden nicht protokolliert. Das Log wird bei etwa 5 MiB rotiert.

redemption.fulfill() und redemption.cancel() sind in dieser ersten Version nicht verfügbar. Twitch-Status und Rückerstattungen werden nicht automatisch geändert. Der Empfang funktioniert auch für benutzerdefinierte Rewards, die eine andere Anwendung erstellt hat.