Перейти к содержанию

Уведомления через вебхук

Рецепт на Node.js — небольшой сервер принимает вебхуки Slicer, проверяет подпись и присылает в Telegram сообщение, когда ролик готов или опубликован.

На этой странице

Монтаж идёт несколько минут, и ждать его в терминале не нужно. Этот рецепт поднимает маленький сервер, который получает вебхуки Slicer и пишет в Telegram: «Ролик готов» со ссылкой на проект, «Опубликовано» со ссылкой на ролик в соцсети. Так же легко отправлять сообщения в Slack, почту или свою систему задач.

Что понадобится: Node.js 20+, сервер с публичным https-адресом (или туннель для проверки), токен с областью webhooks, бот Telegram (его токен выдаёт @BotFather (откроется в новой вкладке)) и id чата, куда писать.

Сервер

notify.mjs
// npm install express
// SLICER_WEBHOOK_SECRET=whsec_… TELEGRAM_BOT_TOKEN=… TELEGRAM_CHAT_ID=… node notify.mjs
import { createHmac, timingSafeEqual } from 'node:crypto';
import express from 'express';

const TOLERANCE_SEC = 5 * 60;

function verifySlicerSignature(rawBody, header, secret) {
  if (typeof header !== 'string') return false;
  const pairs = header.split(',').map((part) => part.trim().split('='));
  const timestamp = pairs.find(([key]) => key === 't')?.[1];
  const signatures = pairs.filter(([key]) => key === 'v1').map(([, value]) => value);
  if (!timestamp || !/^\d+$/.test(timestamp) || signatures.length === 0) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > TOLERANCE_SEC) return false;
  const expected = createHmac('sha256', secret).update(`${timestamp}.`).update(rawBody).digest();
  return signatures.some((value) => {
    const received = Buffer.from(value, 'hex');
    return received.length === expected.length && timingSafeEqual(received, expected);
  });
}

async function telegram(text) {
  const response = await fetch(`https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}/sendMessage`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ chat_id: process.env.TELEGRAM_CHAT_ID, text, disable_web_page_preview: true }),
  });
  if (!response.ok) console.error('Telegram ответил', response.status);
}

function message(event) {
  const item = event.data ?? {};
  switch (event.type) {
    case 'project.succeeded':
      return `🎬 Ролик «${item.title}» готов — ${Math.round(item.durationSec ?? 0)} с.\n${item.appUrl}`;
    case 'export.succeeded':
      return `📦 Экспорт «${item.title}» готов.\n${item.appUrl}`;
    case 'publication.published':
      return `🚀 Опубликовано в ${item.platform}: ${item.url}`;
    case 'ping':
      return '✅ Вебхук Slicer подключён';
    default:
      return null; // остальные события этому боту не нужны
  }
}

const app = express();
const seen = new Set(); // id событий: повторная доставка не пришлёт второе сообщение

app.post('/slicer/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifySlicerSignature(req.body, req.get('Slicer-Signature'), process.env.SLICER_WEBHOOK_SECRET)) {
    return res.status(400).send('invalid signature');
  }
  const event = JSON.parse(req.body.toString('utf8'));
  res.sendStatus(204); // Slicer ждёт ответа не дольше 10 секунд — отвечаем сразу

  if (seen.has(event.id)) return;
  seen.add(event.id);
  const text = message(event);
  if (text) telegram(text).catch((error) => console.error(error));
});

app.listen(Number(process.env.PORT ?? 3000), () => console.log('Жду вебхуки Slicer'));

Подключение

  1. Запустите сервер по https

    Разместите notify.mjs на сервере с публичным https-адресом. Для проверки на своём компьютере подойдёт туннель:

    Терминал
    node notify.mjs &
    cloudflared tunnel --url http://localhost:3000

    Туннель напечатает адрес вида https://….trycloudflare.com.

  2. Зарегистрируйте вебхук

    Терминал
    curl https://api.slicer.media/v1/webhooks \
      -H "Authorization: Bearer $SLICER_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"url":"https://notify.example.com/slicer/webhook","events":["project.succeeded","export.succeeded","publication.published"],"description":"Telegram"}' \
      | jq -r '.data.secret, .data.webhook.id'

    Первая строка ответа — секрет whsec_…: передайте его серверу в SLICER_WEBHOOK_SECRET и перезапустите его. Вторая — id вебхука.

  3. Проверьте

    Терминал
    curl -X POST https://api.slicer.media/v1/webhooks/$WEBHOOK_ID/test -H "Authorization: Bearer $SLICER_TOKEN"

    В чат придёт «✅ Вебхук Slicer подключён». Если нет — посмотрите журнал доставок: GET /v1/webhooks/{id}/deliveries покажет код ответа вашего сервера.

Тонкости

  • Сырое тело. express.raw отдаёт тело как есть — подпись считается именно по нему. Если в приложении уже стоит express.json() глобально, подключите маршрут вебхука до него.
  • Повторы. Если сервер не ответил за 10 секунд или ответил ошибкой, Slicer повторит доставку позже. Множество seen не даёт отправить сообщение дважды; в продакшене храните id событий в Redis или базе.
  • Безопасность. Не логируйте секрет и не отвечайте подробностями на неверную подпись. Ссылка appUrl открывает проект только после входа в аккаунт Slicer, поэтому её можно отправлять в рабочий чат.

Что дальше