Third-Party Interface

Gumawa ng tool na madadala ng BakoshApp: mag-serve ng apat na HTTP endpoint sa isang local port, at tumutugon ang program mo sa mga regalo, like, follow at chat sa TikTok at Kick.

Maaaring magpatakbo ang BakoshApp ng mga aksyon sa loob ng sarili mong program. Nagse-serve ang tool mo ng maliit na HTTP API sa isang local port; binabasa ng BakoshApp ang listahan mo ng mga aksyon at tumatawag ng isa sa tuwing may event sa TikTok o Kick na tumutugma sa trigger ng user. Anumang makokontrol mo mula sa code — smart lights, isang microcontroller, isang soundboard, ibang laro, ang sarili mong bot — ay maaaring tumugon sa live stream nang walang mod ng BakoshApp.

Paano Ito Konektado

  1. Nakikinig ang tool mo sa http://127.0.0.1:8833.
  2. Ikinokonekta ito ng user sa Mga Setting → Third-Party (host at port) at sinusuri ito gamit ang I-test ang koneksyon.
  3. Sa Mga Actions tab ng isang mod, ni-click ng user ang Magdagdag ng bago, pumili ng trigger, at sa Lahat ng Actions → External nag-configure ng Third-Party Action: pinupunan ng BakoshApp ang dalawang listahan ng mga kategorya at aksyon mo.
  4. Sa bawat event na tumutugma, tinatawagan ng BakoshApp ang tool mo, at ang code mo ang gumagawa ng trabaho.

Isang-direksyon lang ang koneksyon: tinatawagan ka ng BakoshApp, hindi kailanman kabaliktaran.

Port at Address

  • 8833 ang default. Maaari itong baguhin ng user sa Mga Setting → Third-Party; sabihin sa mga user mo kung aling port ang ilalagay kung gumagamit ka ng iba.
  • Hindi magagamit ang 8832: nakikinig doon ang BakoshApp mismo (ganito nahahanap ng TikFinity ang BakoshApp), at tinatanggihan ito ng setting.
  • Walang authentication. Makinig sa 127.0.0.1, hindi sa 0.0.0.0, para hindi maabot ang API mula sa network.
  • Palaging gumagamit ang BakoshApp ng plain HTTP.

Mga Endpoint

Dapat i-serve ng tool mo ang lahat ng apat. Ang bawat response ay JSON na may data member.

1. Impormasyon ng App

Kinikilala nito ang tool mo. Ipinapakita ito ng BakoshApp bilang "Nakakonekta sa Mock Tool 1.0 ng Bakosh" sa Mga Setting at sa action configurator.

GET /api/app/info
{ "data": { "name": "Mock Tool", "version": "1.0", "author": "Bakosh" } }

2. Mga Kategorya

Ang unang listahan sa configurator.

GET /api/features/categories
{
  "data": [
    { "categoryId": "smart_device_control", "categoryName": "Control Smart Devices" },
    { "categoryId": "serial_port_control",  "categoryName": "Control Serial Port" }
  ]
}

3. Mga Aksyon

Ang pangalawang listahan: ang mga aksyon ng isang kategorya. Ang categoryId ay URL-encoded.

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" }
  ]
}

Ipinapakita ng mga listahan ang mga item mo sa pagkakasunod-sunod na ibinabalik mo ang mga ito. Ang isang aksyon na na-save na ng user pero hindi mo na ibinabalik ay mananatili sa itaas ng listahan, para hindi ito basta na lang magbago nang tahimik.

Babala

Ang categoryId at actionId ang tanging ina-store ng BakoshApp para sa isang configured na aksyon. Panatilihin silang stable: ang pagpalit ng pangalan ng id ay sisira sa mga umiiral nang setup. Ang mga pangalan ay para sa display lang at maaaring magbago anumang oras.

4. Pag-execute

Tinatawag ito sa tuwing may tumutugmang event na nagpapatakbo ng aksyon.

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://…"
  }
}

Sumagot gamit ang walang laman na data array kapag tinanggap mo na ang request:

{ "data": [] }

Timing

  • Naghihintay ang BakoshApp ng hanggang 5 segundo para sa tatlong GET endpoint at 10 segundo para sa exec. Ang isang request na nabigo o nag-time out ay hindi na inuulit.
  • Kung ang aksyon ay may Bilang na higit sa 1, tinatawagan ng BakoshApp ang exec ng ganoong karaming beses, nang paisa-isa: naghihintay ito ng sagot mo, pagkatapos ng Interval ng row, tapos tumatawag muli.
  • Ang mga tawag para sa parehong row ay dumarating nang paisa-isa; maaaring tumawag sa iyo ang iba't ibang row nang magkakasabay (parallel).

Kaya sumagot nang mabilis at gawin ang mabagal na trabaho sa background — makakapagpadala ang mga viewer ng regalo nang mas mabilis kaysa sa galaw ng serial port.

Ang Context Object

Inilalarawan ng context ang event na nagpatakbo ng aksyon. Ang mga field lang na umiiral para sa event na iyon ang naroroon — suriin bago basahin.

FieldUriPresent
triggerTypeIdnumberpalagi — tingnan sa ibaba
eventTypestringpalagi — gift, like, chat, follow, share, joined, subscribe, tikfinity o test
sourcestringpalagi — eulerstream (TikTok), kick, tikfinity o test
platformstringMga event ng TikTok at Kick — tiktok o kick
usernamestringpalagi — ang handle ng viewer
nicknamestringMga event ng TikTok at Kick — ang display name
userIdstringMga event ng TikTok at Kick
profilePictureUrlstringkadalasan (nag-e-expire ang mga link ng avatar sa TikTok)
commentstringmga mensahe sa chat; sa Kick, pati na rin ang mensaheng nakalakip sa isang regalo
giftNamestringmga regalo
giftIdnumbermga regalo sa TikTok
repeatCountnumbermga regalo sa TikTok — bilang ng regalo sa streak; mga gifted subscription sa Kick — bilang ng tatanggap
diamondCountnumbermga regalo — value sa Diamonds (TikTok) o presyo sa Kicks (Kick)
coinsnumberkapareho ng diamondCount
likeCountnumbermga like
subMonthnumbermga subscription

Ang mga event na relayed mula sa TikFinity ay may dala lang na triggerTypeId (palaging 0), source, eventType at username.

Mga Uri ng Trigger

Ang triggerTypeId ay ang uri ng trigger na kina-configure ng user, kaya nakikilala ang pagkakaiba ng regalong pinili ayon sa pangalan at regalong tinutugma ayon sa value:

IdTrigger
1Ibahagi
3Min-Max (hanay ng value ng regalo)
4Regalo
6Sumali
7Like
9Follow
10Subscribe / Super Fan
11Chat
0TikFinity, o kahit ano pa

Ituring ang mga hindi kilalang id bilang 0; maaaring magdagdag pa ng iba.

Ang Button na Subukan

Tinatawagan ng Subukan sa row ng aksyon (at ng hotkey nito) ang exec gamit ang sample data: ang source at eventType ay test, ang triggerTypeId ay mula sa trigger ng row, ang viewer ay bakosh_app, at ang regalo ay isang Rose (giftId 5655). Walang platform.

Pag-uulat ng mga Problema

Magbalik ng message para ilarawan kung ano ang nagkamali:

{ "message": "Serial port COM3 is not available" }
  • Sa mga GET endpoint, anumang message ay magiging dahilan para ituring ng BakoshApp ang sagot bilang error, kahit na HTTP 200 pa ito.
  • Sa exec, isinusulat ang message sa console at log file ng BakoshApp bilang warning. Hindi nakakakuha ang user ng pop-up.

Iginagalang din ang mga HTTP error status; kung walang message, ang status code lang ang ipinapakita sa log.

Paggamit Kasama ang TikFinity Din

Tumutugma ang interface na ito sa third-party interface ng TikFinity — parehong apat na path at field name — kaya isang tool lang ang makakapag-serve sa dalawa. Magkaiba ang ports: naghahanap ang TikFinity ng mga tool sa 8832, na inookupa ng BakoshApp habang tumatakbo ito, kaya bigyan ang BakoshApp ng ibang port.

Web app ang TikFinity at nangangailangan ito ng CORS headers (Access-Control-Allow-Origin: * at ang mga katulad nito) at sagot sa mga OPTIONS preflight request. Hindi kailangan ng BakoshApp ang mga ito, pero walang masamang mangyayari kung ipapadala mo pa rin ang mga ito. Nagpapadala ang BakoshApp ng mas maraming context field kaysa sa TikFinity — huwag pansinin ang mga hindi mo ginagamit.

Isang Kumpletong Gumaganang Tool

Walang dependencies — i-save bilang tool.mjs at patakbuhin ang 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}`));

Pagsubok sa Tool Mo

  1. Simulan ang tool mo.
  2. Sa BakoshApp, buksan ang Mga Setting → Third-Party, suriin ang host at port, at i-click ang I-test ang koneksyon. Dapat mong makita ang pangalan, bersyon at author ng tool mo.
  3. Sa Mga Actions tab ng isang mod, i-click ang Magdagdag ng bago, pumili ng trigger at i-click ang Susunod. Sa Lahat ng Actions → External, i-click ang Configure sa tabi ng Third-Party Action, piliin ang kategorya at aksyon mo, at i-save.
  4. I-click ang Subukan sa bagong row — makakatanggap ang tool mo ng exec na may eventType: "test".
  5. Mag-live at suriin kung dumarating ang mga totoong event.

Kung sinabi ng configurator na "Hindi makakonekta! Suriin ang iyong integration.", suriin kung nakikinig ang tool sa 127.0.0.1, kung tumutugma ang port sa Mga Setting, at kung ibinabalik ng GET /api/app/info ang sagot nito sa loob ng data.