Giao diện Third-Party
Xây dựng một công cụ mà BakoshApp có thể điều khiển: phục vụ bốn endpoint HTTP trên một cổng cục bộ, và chương trình của bạn phản ứng với quà tặng, lượt thích, lượt theo dõi và chat trên TikTok và Kick.
BakoshApp có thể chạy hành động bên trong chương trình của bạn. Công cụ của bạn phục vụ một HTTP API nhỏ trên một cổng cục bộ; BakoshApp đọc danh sách hành động của bạn và gọi một hành động bất cứ khi nào sự kiện TikTok hoặc Kick khớp với trigger của người dùng. Bất cứ thứ gì bạn có thể điều khiển bằng code — đèn thông minh, vi điều khiển, bàn phát âm thanh, một game khác, bot của riêng bạn — đều có thể phản ứng với buổi livestream mà không cần mod BakoshApp.
Cách các thành phần kết hợp với nhau
- Công cụ của bạn lắng nghe tại
http://127.0.0.1:8833. - Người dùng kết nối nó trong Cài đặt → Third-Party (host và cổng) và kiểm tra bằng Kiểm tra kết nối.
- Trên tab Hành động của mod, người dùng bấm Thêm mới, chọn một trigger, rồi trong Tất cả hành động → Bên ngoài cấu hình một Third-Party Action: BakoshApp điền hai danh sách bằng danh mục và hành động của bạn.
- Với mỗi sự kiện khớp, BakoshApp gọi công cụ của bạn, và code của bạn thực hiện công việc.
Kết nối chỉ theo một chiều: BakoshApp gọi bạn, không bao giờ ngược lại.
Cổng và địa chỉ
- 8833 là mặc định. Người dùng có thể đổi trong Cài đặt → Third-Party; hãy cho người dùng của bạn biết cổng nào cần nhập nếu bạn dùng cổng khác.
- Không thể dùng 8832: chính BakoshApp lắng nghe ở đó (đó là cách TikFinity tìm thấy BakoshApp), và cài đặt sẽ từ chối cổng này.
- Không có xác thực. Hãy lắng nghe trên
127.0.0.1, không phải0.0.0.0, để API không thể truy cập được từ mạng. - BakoshApp luôn dùng HTTP thường, không phải HTTPS.
Endpoint
Công cụ của bạn phải phục vụ đủ cả bốn endpoint. Mọi phản hồi đều là JSON có thành phần data.
1. Thông tin ứng dụng
Xác định công cụ của bạn. BakoshApp hiển thị dòng này dưới dạng "Connected with Mock Tool 1.0 by Bakosh" trong Cài đặt và trong trình cấu hình hành động.
GET /api/app/info
{ "data": { "name": "Mock Tool", "version": "1.0", "author": "Bakosh" } }2. Danh mục
Danh sách đầu tiên trong trình cấu hình.
GET /api/features/categories
{
"data": [
{ "categoryId": "smart_device_control", "categoryName": "Control Smart Devices" },
{ "categoryId": "serial_port_control", "categoryName": "Control Serial Port" }
]
}3. Hành động
Danh sách thứ hai: các hành động của một danh mục. categoryId được URL-encode.
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" }
]
}Các danh sách hiển thị mục của bạn theo thứ tự bạn trả về. Một hành động người dùng đã lưu nhưng bạn không còn trả về nữa sẽ ở lại đầu danh sách, để nó không âm thầm thay đổi.
categoryId và actionId là tất cả những gì BakoshApp lưu lại cho một hành động đã cấu hình. Hãy giữ chúng ổn định: đổi tên một id sẽ làm hỏng các thiết lập hiện có. Tên chỉ để hiển thị và có thể đổi bất cứ lúc nào.
4. Thực thi
Được gọi mỗi khi một sự kiện khớp chạy hành động.
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://…"
}
}Trả lời bằng một mảng data rỗng ngay khi bạn đã chấp nhận yêu cầu:
{ "data": [] }Thời gian
- BakoshApp chờ tối đa 5 giây cho ba endpoint GET và 10 giây cho
exec. Một yêu cầu thất bại hoặc hết thời gian chờ sẽ không được gọi lại. - Nếu hành động có Số lượng lớn hơn 1, BakoshApp sẽ gọi
execđúng số lần đó, lần lượt từng cái: nó chờ câu trả lời của bạn, rồi chờ Khoảng thời gian của hàng, rồi gọi lại. - Các lệnh gọi cho cùng một hàng đến lần lượt từng cái một; các hàng khác nhau có thể gọi bạn song song.
Vì vậy hãy trả lời thật nhanh và làm việc chậm ở nền — người xem có thể gửi quà nhanh hơn tốc độ một cổng serial xử lý.
Đối tượng context
context mô tả sự kiện đã chạy hành động. Chỉ những trường tồn tại cho sự kiện đó mới xuất hiện — hãy kiểm tra trước khi đọc.
| Trường | Loại | Xuất hiện |
|---|---|---|
triggerTypeId | number | luôn có — xem bên dưới |
eventType | string | luôn có — gift, like, chat, follow, share, joined, subscribe, tikfinity hoặc test |
source | string | luôn có — eulerstream (TikTok), kick, tikfinity hoặc test |
platform | string | Sự kiện TikTok và Kick — tiktok hoặc kick |
username | string | luôn có — tên người dùng của người xem |
nickname | string | Sự kiện TikTok và Kick — tên hiển thị |
userId | string | Sự kiện TikTok và Kick |
profilePictureUrl | string | thường có (link avatar TikTok sẽ hết hạn) |
comment | string | tin nhắn chat; trên Kick còn có tin nhắn đính kèm theo quà tặng |
giftName | string | quà tặng |
giftId | number | Quà tặng TikTok |
repeatCount | number | Quà tặng TikTok — số quà trong chuỗi liên tiếp; gói đăng ký tặng của Kick — số người nhận |
diamondCount | number | quà tặng — giá trị tính bằng Diamonds (TikTok) hoặc giá tính bằng Kicks (Kick) |
coins | number | giống như diamondCount |
likeCount | number | lượt thích |
subMonth | number | lượt đăng ký |
Sự kiện chuyển tiếp từ TikFinity chỉ mang theo triggerTypeId (luôn là 0), source, eventType và username.
Loại trigger
triggerTypeId là loại trigger mà người dùng đã cấu hình, để phân biệt một quà được chọn theo tên với một quà được khớp theo giá trị:
| Id | Trigger |
|---|---|
| 1 | Chia sẻ |
| 3 | Min-Max (khoảng giá trị quà tặng) |
| 4 | Quà tặng |
| 6 | Đã tham gia |
| 7 | Lượt thích |
| 9 | Theo dõi |
| 10 | Subscribe / Super Fan |
| 11 | Trò chuyện |
| 0 | TikFinity, hoặc bất cứ thứ gì khác |
Coi các id không xác định là 0; có thể sẽ có thêm id mới.
Nút Kiểm tra
Kiểm tra trên hàng hành động (và phím tắt của nó) gọi exec với dữ liệu mẫu: source và eventType là test, triggerTypeId lấy từ trigger của hàng đó, người xem là bakosh_app, và quà tặng là Rose (giftId 5655). Không có platform.
Báo cáo sự cố
Trả về một message để mô tả điều gì đã sai:
{ "message": "Serial port COM3 is not available" }- Trên các endpoint GET, bất kỳ
messagenào cũng khiến BakoshApp coi câu trả lời là lỗi, kể cả với HTTP 200. - Trên
exec,messageđược ghi vào console và file log của BakoshApp dưới dạng cảnh báo. Người dùng sẽ không thấy popup nào.
Các mã lỗi HTTP cũng được tôn trọng; nếu không có message, log chỉ hiển thị mã trạng thái.
Hoạt động cùng TikFinity
Giao diện này khớp với third-party interface của TikFinity — cùng bốn đường dẫn và tên trường — nên một công cụ có thể phục vụ cả hai. Cổng thì khác nhau: TikFinity tìm công cụ trên cổng 8832, cổng mà BakoshApp chiếm dụng khi đang chạy, vì vậy hãy cho BakoshApp một cổng khác.
TikFinity là một web app và cần header CORS (Access-Control-Allow-Origin: * và các header liên quan) cùng câu trả lời cho các yêu cầu preflight OPTIONS. BakoshApp không cần chúng, nhưng gửi chúng cũng không gây hại gì. BakoshApp gửi nhiều trường context hơn TikFinity — hãy bỏ qua những trường bạn không dùng.
Một công cụ hoàn chỉnh hoạt động được
Không phụ thuộc gì cả — lưu thành tool.mjs và chạy 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}`));Kiểm tra công cụ của bạn
- Khởi động công cụ của bạn.
- Trong BakoshApp, mở Cài đặt → Third-Party, kiểm tra host và cổng, rồi bấm Kiểm tra kết nối. Bạn sẽ thấy tên, phiên bản và tác giả công cụ của mình.
- Trên tab Hành động của mod, bấm Thêm mới, chọn một trigger rồi bấm Tiếp theo. Trong Tất cả hành động → Bên ngoài, bấm Configure cạnh Third-Party Action, chọn danh mục và hành động của bạn, rồi lưu.
- Bấm Kiểm tra trên hàng mới — công cụ của bạn nhận một
execvớieventType: "test". - Lên live và kiểm tra xem sự kiện thật có đến không.
Nếu trình cấu hình báo "Không thể kết nối! Hãy kiểm tra tích hợp của bạn.", hãy kiểm tra xem công cụ có lắng nghe trên 127.0.0.1 không, cổng có khớp với Cài đặt không, và GET /api/app/info có trả về câu trả lời bên trong data không.