Система Event Flow

Руководство по редактору Event Flow

Создавайте надёжные автоматизации Social Stream Ninja. Руководство объясняет основы, логические узлы, прохождение сигналов и частые практические вопросы: как избежать эха чата и когда сочетать блоки AND/NOT.

Русский

0. Краткое введение

Event Flow — редактор на основе узлов. Каждая связь передаёт payload сообщения и логическое состояние (true = продолжить, false = остановить). Используйте источники для подачи событий, логические узлы для проверки условий и действия для выполнения действий: отправки чата, управления оверлеями, ретрансляции и т. д.
Нужно запомнить участников, позже проверить допуск, провести розыгрыш среди уникальных пользователей или очистить один именованный список? Откройте Руководство по памяти о пользователях для модели общего состояния, снимков экрана и импортируемого примера.

Что это за редактор?

Редактор Event Flow — слой расширенной автоматизации Social Stream Ninja. Он дополняет простые переключатели popup и позволяет задать собственную логику маршрутизации. Используйте его, когда нужно:

  • Передавайте чат между сервисами с фильтрами: например, из Twitch в Discord, исключая команды.
  • Создавайте команды на основе лояльности, игры с ключевыми словами и допуск к розыгрышам с логикой AND/OR/NOT.
  • Запускайте пользовательские оверлеи, аудио, сцены OBS или webhooks на основе данных, дополненных в потоке.
  • Объединяйте несколько платформ в одной автоматизации: Kick, Twitch и YouTube через один поток.

Popup предлагает быстрые предустановки, а Event Flow — набор инструментов для собственных сценариев.

Запуск и основы

  • Откройте редактор Event Flow из меню основной панели в приложении или расширении.
  • Каждый проект хранится локально до экспорта. Используйте Export для резервного копирования или обмена.
  • Работайте на рабочих полях, называемых потоками. Каждый поток может подписываться на несколько платформ одновременно.

Обзор узлов

  • Входы (левые разъёмы) ожидают контекст сообщения.
  • Выходы (правые разъёмы) выдают тот же контекст с внесёнными изменениями.
  • Логические узлы могут передавать сигналы как в канал true так и в необязательный канал false .

Структура payload

Каждое сообщение содержит объект JSON. Обязательные ключи соответствуют docs/event-reference.html (platform, type, chatname, chatmessage и т. д.). Добавляйте собственные данные в meta.

Каждый поток начинается с триггера

Узлы действий (зелёные) никогда не запускаются сами: они срабатывают, только когда расположенный выше узел триггера (синий) возвращает true. Поток из одних последовательно соединённых действий выглядит корректно, но всегда бездействует: ничто не запускает цепочку. Имена узлов описывают, что узел делает, а не когда это происходит: Выделить сообщение выделяет сообщение, когда поток доходит до него, а не срабатывает при выделении сообщения в другом месте.

Два последовательно соединённых действия без триггера
❌ Никогда не запускается. Выделение сообщения и озвучивание текста — оба действия; без триггера в начале ничто не запускает цепочку.
Триггер «Любое сообщение», связанный с действиями выделения сообщения и озвучивания текста
✅ Работает. Узел Любое сообщение (или проверка текста, регулярное выражение, событие пожертвования и т. п.) запускает цепочку; оба действия выполняются для каждого подходящего сообщения.

Оверлей Flow Actions (вывод действий)

Начните с шаблона оповещения:

Выберите Пожертвование: празднование + голос для готовой анимации и синтетического клипа благодарности либо расширенный Пожертвование: анимация + звук + фильтр OBS шаблон. Новые шаблоны оповещений изначально выключены, чтобы вы сначала настроили и проверили их. Для шаблона OBS выберите источник и один и тот же обычно выключенный фильтр в обоих действиях фильтра.

Воспроизвести аудиоклип и Multi-Alerts теперь используют общую библиотеку из 17 звуков: аплодисменты, барабанная дробь, свист, кассовый аппарат и другие эффекты, четыре подписанные синтетические английские фразы и простые звуки. Слушать / Остановить воспроизводит локально с видимым статусом. Можно также загрузить запись или выбрать локальный файл приложения. Для меняющихся имён и сообщений используйте существующее Озвучить текст действие.

Event Flow воспроизводит звук через источник браузера Flow Actions ; Multi-Alerts воспроизводит через свой источник. Включайте звук только в одном для одного события, чтобы избежать дублирования. Перейдите к узлу клавишей Tab и нажмите Enter или пробел для редактирования свойств.

Таким узлам, как Воспроизвести аудиоклип, Показать медиаоверлей, и управлению OBS нужна страница для выполнения и отображения. Этой страницей служит оверлей Flow Actions, доступный по адресу actions.html. Держите её открытой в программе вещания (OBS, браузерных док-панелях Streamer.bot и т. п.), чтобы действиям Event Flow было где отображаться.

Триггер «Любое сообщение», связанный с действием воспроизведения аудиоклипа
Этот поток закончен и срабатывает на каждое сообщение, но звук воспроизводится на странице оверлея Flow Actions, а не в редакторе. Кнопка предпросмотра в редакторе воспроизводит звук локально; для работы в эфире оверлей должен быть открыт. Если браузер блокирует автозапуск, нажмите Включить звук на странице Flow Actions, чтобы повторить последний заблокированный клип. Нажатие в другом месте этой страницы также разрешает воспроизведение. Источник браузера OBS обычно разрешает автозапуск.
Как открыть из popup/панели:
  1. Откройте основной popup Social Stream Ninja (окно, загруженное из popup.html или значка расширения).
  2. Прокрутите до карточки Flow Actions. Используйте кнопку [скопировать ссылку] или нажмите URL в карточке.
  3. Ссылка выглядит как https://socialstream.ninja/actions.html?session=YOURSESSION. Вставьте ссылку в источник браузера OBS (рекомендуется 1920×1080) либо откройте в любом браузере для оверлеев.
Использование локальных медиа в настольном приложении:
  1. В действии воспроизведения аудиоклипа или отображения медиаоверлея нажмите Выбрать локальный файл.
  2. Нажмите Скопировать локальный URL Flow Actions для OBS и используйте созданный localhost URL вместо размещённого URL Flow Actions.
  3. Держите SSApp запущенным. Если выбранный файл перемещён, вернитесь к действию и нажмите Указать файл заново.

Расширение Chrome не может само предоставлять файлы с диска. Если нет настольного вспомогательного приложения, используйте загрузку или размещённый URL. См. руководство по медиафайлам для Event Flow для полной настройки.

После загрузки этот оверлей может:

  • Показывать GIPHY или прямые URL медиа, текст и конфетти, запускаемые потоками.
  • Воспроизводить звуки — TTS и аудиоклипы — локально, чтобы зрители их слышали.
  • Управляйте OBS через настройки WebSocket в разделе Flow Actions popup: смена сцен, переключение источников, обновление текста GDI+/FreeType, буфер повтора и т. д.
Режимы управления OBS:
  • API источника браузера: доступно только когда actions.html запущен внутри источника браузера OBS с включённым режимом Расширенный уровень доступа. Здесь работает смена сцен; действия записи, трансляции и буфера повтора могут использовать этот запасной путь.
  • OBS WebSocket: рекомендуется для стабильного управления. Flow Actions Social Stream Ninja использует API OBS WebSocket v5 из OBS 28+ и ожидает современный набор запросов на порту 4455.
  • Пароль: необязательно. Добавляйте только &obspw=... к URL Flow Actions, если сервер OBS настроен на обязательную авторизацию.
  • Диагностика оверлея: добавьте &obsdebug=1 к URL страницы actions.html если при устранении неполадок нужен небольшой индикатор подключения OBS на оверлее.
  • Задать текст источника: напрямую обновляет входы «Текст (GDI+)» и «Текст (FreeType 2)» OBS и поддерживает переменные шаблонов Event Flow, например {counterValue} и {counterTarget}.
  • Старые установки 4.x: если вы всё ещё используете obs-websocket 4.x / порт 4444, действия с источниками, фильтрами, отключением звука и текстом не заработают до обновления OBS / obs-websocket.

См. отдельное Руководство по управлению OBS для всех триггеров, действий, шагов настройки и проверенных примеров.

Рекомендуемый порядок диагностики:
  1. Открыть obs-websocket-test.html.
  2. Убедитесь, что GetVersion, GetCurrentProgramScene, и GetSceneList выполняются успешно.
  3. Сначала выполните там проверку нужного действия, затем тестируйте всю автоматизацию Event Flow.
Держите оверлей открытым. Закрытие Flow Actions приостанавливает все действия Event Flow для оверлеев, аудио и OBS. Лучше скройте страницу или переместите её на другой монитор.

1. Что проходит через узел?

Среда Event Flow передаёт по каждой связи две вещи:

  1. Payload – объект данных события или сообщения.
  2. Сигнал шлюза – это true/false бит, сообщающий следующему узлу, нужно ли выполняться.
Если узел выдаёт false: последующие узлы прекращают работу, если не получают вход по другой ветке (например, через false разъём узла условия). Это позволяет создать запасную логику без дублирования целых потоков.

Ожидания от входа

  • Источники событий (сообщение Twitch, таймеры, ручной триггер и т. п.) игнорируют вход сверху: они создают собственный payload и всегда выдают true если только сам узел не выдаёт ошибку.
  • Узлы преобразования и логики читают payload и могут менять поля, задавать состояние или менять сигнал шлюза на false.
  • Узлы действий срабатывают, только пока шлюз остаётся true. При необходимости они также могут выдать обновлённый payload для продолжения цепочки действий.

Варианты выхода

Один выход

У большинства узлов один выход. Входящий payload и сигнал шлюза выходят без изменений, если узел их не редактирует.

Выходы true/false

Узлы условий, сравнения, регулярных выражений и логики имеют два выхода. Истина продолжает путь через зелёный выход; false становится доступным на сером/красном выходе.

Передача без изменений и переопределение

Некоторые узлы — задание переменной, математика, замена текста — меняют payload, но всё равно передают true/false состояние входа. Другие узлы, например NOT, AND, OR, сами пересчитывают логическое значение.

2. Краткая справка по логическим узлам

Эти блоки отвечают на частые вопросы о значениях true/false.

NOT (НЕ)

  • Входы: 1 логическое значение true/false из предыдущего узла.
  • Выходы: инвертированное логическое значение и неизменённый payload.
  • Поведение по умолчанию: Если ко входу NOT ничего не подключено, он вычисляется как false, поэтому выход равен true.
Пример: Поставьте NOT после «Содержит ключевое слово», чтобы запускать оповещение, когда зритель не использует ключевое слово.

AND (И)

  • Входы: два или более логических сигнала (A, B, ...). Лишние порты можно оставить пустыми.
  • Выходы: true только если все подключённые входы равны true.
  • Используйте AND, когда несколько условий должны выполняться одновременно («подписчик» и «сообщение чата содержит !raffle»).

OR

  • Отправляет true если любой подключённый вход равен true.
  • Удобно для триггеров разных платформ: соедините узлы сообщений Twitch и YouTube с одним OR, затем используйте общее действие.
Всегда ли нужен узел AND?
Нет. Во многих узлах уже есть комбинированные фильтры, например «Уровень пользователя» и «Содержит текст». Используйте AND, если встроенные параметры не покрывают нужное сочетание или нужен общий логический узел для нескольких веток.
NOT и пустые входы: Неподключённый узел NOT всё равно выдаст true. Подключите его к значимому условию или отключите узел, чтобы случайно не разрешить выполнение потока.

3. Примеры небольших потоков

A. Автоответ, если сообщение не является командой

Сообщение Twitch ──▶ Регулярное выражение "^!" ─┐ │ ├─false──▶ Автоответ («Спасибо за общение!») │ └─true──▶ Ничего не делать

Здесь узел регулярного выражения выдаёт true когда сообщение является командой. Мы направляем false выход к ответу, поэтому обычные участники получают подтверждение, а команды проходят дальше.

B. Несколько обязательных проверок через AND

Сообщение YouTube ──▶ Содержит "!queue" ─▶ AND ─▶ Передать в Discord Подаренное участие ─▶ Роль = участник ──▲

Узел AND гарантирует, что в Discord попадут только участники с правильным ключевым словом. Обе ветки отправляют свой логический результат в AND; payload из первой ветки передаётся дальше.

C. Узел NOT для блокировки повторных оповещений

Payload события ─▶ Проверка состояния (isAlertMuted) └─false─▶ NOT ─▶ Воспроизвести празднование

State Check выдаёт значение true когда оповещение отключено. Инвертируя результат, NOT гарантирует, что празднование запускается только при значении флага false.

D. Случайное воспроизведение одного из двух звуков

Поток с узлами RANDOM, NOT и AND для случайного воспроизведения одного из двух аудиоклипов
Случайный выбор 50/50 между двумя аудиоклипами. Узел RANDOM делает выбор один раз на подходящее сообщение: при успехе играет звук A; при неудаче NOT инвертирует результат, а AND пропускает звук B.
Триггер ──▶ RANDOM (50%) ──▶ Воспроизвести звук A │ └──▶ NOT ──▶ AND ──▶ Воспроизвести звук B Триггер ──────────────────▲

Узел AND обязателен. Одинокий NOT выдавал бы true каждый раз, когда RANDOM бездействует, поэтому звук B звучал бы на каждом сообщении, которое не соответствует триггеру. Подключение триггера вторым входом AND ограничивает звук B только подходящими сообщениями. Такой подход работает с любой парой альтернативных действий, не только со звуком.

4. Защита от эха, циклов и обратной ретрансляции

Ретрансляция чата между средами полезна, но может создать бесконечное эхо, если захватывать собственный вывод. Соблюдайте следующие меры:

Примечание о получателе YouTube Shorts:
Триггеры входящих сообщений и получатели исходящих сообщений Relay Chat различают youtube и youtubeshorts. Используйте два действия ретрансляции, если сообщение должно попасть в оба варианта. См. YouTube Shorts и Event Flow.
Ретрансляция чата автоматически пропускает распознанные отражения.
Отражение — исходящее сообщение, повторно захваченное из целевого чата. Текущие действия ретрансляции чата пропускают распознанные отражения; отдельной опции No Reflections нет. Чтобы скрыть или ограничить их показ в док-панели и оверлеях, используйте Reflection Filter («Фильтр отражений») действие с Block All («Блокировать все»), Allow First («Разрешить первое»), или Allow All («Разрешить все»). Это управляет показом при повторном захвате, а не отправкой. Следуйте пошаговой инструкции по настройке ретрансляции Twitch и YouTube для полной настройки.
  • Избегайте дублирования систем ретрансляции. Отключите глобальную ретрансляцию всех сообщений при использовании аналогичных маршрутов Event Flow и проверьте другие сервисы, связывающие те же чаты. Пользовательские метаданные не гарантированно переживут передачу через чат платформы.
  • Используйте узлы Debounce или Cooldown для оповещений не чаще одного раза в X секунд.
  • Разрывайте циклы намеренно. Если две ветки подают данные друг другу, добавьте логический узел, проверяющий переменную состояния «currentlyRelaying», чтобы поток завершался раньше при установленном флаге.

5. Входы, выходы и практические вопросы

Что поступает в узел?

  • Полный payload сообщения.
  • Бит шлюза (true/false).
  • Дополнительный контекст, например переменные состояния и таймеры, который узел явно запрашивает.

Что выходит из узла?

  • Тот же payload, если узел его не меняет.
  • Пересчитанный логический бит (логические узлы) или переданный без изменений бит (действия).
  • Большинство побочных действий, например отправка чата, не меняют payload, но действия с баллами могут добавлять поля статуса, например pointsTotal или pointsSpendError для последующей логики.

Когда делать ветвление?

Когда нужно по-разному реагировать на true и false. Протяните связь от нужного цветного выхода (зелёный = true, серый/красный = false) к следующему узлу.

Помните: Если вы ничего не делаете с выходом false , поток просто заканчивается здесь. Это удобно для фильтров «блокировать всё, что не прошло проверку», но не забудьте подключить false путь, если нужны запасные варианты.

Частые вопросы и ответы

  • Нужно ли ставить AND для каждой пары фильтров? Нет. Многие узлы включают несколько проверок: например, основной фильтр сообщений поддерживает ключевое слово и роль. AND нужен для сложных сочетаний или объединения сигналов разных узлов.
  • Как значения true/false попадают в узел NOT? Любой узел с зелёным выходом выдаёт true по умолчанию. Когда условие не выполнено, выдаётся false. Подключите эту связь к NOT, чтобы инвертировать результат.
  • Может ли узел выдать payload, если возвращает false? Да. Payload всё равно передаётся через выход false; вы решаете, куда направить эту ветку.
  • Как определить участников команды TikTok? Выберите Участник команды TikTok в узле роли пользователя. Он распознаёт уровни и значки фан-клуба/команды TikTok во входящем сообщении и не зависит от настроек основного оверлея чата.
  • Может ли каждый узел озвучивания использовать отдельный голос? Да. Введите поддерживаемое провайдером имя или ID голоса в Переопределение голоса, либо оставьте поле пустым, чтобы использовать голос TTS по умолчанию в Flow Actions.

6. Справочник переменных шаблонов

Несколько действий — показ текста, задание текста источника, отправка и ретрансляция сообщений, озвучивание, вызов webhook и печать термоэтикетки — поддерживают переменные шаблонов которые заменяются данными события во время выполнения. Заключайте имена переменных в фигурные скобки, например {username}.

Основные переменные (обратная совместимость)

ПеременнаяПсевдонимОписаниеПример
{username}{chatname}Отображаемое имя пользователяCoolViewer123
{message}{chatmessage}Текст сообщения чатаВсем привет!
{source}-Имя платформы с заглавной буквыTwitch, YouTube
{type}-Исходное имя платформыtwitch, youtube
{donation}{hasDonation}Строка отображения пожертвования/чаевых$5.00, 500 bits

Дополнительные переменные

ПеременнаяОписаниеПример
{displayname}Отображаемое имя (альтернативное поле)CoolViewer123
{donoValue}Эквивалент пожертвования в USD, предоставленный или рассчитанный; Event Flow получает пороговые значения из нормализованных строк hasDonation вида: значение, $значение, значение + единица или компактная запись единицы/значения. Для неизвестных именованных виртуальных единиц применяется 100 единиц = $0.01 USD; подарки TikTok без цены считаются как одна монета за подарок ($0.01 каждый). {donationAmount} — устаревший псевдоним5.00
{event}Идентификатор типа событияcheer, raid, new_follower
{membership}Статус участияMEMBERSHIP, new_sponsor
{subtitle}Дополнительный контекстУчастник 3 месяца
{userid}ID пользователя на платформе12345678
{chatimg}URL аватара пользователяhttps://...
{contentimg}URL вложенного изображенияhttps://...
{rewardTitle}Название награды, если источник предоставляет поле её заголовка на верхнем уровнеВыделить моё сообщение
{meta}Структурированные данные события (JSON){"viewers":100}
{counterValue}Текущее значение после шага счётчика или его проверки12
{counterTarget}Целевое значение счётчика30
{counterRemaining}Цель счётчика минус текущее значение, не ниже 018
Сопоставление переменных не учитывает регистр. {USERNAME}, {Username}, и {username} работают одинаково.
Поля, добавленные потоком, тоже работают. Если предыдущее действие добавляет значение верхнего уровня в сообщение, следующие шаблоны могут читать его напрямую. Так Check Counter предоставляет {counterValue}, {counterTarget}, и {counterRemaining}.
JSON вызова webhook: Переменные шаблонов работают в строковых значениях JSON на любой глубине вложенности объектов и массивов. Ключи объектов не подставляются; пользовательское тело без подстановок отправляется без изменений.

Примеры шаблонов

  • Показать текст: {username} just cheered {hasDonation}!
  • Задать текстовый источник OBS: {username}: now {counterValue}, need {counterTarget}
  • Ретрансляция чата: [{source}] {username}: {message}
  • TTS: {username} says {message}
  • Оповещение о пожертвовании: {username} donated {donation} - {subtitle}
  • Термоэтикетка: {username}, новую строку, затем {donation}. См. Руководство по термопринтеру для настройки принтера, этикеток фиксированного размера и полного потока.
  • Вызов webhook Discord: {"content":"{message}","username":"{username}","avatar_url":"{chatimg}"}
Отсутствующие переменные заменяются пустыми строками. Если в событии нет определённого поля (например, {donation} в обычном сообщении чата), вместо буквального текста {donation} подставляется пустая строка.

7. Рекомендации по работе

  • Назовите и раскрасьте узлы, чтобы в будущем было понятно назначение каждой ветки.
  • Тестируйте встроенным симулятором («Отправить тестовое событие») перед запуском потока в эфире.
  • Группируйте логику рядом с источником. Фильтруйте как можно раньше, чтобы избежать лишней обработки дальше по цепочке.
  • Храните повторы в узлах состояния. Используйте счётчики, переключатели и отметки времени, чтобы избежать двойных оповещений.
  • Документируйте поля meta. Когда добавляете пользовательские meta ключи, описывайте их, чтобы оверлеи и удалённые клиенты оставались согласованными.
Сохраняйте версии. Экспортируйте поток после каждого значимого этапа. Импорт — самый простой способ вернуться к рабочей версии, если эксперимент пошёл неудачно.

8. Дополнительные возможности

Запускайте пользовательские потоки из Stream Deck или API: именованные триггеры, начальный шаблон, поиск потоков, дополнительные данные, примеры HTTP/WebSocket/P2P и жесты поворотных регуляторов.

Хотите узнать больше?

  • Используйте Узлы состояния (счётчики, переключатели, таймеры) для сохранения контекста между событиями.
  • Объедините Переменные + логика для создания очередей, розыгрышей или систем подсчёта очков.
  • Подключитесь к системе Баллы и награды , чтобы зрители могли намеренно запускать потоки.
  • Используете настольное приложение SSApp? Разблокировать Узлы пользовательского JavaScript для произвольной логики, не покрываемой встроенными узлами.
  • Проверьте Справочник событий для подробной документации payload всех платформ.

Руководство намеренно автономно: скопируйте его локально, адаптируйте для команды и продолжайте экспериментировать в редакторе.

9. Пользовательский JavaScript Только SSApp / настольное приложение

Два узла редактора Event Flow позволяют писать произвольный JavaScript, выполняемый в цепочке потока: Пользовательский код (триггер) и Выполнить пользовательский код (действие). Это способ реализовать всё, что нельзя выразить встроенными узлами.

Нужно настольное приложение. Узлы пользовательского JavaScript отключены в расширении браузера, потому что политика безопасности содержимого Manifest V3 Chrome блокирует new Function() / eval(). Откройте редактор через Настольное приложение SSApp для их включения. В режиме расширения узлы недоступны и помечены «Только настольное приложение».
Редактирование кода: выберите узел пользовательского кода и нажмите Открыть редактор кода для большого окна редактирования. Сохранить и закрыть проверяет синтаксис JavaScript и сохраняет весь поток; Ctrl+S или Cmd+S делает то же самое. Отмена оставляет узел без изменений.
Редактор Event Flow — пустое состояние
Редактор Event Flow. Слева перечислены доступные узлы, на точечном рабочем поле создаются потоки, справа — свойства выбранного узла.

Пользовательский код — узел триггера

Перетащите Пользовательский код из группы Дополнительно на панели Триггеры на рабочее поле. Он действует как шлюз: поток продолжается, только если ваш код возвращает true.

Панель триггеров с узлом пользовательского кода в группе «Дополнительно»
Пользовательский код находится в группе Дополнительно на панели триггеров.
Свойства триггера пользовательского кода с редактором JavaScript
Панель свойств после добавления триггера. Напишите любое выражение, возвращающее true или false.
Сигнатура: ваш код выполняется как function(message) { ... }
Должен возвращать: логическое значение — true чтобы поток продолжился, false для его остановки.
Доступно: объект message (см. API сообщений ниже), а также convertCurrency(value, targetCurrency, source) и convertToUSD(value, source).

Выполнить пользовательский код — узел действия

Перетащите Выполнить пользовательский код из группы Интеграции на панели Действия . Может изменить сообщение, заблокировать его или добавить метаданные для следующих узлов.

Панель действий с пунктом выполнения пользовательского кода в группе интеграций
Действие выполнения пользовательского кода в группе Интеграции на панели действий.
Свойства действия выполнения пользовательского кода с редактором кода
Свойства действия. Верните объект, чтобы объединить изменения с результатом потока.
Сигнатура: ваш код выполняется как function(message, result) { ... }
Должен возвращать: объект или Promise, объединяемый с result— см. API результатов.
Доступно: message (payload события), result (текущее состояние результата потока), printThermal(html, options), а также convertCurrency(value, targetCurrency, source) и convertToUSD(value, source).
Термопечать в SSApp: выберите принтер, настройте ширину бумаги и безопасные поля в Управление принтером, затем верните printThermal('<strong>' + message.chatname + '</strong>'). SSApp без диалогов ставит задание в очередь через нативный API печати Windows и использует сохранённые настройки. Поток может переопределить их параметрами, например { width: '58mm', marginLeft: '3mm', marginRight: '3mm', marginTop: '2mm', marginBottom: '2mm', feed: '3mm', marginType: 'printableArea' }. Возврат Promise позволяет Event Flow дождаться отправки и сообщить об ошибках.
Рабочее поле с триггером пользовательского кода и действием выполнения пользовательского кода рядом
Триггер пользовательского кода (синий) и действие выполнения пользовательского кода (зелёное) на рабочем поле. Соедините выход триггера со входом действия.

Объект message

Оба узла получают полный payload события как message. Поля ниже доступны всегда; события отдельных платформ могут содержать дополнительные поля.

ПолеТип данныхОписаниеПример
message.chatmessageстрокаТекст сообщения чата, может содержать HTML"Hello stream!"
message.chatnameстрокаОтображаемое имя отправителя"CoolViewer"
message.useridстрокаID пользователя на платформе"12345678"
message.typeстрокаПлатформа источника в нижнем регистре"twitch", "youtube", "kick"
message.hasDonationстрокаФорматированная строка пожертвования, если есть"$5.00", "500 bits"
message.donoValueчисло / строкаЭквивалент пожертвования в USD, если его предоставляет источник; корректный ноль учитывается. Иначе Event Flow использует currency.js для пересчёта нормализованных строк hasDonation для сравнения с порогами, включая 100 неизвестных именованных единиц = $0.01 USD. Он не извлекает суммы пожертвований из обычного текста поля chatmessage .5
message.eventстрокаИдентификатор типа события"new_follower", "cheer", "raid"
message.membershipстрокаСтатус участия, если применимо"MEMBERSHIP"
message.subtitleстрокаДополнительная строка контекста"Member for 3 months"
message.modлогическое значениеОтправитель — модераторtrue
message.subscriberлогическое значениеОтправитель — подписчикtrue
message.vipлогическое значениеУ отправителя статус VIPtrue
message.chatimgстрокаURL аватара пользователя"https://..."
message.metaобъектПроизвольные структурированные данные события{ viewers: 120 }
Пересчёт валют: используйте convertCurrency(message.hasDonation, 'EUR', message.type) для преобразования форматированной строки пожертвования в EUR. Возвращает число либо null если запрошенная целевая валюта не поддерживается. Конвертер использует приблизительные внутренние курсы Social Stream Ninja и не обращается к внешним обменным сервисам.

Что возвращает действие

Верните обычный объект из кода действия. Включённые поля объединяются с объектом result ; пропущенные поля сохраняют текущие значения.

Поле возвратаТип данныхДействие
modifiedлогическое значениеУстановите true если изменили message поля. Сообщает следующим узлам, что payload изменён.
messageобъектВерните сообщение с возможными изменениями, чтобы следующие узлы получили их.
blockedлогическое значениеУстановите true чтобы сообщение не отображалось и не ретранслировалось.
Минимальный безопасный возврат: return { modified: false, message };
Даже если вы ничего не изменили, возврат message сохраняет передачу следующему узлу.

Примеры кода

Скопируйте любой пример в поле JavaScript Code соответствующего типа узла.

Примеры триггеров — возвращайте true для продолжения потока

Поиск ключевого слова без учёта регистра
Продолжайте поток только если сообщение содержит конкретное слово или фразу.
// Matches "!hello" anywhere in the message return (message.chatmessage || '').toLowerCase().includes('!hello');
Определение команд регулярным выражением
Находите сообщения, начинающиеся с команды из заданного списка (например, !queue, !raffle, !enter).
return /^!(queue|raffle|enter)\b/i.test(message.chatmessage || '');
Пожертвование выше порога
Срабатывайте только при пожертвовании не ниже минимальной суммы.
const amount = message.donoValue !== undefined && message.donoValue !== null && message.donoValue !== '' ? Number(message.donoValue) : (typeof convertToUSD === 'function' ? convertToUSD(message.hasDonation || '', message.type || '') : Number(String(message.hasDonation || '').replace(/[^0-9.]/g, '') || 0)); return amount >= 5;
Super Chat или Super Sticker YouTube в диапазоне EUR
Преобразуйте стандартную строку пожертвования YouTube в EUR, исключите Jewels/Gifts и выберите один диапазон звукового или визуального эффекта.
var eventName = String(message.event || '').toLowerCase(); if (eventName !== 'superchat' && eventName !== 'supersticker') return false; var eurValue = convertCurrency(message.hasDonation || '', 'EUR', message.type || ''); if (typeof eurValue !== 'number' || !isFinite(eurValue)) return false; message.eurValue = eurValue; return eurValue >= 10 && eurValue < 25;
Фильтр платформы
Обрабатывать события только с заданных платформ.
return ['twitch', 'youtube'].includes(message.type);
Допуск подписчика / VIP / модератора
Разрешайте продолжение потока только привилегированным пользователям.
return !!(message.subscriber || message.vip || message.mod);
Несколько условий — VIP + ключевое слово
Объедините проверку роли и текста сообщения в выражении, которое не покрывается встроенным триггером.
const isPrivileged = !!(message.subscriber || message.vip || message.mod); const isCommand = /^!feature\b/i.test(message.chatmessage || ''); return isPrivileged && isCommand;
Фильтр по длине сообщения
Обрабатывать только достаточно содержательные сообщения — полезно для TTS и ретрансляции, чтобы избегать спама одиночными эмодзи.
return (message.chatmessage || '').replace(/<[^>]+>/g, '').trim().length >= 20;

Примеры действий — возвращайте { modified, message }

Добавить значок или метку к сообщению
Добавьте визуальный индикатор в конец каждого сообщения, проходящего через действие.
message.chatmessage = (message.chatmessage || '').trimEnd() + ' ✅'; return { modified: true, message };
Блокировать сообщение по условию
Проверьте содержимое и молча отбросьте сообщение при совпадении правила — удобно для спама, который нельзя описать фильтром ключевых слов.
const text = (message.chatmessage || '').toLowerCase(); const spamPatterns = ['buy followers', 'free nitro', 'click here']; if (spamPatterns.some(p => text.includes(p))) { return { blocked: true, message }; } return { modified: false, message };
Удалить @упоминания
Удалите все упоминания @username из сообщения перед передачей на другую платформу.
message.chatmessage = (message.chatmessage || '').replace(/@\w+/g, '').trim(); return { modified: true, message };
Оформить объявление о пожертвовании
Перепишите chatmessage в единообразное объявление, если есть пожертвование.
const amount = parseFloat(message.donoValue || 0); if (amount > 0) { const note = (message.chatmessage || '').trim(); message.chatmessage = `💰 ${message.chatname} donated $${amount.toFixed(2)}!` + (note ? ` "${note}"` : ''); return { modified: true, message }; } return { modified: false, message };
Добавить метаданные маршрутизации для последующих узлов
Пометьте сообщение пользовательским полем, которое последующее действие Relay Chat («Переслать в чат») или Отправить сообщение может прочитать через переменную шаблона ({meta}).
message.meta = message.meta || {}; // Assign a donation tier so the next node can use {meta} to decide overlay colour const amount = parseFloat(message.donoValue || 0); message.meta.donationTier = amount >= 20 ? 'gold' : amount >= 5 ? 'silver' : 'bronze'; return { modified: true, message };
Префикс сообщения с учётом платформы
Добавляйте метку платформы перед сообщением при ретрансляции между платформами, чтобы зрители знали источник.
const labels = { twitch: '[Twitch]', youtube: '[YouTube]', kick: '[Kick]', tiktok: '[TikTok]', }; const label = labels[message.type] || `[${message.type || 'Chat'}]`; message.chatmessage = `${label} ${message.chatname}: ${message.chatmessage || ''}`; return { modified: true, message };

Полный пример: бот предложений новых функций для VIP

Этот поток ожидает !feature <text> от подписчиков, VIP или модераторов, переформатирует как предложение новой функции и передаёт другому получателю, например в Discord.

┌──────────────────────┐ ┌──────────────────────────┐ ┌──────────────────┐ │ Триггер пользовательского кода │────▶│ Действие выполнения кода│────▶│ Ретрансляция чата │ │ │ │ │ │ (в Discord) │ │ Условие: VIP/подписчик/мод │ │ Переформатировать текст │ │ │ │ + начинается с │ │ → «📋 Запрос новой функции │ │ │ │ !feature │ │ от {name}: {text}» │ │ │ └──────────────────────┘ └──────────────────────────┘ └──────────────────┘

Шаг 1 — триггер пользовательского кода (вставьте в поле JavaScript Code триггера):

// Only let VIPs, subscribers, and mods through, and only for !feature commands const isPrivileged = !!(message.subscriber || message.vip || message.mod); const isCommand = /^!feature\b/i.test((message.chatmessage || '').trim()); return isPrivileged && isCommand;

Шаг 2 — действие выполнения пользовательского кода (вставьте в поле JavaScript Code действия):

// Strip the "!feature" command word and format a clean announcement const featureText = (message.chatmessage || '') .replace(/^!feature\s*/i, '') .trim(); if (!featureText) { // No text after the command: block rather than relay an empty request return { blocked: true, message }; } message.chatmessage = `📋 Feature request from ${message.chatname}: ${featureText}`; return { modified: true, message };

Шаг 3 — действие ретрансляции чата: добавьте обычный узел ретрансляции чата после действия и укажите Discord или другой получатель. Пользовательский код здесь не нужен — переформатированное message.chatmessage проходит автоматически.

Проверка потока. Нажмите кнопку: Проверить сценарий (в правом верхнем углу редактора), чтобы отправить синтетическое сообщение по цепочке без прямой трансляции. Задайте chatname подписчика и сообщение вроде !feature dark mode support, и убедитесь, что получатель ретрансляции чата получает переформатированную строку.
Панель проверки потока для отправки синтетических тестовых событий
Панель проверки потока. Заполните поля согласно условиям триггера и нажмите Запустить тест для проверки всей цепочки.

Вопросы безопасности

Пользовательский код выполняется с правами процесса рендеринга. В SSApp код узлов пользовательского JS имеет полный доступ к объекту window и любым API, предоставленным скриптом preload (например, window.ninjafy). Считайте импортируемые файлы потоков исполняемым кодом: импортируйте только из доверенных источников.
  • Нет сетевой песочницы. Код действия может вызвать fetch(). Если вы получаете потоки от других людей, проверяйте JS перед активацией.
  • Ошибки перехватываются. Ошибка выполнения в вашем коде возвращает false (триггер) или ничего не делает (действие) и записывает ошибку в консоль DevTools — поток не падает.
  • Ошибки синтаксиса тоже. Ошибка типа SyntaxError на этапе компиляции перехватывается так же. Если узел ничего не делает, проверьте DevTools (F12).