---
read_when:
    - در حال ساخت یا بازآرایی مسیر ارسال یک Plugin کانال پیام‌رسانی هستید
    - به سیاست تحویل پایدار پاسخ نهایی، رسیدها، نهایی‌سازی پیش‌نمایش زنده یا تأیید دریافت نیاز دارید
    - در حال مهاجرت از کمک‌تابع‌های ارسال پیام کانال یا پاسخ قدیمی هستید
summary: 'API چرخهٔ عمر پیام خروجی برای Pluginهای کانال: آداپتورها، رسیدها، ارسال‌های پایدار، پیش‌نمایش زنده و ابزارهای کمکی پایپ‌لاین پاسخ'
title: API خروجی کانال
x-i18n:
    generated_at: "2026-07-27T15:33:44Z"
    model: gpt-5.6
    postprocess_version: locale-links-v1
    prompt_version: 32
    provider: openai
    source_hash: 8edeca81d2e9261f33be1d538153caaea87caedb90dfccac33dd227c924501f1
    source_path: plugins/sdk-channel-outbound.md
    workflow: 16
---

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` تعریف می‌کنند:

```ts
import {
  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 تجزیه می‌کند:

```ts
import { sanitizeForPlainText } from "openclaw/plugin-sdk/channel-outbound";

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` دارد، به‌جای تکرار کد
ارسال، آداپتور پیام را از آن مشتق کنید:

```ts
import { createChannelMessageAdapterFromOutbound } from "openclaw/plugin-sdk/channel-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` استفاده کنید.
