1056 lines
40 KiB
Markdown
1056 lines
40 KiB
Markdown
# Premium – WAHA: Automatisierte Statusmeldungen & KI-gestützte Antworten
|
||
|
||
WAHA ist im UCC weit mehr als ein Werkzeug zum Senden einzelner WhatsApp-Nachrichten.
|
||
Im Premium-Kapitel wird der Dienst zu einem **intelligenten Kommunikationsmodul**, das auf Ereignisse in deinem System reagieren und Informationen automatisiert weitergeben kann.
|
||
|
||
Ziel ist eine **vollständige Integration von WAHA in deine bestehende Infrastruktur** – vor allem in Verbindung mit n8n und Netdata.
|
||
So kannst du dich über wichtige Systemereignisse informieren lassen oder, optional, eingehende Nachrichten automatisch beantworten lassen – datensouverän, lokal und ohne externe Cloud-APIs.
|
||
|
||
Das Kapitel ist in zwei praxisnahe Szenarien unterteilt:
|
||
|
||
1. **Automatische Statusbenachrichtigung über WhatsApp**
|
||
– Wenn Netdata einen Alarm oder kritischen Wert erkennt, löst n8n automatisch eine WhatsApp-Nachricht über WAHA aus.
|
||
– Du erhältst sofort eine Benachrichtigung über Auslastung, Fehler oder Warnungen, ohne dich aktiv einloggen zu müssen.
|
||
|
||
2. **KI-gestützte, regelkonforme Antwortfunktion**
|
||
– Eine eingehende WhatsApp-Nachricht wird von WAHA empfangen, in n8n verarbeitet und an eine lokale KI weitergeleitet.
|
||
– Die KI generiert eine personalisierte Antwort, die über WAHA zurückgesendet wird.
|
||
– Diese Variante bewegt sich aktuell **im erlaubten Rahmen**, da sie nur auf vom Nutzer initiierte Nachrichten reagiert,
|
||
liegt jedoch **im Graubereich zukünftiger Richtlinienänderungen**.
|
||
|
||
> [!CAUTION]
|
||
> Die Nutzung einer automatisierten KI-Antwort über WAHA ist derzeit zulässig, solange jede Kommunikation vom Nutzer selbst initiiert wird
|
||
> und keine Werbung oder massenhafte Nachrichten versendet werden.
|
||
> Beachte jedoch, dass Meta die WhatsApp-Richtlinien jederzeit ändern kann.
|
||
> Sollte WhatsApp künftig KI-basierte Antworten oder WAHA-ähnliche Systeme einschränken,
|
||
> kann dieses Setup Anpassungen erfordern.
|
||
|
||
👉 **Screenshot geeignet:** Diagramm mit Datenfluss zwischen Netdata, n8n, KI-Agent und WAHA
|
||
|
||
Am Ende dieses Kapitels verfügst du über ein vollständig integriertes Benachrichtigungs- und Kommunikationssystem,
|
||
das deine Infrastruktur in Echtzeit überwacht und bei Bedarf automatisiert reagiert –
|
||
ein klarer Schritt in Richtung eines selbstlernenden, souveränen Kontrollzentrums.
|
||
|
||
---
|
||
|
||
## Statusbenachrichtigung über WAHA
|
||
|
||
### Schritt 1 – n8n-Workflow mit Deduplizierung und WAHA-Versand
|
||
|
||
Ziel: Ein **n8n-Workflow** soll Systemereignisse aus Netdata empfangen, diese in Textform bringen, doppelte Alarme herausfiltern und anschließend über **WAHA** als WhatsApp-Nachricht versenden.
|
||
Damit entsteht ein zentraler Benachrichtigungsweg für kritische Zustände im gesamten UCC.
|
||
|
||
👉 **Screenshot geeignet:** n8n-Canvas mit drei verbundenen Nodes: *Webhook (POST)* → *Function (Nachricht & Filter)* → *HTTP Request (WAHA)*
|
||
|
||
#### Node 1 – Webhook (Trigger)
|
||
**Zweck:** Erstellt eine Empfangsadresse, über die Netdata Alarmmeldungen an n8n sendet.
|
||
|
||
**Node-Typ:** Webhook
|
||
**Name:** Eingang – Statusalarm
|
||
**Parameter:**
|
||
- **HTTP Method:** `POST`
|
||
- **Path:** `/waha/statusalarm`
|
||
- **Response Mode:** `Last Node`
|
||
- **Response Code:** `200`
|
||
|
||
> [!NOTE]
|
||
> Nach dem Speichern zeigt n8n automatisch die Webhook-URL an, z. B.:
|
||
> `https://n8n.deinedomain.tld/webhook/waha/statusalarm`
|
||
> Diese URL wird später in der Netdata-Alarmregel eingetragen, damit Netdata Alarme direkt an diesen Workflow sendet.
|
||
|
||
👉 **Screenshot geeignet:** Webhook-Node mit sichtbarer URL `.../webhook/waha/statusalarm`
|
||
|
||
#### Node 2 – Function (Nachricht aufbereiten + Dedupe)
|
||
**Zweck:** Formatiert die eingehenden Daten und verhindert, dass wiederholt identische Alarme mehrfach gesendet werden.
|
||
Die Funktion merkt sich den letzten Alarm jedes Containers für 10 Minuten.
|
||
Wenn innerhalb dieses Zeitraums derselbe Host und Alarm erneut eintreffen, wird die Nachricht unterdrückt.
|
||
|
||
**Node-Typ:** Function
|
||
**Name:** Nachricht & Filter
|
||
**Code:**
|
||
```js
|
||
const p = items[0].json || {};
|
||
const host = p.host || 'unbekannt';
|
||
const alarm = p.alarm || 'Alarm';
|
||
const status = p.status || 'warn';
|
||
const value = (p.value !== undefined && p.value !== null) ? String(p.value) : '-';
|
||
const when = p.when || new Date().toISOString();
|
||
|
||
const key = `${host}_${alarm}_${status}`;
|
||
const now = Date.now();
|
||
const last = $flow.get(key) || 0;
|
||
const interval = 10 * 60 * 1000; // 10 Minuten
|
||
|
||
if (now - last < interval) {
|
||
// gleicher Alarm innerhalb von 10 Minuten -> überspringen
|
||
return [];
|
||
}
|
||
|
||
$flow.set(key, now);
|
||
|
||
const text =
|
||
`⚠️ ${alarm}\n` +
|
||
`Host: ${host}\n` +
|
||
`Status: ${status}\n` +
|
||
`Wert: ${value}\n` +
|
||
`Zeit: ${when}`;
|
||
|
||
return [{ json: { text } }];
|
||
```
|
||
|
||
> [!TIP]
|
||
> Die Filterzeit lässt sich über die Variable `interval` anpassen.
|
||
> Für produktive Umgebungen mit vielen Containern sind 10 bis 15 Minuten meist ideal.
|
||
|
||
👉 **Screenshot geeignet:** Function-Node mit geöffnetem Code-Editor, markiertem Kommentar `// gleicher Alarm innerhalb von 10 Minuten`
|
||
|
||
#### Node 3 – HTTP Request (WAHA – Statusmeldung senden)
|
||
**Zweck:** Sendet die aufbereitete Nachricht über WAHA an WhatsApp.
|
||
|
||
**Node-Typ:** HTTP Request
|
||
**Name:** WAHA – Statusmeldung senden
|
||
**Parameter:**
|
||
- **Method:** `POST`
|
||
- **URL:**
|
||
```
|
||
https://waha.deinedomain.tld/api/sendText
|
||
```
|
||
- **Headers:**
|
||
- `Content-Type`: `application/json`
|
||
- *(optional)* `Authorization`: `Bearer <DEIN_WAHA_API_TOKEN>`
|
||
- **Body → JSON:**
|
||
```json
|
||
{
|
||
"chatId": "49XXXXXXXXXXX@c.us",
|
||
"text": "={{$json[\"text\"]}}"
|
||
}
|
||
```
|
||
|
||
> [!NOTE]
|
||
> **Chat-ID ermitteln:**
|
||
> 1. Sende mit deinem WhatsApp-Gerät eine Testnachricht an den Ziel-Chat.
|
||
> 2. Öffne `https://waha.deinedomain.tld/api/messages`.
|
||
> 3. Kopiere den Wert `chatId` aus der letzten eingegangenen Nachricht.
|
||
> – Einzelchat endet auf `@c.us`
|
||
> – Gruppenchat endet auf `@g.us`
|
||
> 4. Trage diese ID im Node ein.
|
||
|
||
> [!IMPORTANT]
|
||
> WAHA muss im Webinterface den Status **Connected** anzeigen.
|
||
> Bei *Pairing* oder *Disconnected* kann keine Nachricht gesendet werden.
|
||
|
||
👉 **Screenshot geeignet:** HTTP-Request-Node mit sichtbarer `chatId`
|
||
|
||
#### Test des Workflows
|
||
Der Workflow kann manuell getestet werden, bevor Netdata angebunden ist:
|
||
|
||
```bash
|
||
curl -X POST "https://n8n.deinedomain.tld/webhook/waha/statusalarm" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"host": "ucc-core-01",
|
||
"alarm": "CPU high",
|
||
"status": "crit",
|
||
"value": "96%",
|
||
"when": "2025-10-20T18:42:00Z"
|
||
}'
|
||
```
|
||
|
||
Wenn alles funktioniert, erscheint die Meldung wenige Sekunden später als WhatsApp-Nachricht.
|
||
|
||
👉 **Screenshot geeignet:** WhatsApp-Chat mit empfangener Meldung „⚠️ CPU high …“
|
||
|
||
> [!TIP]
|
||
> Wenn keine Nachricht ankommt, prüfe in n8n die **Execution Logs**.
|
||
> Dort sollte der HTTP-Request-Node den Statuscode `200` zurückgeben.
|
||
> Wird keine Ausführung gestartet, ist meist der Webhook-Pfad falsch oder Netdata erreicht den Server nicht.
|
||
|
||
### Schritt 2 – Netdata-Alarmregel im Parent-LXC für WAHA-Benachrichtigung
|
||
|
||
Die zentrale Netdata-Instanz im UCC empfängt bereits alle Metriken der Child-Container per Streaming.
|
||
Damit entfällt die Notwendigkeit, in jedem einzelnen LXC eigene Alarme zu konfigurieren.
|
||
Stattdessen wird **eine einzige Alarmregel** direkt im **Parent-Container** hinterlegt,
|
||
die auf aggregierte Systemwerte zugreift und bei Erreichen bestimmter Schwellwerte eine Nachricht über **n8n → WAHA** sendet.
|
||
|
||
👉 **Screenshot geeignet:** Netdata-Parent-Dashboard mit aktivem Alarm für einen Child-Container
|
||
|
||
> [!INFO]
|
||
> Die zentrale Überwachung mit Netdata setzt voraus, dass alle Child-LXC-Container korrekt angebunden sind.
|
||
> Im **Kapitel 15 – Netdata** wird beschrieben, wie du das Streaming-System einrichtest
|
||
> und wie die einzelnen Container ihre Metriken an den Parent weitergeben.
|
||
> Es wird **dringend empfohlen**, dieses Kapitel vollständig umzusetzen,
|
||
> bevor du hier mit der WAHA-Benachrichtigung fortfährst –
|
||
> sonst können Alarme fehlen oder falsche Hostnamen übermittelt werden.
|
||
|
||
#### Zentrale Alarmregel anlegen
|
||
|
||
1. Öffne auf deinem **Netdata-Parent-LXC** die Health-Konfiguration:
|
||
```bash
|
||
nano /etc/netdata/health.d/waha_notify.conf
|
||
```
|
||
|
||
2. Füge folgende Definition ein:
|
||
```
|
||
alarm: ucc_cpu_high
|
||
on: system.cpu
|
||
lookup: average -1m unaligned of user + system
|
||
units: %
|
||
every: 1m
|
||
warn: $this > 80
|
||
crit: $this > 95
|
||
exec: /usr/libexec/netdata/plugins.d/alarm-notify.sh
|
||
to: webhook
|
||
WEBHOOK_URL="https://n8n.deinedomain.tld/webhook/waha/statusalarm"
|
||
WEBHOOK_HEADER="Content-Type: application/json"
|
||
WEBHOOK_BODY="{\"host\":\"${hostname}\",\"alarm\":\"CPU high\",\"status\":\"${status}\",\"value\":\"${value}\",\"when\":\"$(date -Iseconds)\"}"
|
||
```
|
||
|
||
Diese Regel löst aus, sobald die durchschnittliche CPU-Last eines überwachten Systems über **80 %** steigt (Warnung)
|
||
oder **95 %** erreicht (Kritisch).
|
||
Netdata ruft dann automatisch den im ersten Schritt erstellten **n8n-Webhook** auf,
|
||
der die Nachricht an WAHA weiterleitet.
|
||
|
||
3. Datei speichern (STRG + O, Enter) und schließen (STRG + X).
|
||
|
||
4. Konfiguration neu laden:
|
||
```bash
|
||
systemctl restart netdata
|
||
```
|
||
|
||
👉 **Screenshot geeignet:** geöffnete `waha_notify.conf` mit markierten Zeilen `WEBHOOK_URL` und `WEBHOOK_BODY`
|
||
|
||
#### Test des zentralen Alarms
|
||
|
||
Um die Funktion zu prüfen, kann ein Test-Alarm ausgelöst werden:
|
||
|
||
```bash
|
||
sudo -u netdata /usr/libexec/netdata/plugins.d/alarm-notify.sh test webhook
|
||
```
|
||
|
||
Wenn die Konfiguration korrekt ist,
|
||
sollte im n8n-Log eine eingehende Anfrage erscheinen
|
||
und kurz darauf eine WhatsApp-Nachricht mit dem simulierten Alarmtext eintreffen.
|
||
|
||
👉 **Screenshot geeignet:** WhatsApp-Chat mit empfangener Testnachricht „⚠️ CPU high … (Test)“
|
||
|
||
> [!NOTE]
|
||
> Diese Konfiguration gilt global für alle über Streaming verbundenen Systeme.
|
||
> Netdata sendet bei jeder betroffenen Instanz den Hostnamen automatisch mit (`${hostname}`),
|
||
> sodass du in WhatsApp genau siehst, **welcher Container** den Alarm ausgelöst hat.
|
||
|
||
#### Erweiterung auf weitere Ressourcen
|
||
|
||
Analog zur CPU-Überwachung können weitere Alarme hinzugefügt werden.
|
||
Dazu kopierst du den Block und passt lediglich den Metrikpfad (`on:`) und Namen (`alarm:`) an.
|
||
|
||
Beispiele:
|
||
|
||
**RAM-Überwachung**
|
||
```
|
||
alarm: ucc_ram_high
|
||
on: system.ram
|
||
lookup: average -1m unaligned of used
|
||
units: %
|
||
warn: $this > 80
|
||
crit: $this > 95
|
||
exec: /usr/libexec/netdata/plugins.d/alarm-notify.sh
|
||
to: webhook
|
||
WEBHOOK_URL="https://n8n.deinedomain.tld/webhook/waha/statusalarm"
|
||
WEBHOOK_HEADER="Content-Type: application/json"
|
||
WEBHOOK_BODY="{\"host\":\"${hostname}\",\"alarm\":\"RAM high\",\"status\":\"${status}\",\"value\":\"${value}\",\"when\":\"$(date -Iseconds)\"}"
|
||
```
|
||
|
||
**Speicherplatz-Überwachung**
|
||
```
|
||
alarm: ucc_disk_low
|
||
on: disk_space._
|
||
lookup: max -5m unaligned of used
|
||
units: %
|
||
warn: $this > 85
|
||
crit: $this > 95
|
||
exec: /usr/libexec/netdata/plugins.d/alarm-notify.sh
|
||
to: webhook
|
||
WEBHOOK_URL="https://n8n.deinedomain.tld/webhook/waha/statusalarm"
|
||
WEBHOOK_HEADER="Content-Type: application/json"
|
||
WEBHOOK_BODY="{\"host\":\"${hostname}\",\"alarm\":\"Disk usage high\",\"status\":\"${status}\",\"value\":\"${value}\",\"when\":\"$(date -Iseconds)\"}"
|
||
```
|
||
|
||
> [!TIP]
|
||
> So entsteht eine skalierbare Struktur mit mehreren einfachen Health-Regeln,
|
||
> die alle denselben Webhook nutzen.
|
||
> Dadurch bleibt das System wartungsarm, aber du erhältst bei kritischen Zuständen sofort eine Nachricht auf dein Smartphone.
|
||
|
||
---
|
||
|
||
## Premium – Elyra: Intelligente WhatsApp-Interaktion mit Opt-In-Trigger
|
||
|
||
In diesem Premium-Abschnitt erweiterst du dein bestehendes WAHA-Setup um **Elyra** –
|
||
eine KI-Instanz, die über WhatsApp gezielt auf Nachrichten reagiert, wenn sie vom Nutzer aktiviert wird.
|
||
Dadurch bleibt die Nutzung im Rahmen der WhatsApp-Richtlinien, da Elyra niemals selbstständig Kontakt aufnimmt.
|
||
|
||
👉 **Screenshot geeignet:** WhatsApp-Chat mit „Ich brauche deine Hilfe, Elyra“ als Startbefehl und automatischer Antwort
|
||
|
||
> [!NOTE]
|
||
> Elyra ist ein optionales Modul, das lokale oder externe KI-Modelle (z. B. OpenAI, Ollama) ansprechen kann.
|
||
> Die Integration setzt ein funktionierendes WAHA- und n8n-System aus dem Free-Kapitel voraus.
|
||
>
|
||
> Der Ansatz bewegt sich in einem **zulässigen Graubereich**,
|
||
> da die Kommunikation ausschließlich **user-initiiert** erfolgt und somit kein unerlaubtes Messaging stattfindet.
|
||
|
||
### Aktivierungslogik
|
||
|
||
Elyra reagiert nur, wenn sie bewusst aktiviert wird.
|
||
Die Aktivierung erfolgt über eine eindeutige Eingabe, die als **Opt-In-Trigger** dient:
|
||
|
||
**Trigger-Phrase:**
|
||
> „Ich brauche deine Hilfe, Elyra“
|
||
|
||
Sobald diese Nachricht eingeht, aktiviert n8n den zugehörigen Workflow
|
||
und leitet den Inhalt an den KI-Knoten weiter.
|
||
Elyra verarbeitet die Anfrage, erstellt eine Antwort und sendet sie über WAHA an den Chat zurück.
|
||
|
||
### Beendigungslogik
|
||
|
||
Die Sitzung endet automatisch, wenn:
|
||
|
||
1. Der Nutzer „Danke, Elyra“ schreibt, **oder**
|
||
2. **10 Minuten** lang keine neue Nachricht eingeht.
|
||
|
||
Beide Bedingungen setzen den Workflow-Status zurück,
|
||
sodass Elyra keine weiteren Nachrichten beantwortet, bis erneut ein gültiger Trigger empfangen wird.
|
||
|
||
> [!TIP]
|
||
> Dieses Verhalten entspricht dem Prinzip klassischer Sprachassistenten wie „Hey Siri“ oder „Alexa“
|
||
> und sorgt für klare Grenzen zwischen aktiver und passiver Phase.
|
||
|
||
### Beispielhafte Gesprächssequenz
|
||
|
||
```
|
||
👤 Nutzer: Ich brauche deine Hilfe, Elyra
|
||
🤖 Elyra: Natürlich. Wobei darf ich dir helfen?
|
||
👤 Nutzer: Wann ist der nächste Stream?
|
||
🤖 Elyra: Der nächste Stream findet am Freitag um 19:30 Uhr statt – Thema: Disney Dreams 🌟
|
||
👤 Nutzer: Danke, Elyra
|
||
🤖 Elyra: Gern geschehen. Ich bin wieder im Ruhemodus.
|
||
```
|
||
|
||
👉 **Screenshot geeignet:** WhatsApp-Chat mit Elyra, die freundlich auf eine Zuschauerfrage reagiert und danach automatisch inaktiv wird.
|
||
|
||
#### Node 1 – Webhook (Eingang: WhatsApp-Nachricht)
|
||
|
||
**Zweck:** Empfängt eingehende WhatsApp-Nachrichten, die über WAHA an diesen Workflow weitergeleitet werden.
|
||
|
||
**Node-Typ:** Webhook
|
||
**Name:** Elyra – Nachrichteneingang
|
||
|
||
**Parameter:**
|
||
- **HTTP Method:** `POST`
|
||
- **Path:** `/elyra/inbound`
|
||
- **Response Mode:** `On Received`
|
||
- **Response Code:** `200`
|
||
- **Response Data:** `No Response Body`
|
||
|
||
> [!NOTE]
|
||
> Die Webhook-URL wird nach dem Speichern automatisch angezeigt, z. B.:
|
||
> `https://n8n.deinedomain.tld/webhook/elyra/inbound`
|
||
>
|
||
> Diese Adresse muss im WAHA-Backend als Ziel für eingehende Nachrichten konfiguriert werden –
|
||
> üblicherweise per Weiterleitung oder Trigger-Konfiguration in deinem WhatsApp-Bot-Modul.
|
||
|
||
👉 **Screenshot geeignet:** Webhook-Node mit sichtbarem Pfad `/webhook/elyra/inbound`
|
||
|
||
#### Node 2 – Function (Nachricht vorbereiten + Zeitstempel aktualisieren)
|
||
|
||
**Zweck:**
|
||
Extrahiert die `chatId` aus der eingehenden Nachricht, bereitet die Daten für den weiteren Verlauf auf
|
||
und speichert zusätzlich den Zeitpunkt der letzten Aktivität, um inaktive Sessions später automatisch beenden zu können.
|
||
|
||
**Node-Typ:** Function
|
||
**Name:** Elyra – Nachricht vorbereiten
|
||
|
||
**Code:**
|
||
```js
|
||
const body = items[0].json || {};
|
||
|
||
const chatId = body.chatId || 'unbekannt';
|
||
const text = (body.text || '').trim().toLowerCase();
|
||
const timestamp = body.timestamp || new Date().toISOString();
|
||
|
||
// Zeitstempel der letzten Aktivität speichern
|
||
$global.set('elyra_lastmsg_' + chatId, Date.now());
|
||
|
||
// Standardisierte Ausgabe für den weiteren Workflow
|
||
return [{
|
||
json: {
|
||
chatId,
|
||
text,
|
||
timestamp
|
||
}
|
||
}];
|
||
```
|
||
|
||
> [!NOTE]
|
||
> Der Eintrag `elyra_lastmsg_<chatId>` ermöglicht dem separaten Cleanup-Workflow,
|
||
> inaktive Sessions nach 10 Minuten automatisch zu beenden.
|
||
> Jede eingehende Nachricht aktualisiert diesen Zeitstempel automatisch.
|
||
|
||
👉 **Screenshot geeignet:** Function-Node mit markierter Zeile `$global.set('elyra_lastmsg_' + chatId, Date.now());`
|
||
|
||
#### Node 3 – IF (Trigger: Elyra deaktivieren?)
|
||
|
||
**Zweck:** Erkennt, ob der Nutzer Elyra gezielt deaktivieren will – z. B. durch die Nachricht „Danke, Elyra“.
|
||
|
||
**Node-Typ:** IF
|
||
**Name:** Elyra – Deaktivierung erkennen
|
||
|
||
**Bedingung:**
|
||
- **Mode:** `All`
|
||
- **Type:** `String`
|
||
- **Value 1:** `{{$json["text"]}}`
|
||
- **Operation:** `Contains`
|
||
- **Value 2:** `danke, elyra`
|
||
|
||
> [!TIP]
|
||
> Du kannst weitere Formulierungen ergänzen – z. B. `tschüss elyra`, `ich bin fertig`, etc.
|
||
> Aktuell genügt ein einzelner präziser Begriff zur Deaktivierung.
|
||
|
||
👉 **Screenshot geeignet:** IF-Node mit Bedingung `text contains "danke, elyra"`
|
||
|
||
**Anschlusslogik:**
|
||
- **TRUE →** weiter mit Node 4 – *Elyra – Session deaktivieren*
|
||
- **FALSE →** weiter mit Node 5 – *Elyra – Aktivierung erkennen*
|
||
|
||
#### Node 4 – Set (Elyra-Session deaktivieren)
|
||
|
||
**Zweck:** Setzt den Aktivierungsstatus der Elyra-Session für diesen Chat auf „inaktiv“.
|
||
|
||
**Node-Typ:** Set
|
||
**Name:** Elyra – Session deaktivieren
|
||
|
||
**Operation:**
|
||
- **Add Field:** `elyra_active` → `false`
|
||
|
||
> [!NOTE]
|
||
> Die Variable `elyra_active` wird später mit der `chatId` kombiniert,
|
||
> um eine **sitzungsspezifische Statusvariable** zu erzeugen, z. B.:
|
||
> `elyra_active_491751234567@c.us`
|
||
|
||
👉 **Screenshot geeignet:** Set-Node mit Feld `elyra_active = false`
|
||
|
||
#### Node 5 – IF (Trigger: Elyra aktivieren?)
|
||
|
||
**Zweck:** Erkennt, ob Elyra durch den Nutzer aktiviert werden soll – z. B. mit „Ich brauche deine Hilfe, Elyra“.
|
||
|
||
**Node-Typ:** IF
|
||
**Name:** Elyra – Aktivierung erkennen
|
||
|
||
**Bedingung:**
|
||
- **Mode:** `All`
|
||
- **Type:** `String`
|
||
- **Value 1:** `{{$json["text"]}}`
|
||
- **Operation:** `Contains`
|
||
- **Value 2:** `ich brauche deine hilfe, elyra`
|
||
|
||
> [!TIP]
|
||
> Achte auf die Kleinschreibung, wenn du den Text vorher mit `.toLowerCase()` vereinheitlichst (siehe Node 2).
|
||
> Weitere Aktivierungsphrasen kannst du über zusätzliche OR-Verzweigungen ergänzen.
|
||
|
||
👉 **Screenshot geeignet:** IF-Node mit Bedingung `text contains "ich brauche deine hilfe, elyra"`
|
||
|
||
**Anschlusslogik:**
|
||
- **TRUE →** weiter mit Node 6 – *Sessionzähler prüfen*
|
||
- **FALSE →** weiter mit Node 9 – *Elyra – Aktivstatus prüfen*
|
||
|
||
#### Node 6 – Function (Sessionzähler prüfen)
|
||
|
||
**Zweck:** Zählt die aktuell aktiven Elyra-Sessions anhand der globalen Variablen
|
||
und entscheidet, ob eine weitere Session zulässig ist (Maximalgrenze: 10).
|
||
|
||
**Node-Typ:** Function
|
||
**Name:** Elyra – Sessionzähler prüfen
|
||
|
||
```js
|
||
// Aktuelle Liste aller aktiven Sessions abrufen
|
||
const allKeys = Object.keys($global);
|
||
const activeSessions = allKeys.filter(k => k.startsWith('elyra_active_') && $global.get(k) === true);
|
||
|
||
// Ergebnis als Flag setzen
|
||
item.sessionCount = activeSessions.length;
|
||
item.sessionAllowed = activeSessions.length < 10;
|
||
|
||
return [item];
|
||
```
|
||
|
||
> [!NOTE]
|
||
> Jede aktive Elyra-Session wird im weiteren Verlauf als globale Variable gespeichert, z. B.:
|
||
> `elyra_active_491751234567@c.us = true`
|
||
>
|
||
> Diese Function zählt exakt, wie viele davon derzeit auf `true` stehen.
|
||
|
||
👉 **Screenshot geeignet:** Function-Node mit markierter Zeile `item.sessionAllowed = ...`
|
||
|
||
**Anschlusslogik:**
|
||
- **→** weiter mit Node 7 – *Aktivierung zulässig?* (IF)
|
||
|
||
#### Node 7 – IF (Aktivierung zulässig?)
|
||
|
||
**Zweck:** Prüft, ob die Elyra-Aktivierung für diesen Nutzer erlaubt ist (d. h. weniger als 10 aktive Sessions).
|
||
|
||
**Node-Typ:** IF
|
||
**Name:** Elyra – Aktivierung zulässig?
|
||
|
||
**Bedingung:**
|
||
- **Mode:** `All`
|
||
- **Type:** `Boolean`
|
||
- **Value 1:** `{{$json["sessionAllowed"]}}`
|
||
- **Operation:** `Is true`
|
||
|
||
👉 **Screenshot geeignet:** IF-Node mit Bedingung `sessionAllowed is true`
|
||
|
||
**Anschlusslogik:**
|
||
- **TRUE →** weiter mit Node 8 – *Elyra – Session aktivieren*
|
||
- **FALSE →** weiter mit Node 9 – *Elyra – Aktivstatus prüfen*
|
||
|
||
#### Node 8 – Set (Elyra – Session aktivieren)
|
||
|
||
**Zweck:** Aktiviert Elyra für diesen Nutzer, indem eine globale Sitzungsvariable gesetzt wird.
|
||
|
||
**Node-Typ:** Set
|
||
**Name:** Elyra – Session aktivieren
|
||
|
||
**Operation:**
|
||
- **Add Field:**
|
||
- `elyra_active` → `true`
|
||
|
||
> [!NOTE]
|
||
> Die eigentliche Speicherung erfolgt erst im nächsten Schritt durch Kombination mit der `chatId`.
|
||
> Dieses Feld dient als Zwischenschritt für die globale Speicherung mit Namen wie:
|
||
> `elyra_active_491751234567@c.us`
|
||
|
||
👉 **Screenshot geeignet:** Set-Node mit Feld `elyra_active = true`
|
||
|
||
**Anschlusslogik:**
|
||
- **→** weiter mit Node 9 – *Elyra – Aktivstatus prüfen*
|
||
|
||
|
||
#### Node 9 – IF (Elyra ist aktiv?)
|
||
|
||
**Zweck:** Prüft, ob für diesen Chat eine Elyra-Session aktiv ist.
|
||
|
||
**Node-Typ:** IF
|
||
**Name:** Elyra – Aktivstatus prüfen
|
||
|
||
**Bedingung:**
|
||
- **Mode:** `All`
|
||
- **Type:** `Expression`
|
||
- **Value 1:**
|
||
```js
|
||
{{
|
||
const key = 'elyra_active_' + $json["chatId"];
|
||
return $global.get(key) === true;
|
||
}}
|
||
```
|
||
- **Operation:** `Is true`
|
||
|
||
> [!TIP]
|
||
> Diese Prüfung stellt sicher, dass Elyra nur antwortet, wenn eine Sitzung explizit aktiv ist.
|
||
> Ohne vorherige Aktivierung wird der Nutzer ignoriert – oder optional weitergeleitet.
|
||
|
||
👉 **Screenshot geeignet:** IF-Node mit geöffneter Expression und `return $global.get(...) === true`
|
||
|
||
**Anschlusslogik:**
|
||
- **TRUE →** weiter mit Node 10 – *Thema klassifizieren*
|
||
- **FALSE →** weiter mit Node 16 – *Antwort: Elyra ist nicht aktiv*
|
||
|
||
#### Node 10 – HTTP Request (Themenklassifizierung über Ollama)
|
||
|
||
**Zweck:** Übergibt die empfangene WhatsApp-Nachricht an das lokale Mistral-Modell in Ollama,
|
||
um eine präzise Themenklassifizierung zu erhalten – z. B. `streamzeit`, `eventinfo`, `zeiten`, `adminanfrage`, `chatbefehl` oder `nicht zulässig`.
|
||
|
||
**Node-Typ:** HTTP Request
|
||
**Name:** Elyra – Thema klassifizieren (Ollama)
|
||
|
||
**Parameter:**
|
||
- **HTTP Method:** `POST`
|
||
- **URL:** `http://ollama.deinedomain.tld:11434/api/chat`
|
||
- **Headers:**
|
||
- `Content-Type`: `application/json`
|
||
- **Body Content Type:** `JSON`
|
||
- **Body Parameters:**
|
||
```json
|
||
{
|
||
"model": "mistral",
|
||
"messages": [
|
||
{
|
||
"role": "system",
|
||
"content": "Du bist ein Klassifizierungsagent für WhatsApp-Nachrichten. Ordne die folgende Nachricht exakt einem der folgenden Themen zu:\n\n- streamzeit\n- eventinfo\n- zeiten\n- adminanfrage\n- chatbefehl\n\nWenn keine eindeutige Zuordnung möglich ist oder der Inhalt nicht erlaubt ist, antworte exakt mit: nicht zulässig."
|
||
},
|
||
{
|
||
"role": "user",
|
||
"content": "={{$json[\"text\"]}}"
|
||
}
|
||
],
|
||
"stream": false
|
||
}
|
||
```
|
||
|
||
> [!NOTE]
|
||
> Die Antwort muss **exakt** einem der oben genannten Schlüsselwörter entsprechen.
|
||
> Dadurch kannst du im nächsten Schritt zuverlässig prüfen, ob Elyra antworten darf – auch bei hoher Auslastung.
|
||
|
||
👉 **Screenshot geeignet:** HTTP-Request-Node mit geöffnetem Body, markierter Promptabschnitt `"Ordne die folgende Nachricht ..."`
|
||
|
||
**Anschlusslogik:**
|
||
- **→** weiter mit Node 11 – *Zugriffsprüfung: Thema + Sessionauslastung*
|
||
|
||
#### Node 11 – Function (Zugriffsprüfung: Thema + Sessionauslastung)
|
||
|
||
**Zweck:** Entscheidet, ob die Anfrage trotz möglicher Überlast bearbeitet werden darf.
|
||
Bevorzugte Themen wie `streamzeit`, `eventinfo` und `zeiten` dürfen auch bei Volllast durch –
|
||
alles andere wird bei Erreichen der Kapazitätsgrenze blockiert.
|
||
|
||
**Node-Typ:** Function
|
||
**Name:** Elyra – Zugriff prüfen
|
||
|
||
```js
|
||
const thema = $json["message"]?.content?.toLowerCase() || "nicht zulässig";
|
||
const chatId = $json["chatId"];
|
||
|
||
// Speichern für spätere Entscheidung
|
||
item.thema = thema;
|
||
item.chatId = chatId;
|
||
|
||
// Bevorzugte Themen
|
||
const priorisiert = ["streamzeit", "eventinfo", "zeiten"];
|
||
|
||
// Aktive Sessions zählen
|
||
const allKeys = Object.keys($global);
|
||
const aktive = allKeys.filter(k => k.startsWith('elyra_active_') && $global.get(k) === true);
|
||
const sessionCount = aktive.length;
|
||
|
||
// Entscheidung
|
||
item.sessionCount = sessionCount;
|
||
item.zugriffErlaubt = sessionCount < 10 || priorisiert.includes(thema);
|
||
item.zugriffVerweigert = !item.zugriffErlaubt || thema === "nicht zulässig";
|
||
|
||
return [item];
|
||
```
|
||
|
||
> [!TIP]
|
||
> Durch diese Logik reagiert Elyra auch bei starker Auslastung noch auf einfache Standardfragen.
|
||
> Nur komplexe oder nicht erlaubte Inhalte werden in dem Fall geblockt oder verzögert.
|
||
|
||
👉 **Screenshot geeignet:** Function-Node mit sichtbarer Logik `zugriffErlaubt = sessionCount < 10 || …`
|
||
|
||
**Anschlusslogik:**
|
||
- **TRUE →** weiter mit Node 12 – *Antwort generieren (Ollama)*
|
||
- **FALSE →** weiter mit Node 22 – *Antwort: Kapazitätsgrenze oder Thema abgelehnt*
|
||
|
||
#### Node 12 – Set (Kontext für Themenverzweigung vorbereiten)
|
||
|
||
**Zweck:** Bereitet die vom Klassifizierungsmodell gelieferte Themenkategorie und alle relevanten Kontextdaten für die nachfolgende Verzweigung vor.
|
||
Dieser Node bündelt alle Variablen, die für die spätere Entscheidung im Switch-Node benötigt werden.
|
||
|
||
**Node-Typ:** Set
|
||
**Name:** Elyra – Kontext vorbereiten
|
||
|
||
**Felder hinzufügen:**
|
||
- `thema` → `={{$json["message"]["content"]}}`
|
||
- `chatId` → `={{$json["chatId"]}}`
|
||
- `text` → `={{$json["text"]}}`
|
||
- `timestamp` → `={{$json["timestamp"]}}`
|
||
|
||
> [!NOTE]
|
||
> Die Variable `thema` stammt direkt aus der Klassifizierungsantwort von Mistral (Node 10).
|
||
> Diese wird im nächsten Node verwendet, um über einen `Switch`-Node gezielt auf das Thema zu reagieren.
|
||
|
||
👉 **Screenshot geeignet:** Set-Node mit Feldern `thema`, `chatId`, `text`, `timestamp`
|
||
|
||
**Anschlusslogik:**
|
||
- **→** weiter mit Node 13 – *Switch: Thema auswählen*
|
||
|
||
#### Node 13 – Switch (Thema auswählen und Pfad verzweigen)
|
||
|
||
**Zweck:** Leitet die Verarbeitung abhängig von der klassifizierten Themenkategorie an den passenden Ablauf weiter – z. B. Datenabruf, Rechteprüfung oder feste Antwort.
|
||
|
||
**Node-Typ:** Switch
|
||
**Name:** Elyra – Thema verzweigen
|
||
|
||
**Bedingungstyp:** `String`
|
||
**Vergleichswert:** `={{$json["thema"]}}`
|
||
|
||
**Cases:**
|
||
- **streamzeit** → weiter mit Node 14 – *Twitch-Kalender abrufen*
|
||
- **eventinfo** → weiter mit Node 15 – *Eventtext setzen*
|
||
- **zeiten** → weiter mit Node 16 – *Sendezeit abrufen*
|
||
- **chatbefehl** → weiter mit Node 17 – *Standardantwort setzen*
|
||
- **adminanfrage** → weiter mit Node 18 – *Adminrechte prüfen*
|
||
- **nicht zulässig** → weiter mit Node 19 – *Antwort: Thema nicht erlaubt*
|
||
|
||
> [!TIP]
|
||
> Der Switch kann später beliebig erweitert werden, z. B. für `netdatainfo`, `feedback`, etc.
|
||
> Die Themenbezeichnungen müssen exakt zu denen aus dem Klassifizierungs-Prompt (Node 10) passen.
|
||
|
||
👉 **Screenshot geeignet:** Switch-Node mit geöffneten Case‑Bedingungen `streamzeit`, `adminanfrage`, etc.
|
||
|
||
**Anschlusslogik:**
|
||
- Je nach Thema → zu Node 14–19 (jeweils themenspezifisch)
|
||
|
||
#### Node 14 – Execute Sub‑workflow (Twitch-Kalender abfragen)
|
||
|
||
**Zweck:** Führt einen ausgelagerten Workflow aus, der den nächsten geplanten Streamtermin über die Twitch-API abruft
|
||
und die Antwort in strukturierter Form (z. B. als `streaminfo`) zurückliefert.
|
||
|
||
**Node-Typ:** Execute Sub-workflow
|
||
**Name:** Elyra – Nächsten Stream abrufen
|
||
|
||
**Parameter:**
|
||
- **Workflow:** `elyra_next_stream` *(wird als JSON-Download bereitgestellt)*
|
||
- **Options → Wait for Completion:** `true`
|
||
- **Options → Variables:**
|
||
- `chatId`: `={{$json["chatId"]}}`
|
||
- `text`: `={{$json["text"]}}`
|
||
- `timestamp`: `={{$json["timestamp"]}}`
|
||
|
||
> [!NOTE]
|
||
> Dieser Sub‑Workflow wird nicht im Tutorial dokumentiert, sondern separat als JSON-Datei bereitgestellt.
|
||
> Er ruft die Twitch-API auf, extrahiert den nächsten geplanten Stream
|
||
> und speichert die Information im Feld `streaminfo` für die spätere Antwortgenerierung.
|
||
|
||
👉 **Screenshot geeignet:** Execute Sub-workflow Node mit gewähltem Workflow `elyra_next_stream` und aktiver Option `Wait for Completion`
|
||
|
||
**Anschlusslogik:**
|
||
- **→** weiter mit Node 20 – *Antwort generieren (Ollama mit Kontext)*
|
||
|
||
#### Node 15 – Execute Sub‑workflow (Event-Info abrufen)
|
||
|
||
**Zweck:** Führt einen externen Workflow aus, der die Beschreibung des nächsten geplanten Events liefert –
|
||
z. B. aus einer Kalenderquelle, Textdatei oder Datenbank.
|
||
|
||
**Node-Typ:** Execute Sub-workflow
|
||
**Name:** Elyra – Eventinfo abrufen
|
||
|
||
**Parameter:**
|
||
- **Workflow:** `elyra_eventinfo` *(bereitgestellt als JSON-Download)*
|
||
- **Options → Wait for Completion:** `true`
|
||
- **Options → Variables:**
|
||
- `chatId`: `={{$json["chatId"]}}`
|
||
- `text`: `={{$json["text"]}}`
|
||
- `timestamp`: `={{$json["timestamp"]}}`
|
||
|
||
> [!NOTE]
|
||
> Auch dieser Workflow wird **nicht im Tutorial beschrieben**, sondern als JSON-Datei zum Download bereitgestellt.
|
||
> Er liefert z. B. das Feld `eventinfo` zurück, das später in den Antwort-Prompt eingebaut werden kann.
|
||
|
||
👉 **Screenshot geeignet:** Execute Sub-workflow mit Workflow-Namen `elyra_eventinfo` und gesetzten Parametern
|
||
|
||
**Anschlusslogik:**
|
||
- **→** weiter mit Node 20 – *Antwort generieren (Ollama mit Kontext)*
|
||
|
||
#### Node 16 – Execute Sub‑workflow (Sendezeit abrufen)
|
||
|
||
**Zweck:** Führt einen ausgelagerten Workflow aus, der die aktuelle oder nächste Sendezeit ermittelt –
|
||
z. B. über eine ICS-Datei, einen festgelegten Wochenplan oder eine API-basierte Quelle.
|
||
|
||
**Node-Typ:** Execute Sub-workflow
|
||
**Name:** Elyra – Sendezeit abrufen
|
||
|
||
**Parameter:**
|
||
- **Workflow:** `elyra_zeiten` *(bereitgestellt als JSON-Download)*
|
||
- **Options → Wait for Completion:** `true`
|
||
- **Options → Variables:**
|
||
- `chatId`: `={{$json["chatId"]}}`
|
||
- `text`: `={{$json["text"]}}`
|
||
- `timestamp`: `={{$json["timestamp"]}}`
|
||
|
||
> [!NOTE]
|
||
> Der Workflow `elyra_zeiten` wird separat als JSON bereitgestellt
|
||
> und extrahiert z. B. aus einem Kalender die geplante Startzeit des Streams.
|
||
> Die Ausgabe erfolgt idealerweise als String im Feld `sendezeit`.
|
||
|
||
👉 **Screenshot geeignet:** Execute Sub-workflow mit Workflow `elyra_zeiten` und gesetzten Übergabeparametern
|
||
|
||
**Anschlusslogik:**
|
||
- **→** weiter mit Node 20 – *Antwort generieren (Ollama mit Kontext)*
|
||
|
||
#### Node 17 – Execute Sub‑workflow (Chatbefehle aus StreamElements abrufen)
|
||
|
||
**Zweck:** Führt einen ausgelagerten Workflow aus, der die öffentlich zugängliche StreamElements‑Commands‑Seite abruft,
|
||
alle verfügbaren Chatbefehle extrahiert und als JSON-String bereitstellt.
|
||
|
||
**Node-Typ:** Execute Sub-workflow
|
||
**Name:** Elyra – Chatbefehle laden
|
||
|
||
**Parameter:**
|
||
- **Workflow:** `elyra_chatcommands` *(bereitgestellt als JSON-Download)*
|
||
- **Options → Wait for Completion:** `true`
|
||
- **Options → Variables:**
|
||
- `chatId`: `={{$json["chatId"]}}`
|
||
- `text`: `={{$json["text"]}}`
|
||
|
||
> [!NOTE]
|
||
> Dieser Sub‑Workflow ruft z. B. `https://streamelements.com/bratonien_tv/commands` auf,
|
||
> analysiert das HTML und extrahiert die Befehle samt Beschreibung als JSON‑Objekt.
|
||
> Das Ergebnis wird später an Ollama übergeben, damit Elyra kontextbezogen passende Befehle vorschlagen kann.
|
||
>
|
||
> Auch andere Anbieter wie **Nightbot**, **Moobot** oder **Botisimo** stellen vergleichbare Listen zur Verfügung –
|
||
> im Rahmen dieses Tutorials verwenden wir jedoch ausschließlich **StreamElements** als zentrale Bot-Instanz.
|
||
|
||
👉 **Screenshot geeignet:** Execute Sub-workflow mit Workflow-Namen `elyra_chatcommands`
|
||
|
||
**Anschlusslogik:**
|
||
- **→** weiter mit Node 20 – *Antwort generieren (Ollama mit Kontext)*
|
||
|
||
#### Node 18 – Execute Sub‑workflow (Adminrechte prüfen)
|
||
|
||
**Zweck:** Führt einen externen Workflow aus, der prüft, ob der aktuelle Absender (`chatId`) zu den autorisierten Admins gehört.
|
||
Nur wenn das zutrifft, wird die Anfrage weiterverarbeitet – sonst erhält der Nutzer eine entsprechende Ablehnung.
|
||
|
||
**Node-Typ:** Execute Sub-workflow
|
||
**Name:** Elyra – Adminrechte prüfen
|
||
|
||
**Parameter:**
|
||
- **Workflow:** `elyra_admincheck` *(bereitgestellt als JSON-Download)*
|
||
- **Options → Wait for Completion:** `true`
|
||
- **Options → Variables:**
|
||
- `chatId`: `={{$json["chatId"]}}`
|
||
- `text`: `={{$json["text"]}}`
|
||
|
||
> [!NOTE]
|
||
> Der Sub-Workflow `elyra_admincheck` vergleicht die `chatId` mit einer internen Liste autorisierter Nutzer
|
||
> (z. B. als JSON-Datei, Umgebungsvariable oder Datenbankeintrag).
|
||
> Ist der Nutzer nicht autorisiert, wird das Feld `admin_erlaubt` auf `false` gesetzt und die Anfrage verworfen.
|
||
|
||
👉 **Screenshot geeignet:** Execute Sub-workflow mit Workflow `elyra_admincheck` und Variable `chatId`
|
||
|
||
**Anschlusslogik:**
|
||
- **→** weiter mit Node 20 – *Antwort generieren (Ollama mit Kontext)*
|
||
|
||
#### Node 19 – Set (Ablehnungsprompt für nicht erlaubtes Thema)
|
||
|
||
**Zweck:** Bereitet den Prompt für eine freundlich-bestimmte Ablehnungsantwort durch Ollama vor.
|
||
Dies erfolgt, wenn das Thema vom Klassifizierungsmodell als `nicht zulässig` eingestuft wurde.
|
||
|
||
**Node-Typ:** Set
|
||
**Name:** Elyra – Thema abgelehnt (Prompt vorbereiten)
|
||
|
||
**Felder hinzufügen:**
|
||
- `chatId`: `={{$json["chatId"]}}`
|
||
- `text`: `={{$json["text"]}}`
|
||
- `ablehnungskontext`:
|
||
```text
|
||
Die folgende Anfrage konnte keinem zugelassenen Thema zugeordnet werden.
|
||
Formuliere eine kurze, aber klare Antwort, die dem Nutzer freundlich mitteilt,
|
||
dass diese Art von Anfrage aktuell nicht unterstützt wird.
|
||
Die Antwort soll sachlich bleiben, aber höflich und professionell wirken.
|
||
```
|
||
|
||
👉 **Screenshot geeignet:** Set-Node mit sichtbarem Feld `ablehnungskontext`
|
||
|
||
**Anschlusslogik:**
|
||
- **→** weiter mit Node 20 – *Antwort generieren (Ollama mit Kontext)*
|
||
|
||
#### Node 20 – HTTP Request (Antwort generieren – Ollama)
|
||
|
||
**Zweck:** Sendet die Nutzeranfrage zusammen mit dem relevanten Kontext (z. B. Chatbefehle, Eventinfos, Ablehnungshinweis)
|
||
an das lokale Sprachmodell **Mistral** über den Ollama-Endpunkt und erhält eine formulierte Antwort zurück.
|
||
|
||
**Node-Typ:** HTTP Request
|
||
**Name:** Elyra – Antwort generieren (Ollama)
|
||
|
||
**Parameter:**
|
||
- **Method:** `POST`
|
||
- **URL:**
|
||
```
|
||
http://ollama.<deine-domain>.tld/api/generate
|
||
```
|
||
- **Headers:**
|
||
- `Content-Type`: `application/json`
|
||
- **Body → JSON:**
|
||
```json
|
||
{
|
||
"model": "mistral",
|
||
"stream": false,
|
||
"prompt": "{{$json['ablehnungskontext'] || ''}}{{$json['kontext'] || ''}}\n\nNachricht:\n{{$json['text']}}"
|
||
}
|
||
```
|
||
|
||
> [!NOTE]
|
||
> Der Prompt setzt sich aus verschiedenen Quellen zusammen – je nach Pfad:
|
||
> – bei `chatbefehl`: Liste aller Chatkommandos (`kontext`)
|
||
> – bei `eventinfo` oder `sendezeit`: Zusatzinfos aus Sub-Workflows
|
||
> – bei `nicht erlaubt`: Ablehnungshinweis (`ablehnungskontext`)
|
||
>
|
||
> Der finale Prompt enthält immer den Originaltext des Nutzers.
|
||
|
||
> [!TIP]
|
||
> Du kannst über die `prompt:`-Zusammensetzung bei Bedarf eigene Regeln einbauen,
|
||
> z. B. Priorisierungen, stilistische Hinweise oder Sprachwechsel.
|
||
|
||
👉 **Screenshot geeignet:** HTTP-Request-Node mit Body-Feld `prompt`
|
||
|
||
**Anschlusslogik:**
|
||
- **→** weiter mit Node 21 – *Antwort an WAHA senden*
|
||
|
||
#### Node 21 – HTTP Request (Antwort über WAHA senden)
|
||
|
||
**Zweck:** Sendet die generierte Antwort als WhatsApp-Nachricht über die WAHA‑API an den ursprünglichen Absender.
|
||
|
||
**Node-Typ:** HTTP Request
|
||
**Name:** Elyra – Antwort senden (WAHA)
|
||
|
||
**Parameter:**
|
||
- **Method:** `POST`
|
||
- **URL:**
|
||
```
|
||
https://waha.<deine-domain>.tld/api/sendText
|
||
```
|
||
- **Headers:**
|
||
- `Content-Type`: `application/json`
|
||
- `Authorization`: `Bearer <DEIN_WAHA_API_TOKEN>`
|
||
- **Body → JSON:**
|
||
```json
|
||
{
|
||
"chatId": "={{$json[\"chatId\"]}}",
|
||
"text": "={{$json[\"antwort\"] || $json[\"text\"]}}"
|
||
}
|
||
```
|
||
|
||
> [!IMPORTANT]
|
||
> Der **API-Token ist zwingend erforderlich**, damit WAHA die Anfrage akzeptiert.
|
||
> Du findest ihn im WAHA-Webinterface unter **Settings → API → Token**.
|
||
> Ohne gültigen Token gibt die API den Fehlercode `401 Unauthorized` zurück.
|
||
|
||
👉 **Screenshot geeignet:** HTTP-Request-Node mit gesetztem Authorization‑Header
|
||
|
||
**Anschlusslogik:**
|
||
→ Ende des Haupt-Workflows
|
||
|
||
#### Node 22 – Set (Ablehnungsprompt: Ressourcenmangel)
|
||
|
||
**Zweck:**
|
||
Bereitet den Prompt für eine höfliche, aber bestimmte Ablehnungsantwort durch **Ollama** vor,
|
||
wenn das System aktuell keine ausreichenden Ressourcen (z. B. Rechenkapazität oder Slots) zur Bearbeitung hat.
|
||
|
||
**Node-Typ:**
|
||
Set
|
||
|
||
**Name:**
|
||
Elyra – Thema abgelehnt (Ressourcenmangel)
|
||
|
||
**Felder hinzufügen:**
|
||
- `chatId`:
|
||
`={{$json["chatId"]}}`
|
||
- `text`:
|
||
`={{$json["text"]}}`
|
||
- `ablehnungskontext`:
|
||
```text
|
||
Die Anfrage kann derzeit nicht bearbeitet werden, da die verfügbaren Ressourcen
|
||
(z. B. Rechenkapazität oder Ausführungsslots) vorübergehend ausgelastet sind.
|
||
Formuliere eine höfliche, aber klare Antwort, die den Nutzer darüber informiert,
|
||
dass Elyra aktuell keine weiteren Anfragen annehmen kann.
|
||
Bitte den Nutzer freundlich um Verständnis und erkläre,
|
||
dass er es zu einem späteren Zeitpunkt erneut versuchen kann.
|
||
```
|
||
|
||
👉 **Screenshot geeignet:**
|
||
Set-Node mit sichtbarem Feld `ablehnungskontext`
|
||
|
||
**Anschlusslogik:**
|
||
→ weiter mit **Node 20 – Antwort generieren (Ollama mit Kontext)**
|
||
|
||
### Workflow: Elyra – Session Cleanup (automatische Sitzungsbeendigung)
|
||
|
||
**Zweck:**
|
||
Beendet inaktive Elyra-Sessions automatisch, wenn über 10 Minuten keine Nachricht mehr eingegangen ist.
|
||
Dadurch werden belegte Slots im UCC wieder freigegeben und die Systemkapazität bleibt stabil.
|
||
|
||
Der Workflow wird alle **5 Minuten** automatisch gestartet.
|
||
Er durchsucht den n8n-Global-Store nach aktiven Sessions (`elyra_active_<chatId>`)
|
||
und prüft anhand des zugehörigen Zeitstempels (`elyra_lastmsg_<chatId>`),
|
||
ob die letzte Nutzerinteraktion länger als 10 Minuten her ist.
|
||
Falls ja, wird die Session beendet.
|
||
|
||
👉 **Screenshot geeignet:** Diagramm mit Cron-Trigger → Function-Node „Inaktive Sessions schließen“
|
||
|
||
#### Node 1 – Cron (Trigger)
|
||
|
||
**Zweck:**
|
||
Startet den Workflow regelmäßig, um alte Sessions zu bereinigen.
|
||
|
||
**Node-Typ:** Cron
|
||
**Name:** Elyra – Cleanup Trigger
|
||
|
||
**Parameter:**
|
||
- **Mode:** Every X Minutes
|
||
- **Every:** `5`
|
||
|
||
> [!NOTE]
|
||
> Der Workflow läuft alle 5 Minuten automatisch.
|
||
> Du kannst das Intervall bei Bedarf anpassen – z. B. auf 2 Minuten für sehr aktive Systeme.
|
||
|
||
👉 **Screenshot geeignet:** Cron-Node mit „Every 5 Minutes“
|
||
|
||
**Anschlusslogik:**
|
||
→ weiter mit **Node 2 – Inaktive Sessions schließen**
|
||
|
||
#### Node 2 – Function (Inaktive Sessions schließen)
|
||
|
||
**Zweck:**
|
||
Überprüft alle global gespeicherten Elyra-Sessions und deaktiviert diejenigen,
|
||
bei denen seit mehr als 10 Minuten keine Aktivität registriert wurde.
|
||
|
||
**Node-Typ:** Function
|
||
**Name:** Elyra – Inaktive Sessions schließen
|
||
|
||
**Code:**
|
||
```js
|
||
const allKeys = Object.keys($global);
|
||
const now = Date.now();
|
||
const timeout = 10 * 60 * 1000; // 10 Minuten
|
||
|
||
for (const key of allKeys) {
|
||
if (key.startsWith('elyra_active_') && $global.get(key) === true) {
|
||
const chatId = key.replace('elyra_active_', '');
|
||
const lastMsg = $global.get('elyra_lastmsg_' + chatId) || 0;
|
||
if (now - lastMsg > timeout) {
|
||
$global.set(key, false);
|
||
}
|
||
}
|
||
}
|
||
|
||
return [];
|
||
```
|
||
|
||
> [!TIP]
|
||
> Die Timeout-Dauer (`timeout`) lässt sich frei anpassen.
|
||
> Standardwert sind 10 Minuten (600 000 ms).
|
||
> Für Livestream- oder Event-Kontexte sind 5–10 Minuten in der Regel sinnvoll.
|
||
|
||
👉 **Screenshot geeignet:** Function-Node mit geöffnetem Code-Editor, markierte Zeile `if (now - lastMsg > timeout)`
|
||
|
||
**Anschlusslogik:**
|
||
→ Ende des Workflows
|
||
|
||
---
|
||
|
||
## Abschluss – WAHA Premium
|
||
|
||
Mit dem Abschluss dieses Premium-Kapitels hast du WAHA vollständig in dein UCC integriert.
|
||
Automatisierte Statusmeldungen via n8n und Netdata sowie eine optionale KI-gestützte Antwortfunktion machen dein System nicht nur informativ, sondern auch interaktiv.
|
||
|
||
### Ergebnisse dieses Kapitels
|
||
|
||
| Bereich | Umsetzung |
|
||
|--------|-----------|
|
||
| Statusbenachrichtigung | Netdata-Alarme werden über n8n an WAHA weitergeleitet – du bekommst Warnungen & Fehler direkt auf WhatsApp. |
|
||
| KI-Antwortfunktion | Eingehende Nachrichten aktivieren auf Wunsch deine KI-Instanz („Elyra“) und erhalten automatisierte Antworten – lokal und datensouverän. |
|
||
| Infrastruktur-Integration | Alles läuft innerhalb deines UCC-Systems: keine externe Cloud-API (sofern gewünscht), volle Kontrolle über Daten und Prozesse. |
|
||
|
||
> **Tip:**
|
||
> Dieses Setup lässt sich modular erweitern – z. B. um zusätzliche Trigger, weitere Chat-Kanäle oder erweiterte KI-Funktionalitäten.
|
||
> Dank klarer Trennung und Standardisierung bleibt dein System wartungsarm und skalierbar.
|
||
|
||
Damit ist dieses Kapitel abgeschlossen.
|
||
Dein UCC verfügt nun über ein vollwertiges, internes WhatsApp-Kommunikations- und Benachrichtigungssystem – von den ersten Setup-Schritten bis zur automatisierten Interaktion.
|
||
Viel Erfolg beim Betrieb und bei weiteren Erweiterungen! |