Публічний API

Загальне

Вимкнений за замовчуванням: картка «Локальний API» на сторінці «Дії» вмикає його й показує ключ. Працює на порту сервера оверлеїв (за замовчуванням 5233) лише на цьому ПК.

  • Усі запити мають заголовок X-Spalakh-Key: <ключ>. Заголовок Origin (запит із сторінки в браузері) відхиляється. Ключ — 256 випадкових біт; перевипуск обриває старі інтеграції. Старе ім’я заголовка X-BotValeriy-Key приймається далі, тож наявні інтеграції не треба міняти.

Версія API: 1. Усі адреси мають префікс /api/v1. Старі адреси без версії (/api/actions, /api/ws) лишаються псевдонімами v1, тож Stream Deck і ранні скрипти працюють без змін. Правило сумісності: у v1 можуть з'являтись нові поля, події й адреси, але наявні не зникають і не міняють змісту; несумісна зміна вийде як /api/v2, а v1 житиме далі.

  • GET /api/v1 → {"name","version","endpoints","events"}: версія й перелік адрес та типів подій, які знає ця збірка.
  • GET /api/v1/actions → [{"id","name"}]
  • POST /api/v1/actions/{назва-або-id}/run, тіло (до 4 КБ, необов'язкове) {"user","input"} → 202/404
  • WebSocket /api/v1/ws: першим приходить state (apiVersion, live: true/false/null, actions), далі події:
    • stream.online, stream.offline;
    • action.finished (name, source, user, test, failed, ms);
    • event.follow, event.subscription, event.resub, event.gift-sub, event.raid, event.redeem, event.chat, event.donation, event.bits, event.power-up: події каналу, ті самі, що отримують оверлеї й модулі (data: user, platform (twitch, kick, youtube; порожньо для донату Donatello) і extras із деталями події; тестові позначені extras.test; повтори історії оверлею не надсилаються).
    • event.custom: подія, яку надіслала зовнішня програма (див. нижче): data — name, user, data (поля).
    • variable.changed: глобальну змінну задано чи прибрано (чи цим API, чи дією, чи з екрана «Змінні»): data — name, value (null, якщо прибрано). Змінні глядачів у стрічку не йдуть. Повідомлення — JSON {"type","ts","data"}. Невідомі типи слухач має ігнорувати: їх додаватимуть. Повільний слухач губить найстаріші повідомлення.

Власна подія від зовнішньої програми

POST /api/v1/events, тіло (до 4 КБ) {"name","user","data"}: 202 — прийнято, 400 — некоректне тіло ({"error"}), 401/403/404 — як у решти адрес, 413 — тіло завелике, 429 — забагато записів (до 30 на секунду на весь API).

  • name — обов'язкова назва латиницею: a-z, 0-9, _, ., -, до 40 символів (регістр не має значення, зберігається малими).
  • user — необов'язково, до 50 символів; без нього api. Іде в $(user).
  • data — необов'язково, плаский об'єкт: до 20 ключів a-z0-9_ (до 40 символів), значення — рядок, число чи true/false (до 500 символів, довші обрізаються); вкладені об'єкти й масиви відхиляються.

Подія робить дві речі: з'являється в стрічці як event.custom ({"name","user","data"}) для всіх слухачів сокета і запускає тригери «Зовнішня подія» з тією ж назвою (порожня назва в тригері — будь-яка подія). Дія тригера отримує теги $(name), $(user) і $(data.ключ) для кожного поля; $(input) — назва події. Подія не потрапляє в оверлеї й модулі.

curl -X POST http://127.0.0.1:5233/api/v1/events   -H "X-Spalakh-Key: $KEY" -H "Content-Type: application/json"   -d '{"name":"game.death","user":"alice","data":{"count":7,"boss":"Knight"}}'

Мобільна панель

http://127.0.0.1:<порт>/panel: сторінка з кнопками дій, у яких у редакторі дії увімкнено «Показувати в мобільній панелі» (типово вимкнено, тож панель не відкриває всі дії). Колір кнопки -- #RRGGBB у тому ж редакторі, групи -- папки дій. Ключ той самий, що в API: вводиться на сторінці раз і запам'ятовується в цьому браузері; новий ключ у застосунку розлогінює всі панелі. Запуск іде звичайним ланцюжком, в історії джерело «Панель». Межа частоти спільна з API (30 записів на секунду). Запит зі сторінки іншого сайту (чужий Origin) відхиляється.

З телефона: на картці «Локальний API» увімкніть «Доступ із телефона в локальній мережі» й перезапустіть застосунок. Тоді сервер слухає й локальну мережу, але ззовні відкрито лише /panel: оверлеї, API, аудіо й модулі лишаються доступні тільки з цього комп'ютера. Під прапорцем видно адреси для телефона (IP комп'ютера в мережі). Трафік не шифрується: вмикайте лише вдома. QR-коду поки нема, адресу треба набрати.

Вебхук стороннього сервісу

Сервіси на кшталт донат-платформ, GitHub чи IFTTT не вміють свої заголовки й шлють вкладений JSON. Для них є POST /api/v1/hook/{назва}?key=<ключ>: ключ у адресі (заголовок X-Spalakh-Key теж підходить), {назва} — як name вище.

  • Тіло до 4 КБ: JSON або форма (application/x-www-form-urlencoded). Вкладене сплощується: {"data":{"amount":5,"tags":["a","b"]}} дає $(data.data_amount) = 5 і $(data.data_tags_1) = b; символи поза a-z0-9_ стають _, ключ до 40 символів. Поле форми, значення якого — JSON, сплощується так само (data={"amount":5} → data_amount).
  • Береться не більше 20 полів, решта мовчки відкидається.
  • $(user) — поле user з тіла, інакше webhook.
  • Далі все як у власної події: стрічка event.custom, тригер «Зовнішня подія» з цією назвою, ті самі коди відповіді.
curl -X POST "http://127.0.0.1:5233/api/v1/hook/kofi?key=$KEY" -H "Content-Type: application/json" -d '{"user":"bob","amount":5}'

Ключ у посиланні потрапляє в журнали стороннього сервісу. Якщо він кудись витік, змініть ключ у налаштуваннях API. Сервер слухає лише цей комп'ютер (127.0.0.1), тож щоб сервіс з інтернету достукався, потрібен тунель (ngrok, Cloudflare Tunnel), який ви налаштовуєте самі.

Слухач на Node (пакет ws; браузерна сторінка не підходить: Origin відхиляється):

import WebSocket from "ws";
const ws = new WebSocket("ws://127.0.0.1:5233/api/v1/ws", { headers: { "X-Spalakh-Key": process.env.KEY } });
ws.on("message", (raw) => {
  const { type, data } = JSON.parse(raw);
  if (type === "event.custom") console.log(data.name, data.data);
});

Змінні

Глобальні змінні дій (ті, що в кроці «Змінна» і тегу $(var.назва)) читаються й пишуться тим самим ключем. Змінні глядачів через API недоступні.

  • GET /api/v1/variables/{назва} → 200 {"name","value"} або 404, якщо такої змінної немає (чи бота ще немає).
  • PUT /api/v1/variables/{назва}, тіло (до 4 КБ) {"value","mode"} → 200 {"name","value"} з новим значенням. value — рядок, число чи true/false; mode — set (типово, задати) або add (додати число; не число — 400).

Межі ті самі, що в VariableBank: назва a-z, 0-9, _ до 40 символів, значення до 500 символів (довші обрізаються), до 500 глобальних змінних; порушення — 400 з {"error"}. Записи рахуються в загальний ліміт 30 на секунду (429).

curl -X PUT http://127.0.0.1:5233/api/v1/variables/deaths   -H "X-Spalakh-Key: $KEY" -H "Content-Type: application/json" -d '{"value":1,"mode":"add"}'
curl http://127.0.0.1:5233/api/v1/variables/deaths -H "X-Spalakh-Key: $KEY"
const res = await fetch("http://127.0.0.1:5233/api/v1/variables/deaths", { headers: { "X-Spalakh-Key": KEY } });
const { value } = await res.json(); // Node 18+; з браузера не працює (Origin відхиляється)

Команди по сокету

Той самий сокет /api/v1/ws приймає й команди, тож один зв'язок і слухає, і керує (без окремих HTTP-запитів). Клієнт шле текстове повідомлення (до 4 КБ; більше — сокет закривається з кодом 1009), бот відповідає повідомленням ack лише цьому клієнту:

  • {"type":"run","action":"назва-або-id","user":"…","input":"…","id":"будь-що"} — те саме, що POST …/actions/{…}/run;
  • {"type":"event","name":"game.death","user":"…","data":{…},"id":"…"} — те саме, що POST …/events (правила й межі ті самі).

Відповідь: {"type":"ack","data":{"id":"…","ok":true,"error":null}}; id повертається як надіслано (до 50 символів, необов'язковий), error — причина (action not found, rate limit, bad json, unknown type або текст перевірки події). Помилка не закриває сокет. Команди рахуються в той самий ліміт 30 на секунду.

ws.on("open", () => {
  ws.send(JSON.stringify({ type: "event", id: "1", name: "game.death", data: { count: 7 } }));
  ws.send(JSON.stringify({ type: "run", id: "2", action: "Привітання" }));
});
// далі в ws.on("message"): { type: "ack", data: { id: "1", ok: true, error: null } }
Публічний API · Spalakh Platform