Event Flow 系统

Event Flow 编辑器指南

为 Social Stream Ninja 构建可靠的自动化。本指南介绍基础知识、逻辑节点、信号流,以及创作者最常询问的实用技巧,例如避免聊天回显以及何时组合 AND/NOT 模块。

简体中文

0. 快速入门

Event Flow 是基于节点的编辑器。每条连线携带消息载荷和布尔状态(true = 继续, false = 停止)。使用 来源 以注入事件, 逻辑节点 以筛选决策,以及 操作 以执行实际操作(发送聊天、控制叠加层、转发消息等)。
需要记住参与者、检查后续资格、进行不重复用户抽奖,或清除一个命名列表?打开 用户记忆指南 了解共享状态模型、截图和可导入示例。

这个编辑器是什么?

Event Flow 编辑器是 Social Stream Ninja 的“高级自动化”层。它位于简单弹出窗口开关之上,让您编写自己的路由逻辑。在需要以下功能时使用它:

  • 在服务之间筛选并转发聊天(例如将 Twitch 镜像到 Discord,但阻止命令)。
  • 通过 AND/OR/NOT 逻辑构建基于忠诚度的命令、关键词游戏或抽奖资格限制。
  • 根据在流程中补充的数据,触发自定义叠加层、音频、OBS 场景或 webhook。
  • 在单个自动化中混合多个平台(Kick + Twitch + YouTube 通过一个流程路由)。

可以将弹出窗口视为“快速预设”,将 Event Flow 视为定制工作流的工具集。

启动和基础知识

  • 从主仪表板菜单(桌面应用或扩展)打开 Event Flow 编辑器。
  • 每个项目在导出前都保存在本地。使用 Export 以备份或分享。
  • 在称为以下名称的画布中工作: 流程。每个流程都可以同时订阅多个平台。

节点速览

  • 输入 (左侧端口)接收消息上下文。
  • 输出 (右侧端口)发出相同上下文以及所有编辑。
  • 逻辑节点可能同时发出 true 频道和可选的 false 频道。

载荷结构

每条消息都携带 JSON 对象。必需键遵循 docs/event-reference.html (platform、type、chatname、chatmessage 等)。将自定义数据附加到 meta.

每个流程都从触发器开始

操作节点(绿色)从不自行运行,只有上方的触发器节点(蓝色)求值为以下结果时才触发: true。仅将操作串联起来的流程看似有效,但会一直闲置,因为没有任何东西启动该链。节点名称描述的是节点 做什么,而不是它何时发生: 精选消息(Feature Message) 在流程到达它时精选消息,而不会在您于其他位置精选消息时触发。

两个串联的操作节点,没有触发器节点
❌ 从不运行。Feature Message 和 Speak Text 都是 操作;如果上方没有触发器,就没有任何东西启动该链。
Any Message 触发器连接到 Feature Message 和 Speak Text 操作
✅ 可运行。一个 任意消息(Any Message) 触发器(或 Message Contains、正则表达式、打赏事件等)启动该链;之后,每条匹配消息都会运行这两个操作。

Flow Actions 叠加层(操作输出)

从提醒模板开始:

选择 打赏:庆祝 + 语音 以使用现成的动画和合成感谢语音片段,或使用高级的 打赏:动画 + 声音 + OBS 滤镜 模板。新提醒模板初始为禁用状态,让您可以先配置并测试。对于 OBS 模板,在两个滤镜操作中选择同一个来源和同一个通常关闭的滤镜。

播放音频片段 和 Multi-Alerts 现在共享一个包含 17 种声音的音效库:掌声、击鼓、呼啸、收银机及其他效果,四个带标签的合成英语短语,以及简单声音。 试听 / 停止(Listen / Stop) 在本地预览并显示播放状态。您仍可以上传录音或选择应用本地文件。对于变化的姓名或消息,使用现有的 朗读文本(Speak Text) 操作。

Event Flow 通过以下页面播放: Flow Actions 浏览器源;Multi-Alerts 通过自己的浏览器源播放。对于同一事件,只在其中一个来源启用声音,以免重复播放。使用 Tab 移动到流程节点,按 Enter 或空格编辑其属性。

例如以下节点: 播放音频片段, 显示媒体叠加层,以及 OBS 控件都需要一个渲染界面。该界面就是由以下地址提供的 Flow Actions 叠加层页面: actions.html。让它在直播软件(OBS/Streamer.bot 浏览器停靠面板等)中保持运行,让 Event Flow 操作有地方显示。

Any Message 触发器连接到 Play Audio Clip 操作
该流程完整,并在每条消息时触发,但声音会在 Flow Actions 叠加层页面上播放, 而不是在编辑器中。 编辑器的 Preview 按钮会在本地播放;实时播放需要打开叠加层。如果浏览器阻止自动播放,请点击 启用音频 在 Flow Actions 页面上,以重试最近被阻止的片段。点击该页面其他位置也会启用播放。OBS 浏览器源通常允许自动播放。
如何打开(从弹出窗口/仪表板):
  1. 打开 Social Stream Ninja 主弹出窗口(从以下位置加载的窗口: popup.html 或扩展图标)。
  2. 滚动到“Flow Actions”卡片。使用 [复制链接] 按钮,或点击卡片内的 URL。
  3. 链接类似于 https://socialstream.ninja/actions.html?session=YOURSESSION。将其粘贴到 OBS 浏览器源(建议 1920×1080),或在任意叠加层浏览器中打开。
在独立应用中使用本地媒体:
  1. 在 Play Audio Clip 或 Display Media Overlay 操作中,点击 选择本地文件.
  2. 点击 复制用于 OBS 的本地 Flow Actions URL 并使用生成的 localhost URL 代替托管的 Flow Actions URL。
  3. 保持 SSApp 运行。如果所选文件移动了,请回到操作并点击 重新关联.

Chrome 扩展本身无法提供磁盘文件。没有桌面辅助应用时,请使用 Upload 或托管 URL。请参阅 Event Flow 媒体文件指南 了解完整设置。

加载后,该叠加层可以:

  • 显示由流程触发的 GIPHY 或直接媒体 URL、文本和彩纸。
  • 在本地播放声音(TTS、音频片段),让观众听到。
  • 通过弹出窗口 Flow Actions 部分中的 WebSocket 设置与 OBS 通信(场景切换、来源开关、GDI+/FreeType 文本更新、回放缓冲区等)。
OBS 控制模式:
  • 浏览器源 API: 仅在以下情况下可用: actions.html 在 OBS 浏览器源内运行,带有 高级访问级别(Advanced Access Level)。此处支持场景切换,录制 / 直播 / 回放缓冲区操作也可以将它用作回退方式。
  • OBS WebSocket: 建议使用,以实现一致的控制。Social Stream Ninja Flow Actions 使用 OBS 28+ 的 OBS WebSocket v5 API,并要求端口上提供现代请求集: 4455.
  • 密码: 可选。仅在 OBS 服务器要求身份验证时,才将 &obspw=... 添加到 Flow Actions URL。
  • 叠加层诊断: 附加 &obsdebug=1 到以下页面的 URL: actions.html 如果希望在排查问题时,在叠加层上显示一个小型实时 OBS 连接徽章。
  • 设置文本源(Set Text Source): 直接更新 OBS Text (GDI+) 和 Text (FreeType 2) 输入,并支持 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. 载荷 — 事件或消息数据对象。
  2. 门控信号 — 一个 true/false 位,告知下一个节点是否运行。
如果节点输出 false: 下游节点停止执行,除非它们从其他分支收到输入(例如 false 端口,位于 Condition 节点上)。这样就能轻松构建回退逻辑,无需复制整个流程。

输入要求

  • 事件来源 (Twitch Message、Timers、Manual Trigger 等)忽略上游输入,它们生成自己的载荷,并始终发出 true 除非节点本身出错。
  • 转换和逻辑节点 读取载荷,并可重写字段、设置状态,或将门控信号翻转为 false.
  • 操作节点 仅在门控保持以下值时触发: true。如果您希望继续串联操作,它们仍可输出更新后的载荷。

输出模式

单个输出

大多数节点提供一个输出。进入的内容(载荷 + 门控)会原样输出,除非节点对其进行编辑。

True/False 输出

Condition、Compare、Regex 和 Logic 节点提供两个输出端口。 真(True) 通过绿色端口继续; false 会在灰色/红色端口提供。

透传与覆盖

一些节点(Set Variable、Math、Text Replace)会修改载荷,但仍转发 true/false 来自输入的状态。其他节点(NOT、AND、OR)自行重新计算布尔值。

2. 逻辑节点速查

这些模块解答最常见的“true/false 是什么意思?”问题。

非(NOT)

  • 输入:来自前一节点的 1 个布尔值(true/false)。
  • 输出:反转后的布尔值,以及未改动的载荷。
  • 默认行为: 如果 NOT 输入没有连接任何内容,它会求值为 false,因此输出为 true.
示例: 将 NOT 放在“Contains Keyword”后,即可在以下情况下触发提醒:观众 未使用该关键词。

与(AND)

  • 输入:两个或更多布尔信号(A、B 等)。可让额外端口留空。
  • 输出: true 仅当所有连接输入都等于 true.
  • 当多个条件必须同时满足时使用 AND(“是订阅者” 和 “聊天消息包含 !raffle”)。

OR

  • 发出 true 如果 任意 连接的输入为 true。
  • 适合多平台触发器:将 Twitch 和 YouTube 消息节点接入单个 OR,然后统一下游操作。
始终需要 AND 节点吗?
不需要。许多节点已经提供一体化筛选器(例如“Filter User Level” + “Contains Text”)。仅当内置选项无法涵盖您的组合,或需要供其他分支共用的可复用逻辑连接点时,才使用 AND。
NOT 和空输入: 悬空的 NOT 节点仍会输出 true。保持它连接到有意义的节点,或禁用该节点,以免意外放行流程。

3. 微型流程示例

A. 除命令消息外自动回复

Twitch Message ──▶ Regex Match "^!" ─┐ │ ├─false──▶ Auto Reply ("Thanks for chatting!") │ └─true──▶ Do nothing

此处 Regex 节点发出 true ,条件是消息为命令。我们将 false 端口接到回复,因此普通聊天者会收到确认,而命令直接通过。

B. 使用 AND 要求多项检查

YouTube Message ──▶ Contains "!queue" ─▶ AND ─▶ Relay to Discord Gifted Membership ─▶ User Role = Member ──▲

AND 节点确保只有使用正确关键词的会员才会转发到 Discord。两个分支都将布尔结果发送到 AND 节点;载荷来自 第一个分支 ,并继续向下游传递。

C. 使用 NOT 节点阻止重复提醒

Event Payload ─▶ State Check (isAlertMuted) └─false─▶ NOT ─▶ Play Celebration

State Check 在提醒被静音时输出 true 。通过反转该结果,NOT 节点确保仅在标志为以下值时播放庆祝效果: false.

D. 随机播放两种声音之一

流程使用 RANDOM 门、NOT 门和 AND 门,随机播放两个音频片段之一
以各 50% 概率在两个音频片段中选择。RANDOM 门对每条匹配消息随机判断一次:通过时播放声音 A;失败时,NOT 门反转结果,AND 门让声音 B 播放。
Trigger ──▶ RANDOM (50%) ──▶ Play Sound A │ └──▶ NOT ──▶ AND ──▶ Play Sound B Trigger ──────────────────▲

AND 门不可省略。 独立的 NOT 会输出 true 每当 RANDOM 门闲置时,因此声音 B 会在每条以下聊天消息时播放: 不匹配触发条件的消息。 将触发器连接到 AND 的第二个输入,可将声音 B 限制为仅在消息匹配时播放。同一模式适用于任何二选一的动作组合,不限于音频。

4. 防止回显、循环和转发反馈

跨界面转发聊天很有用,但如果监听自己的输出,就可能产生无限回显。请遵循以下防护措施:

YouTube Shorts 目标说明:
传入触发器和传出 Relay Chat 的目标都会区分 youtube 和 youtubeshorts。当消息应到达两个变体时,使用两个转发操作。请参阅 YouTube Shorts 与 Event Flow.
Relay Chat 自动跳过已识别的回流。
回流是指发送到目标聊天后又被捕获的消息。当前 Relay Chat 操作会跳过这些已识别的回流;没有单独的 No Reflections 复选框。如需隐藏或限制它们在停靠面板和叠加层中的显示,请使用 回流过滤器(Reflection Filter) 操作,带有 全部阻止, 允许第一条,或 全部允许。这控制重新接收时的显示,而不是发送。请遵循 Twitch 和 YouTube 转发教程 了解完整设置。
  • 避免重复转发系统。 使用等效的 Event Flow 路由时,请禁用全局 Relay all,并检查是否有其他服务桥接相同聊天。自定义元数据不能保证在经过平台聊天后仍保留。
  • 使用 Debounce 或 Cooldown 节点 用于每 X 秒只应触发一次的提醒。
  • 有意识地打断循环。 如果两个分支相互输入,请添加逻辑节点检查状态变量(“currentlyRelaying”),以便在标志已设置时提前退出流程。

5. 输入、输出和实用问题

节点接收什么?

  • 完整消息载荷。
  • 门控位(true/false).
  • 节点明确请求的可选上下文(状态变量、计时器)。

什么内容离开节点?

  • 相同载荷,除非节点对其进行编辑。
  • 重新计算的门控位(逻辑节点)或透传位(操作)。
  • 大多数副作用(如发送聊天)不会改变载荷,但积分操作可以附加状态字段,例如 pointsTotal 或 pointsSpendError 用于下游逻辑。

何时分支?

每当希望针对以下内容作出不同反应时: true 与 false。从所需的彩色输出(绿色 = true,灰色/红色 = false)拖动连线到下一个节点。

请记住: 如果不处理 false 输出,流程就会在此结束。这非常适合筛选器(“阻止所有未通过检查的内容”),但别忘了连接 false 路径,如果需要回退处理。

常见问答

  • 每一对筛选器都必须使用 AND 吗? 不需要。许多节点包含多项检查(例如,基本 Message Filter 支持关键词 + 角色)。仅对高级组合,或合并来自不同节点的信号时使用 AND。
  • true/false 值如何到达 NOT 节点? 任何具有绿色输出的节点都会发出 true 作为默认值。条件失败时,它发出 false。将该连线接入 NOT 以反转结果。
  • 节点返回 false 时,仍能发出载荷吗? 可以。载荷仍会通过 false 输出传递;由您决定该分支应通向何处。
  • 如何匹配 TikTok Team 成员? 选择 TikTok Team 成员(TikTok Team Member) 在 User Role 节点中。它识别传入消息中的 TikTok Fan Club/team 等级和徽章,不依赖 Main Chat Overlay 设置。
  • 每个 Speak Text 节点可以使用不同的语音吗? 可以。在以下字段中输入提供商支持的语音名称或 ID: 语音覆盖(Voice Override),或留空以使用 Flow Actions TTS 默认值。

6. 模板变量参考

多个操作节点(Show Text、Set Text Source、Send Message、Relay Chat、TTS Speak、Call Webhook、Print Thermal Label)支持 模板变量 在运行时会被事件数据替换。用花括号包裹变量名,例如 {username}.

核心变量(向后兼容)

变量别名说明示例
{username}{chatname}用户的显示名称CoolViewer123
{message}{chatmessage}聊天消息文本大家好!
{source}-平台名称(首字母大写)Twitch, YouTube
{type}-平台名称(原始值)twitch, youtube
{donation}{hasDonation}打赏显示标签$5.00, 500 bits

扩展变量

变量说明示例
{displayname}显示名称(备用字段)CoolViewer123
{donoValue}提供或估算的等值美元打赏金额;Event Flow 从标准化的以下字段推导阈值: hasDonation 标签,例如数值, $数值、数值 + 单位,或紧凑的单位/数值格式。未知命名虚拟单位按 100 单位 = 0.01 美元换算;未定价的 TikTok 礼物按每份礼物一个金币(每个 0.01 美元)计算。 {donationAmount} 是旧版别名5.00
{event}事件类型标识符cheer, raid, new_follower
{membership}会员状态MEMBERSHIP, new_sponsor
{subtitle}补充上下文已加入会员 3 个月
{userid}用户的平台 ID12345678
{chatimg}用户头像 URLhttps://...
{contentimg}附加的图像 URLhttps://...
{rewardTitle}来源提供顶层奖励标题字段时的奖励名称高亮我的消息(Highlight My Message)
{meta}结构化事件数据(JSON){"viewers":100}
{counterValue}经过 Counter 或 Check Counter 步骤后的当前计数器值12
{counterTarget}计数器目标值30
{counterRemaining}计数器目标减去当前值,最小为 018
变量匹配不区分大小写。 {USERNAME}, {Username},以及 {username} 都以相同方式工作。
流程添加的字段也适用。 如果较早的操作向消息添加了顶层值,后续模板可以直接读取它。这就是 Check Counter 提供 {counterValue}, {counterTarget},以及 {counterRemaining}.
调用 Webhook JSON: 模板变量适用于任意嵌套对象或数组深度的 JSON 字符串值。对象键不进行模板替换,不含占位符的自定义正文会原样发送。

示例模板

  • 显示文本(Show Text): {username} just cheered {hasDonation}!
  • 设置 OBS 文本源(Set OBS Text Source): {username}: now {counterValue}, need {counterTarget}
  • 转发聊天(Relay Chat): [{source}] {username}: {message}
  • TTS: {username} says {message}
  • 打赏提醒: {username} donated {donation} - {subtitle}
  • 热敏标签: {username},换行,然后 {donation}。请参阅 热敏打印机指南 了解打印机设置、固定尺寸标签和完整流程。
  • Discord 调用 Webhook: {"content":"{message}","username":"{username}","avatar_url":"{chatimg}"}
缺失的变量变为空字符串。 如果事件没有某个字段(例如 {donation} 在普通聊天消息中),占位符会替换为空字符串,而不是显示字面的 {donation} 文本。

7. 最佳实践检查表

  • 命名并设置颜色: 为节点设置名称和颜色,以便以后分辨各个分支。
  • 使用内置模拟器测试 (Send Test Event),然后再将流程投入直播。
  • 在来源附近集中逻辑。 尽早筛选,避免后续额外处理。
  • 将重复记录存入状态节点。 使用计数器、开关和时间戳避免重复提醒。
  • 记录 meta 字段。 当您添加自定义的 meta 键时,请记录它们,让叠加层和远程客户端保持一致。
保存版本。 每当达到一个里程碑时就导出流程。如果实验出错,导入是最简单的回退方式。

8. 进一步探索

从 Stream Deck 或 API 运行自定义工作流:命名触发器、入门模板、工作流发现、额外数据、HTTP/WebSocket/P2P 示例和旋钮手势。

准备深入了解?

  • 使用 状态节点 (计数器、开关、计时器)来跟踪事件之间的上下文。
  • 组合 变量和逻辑 以构建队列系统、抽奖或评分引擎。
  • 接入 积分和奖励 系统,让观众可以主动触发流程。
  • 运行 SSApp 桌面应用? 解锁 自定义 JavaScript 节点 用于内置节点未涵盖的任意逻辑。
  • 检查 事件参考 了解所有平台的详细载荷文档。

本指南有意设计为独立文档,您可以复制到本地、为团队调整,并继续在编辑器中尝试。

9. 自定义 JavaScript 仅限 SSApp / 桌面应用

Event Flow 编辑器中的两个节点让您编写在流程处理管线内运行的任意 JavaScript: 自定义代码(Custom Code) (触发器)和 执行自定义代码(Execute Custom Code) (操作)。当内置节点无法表达某种需求时,它们提供自定义途径。

需要桌面应用。 浏览器扩展中禁用了自定义 JavaScript 节点,因为 Chrome Manifest V3 内容安全策略阻止 new Function() / eval()。通过以下入口打开编辑器: SSApp 桌面应用 以启用它们。在扩展模式中,节点显示为灰色,并带有标签 “仅限桌面应用”.
编辑代码: 选择 Custom Code 节点并点击 打开代码编辑器(Open Code Editor) 打开大型编辑窗口。 保存并关闭(Save & Close) 检查 JavaScript 语法并保存整个流程; Ctrl+S 或 Cmd+S 执行相同操作。Cancel 不会更改节点。
Event Flow 编辑器 — 空状态
Event Flow 编辑器。左侧面板列出所有可用节点;点阵画布用于构建流程;右侧面板显示所选节点的属性。

Custom Code — 触发器节点

拖动 自定义代码(Custom Code) 来自 高级 分组,位于 触发器 面板拖到画布上。它充当门控:仅当代码返回以下值时流程才继续: true.

触发器面板,显示 Advanced 分组下的 Custom Code 节点
Custom Code 位于 高级 分组,位于 Triggers 面板。
Custom Code 触发器属性面板,显示 JavaScript 代码编辑器
放置触发器后的属性面板。编写任意返回以下值的表达式: true 或 false.
签名: 您的代码以以下形式运行: function(message) { ... }
必须返回: 布尔值 — true 让流程继续, false 以停止它。
可用: 对象 message (参见 消息 API 如下),加上 convertCurrency(value, targetCurrency, source) 和 convertToUSD(value, source).

Execute Custom Code — 操作节点

拖动 执行自定义代码(Execute Custom Code) 来自 集成 分组,位于 操作 面板。它可以修改消息、阻止消息或附加供下游节点读取的元数据。

操作面板,显示 Integrations 分组下的 Execute Custom Code
Execute Custom Code 位于 集成 分组,位于 Actions 面板。
Execute Custom Code 操作属性面板,显示代码编辑器
操作属性。返回一个对象,将更改合并回流程结果。
签名: 您的代码以以下形式运行: function(message, result) { ... }
应返回: 对象或 Promise,合并到 result— 请参阅 结果 API.
可用: message (事件载荷), result (当前流程结果状态), printThermal(html, options),加上 convertCurrency(value, targetCurrency, source) 和 convertToUSD(value, source).
SSApp 中的热敏打印: 在以下位置选择打印机并校准纸张宽度和安全边距: 打印机控制(Printer Control),然后返回 printThermal('<strong>' + message.chatname + '</strong>')。SSApp 通过原生 Windows 打印机 API 静默排队作业,并使用这些已保存设置。流程可以通过以下选项覆盖它们: { width: '58mm', marginLeft: '3mm', marginRight: '3mm', marginTop: '2mm', marginBottom: '2mm', feed: '3mm', marginType: 'printableArea' }。返回 Promise 使 Event Flow 能够等待提交并报告错误。
画布并排显示 Custom Code 触发器和 Execute Custom Code 操作
画布上放置了 Custom Code 触发器(蓝色)和 Execute Custom Code 操作(绿色)。将触发器的输出端口连接到操作的输入端口即可连接它们。

对象 message

两个节点都以以下形式接收完整事件载荷: 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数值 / 字符串来源提供时的等值美元打赏金额;有效的零值会被采用。Event Flow 回退到 currency.js 转换标准化的 hasDonation 标签用于阈值比较,包括 100 个未知命名单位 = 0.01 美元。它不会解析 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布尔值发送者具有 VIP 身份true
message.chatimg字符串用户头像 URL"https://..."
message.meta对象附加到事件的任意结构化数据{ viewers: 120 }
货币换算: 使用 convertCurrency(message.hasDonation, 'EUR', message.type) 将格式化的打赏标签转换为 EUR。它返回数字,或 null 当不支持请求的目标货币时。转换器使用 Social Stream Ninja 的内部近似汇率,不会联系外部汇率服务。

操作返回的内容

从操作代码返回普通对象。您包含的任何字段都会合并到流程的 result 对象;省略的字段保持当前值。

返回字段数据类型效果
modified布尔值设置 true 如果您更改了 message 字段。告知下游节点载荷已编辑。
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;
EUR 区间内的 YouTube Super Chat 或 Super Sticker
将标准 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) 或 发送消息(Send Message) 操作可从模板变量读取({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)。

┌──────────────────────┐ ┌──────────────────────────┐ ┌──────────────────┐ │ Custom Code Trigger │────▶│ Execute Custom Code Action│────▶│ Relay Chat │ │ │ │ │ │ (to Discord) │ │ Gate: VIP/sub/mod │ │ Reformat message text │ │ │ │ + starts with │ │ → "📋 Feature Request │ │ │ │ !feature │ │ from {name}: {text}" │ │ │ └──────────────────────┘ └──────────────────────────┘ └──────────────────┘

步骤 1 — Custom Code 触发器 (粘贴到触发器的 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 — Execute Custom Code 操作 (粘贴到操作的 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 — Relay Chat 操作:在操作后添加标准 Relay Chat 节点,并配置为指向 Discord(或其他)目标。此处无需自定义代码,重新格式化的 message.chatmessage 会自动流经。

测试流程。 点击此按钮: 测试流程(Test Flow) (位于编辑器右上角),无需直播即可向处理流程发送模拟消息。将 chatname 设为订阅者的名称,添加类似以下内容的消息: !feature dark mode support,并确认 Relay Chat 目标收到重新格式化的字符串。
用于发送模拟测试事件的 Test Flow 面板
Test Flow 面板。填写与触发条件匹配的字段,然后点击 运行测试(Run Test) 以验证完整处理管线。

安全注意事项

自定义代码以渲染进程权限运行。 在 SSApp 内,Custom JS 节点中的代码可以完全访问 window 对象,以及 preload 脚本提供的任何 API(例如 window.ninjafy)。请将导入的流程文件视为可执行代码,只导入可信来源的流程。
  • 没有网络沙盒。 操作代码可以调用 fetch()。如果接受他人分享的流程,请在启用前审核 JS。
  • 错误会被捕获。 代码中的运行时错误会返回 false (触发器)或不执行操作(操作),并记录到 DevTools 控制台,流程不会崩溃。
  • 语法错误也一样。 类型为 SyntaxError 的编译错误也会以相同方式捕获。如果节点似乎没有反应,请检查 DevTools(F12)。