Building plugins
Создание плагинов каналов
Это руководство описывает создание канального плагина, который подключает OpenClaw к платформе обмена сообщениями: безопасность личных сообщений, сопряжение, ветки ответов и исходящие сообщения.
За что отвечает ваш плагин
Канальные плагины не реализуют инструменты отправки, редактирования и реакций; ядро предоставляет один
общий инструмент message. Ваш плагин отвечает за:
- Конфигурацию — определение учётной записи и мастер настройки
- Безопасность — политику личных сообщений и списки разрешений
- Сопряжение — процесс подтверждения личных сообщений
- Грамматику сеансов — сопоставление идентификаторов бесед конкретного провайдера с базовыми чатами, идентификаторами веток и резервными родительскими значениями
- Исходящие сообщения — отправку текста, медиафайлов и опросов на платформу
- Ветки — распределение ответов по веткам
- Индикатор набора Heartbeat — необязательные сигналы набора текста или занятости для целевых объектов доставки Heartbeat
Ядро отвечает за общий инструмент сообщений, подключение к промптам, внешнюю структуру ключа сеанса,
общее ведение данных :thread: и диспетчеризацию.
Адаптер сообщений
Предоставьте адаптер message с defineChannelMessageAdapter из
openclaw/plugin-sdk/channel-outbound. Объявляйте только устойчивые возможности окончательной отправки,
которые действительно поддерживает ваш нативный транспорт, и подкрепляйте их контрактным
тестом, подтверждающим нативный побочный эффект и возвращаемую квитанцию. Для отправки текста и медиафайлов
используйте те же транспортные функции, что и устаревший адаптер outbound. Полное описание
контракта API, матрицы возможностей, правил квитанций, завершения интерактивного предпросмотра,
политики подтверждения получения, тестов и таблицы миграции см. в разделе
API исходящих сообщений канала.
Если существующий адаптер outbound уже содержит подходящие методы отправки и
метаданные возможностей, создайте адаптер message с помощью
createChannelMessageAdapterFromOutbound(...), а не пишите ещё один
мост вручную. Отправки через адаптер возвращают значения MessageReceipt. Для устаревших идентификаторов формируйте
их с помощью listMessageReceiptPlatformIds(...) или
resolveMessageReceiptPrimaryId(...), а не храните параллельные поля messageIds.
Точно объявляйте возможности интерактивного режима и финализатора: ядро использует их, чтобы определить возможности канала, а расхождение между объявленным и фактическим поведением приводит к ошибке контрактного теста:
| Поверхность | Значения |
|---|---|
message.live.capabilities |
draftPreview, previewFinalization, progressUpdates, nativeStreaming, quietFinalization |
message.live.finalizer.capabilities |
finalEdit, normalFallback, discardPending, previewReceipt, retainOnAmbiguousFailure |
Каналы, которые финализируют черновой предпросмотр на месте, должны направлять логику среды выполнения
через defineFinalizableLivePreviewAdapter(...) вместе с
deliverWithFinalizableLivePreviewAdapter(...) и подкреплять объявленные
возможности тестами verifyChannelMessageLiveCapabilityAdapterProofs(...)
и verifyChannelMessageLiveFinalizerProofs(...), чтобы нативный предпросмотр,
ход выполнения, редактирование, резервное поведение или сохранение, очистка и работа квитанций не могли незаметно
отклониться от контракта.
Обработчики входящих сообщений, которые откладывают подтверждения платформы, должны объявлять
message.receive.defaultAckPolicy и supportedAckPolicies, а не скрывать
момент подтверждения в локальном состоянии монитора. Покройте каждую объявленную политику
тестом verifyChannelMessageReceiveAckPolicyAdapterProofs(...).
Устаревшие вспомогательные функции ответов, такие как createChannelTurnReplyPipeline,
dispatchInboundReplyWithBase и recordInboundSessionAndDispatchReply,
остаются доступными для диспетчеров совместимости. Не используйте их в новом
коде канала; вместо этого начинайте с адаптера message, квитанций и вспомогательных функций
жизненного цикла приёма и отправки в openclaw/plugin-sdk/channel-outbound.
Получение входящих сообщений (экспериментальная возможность)
Каналы, переносящие авторизацию входящих сообщений, могут использовать экспериментальный
подпуть openclaw/plugin-sdk/channel-ingress-runtime из путей приёма среды выполнения.
Он принимает сведения платформы, необработанные списки разрешений, дескрипторы маршрутов, сведения
о командах и конфигурацию групп доступа, а затем возвращает проекции отправителя, маршрута, команды и активации,
а также упорядоченный граф входящей обработки, при этом поиск данных платформы и побочные
эффекты остаются в плагине. Сохраняйте нормализацию идентификаторов плагина в
дескрипторе, передаваемом средству разрешения; не сериализуйте необработанные значения сопоставления
из разрешённого состояния или решения. Описание структуры API,
границ ответственности и требований к тестам см. в разделе
API входящей обработки канала. Более старый
подпуть openclaw/plugin-sdk/channel-ingress по-прежнему экспортируется как устаревший
фасад совместимости для сторонних плагинов.
Индикаторы набора текста
Если ваш канал поддерживает индикаторы набора текста вне контекста ответов на входящие сообщения, предоставьте
heartbeat.sendTyping(...) в канальном плагине. Ядро вызывает его с
определённым целевым объектом доставки Heartbeat до запуска модели Heartbeat и
использует общий жизненный цикл поддержания и очистки индикатора набора текста. Добавьте
heartbeat.clearTyping(...), если платформе требуется явный сигнал остановки.
Параметры источников медиафайлов
Если канал добавляет в инструмент сообщений параметры, содержащие источники медиафайлов, предоставьте
имена этих параметров через plugin.actions.describeMessageTool(...).mediaSourceParams.
Ядро использует этот явный список для нормализации путей песочницы и политики
доступа к исходящим медиафайлам, поэтому плагинам не нужны специальные случаи в общем ядре для
параметров аватаров, вложений или обложек, специфичных для провайдера.
Предпочитайте карту с ключами действий, например { "set-profile": ["avatarUrl", "avatarPath"] },
чтобы несвязанные действия не наследовали аргументы медиафайлов другого действия. Плоский массив
также подходит для параметров, намеренно общих для всех предоставленных действий.
Каналы, которым необходимо предоставить временный публичный URL для получения медиафайла
платформой, могут использовать createHostedOutboundMediaStore(...) из
openclaw/plugin-sdk/outbound-media с хранилищами состояния плагина. Оставляйте синтаксический
анализ маршрута платформы и проверку токенов в канальном плагине; общий вспомогательный компонент
отвечает только за загрузку медиафайлов, метаданные срока действия, строки фрагментов и очистку.
Формирование нативной полезной нагрузки
Если каналу требуется специфичное для провайдера формирование message(action="send"),
предпочитайте actions.prepareSendPayload(...). Помещайте нативные карточки, блоки, встроенные элементы и
другие устойчивые данные в payload.channelData.<channel>, а отправку
через адаптер исходящих сообщений или сообщений оставляйте ядру. Используйте actions.handleAction(...) для отправки
только в качестве резервного варианта совместимости для полезных нагрузок, которые невозможно сериализовать и
повторно отправить.
Грамматика бесед сеанса
Если платформа хранит дополнительную область действия внутри идентификаторов бесед, выполняйте их разбор
в плагине с помощью messaging.resolveSessionConversation(...). Это
каноническая точка расширения для сопоставления rawId с базовым идентификатором беседы, необязательным
идентификатором ветки, явным baseConversationId и любыми
parentConversationCandidates. При возврате parentConversationCandidates
располагайте их от самого узкого родительского объекта к самой широкой или базовой беседе.
messaging.resolveParentConversationCandidates(...) — устаревший
резервный механизм совместимости для плагинов, которым нужны только резервные родительские значения поверх
универсального или необработанного идентификатора. Если существуют обе точки расширения, ядро сначала использует
resolveSessionConversation(...).parentConversationCandidates и переходит
к resolveParentConversationCandidates(...), только если каноническая
точка расширения их не предоставляет.
Встроенные плагины, которым требуется такой же разбор до запуска реестра каналов,
могут предоставлять файл верхнего уровня session-key-api.ts с соответствующим
экспортом resolveSessionConversation(...) (см. плагины Feishu и Telegram).
Ядро использует эту безопасную для начальной загрузки поверхность, только когда реестр плагинов
среды выполнения ещё недоступен.
Используйте openclaw/plugin-sdk/channel-route, когда коду плагина нужно нормализовать
поля, подобные маршрутам, сравнить дочернюю ветку с её родительским маршрутом или создать
стабильный ключ дедупликации из { channel, to, accountId, threadId }. Вспомогательная функция
нормализует числовые идентификаторы веток так же, как ядро, поэтому предпочитайте её специальным
сравнениям String(threadId). Плагины со специфичной для провайдера грамматикой целевых объектов
должны предоставлять messaging.resolveOutboundSessionRoute(...), чтобы ядро получало
нативные для провайдера идентификаторы сеанса и ветки без адаптеров синтаксического анализа.
Поддержка привязки бесед на уровне учётной записи
Установите conversationBindings.supportsCurrentConversationBinding, если канал
поддерживает универсальные привязки текущей беседы. createChatChannelPlugin(...)
по умолчанию устанавливает для этой статической возможности значение true.
Если поддержка различается в зависимости от настроенной учётной записи, также реализуйте
conversationBindings.isCurrentConversationBindingSupported({ accountId }).
Ядро выполняет эту синхронную точку расширения только после включения статической возможности.
Возврат false делает универсальные операции проверки возможности текущей беседы,
привязки, поиска, перечисления, обновления времени и отвязки недоступными для этой учётной записи.
Если точка расширения отсутствует, статическая возможность применяется ко всем учётным записям.
Определяйте результат на основе уже загруженной конфигурации учётной записи или состояния среды выполнения. Эта
точка расширения управляет только универсальными привязками текущей беседы; она не заменяет
настроенные правила привязки или маршрутизацию сеансов, принадлежащую плагину. Контрактные тесты
должны охватывать как минимум одну поддерживаемую и одну неподдерживаемую учётную запись через
контракт ChannelPlugin["conversationBindings"], экспортируемый из
openclaw/plugin-sdk/channel-core.
Подтверждения и возможности канала
Большинству канальных плагинов не нужен отдельный код для подтверждений. Ядро отвечает за
/approve в том же чате, общие полезные нагрузки кнопок подтверждения и универсальную резервную доставку.
ChannelPlugin.approvals удалён; вместо него помещайте сведения о доставке, нативном представлении, отрисовке и авторизации
подтверждений в один объект approvalCapability. plugin.auth предназначен только
для входа и выхода — ядро больше не считывает из этого объекта точки расширения авторизации подтверждений.
Используйте approvalCapability.delivery только для нативной маршрутизации подтверждений или подавления
резервного поведения, а approvalCapability.render — только если каналу действительно нужны
собственные полезные нагрузки подтверждений вместо общего средства отрисовки.
Авторизация подтверждений
approvalCapability.authorizeActorActionиapprovalCapability.getActionAvailabilityStateявляются канонической точкой расширения авторизации подтверждений.- Используйте
getActionAvailabilityStateдля доступности авторизации подтверждений в том же чате. Сохраняйте доступность настроенных подтверждающих лиц для/approve, даже когда нативная доставка отключена; вместо неё используйте состояние нативной исходной поверхности для рекомендаций по доставке и настройке. - Если канал предоставляет нативные подтверждения выполнения команд, используйте
approvalCapability.getExecInitiatingSurfaceStateдля состояния исходной поверхности или нативного клиента, если оно отличается от авторизации подтверждений в том же чате. Ядро использует эту специализированную для выполнения точку расширения, чтобы различатьenabledиdisabled, определять, поддерживает ли исходный канал нативные подтверждения выполнения, и включать канал в рекомендации по резервному нативному клиенту.createApproverRestrictedNativeApprovalCapability(...)заполняет это значение для типового случая. - Если канал может определять стабильные идентификаторы личных сообщений, подобные владельцу, из существующей конфигурации,
используйте
createResolvedApproverActionAuthAdapterизopenclaw/plugin-sdk/approval-runtime, чтобы ограничить/approveв том же чате без добавления в ядро логики, специфичной для подтверждений. - Если собственная авторизация подтверждений намеренно допускает только резервный вариант в том же чате, верните
markImplicitSameChatApprovalAuthorization({ authorized: true })изopenclaw/plugin-sdk/approval-auth-runtime; в противном случае ядро рассматривает результат как явную авторизацию подтверждающего лица. - Если принадлежащий каналу нативный обратный вызов разрешает подтверждения напрямую, используйте
isImplicitSameChatApprovalAuthorization(...)перед разрешением, чтобы неявный резервный вариант по-прежнему проходил через обычную авторизацию субъекта канала.
Жизненный цикл полезной нагрузки и рекомендации по настройке
- Используйте
outbound.shouldSuppressLocalPayloadPromptилиoutbound.beforeDeliverPayloadдля специфичного для канала поведения жизненного цикла полезной нагрузки, например скрытия дублирующихся локальных запросов подтверждения или отправки индикаторов набора текста перед доставкой. - Используйте
approvalCapability.describeExecApprovalSetup, если канал должен объяснять в ответе для отключённого пути, какие именно параметры конфигурации необходимы для включения нативных подтверждений выполнения. Точка расширения получает{ channel, channelLabel, accountId }; каналы с именованными учётными записями должны отображать пути уровня учётной записи, напримерchannels.<channel>.accounts.<id>.execApprovals.*, вместо значений по умолчанию верхнего уровня. - Используйте
approvalCapability.describePluginApprovalSetup, если рекомендации при сбое подтверждения плагина безопасно показывать для сбоев подтверждения плагина из-за отсутствия маршрута или истечения времени ожидания.createApproverRestrictedNativeApprovalCapability(...)не выводит это изdescribeExecApprovalSetup; передавайте ту же вспомогательную функцию явно, только если подтверждения плагина и выполнения действительно используют одну и ту же нативную настройку.
Нативная доставка подтверждений
Если каналу требуется нативная доставка запросов на подтверждение, код канала должен заниматься только
нормализацией целей и данными транспорта/представления. Используйте
createChannelExecApprovalProfile, createChannelNativeOriginTargetResolver,
createChannelApproverDmTargetResolver и
createApproverRestrictedNativeApprovalCapability из
openclaw/plugin-sdk/approval-runtime. Поместите данные, специфичные для канала, за
approvalCapability.nativeRuntime, предпочтительно через
createChannelApprovalNativeRuntimeAdapter(...) или
createLazyChannelApprovalNativeRuntimeAdapter(...), чтобы ядро могло собрать
обработчик и отвечать за фильтрацию запросов, маршрутизацию, дедупликацию, истечение срока действия, подписку
Gateway и уведомления о маршрутизации в другое место.
nativeRuntime разделён на несколько более узких интерфейсов:
availability— настроена ли учётная запись и следует ли обрабатывать запросpresentation— преобразование общей модели представления подтверждения в нативные полезные нагрузки для состояний ожидания, завершения и истечения срока действия либо в итоговые действияtransport— подготовка целей, а также отправка, обновление и удаление нативных сообщений подтвержденияinteractions— необязательные хуки привязки, отмены привязки и очистки действий для нативных кнопок или реакций, а также необязательный хукcancelDelivered. РеализуйтеcancelDelivered, когдаdeliverPendingрегистрирует внутрипроцессное или постоянное состояние (например, хранилище целей реакций), чтобы это состояние можно было освободить, если остановка обработчика отменяет доставку до выполненияbindPendingили еслиbindPendingне возвращает дескрипторobserve— необязательные хуки диагностики доставки
Другие вспомогательные средства подтверждения:
- Используйте
createNativeApprovalChannelRouteGatesизopenclaw/plugin-sdk/approval-native-runtime, когда канал поддерживает как нативную доставку из исходного сеанса, так и явные цели перенаправления запросов на подтверждение. Это вспомогательное средство централизует выбор конфигурации подтверждений, обработкуmode, фильтры агентов/сеансов, привязку учётных записей, сопоставление целей сеансов и сопоставление списков целей, тогда как вызывающий код по-прежнему отвечает за идентификатор канала, режим перенаправления по умолчанию, поиск учётной записи, проверку включённости транспорта, нормализацию целей и разрешение цели из источника хода. Не используйте его для создания принадлежащих ядру значений политики канала по умолчанию; явно передавайте документированный режим канала по умолчанию. createChannelNativeOriginTargetResolverпо умолчанию использует общий сопоставитель маршрутов канала для целей{ to, accountId, threadId }. ПередавайтеtargetsMatchтолько тогда, когда у канала есть специфичные для провайдера правила эквивалентности, например сопоставление префиксов временных меток Slack. ПередавайтеnormalizeTargetForMatch, когда каналу требуется канонизировать идентификаторы провайдера до запуска стандартного сопоставителя маршрутов или пользовательского обратного вызоваtargetsMatch, сохраняя при этом исходную цель для доставки. ИспользуйтеnormalizeTargetтолько тогда, когда требуется канонизировать саму разрешённую цель доставки.- Если каналу нужны принадлежащие среде выполнения объекты, например клиент, токен, приложение
Bolt или приёмник вебхуков, регистрируйте их через
openclaw/plugin-sdk/channel-runtime-context. Универсальный реестр контекста среды выполнения позволяет ядру инициализировать обработчики на основе возможностей из состояния запуска канала без добавления вспомогательных обёрток, специфичных для подтверждений. - Используйте низкоуровневые
createChannelApprovalHandlerилиcreateChannelNativeApprovalRuntimeтолько тогда, когда интерфейс на основе возможностей пока недостаточно выразителен. - Каналы с нативными подтверждениями должны направлять и
accountId, иapprovalKindчерез эти вспомогательные средства.accountIdограничивает политику подтверждений для нескольких учётных записей нужной учётной записью бота, аapprovalKindпредоставляет каналу различающееся поведение подтверждений выполнения и плагинов без жёстко заданных ветвей в ядре. - Ядро также отвечает за уведомления о перенаправлении подтверждений. Плагины каналов не должны отправлять
собственные последующие сообщения вида «запрос на подтверждение отправлен в личные сообщения / другой канал»
из
createChannelNativeApprovalRuntime; вместо этого предоставляйте точную маршрутизацию источника и личных сообщений подтверждающего пользователя через общие вспомогательные средства возможностей подтверждения, а ядро должно агрегировать фактические доставки перед отправкой уведомления обратно в чат, инициировавший запрос. - Сохраняйте тип доставленного идентификатора подтверждения на всём пути. Нативные клиенты не должны угадывать или изменять маршрутизацию подтверждений выполнения и плагинов на основе локального состояния канала.
- Передавайте этот явный
approvalKindвresolveApprovalOverGateway. При этом используется каноническая службаapproval.resolve, а если другой интерфейс отвечает первым, возвращается зарегистрированный победитель. Прежний явный входresolveMethodсохраняется для элементов управления на основе команд; новые нативные действия не должны использовать его или выводить тип из идентификатора. - Разные типы подтверждений могут намеренно предоставлять разные нативные интерфейсы. Текущие встроенные примеры: Matrix сохраняет одинаковую нативную маршрутизацию в личные сообщения/каналы и взаимодействие с реакциями для подтверждений выполнения и плагинов, при этом позволяя различать авторизацию по типу подтверждения; Slack сохраняет доступность нативной маршрутизации подтверждений как для идентификаторов выполнения, так и для идентификаторов плагинов.
createApproverRestrictedNativeApprovalAdapterпо-прежнему существует как обёртка совместимости, но новый код должен предпочитать построитель возможностей и предоставлятьapprovalCapabilityв плагине.
Более узкие подпути среды выполнения подтверждений
Для часто используемых точек входа каналов предпочитайте эти более узкие подпути более широкому
экспорту approval-runtime, когда нужна только одна часть этого семейства:
openclaw/plugin-sdk/approval-auth-runtimeopenclaw/plugin-sdk/approval-client-runtimeopenclaw/plugin-sdk/approval-delivery-runtimeopenclaw/plugin-sdk/approval-gateway-runtimeopenclaw/plugin-sdk/approval-reference-runtimeopenclaw/plugin-sdk/approval-handler-adapter-runtimeopenclaw/plugin-sdk/approval-handler-runtimeopenclaw/plugin-sdk/approval-native-runtimeopenclaw/plugin-sdk/approval-reply-runtimeopenclaw/plugin-sdk/channel-runtime-context
Аналогично, предпочитайте openclaw/plugin-sdk/reply-runtime,
openclaw/plugin-sdk/reply-dispatch-runtime,
openclaw/plugin-sdk/reply-reference и
openclaw/plugin-sdk/reply-chunking более широким объединяющим интерфейсам, когда
вам не нужны они все.
Подпути настройки
openclaw/plugin-sdk/setup-runtimeохватывает безопасные для среды выполнения вспомогательные средства настройки:createSetupTranslator, безопасные для импорта адаптеры исправления настроек (createPatchedAccountSetupAdapter,createEnvPatchedAccountSetupAdapter,createSetupInputPresenceValidator), вывод примечаний поиска,promptResolvedAllowFrom,splitSetupEntriesи делегированные построители прокси настройки.openclaw/plugin-sdk/channel-setupохватывает построители настройки необязательной установки и несколько безопасных для настройки примитивов:createOptionalChannelSetupSurface,createOptionalChannelSetupAdapter,createOptionalChannelSetupWizard,DEFAULT_ACCOUNT_ID,createTopLevelChannelDmPolicy,setSetupChannelEnabledиsplitSetupEntries.- Используйте более широкий интерфейс
openclaw/plugin-sdk/setupтолько тогда, когда вам также нужны более тяжёлые общие вспомогательные средства настройки/конфигурации, напримерmoveSingleAccountChannelSectionToDefaultAccount(...).
Если вашему каналу требуется только сообщить «сначала установите этот плагин» в интерфейсах
настройки, предпочитайте createOptionalChannelSetupSurface(...). Созданные
адаптер/мастер запрещают продолжение при ошибках записи конфигурации и завершения настройки, а также повторно используют
одно и то же сообщение о необходимости установки при проверке, завершении и копировании
ссылки на документацию.
Если канал поддерживает настройку или аутентификацию через переменные среды и универсальные процессы запуска/настройки
должны знать имена этих переменных до загрузки среды выполнения, объявите их в
манифесте плагина с помощью channelEnvVars. Сохраняйте envVars среды выполнения канала или локальные
константы только для текста, предназначенного для операторов.
Если канал может появляться в status, channels list, channels status или
проверках SecretRef до запуска среды выполнения плагина, добавьте openclaw.setupEntry в
package.json. Эта точка входа должна быть безопасна для импорта в путях команд,
работающих только на чтение, и должна возвращать метаданные канала, безопасный для настройки адаптер
конфигурации, адаптер состояния и метаданные целей секретов канала, необходимые для этих
сводок. Не запускайте клиенты, прослушиватели или транспортные среды выполнения из точки входа
настройки.
Также сохраняйте узким путь импорта основной точки входа канала. Механизм обнаружения может вычислять
точку входа и модуль плагина канала для регистрации возможностей без
активации канала. Такие файлы, как channel-plugin-api.ts, должны экспортировать
объект плагина канала, не импортируя мастеры настройки, транспортные
клиенты, прослушиватели сокетов, средства запуска подпроцессов или модули запуска служб.
Помещайте эти компоненты среды выполнения в модули, загружаемые из registerFull(...), установщики
среды выполнения или ленивые адаптеры возможностей.
Другие узкие подпути каналов
Для других часто используемых путей каналов предпочитайте узкие вспомогательные средства более широким устаревшим интерфейсам:
openclaw/plugin-sdk/account-core,openclaw/plugin-sdk/account-id,openclaw/plugin-sdk/account-resolutionиopenclaw/plugin-sdk/account-helpersдля конфигурации нескольких учётных записей и резервного выбора учётной записи по умолчаниюopenclaw/plugin-sdk/inbound-envelopeиopenclaw/plugin-sdk/channel-inboundдля связывания входящего маршрута/конверта, регистрации и диспетчеризацииopenclaw/plugin-sdk/channel-targetsдля вспомогательных средств разбора целейopenclaw/plugin-sdk/outbound-mediaдля загрузки медиа иopenclaw/plugin-sdk/channel-outboundдля делегатов исходящей идентификации/отправки и планирования полезной нагрузкиbuildThreadAwareOutboundSessionRoute(...)изopenclaw/plugin-sdk/channel-core, когда исходящий маршрут должен сохранять явныйreplyToId/threadIdили восстанавливать текущий сеанс:thread:после того, как базовый ключ сеанса всё ещё совпадает. Плагины провайдеров могут переопределять приоритет, поведение суффиксов и нормализацию идентификаторов веток, когда их платформа имеет нативную семантику доставки в ветки.openclaw/plugin-sdk/thread-bindings-runtimeдля жизненного цикла привязки веток и регистрации адаптеровopenclaw/plugin-sdk/agent-media-payloadтолько тогда, когда всё ещё требуется устаревшая структура полей полезной нагрузки агента/медиаopenclaw/plugin-sdk/telegram-command-config(устарело: ни один встроенный плагин не использует это в рабочей среде) для нормализации пользовательских команд Telegram, проверки дубликатов/конфликтов и контракта конфигурации команд со стабильным резервным поведением; для нового кода плагинов предпочитайте локальную обработку конфигурации команд в плагине
Каналам, выполняющим только аутентификацию, обычно достаточно стандартного пути: ядро обрабатывает подтверждения, а плагин предоставляет только возможности исходящей доставки/аутентификации. Каналы с нативными подтверждениями, такие как Matrix, Slack, Telegram и пользовательские чат-транспорты, должны использовать общие нативные вспомогательные средства вместо собственной реализации жизненного цикла подтверждений.
Политика входящих упоминаний
Разделяйте обработку входящих упоминаний на два уровня:
- сбор данных, принадлежащий плагину
- оценка общей политики
Используйте openclaw/plugin-sdk/channel-mention-gating для принятия решений по политике упоминаний.
Используйте openclaw/plugin-sdk/channel-inbound только тогда, когда нужен более широкий
экспорт вспомогательных средств входящих сообщений.
Подходит для локальной логики плагина:
- обнаружение ответа боту
- обнаружение цитирования бота
- проверки участия в ветке
- исключение служебных/системных сообщений
- нативные кеши платформы, необходимые для подтверждения участия бота
Подходит для общего вспомогательного средства:
requireMention- результат явного упоминания
- список разрешённых неявных упоминаний
- обход для команд
- итоговое решение о пропуске
Предпочтительная последовательность:
- Вычислите локальные данные об упоминании.
- Передайте эти данные в
resolveInboundMentionDecision({ facts, policy }). - Используйте
decision.effectiveWasMentioned,decision.shouldBypassMentionиdecision.shouldSkipво входном шлюзе.
implicitMentionKindWhen, matchesMentionWithExplicit, resolveInboundMentionDecision,} from "openclaw/plugin-sdk/channel-inbound"; const wasMentioned = matchesMentionWithExplicit({ text, mentionRegexes, explicit: { hasAnyMention, isExplicitlyMentioned, canResolveExplicit, },}); const facts = { canDetectMention: true, wasMentioned, hasAnyMention, implicitMentionKinds: [ ...implicitMentionKindWhen("reply_to_bot", isReplyToBot), ...implicitMentionKindWhen("quoted_bot", isQuoteOfBot), ],}; const decision = resolveInboundMentionDecision({ facts, policy: { isGroup, requireMention, allowedImplicitMentionKinds: requireExplicitMention ? [] : ["reply_to_bot", "quoted_bot"], allowTextCommands, hasControlCommand, commandAuthorized, },}); if (decision.shouldSkip) return;matchesMentionWithExplicit(...) возвращает логическое значение. hasAnyMention,
isExplicitlyMentioned и canResolveExplicit поступают из собственных
нативных метаданных упоминаний канала (объекты сообщений, признаки ответа боту и аналогичные данные);
передавайте значения false/undefined, когда ваша платформа не может их определить.
api.runtime.channel.mentions предоставляет те же общие вспомогательные функции для упоминаний
встроенным плагинам каналов, которые уже зависят от внедрения среды выполнения:
buildMentionRegexes, matchesMentionPatterns, matchesMentionWithExplicit,
implicitMentionKindWhen, resolveInboundMentionDecision.
Если вам нужны только implicitMentionKindWhen и resolveInboundMentionDecision,
импортируйте их из openclaw/plugin-sdk/channel-mention-gating, чтобы не загружать
несвязанные вспомогательные функции среды выполнения для входящих сообщений.
Пошаговое руководство
Пакет и манифест
Создайте стандартные файлы плагина. Поле channels в
openclaw.plugin.json (а не поле kind) указывает, что манифест
владеет каналом. Полное описание метаданных пакета см. в разделе
Настройка и конфигурация плагина:
{"name": "@myorg/openclaw-acme-chat","version": "1.0.0","type": "module","openclaw": { "extensions": ["./index.ts"], "setupEntry": "./setup-entry.ts", "channel": { "id": "acme-chat", "label": "Acme Chat", "blurb": "Подключите OpenClaw к Acme Chat." }}}{"id": "acme-chat","channels": ["acme-chat"],"name": "Acme Chat","description": "Плагин канала Acme Chat","configSchema": { "type": "object", "additionalProperties": false, "properties": {}},"channelConfigs": { "acme-chat": { "schema": { "type": "object", "additionalProperties": false, "properties": { "token": { "type": "string" }, "allowFrom": { "type": "array", "items": { "type": "string" } } } }, "uiHints": { "token": { "label": "Токен бота", "sensitive": true } } }}}configSchema проверяет plugins.entries.acme-chat.config. Используйте его для
настроек, принадлежащих плагину и не относящихся к конфигурации учётной записи канала.
channelConfigs.acme-chat.schema проверяет channels.acme-chat и служит
источником холодного пути для схемы конфигурации, настройки и интерфейса до
загрузки среды выполнения плагина. Полный справочник полей верхнего уровня см. в разделе
Манифест плагина.
Создание объекта плагина канала
Интерфейс ChannelPlugin содержит множество необязательных поверхностей адаптеров. Начните с
минимума — id, config и setup — и добавляйте адаптеры по мере
необходимости.
Создайте src/channel.ts:
import { createChatChannelPlugin, createChannelPluginBase,} from "openclaw/plugin-sdk/channel-core";import type { OpenClawConfig } from "openclaw/plugin-sdk/channel-core";import { acmeChatApi } from "./client.js"; // ваш клиент API платформы type ResolvedAccount = { accountId: string | null; token: string; allowFrom: string[]; dmPolicy: string | undefined;}; function resolveAccount( cfg: OpenClawConfig, accountId?: string | null,): ResolvedAccount { const section = (cfg.channels as Record<string, any>)?.["acme-chat"]; const token = section?.token; if (!token) throw new Error("acme-chat: требуется токен"); return { accountId: accountId ?? null, token, allowFrom: section?.allowFrom ?? [], dmPolicy: section?.dmSecurity, };} export const acmeChatPlugin = createChatChannelPlugin<ResolvedAccount>({ base: createChannelPluginBase({ id: "acme-chat", // Разрешение и проверка учётной записи относятся к `config`, а не к `setup`. // `setup` охватывает запись данных при первоначальной настройке (applyAccountConfig, validateInput). config: { listAccountIds: () => ["default"], resolveAccount, inspectAccount(cfg, accountId) { const section = (cfg.channels as Record<string, any>)?.["acme-chat"]; return { enabled: Boolean(section?.token), configured: Boolean(section?.token), tokenStatus: section?.token ? "available" : "missing", }; }, }, setup: { applyAccountConfig: ({ cfg, input }) => ({ ...cfg, channels: { ...cfg.channels, "acme-chat": { ...(cfg.channels as any)?.["acme-chat"], ...input }, }, }), }, }), // Безопасность личных сообщений: кто может отправлять сообщения боту security: { dm: { channelKey: "acme-chat", resolvePolicy: (account) => account.dmPolicy, resolveAllowFrom: (account) => account.allowFrom, defaultPolicy: "allowlist", }, }, // Сопряжение: процесс подтверждения новых контактов в личных сообщениях pairing: { text: { idLabel: "Имя пользователя Acme Chat", message: "Отправьте этот код для подтверждения своей личности:", notify: async ({ target, code }) => { await acmeChatApi.sendDm(target, `Код сопряжения: ${code}`); }, }, }, // Ветки обсуждений: способ доставки ответов threading: { topLevelReplyToMode: "reply" }, // Исходящие сообщения: отправка сообщений на платформу outbound: { attachedResults: { channel: "acme-chat", sendText: async (params) => { const result = await acmeChatApi.sendMessage( params.to, params.text, ); return { messageId: result.id }; }, }, base: { sendMedia: async (params) => { await acmeChatApi.sendFile(params.to, params.filePath); }, }, },});Для каналов, которые принимают как канонические ключи личных сообщений верхнего уровня, так и устаревшие вложенные ключи, используйте вспомогательные функции из plugin-sdk/channel-config-helpers: resolveChannelDmAccess, resolveChannelDmPolicy, resolveChannelDmAllowFrom и normalizeChannelDmPolicy обеспечивают приоритет локальных значений учётной записи над унаследованными корневыми значениями. Используйте тот же преобразователь вместе с исправлением через doctor посредством normalizeLegacyDmAliases, чтобы среда выполнения и миграция читали один и тот же контракт.
Что createChatChannelPlugin делает за вас
Вместо ручной реализации низкоуровневых интерфейсов адаптеров вы передаёте декларативные параметры, а построитель объединяет их:
| Параметр | Что он подключает |
|---|---|
security.dm |
Ограниченный областью преобразователь безопасности личных сообщений из полей конфигурации |
pairing.text |
Текстовый процесс сопряжения в личных сообщениях с обменом кодом |
threading |
Преобразователь режима ответа (фиксированный, в области учётной записи или пользовательский) |
outbound.attachedResults |
Функции отправки, возвращающие метаданные результата (идентификаторы сообщений); требуется соседний идентификатор channel, чтобы ядро могло проставить канал в возвращаемом результате доставки |
Если вам нужен полный контроль, вместо декларативных параметров также можно передавать необработанные объекты адаптеров.
Необработанные адаптеры исходящих сообщений могут определять функцию chunker(text, limit, ctx).
Необязательный объект ctx.formatting содержит решения по форматированию во время доставки,
например maxLinesPerMessage; применяйте его перед отправкой, чтобы привязка ответов
к веткам и границы фрагментов однократно определялись общей системой доставки исходящих сообщений.
Контексты отправки также содержат replyToIdSource (implicit или explicit),
когда была определена нативная цель ответа, чтобы вспомогательные функции полезной нагрузки могли сохранять
явные теги ответа, не расходуя неявный одноразовый слот ответа.
Подключение точки входа
Создайте index.ts:
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";import { acmeChatPlugin } from "./src/channel.js"; export default defineChannelPluginEntry({ id: "acme-chat", name: "Acme Chat", description: "Плагин канала Acme Chat", plugin: acmeChatPlugin, registerCliMetadata(api) { api.registerCli( ({ program }) => { program .command("acme-chat") .description("Управление Acme Chat"); }, { descriptors: [ { name: "acme-chat", description: "Управление Acme Chat", hasSubcommands: false, }, ], }, ); }, registerFull(api) { api.registerGatewayMethod(/* ... */); },});Поместите принадлежащие каналу дескрипторы CLI в registerCliMetadata(...), чтобы OpenClaw
мог показывать их в корневой справке без активации полной среды выполнения канала,
а при обычной полной загрузке те же дескрипторы использовались для фактической
регистрации команд. Оставьте registerFull(...) для операций, выполняемых только во время работы.
defineChannelPluginEntry автоматически обрабатывает разделение режимов регистрации.
Если registerFull(...) регистрирует RPC-методы Gateway, используйте
префикс, относящийся к конкретному плагину. Пространства имён администрирования ядра (config.*,
exec.approvals.*, wizard.*, update.*) остаются зарезервированными и всегда
разрешаются в operator.admin. Все параметры см. в разделе
Точки входа.
Добавление точки входа настройки
Создайте setup-entry.ts для облегчённой загрузки при первоначальной настройке:
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";import { acmeChatPlugin } from "./src/channel.js"; export default defineSetupPluginEntry(acmeChatPlugin);OpenClaw загружает эту точку вместо полной, когда канал отключён или не настроен. Это позволяет не загружать тяжёлый код среды выполнения во время настройки. Подробности см. в разделе Настройка и конфигурация.
Встроенные каналы рабочей области, которые выносят безопасные для настройки экспорты в отдельные
вспомогательные модули, могут использовать defineBundledChannelSetupEntry(...) из
openclaw/plugin-sdk/channel-entry-contract, если им также требуется
явная функция установки среды выполнения во время настройки.
Обработка входящих сообщений
Ваш плагин должен получать сообщения от платформы и перенаправлять их в OpenClaw. Обычно для этого используется Webhook, который проверяет запрос и передаёт его обработчику входящих сообщений вашего канала:
registerFull(api) { api.registerHttpRoute({ path: "/acme-chat/webhook", auth: "plugin", // аутентификация под управлением плагина (проверяйте подписи самостоятельно) handler: async (req, res) => { const event = parseWebhookPayload(req); // Ваш обработчик входящих сообщений передаёт сообщение в OpenClaw. // Точная схема подключения зависит от SDK вашей платформы — // реальный пример см. во встроенном пакете плагина Microsoft Teams или Google Chat. await handleAcmeChatInbound(api, event); res.statusCode = 200; res.end("ok"); return true; }, });}Тестирование
Размещайте тесты рядом с кодом в src/channel.test.ts:
import { describe, it, expect } from "vitest";import { acmeChatPlugin } from "./channel.js"; describe("acme-chat plugin", () => { it("resolves account from config", () => { const cfg = { channels: { "acme-chat": { token: "test-token", allowFrom: ["user1"] }, }, } as any; const account = acmeChatPlugin.config.resolveAccount(cfg, undefined); expect(account.token).toBe("test-token"); }); it("inspects account without materializing secrets", () => { const cfg = { channels: { "acme-chat": { token: "test-token" } }, } as any; const result = acmeChatPlugin.config.inspectAccount!(cfg, undefined); expect(result.configured).toBe(true); expect(result.tokenStatus).toBe("available"); }); it("reports missing config", () => { const cfg = { channels: {} } as any; const result = acmeChatPlugin.config.inspectAccount!(cfg, undefined); expect(result.configured).toBe(false); });});pnpm test <bundled-plugin-root>/acme-chat/Общие вспомогательные средства тестирования описаны в разделе Тестирование.
Структура файлов
<bundled-plugin-root>/acme-chat/├── package.json # Метаданные openclaw.channel├── openclaw.plugin.json # Манифест со схемой конфигурации├── index.ts # defineChannelPluginEntry├── setup-entry.ts # defineSetupPluginEntry├── api.ts # Публичные экспорты (необязательно)├── runtime-api.ts # Внутренние экспорты среды выполнения (необязательно)└── src/ ├── channel.ts # ChannelPlugin через createChatChannelPlugin ├── channel.test.ts # Тесты ├── client.ts # Клиент API платформы └── runtime.ts # Хранилище среды выполнения (при необходимости)Расширенные темы
Фиксированный, привязанный к учётной записи или пользовательский режим ответа
describeMessageTool и обнаружение действий
inferTargetChatType, looksLikeId, reservedLiterals, resolveTarget
TTS, STT, медиафайлы и субагент через api.runtime
Общий жизненный цикл входящего события: приём, разрешение, запись, отправка, завершение
Дальнейшие действия
- Плагины провайдеров — если ваш плагин также предоставляет модели
- Обзор SDK — полный справочник по импорту подпутей
- Тестирование SDK — утилиты тестирования и тесты контрактов
- Манифест плагина — полная схема манифеста