TokElementsالتوثيق
WIDGET SDK / V1
01

البداية

1
اكتب الترميز والتنسيق

تبويبا HTML وCSS يرسمان الويدجت. الإطار شفاف ويطفو فوق الفيديو، فلا ترسم أبدًا خلفية معتمة تملأ الشاشة.

2
تفاعل مع الأحداث

في تبويب JS، اشترك عبر TE.on('gift', fn). الكائن window.TE محمّل مسبقًا، دون استيراد ودون إعداد.

3
اختبر وأضف

استخدم Simulate أو أزرار Fire للمعاينة دون بث، ثم Add to overlay، أو دع Claude يبنيها عبر MCP.

أول ويدجت لك: إشادة بالهدية· عدّل JS، وستتفاعل المعاينة مباشرة
var box = document.getElementById('box');
TE.on('gift', function (ev) {
  box.textContent = ev.user.name + ' sent ' + ev.gift.name + '!';
  box.classList.remove('pop'); void box.offsetWidth; box.classList.add('pop');
});
معاينة حية
02

ربط Claude (MCP)

ابنِ هذه الويدجت وعدّلها مباشرة من Claude Desktop أو Claude Code. أنشئ رمزًا، وأضف الموصل، وسيتمكن Claude من استدعاء هذه الأدوات على حسابك:

get_docsدليل التأليف الكامل مع كل المحفزات والإجراءات (يقرؤه Claude أولًا).
list_widgetsاعرض الويدجت المحفوظة مع معرّفاتها.
get_widgetاجلب html / css / js لويدجت واحدة.
create_widgetأنشئ ويدجت HTML جديدة من اسم + html/css/js.
update_widgetعدّل ويدجت موجودة.
delete_widgetاحذف ويدجت عبر المعرّف.
list_templatesاعرض القوالب المدمجة للنسخ منها.
get_templateاقرأ html/css/js لقالب.
create_from_templateانسخ قالبًا إلى ويدجت جديدة.
list_community_templatesتصفّح الويدجت التي نشرها المجتمع.
install_community_templateثبّت نسخة خاصة قابلة للتعديل من قالب مجتمعي.
جارٍ التحميل…
03

TE SDK

كل ما تفعله الويدجت يمر عبر الكائن العام window.TE.

TE.on(type, fn)

اشترك في حدث مباشر. `type` هو أي محفز من القائمة أدناه؛ و`fn(ev)` تعمل في كل مرة يقع فيها.

TE.on('*', fn)

التقط كل الأحداث. تستقبل `fn(ev, type)` البيانات واسم الحدث.

TE.off(type, fn)

أزل معالجًا مسجّلًا سابقًا.

TE.onGift(name, fn)

يعمل فقط للهدايا التي يطابق اسمها (جزء من النص دون تمييز حالة الأحرف). احذف `name` لالتقاط كل الهدايا.

TE.onSticker(idOrUrl, fn)

يعمل فقط لإيموجي / ملصق مشترك محدد، بمطابقة معرّفه على TikTok أو رابط صورته أو مصدره.

TE.rules.allowUser / allowGift

مرشّحات أهلية قابلة لإعادة الاستخدام للأدوار وقوائم السماح/المنع واسم الهدية والحد الأدنى لقيمة العملات.

TE.metrics

كائن آخر لقطة للبث (viewers، likes، coins، followers، topGifters…). ويصل أيضًا عبر TE.on('metrics', fn).

TE.demo

تكون true حيث تُعرض الويدجت للاستعراض (المعرض، وبطاقات المعاينة، ومفتاح Demo في المنشئ). وتكون false على الأوفرلي المباشر وأثناء الاختبار في المنشئ أو محرر الأوفرلي، حيث يظهر فقط ما تطلقه أنت أو ما يصل مباشرة. كل ويدجت تعرض نفسها هناك: ضع عينات تشبه الأحداث الحقيقية (رسومات هدايا حقيقية، وأسماء، وصور شخصية) داخل كتلة if (TE.demo)، عبر مسار الكود الحقيقي للويدجت. الويدجت التي تحتوي على هذه الكتلة لا تتلقى أحداث معاينة من المضيف، فلا يتكرر شيء مرتين.

TE.defineSettings([...])

صرّح بعناصر تحكم يعدّلها صانع البث؛ وتُرجع القيم الحالية (القيم الافتراضية مدمجة مع اختيارات صانع البث).

TE.settings

كائن قيم الإعدادات الحالية (بالشكل نفسه الذي أرجعته defineSettings).

TE.on('settings', fn)

يعمل عندما يغيّر صانع البث إعدادًا مباشرة؛ أعد العرض بالقيم الجديدة.

TE.state.get/set/increment

قيم ذرّية قائمة على Promise للمجاميع وحالة اللعبة المشتركة.

TE.collection.*

قوائم مشاركين وجداول نقاط يملكها الخادم بانضمام لمرة واحدة: join وincrement وlist وcount وremove وclear.

TE.queue.*

طوابير FIFO محدودة يملكها الخادم للوسائط والطلبات والإجراءات التي يطلقها المشاهدون.

TE.shared.*

أدخل عدة ويدجت في نطاق حالة قناة مسمّى واحد؛ وتبقى الويدجت غير المرتبطة معزولة.

TE.random.draw(name, opts)

اسحب فائزًا أو أكثر من مجموعة بشكل آمن وسجّل النتيجة.

TE.cooldown.claim(scope, user, ms)

احجز فترة انتظار عامة أو لكل مستخدم بشكل ذرّي؛ وتُرجع claimed وretryAfterMs.

TE.timer.*

ابدأ مؤقتًا زمنيًا محفوظًا يصمد أمام إعادة التحميل، وأوقفه مؤقتًا، وأعد ضبطه، واقرأه.

TE.points.trySpend(user, amount, reason)

تحقّق من النقاط واخصمها في معاملة واحدة؛ افحص result.ok دائمًا قبل تشغيل تفاعل مدفوع.

04

المحفزات

أحداث مباشرة يمكنك الاستماع إليها. كل user يحتوي على { id, name, username, avatar, roles }.

TE.on('gift')

أرسل مشاهد هدية. coins = مجموع الكومبو؛ وstreakEnd تعني انتهاء الكومبو.

{ user, gift: { name, image|null, coins, repeat, combo, streakEnd } }
TE.on('follow')

تابع مشاهد الحساب.

{ user }
TE.on('subscribe')

اشترك مشاهد. months = عدد الأشهر المتتالية.

{ user, months }
TE.on('share')

شارك مشاهد البث المباشر.

{ user }
TE.on('chat')

رسالة دردشة. emotes = روابط صور إيموجي المشتركين المضمّنة في هذه الرسالة.

{ user, comment, emotes:[url,…] }
TE.on('like')

أرسل مشاهد إعجابات. count = هذه الدفعة؛ total = المجموع الجاري.

{ user, count, total }
TE.on('join')

دخل مشاهد الغرفة. isTop = انضم أحد أكثر الداعمين.

{ user, isTop }
TE.on('sticker')

إيموجي مشترك أو ملصق على الشاشة أُرسل مباشرة. url = صورة الإيموجي؛ id = معرّفه على TikTok.

{ user|null, id|null, url, source }
TE.on('metrics')

آخر لقطة للبث. يحتفظ بها TE.metrics؛ ويُطلق الحدث كلما تحدّثت. hostNick وhostAvatar هما اسم صانع البث وصورته الشخصية.

{ live, viewers, likes, followers, coins, hostNick, hostAvatar, topGifters:[…], … }
TE.on('milestone')

تم تجاوز رقم مميز (الإعجابات، العملات، المتابعون، المشتركون).

{ metric, value, label }
TE.on('poll')

استطلاع TikTok المباشر الأصلي: أصوات حقيقية من البداية إلى النهاية.

{ state:'start'|'update'|'end', title, options:[{text,votes}], endsAt }
TE.on('battle')

تحديثات مواجهات LinkMic: بطاقات المواجهة، وتذاكر المعجبين، وأحجام الجيوش.

{ card|null, tickets|null, armies|null, battleId|null }
TE.on('rank')

المركز في التصنيف الساعي ولحظات الصعود في الترتيب.

{ rank|null, from|null, to|null, countdown|null }
TE.on('envelope')

ظرف أحمر / صندوق كنز أُسقط في البث المباشر.

{ }
TE.on('pinned')

ثبّت المضيف تعليقًا.

{ text }
TE.on('deleted')

حذف مشرف رسالة دردشة (يطابق id حدث الدردشة السابق).

{ id }
TE.on('streamState')

بدأ البث أو انتهى.

{ live }
TE.on('apiEvent')

حدث مخصص أُرسل عبر Event API. استمع إلى apiEvent أو مباشرة إلى اسمه المخصص.

{ name, data }
TE.on('nowPlaying')

مقطع Spotify الحالي لصانع البث مع قائمة التالي. يُطلق عند الاتصال وكلما تغيّرت الأغنية أو حالة التشغيل أو التقدم أو القائمة. تكون playback فارغة (null) عندما لا يُشغَّل شيء؛ وqueue هي المقاطع القادمة.

{ connected, playback: { isPlaying, progressMs, durationMs, title, artists:[…], album, artwork|null } | null, queue:[{ title, artist, artwork|null, durationMs }] }
هدية أو إيموجي واحد محدد فقط
TE.onGift('Rose', function (ev) { /* … */ });
TE.onSticker('<sticker id>', function (ev) { /* … */ });

إيموجي قناتك موجودة في منشئ الويدجت: افتح Simulate events، واختر Sticker ثم اختر واحدًا. يحمل حدث الاختبار معرّفه الحقيقي ورابط صورته، فيُختبر المعالج المرتبط بأيٍّ منهما بالشيء الحقيقي.

05

الإجراءات

ما يمكن لويدجت فعله كاستجابة: واجهات متصفح عادية، جاهزة للصق.

عرض صورة

أظهر صورة على الشاشة: رسمة الهدية/الإيموجي، أو ملف مرفوع، أو أي رابط. تُزال تلقائيًا بعد ثوانٍ.

// Show an image, then fade it out
TE.on('gift', function (ev) {
  var img = document.createElement('img');
  img.src = ev.gift.image;                 // ← any image URL works
  img.style.cssText = 'position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);max-width:60%';
  document.body.appendChild(img);
  setTimeout(function () { img.remove(); }, 4000);   // ← how long it stays
});
تشغيل صوت

شغّل صوتًا عند حدث. أتِح إعداد sound ليختار صانع البث ملفه الخاص، دون تعديل الكود.

// Play a sound the streamer chose in settings
var s = TE.defineSettings([{ key: 'sfx', label: 'Alert sound', type: 'sound', default: '' }]);
TE.on('settings', function (ns) { s = ns; });
TE.on('gift', function (ev) {
  if (s.sfx) { var a = new Audio(s.sfx); a.volume = 0.8; a.play(); }
});
عرض / تحريك نص

اكتب نصًا ديناميكيًا وأعد تشغيل حركة CSS بتبديل class.

// Announce the event with a CSS pop animation
var box = document.getElementById('box');   // your element in the HTML tab
TE.on('follow', function (ev) {
  box.textContent = ev.user.name + ' followed!';
  box.classList.remove('pop'); void box.offsetWidth; box.classList.add('pop');
});
عرض إحصائية مباشرة

اربط رقمًا على الشاشة بمقياس مباشر، كالمشاهدين والإعجابات والعملات والمتابعين، يتحدّث تلقائيًا.

// Keep a number in sync with the live stream
TE.on('metrics', function (m) {
  document.getElementById('count').textContent = m.viewers.toLocaleString();
});
القراءة بصوت عالٍ (TTS)

انطق حدثًا بميزة تحويل النص إلى كلام في المتصفح. مثالي للإشادة بالهدايا أو المتابعات.

// Text-to-speech shout-out
TE.on('gift', function (ev) {
  var u = new SpeechSynthesisUtterance(ev.user.name + ' sent ' + ev.gift.name);
  speechSynthesis.speak(u);
});
المعاينة (TE.demo)

كل ويدجت تعرض نفسها في المعرض والمنشئ: عينات تشبه الأحداث الحقيقية، برسومات هدايا وأسماء وصور شخصية حقيقية. أثناء البث تكون TE.demo بقيمة false ولا تظهر إلا الأحداث الحقيقية.

// Preview samples through the real code path. Never on a live overlay.
if (TE.demo) {
  var GIFTS = [
    { name: 'Rose', coins: 1, image: 'https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/eba3a9bb85c33e017f3648eaf88d7189~tplv-obj.png' },
    { name: 'Perfume', coins: 20, image: 'https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/20b8f61246c7b6032777bb81bf4ee055~tplv-obj.png' }
  ];
  var NAMES = ['lunaa', 'nightowl_gaming_official'], n = 0;
  function sample() {
    n++;
    onGift({ user: { name: NAMES[n % 2], avatar: window.__TE_AVATAR }, gift: GIFTS[n % 2] });
  }
  sample();
  setInterval(sample, 6000);
}
تذكّر الأشياء (TE.store)

حفظ متزامن بسيط للحالة المرئية. استخدم بيئة التشغيل المعاملاتية للمشاركين والمشتريات والنقاط المشتركة.

// Persistent state: survives OBS reloads
var total = TE.store.get('total', 0);          // read (with default)
TE.on('gift', function (ev) {
  total += ev.gift.coins;
  TE.store.set('total', total);                 // write (auto-saved)
  render();
});
التفاعل مع أوامر الدردشة

أوامر دردشة بمطابقة تامة مع مجموعات ذرّية تتيح مشاركات وتصويتات ودورات عجلة آمنة.

// One server-authoritative entry per viewer.
// A function keyword reads s.command on every message, so the
// streamer can change the command in settings without a reload.
TE.onCommand(function () { return s.command || '!join'; }, function (c) {
  TE.collection.join('entrants', c.user.id, c.user).then(function (result) {
    if (result.joined) renderCount(result.count);
  });
});
إطلاق إجراءات البث (TE.act)

اطلب تنبيهات الأوفرلي أو الأصوات أو TTS أو العدادات أو وقت السباثون أو تبديل مشاهد OBS مباشرة من الويدجت.

// One call, one stream action
TE.act({ id: 'tts', message: 'New high score!' });
TE.act({ id: 'points', user: ev.user, amount: 50 });
منح نقاط المشاهدين

تحقّق من نقاط الولاء أو أنفقها أو امنحها ضمن معاملة، واستلم الرصيد الناتج.

// Only spin after the points were really debited
TE.onCommand(function () { return s.command || '!spin'; }, function (ev) {
  TE.points.trySpend(ev.user, 50, 'Wheel spin').then(function (result) {
    if (result.ok) spin();
    else showMissing(result.missing);
  });
});
بناء ألعاب عادلة

حالة ذرّية، ومجموعات، وسحب عشوائي آمن، وفترات انتظار، ومؤقتات دائمة، آمنة حتى مع تكرار مصادر المتصفح.

// Fair draw from a server-owned entrant collection
TE.random.draw('entrants', { count: 1 }).then(function (result) {
  if (result.ok) reveal(result.winners[0].value);
});
إضافة عناصر تحكم لصانع البث

أتِح النصوص والألوان والأرقام ومفاتيح التبديل، ويعدّلها صانع البث مباشرة في محرر الأوفرلي، دون كود.

// No-code controls for whoever uses the widget
var s = TE.defineSettings([
  { key: 'title',  label: 'Title', type: 'text',  default: 'Goal' },
  { key: 'accent', label: 'Color', type: 'color', default: '#FF2E4D' },
]);
document.body.style.setProperty('--accent', s.accent);
TE.on('settings', function (ns) { s = ns; /* re-render with new values */ });
06

إعدادات صانع البث

صرّح بعناصر التحكم عبر TE.defineSettings([...]) وسيعدّلها كل من يستخدم الويدجت مباشرة في محرر الأوفرلي، دون كود. كل حقل:

الويدجت الموضوعة التي تحتوي على حقل 'button' واحد أو أكثر تعرض تلك الإجراءات في إعداداتها داخل Overlay Editor. ويعرض Widget Builder الأزرار نفسها لاختبار المعاينة بأمان أثناء البرمجة.

النوعيعرضالقيمة
'text'حقل نص من سطر واحدstring
'number'حقل رقمnumber
'color'منتقي الألواننص hex، مثل "#FF2E4D"
'select'قائمة منسدلة (تحتاج options: [...])أحد الخيارات
'toggle'مفتاح تشغيل/إيقافboolean
'range'شريط تمرير (min / max / step)number
'button'زر إجراء تصريحيincrement أو set أو toggle: JSON مُتحقَّق منه، وليس كودًا في لوحة التحكم أبدًا
'sound'منتقٍ من أصوات صانع البث المرفوعة + رفعرابط الملف المختار ("" = لا شيء)
'image'منتقٍ من صور صانع البث المرفوعة + رفعرابط الملف المختار ("" = لا شيء)
var s = TE.defineSettings([
  { key: 'title',  label: 'Title', type: 'text',   default: 'Follower goal' },
  { key: 'target', label: 'Goal',  type: 'number', default: 100 },
  { key: 'accent', label: 'Color', type: 'color',  default: '#FF2E4D' },
  { key: 'reset',  label: 'Reset', type: 'button', default: 0,
    action: { type: 'increment', step: 1 }, confirm: true, tone: 'danger' },
]);
// re-render when the streamer changes something live
var lastReset = s.reset;
TE.on('settings', function (ns) {
  s = ns;
  if (ns.reset !== lastReset) { lastReset = ns.reset; resetCounter(); }
  render();
});

تعريفات عناصر التحكم بيانات يُتحقَّق منها. يقبل action للزر فقط increment أو set أو toggle. ويرفض عارض لوحة التحكم أو يتجاهل HTML وكود الاستدعاء والروابط العشوائية.

07

العزل والقيود

تعمل الويدجت داخل iframe معزول بسياسة أمان محتوى صارمة، فلا يمكن لويدجت معطّلة أو خبيثة أن تمس حسابك أو بقية الصفحة.

مسموح
  • الصور وملفات GIF الخارجية (رسومات هدايا TikTok، الصور الشخصية، أي رابط)
  • Google Fonts + خطوط @font-face الخاصة بك
  • الصوت عبر new Audio(url)
  • الطلبات إلى واجهات TokElements الخاصة (مثل /api/files/…)
  • حركات CSS وSVG وcanvas وWeb Audio
محظور
  • وسوم <script> الخارجية / شبكات CDN
  • fetch / XHR / WebSocket إلى خوادم أخرى
  • ملفات تعريف الارتباط وlocalStorage والوصول إلى الصفحة الأم
  • تحميل حزم npm
08

أمثلة

ويدجت كاملة جاهزة للنسخ واللصق. كل معاينة حية، إذ تُطلق أحداث تجريبية لتشاهدها تتفاعل.

تفاعل بالإيموجي· يُظهر الإيموجي عند إرساله في الدردشة
var img = document.getElementById('emote');
// fires for every emote — use TE.onSticker('<id>', …) for one specific one
TE.on('sticker', function (ev) {
  img.src = ev.url;
  img.classList.remove('show'); void img.offsetWidth; img.classList.add('show');
});
معاينة حية
شريط هدف المتابعين· يربط شريطًا بمقياس مباشر
var target = 200;                       // ← your goal
TE.on('metrics', function (m) {
  var pct = Math.min(100, m.follows / target * 100);
  document.getElementById('fill').style.width = pct + '%';
  document.getElementById('txt').textContent = m.follows + ' / ' + target;
});
معاينة حية