Plugin maintainer reference
API خروجی کانال
Pluginهای کانال، رفتار پیام خروجی را از
openclaw/plugin-sdk/channel-outbound ارائه میکنند. برای هماهنگسازی
دریافت/زمینه/ارسال از openclaw/plugin-sdk/channel-inbound استفاده کنید.
هسته مالک صفبندی، دوام، پایشگر و تخلیهٔ پایدار ورودی
(createChannelIngressMonitor، createChannelIngressDrain و
openChannelIngressDrain)، سیاست عمومی تلاش مجدد، چرخهٔ عمر پذیرش نوبت
(turnAdoptionLifecycle / bindIngressLifecycleToReplyOptions)، هوکها،
رسیدها و ابزار مشترک message است. Plugin مالک فراخوانیهای بومی
ارسال/ویرایش/حذف، نرمالسازی مقصد، رشتهبندی پلتفرم، نقلقولهای انتخابشده،
پرچمهای اعلان، وضعیت حساب، بازرسی ورودی و کدگذاری محموله،
کلیدهای مسیر، گزارههای غیرقابلتلاشمجدد، مجوز اختیاری جایگزینی
و اثرات جانبی مختص پلتفرم است.
پایشگرهای پایدار ورودی
وقتی یک کانال باید رویدادهای پذیرفتهشدهٔ انتقال را پیش از ارسال
ماندگار کند، از createChannelIngressMonitor(...) استفاده کنید. این مورد، صف ورودی و تخلیهٔ کانال
را با چرخهٔ عمر مشترک پذیرش، نظرسنجی، هرس، تحویل و خاموششدن ترکیب میکند.
تنها زمانی از createChannelIngressDrain(...) سطحپایینتر استفاده کنید که انتقال
مالک قرارداد پذیرش یا پمپاژ اساساً متفاوتی باشد.
گزینههای الزامی عبارتاند از:
| گزینه | قرارداد |
|---|---|
queue |
یک ChannelIngressQueue، یا کارخانهای تنبل که صف در محدودهٔ حساب را باز میکند. |
inspect(raw, context) |
eventId پایدار و laneKey سریالشده را برمیگرداند، یا برای رویدادی نادیدهگرفتهشده null را بازمیگرداند. واقعیتهای زمان مطالبه باید با شناسه و مسیر ماندگارشده مطابقت داشته باشند. |
payload |
نسخهٔ محموله را همراه با سریالسازی/سریالزدایی بدنه فراهم میکند. برای پوش استاندارد رشتهای { version, rawEvent } از storage: "raw-event" استفاده کنید، یا برای یک شکل موجود و مختص کانال، فراخوانهای سفارشی کدگذاری/کدگشایی ارائه دهید. createClaimError نسخههای نامعتبر یا هویت تغییرکرده را طبقهبندی میکند. |
deliver(raw, lifecycle, claim) |
یک رویداد کدگشاییشده را ارسال میکند و چرخهٔ عمر کامل پذیرش را دریافت میکند. ممکن است completed، deferred، failed-retryable یا هیچچیز را برگرداند. |
pollIntervalMs |
هنگام اجرای پایشگر، نظرسنجیهای بازیابی/تخلیه را زمانبندی میکند. |
retention |
تناوب هرس و TTL و سقف تعداد ورودیهای تکمیلشده/ناموفق را فراهم میکند. |
پایشگر پذیرشها را سریال میکند تا پسنشینی افزودن نتواند ترتیب یک مسیر را معکوس کند.
تأخیرهای محدود پیشفرض افزودن، 0، 100 و 300 میلیثانیهاند؛ تمامشدن
فرصتها، بهجای ارسال رویدادی که پایدار نشده است،
فراخوان انتقال را رد میکند. در زمان مطالبه، محمولهٔ نسخهدار را کدگشایی میکند، inspect را
دوباره اجرا میکند و پیش از تحویل، عدم تطابق شناسه یا مسیر را رد میکند.
deliver، onAdopted، onDeferred، onAdoptionFinalizing،
onAbandoned و abortSignal را دریافت میکند. بازگشت بدون واگذاری صریح، یک
رویداد نهاییِ بدون ارسال را پذیرفتهشده علامت میزند. admission همیشه exclusive است. یک
واگذاری معوق، مطالبه را نگه میدارد، درحالیکه خاموششدن یا لغو، کار پذیرفتهنشده
را قابلتلاشمجدد باقی میگذارد. پایشگر، تحویل را مستقل از تسویهٔ مطالبه
پیگیری میکند، زیرا پذیرش میتواند پیش از بازگشت promise تحویل کانال،
یک ردیف را سنگقبرگذاری کند.
تنظیمات اختیاری شامل تأخیرهای سفارشی افزودن، یک بلوک گزینهٔ drain برای
سیاست پیشرفتهٔ ترتیب/همزمانی/تلاش مجدد تخلیه، یک abortSignal خارجی، یک
ساعت، گزارش خطای پمپاژ، کارخانهٔ خطای توقف و سیاست پذیرش است.
پایشگر بازگشتی، admit، start، pause، stop، waitForIdle،
isRunning و isStopped را ارائه میکند. stop ابتدا پذیرشهای پذیرفتهشده را تسویه میکند، سپس
تخلیه را لغو و آزاد میکند، منتظر پمپاژ و تحویلهای فعال میماند و
دوباره آن را آزاد میکند تا رقابت ایجاد تنبل بسته شود.
حذف اطلاعات حساس مختص انتقال، اعتبارسنجی پوش خام، طبقهبندی
غیرقابلتلاشمجدد و شکل محمولهٔ ماندگارشده را در Plugin نگه دارید. انتقالهای Webhook
باید تنها پس از برطرفشدن admit تأیید کنند؛ انتقالهای غیرقابلبازپخش باید
بهجای ارسال بیسروصدای رویداد، تمامشدن فرصتهای افزودن پایدار را آشکار کنند.
آداپتور
بیشتر Pluginها یک آداپتور message تعریف میکنند:
defineChannelMessageAdapter, createMessageReceiptFromOutboundResults,} from "openclaw/plugin-sdk/channel-outbound"; export const demoMessageAdapter = defineChannelMessageAdapter({ id: "demo", durableFinal: { capabilities: { text: true, replyTo: true, thread: true, messageSendingHooks: true, }, }, send: { text: async ({ cfg, to, text, accountId, replyToId, threadId, signal }) => { const sent = await sendDemoMessage({ cfg, to, text, accountId: accountId ?? undefined, replyToId: replyToId ?? undefined, threadId: threadId == null ? undefined : String(threadId), signal, }); return { receipt: createMessageReceiptFromOutboundResults({ results: [{ channel: "demo", messageId: sent.id, conversationId: to }], kind: "text", threadId: threadId == null ? undefined : String(threadId), replyToId: replyToId ?? undefined, }), }; }, },});تنها قابلیتهایی را اعلام کنید که انتقال بومی واقعاً حفظ میکند. هر قابلیت اعلامشدهٔ ارسال، رسید، پیشنمایش زنده و تأیید دریافت را با کمککنندههای قرارداد صادرشده از این زیربخش پوشش دهید.
جلوگیری از بازتاب خروجی
وقتی ممکن است یک پلتفرم پیام خروجی خود Plugin را دوباره بهعنوان ورودی تحویل دهد، recordOutboundMessageIdentity(...) را با کانال، حساب، مکالمه و یک هویت پایدار پیام یا منبع پلتفرم فراخوانی کنید. مسیر مشترک نوبت ورودی، هویتهای منطبق را در یک بازهٔ محدود 30 ثانیهای، پیش از ثبت نشست یا ارسال به عامل، کنار میگذارد؛ هویت منبع را میتوان پیش از ارسال رزرو کرد یا هنگام حذف مسیر کانال تازهسازی کرد تا رقابتهای تحویل بسته شوند. isRecentOutboundMessageIdentity(...) همان پرسوجو را برای عیبیابی و آزمونهای کانال ارائه میکند. برای همان هویت پایدار، یک کش TTL موازی و محلیِ کانال نگهداری نکنید.
پاکسازی متن ساده
وقتی یک آداپتور خروجی باید تگهای قالببندی HTML پشتیبانیشده را به
نشانهگذاری متنی سبک تبدیل کند، از sanitizeForPlainText(...) استفاده کنید. حالت پیشفرض،
نشانگرهای موجودِ پررنگ و خطخورده به سبک چت را حفظ میکند. تنها زمانی
{ style: "markdown" } را ارسال کنید که کانال نتیجه را دوباره بهعنوان Markdown تجزیه میکند:
const chatText = sanitizeForPlainText(text);const markdownText = sanitizeForPlainText(text, { style: "markdown" });سبک Markdown از **bold** و ~~strikethrough~~ استفاده میکند؛ حروف کج و کد درونخطی
در هر دو سبک، _italic_ و نشانگرهای بکتیک را حفظ میکنند. سبک را در
مرز کانال انتخاب کنید، نه با بازنویسی متن نشانگر پس از پاکسازی.
شواهد تحویل
یک MessageReceipt نتیجهٔ بازگشتی آداپتور کانال را ثبت میکند. شناسههای مشخص
پیام پلتفرم نشان میدهند که مسیر ارسال پلتفرم پیام را پذیرفته است؛ آنها
ثابت نمیکنند که دستگاه گیرنده آن را نمایش داده یا خوانده است.
رسیدهای بدون شناسهٔ پیام پلتفرم، فقط فرادادهٔ رسید محلی هستند.
کانالهای دارای رسید خواندن یا وضعیت تحویل به دستگاه باید این واقعیتها را
ازطریق مسیری جداگانه و مختص کانال پیگیری کنند.
اگر آداپتور کانال بتواند ثابت کند که تلاش مجدد برای یک شکست نمیتواند
ارسال قابلمشاهده برای گیرنده را تکراری کند و هیچ فراخوانی قادر به نهاییسازی آغاز نشده است،
new PlatformMessageNotDispatchedError("...", { cause: error }) را از
openclaw/plugin-sdk/error-runtime پرتاب کنید. سپس هسته میتواند شواهد کهنهٔ تلاش
ارسال را پاک کند و قصد صفشده را با ایمنی دوباره امتحان کند. تنها آداپتوری که مالک
مرز نهایی ارسال است میتواند این ادعا را مطرح کند. هرگز پس از آغاز فراخوانی
نهاییسازی/ارسال یا بازگشت نتیجهای مبهم از نشانگر استفاده نکنید؛ علامتگذاری نادرست میتواند
پیامها را تکراری کند.
آداپتورهای خروجی موجود
اگر کانال از قبل یک آداپتور سازگار outbound دارد، بهجای تکرار کد
ارسال، آداپتور پیام را از آن مشتق کنید:
export const messageAdapter = createChannelMessageAdapterFromOutbound({ id: "demo", outbound, durableFinal: { capabilities: { text: true, media: true, }, },});ارسالهای پایدار
کمککنندههای ارسال زمان اجرا نیز در channel-outbound قرار دارند:
sendDurableMessageBatch(...)withDurableMessageSendContext(...)deliverInboundReplyWithMessageSendContext(...)- کمککنندههای پخش جریانی/پیشرفت پیشنویس مانند
resolveChannelDraftStreamingChunking(...)
sendDurableMessageBatch(...) یک نتیجهٔ صریح را برمیگرداند:
| نتیجه | معنا |
|---|---|
sent |
دستکم یک پیام قابلمشاهدهٔ پلتفرم توسط مسیر ارسال پلتفرم پذیرفته شده است |
suppressed |
هیچ پیام پلتفرمی نباید مفقود تلقی شود |
partial_failed |
دستکم یک پیام پلتفرم پیش از شکست محموله یا اثر جانبی بعدی پذیرفته شده است |
failed |
هیچ رسید پلتفرمی تولید نشده است |
وقتی یک دسته شامل محمولههای ارسالشده، سرکوبشده و ناموفق است، از payloadOutcomes
استفاده کنید. لغو توسط هوک را از یک نتیجهٔ خالی و قدیمیِ
تحویل مستقیم استنباط نکنید.
پذیرش تحویل معوق
وقتی یک حساب حلشده نمیتواند با ایمنی ارسال خروجی مدیریتشده توسط هسته
یا تحویل معوق را بپذیرد، از message.durableFinal.admitDeferredDelivery(...) استفاده کنید. هسته
این هوک را پیش از کار خروجی زنده، ازجمله مسیرهایی که ماندگاری صف را رد میکنند،
بهصورت همگام فراخوانی میکند و پیش از بازپخش قصد بازیابیشده نیز دوباره آن را فراخوانی میکند. زمینه
شامل cfg، channel، to، accountId و یک phase از live یا
recovery است.
برای ادامه، { status: "allowed" } را برگردانید. وقتی تحویل نباید
ماندگار، مستقیماً ارسال یا بازپخش شود،
{ status: "permanent_rejection", reason } را برگردانید. رد زنده پیش از ایجاد صف،
هوکهای پیام یا کار پلتفرم شکست میخورد. رد بازیابی، رکورد
صفشده را ناموفق علامت میزند و تطبیق و بازپخش را رد میکند. حذف هوک
بهمعنای مجازبودن است.
هوک یک تصمیم پذیرش همگام است، نه مسیری برای ارسال. فقط پیکربندی یا وضعیت زمان اجرایی را که از قبل بارگذاری شده است بخوانید؛ هیچگونه ورودی/خروجی ناهمگام شبکه، سامانه فایل یا موارد دیگر را انجام ندهید. آزمونهای قرارداد باید هر دو مرحله و هر دو گونه نتیجه را از طریق ChannelMessageDurableFinalAdapter از openclaw/plugin-sdk/channel-outbound آزمایش کنند.
توزیع سازگاری
توزیع پاسخ ورودی را از طریق dispatchChannelInboundReply(...) از channel-inbound سرهمبندی کنید. تحویل پلتفرم را در آداپتور تحویل نگه دارید؛ برای آداپتورهای پیام، ارسالهای ماندگار، رسیدها، پیشنمایش زنده و گزینههای پایپلاین پاسخ از channel-outbound استفاده کنید.