Skip to main content
نشأت هذه الصفحة كمقترح تصميم استشرافي. وقد أُطلق جوهر ذلك التصميم منذ ذلك الحين في src/channels/message/* والمسارين الفرعيين العامين openclaw/plugin-sdk/channel-outbound / channel-inbound. لمعرفة API الحالي، استخدم API الصادر للقنوات و API الوارد للقنوات. تتتبّع هذه الصفحة ما أُطلق، ومواضع اختلاف التنفيذ عن التصور الأصلي، وما لا يزال مفتوحًا.

لماذا حدثت إعادة الهيكلة هذه

نشأت حزمة القنوات من عدة إصلاحات محلية: مساعدات واردة منفصلة لكل مستوى نضج (runtime.channel.inbound.run للمهايئات البسيطة، وruntime.channel.inbound.runPreparedReply للمهايئات الغنية)، ومساعدات قديمة لتوزيع الردود (dispatchInboundReplyWithBase وrecordInboundSessionAndDispatchReply)، وبث معاينة خاص بكل قناة، ومتانة التسليم النهائي المضافة إلى مسارات حمولات الرد الموجودة. نتج عن هذا الشكل عدد مفرط من المفاهيم العامة ومواضع كثيرة جدًا يمكن أن تنحرف فيها دلالات التسليم. فجوة الموثوقية التي فرضت إعادة التصميم:
الثابت المستهدف: بمجرد أن يقرر النواة وجوب وجود رسالة صادرة مرئية، يجب أن تكون نية الإرسال متينة قبل محاولة استدعاء المنصة، ويجب تثبيت إيصال المنصة بعد النجاح. يوفر ذلك استردادًا لمرة واحدة على الأقل افتراضيًا. ولا يتوفر سلوك المرة الواحدة بالضبط إلا عندما يثبت المهايئ وجود خاصية منع التكرار الأصلية أو يطابق محاولة مجهولة النتيجة بعد الإرسال مع حالة المنصة قبل إعادة التشغيل.

ما أُطلق

يوجد النطاق الداخلي في src/channels/message/*: الواجهة العامة: openclaw/plugin-sdk/channel-outbound (مساعدات الإرسال/الإيصال/المتانة/البث المباشر/مسار الرد) وopenclaw/plugin-sdk/channel-inbound (سياق الوارد وrunChannelInboundEvent وdispatchChannelInboundReply). راجع هاتين الصفحتين للاطلاع على أمثلة المهايئات وأسماء الأنواع الحالية وملاحظات الترحيل — فهما مصدر الحقيقة لشكل API، وليس التصورات أدناه.

سياق الإرسال

تمنح withDurableMessageSendContext شيفرة القناة خطوات render وpreviewUpdate وsend وedit وdelete وcommit وfail حول رسالة صادرة واحدة. أما sendDurableMessageBatch فهو غلاف الحالة الشائعة: التصيير ثم الإرسال، ثم التثبيت عند sent/suppressed أو تسجيل الفشل عند حدوث خطأ. تعيد sendDurableMessageBatch نتيجة مميّزة واحدة: تكون المتانة واحدة من required أوbest_effort أو disabled (MessageDurabilityPolicy في src/channels/message/types.ts). يفشل required بشكل مغلق عندما يتعذر تدوين النية المتينة؛ ويتابع best_effort إلى إرسال مباشر عندما لا يتوفر التخزين المستمر؛ بينما يحتفظ disabled بسلوك الإرسال المباشر السابق لإعادة الهيكلة. تستخدم مساعدات التوافق القديمة القيمة disabled افتراضيًا ولا تستنتج required لمجرد أن القناة تحتوي على مهايئ صادر عام. الحد الذي يظل خطيرًا: بعد نجاح استدعاء المنصة وقبل تثبيت الإيصال. إذا توقفت العملية في تلك اللحظة، فلن تتمكن النواة من معرفة ما إذا كانت رسالة المنصة موجودة ما لم يعلن المهايئ reconcileUnknownSend. يصنّف ذلك الخطاف إرسالًا منقطعًا على أنه sent أو not_sent أو unresolved؛ ولا يسمح بإعادة التشغيل إلا not_sent. ترجع القنوات التي لا تدعم المطابقة إلى حالة unknown_after_send (src/channels/message/state.ts وsrc/infra/outbound/delivery-queue-recovery.ts)، وقد تختار إعادة التشغيل لمرة واحدة على الأقل فقط إذا كانت الرسائل المرئية المكررة مقايضة مقبولة وموثقة لتلك القناة.

سياق الاستقبال

يتتبّع createMessageReceiveContext حالة الإقرار/الرفض لكل حدث وارد باستخدام ack() آمنة عند التكرار وnack(error) صريحة. تكون سياسة الإقرار (ChannelMessageReceiveAckPolicy) واحدة من: يستخدم استطلاع Telegram هذا لتخزين علامة مائية آمنة للتحديثات المكتملة (safeCompletedUpdateId في extensions/telegram/src/bot-update-tracker.ts): يواصل grammY ملاحظة كل تحديث عند دخوله سلسلة البرامج الوسيطة، لكن OpenClaw لا يقدّم علامة إعادة التشغيل المخزنة بعد التحديثات إلا إذا أنهت التوزيع، ولذلك تُعاد التحديثات الفاشلة أو التي لا تزال معلقة بعد إعادة التشغيل. ولا يزال إزاحة getUpdates الأصلية في Telegram مملوكة لـ grammY؛ ولم يُبنَ مصدر استطلاع متين بالكامل يتحكم في إعادة التسليم على مستوى المنصة بعد هذه العلامة المائية (راجع الأسئلة المفتوحة).

المعاينة المباشرة

يصوغ src/channels/message/live.ts المعاينة/التحرير/الإنهاء كدورة حياة واحدة: createLiveMessageState وmarkLiveMessagePreviewUpdated وmarkLiveMessageFinalized وmarkLiveMessageCancelled و deliverFinalizableLivePreviewAdapter (إنشاء تحرير نهائي من مسودة، وتطبيقه، والرجوع إلى إرسال عادي عندما يتعذر التحرير أو يفشل). تكون LiveMessageState.phase واحدة من idle | previewing | finalizing | finalized | cancelled؛ وتحدد canFinalizeInPlace ما إذا كان يمكن أن تصبح المعاينة الرسالة النهائية عبر التحرير بدلًا من إرسال جديد.

الإيصالات المتينة

يوحّد MessageReceipt (src/channels/message/types.ts) معرّف رسالة واحدًا أو أكثر من المنصة ناتجًا من إرسال منطقي واحد في platformMessageIds بالإضافة إلى parts لكل جزء (النوع، والفهرس، ومعرّف سلسلة المحادثة، ومعرّف الرسالة المُجاب عنها). ويُحتفظ بمعرّف أساسي لإنشاء سلسلة المحادثة والتحريرات اللاحقة. وهذا ما يجعل عمليات التسليم متعددة الأجزاء (النص مع الوسائط، والنص المجزأ، والرجوع إلى البطاقة) قابلة لإعادة التشغيل وإزالة التكرار بعد إعادة التشغيل.

تقليص SDK العام

استوعبت إعادة الهيكلة أو أوقفت استخدام: مساعدات reply-runtime وreply-dispatch-runtime وreply-reference وreply-chunking وreply-payload المكشوفة بوصفها API عامًا، وinbound-reply-dispatch وchannel-reply-pipeline ومعظم الاستخدامات العامة لـoutbound-runtime. أصبح src/plugin-sdk/channel-message.ts الآن حاوية إعادة تصدير @deprecated تشير إلى channel-outbound / channel-inbound؛ وأزيلت الأسماء المستعارة لوقت تشغيل channel.turn، وأصبحت صفحة التوثيق القديمة /plugins/sdk-channel-turn تعيد التوجيه إلى API الوارد للقنوات. ينبغي لشيفرة Plugin الجديدة استهداف channel-outbound وchannel-inbound مباشرةً.

مواضع اختلاف التنفيذ عن التصميم الأصلي

لم يُطلق تصور التصميم أدناه حرفيًا كما هو موصوف. يُحتفظ بالسجل من أجل الدقة التاريخية؛ فلا تتعامل مع أسماء الأنواع هذه على أنها API حالي.
  • لا يوجد MessageOrigin / shouldDropOpenClawEcho. دعت الخطة الأصلية إلى وسم أصل source: "openclaw" على رسائل فشل Gateway بالإضافة إلى مسند مشترك يتجاهل أصداء الروبوت الموسومة والمؤلفة بواسطة الروبوت في الغرف المشتركة قبل تفويض allowBots. لا يوجد ذلك النوع ولا ذلك المسند في قاعدة الشيفرة. أما allowBots نفسه فهو مفتاح إعداد حقيقي لكل قناة (Slack وDiscord وGoogle Chat وغيرها)، لكن آلية وسم الأصل التي كان يُفترض أن تحميه لم تُبنَ قط. ولا يزال منع صدى فشل Gateway في الغرف التي تتيح الروبوتات فجوة مفتوحة، وليس ضمانًا مُطلقًا.
  • لا توجد مساحة أسماء موحدة core.messages.receive/send/live/state. توجد الدوال المُطلقة مباشرةً في src/channels/message/* (withDurableMessageSendContext وcreateMessageReceiveContext وcreateLiveMessageState وclassifyDurableSendRecoveryState) بدلًا من وجودها خلف واجهة core.messages.*.
  • لا يوجد نوع رسائل موحّد عام ChannelMessage / MessageTarget / MessageRelation. لا تزال النواة تمرر حمولات رد ملموسة (ReplyPayload) وسياقات خاصة بالقنوات عبر مهايئات الإرسال بدلًا من شكل رسالة واحد محايد للمنصة مع علاقة kind: "reply" | "followup" | "broadcast" | "system".
  • تختلف أسماء سياسة الإقرار عن التصور. المُطلق: after_receive_record | after_agent_dispatch | after_durable_send | manual. استخدم التصور الأصلي immediate | after-record | after-durable-send | manual مع حقل لسبب انتهاء مهلة Webhook؛ ولم يُبنَ ذلك الشكل.
  • حلّت مفاتيح إمكانات DurableFinalDeliveryRequirementMap محل كائن MessageCapabilities المتصور. الإمكانات أعلام منطقية مسطحة (text وmedia وpoll وpayload وsilent وreplyTo وthread وnativeQuote وmessageSendingHooks وbatch وreconcileUnknownSend وafterSendSuccess وafterCommit) يجري التحقق منها عبر verifyDurableFinalCapabilityProofs بدلًا من بنية متداخلة على نمط text.chunking / attachments.voice.

مخاطر الترحيل الملموسة (لا تزال ذات صلة)

هذه الآثار الجانبية الخاصة بكل قناة تسبق إعادة الهيكلة ويجب أن تستمر في العمل عبر مسارات الإرسال الجديدة. وهي ليست افتراضية: فكل واحد منها مُنفَّذ ويؤدي دورًا أساسيًا اليوم.
  • iMessage (extensions/imessage/src/monitor/echo-cache.ts, persisted-echo-cache.ts): تسجّل أداة المراقبة الرسائل المرسلة في ذاكرة تخزين مؤقت للصدى بعد نجاح الإرسال. ويجب أن تستمر عمليات الإرسال النهائية الدائمة في ملء ذاكرة التخزين المؤقت تلك، وإلا فقد يعيد OpenClaw إدخال ردوده الخاصة بوصفها رسائل مستخدم واردة.
  • Tlon (extensions/tlon/src/monitor/index.ts): تُلحق توقيعًا اختياريًا للنموذج وتسجّل سلاسل المحادثات التي تمت المشاركة فيها بعد الردود الجماعية. ويجب ألا يتجاوز التسليم الدائم هذه الآثار.
  • Discord وأدوات التوزيع المُعَدّة الأخرى تتولى بالفعل التسليم المباشر وسلوك المعاينة. ولا تكون القناة دائمة من طرف إلى طرف حتى توجّه أداة التوزيع المُعَدّة الخاصة بها الرسائل النهائية صراحةً عبر سياق الإرسال؛ فلا تفترض أن المهايئ العام وحده يوفر التغطية.
  • يجب أن يسلّم التسليم الاحتياطي الصامت في Telegram مصفوفة الحمولة المتوقعة كاملةً، وليس الحمولة الأولى فقط، بعد التجزئة/الإسقاط الاحتياطي.
  • يمكن أن تتضمن مسارات المساعدة في LINE وZalo وNostr وما شابهها معالجة رموز الرد، أو تمرير الوسائط عبر وكيل، أو ذاكرات تخزين مؤقت للرسائل المرسلة، أو أهدافًا لا تعمل إلا عبر ردود النداء. وتظل خاضعة للتسليم الذي تملكه القناة حتى تمثَّل هذه الدلالات في مهايئ الإرسال وتغطيها الاختبارات.
  • يمكن أن تتضمن أدوات الرسائل المباشرة الخاصة رد نداء للرد يكون هدف النقل الصحيح الوحيد. ويجب ألا يخمّن الإرسال الصادر العام هدفًا من حقول المنصة الخام ويتجاوز رد النداء ذاك.

تصنيف الإخفاقات

تصنّف المهايئات إخفاقات النقل ضمن فئات مغلقة على نمط DeliveryFailureKind (عابر، حد المعدل، المصادقة، الإذن، غير موجود، حمولة غير صالحة، تعارض، ملغى، غير معروف). سياسة النواة:
  • أعد المحاولة عند الإخفاقات العابرة وإخفاقات حد المعدل.
  • لا تعد المحاولة عند إخفاقات الحمولة غير الصالحة ما لم يتوفر بديل للتصيير.
  • لا تعد المحاولة عند إخفاقات المصادقة أو الإذن حتى يتغير الإعداد.
  • عند عدم العثور، اسمح للإنهاء المباشر بالانتقال من التعديل إلى إرسال جديد عندما تعلن القناة أن ذلك آمن.
  • عند التعارض، استخدم حالة الإيصال/تكرارية التنفيذ لتحديد ما إذا كانت الرسالة موجودة بالفعل.
  • يتحول أي خطأ يحدث بعد أن تكون استدعاءات المنصة قد نجحت، ولكن قبل تثبيت الإيصال، إلى unknown_after_send ما لم يثبت المهايئ أن عملية المنصة لم تحدث.

أسئلة مفتوحة

  • ما إذا كان ينبغي لـ Telegram أن يستبدل في نهاية المطاف مشغّل الاستقصاء grammY (1.43.0) بمصدر استقصاء دائم بالكامل يتحكم في إعادة التسليم على مستوى المنصة، وليس فقط علامة إعادة التشغيل الدائمة في OpenClaw (safeCompletedUpdateId).
  • ما إذا كان ينبغي لحالة المعاينة المباشرة أن تكون في السجل نفسه الخاص بقصد الإرسال النهائي أم في مخزن شقيق للحالة المباشرة.
  • ما إذا كان منع الصدى عند إخفاق Gateway في الغرف المشتركة المُمكَّن فيها الروبوت يحتاج إلى آلية وسم الأصل التي كان مخططًا لها في الأصل، أو إلى عقد أبسط خاص بكل قناة، أم أنه خارج النطاق.
  • أي القنوات تدعم الأصل/البيانات الوصفية دعمًا أصليًا لمنع الصدى بين الروبوتات، وأيها يحتاج إلى سجل دائم للرسائل الصادرة.

ذو صلة