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

  1. Công cụ của bạn lắng nghe tại http://127.0.0.1:8833.
  2. 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.
  3. 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.
  4. 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ải 0.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.

Cảnh báo

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ườngLoạiXuất hiện
triggerTypeIdnumberluôn có — xem bên dưới
eventTypestringluôn có — gift, like, chat, follow, share, joined, subscribe, tikfinity hoặc test
sourcestringluôn có — eulerstream (TikTok), kick, tikfinity hoặc test
platformstringSự kiện TikTok và Kick — tiktok hoặc kick
usernamestringluôn có — tên người dùng của người xem
nicknamestringSự kiện TikTok và Kick — tên hiển thị
userIdstringSự kiện TikTok và Kick
profilePictureUrlstringthường có (link avatar TikTok sẽ hết hạn)
commentstringtin nhắn chat; trên Kick còn có tin nhắn đính kèm theo quà tặng
giftNamestringquà tặng
giftIdnumberQuà tặng TikTok
repeatCountnumberQuà 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
diamondCountnumberquà tặng — giá trị tính bằng Diamonds (TikTok) hoặc giá tính bằng Kicks (Kick)
coinsnumbergiống như diamondCount
likeCountnumberlượt thích
subMonthnumberlượ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ị:

IdTrigger
1Chia sẻ
3Min-Max (khoảng giá trị quà tặng)
4Quà tặng
6Đã tham gia
7Lượt thích
9Theo dõi
10Subscribe / Super Fan
11Trò chuyện
0TikFinity, 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ỳ message nà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

  1. Khởi động công cụ của bạn.
  2. 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.
  3. 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.
  4. Bấm Kiểm tra trên hàng mới — công cụ của bạn nhận một exec với eventType: "test".
  5. 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.