واجهة Third-Party
ابنِ أداة يمكن لـ BakoshApp التحكم فيها: قدّم أربع نقاط نهاية HTTP على منفذ محلي، ودع برنامجك يتفاعل مع هدايا TikTok وKick وإعجاباتها ومتابعاتها ودردشتها.
يمكن لـ BakoshApp تشغيل إجراءات داخل برنامجك أنت. تُقدّم أداتك واجهة HTTP صغيرة على منفذ محلي؛ يقرأ BakoshApp قائمة إجراءاتك ويستدعي أحدها كلما طابق حدث من TikTok أو Kick محفّز المستخدم. أي شيء يمكنك التحكم فيه بالكود — إضاءة ذكية، متحكم دقيق، لوحة أصوات، لعبة أخرى، بوت خاص بك — يمكنه التفاعل مع بث مباشر دون الحاجة إلى مود من BakoshApp.
كيف يترابط كل ذلك
- تستمع أداتك على
http://127.0.0.1:8833. - يقوم المستخدم بتوصيلها من الإعدادات → Third-Party (المضيف والمنفذ) ويتحقق منها بواسطة اختبار الاتصال.
- في تبويب الإجراءات الخاص بمود ما، ينقر المستخدم على إضافة جديد، ويختار محفّزًا، ثم يهيّئ في جميع الإجراءات → خارجية Third-Party Action: يملأ BakoshApp قائمتين بفئاتك وإجراءاتك.
- عند كل حدث مطابق، يستدعي BakoshApp أداتك، ويقوم الكود الخاص بك بالعمل.
الاتصال أحادي الاتجاه: يستدعيك BakoshApp، وليس العكس أبدًا.
المنفذ والعنوان
- 8833 هو الافتراضي. يمكن للمستخدم تغييره من الإعدادات → Third-Party؛ أخبر مستخدميك بالمنفذ الذي يجب إدخاله إذا استخدمت منفذًا آخر.
- لا يمكن استخدام 8832: يستمع BakoshApp نفسه عليه (هذه هي الطريقة التي يجد بها TikFinity برنامج BakoshApp)، ويرفضه الإعداد.
- لا توجد مصادقة. استمع على
127.0.0.1، وليس0.0.0.0، حتى لا يكون الوصول إلى الواجهة ممكنًا من الشبكة. - يستخدم BakoshApp دائمًا بروتوكول HTTP العادي.
نقاط النهاية
يجب أن تقدّم أداتك جميع النقاط الأربع. كل استجابة هي JSON تحتوي على عنصر data.
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 للإجراء المهيّأ. حافظ على ثباتهما: إعادة تسمية معرّف تُعطّل الإعدادات الحالية. الأسماء للعرض فقط ويمكن تغييرها في أي وقت.
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 حتى 5 ثوانٍ لنقاط GET الثلاث و10 ثوانٍ لـ
exec. الطلب الذي يفشل أو تنتهي مهلته لا يُعاد. - إذا كان العدد في الإجراء أكبر من 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 نوع المحفّز الذي هيّأه المستخدم، بحيث يمكن التفريق بين هدية مختارة بالاسم وهدية مطابَقة بالقيمة:
| المعرّف | المُحفِّز |
|---|---|
| 1 | مشاركة |
| 3 | أدنى-أقصى (نطاق قيمة الهدية) |
| 4 | هدية |
| 6 | انضم |
| 7 | إعجاب |
| 9 | متابعة |
| 10 | Subscribe / Super Fan |
| 11 | دردشة |
| 0 | TikFinity، أو أي شيء آخر |
عامل المعرّفات غير المعروفة على أنها 0؛ فقد تُضاف معرّفات جديدة.
زر الاختبار
يستدعي اختبار في صف الإجراء (ومفتاح الاختصار الخاص به) exec ببيانات نموذجية: يكون source وeventType هما test، ويأتي triggerTypeId من محفّز الصف، والمشاهد هو bakosh_app، والهدية هي Rose (giftId 5655). لا يوجد platform.
الإبلاغ عن المشكلات
أعد message لوصف ما حدث من خطأ:
{ "message": "Serial port COM3 is not available" }- في نقاط GET، أي
messageيجعل BakoshApp يعامل الإجابة كخطأ، حتى مع HTTP 200. - في
exec، تُكتبmessageفي وحدة تحكم BakoshApp وملف السجل كتحذير. لا يحصل المستخدم على نافذة منبثقة.
تُحترَم أكواد حالة أخطاء HTTP أيضًا؛ فبدون message، يعرض السجل رمز الحالة فقط.
العمل مع TikFinity أيضًا
تطابق هذه الواجهة واجهة الطرف الثالث الخاصة بـ TikFinity — نفس المسارات الأربعة وأسماء الحقول — لذا يمكن لأداة واحدة أن تخدم كليهما. تختلف المنافذ: يبحث TikFinity عن الأدوات على المنفذ 8832، الذي يشغله BakoshApp أثناء عمله، لذا أعطِ BakoshApp منفذًا آخر.
TikFinity تطبيق ويب ويحتاج إلى ترويسات CORS (Access-Control-Allow-Origin: * وما شابهها) بالإضافة إلى إجابات على طلبات OPTIONS التمهيدية. لا يحتاجها BakoshApp، لكن إرسالها لا يسبب أي ضرر. يرسل BakoshApp حقول context أكثر من TikFinity — تجاهل ما لا تستخدمه.
أداة عاملة كاملة
بدون أي تبعيات — احفظها باسم 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، تحقّق من المضيف والمنفذ، وانقر على اختبار الاتصال. يجب أن ترى اسم أداتك وإصدارها ومؤلفها.
- في تبويب الإجراءات الخاص بمود ما، انقر على إضافة جديد، اختر محفّزًا وانقر على التالي. في جميع الإجراءات → خارجية، انقر على Configure بجانب Third-Party Action، اختر فئتك وإجراءك، واحفظ.
- انقر على اختبار في الصف الجديد — تستقبل أداتك
execبـeventType: "test". - ابدأ البث المباشر وتحقّق من وصول الأحداث الحقيقية.
إذا ظهرت في أداة التهيئة رسالة «تعذّر الاتصال! تحقق من التكامل.»، تحقّق من أن الأداة تستمع على 127.0.0.1، وأن المنفذ يطابق الإعدادات، وأن GET /api/app/info يُعيد إجابته داخل data.