Third-Party 인터페이스
BakoshApp이 제어할 수 있는 도구를 만들어보세요: 로컬 포트에서 네 개의 HTTP 엔드포인트를 제공하면, 여러분의 프로그램이 TikTok과 Kick의 선물, 좋아요, 팔로우, 채팅에 반응합니다.
BakoshApp은 여러분의 프로그램 안에서 액션을 실행할 수 있어요. 여러분의 도구는 로컬 포트에서 작은 HTTP API를 제공하고, BakoshApp은 여러분의 액션 목록을 읽어와 사용자의 트리거와 일치하는 TikTok 또는 Kick 이벤트가 발생할 때마다 그중 하나를 호출해요. 코드로 제어할 수 있는 것이라면 무엇이든 — 스마트 조명, 마이크로컨트롤러, 사운드보드, 다른 게임, 직접 만든 봇 — BakoshApp Mod 없이도 라이브 스트림에 반응할 수 있어요.
연동 구조
- 여러분의 도구는
http://127.0.0.1:8833에서 대기해요. - 사용자는 설정 → Third-Party에서 호스트와 포트를 연결하고 연결 테스트로 확인해요.
- Mod의 액션 탭에서 사용자가 새로 추가를 클릭하고 트리거를 선택한 다음, 모든 액션 → 외부에서 Third-Party Action을 설정해요: BakoshApp이 두 개의 목록을 여러분의 카테고리와 액션으로 채워요.
- 일치하는 이벤트가 발생할 때마다 BakoshApp이 여러분의 도구를 호출하고, 여러분의 코드가 작업을 수행해요.
연결은 한 방향이에요: BakoshApp이 여러분을 호출할 뿐, 그 반대는 없어요.
포트와 주소
- 8833이 기본값이에요. 사용자는 설정 → Third-Party에서 변경할 수 있으니, 다른 포트를 사용한다면 어떤 포트를 입력해야 하는지 사용자에게 알려주세요.
- 8832는 사용할 수 없어요: BakoshApp 자신이 그 포트에서 대기하고 있고(이 덕분에 TikFinity가 BakoshApp을 찾을 수 있어요), 설정에서도 이 포트를 거부해요.
- 인증은 없어요. API가 네트워크에서 접근되지 않도록
0.0.0.0이 아니라127.0.0.1에서 대기하세요. - BakoshApp은 항상 일반 HTTP를 사용해요.
엔드포인트
여러분의 도구는 네 개를 모두 제공해야 해요. 모든 응답은 data 멤버를 포함한 JSON이에요.
1. 앱 정보
여러분의 도구를 식별해요. BakoshApp은 설정과 액션 설정 화면에 "Connected with Mock Tool 1.0 by Bakosh"라고 표시해요.
GET /api/app/info
{ "data": { "name": "Mock Tool", "version": "1.0", "author": "Bakosh" } }2. 카테고리
설정 화면의 첫 번째 목록이에요.
GET /api/features/categories
{
"data": [
{ "categoryId": "smart_device_control", "categoryName": "Control Smart Devices" },
{ "categoryId": "serial_port_control", "categoryName": "Control Serial Port" }
]
}3. 액션
두 번째 목록: 한 카테고리에 속한 액션들이에요. 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" }
]
}목록은 여러분이 반환한 순서대로 항목을 표시해요. 사용자가 이미 저장했지만 더 이상 반환되지 않는 액션은 목록 맨 위에 남아 있으므로, 조용히 바뀌는 일은 없어요.
categoryId와 actionId는 설정된 액션에 대해 BakoshApp이 저장하는 전부예요. 이 값들은 변하지 않게 유지하세요: 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은 세 개의 GET 엔드포인트는 최대 5초,
exec는 최대 10초까지 기다려요. 실패하거나 시간이 초과된 요청은 다시 시도되지 않아요. - 액션의 횟수가 1보다 크면, BakoshApp은
exec를 그 횟수만큼 차례로 호출해요: 응답을 기다린 다음, 행의 간격만큼 기다렸다가 다시 호출해요. - 같은 행에 대한 호출은 한 번에 하나씩 오지만, 서로 다른 행은 동시에 호출할 수 있어요.
그러니 빠르게 응답하고 느린 작업은 백그라운드에서 처리하세요 — 시청자는 시리얼 포트가 움직이는 속도보다 더 빠르게 선물을 보낼 수 있어요.
context 객체
context는 액션을 실행한 이벤트를 설명해요. 해당 이벤트에 존재하는 필드만 포함되어 있으니, 읽기 전에 먼저 확인하세요.
| 필드 | 유형 | 포함 조건 |
|---|---|---|
triggerTypeId | number | 항상 — 아래 참고 |
eventType | string | 항상 — gift, like, chat, follow, share, joined, subscribe, tikfinity 또는 test |
source | string | 항상 — eulerstream(TikTok), kick, tikfinity 또는 test |
platform | string | TikTok 및 Kick 이벤트 — tiktok 또는 kick |
username | string | 항상 — 시청자의 아이디 |
nickname | string | TikTok 및 Kick 이벤트 — 표시 이름 |
userId | string | TikTok 및 Kick 이벤트 |
profilePictureUrl | string | 대부분(TikTok 아바타 링크는 만료돼요) |
comment | string | 채팅 메시지; Kick에서는 선물에 첨부된 메시지도 포함돼요 |
giftName | string | 선물 |
giftId | number | TikTok 선물 |
repeatCount | number | TikTok 선물 — 연속 선물 개수; Kick 선물 구독 — 수신자 수 |
diamondCount | number | 선물 — Diamonds 가치(TikTok) 또는 Kicks 가격(Kick) |
coins | number | diamondCount와 동일 |
likeCount | number | 좋아요 |
subMonth | number | 구독 |
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의 서드파티 인터페이스와 동일해요 — 같은 네 개의 경로와 필드 이름을 사용하므로 하나의 도구로 둘 다 지원할 수 있어요. 포트는 달라요: TikFinity는 8832 포트에서 도구를 찾는데, BakoshApp이 실행 중일 때 그 포트를 사용하므로 BakoshApp에는 다른 포트를 지정하세요.
TikFinity는 웹 앱이라서 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 안에 응답을 반환하는지 확인하세요.