Система 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 выберите источник и один и тот же обычно выключенный фильтр в обоих действиях фильтра.

Выберите Воспроизвести аудиоклип → Обзор звуков для поиска оригинальных эффектов и заранее записанных синтетических фраз на английском, испанском и бразильском португальском языках. Фильтровать по категории, событию, языку, доставке или продолжительности; сохранять избранное и просматривать локально перед выбором Использовать звук. Звуковая библиотека также открывается самостоятельно и предоставляет звуковые URL-адреса для пользовательских наложений. Multi-Alerts использует тот же браузер.

В потоке событий используйте кнопки «плюс», чтобы создать случайный набор от двух до двадцати звуков, затем выберите Использовать случайный набор. Каждый триггер воспроизводит один выбранный звук. Выбор одного звука или ввод другого звука URL заменяет набор. Параметры воспроизведения элементы управления перекрытием, постановкой в очередь и временем восстановления действия; Остановить аудиоклипы останавливает клипы Flow Actions и очищает очередь. Разместите ограничитель скорости перед любым действием по расходу очков при ограничении платных команд. При воспроизведении аудио отдельные баллы не возвращаются автоматически. Вы по-прежнему можете загрузить запись или выбрать локальный файл приложения. Для изменения имен или сообщений используйте Озвучить текст.

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).