From a0b33cec679de790f2a53e6e87593271f8a8cc76 Mon Sep 17 00:00:00 2001 From: Thomas Dannenberg Date: Wed, 8 Oct 2025 20:14:18 +0000 Subject: [PATCH] Kapitel 10/Free Rhohtext.md aktualisiert --- Kapitel 10/Free Rhohtext.md | 540 ++++++++++++++++++++++++++++++++++++ 1 file changed, 540 insertions(+) diff --git a/Kapitel 10/Free Rhohtext.md b/Kapitel 10/Free Rhohtext.md index c479878..eb78d52 100644 --- a/Kapitel 10/Free Rhohtext.md +++ b/Kapitel 10/Free Rhohtext.md @@ -270,3 +270,543 @@ Nach wenigen Sekunden ist n8n über die gewählte Subdomain erreichbar, z. B.: [!TIP] Falls der Zugriff auch von außen möglich sein soll, muss im Router Port 443 auf den NPM-Host weitergeleitet werden. +## Schritt 5 – Vorbereitung: X-App und Credentials anlegen + +In diesem Schritt richten wir die Verbindung zwischen **n8n** und **X (ehemals Twitter)** ein, damit später automatisch Beiträge gepostet werden können. + +### 5.1 – X-Entwicklerkonto erstellen und Free-Plan aktivieren +1) Gehe zu [https://developer.x.com/](https://developer.x.com/) und melde dich mit deinem normalen X-Account an. +2) Öffne im linken Menü **Products → X API**. +3) Scrolle zu **API Tiers & Pricing** und wähle **Free**. +4) Klicke auf **„Sign Up for Free Account“** (der Button ist etwas unscheinbar). +5) Im Formular die **Use Cases** eintragen – mindestens 250 Zeichen. + Beispiel (englisch, ausreichend allgemein): + > We use the API to read and write posts programmatically in order to build community engagement and automate simple posting flows for our streams. +6) Alle Häkchen für die Bedingungen setzen und die Anmeldung abschließen. + +👉 Screenshot geeignet: Developer-Portal mit ausgewähltem Free-Plan + +[!NOTE] +Mit dem Free-Plan steht genau **eine App** zur Verfügung. +Dieser Plan erlaubt bis zu **500 Posts (Write) und 100 gelesene Posts (Read) pro Monat** – für Streaming-Ankündigungen reicht das in der Regel völlig aus. + +### 5.2 – Überblick im Developer-Dashboard +Nach der Anmeldung erscheint im **Dashboard → Projects & Apps** automatisch ein erstes Projekt mit einer App. + +👉 Screenshot geeignet: Dashboard mit Projekt-Kachel und Post-Limit-Anzeige + +### 5.3 – Keys und Tokens vorbereiten +1) Klicke in der App-Kachel auf das **Zahnrad-Symbol → Keys and Tokens**. +2) Hier später **API Key & Secret** sowie **Bearer Token** generieren – diese brauchen wir gleich in n8n. + +👉 Screenshot geeignet: Bereich „Keys and Tokens“ vor dem Generieren der Keys + +[!WARNING] +Diese Schlüssel sind vertraulich und dürfen nicht öffentlich geteilt oder im Code abgelegt werden. +Speichere sie sicher ab. + +### 5.4 – n8n-Credentials anlegen +1) Öffne in **n8n** das linke Menü **Credentials → + Neu**. +2) Wähle **„X OAuth 2.0 API“** als Typ. +3) Kopiere im geöffneten Fenster die angezeigte **OAuth Redirect URL** – diese wird gleich im Developer-Portal benötigt. +4) Lasse dieses Credentials-Fenster in n8n geöffnet. + +👉 Screenshot geeignet: n8n – neues Credentials-Fenster mit sichtbarer Redirect-URL + +[!TIP] +Die Redirect-URL immer aus dem geöffneten n8n-Fenster kopieren, nicht manuell eintippen. + +### 5.5 – User-Authentication in X einrichten +1) Zurück im **Developer-Portal → Projects & Apps → App Details** wechseln. +2) Bei **User Authentication Settings** auf **„Set Up“** klicken. +3) **App Permissions:** „Read and Write“ auswählen. +4) **Type of App:** „Web App, Automated App or Bot“ auswählen. +5) Bei **Callback URL** die zuvor aus n8n kopierte Redirect-URL einfügen. +6) Bei **Homepage URL** die eigene Domain (z. B. `https://deinedomain.tld`) angeben. +7) **Save** klicken und die folgende Warnung bestätigen. + +👉 Screenshot geeignet: Formular „User Authentication Settings“ mit ausgewählter Berechtigung „Read and Write“ und Callback-URL + +[!IMPORTANT] +Falls die Berechtigung nicht auf „Read and Write“ steht, können später keine Posts veröffentlicht werden. + +### 5.6 – Client-Daten in n8n eintragen und Account verknüpfen +1) Nach dem Speichern erscheinen im Developer-Portal **Client ID** und **Client Secret**. +2) Zurück in das noch geöffnete Credentials-Fenster in n8n wechseln. +3) **Client ID** und **Client Secret** dort eintragen. +4) Auf **„Account verknüpfen“** klicken. +5) Es öffnet sich ein Pop-up-Fenster mit der X-Login-Seite. +6) Mit dem normalen X-Konto anmelden und die App **autorisieren**. + +[!WARNING] +Falls statt „Authorize App“ das X-Dashboard erscheint, das Fenster schließen und den Schritt „Account verknüpfen“ in n8n erneut ausführen. + +👉 Screenshot geeignet: Pop-up mit X-Login und „Authorize App“ + +Damit sind die **X-Credentials** in n8n eingerichtet und einsatzbereit. + +### 5.7 – Twitch-Auth-Credentials anlegen + +Damit n8n die geplanten Streams abrufen kann, brauchen wir eine eigene **Twitch-Anwendung** mit User-Token (Authorization-Code-Flow). +Die benötigte **OAuth-Redirect-URL** kommt direkt aus n8n, deshalb beginnen wir dort. + +#### 5.7.1 – Redirect-URL aus n8n kopieren +1) n8n öffnen → links **Credentials → + Neu**. +2) **„Twitch OAuth2 API“** als Typ auswählen. +3) Im sich öffnenden Fenster die angezeigte **OAuth Redirect URL** vollständig kopieren. +4) Fenster offenlassen, da wir gleich hierher zurückkehren. + +👉 Screenshot geeignet: n8n – neues Twitch-Credential mit sichtbarer Redirect-URL + +[!TIP] +Die Redirect-URL immer direkt aus n8n kopieren, nicht manuell eintippen – sonst schlägt die Authentifizierung fehl. + +#### 5.7.2 – Neue Anwendung in der Twitch-Developer-Console anlegen +1) Twitch-Developer-Console öffnen: [https://dev.twitch.tv/console/apps](https://dev.twitch.tv/console/apps). +2) Mit dem Twitch-Konto anmelden, das später für die Streams genutzt wird. +3) Auf **„Register Your Application“** klicken + *(in der deutschen Oberfläche: **„Deine Anwendung registrieren“**).* +4) Einen aussagekräftigen Namen vergeben, z. B. `n8n-stream-schedule`. +5) Unter **OAuth Redirect URLs** die zuvor aus n8n kopierte URL einfügen. +6) Unter **Category** „Application Integration“ auswählen. +7) Bei **Public / Öffentlich** die Option **„Öffentlich“** aktivieren. +8) Auf **Create** klicken. + +👉 Screenshot geeignet: Twitch-Developer-Console – Formular „Register Your Application / Deine Anwendung registrieren“ mit eingetragener Redirect-URL und aktivierter Option „Öffentlich“ + +[!IMPORTANT] +Die Redirect-URL muss exakt der aus n8n entsprechen, sonst schlägt die spätere Verknüpfung fehl. + +#### 5.7.3 – Client-ID und Secret sichern +1) Nach dem Erstellen erscheint die Anwendung in der Übersicht. +2) Auf **Manage** klicken. +3) Die **Client ID** kopieren. +4) Über **New Secret** einen neuen **Client Secret** erzeugen und kopieren. +5) Beide Werte sicher speichern. + +👉 Screenshot geeignet: Twitch-Developer-Console – App-Details mit sichtbarer Client ID und Button „New Secret“ + +[!WARNING] +Der Client Secret ist nur einmal sichtbar. Nicht öffentlich teilen und sicher aufbewahren. + +#### 5.7.4 – Credential in n8n fertigstellen +1) Zurück zu n8n in das noch geöffnete Credential-Fenster wechseln. +2) Folgende Felder ausfüllen: + * **Client ID:** Wert aus der Twitch-Anwendung einfügen + * **Client Secret:** Wert aus der Twitch-Anwendung einfügen + * **Auth URL:** `https://id.twitch.tv/oauth2/authorize` + * **Access Token URL:** `https://id.twitch.tv/oauth2/token` + * **Grant Type:** `Authorization Code` + * **Scope:** `user:read:broadcast` + * **Auth URI Query Parameters:** leer lassen + * **Authentication:** `Body` +3) Auf **„Account verknüpfen“** klicken. +4) Im neuen Fenster mit dem Twitch-Konto anmelden und die Anwendung **autorisieren**. +5) Falls statt „Authorize App“ das Twitch-Dashboard erscheint, Fenster schließen und den Vorgang **„Account verknüpfen“** erneut ausführen. + +👉 Screenshot geeignet: Twitch – OAuth-Freigabeseite mit „Authorize App“ + +[!NOTE] +Der Scope `user:read:broadcast` erlaubt n8n, die geplanten Streams des angegebenen Kanals über die Helix-API abzurufen. + +### 5.8 – Hinweis zu Access Tokens + +Mit den bisher eingerichteten **Credentials** für X und Twitch sind zwar die Schnittstellen verbunden, aber das reicht für den geplanten Workflow noch nicht aus. +Viele API-Endpunkte – insbesondere bei Twitch – benötigen zusätzlich einen **Access Token** (Bearer-Token) für jeden Abruf. + +Zwar kann dieser Token theoretisch auch manuell über die Developer-Seiten erzeugt und in n8n eingetragen werden, doch solche Tokens laufen nach kurzer Zeit ab (bei Twitch z. B. meist nach 60 Tagen oder früher). +Ein händisches Nachtragen würde bedeuten, dass der Workflow regelmäßig ausfällt und manuell nachgepflegt werden muss. + +[!IMPORTANT] +Wir werden deshalb im **Verlauf des Workflows** einen eigenen Node hinzufügen, der den Access Token automatisch anfordert und bei Bedarf erneuert. +So bleibt der Workflow stabil und benötigt keine manuelle Pflege. + +## Schritt 6 – Workflow in n8n aufbauen + +In diesem Schritt erstellen wir den eigentlichen Workflow in **n8n**. +Wir fügen die benötigten Nodes hinzu und konfigurieren sie so, dass der Workflow regelmäßig die geplanten Twitch-Streams prüft und bei Bedarf automatisch Ankündigungen auf X erstellt. + +### 6.1 – Cron Trigger (Workflow alle 30 Minuten starten) + +**Zweck:** +Der Cron-Trigger ist der Startpunkt des Workflows. +Er sorgt dafür, dass alle 30 Minuten geprüft wird, ob ein geplanter Stream in Kürze startet. + +**Node-Typ:** `Schedule Trigger` +**Name:** `Cron – Workflow alle 30 Minuten starten` + +**Einstellungen im Node:** +- **Trigger Interval:** `Minutes` +- **Minutes Between Triggers:** `30` + +👉 Screenshot geeignet: n8n – Schedule Trigger mit „Trigger Interval: Minutes“ und Wert „30“ + +[!NOTE] +Die Zeitzone wird nicht im Node eingestellt. +Sie richtet sich nach der **Workflow-Zeitzone** (oben rechts im Workflow → *Settings → Time Zone*). +Ist dort nichts gesetzt, gilt die Zeitzone des Servers, auf dem n8n läuft. + +[!TIP] +Die Intervalldauer (hier 30 Minuten) kann bei Bedarf angepasst werden. Für Ankündigungen kurz vor dem Stream reicht ein Intervall von 30 Minuten in der Regel aus. + +### 6.2 – Code Node: Access Token prüfen + +**Zweck:** +Dieser Node prüft bei jedem Durchlauf, ob bereits ein gültiger Twitch-Access-Token vorhanden ist und wie lange er noch gültig ist. +Wenn kein Token existiert oder die Restlaufzeit unter 24 Stunden liegt, markieren wir das für die nächste Stufe (Token erneuern). + +**Node-Typ:** `Code` +**Programmiersprache:** `JavaScript` +**Name:** `Code – Access Token prüfen` + +**Einstellungen:** +- **Language:** `JavaScript` +- Den folgenden Code einfügen: + +```javascript +// Liest gespeicherten Token aus den globalen Workflow-Daten +const sd = $getWorkflowStaticData('global'); +const token = sd.twitch_token || ''; +const expISO = sd.twitch_expires_at || ''; +const expMs = expISO ? new Date(expISO).getTime() : 0; + +const now = Date.now(); +const msLeft = expMs ? (expMs - now) : 0; +const hoursLeft = msLeft > 0 ? (msLeft / (1000*60*60)) : 0; + +// true, wenn kein Token vorhanden ist oder wenn er in <24h abläuft +const needsRefresh = !token || !expMs || hoursLeft < 24; + +return [{ + json: { + needsRefresh, + token, + expires_at: expISO, + hoursLeft: Number(hoursLeft.toFixed(2)) + } +}]; +``` + +👉 Screenshot geeignet: n8n – Code-Node mit eingefügtem JavaScript-Code + +[!NOTE] +Dieser Node speichert nichts, sondern liefert nur Statusdaten (`needsRefresh`, `hoursLeft`, `expires_at`). +Die tatsächliche Erneuerung des Tokens erfolgt später im Workflow. + +### 6.3 – IF Node: Access Token erneuern? + +**Zweck:** +Dieser Node prüft, ob aus dem vorherigen Code-Node das Flag `needsRefresh` auf **true** steht. +Nur in diesem Fall wird im nächsten Schritt ein neuer Access-Token angefordert. + +**Node-Typ:** `IF` +**Name:** `IF – Access Token erneuern?` + +**Einstellungen:** +- **Conditions:** + - `{{$json.needsRefresh}}` + - `is true` *(Type: Boolean)* + +👉 Screenshot geeignet: n8n – IF-Node mit Bedingung `{{$json.needsRefresh}}` → `is true (Boolean)` + +[!IMPORTANT] +Stelle sicher, dass der Vergleichs-Typ auf **Boolean** gesetzt ist, sonst wird der Wert eventuell als Text interpretiert und die Abfrage funktioniert nicht korrekt. + +[!NOTE] +- **TRUE-Pfad:** Token wird im nächsten Schritt erneuert. +- **FALSE-Pfad:** Es wird mit dem bestehenden Token fortgefahren. + +### 6.4 – HTTP Request: Neuen Twitch-Access-Token anfordern *(TRUE-Pfad)* + +**Zweck:** +Fordert über die Twitch-API einen neuen Access-Token an, wenn der IF-Node festgestellt hat, dass keiner vorhanden ist oder der aktuelle bald abläuft. + +**Node-Typ:** `HTTP Request` +**Name:** `HTTP – Access Token anfordern` + +**Einstellungen:** +- **Method:** `POST` +- **URL:** `https://id.twitch.tv/oauth2/token` +- **Send Body:** aktivieren +- **Content Type:** `Form-URL-Encoded` +- **Body Parameters:** + - `client_id` → Wert aus den in n8n hinterlegten Twitch-Credentials + - `client_secret` → Wert aus den Twitch-Credentials + - `grant_type` → `client_credentials` + +👉 Screenshot geeignet: n8n – HTTP-Request-Node mit konfigurierten Body-Parametern + +[!NOTE] +Der Node gibt als Antwort ein JSON-Objekt mit `access_token` und `expires_in` zurück. +Diese Werte werden im nächsten Node gespeichert. + +### 6.5 – Code Node: Neuen Access-Token speichern *(TRUE-Pfad)* + +**Zweck:** +Speichert den vom HTTP-Request erhaltenen Twitch-Access-Token und dessen Ablaufzeitpunkt in den globalen Workflow-Daten. +So bleibt der Token auch für spätere Workflow-Durchläufe verfügbar. + +**Node-Typ:** `Code` +**Programmiersprache:** `JavaScript` +**Name:** `Code – Access Token speichern` + +**Einstellungen:** +- **Language:** `JavaScript` +- Den folgenden Code einfügen: + +```javascript +// Speichert neuen Token + Ablaufzeit in Workflow-Static-Data +const sd = $getWorkflowStaticData('global'); + +const access = $json.access_token; +const ttlSec = Number($json.expires_in || 0); + +// Ablaufzeitpunkt berechnen und speichern +const expISO = new Date(Date.now() + ttlSec * 1000).toISOString(); + +sd.twitch_token = access; +sd.twitch_expires_at = expISO; + +// Optional: verbleibende Stunden ausgeben +const msLeft = (new Date(expISO).getTime() - Date.now()); +const hoursLeft = msLeft / (1000 * 60 * 60); + +return [{ + json: { + token: access, + expires_at: expISO, + hoursLeft: Number(hoursLeft.toFixed(2)) + } +}]; +``` + +👉 Screenshot geeignet: n8n – Code-Node mit eingefügtem JavaScript-Code + +[!NOTE] +Dieser Node speichert den neuen Token und gibt ihn zusätzlich mit seiner Restlaufzeit (`hoursLeft`) aus, sodass nachfolgende Nodes die Werte sofort verwenden können. + +### 6.6 – Set Node: Token aus Cache bereitstellen *(FALSE-Pfad)* + +**Zweck:** +Wenn der IF-Node feststellt, dass der vorhandene Token noch gültig ist, brauchen wir keinen neuen anzufordern. +Dieser Node liest die bereits gespeicherten Token-Daten aus den globalen Workflow-Daten und stellt sie den nachfolgenden Nodes zur Verfügung. + +**Node-Typ:** `Set` +**Name:** `Set – Token aus Cache bereitstellen` + +**Einstellungen:** +- **Mode:** `Add Field` +- **Felder hinzufügen:** + - `token` → `{{$json.token}}` + - `expires_at` → `{{$json.expires_at}}` + - `hoursLeft` → `{{$json.hoursLeft}}` + +👉 Screenshot geeignet: n8n – Set-Node mit eingetragenen drei Feldern (token, expires_at, hoursLeft) + +[!NOTE] +Der Set-Node wird nur im **FALSE-Pfad** des IF-Nodes ausgeführt. +Er sorgt dafür, dass auch ohne Erneuerung dieselben Felder (`token`, `expires_at`, `hoursLeft`) für die nächsten Schritte bereitstehen. + +### 6.7 – Merge Node: Token-Ströme zusammenführen + +**Zweck:** +Führt die beiden möglichen Pfade aus dem IF-Node wieder zusammen: +- TRUE-Pfad: liefert den **neu angeforderten Token** aus dem Code-Node „Access Token speichern“. +- FALSE-Pfad: liefert den **bereits vorhandenen Token** aus dem Set-Node „Token aus Cache bereitstellen“. + +So steht dem Workflow anschließend unabhängig vom Pfad derselbe Datensatz für die weiteren Schritte zur Verfügung. + +**Node-Typ:** `Merge` +**Name:** `Merge – Token zusammenführen` + +**Einstellungen:** +- **Mode:** `Append` +- **Number of Inputs:** `2` + +👉 Screenshot geeignet: n8n – Merge-Node mit „Mode: Append“ und „Number of Inputs: 2“ + +[!NOTE] +Da jeweils nur einer der beiden Pfade aktiv ist, genügt der Append-Modus, um den ausgegebenen Datensatz an den Hauptfluss zurückzugeben. + +### 6.8 – HTTP Request: Twitch-Termine laden + +**Zweck:** +Dieser Node ruft über die Twitch-Helix-API den aktuellen Stream-Zeitplan des Kanals ab, um zu prüfen, ob innerhalb der nächsten 30 Minuten ein Stream startet. + +**Node-Typ:** `HTTP Request` +**Name:** `HTTP – Twitch-Termine laden` + +#### Twitch-User-ID ermitteln (ohne Programmierung) +Die `broadcaster_id` ist eine **numerische ID**, die jedem Twitch-Kanal zugeordnet ist. +Du brauchst diese Zahl (nicht den Kanalnamen), um die API-Abfrage auszuführen. + +**So findest du sie leicht:** + +1) Öffne im Browser die Seite + [https://streamscharts.com/tools/convert-username](https://streamscharts.com/tools/convert-username) +2) Gib deinen Twitch-Kanalnamen ein (z. B. `bratonien_tv`). +3) Klicke auf **Convert**. +4) Die angezeigte Zahl unter „Channel ID“ ist deine `broadcaster_id`. +5) Kopiere diese Zahl und trage sie später im HTTP-Request-Node in das Feld **broadcaster_id** ein. + +👉 Screenshot geeignet: StreamsCharts-Tool mit eingegebenem Kanalnamen und angezeigter Channel-ID + +[!TIP] +Dieses Tool verwendet offiziell die Twitch-API, du brauchst dort keine eigenen Entwickler-Anmeldungen. + +#### Einstellungen im Node +- **Method:** `GET` +- **URL:** `https://api.twitch.tv/helix/schedule` +- **Send Query:** aktivieren +- **Query Parameters:** + - `broadcaster_id` → `` des Kanals + - `start_time` → `{{$now.toUTC().startOf('day').toISO()}}` +- **Send Headers:** aktivieren +- **Header Parameters:** + - `Authorization` → `Bearer {{$json.token}}` + - `Client-ID` → `` + +👉 Screenshot geeignet: n8n – HTTP-Request-Node mit eingetragenen Query- und Header-Parametern + +[!NOTE] +Die `broadcaster_id` ist die eindeutige numerische ID des Kanals. +Der Header **Authorization** enthält den Access-Token aus dem Merge-Node. +Der Header **Client-ID** ist zwingend erforderlich, sonst verweigert die Helix-API den Zugriff. + +### 6.8 – HTTP Request: Twitch-Termine laden + +**Zweck:** +Dieser Node ruft über die Twitch-Helix-API den aktuellen Stream-Zeitplan des Kanals ab, um zu prüfen, ob innerhalb der nächsten 30 Minuten ein Stream startet. + +**Node-Typ:** `HTTP Request` +**Name:** `HTTP – Twitch-Termine laden` + +#### Twitch-User-ID ermitteln (ohne Programmierung) +Die `broadcaster_id` ist eine **numerische ID**, die jedem Twitch-Kanal zugeordnet ist. +Du brauchst diese Zahl (nicht den Kanalnamen), um die API-Abfrage auszuführen. + +**So findest du sie leicht:** + +1) Öffne im Browser die Seite + [https://streamscharts.com/tools/convert-username](https://streamscharts.com/tools/convert-username) +2) Gib deinen Twitch-Kanalnamen ein (z. B. `bratonien_tv`). +3) Klicke auf **Convert**. +4) Die angezeigte Zahl unter „Channel ID“ ist deine `broadcaster_id`. +5) Kopiere diese Zahl und trage sie später im HTTP-Request-Node in das Feld **broadcaster_id** ein. + +👉 Screenshot geeignet: StreamsCharts-Tool mit eingegebenem Kanalnamen und angezeigter Channel-ID + +[!TIP] +Dieses Tool verwendet offiziell die Twitch-API und erfordert keine eigenen Entwickler-Anmeldungen. + +#### Einstellungen im Node +- **Method:** `GET` +- **URL:** `https://api.twitch.tv/helix/schedule` +- **Authentication:** `Generic Credential Type` +- **Generic Auth Type:** `OAuth2 API` → wähle hier die zuvor in n8n angelegten **Twitch-Credentials** aus +- **Send Query Parameters:** aktivieren +- **Query Parameters:** + - **Name:** `broadcaster_id` → `` + - **Name:** `start_time` → `{{ $now.toUTC().startOf('day').toISO() }}` +- **Send Headers:** aktivieren +- **Header Parameters:** + - **Name:** `Client-ID` → `` + +👉 Screenshot geeignet: n8n – HTTP-Request-Node mit allen sichtbaren Parametern wie auf den beiden Referenz-Screenshots + +[!NOTE] +Der Access-Token wird über die ausgewählte OAuth2-Credential automatisch mitgesendet. +Zusätzlich ist nur der **Client-ID**-Header notwendig. +Die Option **Never Error** bleibt deaktiviert, da sie hier nicht benötigt wird. + +### 6.9 – Code-Node: „Parse ICS“ + +**Zweck:** +Dieser Node verarbeitet die Antwort des Nodes **„Twitch-Termine laden“**. +Er liest das JSON aus der Helix-API und erzeugt für jedes Stream-Segment ein einheitliches Objekt mit Start- und Endzeit, Titel, Beschreibung und einer Hash-ID. +So können wir die Termine später leichter filtern und weiterverarbeiten. + +**Node-Typ:** `Code` +**Name:** `Parse ICS` + +👉 Screenshot geeignet: n8n – geöffneter Code-Node mit eingefügtem Skript + +#### Node-Einstellungen +- **Programming Language:** `JavaScript` +- Weitere Einstellungen bleiben auf Standard. + +#### Eingabe +Der Node erwartet die Antwort des HTTP-Requests **„Twitch-Termine laden“**. +Der Inhalt liegt meist als Text-String in `items[0].json.body` vor. + +#### Code-Inhalt +Kopiere den folgenden Code komplett in das Editorfeld des Code-Nodes: + +``` +/** + * INPUT: items[0].json ODER items[0].json.body (String) vom HTTP Request /helix/schedule + * OUTPUT: pro Segment ein Item in folgendem Schema: + * { uid, cancelled, title, description, startIso, endIso, tz, hash } + */ + +const input = items[0]?.json ?? {}; +let payload; + +if (typeof input.body === 'string') { + // HTTP-Node hat "Response Format: Text" -> JSON-String in body + try { payload = JSON.parse(input.body); } + catch (e) { throw new Error('HTTP body ist kein gültiges JSON'); } +} else if (input.data) { + // HTTP-Node hat "Response Format: JSON" + payload = input; +} else if (typeof input === 'string') { + payload = JSON.parse(input); +} else { + throw new Error('Unerwartete Eingabeform. Erwartet JSON oder body-String.'); +} + +const segments = Array.isArray(payload.data?.segments) ? payload.data.segments : []; + +// Hash für eindeutige Segmentkennung +function hashHex(s) { + let h = 5381; + for (let i = 0; i < s.length; i++) h = ((h << 5) + h) ^ s.charCodeAt(i); + return (h >>> 0).toString(16).padStart(8, '0'); +} + +function buildTitle() { return 'Streamtime'; } + +function buildDesc(seg) { + const lines = []; + if (seg.title) lines.push(`Originaltitel: ${seg.title}`); + if (seg.category?.name) lines.push(`Kategorie/Game: ${seg.category.name}`); + return lines.join('\n'); +} + +const tz = 'Europe/Berlin'; + +const out = segments.map(seg => { + const startIso = seg.start_time; // ISO-UTC laut Twitch + const endIso = seg.end_time; + const title = buildTitle(); + const description = buildDesc(seg); + const hash = hashHex(`${title}|${startIso}|${endIso}|${description}`); + + return { + json: { + uid: seg.id, + cancelled: Boolean(seg.canceled_until), + title, description, + startIso, endIso, tz, + hash + } + }; +}); + +return out; +``` + +[!NOTE] +- `tz` ist fest auf `Europe/Berlin` gesetzt, kann aber bei Bedarf angepasst werden. +- Das Feld `cancelled` wird genutzt, um abgesagte Streams zu kennzeichnen. +- Jeder Stream-Eintrag erhält einen Hash, der später als eindeutiger Schlüssel dient. \ No newline at end of file