Kapitel 10/Free Rhohtext.md aktualisiert
This commit is contained in:
@@ -421,6 +421,44 @@ Ein händisches Nachtragen würde bedeuten, dass der Workflow regelmäßig ausf
|
||||
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.
|
||||
|
||||
### 5.9 – Nextcloud-Credentials anlegen
|
||||
|
||||
**Zweck:**
|
||||
Damit n8n automatisch auf Dateien in deiner Nextcloud zugreifen kann (z. B. für vorbereitete Posts oder temporäre Daten), benötigen wir ein eigenes App-Passwort und ein passendes Credential in n8n.
|
||||
|
||||
#### In Nextcloud: App-Passwort erstellen
|
||||
|
||||
1) Melde dich in deiner **Nextcloud** an.
|
||||
2) Öffne oben rechts das **Benutzermenü → Einstellungen**.
|
||||
3) Wähle im linken Menü den Punkt **Sicherheit** (engl. „Security“).
|
||||
4) Scrolle nach unten bis zum Bereich **App-Passwörter**.
|
||||
5) Vergib unter „Neues App-Passwort“ einen Namen, z. B. `n8n`.
|
||||
6) Klicke auf **App-Passwort erstellen**.
|
||||
7) Kopiere den angezeigten Schlüssel sofort – er wird nur einmal angezeigt.
|
||||
👉 Screenshot geeignet: Nextcloud – Bereich *Sicherheit / App-Passwörter*
|
||||
|
||||
[!IMPORTANT]
|
||||
Nur dieses **App-Passwort** wird in n8n verwendet, **nicht** dein normales Login-Passwort.
|
||||
Das App-Passwort kann bei Bedarf jederzeit widerrufen oder neu erstellt werden.
|
||||
|
||||
#### In n8n: Credential hinzufügen
|
||||
|
||||
1) Öffne in n8n oben links **+ → Credentials → Nextcloud**.
|
||||
2) Trage folgende Werte ein:
|
||||
|
||||
| Feld | Wert / Beschreibung |
|
||||
|------|---------------------|
|
||||
| **Name** | `Nextcloud` |
|
||||
| **Base URL** | `https://deine-domain.tld` *(ohne `/remote.php/...`)* |
|
||||
| **Username** | dein Nextcloud-Benutzername |
|
||||
| **Password** | das zuvor erstellte App-Passwort |
|
||||
|
||||
3) Klicke auf **Save**.
|
||||
👉 Screenshot geeignet: n8n – Nextcloud-Credential mit Base URL und App-Passwort
|
||||
|
||||
[!NOTE]
|
||||
Das Credential „Nextcloud“ wird später in Schritt **6.13** verwendet, um Dateien wie `posts.txt` automatisiert aus der Cloud zu laden und lokal zu verarbeiten.
|
||||
|
||||
## Schritt 6 – Workflow in n8n aufbauen
|
||||
|
||||
In diesem Schritt erstellen wir den eigentlichen Workflow in **n8n**.
|
||||
@@ -810,3 +848,179 @@ return out;
|
||||
- `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.
|
||||
|
||||
### 6.10 – Code-Node: „Nächstes Event finden“
|
||||
|
||||
**Zweck:**
|
||||
Dieser Node wählt aus allen vom **„Parse ICS“**-Node gelieferten Terminen den **nächsten bevorstehenden Stream** aus.
|
||||
Er liefert das Start-Datum und die relevanten Infos des nächsten Events als einzelnes Item.
|
||||
|
||||
**Node-Typ:** `Code`
|
||||
**Name:** `Nächstes Event finden`
|
||||
|
||||
👉 Screenshot geeignet: n8n – geöffneter Code-Node mit dem folgenden Script
|
||||
|
||||
#### Node-Einstellungen
|
||||
- **Programming Language:** `JavaScript`
|
||||
- Alle weiteren Optionen bleiben auf Standard.
|
||||
|
||||
#### Eingabe
|
||||
Dieser Node erwartet als Input die komplette Liste der Stream-Segmente aus dem Node **„Parse ICS“**.
|
||||
|
||||
#### Code-Inhalt
|
||||
Kopiere den folgenden Code vollständig in das Code-Feld:
|
||||
|
||||
```
|
||||
/**
|
||||
* INPUT: Array von Items aus "Parse ICS"
|
||||
* OUTPUT: nur das nächste bevorstehende Event
|
||||
*/
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
const upcoming = items
|
||||
.map(i => i.json)
|
||||
.filter(e => !e.cancelled && new Date(e.startIso).getTime() > now)
|
||||
.sort((a, b) => new Date(a.startIso) - new Date(b.startIso));
|
||||
|
||||
if (upcoming.length === 0) {
|
||||
return [{ json: { message: 'Kein bevorstehender Stream gefunden' } }];
|
||||
}
|
||||
|
||||
const next = upcoming[0];
|
||||
|
||||
return [{
|
||||
json: {
|
||||
uid: next.uid,
|
||||
title: next.title,
|
||||
description: next.description,
|
||||
startIso: next.startIso,
|
||||
endIso: next.endIso,
|
||||
tz: next.tz,
|
||||
hash: next.hash
|
||||
}
|
||||
}];
|
||||
```
|
||||
|
||||
[!TIP]
|
||||
Dieser Node gibt **genau ein Item** mit dem nächsten Stream zurück.
|
||||
Fehlt ein Termin, wird eine kurze Meldung im Feld `message` ausgegeben.
|
||||
|
||||
### 6.11 – Code-Node: „Stream prüfen“
|
||||
|
||||
**Zweck:**
|
||||
Dieser Node überprüft, ob der nächste geplante Stream in den kommenden **30 Minuten** beginnt und ob für diesen Stream bereits ein Post veröffentlicht wurde.
|
||||
Nur wenn beide Bedingungen erfüllt sind, wird der Workflow fortgesetzt.
|
||||
|
||||
**Node-Typ:** `Code`
|
||||
**Name:** `Stream prüfen`
|
||||
|
||||
👉 Screenshot geeignet: n8n – geöffneter Code-Node mit folgendem Script
|
||||
|
||||
#### Node-Einstellungen
|
||||
- **Programmiersprache:** `JavaScript`
|
||||
- Keine weiteren Optionen ändern
|
||||
|
||||
#### Eingabe
|
||||
Der Node erhält den Output aus **„Nächstes Event finden“** – also einen einzelnen Stream-Eintrag mit `uid`, `title`, `startIso` und weiteren Feldern.
|
||||
|
||||
#### Code-Inhalt
|
||||
Kopiere den folgenden Code:
|
||||
|
||||
```
|
||||
/**
|
||||
* Prüft:
|
||||
* 1. Startet der Stream in ≤ 30 Minuten?
|
||||
* 2. Wurde bereits ein Post erstellt?
|
||||
*
|
||||
* Zeitumwandlung: Twitch liefert UTC ("2025-10-15T16:00:00Z").
|
||||
* Wir rechnen in lokale Zeit (Europe/Berlin), um MEZ/MESZ korrekt zu berücksichtigen.
|
||||
*/
|
||||
|
||||
const event = items[0]?.json ?? {};
|
||||
if (!event.startIso) {
|
||||
return [{
|
||||
json: {
|
||||
postAllowed: false,
|
||||
reason: 'Kein Termin gefunden'
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
// Aktuelle Zeit + Startzeit in lokale Zeitzone überführen
|
||||
const tz = 'Europe/Berlin';
|
||||
const now = new Date(); // lokale Zeit des n8n-Servers
|
||||
const startUtc = new Date(event.startIso);
|
||||
|
||||
// Startzeit in lokales Format (inkl. Sommer-/Winterzeit)
|
||||
const startLocal = new Date(startUtc.toLocaleString('en-US', { timeZone: tz }));
|
||||
|
||||
// Differenz in Minuten
|
||||
const diffMin = (startLocal - now) / 60000;
|
||||
const within30 = diffMin > 0 && diffMin <= 30;
|
||||
|
||||
// Zugriff auf global gespeicherte IDs
|
||||
const sd = $getWorkflowStaticData('global');
|
||||
sd.postedIds = sd.postedIds || [];
|
||||
|
||||
const alreadyPosted = sd.postedIds.includes(event.uid);
|
||||
|
||||
// Wenn innerhalb von 30 Minuten und noch kein Post → erlauben
|
||||
if (within30 && !alreadyPosted) {
|
||||
sd.postedIds.push(event.uid);
|
||||
return [{
|
||||
json: {
|
||||
...event,
|
||||
postAllowed: true,
|
||||
reason: 'Stream startet bald, noch kein Post vorhanden',
|
||||
localStart: startLocal.toISOString(),
|
||||
diffMinutes: Math.round(diffMin)
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
// Keine Aktion notwendig
|
||||
return [{
|
||||
json: {
|
||||
...event,
|
||||
postAllowed: false,
|
||||
reason: alreadyPosted
|
||||
? 'Für diesen Stream wurde bereits gepostet'
|
||||
: 'Stream liegt außerhalb des 30-Minuten-Fensters',
|
||||
localStart: startLocal.toISOString(),
|
||||
diffMinutes: Math.round(diffMin)
|
||||
}
|
||||
}];
|
||||
```
|
||||
|
||||
[!NOTE]
|
||||
Dieser Node speichert die **UID jedes angekündigten Streams** im Workflow-Speicher (`global static data`).
|
||||
So wird sichergestellt, dass kein Termin doppelt angekündigt wird – auch nicht nach einem Neustart des Systems.
|
||||
Der Zeitvergleich erfolgt in **MEZ/MESZ (Europe/Berlin)**, damit lokale Streamzeiten korrekt erkannt werden.
|
||||
|
||||
### 6.12 – IF-Node: „Post ausführen?“
|
||||
|
||||
**Zweck:**
|
||||
Der IF-Node entscheidet, ob tatsächlich ein Social-Post (z. B. auf X) ausgelöst werden soll.
|
||||
Er prüft das Feld `postAllowed`, das aus dem vorherigen Code-Node stammt.
|
||||
|
||||
**Node-Typ:** `IF`
|
||||
**Name:** `Post ausführen?`
|
||||
|
||||
👉 Screenshot geeignet: n8n – geöffneter IF-Node mit konfigurierter Bedingung
|
||||
|
||||
#### Node-Einstellungen
|
||||
|
||||
**Conditions**
|
||||
|
||||
| Feld | Einstellung |
|
||||
|------|--------------|
|
||||
| **Left Value** | `{{$json.postAllowed}}` |
|
||||
| **Operator** | `is true` |
|
||||
| **Type** | `Boolean` |
|
||||
|
||||
[!NOTE]
|
||||
- Der *True*-Pfad wird nur ausgeführt, wenn der Stream innerhalb der nächsten 30 Minuten startet **und** noch kein Post gesendet wurde.
|
||||
- Der *False*-Pfad kann später genutzt werden, um Debug-Logs oder Benachrichtigungen zu schreiben (z. B. „Kein Post nötig“).
|
||||
|
||||
👉 Screenshot geeignet: n8n – IF-Node mit `{{$json.postAllowed}} is true (Boolean)`
|
||||
Reference in New Issue
Block a user