Zrzur المطوّرون

البوابة

البوابة اتصال WebSocket يدفع الأحداث إلى بوتك. أبقِ اتصالًا واحدًا مفتوحًا لكل عملية بوت.

ما تحتاج إليه

يتضمن Node 22 أو أحدث WebSocket جاهزًا، ويصلح أي عميل WebSocket آخر. تحتاج إلى رمز بوتك (YOUR_BOT_TOKEN).

الاتصال

wss://events.zrzur.com/?version=1&format=json
معامل الاستعلامالقيمالافتراضي
version11
formatjson أو msgpackjson
tokenرمز بوتك، بدل Authenticateلا شيء
intentsمجموع نيّات الاتصال، بدل الحقل في Authenticateانظر أدناه
readyالحقول المطلوبة في Ready، مثل ready=servers&ready=channelsكل شيء

مع format=msgpack يرسل الطرفان إطارات ثنائية.

استخدم Authenticate بدل ?token=، فالعناوين تنتهي في السجلات. أرسله خلال 15 ثانية:

{ "type": "Authenticate", "token": "YOUR_BOT_TOKEN", "intents": 33 }

يجيب السيرفر بهذا الترتيب:

  1. Authenticated: الرمز صحيح.
  2. Ready: بياناتك الأولية. لا تبدأ الأحداث إلا بعده.

كل ما ترسله بعد ذلك محدود بـ1 ميبيبايت للرسالة الواحدة.

حين ترفضك البوابة

ما يصلكالسببما تفعله
{ "type": "Error", "data": { "type": "InvalidSession" } } ثم يُغلق الاتصالرمز خاطئ أو أُعيد تعيينه، أو بوت معلّق، أو لا Authenticate خلال 15 ثانيةتوقف. لا تُعد الاتصال في حلقة
{ "type": "Error", "data": { "type": "MissingIntent", "intent": "SERVER_MEMBERS" } } ثم إغلاق بالرمز 4014نية خاصة معطلة. يذكرها سبب الإغلاق، مثل Disallowed intents: MESSAGE_CONTENT (switched on: none)فعّلها في بوابة المطورين ثم اتصل مجددًا، أو اتصل دونها
{ "type": "Error", "data": { "type": "InternalError" } } ثم إغلاق بالرمز 1013تعذّر على الخادم قراءة إعدادات البوت في تلك اللحظةاتصل مجددًا، بالانتظار المذكور في إعادة الاتصال

ابقَ متصلًا

أرسل Ping كل 20 ثانية:

{ "type": "Ping", "data": 1730000000000 }

يجيب السيرفر بـPong مع data نفسها. وdata رقم أو قائمة بايتات.

لا تغلق بوابة زرزور نفسها اتصال بوت هادئًا، لكن الوسطاء وموجّهات المنازل يفعلون، والخط الميت لا يعطي أي إشارة. لذلك فالنبضة وسيلتك أنت للاكتشاف: إذا لم يصل Pong خلال 60 ثانية، أغلق المقبس واتصل مجددًا.

إعادة الاتصال

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

  1. عند إغلاق المقبس انتظر ثانية واحدة، وضاعف الانتظار بعد كل فشل حتى 30 ثانية. أضف قليلًا من العشوائية كي لا تعود البوتات كلها معًا.
  2. اتصل وأرسل Resume إن كانت الجلسة قابلة للاستئناف (انظر الاستئناف بعد انقطاع)، وإلا فأرسل Authenticate. بعد Authenticate يصلك Ready جديد.
  3. أعد ضبط الانتظار بعد Ready أو Resumed.
  4. لا تُعد المحاولة بعد حدث Error من النوع InvalidSession، ولا بعد الرمز 4014: سيفشل الطلب نفسه مجددًا. توقف وأخبر شخصًا. أما بعد الرمز 1013 فأعد المحاولة: تعذّر على الخادم قراءة إعدادات البوت في تلك اللحظة.

الاستئناف بعد انقطاع

تستطيع الجلسة أن تكمل من حيث توقفت، فلا يضيع شيء في انقطاع قصير. اطلب ذلك عند المصادقة:

{
  "type": "Authenticate",
  "token": "YOUR_BOT_TOKEN",
  "intents": 33,
  "resumable": true
}

عندها يحمل Ready الحقل session_id. وتحمل كل حزمة بعد Ready الحقل seq، وهو عدّاد لكل جلسة يبدأ من 1: احتفظ بآخر رقم عالجته. لحزمة Bulk رقم seq واحد، وليس لعناصرها أرقام. ولا يحمل Authenticated وReady وPong وError وInvalidSession وResumed الحقل seq.

بعد الانقطاع تُبقي البوابة الجلسة دقيقتين، ومعها آخر 1000 حدث على الأكثر وبحجم لا يتجاوز 4 ميبيبايت. افتح مقبسًا جديدًا وأرسل هذه الرسالة أولًا، بدلًا من Authenticate:

{
  "type": "Resume",
  "token": "YOUR_BOT_TOKEN",
  "session_id": "YOUR_SESSION_ID",
  "seq": 41
}
ما يصلكالمعنىما تفعله
الأحداث الفائتة بالترتيب، ثم Resumedالجلسة مستمرة. لا يصل Ready جديدتابع عملك، واستمر في عدّ seq
{ "type": "InvalidSession", "resumable": false }زالت الجلسة، أو لم يعد ما فاتك يتسع في المحفوظأرسل Authenticate على المقبس نفسه، ثم استدرك ما فاتك عبر REST

قد تظهر فجوات في seq عند الإعادة: أحداث الكتابة لا تُحفظ، وأحداث الأماكن التي لم يعد البوت يراها تُحذف. تُبقي البوابة 3 جلسات منقطعة على الأكثر لكل بوت. ورفض Resume مرة ثانية على المقبس نفسه يغلقه.

دون "resumable": true لا يُحفظ شيء، والأحداث المرسلة في أثناء غيابك تضيع. بعد Ready جديد اجلب عبر REST ما لا يجوز أن يفوت بوتك، مثل الرسائل الأخيرة في قناة (GET /channels/{target}/messages مع after وsort=Oldest، انظر الرسائل). الأمر المستخدم في أثناء غياب البوت يفشل عند من استخدمه، ويستطيع إعادة المحاولة.

التجزئة غير مدعومة

يستقبل الاتصال كل ما تغطيه نيّات الاتصال. لا سبيل إلى توزيع السيرفرات بين الاتصالات. لا يمكن بعد تقسيم عملية بوت كبرت كثيرًا، فأبقِ العمل لكل حدث صغيرًا، وانقل العمل البطيء إلى طابور. انظر التشغيل الفعلي.

يعمل اتصالان بالرمز نفسه، ويستقبل كلاهما كل شيء. عندها يصل InteractionCreate إلى الاثنين، فلا تشغّل اتصالين إلا في أثناء إعادة التشغيل.

حزم Bulk

قد تصل عدة أحداث في حزمة واحدة:

{
  "type": "Bulk",
  "v": [{ "type": "ServerMemberUpdate" }, { "type": "InteractionCreate" }]
}

افتح Bulk دائمًا وعالج كل حدث. البوت الذي يتجاهلها تفوته أحداث، منها استخدام الأوامر.

ما تستطيع إرساله

MessageFields
Authenticatetoken, intents, resumableرسالتك الأولى. token هو رمز البوت، وintents مجموع نيّات الاتصال التي تريدها. مع resumable: true يمكن استئناف الجلسة بعد انقطاع، ويحمل Ready الحقل session_id.
Resumetoken, session_id, seqالرسالة الأولى على مقبس جديد بعد انقطاع، بدلًا من Authenticate. تصل بالترتيب الأحداث التي تلي seq وما زال يحق لك رؤيتها، ثم Resumed. إن زالت الجلسة تتلقى InvalidSession.
BeginTypingchannelيُظهر البوت وهو يكتب في channel. أعد إرساله كل بضع ثوانٍ لإبقائه ظاهرًا.
EndTypingchannelيوقف إظهار الكتابة في channel.
Subscribeserver_idاطلب تحديثات الحضور والأعضاء في server_id. يحتاج البوت إلى SERVER_MEMBERS أو PRESENCE ليرى هذه التحديثات.
Pingdata, respondedيختبر الخط. data رقم من اختيارك ويعود في Pong.

نيّات الاتصال

تحدد نيّات الاتصال (intents) ما يستقبله الاتصال. تطبقها البوابة، فلا يصل البوت أبدًا ما لم يطلبه. اجمع أرقام النيّات التي تريدها وأرسل المجموع. دون intents يحصل البوت على كل النيّات المجانية، أي 63.

النيةالقيمةما يصلك
SERVERS1أحداث السيرفرات والقنوات والأدوار والرموز التعبيرية والويب هوك، والسيرفرات وقنواتها في Ready. تصل الرسائل الخاصة والمجموعات في كل حال، في Ready وفي ChannelCreate
SERVER_MESSAGES2الرسائل والكتابة في قنوات السيرفرات
DIRECT_MESSAGES4الرسائل والكتابة في الرسائل الخاصة والمجموعات
REACTIONS8إضافة التفاعلات وإزالتها
VOICE_STATES16دخول الناس القنوات الصوتية وخروجهم، والمكالمات، وتغيّرات المنصّة، وتشغيل أصوات لوحة الأصوات، وحالات الصوت في Ready
INTERACTIONS32InteractionCreate
MESSAGE_CONTENT1024خاصة. نص الرسائل وتضميناتها وملفاتها ومكوّناتها
SERVER_MEMBERS2048خاصة. انضمام الأعضاء ومغادرتهم وتغيرهم، وقائمة الأعضاء
PRESENCE4096خاصة. من المتصل، وحالته

البوت الذي يجيب عن الأوامر فقط يحتاج إلى SERVERS + INTERACTIONS = 33. دون INTERACTIONS لا يسمع بوتك الأوامر.

تُفعَّل النيّات الخاصة للبوت في بوابة المطورين. وطلب نية معطلة يغلق الاتصال بالرمز 4014. بعد 100 سيرفر لا تبقى إلا النيّات الخاصة التي وافق عليها زرزور: تُستبعد الأخرى من الاتصال دون إغلاقه، وتنطبق قواعد REST أدناه.

القواعد نفسها تسري على REST:

يسري تغيير نيّات البوت في اتصاله التالي.

حين تكون نية خاصة معطلة

يُحفظ المفتاح في بوابة المطورين لحظة تقلبه، وتؤكد البوابة ذلك. تقرأ البوابة المفاتيح حين يبدأ الاتصال، فيحتفظ الاتصال المفتوح بالنيّات التي بدأ بها: أغلق المقبس واتصل مجددًا. يبقى الرمز كما هو. أما REST فيقرؤها مع كل طلب، فتتبع إجاباته المفتاح فورًا.

يسمّي MissingIntent النيّات المعطلة. إذا كان بوتك يعمل دون إحداها، فاتصل مجددًا دونها بدل أن تتوقف:

const MESSAGE_CONTENT = 1024;
// SERVERS + SERVER_MESSAGES + DIRECT_MESSAGES + INTERACTIONS + MESSAGE_CONTENT
let intents = 1 + 2 + 4 + 32 + MESSAGE_CONTENT;

function connect() {
  const socket = new WebSocket("wss://events.zrzur.com/?version=1&format=json");
  socket.addEventListener("open", () =>
    socket.send(
      JSON.stringify({
        type: "Authenticate",
        token: process.env.ZRZUR_BOT_TOKEN,
        intents,
      }),
    ),
  );
  socket.addEventListener("message", ({ data }) => {
    const event = JSON.parse(data);
    if (event.type === "Error" && event.data.type === "MissingIntent")
      console.warn(`Switched off in the Developer Portal: ${event.data.intent}`);
  });
  socket.addEventListener("close", ({ code }) => {
    if (code === 4014 && intents & MESSAGE_CONTENT) {
      // go on without message text: mentions and direct messages keep theirs
      intents &= ~MESSAGE_CONTENT;
      connect();
    }
  });
}

connect();

دون MESSAGE_CONTENT يظل بوتك يقرأ الرسائل التي تذكره، ورسائله هو، والرسائل الخاصة معه. وأضف حلقة إعادة الاتصال من إعادة الاتصال لكل إغلاق آخر.

مخطط قابل للقراءة آليًا

يعيد GET /gateway/schema من الواجهة كل حدث (server) وكل ما يستطيع العميل إرساله (client) في صورة JSON Schema، مع نيّات الاتصال. وهو مولّد من الأنواع التي تستخدمها البوابة.

مثال كامل

في البوت الكامل كل هذا: نيّات الاتصال، والنبضة، والمراقبة، وBulk، والانتظار المتزايد، والحالتان اللتان يتوقف فيهما.

التالي