chore(i18n): refresh uk translations
This commit is contained in:
parent
cfc114e1f0
commit
5b55128fff
@ -1,34 +1,30 @@
|
||||
---
|
||||
read_when:
|
||||
- Робота над функціями каналу Microsoft Teams
|
||||
summary: Статус підтримки, можливості та конфігурація бота Microsoft Teams
|
||||
summary: Стан підтримки, можливості та налаштування бота Microsoft Teams
|
||||
title: Microsoft Teams
|
||||
x-i18n:
|
||||
generated_at: "2026-04-29T07:51:49Z"
|
||||
generated_at: "2026-04-30T00:42:15Z"
|
||||
model: gpt-5.5
|
||||
provider: openai
|
||||
source_hash: 535bd7f9713f221572a99ae3a7a39d7acdd5a1e41c2d79a43d4caf9c2ce2b159
|
||||
source_hash: c2c8cd13a72941a18d609b1f7263d9b9ed3284873f9b1483975ca1356b543979
|
||||
source_path: channels/msteams.md
|
||||
workflow: 16
|
||||
---
|
||||
|
||||
Статус: підтримуються текст + вкладення в DM; надсилання файлів у канали/групи потребує `sharePointSiteId` + дозволів Graph (див. [Надсилання файлів у групових чатах](#sending-files-in-group-chats)). Опитування надсилаються через Adaptive Cards. Дії повідомлень надають явний `upload-file` для надсилання, де файл є основним.
|
||||
Стан: підтримуються текст і вкладення в DM; надсилання файлів у канал/групу потребує `sharePointSiteId` + дозволів Graph (див. [Надсилання файлів у групових чатах](#sending-files-in-group-chats)). Опитування надсилаються через Adaptive Cards. Дії з повідомленнями надають явний `upload-file` для надсилань, де файл є першим.
|
||||
|
||||
## Вбудований Plugin
|
||||
|
||||
Microsoft Teams постачається як вбудований Plugin у поточних випусках OpenClaw, тому
|
||||
окреме встановлення у звичайній пакетній збірці не потрібне.
|
||||
Microsoft Teams постачається як вбудований Plugin у поточних випусках OpenClaw, тому в звичайній пакетованій збірці окреме встановлення не потрібне.
|
||||
|
||||
Якщо ви використовуєте старішу збірку або власне встановлення, що виключає вбудований Teams,
|
||||
встановіть поточний npm-пакет, коли його буде опубліковано:
|
||||
Якщо ви використовуєте старішу збірку або власне встановлення, яке виключає вбудований Teams, встановіть поточний npm-пакет, коли його буде опубліковано:
|
||||
|
||||
```bash
|
||||
openclaw plugins install @openclaw/msteams
|
||||
```
|
||||
|
||||
Якщо npm повідомляє, що пакет, яким володіє OpenClaw, застарілий, використовуйте поточну пакетну
|
||||
збірку OpenClaw або локальний шлях checkout, доки не буде
|
||||
опубліковано новіший npm-пакет.
|
||||
Якщо npm повідомляє, що пакет, який належить OpenClaw, застарілий, використовуйте поточну пакетовану збірку OpenClaw або шлях до локального checkout, доки не буде опубліковано новіший npm-пакет.
|
||||
|
||||
Локальний checkout (під час запуску з git-репозиторію):
|
||||
|
||||
@ -36,7 +32,7 @@ openclaw plugins install @openclaw/msteams
|
||||
openclaw plugins install ./path/to/local/msteams-plugin
|
||||
```
|
||||
|
||||
Докладніше: [Plugins](/uk/tools/plugin)
|
||||
Докладно: [Plugins](/uk/tools/plugin)
|
||||
|
||||
## Швидке налаштування
|
||||
|
||||
@ -51,12 +47,12 @@ teams status # verify you're logged in and see your tenant info
|
||||
```
|
||||
|
||||
<Note>
|
||||
Teams CLI зараз перебуває в preview. Команди та прапорці можуть змінюватися між випусками.
|
||||
Teams CLI наразі перебуває в preview. Команди й прапорці можуть змінюватися між випусками.
|
||||
</Note>
|
||||
|
||||
**2. Запустіть тунель** (Teams не може дістатися до localhost)
|
||||
**2. Запустіть тунель** (Teams не може дістатися localhost)
|
||||
|
||||
Встановіть і автентифікуйте devtunnel CLI, якщо ще цього не зробили ([посібник із початку роботи](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started)).
|
||||
Встановіть і автентифікуйте devtunnel CLI, якщо ви ще цього не зробили ([посібник із початку роботи](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started)).
|
||||
|
||||
```bash
|
||||
# One-time setup (persistent URL across sessions):
|
||||
@ -69,10 +65,10 @@ devtunnel host my-openclaw-bot
|
||||
```
|
||||
|
||||
<Note>
|
||||
`--allow-anonymous` потрібен, бо Teams не може автентифікуватися з devtunnels. Кожен вхідний запит бота все одно автоматично перевіряється Teams SDK.
|
||||
`--allow-anonymous` потрібен, оскільки Teams не може автентифікуватися з devtunnels. Кожен вхідний запит бота все одно автоматично перевіряється Teams SDK.
|
||||
</Note>
|
||||
|
||||
Альтернативи: `ngrok http 3978` або `tailscale funnel 3978` (але вони можуть змінювати URL у кожній сесії).
|
||||
Альтернативи: `ngrok http 3978` або `tailscale funnel 3978` (але вони можуть змінювати URL кожної сесії).
|
||||
|
||||
**3. Створіть застосунок**
|
||||
|
||||
@ -85,9 +81,9 @@ teams app create \
|
||||
Ця одна команда:
|
||||
|
||||
- Створює застосунок Entra ID (Azure AD)
|
||||
- Генерує клієнтський секрет
|
||||
- Збирає та завантажує маніфест застосунку Teams (з іконками)
|
||||
- Реєструє бота (типово керований Teams — передплата Azure не потрібна)
|
||||
- Генерує client secret
|
||||
- Збирає й завантажує маніфест застосунку Teams (з іконками)
|
||||
- Реєструє бота (типово керується Teams — підписка Azure не потрібна)
|
||||
|
||||
Вивід покаже `CLIENT_ID`, `CLIENT_SECRET`, `TENANT_ID` і **Teams App ID** — занотуйте їх для наступних кроків. Він також запропонує встановити застосунок безпосередньо в Teams.
|
||||
|
||||
@ -111,7 +107,7 @@ teams app create \
|
||||
|
||||
**5. Встановіть застосунок у Teams**
|
||||
|
||||
`teams app create` запропонує встановити застосунок — виберіть "Встановити в Teams". Якщо ви пропустили це, посилання можна отримати пізніше:
|
||||
`teams app create` запропонує встановити застосунок — виберіть "Install in Teams". Якщо ви пропустили це, можете отримати посилання пізніше:
|
||||
|
||||
```bash
|
||||
teams app get <teamsAppId> --install-link
|
||||
@ -123,23 +119,23 @@ teams app get <teamsAppId> --install-link
|
||||
teams app doctor <teamsAppId>
|
||||
```
|
||||
|
||||
Це запускає діагностику реєстрації бота, конфігурації застосунку AAD, чинності маніфесту та налаштування SSO.
|
||||
Це запускає діагностику реєстрації бота, конфігурації застосунку AAD, чинності маніфесту й налаштування SSO.
|
||||
|
||||
Для production-розгортань розгляньте використання [федеративної автентифікації](/uk/channels/msteams#federated-authentication-certificate-plus-managed-identity) (сертифікат або керована ідентичність) замість клієнтських секретів.
|
||||
Для production-розгортань розгляньте використання [федеративної автентифікації](/uk/channels/msteams#federated-authentication-certificate-plus-managed-identity) (сертифікат або managed identity) замість client secrets.
|
||||
|
||||
<Note>
|
||||
Групові чати типово заблоковані (`channels.msteams.groupPolicy: "allowlist"`). Щоб дозволити відповіді в групах, задайте `channels.msteams.groupAllowFrom` або використайте `groupPolicy: "open"`, щоб дозволити будь-якому учаснику (з вимогою згадки).
|
||||
Групові чати типово заблоковані (`channels.msteams.groupPolicy: "allowlist"`). Щоб дозволити відповіді в групах, задайте `channels.msteams.groupAllowFrom` або використайте `groupPolicy: "open"`, щоб дозволити будь-якого учасника (з обмеженням за згадкою).
|
||||
</Note>
|
||||
|
||||
## Цілі
|
||||
|
||||
- Спілкуватися з OpenClaw через DM, групові чати або канали Teams.
|
||||
- Зберігати детерміновану маршрутизацію: відповіді завжди повертаються до каналу, з якого вони надійшли.
|
||||
- Зберігати детермінізм маршрутизації: відповіді завжди повертаються в канал, з якого надійшли.
|
||||
- Типово використовувати безпечну поведінку каналів (згадки обов’язкові, якщо не налаштовано інакше).
|
||||
|
||||
## Записи конфігурації
|
||||
|
||||
Типово Microsoft Teams дозволено записувати оновлення конфігурації, спричинені `/config set|unset` (потребує `commands.config: true`).
|
||||
Типово Microsoft Teams дозволено записувати оновлення конфігурації, ініційовані `/config set|unset` (потребує `commands.config: true`).
|
||||
|
||||
Вимкніть за допомогою:
|
||||
|
||||
@ -153,16 +149,16 @@ teams app doctor <teamsAppId>
|
||||
|
||||
**Доступ до DM**
|
||||
|
||||
- Типово: `channels.msteams.dmPolicy = "pairing"`. Невідомі відправники ігноруються, доки їх не схвалять.
|
||||
- `channels.msteams.allowFrom` має використовувати стабільні object ID AAD.
|
||||
- Не покладайтеся на зіставлення UPN/відображуваного імені для allowlist — вони можуть змінюватися. OpenClaw типово вимикає пряме зіставлення імен; увімкніть його явно через `channels.msteams.dangerouslyAllowNameMatching: true`.
|
||||
- Майстер може зіставляти імена з ID через Microsoft Graph, коли облікові дані це дозволяють.
|
||||
- Типово: `channels.msteams.dmPolicy = "pairing"`. Невідомі відправники ігноруються, доки їх не схвалено.
|
||||
- `channels.msteams.allowFrom` має використовувати стабільні object IDs AAD.
|
||||
- Не покладайтеся на зіставлення UPN/display-name для allowlists — вони можуть змінюватися. OpenClaw типово вимикає пряме зіставлення імен; увімкніть його явно через `channels.msteams.dangerouslyAllowNameMatching: true`.
|
||||
- Майстер може зіставляти імена з IDs через Microsoft Graph, коли облікові дані це дозволяють.
|
||||
|
||||
**Доступ до груп**
|
||||
**Доступ груп**
|
||||
|
||||
- Типово: `channels.msteams.groupPolicy = "allowlist"` (заблоковано, якщо ви не додасте `groupAllowFrom`). Використайте `channels.defaults.groupPolicy`, щоб перевизначити типове значення, коли воно не задане.
|
||||
- `channels.msteams.groupAllowFrom` контролює, які відправники можуть запускати обробку в групових чатах/каналах (із fallback до `channels.msteams.allowFrom`).
|
||||
- Задайте `groupPolicy: "open"`, щоб дозволити будь-якому учаснику (типово все одно з вимогою згадки).
|
||||
- Типово: `channels.msteams.groupPolicy = "allowlist"` (заблоковано, якщо ви не додасте `groupAllowFrom`). Використовуйте `channels.defaults.groupPolicy`, щоб перевизначити типове значення, коли воно не задане.
|
||||
- `channels.msteams.groupAllowFrom` керує тим, які відправники можуть запускати дії в групових чатах/каналах (із fallback до `channels.msteams.allowFrom`).
|
||||
- Задайте `groupPolicy: "open"`, щоб дозволити будь-якого учасника (типово все одно з обмеженням за згадкою).
|
||||
- Щоб не дозволяти **жодних каналів**, задайте `channels.msteams.groupPolicy: "disabled"`.
|
||||
|
||||
Приклад:
|
||||
@ -180,12 +176,12 @@ teams app doctor <teamsAppId>
|
||||
|
||||
**Teams + allowlist каналів**
|
||||
|
||||
- Обмежуйте відповіді в групах/каналах, перелічуючи команди та канали в `channels.msteams.teams`.
|
||||
- Ключі мають використовувати стабільні ID команд і conversation ID каналів.
|
||||
- Коли `groupPolicy="allowlist"` і наявний allowlist Teams, приймаються лише перелічені команди/канали (з вимогою згадки).
|
||||
- Обмежуйте відповіді в групах/каналах, перелічуючи команди й канали в `channels.msteams.teams`.
|
||||
- Ключі мають використовувати стабільні Teams conversation IDs із посилань Teams, а не змінні відображувані назви.
|
||||
- Коли `groupPolicy="allowlist"` і наявний allowlist команд, приймаються лише перелічені команди/канали (з обмеженням за згадкою).
|
||||
- Майстер налаштування приймає записи `Team/Channel` і зберігає їх для вас.
|
||||
- Під час запуску OpenClaw зіставляє імена команд/каналів та імена allowlist користувачів з ID (коли дозволи Graph це дозволяють)
|
||||
і записує зіставлення в журнал; нерозв’язані імена команд/каналів зберігаються як введені, але типово ігноруються для маршрутизації, якщо не ввімкнено `channels.msteams.dangerouslyAllowNameMatching: true`.
|
||||
- Під час запуску OpenClaw зіставляє назви team/channel і user allowlist з IDs (коли дозволи Graph це дозволяють)
|
||||
і логує мапінг; нерозв’язані назви team/channel зберігаються так, як введені, але типово ігноруються для маршрутизації, якщо не ввімкнено `channels.msteams.dangerouslyAllowNameMatching: true`.
|
||||
|
||||
Приклад:
|
||||
|
||||
@ -209,7 +205,7 @@ teams app doctor <teamsAppId>
|
||||
<details>
|
||||
<summary><strong>Ручне налаштування (без Teams CLI)</strong></summary>
|
||||
|
||||
Якщо ви не можете використовувати Teams CLI, ви можете налаштувати бота вручну через Azure Portal.
|
||||
Якщо ви не можете використовувати Teams CLI, можете налаштувати бота вручну через Azure Portal.
|
||||
|
||||
### Як це працює
|
||||
|
||||
@ -217,25 +213,25 @@ teams app doctor <teamsAppId>
|
||||
2. Створіть **Azure Bot** (App ID + secret + tenant ID).
|
||||
3. Зберіть **пакет застосунку Teams**, який посилається на бота й містить дозволи RSC нижче.
|
||||
4. Завантажте/встановіть застосунок Teams у команду (або personal scope для DM).
|
||||
5. Налаштуйте `msteams` у `~/.openclaw/openclaw.json` (або змінних середовища) і запустіть Gateway.
|
||||
6. Gateway типово слухає трафік Webhook Bot Framework на `/api/messages`.
|
||||
5. Налаштуйте `msteams` у `~/.openclaw/openclaw.json` (або env vars) і запустіть Gateway.
|
||||
6. Gateway типово слухає трафік Bot Framework Webhook на `/api/messages`.
|
||||
|
||||
### Крок 1: Створіть Azure Bot
|
||||
|
||||
1. Перейдіть до [Створити Azure Bot](https://portal.azure.com/#create/Microsoft.AzureBot)
|
||||
2. Заповніть вкладку **Основи**:
|
||||
1. Перейдіть до [Create Azure Bot](https://portal.azure.com/#create/Microsoft.AzureBot)
|
||||
2. Заповніть вкладку **Basics**:
|
||||
|
||||
| Поле | Значення |
|
||||
| ------------------ | -------------------------------------------------------- |
|
||||
| **Дескриптор бота** | Ім’я вашого бота, напр., `openclaw-msteams` (має бути унікальним) |
|
||||
| **Передплата** | Виберіть вашу передплату Azure |
|
||||
| **Група ресурсів** | Створіть нову або використайте наявну |
|
||||
| **Ціновий рівень** | **Free** для розробки/тестування |
|
||||
| **Тип застосунку** | **Single Tenant** (рекомендовано - див. примітку нижче) |
|
||||
| **Тип створення** | **Create new Microsoft App ID** |
|
||||
| **Bot handle** | Назва вашого бота, напр., `openclaw-msteams` (має бути унікальною) |
|
||||
| **Subscription** | Виберіть вашу підписку Azure |
|
||||
| **Resource group** | Створіть нову або використайте наявну |
|
||||
| **Pricing tier** | **Free** для розробки/тестування |
|
||||
| **Type of App** | **Single Tenant** (рекомендовано - див. примітку нижче) |
|
||||
| **Creation type** | **Create new Microsoft App ID** |
|
||||
|
||||
<Warning>
|
||||
Створення нових мультитенантних ботів застаріло після 2025-07-31. Використовуйте **Single Tenant** для нових ботів.
|
||||
Створення нових multi-tenant ботів було застарілим після 2025-07-31. Використовуйте **Single Tenant** для нових ботів.
|
||||
</Warning>
|
||||
|
||||
3. Натисніть **Review + create** → **Create** (зачекайте ~1-2 хвилини)
|
||||
@ -253,7 +249,7 @@ teams app doctor <teamsAppId>
|
||||
1. В Azure Bot → **Configuration**
|
||||
2. Задайте **Messaging endpoint** як URL вашого Webhook:
|
||||
- Production: `https://your-domain.com/api/messages`
|
||||
- Локальна розробка: використайте тунель (див. [Локальна розробка](#local-development-tunneling) нижче)
|
||||
- Локальна розробка: використовуйте тунель (див. [Локальна розробка](#local-development-tunneling) нижче)
|
||||
|
||||
### Крок 4: Увімкніть канал Teams
|
||||
|
||||
@ -263,12 +259,12 @@ teams app doctor <teamsAppId>
|
||||
|
||||
### Крок 5: Зберіть маніфест застосунку Teams
|
||||
|
||||
- Додайте запис `bot` з `botId = <App ID>`.
|
||||
- Додайте запис `bot` із `botId = <App ID>`.
|
||||
- Scopes: `personal`, `team`, `groupChat`.
|
||||
- `supportsFiles: true` (потрібно для обробки файлів у personal scope).
|
||||
- Додайте дозволи RSC (див. [Дозволи RSC](#current-teams-rsc-permissions-manifest)).
|
||||
- Створіть іконки: `outline.png` (32x32) і `color.png` (192x192).
|
||||
- Заархівуйте всі три файли разом: `manifest.json`, `outline.png`, `color.png`.
|
||||
- Запакуйте всі три файли разом у zip: `manifest.json`, `outline.png`, `color.png`.
|
||||
|
||||
### Крок 6: Налаштуйте OpenClaw
|
||||
|
||||
@ -294,19 +290,19 @@ teams app doctor <teamsAppId>
|
||||
|
||||
</details>
|
||||
|
||||
## Федеративна автентифікація (сертифікат плюс керована ідентичність)
|
||||
## Федеративна автентифікація (сертифікат плюс managed identity)
|
||||
|
||||
> Додано у 2026.4.11
|
||||
|
||||
Для production-розгортань OpenClaw підтримує **федеративну автентифікацію** як безпечнішу альтернативу клієнтським секретам. Доступні два методи:
|
||||
Для production-розгортань OpenClaw підтримує **федеративну автентифікацію** як безпечнішу альтернативу client secrets. Доступні два методи:
|
||||
|
||||
### Варіант A: Автентифікація на основі сертифіката
|
||||
|
||||
Використайте PEM-сертифікат, зареєстрований у вашій реєстрації застосунку Entra ID.
|
||||
Використовуйте PEM-сертифікат, зареєстрований у вашій реєстрації застосунку Entra ID.
|
||||
|
||||
**Налаштування:**
|
||||
|
||||
1. Згенеруйте або отримайте сертифікат (PEM-формат із приватним ключем).
|
||||
1. Згенеруйте або отримайте сертифікат (формат PEM із приватним ключем).
|
||||
2. В Entra ID → App Registration → **Certificates & secrets** → **Certificates** → завантажте публічний сертифікат.
|
||||
|
||||
**Конфігурація:**
|
||||
@ -326,26 +322,26 @@ teams app doctor <teamsAppId>
|
||||
}
|
||||
```
|
||||
|
||||
**Змінні середовища:**
|
||||
**Env vars:**
|
||||
|
||||
- `MSTEAMS_AUTH_TYPE=federated`
|
||||
- `MSTEAMS_CERTIFICATE_PATH=/path/to/cert.pem`
|
||||
|
||||
### Варіант B: Azure Managed Identity
|
||||
|
||||
Використовуйте Azure Managed Identity для автентифікації без пароля. Це ідеально для розгортань в інфраструктурі Azure (AKS, App Service, Azure VMs), де доступна керована ідентичність.
|
||||
Використовуйте Azure Managed Identity для автентифікації без пароля. Це ідеально для розгортань в інфраструктурі Azure (AKS, App Service, Azure VMs), де доступна managed identity.
|
||||
|
||||
**Як це працює:**
|
||||
|
||||
1. Pod/VM бота має керовану ідентичність (system-assigned або user-assigned).
|
||||
2. **Федеративні облікові дані ідентичності** зв’язують керовану ідентичність із реєстрацією застосунку Entra ID.
|
||||
3. Під час виконання OpenClaw використовує `@azure/identity`, щоб отримувати токени з endpoint Azure IMDS (`169.254.169.254`).
|
||||
4. Токен передається до Teams SDK для автентифікації бота.
|
||||
1. Pod/VM бота має managed identity (system-assigned або user-assigned).
|
||||
2. **Federated identity credential** пов’язує managed identity із реєстрацією застосунку Entra ID.
|
||||
3. Під час виконання OpenClaw використовує `@azure/identity`, щоб отримувати токени з Azure IMDS endpoint (`169.254.169.254`).
|
||||
4. Токен передається в Teams SDK для автентифікації бота.
|
||||
|
||||
**Передумови:**
|
||||
|
||||
- Інфраструктура Azure з увімкненою керованою ідентичністю (AKS workload identity, App Service, VM)
|
||||
- Федеративні облікові дані ідентичності, створені в реєстрації застосунку Entra ID
|
||||
- Інфраструктура Azure з увімкненою managed identity (AKS workload identity, App Service, VM)
|
||||
- Federated identity credential, створений у реєстрації застосунку Entra ID
|
||||
- Мережевий доступ до IMDS (`169.254.169.254:80`) з pod/VM
|
||||
|
||||
**Конфігурація (system-assigned managed identity):**
|
||||
@ -365,7 +361,7 @@ teams app doctor <teamsAppId>
|
||||
}
|
||||
```
|
||||
|
||||
**Конфігурація (керована ідентичність, призначена користувачем):**
|
||||
**Конфігурація (призначена користувачем керована ідентичність):**
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -389,12 +385,12 @@ teams app doctor <teamsAppId>
|
||||
- `MSTEAMS_USE_MANAGED_IDENTITY=true`
|
||||
- `MSTEAMS_MANAGED_IDENTITY_CLIENT_ID=<client-id>` (лише для призначеної користувачем)
|
||||
|
||||
### Налаштування AKS Workload Identity
|
||||
### Налаштування ідентичності робочого навантаження AKS
|
||||
|
||||
Для розгортань AKS, що використовують ідентичність робочого навантаження:
|
||||
|
||||
1. **Увімкніть ідентичність робочого навантаження** у вашому кластері AKS.
|
||||
2. **Створіть облікові дані федеративної ідентичності** для реєстрації застосунку Entra ID:
|
||||
2. **Створіть облікові дані федеративної ідентичності** у реєстрації застосунку Entra ID:
|
||||
|
||||
```bash
|
||||
az ad app federated-credential create --id <APP_OBJECT_ID> --parameters '{
|
||||
@ -405,7 +401,7 @@ teams app doctor <teamsAppId>
|
||||
}'
|
||||
```
|
||||
|
||||
3. **Додайте анотацію до сервісного облікового запису Kubernetes** з клієнтським ID застосунку:
|
||||
3. **Додайте анотацію до сервісного облікового запису Kubernetes** з ID клієнта застосунку:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
@ -416,7 +412,7 @@ teams app doctor <teamsAppId>
|
||||
azure.workload.identity/client-id: "<APP_CLIENT_ID>"
|
||||
```
|
||||
|
||||
4. **Додайте мітку до pod** для ін’єкції ідентичності робочого навантаження:
|
||||
4. **Додайте мітку до пода** для інʼєкції ідентичності робочого навантаження:
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
@ -424,17 +420,17 @@ teams app doctor <teamsAppId>
|
||||
azure.workload.identity/use: "true"
|
||||
```
|
||||
|
||||
5. **Забезпечте мережевий доступ** до IMDS (`169.254.169.254`) — якщо використовуєте NetworkPolicy, додайте правило вихідного трафіку, що дозволяє трафік до `169.254.169.254/32` на порту 80.
|
||||
5. **Забезпечте мережевий доступ** до IMDS (`169.254.169.254`) — якщо використовується NetworkPolicy, додайте правило вихідного трафіку, яке дозволяє трафік до `169.254.169.254/32` на порті 80.
|
||||
|
||||
### Порівняння типів автентифікації
|
||||
|
||||
| Метод | Конфігурація | Переваги | Недоліки |
|
||||
| ---------------------- | --------------------------------------------- | ------------------------------------- | ----------------------------------------------- |
|
||||
| **Клієнтський секрет** | `appPassword` | Просте налаштування | Потрібна ротація секрету, менш безпечно |
|
||||
| **Сертифікат** | `authType: "federated"` + `certificatePath` | Немає спільного секрету через мережу | Додаткове керування сертифікатами |
|
||||
| **Керована ідентичність** | `authType: "federated"` + `useManagedIdentity` | Без паролів, не потрібно керувати секретами | Потрібна інфраструктура Azure |
|
||||
| Метод | Конфігурація | Переваги | Недоліки |
|
||||
| ------------------------ | --------------------------------------------- | ------------------------------------- | --------------------------------------------- |
|
||||
| **Секрет клієнта** | `appPassword` | Просте налаштування | Потрібна ротація секрету, менш безпечно |
|
||||
| **Сертифікат** | `authType: "federated"` + `certificatePath` | Немає спільного секрету через мережу | Додаткове керування сертифікатами |
|
||||
| **Керована ідентичність** | `authType: "federated"` + `useManagedIdentity` | Без пароля, не потрібно керувати секретами | Потрібна інфраструктура Azure |
|
||||
|
||||
**Поведінка за замовчуванням:** Якщо `authType` не задано, OpenClaw за замовчуванням використовує автентифікацію через клієнтський секрет. Наявні конфігурації продовжують працювати без змін.
|
||||
**Поведінка за замовчуванням:** Коли `authType` не задано, OpenClaw за замовчуванням використовує автентифікацію через секрет клієнта. Наявні конфігурації продовжують працювати без змін.
|
||||
|
||||
## Локальна розробка (тунелювання)
|
||||
|
||||
@ -451,7 +447,7 @@ devtunnel host my-openclaw-bot
|
||||
|
||||
Альтернативи: `ngrok http 3978` або `tailscale funnel 3978` (URL-адреси можуть змінюватися в кожному сеансі).
|
||||
|
||||
Якщо URL-адреса вашого тунелю змінилася, оновіть endpoint:
|
||||
Якщо URL-адреса вашого тунелю зміниться, оновіть кінцеву точку:
|
||||
|
||||
```bash
|
||||
teams app update <teamsAppId> --endpoint "https://<new-url>/api/messages"
|
||||
@ -470,49 +466,49 @@ teams app doctor <teamsAppId>
|
||||
**Надішліть тестове повідомлення:**
|
||||
|
||||
1. Установіть застосунок Teams (скористайтеся посиланням для встановлення з `teams app get <id> --install-link`)
|
||||
2. Знайдіть бота в Teams і надішліть приватне повідомлення
|
||||
2. Знайдіть бота в Teams і надішліть йому DM
|
||||
3. Перевірте журнали Gateway на наявність вхідної активності
|
||||
|
||||
## Змінні середовища
|
||||
|
||||
Усі ключі конфігурації можна натомість задати через змінні середовища:
|
||||
Усі ключі конфігурації натомість можна задавати через змінні середовища:
|
||||
|
||||
- `MSTEAMS_APP_ID`
|
||||
- `MSTEAMS_APP_PASSWORD`
|
||||
- `MSTEAMS_TENANT_ID`
|
||||
- `MSTEAMS_AUTH_TYPE` (необов’язково: `"secret"` або `"federated"`)
|
||||
- `MSTEAMS_CERTIFICATE_PATH` (федеративна + сертифікат)
|
||||
- `MSTEAMS_CERTIFICATE_THUMBPRINT` (необов’язково, не потрібне для автентифікації)
|
||||
- `MSTEAMS_USE_MANAGED_IDENTITY` (федеративна + керована ідентичність)
|
||||
- `MSTEAMS_MANAGED_IDENTITY_CLIENT_ID` (лише MI, призначена користувачем)
|
||||
- `MSTEAMS_AUTH_TYPE` (необовʼязково: `"secret"` або `"federated"`)
|
||||
- `MSTEAMS_CERTIFICATE_PATH` (федеративна автентифікація + сертифікат)
|
||||
- `MSTEAMS_CERTIFICATE_THUMBPRINT` (необовʼязково, не потрібно для автентифікації)
|
||||
- `MSTEAMS_USE_MANAGED_IDENTITY` (федеративна автентифікація + керована ідентичність)
|
||||
- `MSTEAMS_MANAGED_IDENTITY_CLIENT_ID` (лише призначена користувачем керована ідентичність)
|
||||
|
||||
## Дія з інформацією про учасника
|
||||
|
||||
OpenClaw надає дію `member-info` на основі Graph для Microsoft Teams, щоб агенти й автоматизації могли отримувати відомості про учасників каналу (відображуване ім’я, електронну пошту, роль) безпосередньо з Microsoft Graph.
|
||||
OpenClaw надає дію `member-info` на основі Graph для Microsoft Teams, щоб агенти й автоматизації могли отримувати відомості про учасників каналу (відображуване імʼя, електронна пошта, роль) безпосередньо з Microsoft Graph.
|
||||
|
||||
Вимоги:
|
||||
|
||||
- Дозвіл RSC `Member.Read.Group` (уже є в рекомендованому маніфесті)
|
||||
- Для пошуків між командами: дозвіл Graph Application `User.Read.All` зі згодою адміністратора
|
||||
- Для пошуку між командами: дозвіл Graph Application `User.Read.All` зі згодою адміністратора
|
||||
|
||||
Дію обмежує `channels.msteams.actions.memberInfo` (за замовчуванням: увімкнено, коли доступні облікові дані Graph).
|
||||
Дія керується параметром `channels.msteams.actions.memberInfo` (за замовчуванням: увімкнено, коли доступні облікові дані Graph).
|
||||
|
||||
## Контекст історії
|
||||
|
||||
- `channels.msteams.historyLimit` керує тим, скільки останніх повідомлень каналу/групи загортаються в prompt.
|
||||
- Якщо не задано, використовується `messages.groupChat.historyLimit`. Установіть `0`, щоб вимкнути (за замовчуванням 50).
|
||||
- Отримана історія thread фільтрується за списками дозволених відправників (`allowFrom` / `groupAllowFrom`), тому ініціалізація контексту thread включає лише повідомлення від дозволених відправників.
|
||||
- Контекст цитованих вкладень (`ReplyTo*`, отриманий із HTML-відповіді Teams) наразі передається як отримано.
|
||||
- Іншими словами, списки дозволених відправників визначають, хто може запускати агента; сьогодні фільтруються лише окремі додаткові шляхи контексту.
|
||||
- Історію приватних повідомлень можна обмежити за допомогою `channels.msteams.dmHistoryLimit` (ходи користувача). Перевизначення для окремих користувачів: `channels.msteams.dms["<user_id>"].historyLimit`.
|
||||
- `channels.msteams.historyLimit` керує тим, скільки останніх повідомлень каналу/групи загортається в запит.
|
||||
- Має резервне значення `messages.groupChat.historyLimit`. Установіть `0`, щоб вимкнути (за замовчуванням 50).
|
||||
- Отримана історія треду фільтрується за списками дозволених відправників (`allowFrom` / `groupAllowFrom`), тому початкове наповнення контексту треду містить лише повідомлення від дозволених відправників.
|
||||
- Контекст цитованих вкладень (отриманий із HTML-відповіді Teams `ReplyTo*`) наразі передається як отримано.
|
||||
- Іншими словами, списки дозволених визначають, хто може запускати агента; сьогодні фільтруються лише окремі шляхи додаткового контексту.
|
||||
- Історію DM можна обмежити за допомогою `channels.msteams.dmHistoryLimit` (ходи користувача). Перевизначення для окремих користувачів: `channels.msteams.dms["<user_id>"].historyLimit`.
|
||||
|
||||
## Поточні дозволи Teams RSC (маніфест)
|
||||
## Поточні дозволи RSC Teams (маніфест)
|
||||
|
||||
Це **наявні resourceSpecific дозволи** у нашому маніфесті застосунку Teams. Вони застосовуються лише всередині команди/чату, де встановлено застосунок.
|
||||
Це **наявні ресурсно-специфічні дозволи** в маніфесті нашого застосунку Teams. Вони застосовуються лише всередині команди/чату, де встановлено застосунок.
|
||||
|
||||
**Для каналів (область команди):**
|
||||
|
||||
- `ChannelMessage.Read.Group` (Application) - отримувати всі повідомлення каналу без @mention
|
||||
- `ChannelMessage.Read.Group` (Application) - отримувати всі повідомлення каналу без @згадки
|
||||
- `ChannelMessage.Send.Group` (Application)
|
||||
- `Member.Read.Group` (Application)
|
||||
- `Owner.Read.Group` (Application)
|
||||
@ -522,7 +518,7 @@ OpenClaw надає дію `member-info` на основі Graph для Microsof
|
||||
|
||||
**Для групових чатів:**
|
||||
|
||||
- `ChatMessage.Read.Chat` (Application) - отримувати всі повідомлення групового чату без @mention
|
||||
- `ChatMessage.Read.Chat` (Application) - отримувати всі повідомлення групового чату без @згадки
|
||||
|
||||
Щоб додати дозволи RSC через Teams CLI:
|
||||
|
||||
@ -532,7 +528,7 @@ teams app rsc add <teamsAppId> ChannelMessage.Read.Group --type Application
|
||||
|
||||
## Приклад маніфесту Teams (відредаговано)
|
||||
|
||||
Мінімальний дійсний приклад із потрібними полями. Замініть ID та URL-адреси.
|
||||
Мінімальний, чинний приклад із потрібними полями. Замініть ID та URL-адреси.
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -580,17 +576,17 @@ teams app rsc add <teamsAppId> ChannelMessage.Read.Group --type Application
|
||||
}
|
||||
```
|
||||
|
||||
### Застереження щодо маніфесту (обов’язкові поля)
|
||||
### Застереження щодо маніфесту (обовʼязкові поля)
|
||||
|
||||
- `bots[].botId` **має** збігатися з Azure Bot App ID.
|
||||
- `webApplicationInfo.id` **має** збігатися з Azure Bot App ID.
|
||||
- `bots[].scopes` має включати поверхні, які ви плануєте використовувати (`personal`, `team`, `groupChat`).
|
||||
- `bots[].supportsFiles: true` потрібне для обробки файлів в особистій області.
|
||||
- `authorization.permissions.resourceSpecific` має включати читання/надсилання каналів, якщо вам потрібен трафік каналів.
|
||||
- `bots[].supportsFiles: true` потрібен для обробки файлів в особистій області.
|
||||
- `authorization.permissions.resourceSpecific` має включати читання/надсилання для каналів, якщо вам потрібен трафік каналів.
|
||||
|
||||
### Оновлення наявного застосунку
|
||||
|
||||
Щоб оновити вже встановлений застосунок Teams (наприклад, додати дозволи RSC):
|
||||
Щоб оновити вже встановлений застосунок Teams (наприклад, щоб додати дозволи RSC):
|
||||
|
||||
```bash
|
||||
# Download, edit, and re-upload the manifest
|
||||
@ -603,11 +599,11 @@ teams app manifest upload manifest.json <teamsAppId>
|
||||
Після оновлення перевстановіть застосунок у кожній команді, щоб нові дозволи набули чинності, і **повністю закрийте та перезапустіть Teams** (не просто закрийте вікно), щоб очистити кешовані метадані застосунку.
|
||||
|
||||
<details>
|
||||
<summary>Оновлення маніфесту вручну (без CLI)</summary>
|
||||
<summary>Ручне оновлення маніфесту (без CLI)</summary>
|
||||
|
||||
1. Оновіть ваш `manifest.json` новими налаштуваннями
|
||||
2. **Збільште поле `version`** (наприклад, `1.0.0` → `1.1.0`)
|
||||
3. **Повторно запакуйте в zip** маніфест з іконками (`manifest.json`, `outline.png`, `color.png`)
|
||||
3. **Заново запакуйте в zip** маніфест з іконками (`manifest.json`, `outline.png`, `color.png`)
|
||||
4. Завантажте новий zip:
|
||||
- **Teams Admin Center:** застосунки Teams → Керування застосунками → знайдіть ваш застосунок → Завантажити нову версію
|
||||
- **Sideload:** у Teams → Застосунки → Керування вашими застосунками → Завантажити власний застосунок
|
||||
@ -622,57 +618,57 @@ teams app manifest upload manifest.json <teamsAppId>
|
||||
|
||||
- Читання **текстового** вмісту повідомлень каналу.
|
||||
- Надсилання **текстового** вмісту повідомлень каналу.
|
||||
- Отримання файлових вкладень у **особистих (DM)** повідомленнях.
|
||||
- Отримання файлових вкладень **особистих (DM)** повідомлень.
|
||||
|
||||
Не працює:
|
||||
|
||||
- **Вміст зображень або файлів** у каналах/групах (payload містить лише HTML-заглушку).
|
||||
- **Вміст зображень або файлів** у каналі/групі (корисне навантаження містить лише HTML-заглушку).
|
||||
- Завантаження вкладень, збережених у SharePoint/OneDrive.
|
||||
- Читання історії повідомлень (поза live-подією Webhook).
|
||||
- Читання історії повідомлень (поза межами події live Webhook).
|
||||
|
||||
### З **Teams RSC + дозволами Microsoft Graph Application**
|
||||
|
||||
Додає:
|
||||
|
||||
- Завантаження hosted contents (зображень, вставлених у повідомлення).
|
||||
- Завантаження розміщеного вмісту (зображень, вставлених у повідомлення).
|
||||
- Завантаження файлових вкладень, збережених у SharePoint/OneDrive.
|
||||
- Читання історії повідомлень каналів/чатів через Graph.
|
||||
- Читання історії повідомлень каналу/чату через Graph.
|
||||
|
||||
### RSC проти Graph API
|
||||
|
||||
| Можливість | Дозволи RSC | Graph API |
|
||||
| ----------------------- | -------------------- | -------------------------------------- |
|
||||
| **Повідомлення в реальному часі** | Так (через Webhook) | Ні (лише опитування) |
|
||||
| **Історичні повідомлення** | Ні | Так (можна запитувати історію) |
|
||||
| **Складність налаштування** | Лише маніфест застосунку | Потрібна згода адміністратора + token flow |
|
||||
| **Працює офлайн** | Ні (має бути запущено) | Так (запит у будь-який час) |
|
||||
| Можливість | Дозволи RSC | Graph API |
|
||||
| --------------------------- | ------------------- | ------------------------------------------ |
|
||||
| **Повідомлення в реальному часі** | Так (через Webhook) | Ні (лише опитування) |
|
||||
| **Історичні повідомлення** | Ні | Так (можна запитувати історію) |
|
||||
| **Складність налаштування** | Лише маніфест застосунку | Потрібна згода адміністратора + потік токенів |
|
||||
| **Працює офлайн** | Ні (має бути запущено) | Так (запит у будь-який час) |
|
||||
|
||||
**Підсумок:** RSC призначений для прослуховування в реальному часі; Graph API — для доступу до історії. Щоб наздогнати пропущені повідомлення під час офлайну, потрібен Graph API з `ChannelMessage.Read.All` (потрібна згода адміністратора).
|
||||
**Підсумок:** RSC призначено для прослуховування в реальному часі; Graph API — для доступу до історії. Щоб наздоганяти пропущені повідомлення під час офлайну, потрібен Graph API з `ChannelMessage.Read.All` (потрібна згода адміністратора).
|
||||
|
||||
## Медіа й історія з увімкненим Graph (потрібно для каналів)
|
||||
## Медіа та історія з увімкненим Graph (потрібно для каналів)
|
||||
|
||||
Якщо вам потрібні зображення/файли в **каналах** або ви хочете отримувати **історію повідомлень**, потрібно ввімкнути дозволи Microsoft Graph і надати згоду адміністратора.
|
||||
|
||||
1. У **реєстрації застосунку** Entra ID (Azure AD) додайте **дозволи Application** Microsoft Graph:
|
||||
- `ChannelMessage.Read.All` (вкладення каналів + історія)
|
||||
1. В **реєстрації застосунку** Entra ID (Azure AD) додайте **дозволи Application** Microsoft Graph:
|
||||
- `ChannelMessage.Read.All` (вкладення каналу + історія)
|
||||
- `Chat.Read.All` або `ChatMessage.Read.All` (групові чати)
|
||||
2. **Надайте згоду адміністратора** для tenant.
|
||||
3. Збільште **версію маніфесту** застосунку Teams, повторно завантажте його та **перевстановіть застосунок у Teams**.
|
||||
2. **Надайте згоду адміністратора** для клієнта.
|
||||
3. Підвищте **версію маніфесту** застосунку Teams, повторно завантажте його та **перевстановіть застосунок у Teams**.
|
||||
4. **Повністю закрийте та перезапустіть Teams**, щоб очистити кешовані метадані застосунку.
|
||||
|
||||
**Додатковий дозвіл для згадок користувачів:** User @mentions працюють одразу для користувачів у розмові. Однак, якщо ви хочете динамічно шукати й згадувати користувачів, яких **немає в поточній розмові**, додайте дозвіл `User.Read.All` (Application) і надайте згоду адміністратора.
|
||||
**Додатковий дозвіл для згадок користувачів:** @згадки користувачів працюють одразу для користувачів у розмові. Однак, якщо ви хочете динамічно шукати й згадувати користувачів, яких **немає в поточній розмові**, додайте дозвіл `User.Read.All` (Application) і надайте згоду адміністратора.
|
||||
|
||||
## Відомі обмеження
|
||||
|
||||
### Тайм-аути Webhook
|
||||
|
||||
Teams доставляє повідомлення через HTTP Webhook. Якщо обробка триває занадто довго (наприклад, повільні відповіді LLM), ви можете побачити:
|
||||
Teams доставляє повідомлення через HTTP Webhook. Якщо обробка триває надто довго (наприклад, повільні відповіді LLM), ви можете побачити:
|
||||
|
||||
- Тайм-аути Gateway
|
||||
- Teams повторно надсилає повідомлення (що спричиняє дублікати)
|
||||
- Повторні спроби Teams надіслати повідомлення (що спричиняє дублікати)
|
||||
- Відкинуті відповіді
|
||||
|
||||
OpenClaw обробляє це, швидко повертаючи відповідь і проактивно надсилаючи відповіді, але дуже повільні відповіді все одно можуть спричиняти проблеми.
|
||||
OpenClaw обробляє це, швидко повертаючи відповідь і проактивно надсилаючи повідомлення, але дуже повільні відповіді все одно можуть спричиняти проблеми.
|
||||
|
||||
### Форматування
|
||||
|
||||
@ -680,28 +676,28 @@ Markdown у Teams обмеженіший, ніж у Slack або Discord:
|
||||
|
||||
- Базове форматування працює: **жирний**, _курсив_, `code`, посилання
|
||||
- Складний markdown (таблиці, вкладені списки) може відображатися некоректно
|
||||
- Adaptive Cards підтримуються для опитувань і надсилання семантичних презентацій (див. нижче)
|
||||
- Adaptive Cards підтримуються для опитувань і семантичних presentation-відправлень (див. нижче)
|
||||
|
||||
## Конфігурація
|
||||
|
||||
Ключові налаштування (спільні шаблони каналів див. у `/gateway/configuration`):
|
||||
Ключові налаштування (див. `/gateway/configuration` для спільних патернів каналів):
|
||||
|
||||
- `channels.msteams.enabled`: увімкнути/вимкнути канал.
|
||||
- `channels.msteams.appId`, `channels.msteams.appPassword`, `channels.msteams.tenantId`: облікові дані бота.
|
||||
- `channels.msteams.webhook.port` (за замовчуванням `3978`)
|
||||
- `channels.msteams.webhook.path` (за замовчуванням `/api/messages`)
|
||||
- `channels.msteams.dmPolicy`: `pairing | allowlist | open | disabled` (за замовчуванням: pairing)
|
||||
- `channels.msteams.allowFrom`: allowlist для особистих повідомлень (рекомендовано AAD object IDs). Майстер під час налаштування зіставляє імена з ID, коли доступний Graph.
|
||||
- `channels.msteams.dangerouslyAllowNameMatching`: аварійний перемикач для повторного ввімкнення змінного зіставлення за UPN/відображуваним іменем і прямої маршрутизації за назвою команди/каналу.
|
||||
- `channels.msteams.webhook.port` (типово `3978`)
|
||||
- `channels.msteams.webhook.path` (типово `/api/messages`)
|
||||
- `channels.msteams.dmPolicy`: `pairing | allowlist | open | disabled` (типово: pairing)
|
||||
- `channels.msteams.allowFrom`: allowlist для DM (рекомендовано AAD object IDs). Майстер зіставляє імена з ID під час налаштування, коли доступний доступ до Graph.
|
||||
- `channels.msteams.dangerouslyAllowNameMatching`: аварійний перемикач для повторного ввімкнення зіставлення за змінними UPN/display-name і прямої маршрутизації за назвою команди/каналу.
|
||||
- `channels.msteams.textChunkLimit`: розмір фрагмента вихідного тексту.
|
||||
- `channels.msteams.chunkMode`: `length` (за замовчуванням) або `newline`, щоб розділяти за порожніми рядками (межами абзаців) перед поділом за довжиною.
|
||||
- `channels.msteams.mediaAllowHosts`: allowlist для хостів вхідних вкладень (за замовчуванням домени Microsoft/Teams).
|
||||
- `channels.msteams.mediaAuthAllowHosts`: allowlist для додавання заголовків Authorization під час повторних спроб медіа (за замовчуванням хости Graph + Bot Framework).
|
||||
- `channels.msteams.requireMention`: вимагати @mention у каналах/групах (за замовчуванням true).
|
||||
- `channels.msteams.chunkMode`: `length` (типово) або `newline`, щоб ділити за порожніми рядками (межами абзаців) перед поділом за довжиною.
|
||||
- `channels.msteams.mediaAllowHosts`: allowlist для хостів вхідних вкладень (типово домени Microsoft/Teams).
|
||||
- `channels.msteams.mediaAuthAllowHosts`: allowlist для додавання заголовків Authorization під час повторних спроб роботи з медіа (типово хости Graph + Bot Framework).
|
||||
- `channels.msteams.requireMention`: вимагати @mention у каналах/групах (типово true).
|
||||
- `channels.msteams.replyStyle`: `thread | top-level` (див. [Стиль відповіді](#reply-style-threads-vs-posts)).
|
||||
- `channels.msteams.teams.<teamId>.replyStyle`: перевизначення для окремої команди.
|
||||
- `channels.msteams.teams.<teamId>.requireMention`: перевизначення для окремої команди.
|
||||
- `channels.msteams.teams.<teamId>.tools`: стандартні перевизначення політики інструментів для окремої команди (`allow`/`deny`/`alsoAllow`), які використовуються, коли перевизначення каналу відсутнє.
|
||||
- `channels.msteams.teams.<teamId>.tools`: стандартні перевизначення політики інструментів для окремої команди (`allow`/`deny`/`alsoAllow`), що використовуються, коли немає перевизначення для каналу.
|
||||
- `channels.msteams.teams.<teamId>.toolsBySender`: стандартні перевизначення політики інструментів для окремої команди й окремого відправника (підтримується wildcard `"*"`).
|
||||
- `channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle`: перевизначення для окремого каналу.
|
||||
- `channels.msteams.teams.<teamId>.channels.<conversationId>.requireMention`: перевизначення для окремого каналу.
|
||||
@ -709,35 +705,35 @@ Markdown у Teams обмеженіший, ніж у Slack або Discord:
|
||||
- `channels.msteams.teams.<teamId>.channels.<conversationId>.toolsBySender`: перевизначення політики інструментів для окремого каналу й окремого відправника (підтримується wildcard `"*"`).
|
||||
- Ключі `toolsBySender` мають використовувати явні префікси:
|
||||
`id:`, `e164:`, `username:`, `name:` (застарілі ключі без префікса все ще зіставляються лише з `id:`).
|
||||
- `channels.msteams.actions.memberInfo`: увімкнути або вимкнути дію інформації про учасника на основі Graph (за замовчуванням: увімкнено, коли доступні облікові дані Graph).
|
||||
- `channels.msteams.authType`: тип автентифікації — `"secret"` (за замовчуванням) або `"federated"`.
|
||||
- `channels.msteams.certificatePath`: шлях до файлу PEM-сертифіката (федеративна автентифікація + автентифікація за сертифікатом).
|
||||
- `channels.msteams.certificateThumbprint`: відбиток сертифіката (необов’язково, не потрібен для автентифікації).
|
||||
- `channels.msteams.useManagedIdentity`: увімкнути автентифікацію через керовану ідентичність (федеративний режим).
|
||||
- `channels.msteams.managedIdentityClientId`: client ID для user-assigned managed identity.
|
||||
- `channels.msteams.actions.memberInfo`: увімкнути або вимкнути дію отримання інформації про учасника на основі Graph (типово: увімкнено, коли доступні облікові дані Graph).
|
||||
- `channels.msteams.authType`: тип автентифікації — `"secret"` (типово) або `"federated"`.
|
||||
- `channels.msteams.certificatePath`: шлях до PEM-файлу сертифіката (федеративна автентифікація + автентифікація сертифікатом).
|
||||
- `channels.msteams.certificateThumbprint`: відбиток сертифіката (необов’язково, не потрібно для автентифікації).
|
||||
- `channels.msteams.useManagedIdentity`: увімкнути автентифікацію managed identity (федеративний режим).
|
||||
- `channels.msteams.managedIdentityClientId`: client ID для призначеної користувачем managed identity.
|
||||
- `channels.msteams.sharePointSiteId`: SharePoint site ID для завантаження файлів у групових чатах/каналах (див. [Надсилання файлів у групових чатах](#sending-files-in-group-chats)).
|
||||
|
||||
## Маршрутизація та сеанси
|
||||
## Маршрутизація та сесії
|
||||
|
||||
- Ключі сеансів відповідають стандартному формату агента (див. [/concepts/session](/uk/concepts/session)):
|
||||
- Особисті повідомлення спільно використовують головний сеанс (`agent:<agentId>:<mainKey>`).
|
||||
- Ключі сесій відповідають стандартному формату агента (див. [/concepts/session](/uk/concepts/session)):
|
||||
- Прямі повідомлення спільно використовують основну сесію (`agent:<agentId>:<mainKey>`).
|
||||
- Повідомлення каналів/груп використовують conversation id:
|
||||
- `agent:<agentId>:msteams:channel:<conversationId>`
|
||||
- `agent:<agentId>:msteams:group:<conversationId>`
|
||||
|
||||
## Стиль відповіді: гілки проти дописів
|
||||
## Стиль відповіді: треди проти дописів
|
||||
|
||||
Teams нещодавно запровадив два стилі інтерфейсу каналів поверх тієї самої базової моделі даних:
|
||||
Teams нещодавно запровадив два стилі UI каналу поверх тієї самої базової моделі даних:
|
||||
|
||||
| Стиль | Опис | Рекомендований `replyStyle` |
|
||||
| ------------------------ | --------------------------------------------------------- | ------------------------ |
|
||||
| **Posts** (класичний) | Повідомлення відображаються як картки з гілковими відповідями під ними | `thread` (за замовчуванням) |
|
||||
| **Posts** (класичний) | Повідомлення відображаються як картки з тредовими відповідями під ними | `thread` (типово) |
|
||||
| **Threads** (як у Slack) | Повідомлення йдуть лінійно, більше схоже на Slack | `top-level` |
|
||||
|
||||
**Проблема:** Teams API не показує, який стиль інтерфейсу використовує канал. Якщо використати неправильний `replyStyle`:
|
||||
**Проблема:** Teams API не показує, який стиль UI використовує канал. Якщо використати неправильний `replyStyle`:
|
||||
|
||||
- `thread` у каналі зі стилем Threads → відповіді виглядають незручно вкладеними
|
||||
- `top-level` у каналі зі стилем Posts → відповіді відображаються як окремі дописи верхнього рівня, а не в гілці
|
||||
- `thread` у каналі стилю Threads → відповіді виглядають незручно вкладеними
|
||||
- `top-level` у каналі стилю Posts → відповіді відображаються як окремі дописи верхнього рівня, а не всередині треду
|
||||
|
||||
**Рішення:** Налаштуйте `replyStyle` для кожного каналу залежно від того, як налаштовано канал:
|
||||
|
||||
@ -764,33 +760,33 @@ Teams нещодавно запровадив два стилі інтерфей
|
||||
|
||||
**Поточні обмеження:**
|
||||
|
||||
- **Особисті повідомлення:** Зображення та файлові вкладення працюють через файлові API бота Teams.
|
||||
- **Канали/групи:** Вкладення зберігаються в сховищі M365 (SharePoint/OneDrive). Корисне навантаження Webhook містить лише HTML-заглушку, а не фактичні байти файлу. **Для завантаження вкладень каналів потрібні дозволи Graph API**.
|
||||
- Для явного надсилання, де файл є основним вмістом, використовуйте `action=upload-file` з `media` / `filePath` / `path`; необов’язковий `message` стає супровідним текстом/коментарем, а `filename` перевизначає назву завантаженого файлу.
|
||||
- **DM:** Зображення й файлові вкладення працюють через файлові API бота Teams.
|
||||
- **Канали/групи:** Вкладення зберігаються в сховищі M365 (SharePoint/OneDrive). Webhook payload містить лише HTML-заглушку, а не фактичні байти файлу. **Для завантаження вкладень каналів потрібні дозволи Graph API**.
|
||||
- Для явних надсилань, де файл є основним вмістом, використовуйте `action=upload-file` з `media` / `filePath` / `path`; необов’язковий `message` стає супровідним текстом/коментарем, а `filename` перевизначає назву завантаженого файлу.
|
||||
|
||||
Без дозволів Graph повідомлення каналу із зображеннями будуть отримані лише як текст (вміст зображення недоступний боту).
|
||||
За замовчуванням OpenClaw завантажує медіа лише з імен хостів Microsoft/Teams. Перевизначте через `channels.msteams.mediaAllowHosts` (використовуйте `["*"]`, щоб дозволити будь-який хост).
|
||||
Заголовки Authorization додаються лише для хостів у `channels.msteams.mediaAuthAllowHosts` (за замовчуванням хости Graph + Bot Framework). Тримайте цей список суворим (уникайте суфіксів для кількох tenant).
|
||||
Без дозволів Graph повідомлення каналів із зображеннями надходитимуть лише як текст (вміст зображення недоступний боту).
|
||||
Типово OpenClaw завантажує медіа лише з імен хостів Microsoft/Teams. Перевизначте це за допомогою `channels.msteams.mediaAllowHosts` (використовуйте `["*"]`, щоб дозволити будь-який хост).
|
||||
Заголовки Authorization додаються лише для хостів у `channels.msteams.mediaAuthAllowHosts` (типово хости Graph + Bot Framework). Тримайте цей список суворим (уникайте багатотенантних суфіксів).
|
||||
|
||||
## Надсилання файлів у групових чатах
|
||||
|
||||
Боти можуть надсилати файли в особистих повідомленнях за допомогою потоку FileConsentCard (вбудовано). Однак **надсилання файлів у групових чатах/каналах** потребує додаткового налаштування:
|
||||
Боти можуть надсилати файли в DM за допомогою потоку FileConsentCard (вбудовано). Однак **надсилання файлів у групових чатах/каналах** потребує додаткового налаштування:
|
||||
|
||||
| Контекст | Як надсилаються файли | Потрібне налаштування |
|
||||
| ------------------------ | -------------------------------------------- | ----------------------------------------------- |
|
||||
| **Особисті повідомлення** | FileConsentCard → користувач приймає → бот завантажує | Працює одразу |
|
||||
| **DM** | FileConsentCard → користувач приймає → бот завантажує | Працює одразу |
|
||||
| **Групові чати/канали** | Завантаження в SharePoint → поширення посилання | Потрібні `sharePointSiteId` + дозволи Graph |
|
||||
| **Зображення (будь-який контекст)** | Inline у кодуванні Base64 | Працює одразу |
|
||||
| **Зображення (будь-який контекст)** | Вбудовано з кодуванням Base64 | Працює одразу |
|
||||
|
||||
### Чому груповим чатам потрібен SharePoint
|
||||
|
||||
Боти не мають персонального диска OneDrive (endpoint Graph API `/me/drive` не працює для application identities). Щоб надсилати файли в групових чатах/каналах, бот завантажує їх на **сайт SharePoint** і створює посилання для спільного доступу.
|
||||
Боти не мають особистого диска OneDrive (endpoint Graph API `/me/drive` не працює для ідентичностей застосунків). Щоб надсилати файли в групові чати/канали, бот завантажує їх на **сайт SharePoint** і створює посилання для поширення.
|
||||
|
||||
### Налаштування
|
||||
|
||||
1. **Додайте дозволи Graph API** в Entra ID (Azure AD) → App Registration:
|
||||
- `Sites.ReadWrite.All` (Application) - завантаження файлів у SharePoint
|
||||
- `Chat.Read.All` (Application) - необов’язково, вмикає посилання для спільного доступу на рівні користувача
|
||||
- `Chat.Read.All` (Application) - необов’язково, вмикає посилання для поширення для окремих користувачів
|
||||
|
||||
2. **Надайте згоду адміністратора** для tenant.
|
||||
|
||||
@ -821,40 +817,40 @@ Teams нещодавно запровадив два стилі інтерфей
|
||||
}
|
||||
```
|
||||
|
||||
### Поведінка спільного доступу
|
||||
### Поведінка поширення
|
||||
|
||||
| Дозвіл | Поведінка спільного доступу |
|
||||
| Дозвіл | Поведінка поширення |
|
||||
| --------------------------------------- | --------------------------------------------------------- |
|
||||
| Лише `Sites.ReadWrite.All` | Посилання для спільного доступу в межах організації (доступ має будь-хто в організації) |
|
||||
| `Sites.ReadWrite.All` + `Chat.Read.All` | Посилання для спільного доступу на рівні користувача (доступ мають лише учасники чату) |
|
||||
| лише `Sites.ReadWrite.All` | Посилання для поширення на всю організацію (доступ має будь-хто в організації) |
|
||||
| `Sites.ReadWrite.All` + `Chat.Read.All` | Посилання для поширення для окремих користувачів (доступ мають лише учасники чату) |
|
||||
|
||||
Спільний доступ на рівні користувача безпечніший, оскільки лише учасники чату можуть отримати доступ до файлу. Якщо дозволу `Chat.Read.All` немає, бот повертається до спільного доступу в межах організації.
|
||||
Поширення для окремих користувачів безпечніше, оскільки доступ до файлу мають лише учасники чату. Якщо дозволу `Chat.Read.All` бракує, бот повертається до поширення на всю організацію.
|
||||
|
||||
### Резервна поведінка
|
||||
|
||||
| Сценарій | Результат |
|
||||
| ------------------------------------------------- | -------------------------------------------------- |
|
||||
| Груповий чат + файл + налаштований `sharePointSiteId` | Завантаження в SharePoint, надсилання посилання для спільного доступу |
|
||||
| Груповий чат + файл + без `sharePointSiteId` | Спроба завантаження в OneDrive (може завершитися помилкою), надсилання лише тексту |
|
||||
| Груповий чат + файл + налаштовано `sharePointSiteId` | Завантаження в SharePoint, надсилання посилання для поширення |
|
||||
| Груповий чат + файл + немає `sharePointSiteId` | Спроба завантаження в OneDrive (може завершитися помилкою), надсилання лише тексту |
|
||||
| Особистий чат + файл | Потік FileConsentCard (працює без SharePoint) |
|
||||
| Будь-який контекст + зображення | Inline у кодуванні Base64 (працює без SharePoint) |
|
||||
| Будь-який контекст + зображення | Вбудовано з кодуванням Base64 (працює без SharePoint) |
|
||||
|
||||
### Місце зберігання файлів
|
||||
### Розташування збережених файлів
|
||||
|
||||
Завантажені файли зберігаються в папці `/OpenClawShared/` у стандартній бібліотеці документів налаштованого сайту SharePoint.
|
||||
|
||||
## Опитування (Adaptive Cards)
|
||||
|
||||
OpenClaw надсилає опитування Teams як Adaptive Cards (нативного Teams poll API немає).
|
||||
OpenClaw надсилає опитування Teams як Adaptive Cards (нативного API опитувань Teams немає).
|
||||
|
||||
- CLI: `openclaw message poll --channel msteams --target conversation:<id> ...`
|
||||
- Голоси записуються Gateway у `~/.openclaw/msteams-polls.json`.
|
||||
- Gateway має залишатися онлайн, щоб записувати голоси.
|
||||
- Опитування поки що не публікують підсумки результатів автоматично (за потреби перегляньте файл сховища).
|
||||
- Опитування поки що не публікують автоматично підсумки результатів (за потреби перегляньте файл сховища).
|
||||
|
||||
## Картки презентацій
|
||||
## Presentation-картки
|
||||
|
||||
Надсилайте семантичні презентаційні payload користувачам або розмовам Teams за допомогою інструмента `message` або CLI. OpenClaw відтворює їх як Teams Adaptive Cards із загального контракту презентацій.
|
||||
Надсилайте семантичні presentation payloads користувачам або conversations Teams за допомогою інструмента `message` або CLI. OpenClaw рендерить їх як Teams Adaptive Cards із загального presentation-контракту.
|
||||
|
||||
Параметр `presentation` приймає семантичні блоки. Коли надано `presentation`, текст повідомлення необов’язковий.
|
||||
|
||||
@ -880,13 +876,13 @@ openclaw message send --channel msteams \
|
||||
--presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello!"}]}'
|
||||
```
|
||||
|
||||
Докладніше про формат цілі див. у [Формати цілей](#target-formats) нижче.
|
||||
Докладніше про формат target див. у розділі [Формати target](#target-formats) нижче.
|
||||
|
||||
## Формати цілей
|
||||
## Формати target
|
||||
|
||||
Цілі MSTeams використовують префікси, щоб розрізняти користувачів і розмови:
|
||||
Targets MSTeams використовують префікси, щоб розрізняти користувачів і conversations:
|
||||
|
||||
| Тип цілі | Формат | Приклад |
|
||||
| Тип target | Формат | Приклад |
|
||||
| ------------------- | -------------------------------- | --------------------------------------------------- |
|
||||
| Користувач (за ID) | `user:<aad-object-id>` | `user:40a1a0ed-4ff2-4164-a219-55518990c197` |
|
||||
| Користувач (за іменем) | `user:<display-name>` | `user:John Smith` (потрібен Graph API) |
|
||||
@ -934,24 +930,24 @@ openclaw message send --channel msteams --target "conversation:19:abc...@thread.
|
||||
```
|
||||
|
||||
<Note>
|
||||
Без префікса `user:` імена за замовчуванням розпізнаються як групи або команди. Завжди використовуйте `user:`, коли націлюєтеся на людей за відображуваним іменем.
|
||||
Без префікса `user:` імена за замовчуванням обробляються як групи або команди. Завжди використовуйте `user:`, коли вказуєте людей за відображуваним іменем.
|
||||
</Note>
|
||||
|
||||
## Проактивний обмін повідомленнями
|
||||
## Проактивні повідомлення
|
||||
|
||||
- Проактивні повідомлення можливі лише **після** взаємодії користувача, оскільки в цей момент ми зберігаємо посилання на розмову.
|
||||
- Див. `/gateway/configuration` щодо `dmPolicy` і керування списком дозволених.
|
||||
- Див. `/gateway/configuration` для `dmPolicy` і керування allowlist.
|
||||
|
||||
## Ідентифікатори команд і каналів (поширена пастка)
|
||||
## ID команди та каналу (поширена помилка)
|
||||
|
||||
Параметр запиту `groupId` в URL Teams **НЕ** є ідентифікатором команди, який використовується для конфігурації. Натомість витягайте ідентифікатори зі шляху URL:
|
||||
Параметр запиту `groupId` в URL Teams **НЕ** є ID команди, що використовується для конфігурації. Натомість витягуйте ID зі шляху URL:
|
||||
|
||||
**URL команди:**
|
||||
|
||||
```
|
||||
https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=...
|
||||
└────────────────────────────┘
|
||||
Team ID (URL-decode this)
|
||||
ID розмови команди (URL-декодуйте це)
|
||||
```
|
||||
|
||||
**URL каналу:**
|
||||
@ -959,46 +955,46 @@ https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?gro
|
||||
```
|
||||
https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=...
|
||||
└─────────────────────────┘
|
||||
Channel ID (URL-decode this)
|
||||
ID каналу (URL-декодуйте це)
|
||||
```
|
||||
|
||||
**Для конфігурації:**
|
||||
|
||||
- Ідентифікатор команди = сегмент шляху після `/team/` (URL-декодований, наприклад, `19:Bk4j...@thread.tacv2`)
|
||||
- Ідентифікатор каналу = сегмент шляху після `/channel/` (URL-декодований)
|
||||
- **Ігноруйте** параметр запиту `groupId`
|
||||
- Ключ команди = сегмент шляху після `/team/` (URL-декодований, напр., `19:Bk4j...@thread.tacv2`; старіші тенанти можуть показувати `@thread.skype`, що також є чинним)
|
||||
- Ключ каналу = сегмент шляху після `/channel/` (URL-декодований)
|
||||
- **Ігноруйте** параметр запиту `groupId` для маршрутизації OpenClaw. Це ID групи Microsoft Entra, а не ID розмови Bot Framework, що використовується у вхідних активностях Teams.
|
||||
|
||||
## Приватні канали
|
||||
|
||||
Боти мають обмежену підтримку в приватних каналах:
|
||||
|
||||
| Функція | Стандартні канали | Приватні канали |
|
||||
| ---------------------------- | ----------------- | ------------------------------- |
|
||||
| Встановлення бота | Так | Обмежено |
|
||||
| Повідомлення в реальному часі (webhook) | Так | Може не працювати |
|
||||
| Дозволи RSC | Так | Можуть поводитися інакше |
|
||||
| @згадки | Так | Якщо бот доступний |
|
||||
| Історія Graph API | Так | Так (за наявності дозволів) |
|
||||
| Функція | Стандартні канали | Приватні канали |
|
||||
| ----------------------------- | ----------------- | ---------------------------- |
|
||||
| Встановлення бота | Так | Обмежено |
|
||||
| Повідомлення в реальному часі (Webhook) | Так | Може не працювати |
|
||||
| Дозволи RSC | Так | Можуть поводитися інакше |
|
||||
| @mentions | Так | Якщо бот доступний |
|
||||
| Історія Graph API | Так | Так (з дозволами) |
|
||||
|
||||
**Обхідні рішення, якщо приватні канали не працюють:**
|
||||
**Обхідні варіанти, якщо приватні канали не працюють:**
|
||||
|
||||
1. Використовуйте стандартні канали для взаємодії з ботом
|
||||
2. Використовуйте DM - користувачі завжди можуть написати боту напряму
|
||||
2. Використовуйте приватні повідомлення - користувачі завжди можуть написати боту напряму
|
||||
3. Використовуйте Graph API для доступу до історії (потрібен `ChannelMessage.Read.All`)
|
||||
|
||||
## Усунення несправностей
|
||||
## Усунення неполадок
|
||||
|
||||
### Поширені проблеми
|
||||
|
||||
- **Зображення не відображаються в каналах:** відсутні дозволи Graph або згода адміністратора. Перевстановіть застосунок Teams і повністю закрийте/знову відкрийте Teams.
|
||||
- **Немає відповідей у каналі:** за замовчуванням потрібні згадки; встановіть `channels.msteams.requireMention=false` або налаштуйте окремо для команди/каналу.
|
||||
- **Невідповідність версії (Teams усе ще показує старий маніфест):** видаліть і повторно додайте застосунок, а потім повністю закрийте Teams, щоб оновити дані.
|
||||
- **401 Unauthorized від webhook:** очікувано під час ручного тестування без Azure JWT - це означає, що кінцева точка доступна, але автентифікація не вдалася. Використовуйте Azure Web Chat для коректного тестування.
|
||||
- **Зображення не відображаються в каналах:** відсутні дозволи Graph або згода адміністратора. Перевстановіть застосунок Teams і повністю закрийте та знову відкрийте Teams.
|
||||
- **Немає відповідей у каналі:** згадки потрібні за замовчуванням; задайте `channels.msteams.requireMention=false` або налаштуйте для конкретної команди/каналу.
|
||||
- **Невідповідність версії (Teams досі показує старий маніфест):** видаліть і повторно додайте застосунок, а також повністю закрийте Teams для оновлення.
|
||||
- **401 Unauthorized від Webhook:** очікувано під час ручного тестування без Azure JWT - це означає, що endpoint доступний, але автентифікація не пройшла. Використовуйте Azure Web Chat для коректного тестування.
|
||||
|
||||
### Помилки завантаження маніфесту
|
||||
|
||||
- **"Icon file cannot be empty":** маніфест посилається на файли піктограм розміром 0 байтів. Створіть коректні PNG-піктограми (32x32 для `outline.png`, 192x192 для `color.png`).
|
||||
- **"webApplicationInfo.Id already in use":** застосунок усе ще встановлено в іншій команді/чаті. Спочатку знайдіть і видаліть його або зачекайте 5-10 хвилин на поширення змін.
|
||||
- **"Icon file cannot be empty":** маніфест посилається на файли іконок розміром 0 байтів. Створіть коректні PNG-іконки (32x32 для `outline.png`, 192x192 для `color.png`).
|
||||
- **"webApplicationInfo.Id already in use":** застосунок досі встановлений в іншій команді/чаті. Спершу знайдіть і видаліть його або зачекайте 5-10 хвилин на поширення змін.
|
||||
- **"Something went wrong" під час завантаження:** натомість завантажте через [https://admin.teams.microsoft.com](https://admin.teams.microsoft.com), відкрийте DevTools браузера (F12) → вкладку Network і перевірте тіло відповіді на фактичну помилку.
|
||||
- **Sideload не вдається:** спробуйте "Upload an app to your org's app catalog" замість "Upload a custom app" - це часто обходить обмеження sideload.
|
||||
|
||||
@ -1007,23 +1003,23 @@ https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?gr
|
||||
1. Переконайтеся, що `webApplicationInfo.id` точно збігається з App ID вашого бота
|
||||
2. Повторно завантажте застосунок і перевстановіть його в команді/чаті
|
||||
3. Перевірте, чи адміністратор вашої організації не заблокував дозволи RSC
|
||||
4. Підтвердьте, що використовуєте правильну область: `ChannelMessage.Read.Group` для команд, `ChatMessage.Read.Chat` для групових чатів
|
||||
4. Підтвердьте, що використовуєте правильний scope: `ChannelMessage.Read.Group` для команд, `ChatMessage.Read.Chat` для групових чатів
|
||||
|
||||
## Посилання
|
||||
|
||||
- [Створити Azure Bot](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration) - посібник із налаштування Azure Bot
|
||||
- [Портал розробника Teams](https://dev.teams.microsoft.com/apps) - створення й керування застосунками Teams
|
||||
- [Створення Azure Bot](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration) - посібник із налаштування Azure Bot
|
||||
- [Teams Developer Portal](https://dev.teams.microsoft.com/apps) - створення й керування застосунками Teams
|
||||
- [Схема маніфесту застосунку Teams](https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema)
|
||||
- [Отримання повідомлень каналу за допомогою RSC](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/channel-messages-with-rsc)
|
||||
- [Отримання повідомлень каналу з RSC](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/channel-messages-with-rsc)
|
||||
- [Довідник дозволів RSC](https://learn.microsoft.com/en-us/microsoftteams/platform/graph-api/rsc/resource-specific-consent)
|
||||
- [Обробка файлів ботом Teams](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bots-filesv4) (канал/група потребує Graph)
|
||||
- [Проактивний обмін повідомленнями](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages)
|
||||
- [@microsoft/teams.cli](https://www.npmjs.com/package/@microsoft/teams.cli) - Teams CLI для керування ботом
|
||||
- [Обробка файлів ботом Teams](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bots-filesv4) (для каналу/групи потрібен Graph)
|
||||
- [Проактивні повідомлення](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages)
|
||||
- [@microsoft/teams.cli](https://www.npmjs.com/package/@microsoft/teams.cli) - Teams CLI для керування ботами
|
||||
|
||||
## Пов’язане
|
||||
|
||||
- [Огляд каналів](/uk/channels) — усі підтримувані канали
|
||||
- [Сполучення](/uk/channels/pairing) — автентифікація DM і процес сполучення
|
||||
- [Сполучення](/uk/channels/pairing) — автентифікація в приватних повідомленнях і потік сполучення
|
||||
- [Групи](/uk/channels/groups) — поведінка групового чату та керування згадками
|
||||
- [Маршрутизація каналів](/uk/channels/channel-routing) — маршрутизація сеансів для повідомлень
|
||||
- [Безпека](/uk/gateway/security) — модель доступу та зміцнення безпеки
|
||||
- [Маршрутизація каналів](/uk/channels/channel-routing) — маршрутизація сесій для повідомлень
|
||||
- [Безпека](/uk/gateway/security) — модель доступу та посилення захисту
|
||||
|
||||
@ -2,129 +2,129 @@
|
||||
read_when:
|
||||
- Ви створюєте новий Plugin каналу обміну повідомленнями
|
||||
- Ви хочете підключити OpenClaw до платформи обміну повідомленнями
|
||||
- Потрібно зрозуміти поверхню адаптера ChannelPlugin
|
||||
- Потрібно розуміти інтерфейс адаптера ChannelPlugin
|
||||
sidebarTitle: Channel Plugins
|
||||
summary: Покроковий посібник зі створення Plugin каналу обміну повідомленнями для OpenClaw
|
||||
title: Створення Plugin-ів каналів
|
||||
title: Створення Plugin для каналів
|
||||
x-i18n:
|
||||
generated_at: "2026-04-29T15:39:41Z"
|
||||
generated_at: "2026-04-30T00:42:36Z"
|
||||
model: gpt-5.5
|
||||
provider: openai
|
||||
source_hash: 03384057a4316b87c6088d3859d16ed4546c803f7c64639cd12be293f4841258
|
||||
source_hash: 068cd797f7761efa54f4fdeb7cb4aa784ceace959f1af12bc549c16ed2776b72
|
||||
source_path: plugins/sdk-channel-plugins.md
|
||||
workflow: 16
|
||||
---
|
||||
|
||||
Цей посібник покроково показує, як створити Plugin каналу, що підключає OpenClaw до
|
||||
Цей посібник показує, як створити Plugin каналу, який підключає OpenClaw до
|
||||
платформи обміну повідомленнями. Наприкінці ви матимете робочий канал із безпекою DM,
|
||||
спарюванням, ланцюжками відповідей і вихідними повідомленнями.
|
||||
сполученням, гілкуванням відповідей і вихідними повідомленнями.
|
||||
|
||||
<Info>
|
||||
Якщо ви ще не створювали жодного Plugin OpenClaw, спершу прочитайте
|
||||
[Початок роботи](/uk/plugins/building-plugins), щоб дізнатися про базову структуру
|
||||
пакета та налаштування маніфесту.
|
||||
Якщо ви раніше не створювали жодного Plugin OpenClaw, спершу прочитайте
|
||||
[Початок роботи](/uk/plugins/building-plugins), щоб дізнатися базову структуру
|
||||
пакета й налаштування маніфесту.
|
||||
</Info>
|
||||
|
||||
## Як працюють Plugins каналів
|
||||
## Як працюють Plugin каналів
|
||||
|
||||
Plugins каналів не потребують власних інструментів надсилання/редагування/реакцій. OpenClaw тримає один
|
||||
Plugin для каналів не потребують власних інструментів надсилання, редагування чи реакцій. OpenClaw тримає один
|
||||
спільний інструмент `message` у ядрі. Ваш Plugin відповідає за:
|
||||
|
||||
- **Конфігурація** — визначення акаунта та майстер налаштування
|
||||
- **Безпека** — політика DM та списки дозволених
|
||||
- **Спарювання** — потік підтвердження через DM
|
||||
- **Граматика сесії** — як ідентифікатори розмов конкретного провайдера відображаються на базові чати, ідентифікатори ланцюжків і батьківські резервні варіанти
|
||||
- **Вихідні повідомлення** — надсилання тексту, медіа та опитувань на платформу
|
||||
- **Ланцюжки** — як групуються відповіді
|
||||
- **Heartbeat typing** — необов'язкові сигнали набору/зайнятості для цілей доставки Heartbeat
|
||||
- **Конфігурація** — визначення акаунта й майстер налаштування
|
||||
- **Безпека** — політика DM і списки дозволених
|
||||
- **Сполучення** — потік схвалення через DM
|
||||
- **Граматика сеансів** — як специфічні для провайдера ідентифікатори розмов зіставляються з базовими чатами, ідентифікаторами гілок і батьківськими резервними варіантами
|
||||
- **Вихідні повідомлення** — надсилання тексту, медіа й опитувань на платформу
|
||||
- **Гілкування** — як відповіді потрапляють у гілки
|
||||
- **Набір для Heartbeat** — необов'язкові сигнали набору/зайнятості для цілей доставки Heartbeat
|
||||
|
||||
Ядро відповідає за спільний інструмент повідомлень, підключення промптів, зовнішню форму ключа сесії,
|
||||
Ядро відповідає за спільний інструмент повідомлень, підключення промптів, зовнішню форму ключа сеансу,
|
||||
загальний облік `:thread:` і диспетчеризацію.
|
||||
|
||||
Якщо ваш канал підтримує індикатори набору поза вхідними відповідями, експонуйте
|
||||
`heartbeat.sendTyping(...)` у Plugin каналу. Ядро викликає його з
|
||||
визначеною ціллю доставки Heartbeat перед початком запуску моделі Heartbeat і
|
||||
використовує спільний життєвий цикл підтримання/очищення індикатора набору. Додайте `heartbeat.clearTyping(...)`,
|
||||
розв'язаною ціллю доставки Heartbeat перед початком запуску моделі Heartbeat і
|
||||
використовує спільний життєвий цикл підтримання та очищення набору. Додайте `heartbeat.clearTyping(...)`,
|
||||
коли платформі потрібен явний сигнал зупинки.
|
||||
|
||||
Якщо ваш канал додає параметри інструмента повідомлень, що несуть джерела медіа, експонуйте ці
|
||||
Якщо ваш канал додає параметри інструмента повідомлень, які несуть джерела медіа, експонуйте ці
|
||||
назви параметрів через `describeMessageTool(...).mediaSourceParams`. Ядро використовує
|
||||
цей явний список для нормалізації шляхів sandbox і політики доступу до вихідних медіа,
|
||||
тож Plugins не потребують спеціальних випадків у спільному ядрі для параметрів аватарів,
|
||||
вкладень або обкладинок, специфічних для провайдера.
|
||||
Бажано повертати мапу за ключами дій, наприклад
|
||||
цей явний список для нормалізації шляхів пісочниці й політики доступу до вихідних медіа,
|
||||
тому Plugin не потребують спеціальних випадків у спільному ядрі для специфічних для провайдера
|
||||
параметрів аватара, вкладення або зображення обкладинки.
|
||||
Надавайте перевагу поверненню мапи з ключами за діями, наприклад
|
||||
`{ "set-profile": ["avatarUrl", "avatarPath"] }`, щоб непов'язані дії не
|
||||
успадковували медіааргументи іншої дії. Плоский масив також працює для параметрів, які
|
||||
навмисно спільні для всіх експонованих дій.
|
||||
навмисно спільні для кожної експонованої дії.
|
||||
|
||||
Якщо ваша платформа зберігає додатковий scope всередині ідентифікаторів розмов, тримайте цей парсинг
|
||||
Якщо ваша платформа зберігає додаткову область усередині ідентифікаторів розмов, тримайте цей розбір
|
||||
у Plugin через `messaging.resolveSessionConversation(...)`. Це
|
||||
канонічний хук для відображення `rawId` на базовий ідентифікатор розмови, необов'язковий ідентифікатор ланцюжка,
|
||||
явний `baseConversationId` і будь-які `parentConversationCandidates`.
|
||||
Коли повертаєте `parentConversationCandidates`, тримайте їх упорядкованими від
|
||||
найвужчого батьківського елемента до найширшої/базової розмови.
|
||||
канонічний хук для зіставлення `rawId` з базовим ідентифікатором розмови, необов'язковим ідентифікатором гілки,
|
||||
явним `baseConversationId` і будь-якими `parentConversationCandidates`.
|
||||
Коли ви повертаєте `parentConversationCandidates`, зберігайте їх упорядкованими від
|
||||
найвужчого батька до найширшої/базової розмови.
|
||||
|
||||
Використовуйте `openclaw/plugin-sdk/channel-route`, коли коду Plugin потрібно нормалізувати
|
||||
поля, схожі на маршрути, порівняти дочірній ланцюжок із його батьківським маршрутом або побудувати
|
||||
стабільний ключ дедуплікації з `{ channel, to, accountId, threadId }`. Помічник
|
||||
нормалізує числові ідентифікатори ланцюжків так само, як це робить ядро, тому Plugins мають віддавати
|
||||
йому перевагу над ситуативними порівняннями `String(threadId)`.
|
||||
Plugins із граматикою цілей, специфічною для провайдера, можуть інжектувати свій парсер у
|
||||
`resolveChannelRouteTargetWithParser(...)` і все одно отримувати ту саму форму цілі маршруту
|
||||
та семантику резервного ланцюжка, яку використовує ядро.
|
||||
поля, схожі на маршрути, порівняти дочірню гілку з її батьківським маршрутом або побудувати
|
||||
стабільний ключ дедуплікації з `{ channel, to, accountId, threadId }`. Допоміжний засіб
|
||||
нормалізує числові ідентифікатори гілок так само, як це робить ядро, тому Plugin мають надавати
|
||||
йому перевагу перед спеціальними порівняннями `String(threadId)`.
|
||||
Plugin із специфічною для провайдера граматикою цілей можуть ін'єктувати свій парсер у
|
||||
`resolveChannelRouteTargetWithParser(...)` і все одно отримати ту саму форму цілі маршруту
|
||||
та семантику резервних гілок, яку використовує ядро.
|
||||
|
||||
Вбудовані Plugins, яким потрібен той самий парсинг до запуску реєстру каналів,
|
||||
також можуть експонувати файл верхнього рівня `session-key-api.ts` з відповідним
|
||||
експортом `resolveSessionConversation(...)`. Ядро використовує цю безпечну для bootstrap поверхню
|
||||
лише тоді, коли runtime-реєстр Plugin ще недоступний.
|
||||
Вбудовані Plugin, яким потрібен той самий розбір до завантаження реєстру каналів,
|
||||
також можуть експонувати файл верхнього рівня `session-key-api.ts` із відповідним
|
||||
експортом `resolveSessionConversation(...)`. Ядро використовує цю безпечну для початкового завантаження поверхню
|
||||
лише тоді, коли реєстр Plugin часу виконання ще недоступний.
|
||||
|
||||
`messaging.resolveParentConversationCandidates(...)` залишається доступним як
|
||||
застарілий резерв сумісності, коли Plugin потребує лише батьківських резервних варіантів поверх
|
||||
загального/raw ідентифікатора. Якщо існують обидва хуки, ядро спершу використовує
|
||||
`resolveSessionConversation(...).parentConversationCandidates` і лише
|
||||
застарілий резервний механізм сумісності, коли Plugin потребує лише батьківських резервних варіантів поверх
|
||||
загального/сирого ідентифікатора. Якщо існують обидва хуки, ядро спочатку використовує
|
||||
`resolveSessionConversation(...).parentConversationCandidates` і лише потім
|
||||
переходить до `resolveParentConversationCandidates(...)`, коли канонічний хук
|
||||
їх пропускає.
|
||||
їх не надає.
|
||||
|
||||
## Підтвердження та можливості каналу
|
||||
## Схвалення та можливості каналів
|
||||
|
||||
Більшості Plugins каналів не потрібен код, специфічний для підтверджень.
|
||||
Більшість Plugin для каналів не потребують коду, специфічного для схвалень.
|
||||
|
||||
- Ядро відповідає за same-chat `/approve`, спільні payload підтверджувальних кнопок і загальну резервну доставку.
|
||||
- Віддавайте перевагу одному об'єкту `approvalCapability` у Plugin каналу, коли каналу потрібна поведінка, специфічна для підтверджень.
|
||||
- `ChannelPlugin.approvals` видалено. Розміщуйте факти доставки/native/render/auth для підтверджень у `approvalCapability`.
|
||||
- `plugin.auth` призначений лише для входу/виходу; ядро більше не читає хуки auth підтверджень із цього об'єкта.
|
||||
- `approvalCapability.authorizeActorAction` і `approvalCapability.getActionAvailabilityState` є канонічним seam auth підтверджень.
|
||||
- Використовуйте `approvalCapability.getActionAvailabilityState` для доступності auth same-chat підтверджень.
|
||||
- Якщо ваш канал експонує native exec підтвердження, використовуйте `approvalCapability.getExecInitiatingSurfaceState` для стану initiating-surface/native-client, коли він відрізняється від auth same-chat підтверджень. Ядро використовує цей exec-специфічний хук, щоб розрізняти `enabled` і `disabled`, вирішувати, чи підтримує початковий канал native exec підтвердження, і включати канал у fallback-підказки native-client. `createApproverRestrictedNativeApprovalCapability(...)` заповнює це для типового випадку.
|
||||
- Використовуйте `outbound.shouldSuppressLocalPayloadPrompt` або `outbound.beforeDeliverPayload` для поведінки життєвого циклу payload, специфічної для каналу, наприклад приховування дубльованих локальних промптів підтвердження або надсилання індикаторів набору перед доставкою.
|
||||
- Використовуйте `approvalCapability.delivery` лише для маршрутизації native підтверджень або пригнічення fallback.
|
||||
- Використовуйте `approvalCapability.nativeRuntime` для фактів native підтверджень, якими володіє канал. Тримайте його lazy на гарячих entrypoints каналу за допомогою `createLazyChannelApprovalNativeRuntimeAdapter(...)`, який може імпортувати ваш runtime-модуль на вимогу, водночас дозволяючи ядру зібрати життєвий цикл підтвердження.
|
||||
- Використовуйте `approvalCapability.render` лише тоді, коли каналу справді потрібні власні payload підтвердження замість спільного renderer.
|
||||
- Використовуйте `approvalCapability.describeExecApprovalSetup`, коли канал хоче, щоб відповідь disabled-path пояснювала точні перемикачі конфігурації, потрібні для ввімкнення native exec підтверджень. Хук отримує `{ channel, channelLabel, accountId }`; канали з іменованими акаунтами мають рендерити шляхи в межах акаунта, як-от `channels.<channel>.accounts.<id>.execApprovals.*`, замість top-level defaults.
|
||||
- Якщо канал може вивести стабільні owner-like DM ідентичності з наявної конфігурації, використовуйте `createResolvedApproverActionAuthAdapter` з `openclaw/plugin-sdk/approval-runtime`, щоб обмежити same-chat `/approve` без додавання логіки ядра, специфічної для підтверджень.
|
||||
- Якщо каналу потрібна доставка native підтверджень, зосередьте код каналу на нормалізації цілі та фактах транспорту/представлення. Використовуйте `createChannelExecApprovalProfile`, `createChannelNativeOriginTargetResolver`, `createChannelApproverDmTargetResolver` і `createApproverRestrictedNativeApprovalCapability` з `openclaw/plugin-sdk/approval-runtime`. Розміщуйте специфічні для каналу факти за `approvalCapability.nativeRuntime`, бажано через `createChannelApprovalNativeRuntimeAdapter(...)` або `createLazyChannelApprovalNativeRuntimeAdapter(...)`, щоб ядро могло зібрати handler і відповідати за фільтрацію запитів, маршрутизацію, дедуплікацію, завершення терміну дії, підписку Gateway і сповіщення про маршрутизацію в інше місце. `nativeRuntime` розділено на кілька менших seam:
|
||||
- `createChannelNativeOriginTargetResolver` за замовчуванням використовує спільний matcher channel-route для цілей `{ to, accountId, threadId }`. Передавайте `targetsMatch` лише тоді, коли канал має специфічні для провайдера правила еквівалентності, наприклад зіставлення префікса timestamp у Slack.
|
||||
- Передавайте `normalizeTargetForMatch` у `createChannelNativeOriginTargetResolver`, коли каналу потрібно канонізувати ідентифікатори провайдера перед запуском стандартного route matcher або власного callback `targetsMatch`, зберігаючи оригінальну ціль для доставки. Використовуйте `normalizeTarget` лише тоді, коли саму визначену ціль доставки потрібно канонізувати.
|
||||
- Ядро відповідає за `/approve` у тому самому чаті, спільні корисні навантаження кнопок схвалення й загальну резервну доставку.
|
||||
- Надавайте перевагу одному об'єкту `approvalCapability` у Plugin каналу, коли каналу потрібна поведінка, специфічна для схвалень.
|
||||
- `ChannelPlugin.approvals` видалено. Розміщуйте факти про доставку, нативність, рендеринг і автентифікацію схвалень у `approvalCapability`.
|
||||
- `plugin.auth` призначений лише для входу/виходу; ядро більше не читає хуки автентифікації схвалень із цього об'єкта.
|
||||
- `approvalCapability.authorizeActorAction` і `approvalCapability.getActionAvailabilityState` є канонічною точкою інтеграції для автентифікації схвалень.
|
||||
- Використовуйте `approvalCapability.getActionAvailabilityState` для доступності автентифікації схвалень у тому самому чаті.
|
||||
- Якщо ваш канал експонує нативні exec-схвалення, використовуйте `approvalCapability.getExecInitiatingSurfaceState` для стану поверхні ініціювання/нативного клієнта, коли він відрізняється від автентифікації схвалень у тому самому чаті. Ядро використовує цей специфічний для exec хук, щоб відрізняти `enabled` від `disabled`, вирішувати, чи канал ініціювання підтримує нативні exec-схвалення, і включати канал у підказки щодо резервного переходу для нативного клієнта. `createApproverRestrictedNativeApprovalCapability(...)` заповнює це для типового випадку.
|
||||
- Використовуйте `outbound.shouldSuppressLocalPayloadPrompt` або `outbound.beforeDeliverPayload` для специфічної для каналу поведінки життєвого циклу корисного навантаження, наприклад приховування дубльованих локальних запитів на схвалення або надсилання індикаторів набору перед доставкою.
|
||||
- Використовуйте `approvalCapability.delivery` лише для маршрутизації нативних схвалень або пригнічення резервної доставки.
|
||||
- Використовуйте `approvalCapability.nativeRuntime` для належних каналу фактів про нативні схвалення. Тримайте його лінивим на гарячих точках входу каналу за допомогою `createLazyChannelApprovalNativeRuntimeAdapter(...)`, який може імпортувати ваш модуль часу виконання на вимогу й водночас дозволяє ядру зібрати життєвий цикл схвалень.
|
||||
- Використовуйте `approvalCapability.render` лише тоді, коли каналу справді потрібні власні корисні навантаження схвалень замість спільного рендерера.
|
||||
- Використовуйте `approvalCapability.describeExecApprovalSetup`, коли канал хоче, щоб відповідь для вимкненого шляху пояснювала точні параметри конфігурації, потрібні для ввімкнення нативних exec-схвалень. Хук отримує `{ channel, channelLabel, accountId }`; канали з іменованими акаунтами мають рендерити шляхи, обмежені акаунтом, наприклад `channels.<channel>.accounts.<id>.execApprovals.*`, замість типових значень верхнього рівня.
|
||||
- Якщо канал може вивести стабільні DM-ідентичності на кшталт власника з наявної конфігурації, використовуйте `createResolvedApproverActionAuthAdapter` з `openclaw/plugin-sdk/approval-runtime`, щоб обмежити `/approve` у тому самому чаті без додавання специфічної для схвалень логіки ядра.
|
||||
- Якщо каналу потрібна доставка нативних схвалень, тримайте код каналу зосередженим на нормалізації цілей і фактах транспорту/представлення. Використовуйте `createChannelExecApprovalProfile`, `createChannelNativeOriginTargetResolver`, `createChannelApproverDmTargetResolver` і `createApproverRestrictedNativeApprovalCapability` з `openclaw/plugin-sdk/approval-runtime`. Розмістіть специфічні для каналу факти за `approvalCapability.nativeRuntime`, в ідеалі через `createChannelApprovalNativeRuntimeAdapter(...)` або `createLazyChannelApprovalNativeRuntimeAdapter(...)`, щоб ядро могло зібрати обробник і відповідати за фільтрацію запитів, маршрутизацію, дедуплікацію, строк дії, підписку Gateway і повідомлення про маршрутизацію в інше місце. `nativeRuntime` поділено на кілька менших точок інтеграції:
|
||||
- `createChannelNativeOriginTargetResolver` типово використовує спільний зіставлювач channel-route для цілей `{ to, accountId, threadId }`. Передавайте `targetsMatch` лише тоді, коли канал має специфічні для провайдера правила еквівалентності, наприклад зіставлення префікса часової мітки Slack.
|
||||
- Передавайте `normalizeTargetForMatch` до `createChannelNativeOriginTargetResolver`, коли каналу потрібно канонізувати ідентифікатори провайдера перед запуском типового зіставлювача маршрутів або власного callback `targetsMatch`, зберігаючи початкову ціль для доставки. Використовуйте `normalizeTarget` лише тоді, коли сама розв'язана ціль доставки має бути канонізована.
|
||||
- `availability` — чи налаштовано акаунт і чи потрібно обробляти запит
|
||||
- `presentation` — відображає спільну view model підтвердження в pending/resolved/expired native payload або фінальні дії
|
||||
- `transport` — готує цілі та надсилає/оновлює/видаляє native повідомлення підтвердження
|
||||
- `interactions` — необов'язкові хуки bind/unbind/clear-action для native кнопок або реакцій
|
||||
- `presentation` — зіставлення спільної моделі подання схвалення з очікуваними/вирішеними/простроченими нативними корисними навантаженнями або фінальними діями
|
||||
- `transport` — підготовка цілей і надсилання/оновлення/видалення нативних повідомлень схвалення
|
||||
- `interactions` — необов'язкові хуки прив'язування/відв'язування/очищення дії для нативних кнопок або реакцій
|
||||
- `observe` — необов'язкові хуки діагностики доставки
|
||||
- Якщо каналу потрібні runtime-об'єкти, якими він володіє, наприклад client, token, Bolt app або webhook receiver, реєструйте їх через `openclaw/plugin-sdk/channel-runtime-context`. Загальний реєстр runtime-context дає ядру змогу bootstrap capability-driven handlers зі стану запуску каналу без додавання glue wrapper, специфічного для підтверджень.
|
||||
- Звертайтеся до нижчорівневих `createChannelApprovalHandler` або `createChannelNativeApprovalRuntime` лише тоді, коли capability-driven seam ще недостатньо виразний.
|
||||
- Канали native підтверджень мають маршрутизувати і `accountId`, і `approvalKind` через ці helpers. `accountId` утримує політику підтверджень для кількох акаунтів у межах правильного bot account, а `approvalKind` зберігає поведінку exec і plugin підтверджень доступною для каналу без hardcoded branches у ядрі.
|
||||
- Тепер ядро також відповідає за сповіщення про reroute підтверджень. Plugins каналів не мають надсилати власні follow-up повідомлення "підтвердження пішло в DM / інший канал" із `createChannelNativeApprovalRuntime`; натомість експонуйте точну маршрутизацію origin + approver-DM через спільні helpers capability підтверджень і дозвольте ядру агрегувати фактичні доставки перед публікацією будь-якого сповіщення назад у початковий чат.
|
||||
- Зберігайте тип доставленого ідентифікатора підтвердження end-to-end. Native clients не повинні
|
||||
вгадувати або переписувати маршрутизацію exec vs plugin підтверджень зі стану, локального для каналу.
|
||||
- Різні типи підтверджень можуть навмисно експонувати різні native поверхні.
|
||||
- Якщо каналу потрібні об'єкти, що належать часу виконання, як-от клієнт, токен, застосунок Bolt або Webhook-приймач, реєструйте їх через `openclaw/plugin-sdk/channel-runtime-context`. Загальний реєстр контексту часу виконання дозволяє ядру початково завантажувати обробники, керовані можливостями, зі стану запуску каналу без додавання специфічного для схвалень зв'язувального коду обгортки.
|
||||
- Звертайтеся до нижчорівневих `createChannelApprovalHandler` або `createChannelNativeApprovalRuntime` лише тоді, коли точка інтеграції, керована можливостями, ще недостатньо виразна.
|
||||
- Канали нативних схвалень мають маршрутизувати і `accountId`, і `approvalKind` через ці допоміжні засоби. `accountId` утримує політику схвалень для кількох акаунтів у межах правильного акаунта бота, а `approvalKind` зберігає доступність поведінки exec-схвалень і схвалень Plugin для каналу без жорстко заданих розгалужень у ядрі.
|
||||
- Ядро тепер також відповідає за повідомлення про перемаршрутизацію схвалень. Plugin каналів не мають надсилати власні подальші повідомлення "схвалення пішло в DM / інший канал" з `createChannelNativeApprovalRuntime`; натомість експонуйте точну маршрутизацію джерела + DM схвалювача через спільні допоміжні засоби можливостей схвалення й дозвольте ядру агрегувати фактичні доставки перед публікацією будь-якого повідомлення назад у чат ініціювання.
|
||||
- Зберігайте тип доставленого ідентифікатора схвалення наскрізно. Нативні клієнти не повинні
|
||||
вгадувати або переписувати маршрутизацію exec-схвалень і схвалень Plugin з локального стану каналу.
|
||||
- Різні типи схвалень можуть навмисно експонувати різні нативні поверхні.
|
||||
Поточні вбудовані приклади:
|
||||
- Slack зберігає native маршрутизацію підтверджень доступною і для exec, і для plugin ids.
|
||||
- Matrix зберігає ту саму native маршрутизацію DM/каналу та UX реакцій для exec
|
||||
і plugin підтверджень, водночас дозволяючи auth відрізнятися за типом підтвердження.
|
||||
- `createApproverRestrictedNativeApprovalAdapter` все ще існує як wrapper сумісності, але новий код має віддавати перевагу builder capability і експонувати `approvalCapability` у Plugin.
|
||||
- Slack зберігає доступність маршрутизації нативних схвалень і для exec, і для ідентифікаторів Plugin.
|
||||
- Matrix зберігає ту саму нативну маршрутизацію DM/каналу й UX реакцій для exec
|
||||
і схвалень Plugin, водночас дозволяючи автентифікації відрізнятися за типом схвалення.
|
||||
- `createApproverRestrictedNativeApprovalAdapter` усе ще існує як обгортка сумісності, але новий код має надавати перевагу побудовувачу можливостей і експонувати `approvalCapability` у Plugin.
|
||||
|
||||
Для гарячих entrypoints каналу віддавайте перевагу вужчим runtime subpaths, коли вам потрібна лише
|
||||
одна частина цієї родини:
|
||||
Для гарячих точок входу каналу надавайте перевагу вужчим підшляхам часу виконання, коли вам потрібна лише
|
||||
одна частина цього сімейства:
|
||||
|
||||
- `openclaw/plugin-sdk/approval-auth-runtime`
|
||||
- `openclaw/plugin-sdk/approval-client-runtime`
|
||||
@ -136,125 +136,128 @@ Plugins із граматикою цілей, специфічною для пр
|
||||
- `openclaw/plugin-sdk/approval-reply-runtime`
|
||||
- `openclaw/plugin-sdk/channel-runtime-context`
|
||||
|
||||
Так само віддавайте перевагу `openclaw/plugin-sdk/setup-runtime`,
|
||||
Так само надавайте перевагу `openclaw/plugin-sdk/setup-runtime`,
|
||||
`openclaw/plugin-sdk/setup-adapter-runtime`,
|
||||
`openclaw/plugin-sdk/reply-runtime`,
|
||||
`openclaw/plugin-sdk/reply-dispatch-runtime`,
|
||||
`openclaw/plugin-sdk/reply-reference` і
|
||||
`openclaw/plugin-sdk/reply-chunking`, коли вам не потрібна ширша umbrella
|
||||
`openclaw/plugin-sdk/reply-chunking`, коли вам не потрібна ширша загальна
|
||||
поверхня.
|
||||
|
||||
Зокрема для налаштування:
|
||||
Для налаштування зокрема:
|
||||
|
||||
- `openclaw/plugin-sdk/setup-runtime` охоплює runtime-safe helpers налаштування:
|
||||
import-safe setup patch adapters (`createPatchedAccountSetupAdapter`,
|
||||
- `openclaw/plugin-sdk/setup-runtime` охоплює безпечні для часу виконання допоміжні засоби налаштування:
|
||||
безпечні для імпорту адаптери патчів налаштування (`createPatchedAccountSetupAdapter`,
|
||||
`createEnvPatchedAccountSetupAdapter`,
|
||||
`createSetupInputPresenceValidator`), виведення lookup-note,
|
||||
`createSetupInputPresenceValidator`), вивід lookup-note,
|
||||
`promptResolvedAllowFrom`, `splitSetupEntries` і делеговані
|
||||
builders setup-proxy
|
||||
- `openclaw/plugin-sdk/setup-adapter-runtime` — це вузький env-aware adapter
|
||||
seam для `createEnvPatchedAccountSetupAdapter`
|
||||
- `openclaw/plugin-sdk/channel-setup` охоплює builders налаштування optional-install
|
||||
плюс кілька setup-safe primitives:
|
||||
побудовувачі проксі налаштування
|
||||
- `openclaw/plugin-sdk/setup-adapter-runtime` — це вузька, обізнана про змінні середовища
|
||||
точка інтеграції адаптера для `createEnvPatchedAccountSetupAdapter`
|
||||
- `openclaw/plugin-sdk/channel-setup` охоплює побудовувачі налаштування
|
||||
необов'язкового встановлення плюс кілька безпечних для налаштування примітивів:
|
||||
`createOptionalChannelSetupSurface`, `createOptionalChannelSetupAdapter`,
|
||||
|
||||
Якщо ваш канал підтримує налаштування або auth на основі env і загальні потоки startup/config
|
||||
мають знати ці env назви до завантаження runtime, оголосіть їх у
|
||||
маніфесті Plugin через `channelEnvVars`. Тримайте runtime `envVars` каналу або локальні
|
||||
константи лише для operator-facing copy.
|
||||
Якщо ваш канал підтримує налаштування або автентифікацію, керовані змінними середовища, і загальні потоки запуску/конфігурації
|
||||
мають знати ці назви змінних середовища до завантаження часу виконання, оголосіть їх у
|
||||
маніфесті Plugin через `channelEnvVars`. Тримайте `envVars` часу виконання каналу або локальні
|
||||
константи лише для текстів, звернених до операторів.
|
||||
|
||||
Якщо ваш канал може з’являтися в `status`, `channels list`, `channels status` або
|
||||
скануваннях SecretRef до запуску середовища виконання plugin, додайте `openclaw.setupEntry` у
|
||||
скануваннях SecretRef до запуску середовища виконання Plugin, додайте `openclaw.setupEntry` у
|
||||
`package.json`. Ця точка входу має бути безпечною для імпорту в шляхах команд
|
||||
лише для читання й має повертати метадані каналу, безпечний для налаштування адаптер конфігурації, адаптер статусу
|
||||
та метадані цілі секретів каналу, потрібні для цих зведень. Не
|
||||
запускайте клієнти, слухачі або транспортні середовища виконання з точки входу налаштування.
|
||||
лише для читання і має повертати метадані каналу, безпечний для налаштування
|
||||
адаптер конфігурації, адаптер статусу та метадані цілі секретів каналу, потрібні
|
||||
для цих зведень. Не запускайте клієнти, слухачі або транспортні середовища
|
||||
виконання з точки входу налаштування.
|
||||
|
||||
Також тримайте шлях імпорту основної точки входу каналу вузьким. Виявлення може оцінити
|
||||
точку входу й модуль channel plugin, щоб зареєструвати можливості без активації
|
||||
каналу. Файли на кшталт `channel-plugin-api.ts` мають експортувати об’єкт channel
|
||||
plugin без імпорту майстрів налаштування, транспортних клієнтів, socket
|
||||
listeners, засобів запуску підпроцесів або модулів запуску сервісу. Розміщуйте ці runtime
|
||||
частини в модулях, завантажених із `registerFull(...)`, runtime setters або lazy
|
||||
capability adapters.
|
||||
Шлях імпорту головної точки входу каналу також має бути вузьким. Виявлення може
|
||||
оцінювати точку входу й модуль Plugin каналу, щоб зареєструвати можливості без
|
||||
активації каналу. Файли на кшталт `channel-plugin-api.ts` мають експортувати
|
||||
об’єкт Plugin каналу без імпорту майстрів налаштування, транспортних клієнтів,
|
||||
слухачів сокетів, запускувачів підпроцесів або модулів запуску сервісів.
|
||||
Розміщуйте ці частини середовища виконання в модулях, що завантажуються з
|
||||
`registerFull(...)`, сетерах середовища виконання або лінивих адаптерах
|
||||
можливостей.
|
||||
|
||||
`createOptionalChannelSetupWizard`, `DEFAULT_ACCOUNT_ID`,
|
||||
`createTopLevelChannelDmPolicy`, `setSetupChannelEnabled`, і
|
||||
`createTopLevelChannelDmPolicy`, `setSetupChannelEnabled` і
|
||||
`splitSetupEntries`
|
||||
|
||||
- використовуйте ширший seam `openclaw/plugin-sdk/setup` лише тоді, коли вам також потрібні
|
||||
важчі спільні помічники налаштування/конфігурації, такі як
|
||||
- використовуйте ширший шов `openclaw/plugin-sdk/setup` лише тоді, коли вам
|
||||
також потрібні важчі спільні помічники налаштування/конфігурації, як-от
|
||||
`moveSingleAccountChannelSectionToDefaultAccount(...)`
|
||||
|
||||
Якщо ваш канал хоче лише показувати "спершу встановіть цей plugin" на поверхнях
|
||||
налаштування, віддайте перевагу `createOptionalChannelSetupSurface(...)`. Згенерований
|
||||
адаптер/майстер fail closed під час записів конфігурації та фіналізації, і вони повторно використовують
|
||||
те саме повідомлення про обов’язкове встановлення у валідації, фіналізації та тексті
|
||||
посилання на документацію.
|
||||
Якщо ваш канал лише хоче показувати «спершу встановіть цей Plugin» у поверхнях
|
||||
налаштування, надайте перевагу `createOptionalChannelSetupSurface(...)`.
|
||||
Згенерований адаптер/майстер безпечно відмовляється від записів конфігурації та
|
||||
фіналізації, і вони повторно використовують одне й те саме повідомлення про
|
||||
потрібне встановлення у валідації, фіналізації та тексті посилання на документацію.
|
||||
|
||||
Для інших гарячих шляхів каналів віддавайте перевагу вузьким помічникам замість ширших застарілих
|
||||
поверхонь:
|
||||
Для інших гарячих шляхів каналу надавайте перевагу вузьким помічникам над
|
||||
ширшими застарілими поверхнями:
|
||||
|
||||
- `openclaw/plugin-sdk/account-core`,
|
||||
`openclaw/plugin-sdk/account-id`,
|
||||
`openclaw/plugin-sdk/account-resolution`, і
|
||||
`openclaw/plugin-sdk/account-helpers` для багаторахункової конфігурації та
|
||||
fallback для стандартного акаунта
|
||||
`openclaw/plugin-sdk/account-resolution` і
|
||||
`openclaw/plugin-sdk/account-helpers` для конфігурації кількох облікових
|
||||
записів і резервного переходу до стандартного облікового запису
|
||||
- `openclaw/plugin-sdk/inbound-envelope` і
|
||||
`openclaw/plugin-sdk/inbound-reply-dispatch` для inbound route/envelope та
|
||||
зв’язування record-and-dispatch
|
||||
`openclaw/plugin-sdk/inbound-reply-dispatch` для вхідного маршруту/конверта та
|
||||
зв’язування запису й диспетчеризації
|
||||
- `openclaw/plugin-sdk/messaging-targets` для розбору/зіставлення цілей
|
||||
- `openclaw/plugin-sdk/outbound-media` і
|
||||
`openclaw/plugin-sdk/outbound-runtime` для завантаження медіа плюс outbound
|
||||
identity/send delegates і планування payload
|
||||
`openclaw/plugin-sdk/outbound-runtime` для завантаження медіа, а також
|
||||
вихідних делегатів ідентичності/надсилання та планування корисного
|
||||
навантаження
|
||||
- `buildThreadAwareOutboundSessionRoute(...)` з
|
||||
`openclaw/plugin-sdk/channel-core`, коли outbound route має зберігати явний
|
||||
`replyToId`/`threadId` або відновлювати поточну сесію `:thread:`
|
||||
після того, як базовий ключ сесії все ще збігається. Provider plugins можуть перевизначати
|
||||
пріоритет, поведінку суфікса та нормалізацію thread id, коли їхня платформа
|
||||
має нативну семантику доставки в thread.
|
||||
- `openclaw/plugin-sdk/thread-bindings-runtime` для життєвого циклу thread-binding
|
||||
і реєстрації адаптера
|
||||
- `openclaw/plugin-sdk/agent-media-payload` лише тоді, коли все ще потрібна застаріла розкладка полів agent/media
|
||||
payload
|
||||
- `openclaw/plugin-sdk/telegram-command-config` для нормалізації користувацьких команд Telegram,
|
||||
валідації дублікатів/конфліктів і fallback-stable контракту конфігурації
|
||||
команд
|
||||
`openclaw/plugin-sdk/channel-core`, коли вихідний маршрут має зберігати явний
|
||||
`replyToId`/`threadId` або відновлювати поточну сесію `:thread:` після того,
|
||||
як базовий ключ сесії все ще збігається. Plugin провайдера можуть
|
||||
перевизначати пріоритет, поведінку суфіксів і нормалізацію ідентифікаторів
|
||||
гілок, коли їхня платформа має нативну семантику доставки в гілки.
|
||||
- `openclaw/plugin-sdk/thread-bindings-runtime` для життєвого циклу прив’язок
|
||||
гілок і реєстрації адаптерів
|
||||
- `openclaw/plugin-sdk/agent-media-payload` лише тоді, коли все ще потрібна
|
||||
застаріла схема полів корисного навантаження агента/медіа
|
||||
- `openclaw/plugin-sdk/telegram-command-config` для нормалізації користувацьких
|
||||
команд Telegram, валідації дублікатів/конфліктів і стабільного за резервним
|
||||
переходом контракту конфігурації команд
|
||||
|
||||
Канали лише з автентифікацією зазвичай можуть зупинитися на стандартному шляху: core обробляє approvals, а plugin лише відкриває outbound/auth можливості. Нативні канали approval, такі як Matrix, Slack, Telegram і користувацькі chat transports, мають використовувати спільні нативні помічники замість написання власного життєвого циклу approval.
|
||||
Канали лише для автентифікації зазвичай можуть зупинитися на стандартному шляху: ядро обробляє схвалення, а Plugin лише надає вихідні/автентифікаційні можливості. Нативні канали схвалення, як-от Matrix, Slack, Telegram і користувацькі чат-транспорти, мають використовувати спільні нативні помічники замість власноручної реалізації життєвого циклу схвалень.
|
||||
|
||||
## Політика inbound згадок
|
||||
## Політика вхідних згадок
|
||||
|
||||
Тримайте обробку inbound згадок розділеною на два шари:
|
||||
Тримайте обробку вхідних згадок розділеною на два рівні:
|
||||
|
||||
- збирання доказів, що належить plugin
|
||||
- збирання доказів, яким володіє Plugin
|
||||
- оцінювання спільної політики
|
||||
|
||||
Використовуйте `openclaw/plugin-sdk/channel-mention-gating` для рішень mention-policy.
|
||||
Використовуйте `openclaw/plugin-sdk/channel-inbound` лише тоді, коли вам потрібен ширший inbound
|
||||
barrel помічників.
|
||||
Використовуйте `openclaw/plugin-sdk/channel-mention-gating` для рішень політики
|
||||
згадок. Використовуйте `openclaw/plugin-sdk/channel-inbound` лише тоді, коли вам
|
||||
потрібен ширший barrel вхідних помічників.
|
||||
|
||||
Добре підходить для plugin-local логіки:
|
||||
Добре підходить для локальної логіки Plugin:
|
||||
|
||||
- виявлення reply-to-bot
|
||||
- виявлення quoted-bot
|
||||
- перевірки thread-participation
|
||||
- виключення service/system-message
|
||||
- platform-native кеші, потрібні для доведення участі bot
|
||||
- виявлення відповіді боту
|
||||
- виявлення процитованого бота
|
||||
- перевірки участі в гілці
|
||||
- виключення сервісних/системних повідомлень
|
||||
- нативні для платформи кеші, потрібні для підтвердження участі бота
|
||||
|
||||
Добре підходить для спільного помічника:
|
||||
|
||||
- `requireMention`
|
||||
- явний результат згадки
|
||||
- allowlist неявних згадок
|
||||
- результат явної згадки
|
||||
- список дозволених неявних згадок
|
||||
- обхід команд
|
||||
- остаточне рішення про пропуск
|
||||
- остаточне рішення пропустити
|
||||
|
||||
Бажаний потік:
|
||||
Рекомендований потік:
|
||||
|
||||
1. Обчисліть локальні факти згадок.
|
||||
1. Обчисліть локальні факти згадки.
|
||||
2. Передайте ці факти в `resolveInboundMentionDecision({ facts, policy })`.
|
||||
3. Використовуйте `decision.effectiveWasMentioned`, `decision.shouldBypassMention` і `decision.shouldSkip` у вашому inbound gate.
|
||||
3. Використовуйте `decision.effectiveWasMentioned`, `decision.shouldBypassMention` і `decision.shouldSkip` у вашому вхідному шлюзі.
|
||||
|
||||
```typescript
|
||||
import {
|
||||
@ -293,8 +296,8 @@ const decision = resolveInboundMentionDecision({
|
||||
if (decision.shouldSkip) return;
|
||||
```
|
||||
|
||||
`api.runtime.channel.mentions` відкриває ті самі спільні помічники згадок для
|
||||
вбудованих channel plugins, які вже залежать від runtime injection:
|
||||
`api.runtime.channel.mentions` надає ті самі спільні помічники згадок для
|
||||
вбудованих Plugin каналів, які вже залежать від ін’єкції середовища виконання:
|
||||
|
||||
- `buildMentionRegexes`
|
||||
- `matchesMentionPatterns`
|
||||
@ -304,21 +307,21 @@ if (decision.shouldSkip) return;
|
||||
|
||||
Якщо вам потрібні лише `implicitMentionKindWhen` і
|
||||
`resolveInboundMentionDecision`, імпортуйте з
|
||||
`openclaw/plugin-sdk/channel-mention-gating`, щоб уникнути завантаження не пов’язаних inbound
|
||||
runtime helpers.
|
||||
`openclaw/plugin-sdk/channel-mention-gating`, щоб уникнути завантаження
|
||||
непов’язаних вхідних помічників середовища виконання.
|
||||
|
||||
Старіші помічники `resolveMentionGating*` залишаються в
|
||||
`openclaw/plugin-sdk/channel-inbound` лише як експорти сумісності. Новий код
|
||||
має використовувати `resolveInboundMentionDecision({ facts, policy })`.
|
||||
`openclaw/plugin-sdk/channel-inbound` лише як експорти сумісності. Новий код має
|
||||
використовувати `resolveInboundMentionDecision({ facts, policy })`.
|
||||
|
||||
## Покроковий огляд
|
||||
|
||||
<Steps>
|
||||
<a id="step-1-package-and-manifest"></a>
|
||||
<Step title="Пакет і маніфест">
|
||||
Створіть стандартні файли plugin. Поле `channel` у `package.json` — це
|
||||
те, що робить це channel plugin. Повну поверхню package-metadata див.
|
||||
у [Налаштування й конфігурація Plugin](/uk/plugins/sdk-setup#openclaw-channel):
|
||||
Створіть стандартні файли Plugin. Поле `channel` у `package.json` — це те,
|
||||
що робить його Plugin каналу. Повну поверхню метаданих пакета див. у
|
||||
[Налаштування та конфігурація Plugin](/uk/plugins/sdk-setup#openclaw-channel):
|
||||
|
||||
<CodeGroup>
|
||||
```json package.json
|
||||
@ -375,16 +378,17 @@ runtime helpers.
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
`configSchema` валідує `plugins.entries.acme-chat.config`. Використовуйте його для
|
||||
налаштувань, що належать plugin і не є конфігурацією акаунта каналу. `channelConfigs`
|
||||
валідує `channels.acme-chat` і є cold-path джерелом, яке використовують schema конфігурації,
|
||||
налаштування та UI поверхні до завантаження runtime plugin.
|
||||
`configSchema` валідує `plugins.entries.acme-chat.config`. Використовуйте її
|
||||
для налаштувань, якими володіє Plugin і які не є конфігурацією облікового
|
||||
запису каналу. `channelConfigs` валідує `channels.acme-chat` і є джерелом
|
||||
холодного шляху, яке використовують схема конфігурації, налаштування та
|
||||
UI-поверхні до завантаження середовища виконання Plugin.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Побудуйте об’єкт channel plugin">
|
||||
Інтерфейс `ChannelPlugin` має багато необов’язкових поверхонь адаптерів. Почніть із
|
||||
мінімуму — `id` і `setup` — і додавайте адаптери за потреби.
|
||||
<Step title="Створіть об’єкт Plugin каналу">
|
||||
Інтерфейс `ChannelPlugin` має багато необов’язкових поверхонь адаптерів.
|
||||
Почніть із мінімуму — `id` і `setup` — і додавайте адаптери за потреби.
|
||||
|
||||
Створіть `src/channel.ts`:
|
||||
|
||||
@ -479,35 +483,42 @@ runtime helpers.
|
||||
});
|
||||
```
|
||||
|
||||
Для каналів, які приймають і канонічні top-level DM keys, і застарілі вкладені ключі, використовуйте помічники з `plugin-sdk/channel-config-helpers`: `resolveChannelDmAccess`, `resolveChannelDmPolicy`, `resolveChannelDmAllowFrom` і `normalizeChannelDmPolicy` тримають account-local значення попереду успадкованих root значень. Поєднайте той самий resolver із doctor repair через `normalizeLegacyDmAliases`, щоб runtime і migration читали той самий контракт.
|
||||
Для каналів, які приймають і канонічні DM-ключі верхнього рівня, і
|
||||
застарілі вкладені ключі, використовуйте помічники з
|
||||
`plugin-sdk/channel-config-helpers`: `resolveChannelDmAccess`,
|
||||
`resolveChannelDmPolicy`, `resolveChannelDmAllowFrom` і
|
||||
`normalizeChannelDmPolicy` зберігають локальні для облікового запису
|
||||
значення перед успадкованими кореневими значеннями. Поєднайте той самий
|
||||
резолвер із ремонтом doctor через `normalizeLegacyDmAliases`, щоб
|
||||
середовище виконання й міграція читали один і той самий контракт.
|
||||
|
||||
<Accordion title="Що createChatChannelPlugin робить за вас">
|
||||
Замість того, щоб реалізовувати низькорівневі інтерфейси адаптерів вручну, ви передаєте
|
||||
декларативні параметри, а builder компонуватиме їх:
|
||||
<Accordion title="Що createChatChannelPlugin робить для вас">
|
||||
Замість ручної реалізації низькорівневих інтерфейсів адаптерів ви передаєте
|
||||
декларативні параметри, а builder компонує їх:
|
||||
|
||||
| Параметр | Що він зв’язує |
|
||||
| --- | --- |
|
||||
| `security.dm` | Scoped DM security resolver з полів конфігурації |
|
||||
| `pairing.text` | Текстовий потік DM pairing з обміном кодом |
|
||||
| `threading` | Resolver режиму reply-to (фіксований, account-scoped або користувацький) |
|
||||
| `outbound.attachedResults` | Функції надсилання, які повертають result metadata (message IDs) |
|
||||
| `security.dm` | Обмежений DM-резолвер безпеки з полів конфігурації |
|
||||
| `pairing.text` | Текстовий потік DM-сполучення з обміном кодом |
|
||||
| `threading` | Резолвер режиму відповіді (фіксований, scoped за обліковим записом або користувацький) |
|
||||
| `outbound.attachedResults` | Функції надсилання, що повертають метадані результату (ідентифікатори повідомлень) |
|
||||
|
||||
Ви також можете передати необроблені об’єкти адаптерів замість декларативних параметрів,
|
||||
якщо вам потрібен повний контроль.
|
||||
Ви також можете передати сирі об’єкти адаптерів замість декларативних
|
||||
параметрів, якщо вам потрібен повний контроль.
|
||||
|
||||
Raw outbound adapters may define a `chunker(text, limit, ctx)` function.
|
||||
The optional `ctx.formatting` carries delivery-time formatting decisions
|
||||
such as `maxLinesPerMessage`; apply it before sending so reply threading
|
||||
and chunk boundaries are resolved once by shared outbound delivery.
|
||||
Send contexts also include `replyToIdSource` (`implicit` or `explicit`)
|
||||
when a native reply target was resolved, so payload helpers can preserve
|
||||
explicit reply tags without consuming an implicit single-use reply slot.
|
||||
Адаптери сирого вихідного доставлення можуть визначати функцію `chunker(text, limit, ctx)`.
|
||||
Необов’язкове `ctx.formatting` передає рішення щодо форматування під час доставлення,
|
||||
як-от `maxLinesPerMessage`; застосовуйте його перед надсиланням, щоб гілкування відповідей
|
||||
і межі фрагментів один раз визначалися спільним вихідним доставленням.
|
||||
Контексти надсилання також містять `replyToIdSource` (`implicit` або `explicit`),
|
||||
коли було визначено нативну ціль відповіді, тож допоміжні функції корисного навантаження можуть зберігати
|
||||
явні теги відповіді, не витрачаючи неявний одноразовий слот відповіді.
|
||||
</Accordion>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Wire the entry point">
|
||||
Create `index.ts`:
|
||||
Створіть `index.ts`:
|
||||
|
||||
```typescript index.ts
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
|
||||
@ -544,12 +555,12 @@ runtime helpers.
|
||||
|
||||
Розмістіть CLI-дескриптори, що належать каналу, у `registerCliMetadata(...)`, щоб OpenClaw
|
||||
міг показувати їх у кореневій довідці без активації повного runtime каналу,
|
||||
тоді як звичайні повні завантаження й надалі підхоплюватимуть ті самі дескриптори для справжньої
|
||||
тоді як звичайні повні завантаження все одно підхоплюватимуть ті самі дескриптори для справжньої
|
||||
реєстрації команд. Залиште `registerFull(...)` для роботи, потрібної лише під час runtime.
|
||||
Якщо `registerFull(...)` реєструє RPC-методи Gateway, використовуйте
|
||||
префікс, специфічний для Plugin. Основні адміністративні простори імен (`config.*`,
|
||||
префікс, специфічний для Plugin. Простори імен адміністрування ядра (`config.*`,
|
||||
`exec.approvals.*`, `wizard.*`, `update.*`) залишаються зарезервованими й завжди
|
||||
розв’язуються як `operator.admin`.
|
||||
розв’язуються в `operator.admin`.
|
||||
`defineChannelPluginEntry` автоматично обробляє розділення режимів реєстрації. Див.
|
||||
[Точки входу](/uk/plugins/sdk-entrypoints#definechannelpluginentry) для всіх
|
||||
параметрів.
|
||||
@ -557,7 +568,7 @@ runtime helpers.
|
||||
</Step>
|
||||
|
||||
<Step title="Add a setup entry">
|
||||
Створіть `setup-entry.ts` для легкого завантаження під час onboarding:
|
||||
Створіть `setup-entry.ts` для легкого завантаження під час онбордингу:
|
||||
|
||||
```typescript setup-entry.ts
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
|
||||
@ -567,20 +578,20 @@ runtime helpers.
|
||||
```
|
||||
|
||||
OpenClaw завантажує це замість повної точки входу, коли канал вимкнений
|
||||
або не налаштований. Це дає змогу не підтягувати важкий runtime-код під час потоків налаштування.
|
||||
Докладніше див. [Налаштування та конфігурація](/uk/plugins/sdk-setup#setup-entry).
|
||||
або не налаштований. Це запобігає підтягуванню важкого runtime-коду під час потоків налаштування.
|
||||
Докладніше див. [Налаштування й конфігурація](/uk/plugins/sdk-setup#setup-entry).
|
||||
|
||||
Канали bundled workspace, які виносять setup-safe експорти в sidecar
|
||||
Канали з комплектного робочого простору, які виокремлюють безпечні для налаштування експорти в допоміжні
|
||||
модулі, можуть використовувати `defineBundledChannelSetupEntry(...)` з
|
||||
`openclaw/plugin-sdk/channel-entry-contract`, коли їм також потрібен
|
||||
явний setter runtime під час setup.
|
||||
явний runtime-сетер на етапі налаштування.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Handle inbound messages">
|
||||
Ваш Plugin має отримувати повідомлення з платформи й пересилати їх до
|
||||
OpenClaw. Типовий патерн — Webhook, який перевіряє запит і
|
||||
передає його через inbound-обробник вашого каналу:
|
||||
Ваш Plugin має отримувати повідомлення з платформи та пересилати їх до
|
||||
OpenClaw. Типовий шаблон — це Webhook, який перевіряє запит і
|
||||
передає його через вхідний обробник вашого каналу:
|
||||
|
||||
```typescript
|
||||
registerFull(api) {
|
||||
@ -604,16 +615,16 @@ runtime helpers.
|
||||
```
|
||||
|
||||
<Note>
|
||||
Обробка inbound-повідомлень є специфічною для каналу. Кожен channel Plugin володіє
|
||||
власним inbound pipeline. Перегляньте bundled channel Plugins
|
||||
(наприклад, пакет Plugin для Microsoft Teams або Google Chat) для реальних патернів.
|
||||
Обробка вхідних повідомлень залежить від каналу. Кожен канальний Plugin володіє
|
||||
власним вхідним конвеєром. Перегляньте комплектні канальні Plugins
|
||||
(наприклад, пакет Plugin для Microsoft Teams або Google Chat), щоб побачити реальні шаблони.
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
|
||||
<a id="step-6-test"></a>
|
||||
<Step title="Test">
|
||||
Пишіть colocated тести в `src/channel.test.ts`:
|
||||
Пишіть колоковані тести в `src/channel.test.ts`:
|
||||
|
||||
```typescript src/channel.test.ts
|
||||
import { describe, it, expect } from "vitest";
|
||||
@ -651,7 +662,7 @@ runtime helpers.
|
||||
pnpm test -- <bundled-plugin-root>/acme-chat/
|
||||
```
|
||||
|
||||
Для спільних test helpers див. [Тестування](/uk/plugins/sdk-testing).
|
||||
Про спільні допоміжні засоби тестування див. [Тестування](/uk/plugins/sdk-testing).
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
@ -677,7 +688,7 @@ runtime helpers.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Threading options" icon="git-branch" href="/uk/plugins/sdk-entrypoints#registration-mode">
|
||||
Фіксовані, account-scoped або користувацькі режими відповіді
|
||||
Фіксовані, прив’язані до облікового запису або користувацькі режими відповіді
|
||||
</Card>
|
||||
<Card title="Message tool integration" icon="puzzle" href="/uk/plugins/architecture#channel-plugins-and-the-shared-message-tool">
|
||||
describeMessageTool і виявлення дій
|
||||
@ -686,26 +697,29 @@ runtime helpers.
|
||||
inferTargetChatType, looksLikeId, resolveTarget
|
||||
</Card>
|
||||
<Card title="Runtime helpers" icon="settings" href="/uk/plugins/sdk-runtime">
|
||||
TTS, STT, медіа, subagent через api.runtime
|
||||
TTS, STT, медіа, підагент через api.runtime
|
||||
</Card>
|
||||
<Card title="Channel turn kernel" icon="bolt" href="/uk/plugins/sdk-channel-turn">
|
||||
Спільний життєвий цикл вхідного ходу: приймання, розв’язання, запис, передавання, завершення
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Note>
|
||||
Деякі bundled helper seams досі існують для супроводу bundled-plugin і
|
||||
сумісності. Вони не є рекомендованим патерном для нових channel Plugins;
|
||||
віддавайте перевагу generic channel/setup/reply/runtime subpaths зі спільної поверхні SDK,
|
||||
якщо ви не супроводжуєте цю сім’ю bundled Plugins безпосередньо.
|
||||
Деякі комплектні допоміжні шви все ще існують для обслуговування комплектних Plugins і
|
||||
сумісності. Вони не є рекомендованим шаблоном для нових канальних Plugins;
|
||||
віддавайте перевагу загальним підшляхам channel/setup/reply/runtime зі спільної поверхні SDK,
|
||||
якщо ви не підтримуєте цю родину комплектних Plugins напряму.
|
||||
</Note>
|
||||
|
||||
## Наступні кроки
|
||||
|
||||
- [Provider Plugins](/uk/plugins/sdk-provider-plugins) — якщо ваш Plugin також надає моделі
|
||||
- [Огляд SDK](/uk/plugins/sdk-overview) — повний довідник імпортів subpath
|
||||
- [Тестування SDK](/uk/plugins/sdk-testing) — тестові утиліти й contract tests
|
||||
- [Огляд SDK](/uk/plugins/sdk-overview) — повна довідка щодо імпорту підшляхів
|
||||
- [Тестування SDK](/uk/plugins/sdk-testing) — тестові утиліти та контрактні тести
|
||||
- [Маніфест Plugin](/uk/plugins/manifest) — повна схема маніфесту
|
||||
|
||||
## Пов’язане
|
||||
|
||||
- [Налаштування Plugin SDK](/uk/plugins/sdk-setup)
|
||||
- [Створення Plugins](/uk/plugins/building-plugins)
|
||||
- [Agent harness Plugins](/uk/plugins/sdk-agent-harness)
|
||||
- [Plugins агентного стенда](/uk/plugins/sdk-agent-harness)
|
||||
|
||||
400
docs/uk/plugins/sdk-channel-turn.md
Normal file
400
docs/uk/plugins/sdk-channel-turn.md
Normal file
@ -0,0 +1,400 @@
|
||||
---
|
||||
read_when:
|
||||
- Ви створюєте Plugin каналу й хочете використовувати спільний життєвий цикл вхідного кроку взаємодії
|
||||
- Ви переносите монітор каналу з самописного коду-зв’язки для запису/диспетчеризації
|
||||
- Потрібно розуміти етапи допуску, приймання, класифікації, попередньої перевірки, розв’язання, запису, диспетчеризації та завершення
|
||||
sidebarTitle: Channel turn
|
||||
summary: runtime.channel.turn -- спільне ядро вхідних ходів, яке вбудовані та сторонні плагіни каналів використовують для запису, диспетчеризації та фіналізації ходів агента
|
||||
title: Ядро ходу каналу
|
||||
x-i18n:
|
||||
generated_at: "2026-04-30T00:42:46Z"
|
||||
model: gpt-5.5
|
||||
provider: openai
|
||||
source_hash: dc918da4c43f955f509aed18a93129db26efe21686c30f9328a5639f3e700984
|
||||
source_path: plugins/sdk-channel-turn.md
|
||||
workflow: 16
|
||||
---
|
||||
|
||||
Ядро обробки ходу каналу — це спільна вхідна машина станів, яка перетворює нормалізовану подію платформи на хід агента. Плагіни каналів надають факти платформи й callback доставлення. Core володіє оркестрацією: приймання, класифікація, попередня перевірка, розв’язання, авторизація, складання, запис, dispatch і фіналізація.
|
||||
|
||||
Використовуйте це, коли ваш плагін перебуває на гарячому шляху вхідного повідомлення. Для подій, що не є повідомленнями (slash-команди, модальні вікна, взаємодії з кнопками, події життєвого циклу, реакції, стан голосу), залишайте їх локальними для плагіна. Ядро володіє лише подіями, які можуть стати текстовим ходом агента.
|
||||
|
||||
<Info>
|
||||
До ядра звертаються через інʼєктований runtime плагіна як `runtime.channel.turn.*`. Тип runtime плагіна експортується з `openclaw/plugin-sdk/core`, тож сторонні нативні плагіни можуть використовувати ці точки входу так само, як це роблять bundled-плагіни каналів.
|
||||
</Info>
|
||||
|
||||
## Навіщо спільне ядро
|
||||
|
||||
Плагіни каналів повторюють той самий вхідний потік: нормалізувати, маршрутизувати, пропустити через gates, побудувати контекст, записати метадані сесії, dispatch ходу агента, фіналізувати стан доставлення. Без спільного ядра зміну в gating згадок, видимих відповідях лише для інструментів, метаданих сесії, pending-історії або фіналізації dispatch потрібно застосовувати окремо для кожного каналу.
|
||||
|
||||
Ядро навмисно тримає чотири поняття окремими:
|
||||
|
||||
- `ConversationFacts`: звідки надійшло повідомлення
|
||||
- `RouteFacts`: який агент і яка сесія мають його обробити
|
||||
- `ReplyPlanFacts`: куди мають іти видимі відповіді
|
||||
- `MessageFacts`: яке тіло й додатковий контекст має бачити агент
|
||||
|
||||
Slack DM, теми Telegram, потоки Matrix і topic-сесії Feishu на практиці розрізняють усе це. Якщо трактувати їх як один ідентифікатор, з часом виникає drift.
|
||||
|
||||
## Життєвий цикл етапів
|
||||
|
||||
Ядро виконує той самий фіксований pipeline незалежно від каналу:
|
||||
|
||||
1. `ingest` -- адаптер перетворює raw-подію платформи на `NormalizedTurnInput`
|
||||
2. `classify` -- адаптер оголошує, чи може ця подія почати хід агента
|
||||
3. `preflight` -- адаптер виконує dedupe, self-echo, hydration, debounce, decryption, часткове попереднє заповнення фактів
|
||||
4. `resolve` -- адаптер повертає повністю зібраний хід (route, reply plan, message, delivery)
|
||||
5. `authorize` -- політика DM, group, mention і command застосовується до зібраних фактів
|
||||
6. `assemble` -- `FinalizedMsgContext` будується з фактів через `buildContext`
|
||||
7. `record` -- зберігаються метадані вхідної сесії та останній route
|
||||
8. `dispatch` -- хід агента виконується через buffered block dispatcher
|
||||
9. `finalize` -- adapter `onFinalize` виконується навіть у разі помилки dispatch
|
||||
|
||||
Кожен етап emits структуровану подію журналу, коли надано callback `log`. Див. [Спостережуваність](#observability).
|
||||
|
||||
## Види допуску
|
||||
|
||||
Ядро не кидає помилку, коли хід відхиляється gate. Воно повертає `ChannelTurnAdmission`:
|
||||
|
||||
| Вид | Коли |
|
||||
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `dispatch` | Хід допущено. Хід агента запускається, і шлях видимої відповіді виконується. |
|
||||
| `observeOnly` | Хід виконується end-to-end, але delivery-адаптер не надсилає нічого видимого. Використовується для broadcast observer-агентів та інших пасивних multi-agent потоків. |
|
||||
| `handled` | Подію платформи було оброблено локально (життєвий цикл, реакція, кнопка, модальне вікно). Ядро пропускає dispatch. |
|
||||
| `drop` | Шлях пропуску. Опційно `recordHistory: true` зберігає повідомлення в pending group history, щоб майбутня згадка мала контекст. |
|
||||
|
||||
Допуск може надходити з `classify` (клас події сказав, що вона не може почати хід), з `preflight` (dedupe, self-echo, відсутня згадка із записом історії) або безпосередньо з `resolveTurn`.
|
||||
|
||||
## Точки входу
|
||||
|
||||
Runtime надає три бажані точки входу, щоб адаптери могли підключатися на рівні, який відповідає каналу.
|
||||
|
||||
```typescript
|
||||
runtime.channel.turn.run(...) // adapter-driven full pipeline
|
||||
runtime.channel.turn.runPrepared(...) // channel owns dispatch; kernel runs record + finalize
|
||||
runtime.channel.turn.buildContext(...) // pure facts to FinalizedMsgContext mapping
|
||||
```
|
||||
|
||||
Два старіші runtime helpers залишаються доступними для сумісності з Plugin SDK:
|
||||
|
||||
```typescript
|
||||
runtime.channel.turn.runResolved(...) // deprecated compatibility alias; prefer run
|
||||
runtime.channel.turn.dispatchAssembled(...) // deprecated compatibility alias; prefer run or runPrepared
|
||||
```
|
||||
|
||||
### run
|
||||
|
||||
Використовуйте, коли ваш канал може виразити свій вхідний потік як `ChannelTurnAdapter<TRaw>`. Адаптер має callbacks для `ingest`, необовʼязкового `classify`, необовʼязкового `preflight`, обовʼязкового `resolveTurn` і необовʼязкового `onFinalize`.
|
||||
|
||||
```typescript
|
||||
await runtime.channel.turn.run({
|
||||
channel: "tlon",
|
||||
accountId,
|
||||
raw: platformEvent,
|
||||
adapter: {
|
||||
ingest(raw) {
|
||||
return {
|
||||
id: raw.messageId,
|
||||
timestamp: raw.timestamp,
|
||||
rawText: raw.body,
|
||||
textForAgent: raw.body,
|
||||
};
|
||||
},
|
||||
classify(input) {
|
||||
return { kind: "message", canStartAgentTurn: input.rawText.length > 0 };
|
||||
},
|
||||
async preflight(input, eventClass) {
|
||||
if (await isDuplicate(input.id)) {
|
||||
return { admission: { kind: "drop", reason: "dedupe" } };
|
||||
}
|
||||
return {};
|
||||
},
|
||||
resolveTurn(input) {
|
||||
return buildAssembledTurn(input);
|
||||
},
|
||||
onFinalize(result) {
|
||||
clearPendingGroupHistory(result);
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
`run` — правильна форма, коли канал має невелику логіку адаптера й виграє від володіння життєвим циклом через hooks.
|
||||
|
||||
### runPrepared
|
||||
|
||||
Використовуйте, коли канал має складний локальний dispatcher із previews, retries, edits або thread bootstrap, які мають залишатися у власності каналу. Ядро все одно записує вхідну сесію перед dispatch і повертає уніфікований `DispatchedChannelTurnResult`.
|
||||
|
||||
```typescript
|
||||
const { dispatchResult } = await runtime.channel.turn.runPrepared({
|
||||
channel: "matrix",
|
||||
accountId,
|
||||
routeSessionKey,
|
||||
storePath,
|
||||
ctxPayload,
|
||||
recordInboundSession,
|
||||
record: {
|
||||
onRecordError,
|
||||
updateLastRoute,
|
||||
},
|
||||
onPreDispatchFailure: async (err) => {
|
||||
await stopStatusReactions();
|
||||
},
|
||||
runDispatch: async () => {
|
||||
return await runMatrixOwnedDispatcher();
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Rich channels (Matrix, Mattermost, Microsoft Teams, Feishu, QQ Bot) використовують `runPrepared`, тому що їхній dispatcher оркеструє platform-specific поведінку, про яку ядро не повинно знати.
|
||||
|
||||
### buildContext
|
||||
|
||||
Чиста функція, яка відображає bundles фактів у `FinalizedMsgContext`. Використовуйте її, коли ваш канал hand-rolls частину pipeline, але хоче узгоджену форму контексту.
|
||||
|
||||
```typescript
|
||||
const ctxPayload = runtime.channel.turn.buildContext({
|
||||
channel: "googlechat",
|
||||
accountId,
|
||||
messageId,
|
||||
timestamp,
|
||||
from,
|
||||
sender,
|
||||
conversation,
|
||||
route,
|
||||
reply,
|
||||
message,
|
||||
access,
|
||||
media,
|
||||
supplemental,
|
||||
});
|
||||
```
|
||||
|
||||
`buildContext` також корисний усередині callbacks `resolveTurn` під час складання ходу для `run`.
|
||||
|
||||
<Note>
|
||||
Застарілі SDK helpers, як-от `dispatchInboundReplyWithBase`, досі bridge through assembled-turn helper. Новий код плагіна має використовувати `run` або `runPrepared`.
|
||||
</Note>
|
||||
|
||||
## Типи фактів
|
||||
|
||||
Факти, які ядро споживає з вашого адаптера, не залежать від платформи. Перекладайте обʼєкти платформи в ці форми, перш ніж передавати їх ядру.
|
||||
|
||||
### NormalizedTurnInput
|
||||
|
||||
| Поле | Призначення |
|
||||
| ---------------- | ---------------------------------------------------------------------------- |
|
||||
| `id` | Стабільний message id, що використовується для dedupe і журналів |
|
||||
| `timestamp` | Необовʼязкові epoch ms |
|
||||
| `rawText` | Тіло в тому вигляді, у якому його отримано від платформи |
|
||||
| `textForAgent` | Необовʼязкове очищене тіло для агента (mention strip, typing trim) |
|
||||
| `textForCommands` | Необовʼязкове тіло, що використовується для парсингу `/command` |
|
||||
| `raw` | Необовʼязкове pass-through посилання для callbacks адаптера, яким потрібен оригінал |
|
||||
|
||||
### ChannelEventClass
|
||||
|
||||
| Поле | Призначення |
|
||||
| ---------------------- | ---------------------------------------------------------------------- |
|
||||
| `kind` | `message`, `command`, `interaction`, `reaction`, `lifecycle`, `unknown` |
|
||||
| `canStartAgentTurn` | Якщо false, ядро повертає `{ kind: "handled" }` |
|
||||
| `requiresImmediateAck` | Підказка для адаптерів, яким потрібно ACK перед dispatch |
|
||||
|
||||
### SenderFacts
|
||||
|
||||
| Поле | Призначення |
|
||||
| ------------- | ---------------------------------------------------------------------- |
|
||||
| `id` | Стабільний sender id платформи |
|
||||
| `name` | Відображуване імʼя |
|
||||
| `username` | Handle, якщо відрізняється від `name` |
|
||||
| `tag` | Дискримінатор у стилі Discord або тег платформи |
|
||||
| `roles` | Role ids, що використовуються для зіставлення member-role allowlist |
|
||||
| `isBot` | True, коли відправник є відомим ботом (ядро використовує для dropping) |
|
||||
| `isSelf` | True, коли відправник є самим налаштованим агентом |
|
||||
| `displayLabel` | Попередньо відрендерений label для envelope-тексту |
|
||||
|
||||
### ConversationFacts
|
||||
|
||||
| Поле | Призначення |
|
||||
| ----------------- | ---------------------------------------------------------------------- |
|
||||
| `kind` | `direct`, `group` або `channel` |
|
||||
| `id` | Conversation id, що використовується для routing |
|
||||
| `label` | Людський label для envelope |
|
||||
| `spaceId` | Необовʼязковий outer space identifier (Slack workspace, Matrix homeserver) |
|
||||
| `parentId` | Outer conversation id, коли це thread |
|
||||
| `threadId` | Thread id, коли це повідомлення всередині thread |
|
||||
| `nativeChannelId` | Platform-native channel id, коли відрізняється від routing id |
|
||||
| `routePeer` | Peer, що використовується для lookup `resolveAgentRoute` |
|
||||
|
||||
### RouteFacts
|
||||
|
||||
| Поле | Призначення |
|
||||
| ----------------------- | -------------------------------------------------------------- |
|
||||
| `agentId` | Агент, який має обробити цей хід |
|
||||
| `accountId` | Необовʼязковий override (multi-account channels) |
|
||||
| `routeSessionKey` | Session key, що використовується для routing |
|
||||
| `dispatchSessionKey` | Session key, що використовується під час dispatch, коли відрізняється від route key |
|
||||
| `persistedSessionKey` | Session key, що записується в persisted session metadata |
|
||||
| `parentSessionKey` | Parent для branched/threaded sessions |
|
||||
| `modelParentSessionKey` | Model-side parent для branched sessions |
|
||||
| `mainSessionKey` | Main DM owner pin для direct conversations |
|
||||
| `createIfMissing` | Дозволити етапу record створити відсутній session row |
|
||||
|
||||
### ReplyPlanFacts
|
||||
|
||||
| Поле | Призначення |
|
||||
| ------------------------- | -------------------------------------------------------------- |
|
||||
| `to` | Логічна ціль відповіді, записана в контекст `To` |
|
||||
| `originatingTo` | Початкова ціль контексту (`OriginatingTo`) |
|
||||
| `nativeChannelId` | Нативний для платформи ідентифікатор каналу для доставлення |
|
||||
| `replyTarget` | Кінцеве місце призначення видимої відповіді, якщо воно відрізняється від `to` |
|
||||
| `deliveryTarget` | Нижчорівневе перевизначення доставлення |
|
||||
| `replyToId` | Ідентифікатор процитованого/прив’язаного повідомлення |
|
||||
| `replyToIdFull` | Повна форма процитованого ідентифікатора, коли платформа має обидві |
|
||||
| `messageThreadId` | Ідентифікатор треду на момент доставлення |
|
||||
| `threadParentId` | Ідентифікатор батьківського повідомлення треду |
|
||||
| `sourceReplyDeliveryMode` | `thread`, `reply`, `channel`, `direct` або `none` |
|
||||
|
||||
### AccessFacts
|
||||
|
||||
`AccessFacts` містить булеві значення, потрібні етапу авторизації. Зіставлення ідентичності залишається в каналі: ядро лише споживає результат.
|
||||
|
||||
| Поле | Призначення |
|
||||
| ---------- | ------------------------------------------------------------------------- |
|
||||
| `dm` | Рішення дозволити/спарувати/заборонити DM і список `allowFrom` |
|
||||
| `group` | Політика групи, дозвіл маршруту, дозвіл відправника, allowlist, вимога згадки |
|
||||
| `commands` | Авторизація команд у налаштованих авторизаторах |
|
||||
| `mentions` | Чи можливе виявлення згадки та чи було згадано агента |
|
||||
|
||||
### MessageFacts
|
||||
|
||||
| Поле | Призначення |
|
||||
| ---------------- | -------------------------------------------------------------- |
|
||||
| `body` | Кінцеве тіло конверта (відформатоване) |
|
||||
| `rawBody` | Сире вхідне тіло |
|
||||
| `bodyForAgent` | Тіло, яке бачить агент |
|
||||
| `commandBody` | Тіло, використане для розбору команд |
|
||||
| `envelopeFrom` | Попередньо відрендерена мітка відправника для конверта |
|
||||
| `senderLabel` | Необов’язкове перевизначення для відрендереного відправника |
|
||||
| `preview` | Короткий редагований попередній перегляд для журналів |
|
||||
| `inboundHistory` | Останні записи вхідної історії, коли канал зберігає буфер |
|
||||
|
||||
### SupplementalContextFacts
|
||||
|
||||
Додатковий контекст охоплює контекст цитати, пересланого повідомлення та початкового завантаження треду. Ядро застосовує налаштовану політику `contextVisibility`. Адаптер каналу надає лише факти та прапорці `senderAllowed`, щоб політика між каналами залишалася узгодженою.
|
||||
|
||||
### InboundMediaFacts
|
||||
|
||||
Медіа мають форму фактів. Завантаження з платформи, авторизація, політика SSRF, правила CDN і розшифрування залишаються локальними для каналу. Ядро перетворює факти на `MediaPath`, `MediaUrl`, `MediaType`, `MediaPaths`, `MediaUrls`, `MediaTypes` і `MediaTranscribedIndexes`.
|
||||
|
||||
## Контракт адаптера
|
||||
|
||||
Для повного `run` форма адаптера така:
|
||||
|
||||
```typescript
|
||||
type ChannelTurnAdapter<TRaw> = {
|
||||
ingest(raw: TRaw): Promise<NormalizedTurnInput | null> | NormalizedTurnInput | null;
|
||||
classify?(input: NormalizedTurnInput): Promise<ChannelEventClass> | ChannelEventClass;
|
||||
preflight?(
|
||||
input: NormalizedTurnInput,
|
||||
eventClass: ChannelEventClass,
|
||||
): Promise<PreflightFacts | ChannelTurnAdmission | null | undefined>;
|
||||
resolveTurn(
|
||||
input: NormalizedTurnInput,
|
||||
eventClass: ChannelEventClass,
|
||||
preflight: PreflightFacts,
|
||||
): Promise<ChannelTurnResolved> | ChannelTurnResolved;
|
||||
onFinalize?(result: ChannelTurnResult): Promise<void> | void;
|
||||
};
|
||||
```
|
||||
|
||||
`resolveTurn` повертає `ChannelTurnResolved`, тобто `AssembledChannelTurn` з необов’язковим типом допуску. Повернення `{ admission: { kind: "observeOnly" } }` запускає хід без створення видимого виводу. Адаптер усе ще володіє callback доставлення; для цього ходу він просто стає no-op.
|
||||
|
||||
`onFinalize` виконується для кожного результату, включно з помилками диспетчеризації. Використовуйте його, щоб очищати очікувану групову історію, прибирати реакції підтвердження, зупиняти індикатори стану та скидати локальний стан.
|
||||
|
||||
## Адаптер доставлення
|
||||
|
||||
Ядро не викликає платформу напряму. Канал передає ядру `ChannelTurnDeliveryAdapter`:
|
||||
|
||||
```typescript
|
||||
type ChannelTurnDeliveryAdapter = {
|
||||
deliver(payload: ReplyPayload, info: ChannelDeliveryInfo): Promise<ChannelDeliveryResult | void>;
|
||||
onError?(err: unknown, info: { kind: string }): void;
|
||||
};
|
||||
|
||||
type ChannelDeliveryResult = {
|
||||
messageIds?: string[];
|
||||
threadId?: string;
|
||||
replyToId?: string;
|
||||
visibleReplySent?: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
`deliver` викликається один раз для кожного буферизованого фрагмента відповіді. Повертайте ідентифікатори повідомлень платформи, коли канал їх має, щоб диспетчер міг зберігати прив’язки тредів і редагувати пізніші фрагменти. Для ходів лише зі спостереженням повертайте `{ visibleReplySent: false }` або використовуйте `createNoopChannelTurnDeliveryAdapter()`.
|
||||
|
||||
## Параметри запису
|
||||
|
||||
Етап запису обгортає `recordInboundSession`. Більшість каналів можуть використовувати типові значення. Перевизначайте через `record`:
|
||||
|
||||
```typescript
|
||||
record: {
|
||||
groupResolution,
|
||||
createIfMissing: true,
|
||||
updateLastRoute,
|
||||
onRecordError: (err) => log.warn("record failed", err),
|
||||
trackSessionMetaTask: (task) => pendingTasks.push(task),
|
||||
}
|
||||
```
|
||||
|
||||
Диспетчер очікує завершення етапу запису. Якщо запис викидає помилку, ядро запускає `onPreDispatchFailure` (коли його передано до `runPrepared`) і повторно викидає помилку.
|
||||
|
||||
## Спостережуваність
|
||||
|
||||
Кожен етап випромінює структуровану подію, коли надано callback `log`:
|
||||
|
||||
```typescript
|
||||
await runtime.channel.turn.run({
|
||||
channel: "twitch",
|
||||
accountId,
|
||||
raw,
|
||||
adapter,
|
||||
log: (event) => {
|
||||
runtime.log?.debug?.(`turn.${event.stage}:${event.event}`, {
|
||||
channel: event.channel,
|
||||
accountId: event.accountId,
|
||||
messageId: event.messageId,
|
||||
sessionKey: event.sessionKey,
|
||||
admission: event.admission,
|
||||
reason: event.reason,
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Етапи журналювання: `ingest`, `classify`, `preflight`, `resolve`, `authorize`, `assemble`, `record`, `dispatch`, `finalize`. Уникайте журналювання сирих тіл; використовуйте `MessageFacts.preview` для коротких редагованих попередніх переглядів.
|
||||
|
||||
## Що залишається локальним для каналу
|
||||
|
||||
Ядро володіє оркестрацією. Канал усе ще володіє:
|
||||
|
||||
- Транспортами платформи (gateway, REST, websocket, polling, webhooks)
|
||||
- Розв’язанням ідентичності та зіставленням відображуваних імен
|
||||
- Нативними командами, slash-командами, автодоповненням, модальними вікнами, кнопками, голосовим станом
|
||||
- Рендерингом карток, модальних вікон і adaptive-card
|
||||
- Авторизацією медіа, правилами CDN, зашифрованими медіа, транскрипцією
|
||||
- API редагування, реакцій, редагування/приховування та присутності
|
||||
- Backfill і отриманням історії на боці платформи
|
||||
- Потоками спарування, які потребують специфічної для платформи перевірки
|
||||
|
||||
Якщо двом каналам починає бути потрібен той самий helper для одного з цих пунктів, винесіть спільний SDK helper замість проштовхування його в ядро.
|
||||
|
||||
## Стабільність
|
||||
|
||||
`runtime.channel.turn.*` є частиною публічної поверхні runtime Plugin. Типи фактів (`SenderFacts`, `ConversationFacts`, `RouteFacts`, `ReplyPlanFacts`, `AccessFacts`, `MessageFacts`, `SupplementalContextFacts`, `InboundMediaFacts`) і форми допуску (`ChannelTurnAdmission`, `ChannelEventClass`) доступні через `PluginRuntime` з `openclaw/plugin-sdk/core`.
|
||||
|
||||
Застосовуються правила зворотної сумісності: нові поля фактів є додатковими, типи допуску не перейменовуються, а назви точок входу залишаються стабільними. Нові потреби каналу, які вимагають неадитивної зміни, мають проходити через процес міграції SDK Plugin.
|
||||
|
||||
## Пов’язане
|
||||
|
||||
- [Створення plugins каналів](/uk/plugins/sdk-channel-plugins) для ширшого контракту plugin каналу
|
||||
- [Runtime helpers Plugin](/uk/plugins/sdk-runtime) для інших поверхонь `runtime.*`
|
||||
- [Внутрішня архітектура Plugin](/uk/plugins/architecture-internals) для конвеєра завантаження та механіки реєстру
|
||||
Loading…
Reference in New Issue
Block a user