Third-Party インターフェース
ローカルポートで 4 つの HTTP エンドポイントを提供して BakoshApp から操作できるツールを作り、TikTok と Kick のギフト・いいね・フォロー・チャットにプログラムを反応させる方法です。
BakoshApp は、あなたのプログラムの中でアクションを実行できます。あなたのツールはローカルポートで小さな HTTP API を提供し、BakoshApp はそのアクション一覧を読み込んで、TikTok や Kick のイベントがユーザーのトリガーに一致するたびにアクションを呼び出します。スマート照明、マイコン、サウンドボード、別のゲーム、自作のボットなど、コードで制御できるものなら何でも、BakoshApp の Mod なしでライブ配信に反応させられます。
全体の流れ
- ツールは
http://127.0.0.1:8833で待ち受けます。 - ユーザーは設定 → Third-Party でツールに接続し(ホストとポート)、接続をテストで確認します。
- ユーザーは Mod のアクションタブで新規追加をクリックしてトリガーを選び、すべてのアクション → 外部で Third-Party Action を設定します。このとき BakoshApp は、あなたのカテゴリとアクションで 2 つのリストを埋めます。
- 一致するイベントが起きるたびに BakoshApp がツールを呼び出し、あなたのコードが処理を行います。
接続は一方向です。BakoshApp があなたのツールを呼び出し、その逆はありません。
ポートとアドレス
- デフォルトは 8833 です。ユーザーは設定 → Third-Party で変更できます。別のポートを使う場合は、どのポートを入力すればよいかをユーザーに伝えてください。
- 8832 は使えません。BakoshApp 自身がこのポートで待ち受けており(TikFinity はこれで BakoshApp を見つけます)、設定でも受け付けられません。
- 認証はありません。API にネットワークからアクセスされないよう、
0.0.0.0ではなく127.0.0.1で待ち受けてください。 - BakoshApp は常にプレーンな HTTP を使います。
エンドポイント
ツールは 4 つすべてを提供する必要があります。レスポンスはすべて、data メンバーを持つ JSON です。
1. アプリ情報
ツールを識別します。BakoshApp は、設定画面とアクションの設定画面に「Bakosh の Mock Tool 1.0 に接続しました」と表示します。
GET /api/app/info
{ "data": { "name": "Mock Tool", "version": "1.0", "author": "Bakosh" } }2. カテゴリ
設定画面の 1 つ目のリストです。
GET /api/features/categories
{
"data": [
{ "categoryId": "smart_device_control", "categoryName": "Control Smart Devices" },
{ "categoryId": "serial_port_control", "categoryName": "Control Serial Port" }
]
}3. アクション
2 つ目のリストで、1 つのカテゴリに属するアクションです。categoryId は URL エンコードされます。
GET /api/features/actions?categoryId=smart_device_control
{
"data": [
{ "actionId": "turn_lights_on", "actionName": "Turn Lights On" },
{ "actionId": "turn_lights_off", "actionName": "Turn Lights Off" }
]
}リストには、ツールが返した順に項目が表示されます。ユーザーが保存済みのアクションをツールが返さなくなっても、そのアクションはリストの先頭に残るため、知らないうちに変わることはありません。
設定済みのアクションについて BakoshApp が保存するのは、categoryId と actionId だけです。これらは固定しておいてください。ID を変更すると、既存の設定が壊れます。名前は表示専用なので、いつでも変更できます。
4. 実行
一致するイベントがアクションを実行するたびに呼び出されます。
POST /api/features/actions/exec
{
"categoryId": "smart_device_control",
"actionId": "turn_lights_on",
"context": {
"triggerTypeId": 4,
"eventType": "gift",
"source": "eulerstream",
"platform": "tiktok",
"userId": "123456789",
"username": "testuser",
"nickname": "Test User",
"giftName": "Rose",
"giftId": 5655,
"repeatCount": 3,
"diamondCount": 1,
"coins": 1,
"profilePictureUrl": "https://…"
}
}リクエストを受け付けたら、空の data 配列で応答します。
{ "data": [] }タイミング
- BakoshApp は、3 つの GET エンドポイントでは最大 5 秒、
execでは最大 10 秒待ちます。失敗またはタイムアウトしたリクエストは再送されません。 - アクションの回数が 1 より大きい場合、BakoshApp はその回数だけ
execを順番に呼び出します。応答を待ち、次にその行の間隔だけ待ってから、再び呼び出します。 - 同じ行からの呼び出しは 1 つずつ届きますが、異なる行からは並行して呼び出されることがあります。
そのため、応答はすばやく返し、時間のかかる処理はバックグラウンドで行ってください。視聴者は、シリアルポートが動くよりも速くギフトを送れます。
context オブジェクト
context は、アクションを実行したイベントを表します。そのイベントに存在するフィールドだけが含まれるため、読み取る前に有無を確認してください。
| フィールド | 種類 | 含まれる条件 |
|---|---|---|
triggerTypeId | 数値 | 常に — 下記を参照 |
eventType | 文字列 | 常に — gift、like、chat、follow、share、joined、subscribe、tikfinity、test のいずれか |
source | 文字列 | 常に — eulerstream(TikTok)、kick、tikfinity、test のいずれか |
platform | 文字列 | TikTok と Kick のイベント — tiktok または kick |
username | 文字列 | 常に — 視聴者のハンドル名 |
nickname | 文字列 | TikTok と Kick のイベント — 表示名 |
userId | 文字列 | TikTok と Kick のイベント |
profilePictureUrl | 文字列 | 通常は含まれる(TikTok のアバターのリンクには有効期限があります) |
comment | 文字列 | チャットメッセージ。Kick ではギフトに添えられたメッセージも含む |
giftName | 文字列 | ギフト |
giftId | 数値 | TikTok のギフト |
repeatCount | 数値 | TikTok のギフト — ストリーク内のギフト数。Kick のギフトサブスク — 受け取った人数 |
diamondCount | 数値 | ギフト — ダイヤ換算の価値(TikTok)または Kicks での価格(Kick) |
coins | 数値 | diamondCount と同じ |
likeCount | 数値 | いいね |
subMonth | 数値 | サブスクリプション |
TikFinity から中継されたイベントには、triggerTypeId(常に 0)、source、eventType、username だけが含まれます。
トリガーの種類
triggerTypeId はユーザーが設定したトリガーの種類を表すため、名前で選んだギフトと価値で一致したギフトを区別できます。
| ID | トリガー |
|---|---|
| 1 | シェア |
| 3 | Min-Max(ギフトの価値の範囲) |
| 4 | ギフト |
| 6 | 参加 |
| 7 | いいね |
| 9 | フォロー |
| 10 | Subscribe / Super Fan |
| 11 | チャット |
| 0 | TikFinity、またはその他すべて |
不明な ID は 0 として扱ってください。今後、種類が追加される可能性があります。
テストボタン
アクション行のテスト(とそのホットキー)は、サンプルデータで exec を呼び出します。source と eventType は test、triggerTypeId はその行のトリガーに基づく値、視聴者は bakosh_app、ギフトは Rose(giftId 5655)です。platform は含まれません。
問題の報告
何が問題だったかを伝えるには、message を返します。
{ "message": "Serial port COM3 is not available" }- GET エンドポイントでは、
messageが含まれていると、HTTP 200 であっても BakoshApp は応答をエラーとして扱います。 execでは、messageは警告として BakoshApp のコンソールとログファイルに書き込まれます。ユーザーにポップアップは表示されません。
HTTP のエラーステータスも考慮されます。message がない場合、ログにはステータスコードだけが表示されます。
TikFinity との併用
このインターフェースは TikFinity のサードパーティインターフェースと同じ(4 つのパスとフィールド名が共通)なので、1 つのツールで両方に対応できます。ただしポートは異なります。TikFinity はポート 8832 でツールを探しますが、このポートは実行中の BakoshApp が使用しているため、BakoshApp には別のポートを割り当ててください。
TikFinity は Web アプリなので、CORS ヘッダー(Access-Control-Allow-Origin: * など)と、OPTIONS プリフライトリクエストへの応答が必要です。BakoshApp にはどちらも不要ですが、送っても問題ありません。BakoshApp は TikFinity よりも多くの context フィールドを送るので、使わないものは無視してください。
動作する完全なツールの例
依存関係はありません。tool.mjs として保存し、node tool.mjs で実行します。
import http from 'node:http';
const PORT = 8833;
const APP_INFO = { name: 'Mock Tool', version: '1.0', author: 'Bakosh' };
const CATALOG = [
{
categoryId: 'smart_device_control',
categoryName: 'Control Smart Devices',
actions: [
{ actionId: 'turn_lights_on', actionName: 'Turn Lights On' },
{ actionId: 'turn_lights_off', actionName: 'Turn Lights Off' },
],
},
];
// Wildcard CORS keeps the same tool usable from TikFinity as well.
const CORS = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': '*',
};
const send = (res, status, body) => {
res.writeHead(status, { 'Content-Type': 'application/json', ...CORS });
res.end(JSON.stringify(body));
};
const readBody = (req) =>
new Promise((resolve) => {
let raw = '';
req.on('data', (c) => { raw += c; });
req.on('end', () => { try { resolve(JSON.parse(raw || '{}')); } catch { resolve({}); } });
});
// Your actual work goes here. Return a string to report a problem.
function runAction(categoryId, actionId, context) {
console.log(`${categoryId}/${actionId} fired by ${context.username ?? 'someone'} on ${context.platform ?? context.source}`);
// turnLightsOn() …
return null;
}
http.createServer(async (req, res) => {
const url = new URL(req.url, `http://127.0.0.1:${PORT}`);
if (req.method === 'OPTIONS') { // preflight (TikFinity only)
res.writeHead(204, CORS);
return res.end();
}
if (req.method === 'GET' && url.pathname === '/api/app/info') {
return send(res, 200, { data: APP_INFO });
}
if (req.method === 'GET' && url.pathname === '/api/features/categories') {
return send(res, 200, {
data: CATALOG.map(({ categoryId, categoryName }) => ({ categoryId, categoryName })),
});
}
if (req.method === 'GET' && url.pathname === '/api/features/actions') {
const cat = CATALOG.find((c) => c.categoryId === url.searchParams.get('categoryId'));
return send(res, 200, { data: cat ? cat.actions : [] });
}
if (req.method === 'POST' && url.pathname === '/api/features/actions/exec') {
const body = await readBody(req);
const problem = runAction(body.categoryId ?? '', body.actionId ?? '', body.context ?? {});
return problem ? send(res, 200, { message: problem }) : send(res, 200, { data: [] });
}
send(res, 404, { message: `No such endpoint: ${url.pathname}` });
}).listen(PORT, '127.0.0.1', () => console.log(`Listening on http://127.0.0.1:${PORT}`));ツールのテスト
- ツールを起動します。
- BakoshApp で設定 → Third-Party を開き、ホストとポートを確認して接続をテストをクリックします。ツールの名前、バージョン、作者が表示されれば成功です。
- Mod のアクションタブで新規追加をクリックし、トリガーを選んで次へをクリックします。すべてのアクション → 外部で、Third-Party Action の横にある Configure をクリックし、カテゴリとアクションを選んで保存します。
- 新しい行のテストをクリックすると、ツールは
eventType: "test"を含むexecを受け取ります。 - ライブ配信を始めて、実際のイベントが届くことを確認します。
設定画面に「接続できません!連携を確認してください。」と表示される場合は、ツールが 127.0.0.1 で待ち受けているか、ポートが設定と一致しているか、GET /api/app/info が応答を data の中に入れて返しているかを確認してください。