Event Flowシステム

Event Flowエディターガイド

Social Stream Ninjaで安定した自動処理を作りましょう。このガイドでは、基本、ロジックノード、信号の流れ、チャットの反射回避やAND/NOTの組み合わせなど、クリエイターからよく寄せられる実用的なポイントを説明します。

日本語

0. 全体像をつかむ

Event Flow はノードで組み立てるエディターです。各接続線は、メッセージのペイロードとブール値の状態を運びます(true = 続行、 false = 停止)。次を使用してください: ソース でイベントを入力し、 ロジックノード で条件を判定し、 アクション で処理を実行します(チャット送信、オーバーレイ操作、メッセージ中継など)。
参加者を記録したり、後から参加資格を確認したり、ユーザーの重複を避けて抽選したり、特定のリストだけを消去したりするには、次を開きます: User Memoryガイド で、共有状態の仕組み、スクリーンショット、インポートできる例を確認できます。

このエディターとは?

Event Flowエディターは、Social Stream Ninjaで高度な自動処理を作るための機能です。ポップアップの簡単なスイッチに加えて、独自の振り分けロジックを記述できます。次のような場合に使用します:

  • フィルターを使ってサービス間でチャットを中継します(例:コマンドを除外してTwitchからDiscordへ転送)。
  • AND/OR/NOTロジックで、ロイヤルティに基づくコマンド、キーワードゲーム、抽選参加条件を作れます。
  • フロー内で追加した情報に基づき、カスタムオーバーレイ、音声、OBSシーン、Webhookを起動します。
  • 1つの自動処理で複数のプラットフォームを組み合わせられます(Kick、Twitch、YouTubeを1つのフローに通すなど)。

ポップアップは手軽なプリセット、Event Flowは独自のワークフローを作る道具として使えます。

起動と基本操作

  • メインダッシュボードのメニューからEvent Flowエディターを開きます(デスクトップアプリまたは拡張機能)。
  • 各プロジェクトはエクスポートするまでローカルに保存されます。次を使用してください: Export でバックアップまたは共有します。
  • 次の名前のキャンバス内で作業します: フロー。各フローは同時に複数のプラットフォームを購読できます。

ノード一覧

  • 入力 (左のポート)はメッセージの文脈を受け取ります。
  • 出力 (右のポート)は、編集を反映した同じ文脈を出力します。
  • ロジックノードは、次の両方を出力する場合があります: true チャンネルと、必要に応じて出力する false チャンネル。

ペイロードの構造

すべてのメッセージはJSONオブジェクトを持ちます。必須キーは次に従います: docs/event-reference.html (platform、type、chatname、chatmessageなど)。独自データは次に付けてください: meta.

すべてのフローはトリガーから始まります

アクションノード(緑)は単独では動きません。上流のトリガーノード(青)が次の値を出したときだけ実行されます: true。アクションだけをつないだフローは、有効に見えても、開始するものがないため常に待機したままです。ノード名は、そのノードが 何をするかを表し、いつ発生するかを表すものではありません: メッセージを取り上げる は、フローがそのノードに達したときにメッセージを取り上げます。ほかの場所でメッセージを取り上げても発火しません。

トリガーノードがなく、2つのアクションノードだけが接続されている状態
❌ 実行されません。Feature MessageとSpeak Textはどちらも アクション。先頭にトリガーがなければ、何も開始しません。
Feature MessageとSpeak Textアクションへ接続したAny Messageトリガー
✅ 動作します。 任意のメッセージ(Any Message) トリガー(またはMessage Contains、正規表現、寄付イベントなど)が処理を開始し、一致するメッセージごとに両方のアクションが実行されます。

Flow Actionsオーバーレイ(アクション出力)

アラートのテンプレートから始めます:

選ぶ項目: 寄付:お祝い演出+音声 を使うと、用意済みのアニメーションと合成音声のお礼クリップを利用できます。または高度な次のテンプレートを使用します: 寄付:アニメーション+サウンド+OBSフィルター テンプレート。新しいアラートテンプレートは、先に設定とテストができるよう無効な状態で追加されます。OBSテンプレートでは、両方のフィルターアクションで同じソースと、通常はオフの同じフィルターを選択します。

音声クリップを再生(Play Audio Clip) とMulti-Alertsは、拍手、ドラムロール、風切り音、レジなどの効果音、ラベル付きの英語合成音声フレーズ4種類、シンプルな音を含む17種類のサウンドライブラリを共有しています。 試聴/停止(Listen / Stop) では再生状態を確認しながらローカルでプレビューできます。録音のアップロードやアプリ内のローカルファイルの選択もできます。名前やメッセージが変化する場合は、既存の次の機能を使用します: テキストを読み上げる アクション。

Event Flowは次を通じて再生します: Flow Actions ブラウザーソースを通じて再生します。Multi-Alertsは専用のブラウザーソースを通じて再生します。同じイベントが二重に再生されないよう、音声はどちらか一方だけで有効にしてください。Tabキーでフローノードに移動し、EnterまたはSpaceキーを押すとプロパティを編集できます。

次のようなノード: 音声クリップを再生(Play Audio Clip), メディアオーバーレイを表示(Display Media Overlay)やOBS操作には、描画用の画面が必要です。その画面は、次で提供するFlow Actionsオーバーレイページです: actions.html。配信ソフト(OBS/Streamer.botのブラウザドックなど)で動かしたままにし、Event Flowのアクションの表示先を用意してください。

Play Audio Clipアクションへ接続したAny Messageトリガー
このフローは完成しており、すべてのメッセージで発火します。ただし、音声が再生されるのは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」アクションで次をクリックします: ローカルファイルを選択(Choose Local File).
  2. クリックする項目: OBS用のローカルFlow Actions URLをコピー(Copy Local Flow Actions URL for OBS) 生成されたlocalhostのURLを、ホストされたFlow ActionsのURLの代わりに使用します。
  3. SSAppを起動したままにしてください。選択したファイルを移動した場合は、アクションに戻って次をクリックします: 再リンク(Relink).

Chrome拡張機能だけではディスク上のファイルを配信できません。デスクトップアプリを併用できない場合は、アップロードまたはホストされたURLを使用します。次を参照してください: Event Flow用メディアファイルガイド で設定の全手順を確認できます。

読み込まれたオーバーレイは、次のことができます:

  • フローをきっかけにGIPHYや直接指定したメディアURL、テキスト、紙吹雪を表示します。
  • 視聴者に聞こえるよう、音声(読み上げ、音声クリップ)をローカルで再生します。
  • ポップアップの「Flow Actions」セクションにあるWebSocket設定を使ってOBSを操作します(シーン切り替え、ソースの有効/無効、GDI+/FreeTypeのテキスト更新、リプレイバッファーなど)。
OBS操作モード:
  • ブラウザソースAPI: 次の場合にのみ使用できます: actions.html がOBSのブラウザーソース内で実行され、次が設定されている場合: 高度なアクセスレベル。ここではシーン切り替えが動作し、録画 / 配信 / リプレイバッファの操作も代替経路として使えます。
  • OBS WebSocket: を、安定した操作のために推奨します。Social Stream NinjaのFlow ActionsはOBS 28以降のOBS WebSocket v5 APIを使用し、次のポートで新しいリクエスト形式を想定しています: 4455.
  • パスワード: は任意です。次を追加するのは必要な場合だけです: &obspw=... をFlow ActionsのURLに追加するのは、OBSサーバーで認証を必須に設定している場合だけです。
  • オーバーレイの診断: 末尾に追加 &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の実行処理は、すべての接続線を通じて2つのものを渡します:

  1. ペイロード – イベントまたはメッセージのデータオブジェクト。
  2. ゲート信号 – 次の値: true/false という、次のノードを実行するかどうかを示すビット。
ノードがfalseを出力した場合: 後続のノードは、別の分岐から入力を受け取らない限り実行を停止します(例: false というConditionノードの端子)。これにより、フロー全体を複製せずに代替処理を簡単に作れます。

入力の要件

  • イベントソース (Twitch Message、Timers、Manual Triggerなど)は上流の入力を無視し、独自のペイロードを作って常に次を出力します: true ノード自体でエラーが発生した場合を除きます。
  • 変換ノードとロジックノード はペイロードを読み取り、フィールドの書き換え、状態の設定、ゲート信号の変更を行えます。変更先: false.
  • アクションノード はゲートが次の状態のときにのみ発火します: true。アクションをつなぎ続けたい場合は、更新済みのペイロードを出力することもできます。

出力パターン

単一の出力

ほとんどのノードには出力が1つあります。入力されたもの(ペイロード+ゲート)は、ノードで編集しない限りそのまま出力されます。

True/False出力

Condition、Compare、Regex、Logicノードには2つの出力ポートがあります。 True は緑色のポートを通って進みます。 false は灰色/赤色のポートで利用できます。

そのまま渡す場合と上書きする場合

一部のノード(Set Variable、Math、Text Replace)はペイロードを変更しますが、次はそのまま転送します: true/false 状態を入力からそのまま渡します。ほかのノード(NOT、AND、OR)はブール値を自身で再計算します。

2. ロジックノード早見表

この説明では、「true/falseは何を意味するのか」というよくある疑問に答えます。

NOT

  • 入力:前のノードから得た1つのブール値(true/false)。
  • 出力:反転したブール値と、変更していないペイロード。
  • 標準の動作: NOTの入力に何も接続されていない場合、評価結果は次になります: falseとなるため、出力は true.
例: 「Contains Keyword」の後にNOTを置くと、次の場合にアラートを発火できます:視聴者が キーワードを使っていない場合。

AND

  • 入力:2つ以上のブール信号(A、B、...)。余分なポートは空のままで構いません。
  • 出力: true となるのは、接続されたすべての入力が次に等しい場合だけです: true.
  • 複数の条件を同時に満たす必要がある場合にANDを使用します(「サブスクライバーである」 および 「チャットメッセージに!raffleを含む」など)。

OR

  • 送信する値: true 条件: いずれかの 接続された入力がtrueである場合です。
  • 複数プラットフォームのトリガーに便利です。TwitchとYouTubeのメッセージノードを1つのORに接続し、その先のアクションをまとめます。
常にANDノードが必要ですか?
いいえ。多くのノードには複数条件をまとめたフィルターがあります(例:「Filter User Level」+「Contains Text」)。組み込みの設定で条件の組み合わせを表せない場合や、ほかの分岐と共有するロジックの接続点を作りたい場合にのみANDを使用してください。
NOTと空の入力: 接続されていないNOTノードでも、次を出力します: true。意味のある入力へ接続するかノードを無効にし、意図せずフローを通さないようにしてください。

3. 小さなフローの例

A. コマンド以外のメッセージに自動返信

Twitchメッセージ ──▶ 正規表現一致「^!」 ─┐ │ ├─false──▶ 自動返信(「チャットしてくれてありがとう!」) │ └─true──▶ 何もしない

この例では、メッセージがコマンドの場合にRegexノードが true を出力します。 false 端子を返信につなぎます。通常のチャットには返答し、コマンドはそのまま通過させます。

B. ANDで複数の確認を必須にする

YouTubeメッセージ ──▶ 「!queue」を含む ─▶ AND ─▶ Discordに中継 ギフトメンバーシップ ─▶ ユーザーの役割=メンバー ──▲

ANDノードは、正しいキーワードを使ったメンバーだけをDiscordに中継するために使います。両方の分岐がブール値の結果をANDノードに送ります。後続へ進むのは、 最初の分岐 のペイロードです。

C. NOTノードで重複アラートを防ぐ

イベントペイロード ─▶ 状態チェック(isAlertMuted) └─false─▶ NOT ─▶ お祝い演出を再生

State Check は、アラートがミュートされている場合に true を出力します。その結果を反転させることで、NOTノードはフラグが次の場合にのみお祝い演出を再生します: false.

D. 2つの音からランダムに再生

RANDOMゲート、NOTゲート、ANDゲートを使い、2つの音声クリップのいずれかをランダムに再生するフロー
2つの音声クリップを半々の確率で選びます。RANDOMゲートは条件に合うメッセージごとに1回抽選し、通ると音Aを再生します。通らなければNOTゲートが結果を反転し、ANDゲートが音Bを通します。
トリガー ──▶ RANDOM (50%) ──▶ サウンドAを再生 │ └──▶ NOT ──▶ AND ──▶ サウンドBを再生 トリガー ──────────────────▲

ANDゲートは必須です。 入力のないNOTは、次を出力します: true をRANDOMゲートが動作していないときにも返すため、サウンドBは次のようなすべてのチャットメッセージで再生されてしまいます: トリガーに一致しないメッセージ。 トリガーをANDの2つ目の入力に接続すると、条件に一致するメッセージでのみサウンドBが再生されます。このパターンは音声だけでなく、どちらか一方を実行するアクションの組み合わせにも使えます。

4. 反射、ループ、中継のフィードバックを防ぐ

複数の画面間でチャットを中継する機能は便利ですが、自分の出力を受信すると無限に反復する場合があります。次の対策を行ってください:

YouTube Shortsの送信先に関する注意:
受信トリガーと送信するRelay Chatの宛先は、どちらも次の2つを区別します: youtube および youtubeshorts。両方の種類へ送る場合は、中継アクションを2つ使ってください。参照: YouTube ShortsとEvent Flow.
Relay Chatは、折り返されたと認識したメッセージを自動的にスキップします。
反射とは、送信したメッセージが送信先チャットから再び取得されることです。現在のRelay Chatは、認識した反射をスキップします。別個のNo Reflectionsチェックボックスはありません。ドックやオーバーレイでの表示を隠したり制限したりするには、次を使います: 反射フィルター(Reflection Filter) 次を使うアクション: すべてブロック(Block All), 最初の1件を許可(Allow First)、または すべて許可(Allow All)。これは再取得時の表示を制御するもので、送信を制御するものではありません。次を参照してください: TwitchとYouTubeの中継手順 で設定の全手順を確認できます。
  • 中継システムの重複を避けます。 同等のEvent Flow経路を使う場合は全体のRelay allを無効にし、同じチャットをつなぐ他のサービスも確認してください。独自メタデータがプラットフォームのチャットを経由しても残る保証はありません。
  • DebounceまたはCooldownノードを使用する と、X秒に1回だけ発火するアラートを作れます。
  • 循環は意図的に断ち切ります。 2つの分岐が互いに送り合う場合は、状態変数(「currentlyRelaying」)を確認するロジックノードを追加し、そのフラグが立っているときにフローを早期終了させます。

5. 入力、出力、実用上の質問

ノードには何が入力されますか?

  • メッセージの完全なペイロード。
  • ゲートのビット(true/false).
  • ノードが明示的に要求する追加のコンテキスト(状態変数、タイマー)。

ノードから出るものは?

  • ノードで編集しない限り、同じペイロード。
  • 再計算したゲート判定値(ロジックノード)またはそのまま渡す判定値(アクション)。
  • チャット送信など、ほとんどの副作用はペイロードを変更しませんが、ポイントアクションは次のような状態フィールドを追加できます: pointsTotal または pointsSpendError を後続のロジックで使えます。

分岐はいつ使いますか?

次に応じて異なる反応をさせたい場合です: true と false。必要な色の出力(緑 = true、灰色/赤 = false)から次のノードへ線をドラッグします。

覚えておくこと: 次に対して何も操作しない場合: false 出力に何も接続しなければ、フローはそこで終了します。これはフィルター(「判定を通らないものはすべてブロック」)には適していますが、必要に応じて次の経路も接続してください: false の経路を、代替処理が必要な場合に接続します。

よくある質問と回答

  • 2つのフィルターを組み合わせるたびにANDが必要ですか? いいえ。多くのノードに複数の判定が含まれています(例えば基本のMessage Filterはキーワード+役割に対応しています)。ANDは高度な条件の組み合わせや、異なるノードからの信号をまとめる場合にのみ使用してください。
  • true/falseの値はどのようにNOTノードに届きますか? 緑の出力を持つノードは、次を出力します: true が既定です。条件を満たさない場合は、次を出力します: false。その線をNOTにつなぐと結果を反転できます。
  • falseを返してもペイロードを出力できますか? はい。ペイロードはfalse出力にも流れます。その分岐をどこへ接続するかは自分で決められます。
  • TikTokのチームメンバーを判定するには? 選ぶ項目: TikTokチームメンバー をUser Roleノードで選択します。受信メッセージのTikTokファンクラブ/チームのレベルやバッジを認識し、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ギフトは1ギフトにつき1コイン(0.01米ドル)として扱います。 {donationAmount} は旧形式の別名です5.00
{event}イベントの種類を示す識別子cheer, raid, new_follower
{membership}メンバーシップ状態MEMBERSHIP, new_sponsor
{subtitle}追加の文脈メンバー歴3か月
{userid}プラットフォーム上のユーザーID12345678
{chatimg}ユーザーのアバターURLhttps://...
{contentimg}添付画像URLhttps://...
{rewardTitle}ソースがトップレベルの報酬タイトルフィールドを提供する場合の報酬名自分のメッセージを強調表示
{meta}構造化されたイベントデータ(JSON){"viewers":100}
{counterValue}CounterまたはCheck Counter処理後の現在のカウンター値12
{counterTarget}カウンターの目標値30
{counterRemaining}カウンターの目標から現在値を引いた値。下限は018
変数名の照合では大文字と小文字を区別しません。 {USERNAME}, {Username}、および {username} はいずれも同じように使えます。
フローが追加したフィールドも使用できます。 前のアクションがメッセージのトップレベルに値を追加した場合、後続のテンプレートでその値を直接参照できます。これは次の仕組みです: Check Counter が公開する {counterValue}, {counterTarget}、および {counterRemaining}.
Call WebhookのJSON: テンプレート変数は、オブジェクトや配列のネストの深さに関係なく、JSONの文字列値で使用できます。オブジェクトのキーにはテンプレートを適用しません。プレースホルダーを含まないカスタム本文はそのまま送信します。

テンプレート例

  • テキストを表示(Show Text): {username} just cheered {hasDonation}!
  • OBSテキストソースを設定: {username}: now {counterValue}, need {counterTarget}
  • Relay Chat: [{source}] {username}: {message}
  • TTS: {username} says {message}
  • 寄付アラート: {username} donated {donation} - {subtitle}
  • 感熱ラベル: {username}、改行、次の値を入力します: {donation}。参照先: 感熱プリンターガイド で、プリンター設定、固定サイズのラベル、完成したフローを確認できます。
  • DiscordのCall 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エディターでは、2種類のノードで任意のJavaScriptを記述し、フローの処理中に実行できます: カスタムコード(Custom Code) (トリガー)と カスタムコードを実行(Execute Custom Code) (アクション)。組み込みノードで表現できない処理を実現するための手段です。

デスクトップアプリが必要です。 ChromeのManifest V3コンテンツセキュリティポリシーが次を禁止するため、ブラウザ拡張機能ではカスタムJavaScriptノードが無効です: new Function() / eval()。次からエディタを開きます: SSAppデスクトップアプリ で有効にします。拡張機能モードではノードがグレー表示になり、次のラベルが付きます: 「デスクトップ専用」.
コードの編集: Custom Codeノードを選択し、次をクリックします: コードエディターを開く をクリックすると、大きな編集ウィンドウが開きます。 保存して閉じる(Save & Close) はJavaScriptの構文を確認し、フロー全体を保存します。 Ctrl+S または Cmd+S も同じ処理を行います。キャンセルした場合、ノードは変更されません。
Event Flowエディター — 初期状態
Event Flowエディター。左のパネルには使用できるすべてのノードが並び、点線のキャンバスでフローを作成し、右のパネルには選択したノードのプロパティが表示されます。

Custom Code — トリガーノード

ドラッグする項目: カスタムコード(Custom Code) の送信元: 高度な機能 グループ。場所: トリガー パネルからキャンバスに配置します。ゲートとして働き、コードが次を返した場合にのみフローを続行します: true.

「Advanced」グループ内のCustom Codeノードを示すトリガーパネル
Custom Codeは次にあります: 高度な機能 グループ(Triggersパネル内)。
JavaScriptコードエディタを表示したCustom Codeトリガーのプロパティパネル
トリガー配置後のプロパティパネル。次を返す式を記述します: true または false.
シグネチャ: コードは次の形式で実行されます: function(message) { ... }
必須の戻り値: ブール値 — true でフローを続行し、 false で停止します。
利用可能: オブジェクト: message (参照: メッセージAPI を参照)、さらに convertCurrency(value, targetCurrency, source) および convertToUSD(value, source).

Execute Custom Code — アクションノード

ドラッグする項目: カスタムコードを実行(Execute Custom Code) の送信元: 連携(Integrations) グループ。場所: アクション パネル内。メッセージの変更、ブロック、後続ノードが読み取れるメタデータの追加ができます。

Integrationsグループ内のExecute Custom Codeを表示したアクションパネル
次のカテゴリにある「Execute Custom Code」: 連携(Integrations) グループ(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) で、書式設定された寄付表示をユーロに換算します。戻り値は数値、または次の値です: 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;
ユーロ建ての指定金額範囲にあるYouTube Super ChatまたはSuper Sticker
標準のYouTube寄付ラベルをEURに換算し、Jewels/Giftsを除外して、1つの音声または視覚効果の範囲を選びます。
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+キーワード
組み込みトリガーだけでは表せない役割の確認と本文条件を、1つの式にまとめます。
const isPrivileged = !!(message.subscriber || message.vip || message.mod); const isCommand = /^!feature\b/i.test(message.chatmessage || ''); return isPrivileged && isCommand;
メッセージの長さによるゲート
十分な内容を持つメッセージだけを処理します(絵文字1つだけの連投を避けたい読み上げや中継に便利です)。
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トリガー │────▶│ Execute Custom Codeアクション│────▶│ Relay Chat │ │ │ │ │ │ (Discordへ) │ │ ゲート:VIP/購読者/mod │ │ メッセージを整形 │ │ │ │ +次で始まる │ │ → "📋 機能追加リクエスト │ │ │ │ !feature │ │ {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()。他の人が共有したフローを使う場合は、有効にする前にJavaScriptを確認してください。
  • エラーは捕捉されます。 コードの実行時エラーでは、次を返します: false (トリガー)または何もしない動作(アクション)となり、DevToolsコンソールへ記録されます。フローはクラッシュしません。
  • 構文エラーも捕捉されます。 コンパイル時の SyntaxError も同様に捕捉されます。ノードが何もしていないように見える場合は、DevTools(F12)を確認してください。