@aspalagin/openclaw-max 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +424 -0
  3. package/SKILL.md +108 -0
  4. package/dist/index.js +17 -0
  5. package/dist/setup-entry.js +8 -0
  6. package/dist/src/accounts.js +148 -0
  7. package/dist/src/actions.js +421 -0
  8. package/dist/src/api.js +553 -0
  9. package/dist/src/callbacks.js +111 -0
  10. package/dist/src/channel-agent-prompt.js +38 -0
  11. package/dist/src/channel-config.js +103 -0
  12. package/dist/src/channel-directory.js +84 -0
  13. package/dist/src/channel-lifecycle.js +219 -0
  14. package/dist/src/channel-outbound.js +133 -0
  15. package/dist/src/channel-policy.js +130 -0
  16. package/dist/src/channel.js +76 -0
  17. package/dist/src/config-schema.js +133 -0
  18. package/dist/src/deliver.js +126 -0
  19. package/dist/src/dispatch.js +186 -0
  20. package/dist/src/format.js +31 -0
  21. package/dist/src/inbound-attachments.js +125 -0
  22. package/dist/src/inbound.js +362 -0
  23. package/dist/src/media-temp.js +92 -0
  24. package/dist/src/model-buttons.js +96 -0
  25. package/dist/src/monitor-types.js +27 -0
  26. package/dist/src/monitor.js +40 -0
  27. package/dist/src/onboarding.js +180 -0
  28. package/dist/src/polling.js +98 -0
  29. package/dist/src/presentation.js +374 -0
  30. package/dist/src/runtime.js +36 -0
  31. package/dist/src/russian-trusted-ca.js +84 -0
  32. package/dist/src/send.js +612 -0
  33. package/dist/src/state.js +111 -0
  34. package/dist/src/sticker-cache.js +26 -0
  35. package/dist/src/stream-draft.js +102 -0
  36. package/dist/src/types.js +5 -0
  37. package/dist/src/webhook-queue.js +215 -0
  38. package/dist/src/webhook-runner.js +233 -0
  39. package/dist/src/webhook.js +196 -0
  40. package/openclaw.plugin.json +263 -0
  41. package/package.json +100 -0
  42. package/scripts/check-cycles.mjs +38 -0
  43. package/scripts/test-api.mjs +169 -0
  44. package/scripts/test-send.mjs +162 -0
  45. package/scripts/update-schema.mjs +48 -0
  46. package/src/accounts.ts +193 -0
  47. package/src/actions.ts +501 -0
  48. package/src/api.ts +758 -0
  49. package/src/callbacks.ts +140 -0
  50. package/src/channel-agent-prompt.ts +47 -0
  51. package/src/channel-config.ts +138 -0
  52. package/src/channel-directory.ts +90 -0
  53. package/src/channel-lifecycle.ts +256 -0
  54. package/src/channel-outbound.ts +159 -0
  55. package/src/channel-policy.ts +185 -0
  56. package/src/channel.ts +99 -0
  57. package/src/config-schema.ts +154 -0
  58. package/src/deliver.ts +168 -0
  59. package/src/dispatch.ts +224 -0
  60. package/src/format.ts +33 -0
  61. package/src/inbound-attachments.ts +152 -0
  62. package/src/inbound.ts +412 -0
  63. package/src/media-temp.ts +103 -0
  64. package/src/model-buttons.ts +150 -0
  65. package/src/monitor-types.ts +61 -0
  66. package/src/monitor.ts +49 -0
  67. package/src/onboarding.ts +215 -0
  68. package/src/polling.ts +125 -0
  69. package/src/presentation.ts +452 -0
  70. package/src/runtime.ts +56 -0
  71. package/src/russian-trusted-ca.ts +86 -0
  72. package/src/send.ts +774 -0
  73. package/src/state.ts +149 -0
  74. package/src/sticker-cache.ts +28 -0
  75. package/src/stream-draft.ts +125 -0
  76. package/src/types.ts +263 -0
  77. package/src/webhook-queue.ts +248 -0
  78. package/src/webhook-runner.ts +296 -0
  79. package/src/webhook.ts +268 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arseniy Palagin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,424 @@
1
+ # openclaw-max – плагин MAX для OpenClaw
2
+
3
+ Канал-плагин для подключения AI-ассистента [OpenClaw](https://openclaw.ai) к мессенджеру [MAX](https://max.ru) (ex-VK Teams / ICQ New).
4
+
5
+ Версия 0.7.1, изменения — в [CHANGELOG.md](CHANGELOG.md). Проверено на OpenClaw 2026.9.6 и схеме MAX Bot API 0.0.33.
6
+
7
+ ## Что это
8
+
9
+ Плагин позволяет общаться с OpenClaw-ботом через мессенджер MAX — так же, как через Telegram. Поддерживает:
10
+
11
+ - Приём и отправку текстовых сообщений (MAX-диалект markdown: `++подчёркивание++`, упоминания `max://user/id`)
12
+ - Вложения (фото, видео, аудио, файлы, стикеры, контакты, геолокация)
13
+ - Inline-кнопки: callback / link / message / clipboard / open_app / request_contact / request_geo_location
14
+ - Контракт OpenClaw `presentation`: заголовок, текст, context, divider, таблицы и графики (моноширинно), кнопки и select → inline-клавиатура; нажатия approval/ask_user/command/callback возвращаются в gateway
15
+ - Закрепление сообщений (pin/unpin) и `delivery.pin` для отправляемых сообщений
16
+ - Long polling (с персистентным marker) и Webhook на HTTP-сервере gateway (секрет, мгновенный ACK, дедупликация повторов) — см. «Webhook»
17
+ - Реестр чатов из событий bot_added/bot_started (замена deprecated GET /chats)
18
+ - Ретраи на 429/сетевые сбои и `attachment.not.ready`
19
+ - Мультиаккаунт
20
+ - DM-security и pairing
21
+
22
+ ## TLS: сертификат Минцифры (важно!)
23
+
24
+ С июля 2026 MAX Bot API живёт на `platform-api2.max.ru` с сертификатом, выпущенным
25
+ Russian Trusted Sub CA (Минцифры), которого нет в стандартном доверенном наборе Node.js.
26
+ Плагин решает это сам: все запросы к API идут через выделенный undici-dispatcher,
27
+ в CA-набор которого добавлены Russian Trusted Root/Sub CA (встроены в пакет,
28
+ `src/russian-trusted-ca.ts`). Доверие ограничено только соединениями плагина —
29
+ процесс-wide trust store не трогается, `NODE_EXTRA_CA_CERTS` не требуется.
30
+
31
+ ## Структура проекта
32
+
33
+ ```
34
+ openclaw-max/
35
+ ├── index.ts # Точка входа (defineChannelPluginEntry) → dist/index.js
36
+ ├── setup-entry.ts # Точка входа setup-режима (defineSetupPluginEntry) → dist/setup-entry.js
37
+ ├── openclaw.plugin.json # Манифест плагина
38
+ ├── package.json
39
+ ├── tsconfig.json
40
+ ├── README.md
41
+ ├── src/
42
+ │ ├── channel.ts # maxPlugin: сборка адаптеров канала
43
+ │ ├── channel-config.ts # Аккаунты, включение/удаление, CLI setup
44
+ │ ├── channel-policy.ts # DM-политика, группы (requireMention, tools), pairing
45
+ │ ├── channel-directory.ts # Нормализация целей, directory (self/peers/groups)
46
+ │ ├── channel-outbound.ts # sendText/sendPayload/sendMedia, presentation, pin
47
+ │ ├── channel-lifecycle.ts # Старт/стоп аккаунта, статус, probe, аудит групп
48
+ │ ├── channel-agent-prompt.ts # Подсказки агенту (стикеры, location, кнопки…)
49
+ │ ├── monitor.ts # Точка входа приёма: выбор транспорта
50
+ │ ├── monitor-types.ts # Опции монитора, статус, подписанные update_type
51
+ │ ├── polling.ts # Long polling (marker), снятие чужих подписок
52
+ │ ├── webhook-runner.ts # Webhook-режим: секрет, подписка, сверка, запуск очереди
53
+ │ ├── webhook-queue.ts # Webhook: очередь апдейтов, обработка в задаче аккаунта
54
+ │ ├── webhook.ts # Webhook: роут gateway, secret, быстрый ACK, дедупликация
55
+ │ ├── dispatch.ts # Разбор update'ов по update_type
56
+ │ ├── inbound.ts # Gate DM/групп, контекст, запуск агента
57
+ │ ├── inbound-attachments.ts # Скачивание и описание входящих вложений
58
+ │ ├── callbacks.ts # Нажатия кнопок, approval/ask_user
59
+ │ ├── deliver.ts # Доставка ответа: чанки, альбомы, pin
60
+ │ ├── stream-draft.ts # Edit-стриминг (streamMode: partial)
61
+ │ ├── send.ts # Send-хелперы (sendWithBody, кнопки, медиа, pin)
62
+ │ ├── actions.ts # message-tool actions (send/edit/delete/pin/…)
63
+ │ ├── presentation.ts # Рендер presentation и callback-конверты
64
+ │ ├── api.ts # HTTP-клиент MAX API (retry, TLS, upload, лимитер)
65
+ │ ├── types.ts # TypeScript-типы MAX Bot API
66
+ │ ├── russian-trusted-ca.ts # Встроенные сертификаты Минцифры
67
+ │ ├── format.ts # Конвертация markdown в MAX-диалект
68
+ │ ├── media-temp.ts # Временные файлы медиа, очистка имён
69
+ │ ├── state.ts # Персист: marker, реестр чатов, секрет webhook
70
+ │ ├── accounts.ts # Резолвинг аккаунтов из конфига
71
+ │ ├── config-schema.ts # Zod-схема конфига
72
+ │ ├── model-buttons.ts # Кнопки выбора модели
73
+ │ ├── onboarding.ts # Setup wizard
74
+ │ ├── sticker-cache.ts # Кэш кодов стикеров
75
+ │ └── __fixtures__/ # Снимок схемы MAX (max-schema-<версия>.yaml)
76
+ └── scripts/
77
+ ├── test-api.mjs # Проверка токена и API
78
+ ├── test-send.mjs # Тест send + edit + delete
79
+ ├── check-cycles.mjs # Проверка циклических импортов src/
80
+ └── update-schema.mjs # Обновление снимка схемы MAX
81
+ ```
82
+
83
+ ## Установка
84
+
85
+ ### 1. Создать бота в MAX
86
+
87
+ 1. Зайти на [business.max.ru](https://business.max.ru/self) (нужно юрлицо/ИП)
88
+ 2. Создать профиль организации и пройти верификацию
89
+ 3. Раздел **Чат-боты** → **Создать** (название, лого 500x500, описание)
90
+ 4. Дождаться модерации (до 48ч по рабочим дням)
91
+ 5. После модерации: **Чат-боты → Интеграция → Получить токен**
92
+
93
+ ### 2. Установить плагин
94
+
95
+ Через ClawHub (рекомендуется):
96
+
97
+ ```bash
98
+ openclaw plugins install clawhub:@aspalagin/openclaw-max
99
+ ```
100
+
101
+ Через npm:
102
+
103
+ ```bash
104
+ openclaw plugins install npm:@aspalagin/openclaw-max
105
+ ```
106
+
107
+ Вручную из исходников (для разработки):
108
+
109
+ ```bash
110
+ cd ~/.openclaw/extensions
111
+ git clone https://github.com/aspalagin/openclaw-max openclaw-max
112
+ cd openclaw-max && npm install && npm run build
113
+ ```
114
+
115
+ ### 3. Настроить конфиг
116
+
117
+ Добавить секцию в `~/.openclaw/openclaw.json`:
118
+
119
+ ```jsonc
120
+ {
121
+ "channels": {
122
+ "max": {
123
+ // Токен бота из business.max.ru → Чат-боты → Интеграция
124
+ "botToken": "ваш_токен_бота",
125
+
126
+ // Список user_id, которым разрешено писать боту
127
+ // Узнать свой user_id: написать боту, посмотреть в логах
128
+ "allowFrom": ["12345678"],
129
+
130
+ // Политика DM-доступа:
131
+ // "allowlist" — только из allowFrom (по умолчанию)
132
+ // "open" — любой может писать
133
+ // "pairing" — новые контакты проходят pairing-код
134
+ "dmPolicy": "allowlist"
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ ### 4. Перезапустить gateway
141
+
142
+ ```bash
143
+ openclaw gateway restart
144
+ ```
145
+
146
+ ## Настройка — описание полей
147
+
148
+ | Поле | Тип | Обязательно | Описание |
149
+ |------|-----|-------------|----------|
150
+ | `botToken` | string | да | API-токен бота (или env `MAX_BOT_TOKEN`) |
151
+ | `allowFrom` | string[] | да* | Список разрешённых user_id |
152
+ | `dmPolicy` | string | нет | Политика DM: pairing (по умолчанию) / allowlist / open / disabled |
153
+ | `groupPolicy` | string | нет | Политика групп: allowlist (по умолчанию) / open / disabled |
154
+ | `groups` | object | нет | Пер-групповые настройки (requireMention, tools, …) |
155
+ | `transport` | string | нет | `polling` / `webhook`; по умолчанию `webhook`, если задан `webhookUrl`, иначе `polling` |
156
+ | `webhookUrl` | string | нет | Публичный HTTPS-адрес для MAX; включает webhook-режим (см. «Webhook») |
157
+ | `webhookSecretFile` | string | нет | Файл с секретом вебхука (как `tokenFile`) |
158
+ | `webhookSecret` | string | нет | Секрет вебхука строкой; если не задан ни он, ни файл — генерируется и хранится в state-файле аккаунта |
159
+ | `webhookPath` | string | нет | Путь роута на gateway, если отличается от pathname `webhookUrl` (по умолчанию `/max/webhook`) |
160
+ | `streamMode` | string | нет | off (по умолчанию) / partial / block |
161
+ | `mediaMaxMb` | number | нет | Лимит скачивания медиа, МБ (по умолчанию 20) |
162
+ | `markSeen` | boolean | нет | Слать mark_seen на входящие (по умолчанию true) |
163
+ | `commands` | array | нет | Команды бота `[{name, description}]` — регистрируются через PATCH /me/commands (до 32) |
164
+
165
+ \* Обязательно при `dmPolicy: "allowlist"`.
166
+
167
+ ## Webhook
168
+
169
+ По умолчанию плагин получает события long polling'ом. В webhook-режиме MAX сам
170
+ присылает `POST` на публичный адрес, а плагин обслуживает его роутом на
171
+ HTTP-сервере gateway (тот же порт, что и Control UI; отдельный сервер не
172
+ поднимается).
173
+
174
+ ### Требования MAX
175
+
176
+ - Только HTTPS на порту 443 с сертификатом доверенного CA (самоподписанный не подойдёт).
177
+ - Ответ 200 не позже чем через 30 с. Плагин отвечает сразу после проверки
178
+ секрета и формы тела, а обработку (агент может думать минутами) ведёт асинхронно.
179
+ - При неудаче MAX повторяет доставку до 10 раз с растущим интервалом, а после
180
+ 8 часов без успеха **сам снимает подписку**. Повторы одного и того же
181
+ обновления плагин отсекает (LRU по `update_type` + `timestamp` + `mid`/`callback_id`).
182
+ - Каждый запрос несёт заголовок `X-Max-Bot-Api-Secret`; без совпадения — 401.
183
+ - **Пока подписка активна, long polling у MAX не работает.** Поэтому в
184
+ polling-режиме плагин при старте удаляет найденную подписку (с warning в логе).
185
+
186
+ ### Как устроено
187
+
188
+ 1. При старте аккаунта регистрируется роут `auth: "plugin"`, `match: "exact"` на
189
+ пути `webhookPath` → pathname `webhookUrl` → `/max/webhook`. Не удалось
190
+ зарегистрировать (например, путь занят другим плагином) — старт аккаунта падает.
191
+ 2. `GET /subscriptions`: подписки этого бота на другие URL удаляются
192
+ (`DELETE /subscriptions?url=`).
193
+ 3. `POST /subscriptions {url, update_types, secret}` — на каждом старте, чтобы
194
+ секрет и список событий совпадали с текущим конфигом.
195
+ 4. При остановке роут снимается, **подписка остаётся**: рестарт gateway не
196
+ теряет события (MAX повторит недоставленные). Чтобы выключить webhook,
197
+ переключите `transport: "polling"` — плагин удалит подписку при старте, —
198
+ или удалите её вручную `DELETE /subscriptions?url=<webhookUrl>`.
199
+ 5. Пока аккаунт работает, раз в 12 минут `GET /subscriptions` сверяет, что
200
+ подписка на `webhookUrl` жива. Если MAX её снял (8 часов недоставки —
201
+ упал туннель или gateway), плагин пересоздаёт её `POST /subscriptions` и
202
+ пишет warning; ошибка сети при проверке только логируется. Таймер
203
+ останавливается вместе с аккаунтом.
204
+ 6. **Обработка идёт в задаче аккаунта, а не в HTTP-запросе.** Обработчик роута
205
+ проверяет секрет, тело и дубликат, сразу отвечает `200 {"ok":true}` и только
206
+ кладёт апдейт в очередь аккаунта. Задача аккаунта (та же, что держит роут)
207
+ забирает апдейты и вызывает обработку: в одном чате строго по порядку,
208
+ разные чаты — параллельно, не больше 4 одновременно. Причина: gateway
209
+ выдаёт HTTP-обработчику допуск работы только на время запроса; запуск
210
+ агента из контекста уже завершённого запроса отвергается как
211
+ `GatewayDrainingError: Gateway is draining` (так было в 0.7.0). При
212
+ остановке аккаунта начатые апдейты дорабатывают (до 3 с ожидания), а
213
+ принятые, но не начатые передаются следующему старту аккаунта в этом же
214
+ процессе — MAX их уже не повторит.
215
+
216
+ Секрет берётся из `webhookSecret`, иначе из `webhookSecretFile`, иначе
217
+ генерируется один раз и сохраняется в `~/.openclaw/max/state-<account>.json`
218
+ (переживает рестарты). Формат MAX: 5–256 символов `A-Z a-z 0-9 _ -`.
219
+
220
+ ### Пример (эта установка)
221
+
222
+ Публичный адрес `https://max.kotbanzai.com/max/webhook` проксируется Cloudflare
223
+ Tunnel на `http://127.0.0.1:18789` (наружу открыт только путь `/max/webhook`).
224
+
225
+ ```bash
226
+ umask 077
227
+ head -c 48 /dev/urandom | base64 | tr -dc 'A-Za-z0-9_-' | head -c 48 \
228
+ > /root/.openclaw/secrets/max-webhook-secret
229
+ ```
230
+
231
+ ```json
232
+ {
233
+ "channels": {
234
+ "max": {
235
+ "tokenFile": "/root/.openclaw/secrets/gateway-max-bot-token",
236
+ "transport": "webhook",
237
+ "webhookUrl": "https://max.kotbanzai.com/max/webhook",
238
+ "webhookSecretFile": "/root/.openclaw/secrets/max-webhook-secret"
239
+ }
240
+ }
241
+ }
242
+ ```
243
+
244
+ ### Порядок включения
245
+
246
+ 1. Сначала задеплоить эту версию плагина и **полностью перезапустить gateway**
247
+ с новым кодом, оставаясь в polling-режиме. Старые версии плагина при
248
+ `webhookUrl` подписывались, но роут не регистрировали — бот глох.
249
+ 2. Затем добавить в конфиг `transport`/`webhookUrl`/`webhookSecretFile`
250
+ (поля `transport` и `webhookSecretFile` знает только новая схема) и
251
+ перезапустить канал/gateway. Подписку создаёт сам плагин при старте.
252
+ 3. Проверить: `GET /subscriptions` показывает ровно один URL, в логе
253
+ `MAX webhook subscribed`, сообщение боту доходит до агента.
254
+
255
+ Пошаговый runbook деплоя 0.7.0 на этой установке (бэкапы, проверки, откаты):
256
+ `/root/.openclaw/workspace-arseniy/deliverables/max-plugin-upgrade-20260926/deploy-runbook.md`
257
+ (рабочий каталог ассистента, вне репозитория).
258
+
259
+ Откат: `transport: "polling"` (или убрать `webhookUrl`) и перезапуск — подписка
260
+ удаляется при старте polling. Если плагин не стартует, снять подписку руками:
261
+ `curl -X DELETE "https://platform-api2.max.ru/subscriptions?url=<webhookUrl>" -H "Authorization: <token>"`.
262
+
263
+ ## Использование
264
+
265
+ ### Как написать боту
266
+
267
+ 1. Найти бота в MAX по нику (например `@idИНН_bot`)
268
+ 2. Нажать **Старт** или отправить любое сообщение
269
+ 3. Бот ответит, если ваш `user_id` в `allowFrom`
270
+
271
+ ### Как узнать свой user_id
272
+
273
+ Написать боту, затем посмотреть в логах OpenClaw:
274
+
275
+ ```bash
276
+ openclaw logs --follow
277
+ # В логах будет: sender.user_id: 12345678
278
+ ```
279
+
280
+ Или запустить тест-скрипт:
281
+
282
+ ```bash
283
+ MAX_BOT_TOKEN=xxx node scripts/test-api.mjs
284
+ # В разделе updates будет виден ваш user_id
285
+ ```
286
+
287
+ ### allowFrom и pairing
288
+
289
+ - **allowlist** (по умолчанию): только user_id из списка могут общаться с ботом
290
+ - **open**: любой пользователь MAX может писать боту
291
+ - **pairing**: новый пользователь получает код, который нужно подтвердить у владельца
292
+
293
+ ## Мультиаккаунт
294
+
295
+ Можно подключить несколько ботов MAX — например, рабочий и личный:
296
+
297
+ ```jsonc
298
+ {
299
+ "channels": {
300
+ "max": {
301
+ // Аккаунт по умолчанию
302
+ "botToken": "токен_основного_бота",
303
+ "allowFrom": ["12345678"],
304
+
305
+ // Дополнительные аккаунты
306
+ "accounts": {
307
+ "zaya": {
308
+ "botToken": "токен_второго_бота",
309
+ "allowFrom": ["87654321"],
310
+ "dmPolicy": "open"
311
+ }
312
+ }
313
+ }
314
+ }
315
+ }
316
+ ```
317
+
318
+ Обращение к конкретному аккаунту:
319
+
320
+ ```
321
+ openclaw --account zaya send "Привет из второго бота"
322
+ ```
323
+
324
+ ## Разработка
325
+
326
+ ### Требования
327
+
328
+ - Node.js 22+
329
+ - OpenClaw 2026.9.6+ (devDependency, SDK-подпути `openclaw/plugin-sdk/*`)
330
+
331
+ ### Сборка
332
+
333
+ ```bash
334
+ cd openclaw-max
335
+ npm ci
336
+ npm run build # tsc → dist/index.js, dist/setup-entry.js, dist/src/*.js (без тестов)
337
+ ```
338
+
339
+ `build` собирает и при ошибках типов (`--noEmitOnError false`), поэтому перед ним
340
+ запускайте `npm run typecheck`.
341
+
342
+ ### Проверки (как в CI)
343
+
344
+ ```bash
345
+ npm run format:check # prettier
346
+ npm run lint # eslint, 0 ошибок и 0 предупреждений
347
+ npm run check:cycles # циклические импорты между модулями src/
348
+ npm run typecheck
349
+ npm test # vitest, включая сверку со схемой MAX
350
+ ```
351
+
352
+ ### Схема MAX Bot API
353
+
354
+ `src/schema-conformance.test.ts` сверяет подписку, типы update/кнопок/вложений и
355
+ тела запросов со снимком `src/__fixtures__/max-schema-<версия>.yaml`
356
+ ([max-messenger/api-schema](https://github.com/max-messenger/api-schema)).
357
+ Обновить снимок:
358
+
359
+ ```bash
360
+ npm run schema:update # последний коммит репозитория схемы
361
+ npm run schema:update -- <commit> # конкретный коммит, тег или ветка
362
+ npm test
363
+ ```
364
+
365
+ Скрипт заменяет старый снимок новым (имя по `info.version`) и пишет в заголовок
366
+ файла коммит и дату. Упавший тест сверки означает, что плагин отправляет или ждёт
367
+ то, чего в новой схеме нет: поправить код или тип, затем закоммитить снимок.
368
+
369
+ ### Тест API (проверка что токен работает)
370
+
371
+ ```bash
372
+ # Проверить бота: GET /me + GET /updates
373
+ MAX_BOT_TOKEN=xxx node scripts/test-api.mjs
374
+
375
+ # С отправкой тестового сообщения в чат
376
+ MAX_BOT_TOKEN=xxx node scripts/test-api.mjs <chat_id>
377
+ ```
378
+
379
+ ### Тест отправки (send + edit + delete)
380
+
381
+ ```bash
382
+ # Полный цикл: отправить → подождать → отредактировать → подождать → удалить
383
+ MAX_BOT_TOKEN=xxx node scripts/test-send.mjs <chat_id> "Текст сообщения"
384
+ ```
385
+
386
+ ## Поддержка
387
+
388
+ - Вопросы по использованию и предложения: [GitHub Issues](https://github.com/aspalagin/openclaw-max/issues)
389
+ - Сообщения об уязвимостях: [Security policy](SECURITY.md)
390
+ - Правила участия: [CONTRIBUTING.md](CONTRIBUTING.md)
391
+
392
+ ## MAX Bot API — краткая справка
393
+
394
+ Базовый URL: `https://platform-api2.max.ru` (старый `platform-api.max.ru` отключается 19.07.2026).
395
+
396
+ | Метод | Endpoint | Описание |
397
+ |-------|----------|----------|
398
+ | GET /me | Информация о боте | user_id, name, username, commands |
399
+ | PATCH /me/commands | Команды бота | commands (полная замена списка, до 32) |
400
+ | GET /updates | Long polling | marker, timeout, types |
401
+ | POST /messages | Отправить | ?chat_id или ?user_id |
402
+ | PUT /messages | Редактировать | ?message_id (до 24ч) |
403
+ | DELETE /messages | Удалить | ?message_id (до 24ч) |
404
+ | POST /answers | Ответ на callback | ?callback_id |
405
+ | GET /chats/{id}/members/me | Членство бота | is_admin (нужно для получения событий групп) |
406
+ | PUT/DELETE /chats/{id}/pin | Закрепить/открепить | message_id |
407
+ | GET /videos/{token} | Playback-ссылки видео | urls может быть null, пока видео обрабатывается |
408
+ | POST /uploads | URL для загрузки медиа | type=image/video/audio/file |
409
+
410
+ Авторизация: заголовок `Authorization: <token>`
411
+ Лимит: 30 запросов/сек
412
+ Документация: [dev.max.ru/docs-api](https://dev.max.ru/docs-api)
413
+
414
+ Примечания:
415
+ - `GET /chats` объявлен deprecated (июнь 2026) — плагин собирает чаты в собственный реестр из событий.
416
+ - В группах long polling доставляет события только боту-администратору (проверяется в `openclaw channels status --audit`).
417
+ - Webhook: только HTTPS:443 с доверенным сертификатом; MAX ждёт HTTP 200 не дольше 30с (плагин отвечает мгновенно, обработка асинхронная).
418
+
419
+ ## Авторы
420
+
421
+ - **[Petlevoy](https://github.com/petlevoy)** - отец проекта
422
+ - **Яков** (@Helpdesk_VP_bot) — архитектура, координация
423
+ - **Банзай** (@KotBanzaiBot) — реализация модулей, типы, тесты
424
+ - **openclaw-max subagent** — интеграция с OpenClaw Plugin SDK
package/SKILL.md ADDED
@@ -0,0 +1,108 @@
1
+ ---
2
+ name: MAX
3
+ description: Канал-плагин для подключения OpenClaw к мессенджеру MAX (max.ru). Поддерживает отправку и получение текстовых сообщений, медиа, inline-кнопки (callback/link/message/clipboard/open_app), pin/unpin, форматирование в MAX-диалекте markdown. Работает в режимах long polling и webhook. Используй когда нужно отправить сообщение через мессенджер MAX или обработать входящие сообщения из MAX.
4
+ ---
5
+
6
+ # MAX — плагин для OpenClaw
7
+
8
+ Канал-плагин для интеграции OpenClaw с мессенджером [MAX](https://max.ru) через Bot API (`platform-api2.max.ru`).
9
+
10
+ ## Что делает
11
+
12
+ - **Текстовые сообщения** — отправка и получение; исходящий markdown конвертируется в MAX-диалект (`++подчёркивание++`)
13
+ - **Медиа** — фото, видео, аудио (включая m4a/heic с iPhone), файлы, стикеры, контакты, геолокация
14
+ - **Inline-кнопки** — callback, link, message (suggested replies), clipboard, open_app, request_contact, request_geo_location
15
+ - **Pin/unpin** — закрепление сообщений в чатах
16
+ - **Long polling** — с персистентным marker (рестарты без потери/дублей событий)
17
+ - **Webhook** — HTTPS c обязательным secret (X-Max-Bot-Api-Secret), мгновенный ACK, последовательная обработка
18
+ - **Команды бота** — регистрация через PATCH /me/commands из `channels.max.commands`
19
+ - **Мультиаккаунт**, DM-security (pairing/allowlist/open), пер-групповые политики
20
+
21
+ TLS-сертификат Минцифры для platform-api2.max.ru встроен в плагин — дополнительная настройка не нужна.
22
+
23
+ ## Установка
24
+
25
+ Через ClawHub (рекомендуется):
26
+
27
+ ```bash
28
+ openclaw plugins install clawhub:@aspalagin/openclaw-max
29
+ ```
30
+
31
+ Через npm:
32
+
33
+ ```bash
34
+ openclaw plugins install npm:@aspalagin/openclaw-max
35
+ ```
36
+
37
+ Вручную из исходников (для разработки):
38
+
39
+ ```bash
40
+ cd ~/.openclaw/extensions
41
+ git clone https://github.com/aspalagin/openclaw-max openclaw-max
42
+ cd openclaw-max && npm install && npm run build
43
+ ```
44
+
45
+ ## Настройка
46
+
47
+ В `~/.openclaw/openclaw.json`:
48
+
49
+ ```jsonc
50
+ {
51
+ "channels": {
52
+ "max": {
53
+ "enabled": true,
54
+ "botToken": "ТОКЕН_БОТА", // или env MAX_BOT_TOKEN
55
+ "dmPolicy": "pairing", // pairing | allowlist | open | disabled
56
+ "allowFrom": ["12345678"],
57
+ "commands": [
58
+ { "name": "status", "description": "Статус ассистента" }
59
+ ]
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ Webhook-режим: добавить `"transport": "webhook"`, `"webhookUrl": "https://ваш-домен/max/webhook"`
66
+ (только HTTPS:443, доверенный сертификат) и `"webhookSecretFile"` (без него secret
67
+ генерируется и хранится в state-файле). Роут поднимается на HTTP-сервере gateway,
68
+ подписку создаёт плагин; порядок включения и откат — README, раздел «Webhook».
69
+
70
+ ## Примеры использования
71
+
72
+ ### Отправка сообщения
73
+
74
+ ```bash
75
+ openclaw message send --channel max --target "144660345" --message "Привет из OpenClaw!"
76
+ ```
77
+
78
+ Цели: числовой chat_id, `user:<id>` (адресация в личку; голое число, для которого MAX ответил `dialog.not.found`, повторяется как user_id), `@username` и ссылки max.ru не поддерживаются: MAX Bot API не резолвит их (`chat.not.found`), плагин сразу отвечает понятной ошибкой — для чатов и каналов используйте числовой chat_id.
79
+
80
+ ### Кнопки (из message-tool агента)
81
+
82
+ ```
83
+ message(action="send", target="CHAT_ID", message="Выберите:",
84
+ buttons=[[{"text":"Да","type":"callback","payload":"yes"},
85
+ {"text":"Подробнее","type":"message"}]])
86
+ ```
87
+
88
+ ### Pin
89
+
90
+ ```
91
+ message(action="pin", target="CHAT_ID", messageId="MID")
92
+ message(action="unpin", target="CHAT_ID")
93
+ ```
94
+
95
+ В диалогах (личка, `user:<id>`) MAX не поддерживает закреп: плагин не вызывает API и возвращает `pinned: false` с причиной.
96
+
97
+ ### Несколько вложений
98
+
99
+ `message(action="send", target=…, message="Подпись", attachments=[{"path": …}, {"path": …}])` отправляет все вложения: фото/видео альбомами до 12, файлы и аудио по одному; в ответе `messageIds`.
100
+
101
+ ## Требования
102
+
103
+ - OpenClaw 2026.x (plugin SDK)
104
+ - Node.js >= 20
105
+
106
+ ## Поддержка
107
+
108
+ - Issues: https://github.com/aspalagin/openclaw-max/issues
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * MAX messenger channel plugin for OpenClaw — full entry.
3
+ *
4
+ * defineChannelPluginEntry registers the channel and stores the plugin runtime
5
+ * (setMaxRuntime) in every registration mode that loads channels; setup-only
6
+ * loads use setup-entry.ts instead.
7
+ */
8
+ import { defineChannelPluginEntry } from 'openclaw/plugin-sdk/channel-core';
9
+ import { maxPlugin } from './src/channel.js';
10
+ import { setMaxRuntime } from './src/runtime.js';
11
+ export default defineChannelPluginEntry({
12
+ id: 'openclaw-max',
13
+ name: 'MAX',
14
+ description: 'MAX messenger channel plugin (max.ru Bot API)',
15
+ plugin: maxPlugin,
16
+ setRuntime: setMaxRuntime,
17
+ });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * MAX messenger channel plugin — setup entry. OpenClaw loads it instead of
3
+ * index.ts when the channel is disabled or not configured yet (onboarding,
4
+ * config repair): the channel plugin only, no runtime wiring.
5
+ */
6
+ import { defineSetupPluginEntry } from 'openclaw/plugin-sdk/channel-core';
7
+ import { maxPlugin } from './src/channel.js';
8
+ export default defineSetupPluginEntry(maxPlugin);