Interface Third-Party

Créez un outil que BakoshApp peut piloter : exposez quatre endpoints HTTP sur un port local, et votre programme réagira aux cadeaux, likes, follows et messages du chat de TikTok et Kick.

BakoshApp peut exécuter des actions dans votre programme. Votre outil expose une petite API HTTP sur un port local ; BakoshApp lit votre liste d'actions et en appelle une chaque fois qu'un événement TikTok ou Kick correspond au déclencheur de l'utilisateur. Tout ce que vous pouvez piloter par du code — lampes connectées, microcontrôleur, soundboard, autre jeu, votre propre bot — peut réagir à un live sans mod BakoshApp.

Comment tout s'articule

  1. Votre outil écoute sur http://127.0.0.1:8833.
  2. L'utilisateur le connecte dans Paramètres → Third-Party (hôte et port) et le vérifie avec Tester la connexion.
  3. Dans l'onglet Actions d'un mod, l'utilisateur clique sur Ajouter nouveau, choisit un déclencheur et, dans Toutes les actions → Externes, configure une Third-Party Action : BakoshApp remplit deux listes avec vos catégories et vos actions.
  4. À chaque événement correspondant, BakoshApp appelle votre outil, et votre code fait le travail.

La connexion est à sens unique : BakoshApp vous appelle, jamais l'inverse.

Port et adresse

  • 8833 est la valeur par défaut. L'utilisateur peut la modifier dans Paramètres → Third-Party ; si vous utilisez un autre port, indiquez à vos utilisateurs lequel saisir.
  • Le port 8832 est inutilisable : BakoshApp lui-même y écoute (c'est ainsi que TikFinity trouve BakoshApp), et le paramètre le refuse.
  • Il n'y a pas d'authentification. Écoutez sur 127.0.0.1, et non sur 0.0.0.0, pour que l'API ne soit pas accessible depuis le réseau.
  • BakoshApp utilise toujours du HTTP en clair.

Endpoints

Votre outil doit exposer les quatre. Chaque réponse est un JSON contenant un membre data.

1. Infos de l'application

Identifie votre outil. BakoshApp l'affiche sous la forme « Connecté à Mock Tool 1.0 par Bakosh » dans les Paramètres et dans le configurateur d'action.

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

2. Catégories

La première liste du configurateur.

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

3. Actions

La deuxième liste : les actions d'une catégorie. categoryId est encodé pour l'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" }
  ]
}

Les listes affichent vos éléments dans l'ordre où vous les renvoyez. Une action déjà enregistrée par l'utilisateur mais que vous ne renvoyez plus reste en haut de la liste, pour qu'elle ne change pas à son insu.

Avertissement

categoryId et actionId sont les seules données que BakoshApp enregistre pour une action configurée. Gardez-les stables : renommer un id casse les configurations existantes. Les noms ne servent qu'à l'affichage et peuvent changer à tout moment.

4. Exécution

Appelé chaque fois qu'un événement correspondant exécute l'action.

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

Répondez avec un tableau data vide une fois la requête acceptée :

{ "data": [] }

Délais

  • BakoshApp attend jusqu'à 5 secondes pour les trois endpoints GET et 10 secondes pour exec. Une requête qui échoue ou expire n'est pas renvoyée.
  • Si l'action a une Quantité supérieure à 1, BakoshApp appelle exec autant de fois, l'une après l'autre : il attend votre réponse, puis l'Intervalle de la ligne, puis rappelle.
  • Les appels d'une même ligne arrivent un par un ; des lignes différentes peuvent vous appeler en parallèle.

Répondez donc vite et faites le travail lent en arrière-plan — les spectateurs peuvent envoyer des cadeaux plus vite qu'un port série ne peut suivre.

L'objet context

context décrit l'événement qui a exécuté l'action. Seuls les champs qui existent pour cet événement sont présents — vérifiez-les avant de les lire.

ChampTypePrésent
triggerTypeIdnumbertoujours — voir ci-dessous
eventTypestringtoujours — gift, like, chat, follow, share, joined, subscribe, tikfinity ou test
sourcestringtoujours — eulerstream (TikTok), kick, tikfinity ou test
platformstringévénements TikTok et Kick — tiktok ou kick
usernamestringtoujours — l'identifiant du spectateur
nicknamestringévénements TikTok et Kick — le nom affiché
userIdstringévénements TikTok et Kick
profilePictureUrlstringgénéralement (les liens d'avatar TikTok expirent)
commentstringmessages du chat ; sur Kick, aussi le message joint à un cadeau
giftNamestringcadeaux
giftIdnumbercadeaux TikTok
repeatCountnumbercadeaux TikTok — nombre de cadeaux dans la série ; abonnements offerts sur Kick — nombre de destinataires
diamondCountnumbercadeaux — valeur en diamants (TikTok) ou prix en Kicks (Kick)
coinsnumberidentique à diamondCount
likeCountnumberlikes
subMonthnumberabonnements

Les événements relayés par TikFinity ne contiennent que triggerTypeId (toujours 0), source, eventType et username.

Types de déclencheurs

triggerTypeId est le type du déclencheur configuré par l'utilisateur ; on distingue ainsi un cadeau choisi par son nom d'un cadeau reconnu par sa valeur :

IdDéclencheur
1Partage
3Min-Max (plage de valeur du cadeau)
4Cadeau
6Arrivé
7Like
9Suivre
10Subscribe / Super Fan
11Chat
0TikFinity, ou tout le reste

Traitez les id inconnus comme 0 ; d'autres pourront être ajoutés.

Le bouton Tester

Tester sur la ligne de l'action (et son raccourci clavier) appelle exec avec des données d'exemple : source et eventType valent test, triggerTypeId provient du déclencheur de la ligne, le spectateur est bakosh_app et le cadeau est une Rose (giftId 5655). Il n'y a pas de platform.

Signaler les problèmes

Renvoyez un message pour décrire ce qui n'a pas fonctionné :

{ "message": "Serial port COM3 is not available" }
  • Sur les endpoints GET, la présence d'un message fait que BakoshApp traite la réponse comme une erreur, même avec un HTTP 200.
  • Sur exec, le message est écrit dans la console et le fichier journal de BakoshApp sous forme d'avertissement. L'utilisateur ne voit aucune fenêtre contextuelle.

Les statuts d'erreur HTTP sont également pris en compte ; sans message, le journal n'affiche que le code de statut.

Fonctionner aussi avec TikFinity

L'interface correspond à l'interface third-party de TikFinity (mêmes quatre chemins, mêmes noms de champs), si bien qu'un seul outil peut servir les deux. Les ports diffèrent : TikFinity cherche les outils sur le port 8832, que BakoshApp occupe tant qu'il tourne ; attribuez donc un autre port à BakoshApp.

TikFinity est une application web : elle a besoin des en-têtes CORS (Access-Control-Allow-Origin: * et consorts) ainsi que de réponses aux requêtes preflight OPTIONS. BakoshApp n'en a pas besoin, mais les envoyer ne pose aucun problème. BakoshApp envoie plus de champs context que TikFinity — ignorez ceux dont vous ne vous servez pas.

Un outil complet et fonctionnel

Aucune dépendance — enregistrez-le sous tool.mjs et lancez 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}`));

Tester votre outil

  1. Lancez votre outil.
  2. Dans BakoshApp, ouvrez Paramètres → Third-Party, vérifiez l'hôte et le port, puis cliquez sur Tester la connexion. Vous devriez voir le nom, la version et l'auteur de votre outil.
  3. Dans l'onglet Actions d'un mod, cliquez sur Ajouter nouveau, choisissez un déclencheur et cliquez sur Suivant. Dans Toutes les actions → Externes, cliquez sur Configure à côté de Third-Party Action, choisissez votre catégorie et votre action, puis enregistrez.
  4. Cliquez sur Tester sur la nouvelle ligne — votre outil reçoit un exec avec eventType: "test".
  5. Lancez votre live et vérifiez que les vrais événements arrivent.

Si le configurateur affiche « Connexion impossible ! Vérifiez votre intégration. », vérifiez que l'outil écoute sur 127.0.0.1, que le port correspond à celui des Paramètres et que GET /api/app/info renvoie sa réponse dans data.