@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.
- package/LICENSE +21 -0
- package/README.md +424 -0
- package/SKILL.md +108 -0
- package/dist/index.js +17 -0
- package/dist/setup-entry.js +8 -0
- package/dist/src/accounts.js +148 -0
- package/dist/src/actions.js +421 -0
- package/dist/src/api.js +553 -0
- package/dist/src/callbacks.js +111 -0
- package/dist/src/channel-agent-prompt.js +38 -0
- package/dist/src/channel-config.js +103 -0
- package/dist/src/channel-directory.js +84 -0
- package/dist/src/channel-lifecycle.js +219 -0
- package/dist/src/channel-outbound.js +133 -0
- package/dist/src/channel-policy.js +130 -0
- package/dist/src/channel.js +76 -0
- package/dist/src/config-schema.js +133 -0
- package/dist/src/deliver.js +126 -0
- package/dist/src/dispatch.js +186 -0
- package/dist/src/format.js +31 -0
- package/dist/src/inbound-attachments.js +125 -0
- package/dist/src/inbound.js +362 -0
- package/dist/src/media-temp.js +92 -0
- package/dist/src/model-buttons.js +96 -0
- package/dist/src/monitor-types.js +27 -0
- package/dist/src/monitor.js +40 -0
- package/dist/src/onboarding.js +180 -0
- package/dist/src/polling.js +98 -0
- package/dist/src/presentation.js +374 -0
- package/dist/src/runtime.js +36 -0
- package/dist/src/russian-trusted-ca.js +84 -0
- package/dist/src/send.js +612 -0
- package/dist/src/state.js +111 -0
- package/dist/src/sticker-cache.js +26 -0
- package/dist/src/stream-draft.js +102 -0
- package/dist/src/types.js +5 -0
- package/dist/src/webhook-queue.js +215 -0
- package/dist/src/webhook-runner.js +233 -0
- package/dist/src/webhook.js +196 -0
- package/openclaw.plugin.json +263 -0
- package/package.json +100 -0
- package/scripts/check-cycles.mjs +38 -0
- package/scripts/test-api.mjs +169 -0
- package/scripts/test-send.mjs +162 -0
- package/scripts/update-schema.mjs +48 -0
- package/src/accounts.ts +193 -0
- package/src/actions.ts +501 -0
- package/src/api.ts +758 -0
- package/src/callbacks.ts +140 -0
- package/src/channel-agent-prompt.ts +47 -0
- package/src/channel-config.ts +138 -0
- package/src/channel-directory.ts +90 -0
- package/src/channel-lifecycle.ts +256 -0
- package/src/channel-outbound.ts +159 -0
- package/src/channel-policy.ts +185 -0
- package/src/channel.ts +99 -0
- package/src/config-schema.ts +154 -0
- package/src/deliver.ts +168 -0
- package/src/dispatch.ts +224 -0
- package/src/format.ts +33 -0
- package/src/inbound-attachments.ts +152 -0
- package/src/inbound.ts +412 -0
- package/src/media-temp.ts +103 -0
- package/src/model-buttons.ts +150 -0
- package/src/monitor-types.ts +61 -0
- package/src/monitor.ts +49 -0
- package/src/onboarding.ts +215 -0
- package/src/polling.ts +125 -0
- package/src/presentation.ts +452 -0
- package/src/runtime.ts +56 -0
- package/src/russian-trusted-ca.ts +86 -0
- package/src/send.ts +774 -0
- package/src/state.ts +149 -0
- package/src/sticker-cache.ts +28 -0
- package/src/stream-draft.ts +125 -0
- package/src/types.ts +263 -0
- package/src/webhook-queue.ts +248 -0
- package/src/webhook-runner.ts +296 -0
- 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);
|