Plugins
نمایش پیام
ارائهٔ پیام، قرارداد مشترک OpenClaw برای رابط کاربری غنی چت خروجی است. این امکان را به عاملها، فرمانهای CLI، جریانهای تأیید و Pluginها میدهد که هدف پیام را یکبار توصیف کنند، در حالی که هر Plugin کانال بهترین شکل بومی ممکن را رندر میکند.
از ارائه برای رابط کاربری قابلحمل پیام استفاده کنید:
- بخشهای متنی
- متن کوچک زمینه/پاورقی
- جداکنندهها
- دکمهها
- منوهای انتخاب
- عنوان و لحن کارت
فیلدهای بومی جدیدِ ارائهدهنده مانند Discord components، Slack
blocks، Telegram buttons، Teams card یا Feishu card را به ابزار مشترک
پیام اضافه نکنید. اینها خروجیهای رندرکننده هستند که مالکیتشان با Plugin کانال است.
قرارداد
نویسندگان Plugin قرارداد عمومی را از اینجا import میکنند:
MessagePresentation,
ReplyPayloadDelivery,
} from "openclaw/plugin-sdk/interactive-runtime";
شکل:
type MessagePresentation = {
title?: string;
tone?: "neutral" | "info" | "success" | "warning" | "danger";
blocks: MessagePresentationBlock[];
};
type MessagePresentationBlock =
| { type: "text"; text: string }
| { type: "context"; text: string }
| { type: "divider" }
| { type: "buttons"; buttons: MessagePresentationButton[] }
| { type: "select"; placeholder?: string; options: MessagePresentationOption[] };
type MessagePresentationButton = {
label: string;
value?: string;
url?: string;
style?: "primary" | "secondary" | "success" | "danger";
};
type MessagePresentationOption = {
label: string;
value: string;
};
type ReplyPayloadDelivery = {
pin?:
| boolean
| {
enabled: boolean;
notify?: boolean;
required?: boolean;
};
};
معنای دکمهها:
valueمقدار اقدام برنامه است که وقتی کانال از کنترلهای قابل کلیک پشتیبانی میکند، از مسیر تعامل موجود همان کانال بازگردانده میشود.urlدکمهٔ پیوند است. میتواند بدونvalueوجود داشته باشد.labelالزامی است و در جایگزین متنی نیز استفاده میشود.styleراهنماست. رندرکنندهها باید سبکهای پشتیبانینشده را به یک پیشفرض امن نگاشت کنند، نه اینکه ارسال را ناموفق کنند.
معنای انتخاب:
options[].valueمقدار انتخابشدهٔ برنامه است.placeholderراهنماست و ممکن است کانالهایی که پشتیبانی بومی از انتخاب ندارند آن را نادیده بگیرند.- اگر کانالی از انتخابها پشتیبانی نکند، متن جایگزین برچسبها را فهرست میکند.
مثالهای تولیدکننده
کارت ساده:
{
"title": "Deploy approval",
"tone": "warning",
"blocks": [
{ "type": "text", "text": "Canary is ready to promote." },
{ "type": "context", "text": "Build 1234, staging passed." },
{
"type": "buttons",
"buttons": [
{ "label": "Approve", "value": "deploy:approve", "style": "success" },
{ "label": "Decline", "value": "deploy:decline", "style": "danger" }
]
}
]
}
دکمهٔ پیوند فقط با URL:
{
"blocks": [
{ "type": "text", "text": "Release notes are ready." },
{
"type": "buttons",
"buttons": [{ "label": "Open notes", "url": "https://example.com/release" }]
}
]
}
منوی انتخاب:
{
"title": "Choose environment",
"blocks": [
{
"type": "select",
"placeholder": "Environment",
"options": [
{ "label": "Canary", "value": "env:canary" },
{ "label": "Production", "value": "env:prod" }
]
}
]
}
ارسال با CLI:
openclaw message send --channel slack \
--target channel:C123 \
--message "Deploy approval" \
--presentation '{"title":"Deploy approval","tone":"warning","blocks":[{"type":"text","text":"Canary is ready."},{"type":"buttons","buttons":[{"label":"Approve","value":"deploy:approve","style":"success"},{"label":"Decline","value":"deploy:decline","style":"danger"}]}]}'
تحویل سنجاقشده:
openclaw message send --channel telegram \
--target -1001234567890 \
--message "Topic opened" \
--pin
تحویل سنجاقشده با JSON صریح:
{
"pin": {
"enabled": true,
"notify": true,
"required": false
}
}
قرارداد رندرکننده
Pluginهای کانال، پشتیبانی رندر را روی آداپتر خروجی خود اعلام میکنند:
const adapter: ChannelOutboundAdapter = {
deliveryMode: "direct",
presentationCapabilities: {
supported: true,
buttons: true,
selects: true,
context: true,
divider: true,
},
deliveryCapabilities: {
pin: true,
},
renderPresentation({ payload, presentation, ctx }) {
return renderNativePayload(payload, presentation, ctx);
},
async pinDeliveredMessage({ target, messageId, pin }) {
await pinNativeMessage(target, messageId, { notify: pin.notify === true });
},
};
فیلدهای قابلیت عمداً booleanهای ساده هستند. آنها توصیف میکنند رندرکننده چه چیزی را میتواند تعاملی کند، نه همهٔ محدودیتهای بومی هر پلتفرم را. رندرکنندهها همچنان مالک محدودیتهای خاص پلتفرم مانند بیشینهٔ تعداد دکمه، تعداد بلوک و اندازهٔ کارت هستند.
جریان رندر هسته
وقتی یک ReplyPayload یا اقدام پیام شامل presentation باشد، هسته:
- payload ارائه را نرمالسازی میکند.
- آداپتر خروجی کانال هدف را resolve میکند.
presentationCapabilitiesرا میخواند.- وقتی آداپتر بتواند payload را رندر کند،
renderPresentationرا فراخوانی میکند. - وقتی آداپتر وجود نداشته باشد یا نتواند رندر کند، به متن محافظهکارانه fallback میکند.
- payload حاصل را از مسیر معمول تحویل کانال ارسال میکند.
- فرادادهٔ تحویل مانند
delivery.pinرا پس از نخستین پیام موفقِ ارسالشده اعمال میکند.
هسته مالک رفتار fallback است تا تولیدکنندهها بتوانند مستقل از کانال بمانند. Pluginهای کانال مالک رندر بومی و رسیدگی به تعامل هستند.
قواعد تنزل
ارائه باید برای ارسال روی کانالهای محدود امن باشد.
متن fallback شامل این موارد است:
titleبهعنوان خط نخست- بلوکهای
textبهعنوان بندهای معمول - بلوکهای
contextبهعنوان خطهای فشردهٔ زمینه - بلوکهای
dividerبهعنوان جداکنندهٔ بصری - برچسبهای دکمه، شامل URLها برای دکمههای پیوند
- برچسبهای گزینهٔ انتخاب
کنترلهای بومی پشتیبانینشده باید تنزل پیدا کنند، نه اینکه کل ارسال را ناموفق کنند. مثالها:
- Telegram با دکمههای inline غیرفعال، متن fallback ارسال میکند.
- کانالی بدون پشتیبانی از انتخاب، گزینههای انتخاب را بهصورت متن فهرست میکند.
- دکمهٔ فقط URL یا به یک دکمهٔ پیوند بومی تبدیل میشود یا به یک خط URL fallback.
- شکستهای اختیاری سنجاقکردن، پیام تحویلشده را ناموفق نمیکنند.
استثنای اصلی delivery.pin.required: true است؛ اگر سنجاقکردن بهصورت
الزامی درخواست شده باشد و کانال نتواند پیام ارسالشده را سنجاق کند، تحویل شکست را گزارش میکند.
نگاشت ارائهدهنده
رندرکنندههای بستهبندیشدهٔ فعلی:
| کانال | هدف رندر بومی | نکات |
|---|---|---|
| Discord | مؤلفهها و کانتینرهای مؤلفه | برای تولیدکنندههای موجود payload بومیِ ارائهدهنده، channelData.discord.components قدیمی را حفظ میکند، اما ارسالهای مشترک جدید باید از presentation استفاده کنند. |
| Slack | Block Kit | برای تولیدکنندههای موجود payload بومیِ ارائهدهنده، channelData.slack.blocks قدیمی را حفظ میکند، اما ارسالهای مشترک جدید باید از presentation استفاده کنند. |
| Telegram | متن بههمراه صفحهکلیدهای inline | دکمهها/انتخابها برای سطح هدف به قابلیت دکمهٔ inline نیاز دارند؛ در غیر این صورت از متن fallback استفاده میشود. |
| Mattermost | متن بههمراه props تعاملی | بلوکهای دیگر به متن تنزل پیدا میکنند. |
| Microsoft Teams | Adaptive Cards | وقتی هم متن سادهٔ message و هم کارت ارائه شوند، متن نیز همراه کارت درج میشود. |
| Feishu | کارتهای تعاملی | سربرگ کارت میتواند از title استفاده کند؛ بدنه از تکرار آن عنوان پرهیز میکند. |
| کانالهای ساده | متن fallback | کانالهای بدون رندرکننده همچنان خروجی خوانا دریافت میکنند. |
سازگاری payload بومیِ ارائهدهنده، امکانی انتقالی برای تولیدکنندههای reply موجود است. این دلیلی برای افزودن فیلدهای بومی مشترک جدید نیست.
Presentation در برابر InteractiveReply
InteractiveReply زیرمجموعهٔ داخلی قدیمیتری است که توسط کمککنندههای تأیید و تعامل
استفاده میشود. از این موارد پشتیبانی میکند:
- متن
- دکمهها
- انتخابها
MessagePresentation قرارداد canonical مشترک ارسال است. این موارد را اضافه میکند:
- عنوان
- لحن
- زمینه
- جداکننده
- دکمههای فقط URL
- فرادادهٔ عمومی تحویل از طریق
ReplyPayload.delivery
هنگام پلزدن از کد قدیمیتر، از helperهای openclaw/plugin-sdk/interactive-runtime استفاده کنید:
interactiveReplyToPresentation,
normalizeMessagePresentation,
presentationToInteractiveReply,
renderMessagePresentationFallbackText,
} from "openclaw/plugin-sdk/interactive-runtime";
کد جدید باید مستقیماً MessagePresentation را بپذیرد یا تولید کند.
سنجاق تحویل
سنجاقکردن رفتار تحویل است، نه ارائه. بهجای فیلدهای بومی ارائهدهنده مانند
channelData.telegram.pin از delivery.pin استفاده کنید.
معناشناسی:
pin: trueنخستین پیام تحویلشدهٔ موفق را سنجاق میکند.- مقدار پیشفرض
pin.notifyبرابرfalseاست. - مقدار پیشفرض
pin.requiredبرابرfalseاست. - شکستهای اختیاری سنجاقکردن تنزل پیدا میکنند و پیام ارسالشده را دستنخورده باقی میگذارند.
- شکستهای الزامی سنجاقکردن باعث شکست تحویل میشوند.
- پیامهای چندبخشی نخستین بخش تحویلشده را سنجاق میکنند، نه بخش پایانی را.
اقدامهای دستی پیام برای pin، unpin و pins همچنان برای پیامهای موجود
در جایی که ارائهدهنده از آن عملیاتها پشتیبانی میکند وجود دارند.
چکلیست نویسندهٔ Plugin
- وقتی کانال میتواند ارائهٔ معنایی را رندر کند یا بهطور امن تنزل دهد،
presentationرا ازdescribeMessageTool(...)اعلام کنید. presentationCapabilitiesرا به آداپتر خروجی runtime اضافه کنید.renderPresentationرا در کد runtime پیادهسازی کنید، نه در کد راهاندازی Plugin در control-plane.- کتابخانههای UI بومی را از مسیرهای داغ setup/catalog دور نگه دارید.
- محدودیتهای پلتفرم را در رندرکننده و تستها حفظ کنید.
- برای دکمههای پشتیبانینشده، انتخابها، دکمههای URL، تکرار عنوان/متن، و ارسالهای ترکیبی
messageبههمراهpresentationتست fallback اضافه کنید. - پشتیبانی از سنجاق تحویل را فقط وقتی از طریق
deliveryCapabilities.pinوpinDeliveredMessageاضافه کنید که ارائهدهنده بتواند id پیام ارسالشده را سنجاق کند. - فیلدهای کارت/بلوک/مؤلفه/دکمهٔ بومیِ ارائهدهندهٔ جدید را از طریق schema اقدام پیام مشترک expose نکنید.