Bei Offline eine Nachricht in Teams oder Slack
Ein kleiner Empfänger macht aus dem Webhook für Zustandswechsel eine Nachricht im Team-Chat.
- Für wen
- Entwicklerinnen und Entwickler, die Displays für ein Team betreuen, das in Slack oder Microsoft Teams arbeitet
- Voraussetzung
- ein Paket mit Webhooks (ab Premium), die Rolle Inhaber für den Endpunkt, ein Schlüssel
ak_…der Klasse Nur Lesen für die Namen der Displays und ein Server mit öffentlicher https-Adresse - Ort
- Ihr eigener Server und Einstellungen › Für Entwickler › Webhooks
Am Ende schreibt ein kleiner Empfänger eine Nachricht in Ihren Team-Chat, sobald ein Display offline geht, und noch eine, wenn es zurück ist. Niemand muss die Flotte offen halten, um zu merken, dass der Fernseher im Schaufenster schwarz ist.
So funktioniert es
Szenera kann nicht direkt in einen Chat schreiben: Der Chat erwartet sein eigenes Nachrichtenformat, und Szenera signiert jede Zustellung. Dazwischen sitzt ein eigener Empfänger:
- Szenera schickt das Ereignis
fleet.player.status-changedmitplayerId,fromundto. - Der Empfänger prüft die Signatur, übergeht Wiederholungen und liest den Namen des Displays über die API.
- Er schickt eine Nachricht an den eingehenden Webhook Ihres Chats.
Als offline gilt ein Display, das sich fünf Minuten nicht gemeldet hat. Davor ist es kurz stale. Der Empfänger meldet nur den Schritt nach offline und die Rückkehr nach online, damit ein kurzes Wackeln den Chat nicht flutet.
Schritte
-
Legen Sie im Chat den eingehenden Webhook an.
- Slack: Legen Sie eine App mit Incoming Webhooks für den Kanal an. Die Adresse beginnt mit
https://hooks.slack.com/. - Microsoft Teams: Öffnen Sie im Kanal die Workflows und legen Sie aus einer Vorlage einen Ablauf an, der eingehende Webhook-Anfragen in den Kanal schreibt. Der Ablauf gibt Ihnen eine Adresse, die Adaptive Cards annimmt.
- Slack: Legen Sie eine App mit Incoming Webhooks für den Kanal an. Die Adresse beginnt mit
-
Speichern Sie den Empfänger auf Ihrem Server und starten Sie ihn mit den vier Werten:
TypeScript// offline-alert.ts: node offline-alert.tsimport { createServer } from 'node:http';import { createHmac, timingSafeEqual } from 'node:crypto'; const SECRET = process.env.WEBHOOK_SECRET ?? ''; // whsec_…const API = process.env.SZENERA_API;const KEY = process.env.SZENERA_API_KEY; // Klasse Nur Lesenconst CHAT = process.env.CHAT_WEBHOOK_URL ?? ''; // Slack oder Teamsconst seen = new Set<string>(); function isFromSignage(rawBody: string, header: string): boolean { const expected = createHmac('sha256', SECRET).update(rawBody).digest('base64url'); const a = Buffer.from(expected); const b = Buffer.from(header); return a.length === b.length && timingSafeEqual(a, b);} async function displayName(playerId: string): Promise<string> { const res = await fetch(`${API}/v1/players/${playerId}`, { headers: { Authorization: `Bearer ${KEY}` }, }); return res.ok ? (await res.json()).player.name : playerId;} function chatMessage(text: string): unknown { if (CHAT.startsWith('https://hooks.slack.com/')) return { text }; return { type: 'message', attachments: [ { contentType: 'application/vnd.microsoft.card.adaptive', content: { type: 'AdaptiveCard', version: '1.4', body: [{ type: 'TextBlock', text, wrap: true }] }, }, ], };} createServer((req, res) => { const chunks: Buffer[] = []; req.on('data', (chunk) => chunks.push(chunk)); req.on('end', async () => { const raw = Buffer.concat(chunks).toString('utf8'); if (!isFromSignage(raw, String(req.headers['x-signature'] ?? ''))) { res.writeHead(401).end(); return; } res.writeHead(204).end(); // Binnen 10 Sekunden antworten, danach arbeiten const deliveryId = String(req.headers['x-delivery-id']); if (seen.has(deliveryId) || req.headers['x-event-type'] !== 'fleet.player.status-changed') return; seen.add(deliveryId); const { playerId, from, to } = JSON.parse(raw); let text = ''; if (to === 'offline') text = `${await displayName(playerId)} is offline.`; if (to === 'online' && from === 'offline') text = `${await displayName(playerId)} is back online.`; if (!text) return; await fetch(CHAT, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(chatMessage(text)), }); });}).listen(8080);Terminalexport WEBHOOK_SECRET="whsec_…" CHAT_WEBHOOK_URL="https://hooks.slack.com/services/…"node offline-alert.tsStellen Sie den Empfänger hinter Ihre https-Adresse, zum Beispiel
https://alerts.example.com/szenera. Die Mengeseenlebt im Speicher. Startet der Empfänger neu, kann eine Wiederholung eine zweite Nachricht auslösen, was hier nicht schadet. Für alles, was nur einmal geschehen darf, speichern Sie die Kennungen. -
Legen Sie den Endpunkt an, im Portal unter Einstellungen › Für Entwickler › Webhooks mit Endpunkt anlegen. Tragen Sie die Adresse unter Endpunkt-URL ein, wählen Sie unter Abonnierte Ereignisse
fleet.player.status-changedund klicken Sie auf Anlegen. Kopieren Sie das Secret aus Signatur-Secret — nur jetzt sichtbar nachWEBHOOK_SECRETund starten Sie den Empfänger neu.Die Route
POST /v1/webhooksgeht auch, aber nur mit dem Zugangs-Token einer Anmeldung als Inhaber. Ein API-Schlüssel kann die Webhook-Rechte nicht tragen, siehe Einen Webhook-Endpunkt anlegen. -
Probieren Sie es aus. Ziehen Sie den Netzwerkstecker eines Displays oder schließen Sie den Browser-Tab eines Web-Displays. Nach etwa fünf Minuten kommt die Nachricht im Chat an. Verbinden Sie es wieder, folgt die zweite.
Ergebnis prüfen
- Im Zustell-Log des Endpunkts zeigt jede Zustellung Zugestellt mit
HTTP 204. - Der Chat zeigt den Namen des Displays, nicht seine Kennung. Steht dort die Kennung, kann der Schlüssel das Display nicht lesen.
- Eine Zustellung, die Sie im Zustell-Log mit Erneut zustellen wiederholen, löst keine zweite Nachricht aus.
Wenn etwas nicht klappt
| Symptom | Bedeutung | Lösung |
|---|---|---|
Das Zustell-Log zeigt HTTP 401 |
Die Signatur passt nicht: falsches Secret, oder ein Proxy verändert den Rumpf. | Das Secret neu kopieren und sicherstellen, dass nichts den Rumpf vor der Prüfung umformt. |
| Das Zustell-Log zeigt Zugestellt, aber der Chat bleibt leer | Der Chat weist die Nachricht ab. | Die Antwort des Chats protokollieren. Slack antwortet bei falschem Format mit invalid_payload, ein Teams-Ablauf zeigt den gescheiterten Lauf in seinem Verlauf. |
| Der Chat zeigt die Kennung statt des Namens | GET /v1/players/{id} scheitert: Der Schlüssel fehlt, ist abgelaufen oder hat kein fleet.player.read. |
Einen Schlüssel der Klasse Nur Lesen verwenden. |
| Nach fünf Minuten kommt keine Zustellung | Der Endpunkt hat das Ereignis nicht abonniert, oder das Display war schon offline. | Abonnierte Ereignisse prüfen, siehe Wenn keine Zustellung ankommt. |