@aspalagin/openclaw-max 0.7.2 → 0.8.0
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 +2 -0
- package/README.md +412 -344
- package/README_EN.md +492 -0
- package/SKILL.md +45 -37
- package/assets/icon.png +0 -0
- package/dist/secret-contract-api.js +6 -0
- package/dist/src/access-policy.js +123 -0
- package/dist/src/accounts.js +170 -23
- package/dist/src/action-scope.js +213 -0
- package/dist/src/actions.js +159 -31
- package/dist/src/api.js +99 -83
- package/dist/src/callbacks.js +27 -7
- package/dist/src/channel-agent-prompt.js +8 -19
- package/dist/src/channel-config.js +25 -3
- package/dist/src/channel-lifecycle.js +28 -5
- package/dist/src/channel-outbound.js +43 -9
- package/dist/src/channel.js +16 -0
- package/dist/src/command-menu.js +199 -0
- package/dist/src/config-schema.js +106 -10
- package/dist/src/deliver.js +144 -48
- package/dist/src/dispatch.js +12 -11
- package/dist/src/gateway-drain.js +40 -0
- package/dist/src/group-mention.js +129 -0
- package/dist/src/inbound-attachments.js +162 -20
- package/dist/src/inbound.js +388 -134
- package/dist/src/media-temp.js +31 -20
- package/dist/src/network.js +216 -0
- package/dist/src/onboarding.js +4 -2
- package/dist/src/polling.js +180 -12
- package/dist/src/presentation.js +11 -0
- package/dist/src/progress-draft.js +168 -0
- package/dist/src/runtime.js +15 -0
- package/dist/src/secret-contract.js +149 -0
- package/dist/src/send.js +250 -46
- package/dist/src/stream-draft.js +83 -41
- package/dist/src/turn-adoption.js +24 -0
- package/dist/src/typing.js +55 -0
- package/dist/src/update-journal.js +267 -0
- package/dist/src/webhook-queue.js +151 -26
- package/dist/src/webhook-runner.js +61 -9
- package/dist/src/webhook.js +34 -15
- package/openclaw.plugin.json +986 -13
- package/package.json +25 -5
- package/src/access-policy.ts +154 -0
- package/src/accounts.ts +260 -25
- package/src/action-scope.ts +274 -0
- package/src/actions.ts +188 -35
- package/src/api.ts +158 -101
- package/src/callbacks.ts +26 -7
- package/src/channel-agent-prompt.ts +8 -19
- package/src/channel-config.ts +32 -3
- package/src/channel-lifecycle.ts +33 -6
- package/src/channel-outbound.ts +50 -7
- package/src/channel.ts +16 -0
- package/src/command-menu.ts +277 -0
- package/src/config-schema.ts +111 -10
- package/src/deliver.ts +174 -55
- package/src/dispatch.ts +12 -11
- package/src/gateway-drain.ts +45 -0
- package/src/group-mention.ts +191 -0
- package/src/inbound-attachments.ts +173 -22
- package/src/inbound.ts +443 -130
- package/src/media-temp.ts +35 -23
- package/src/network.ts +233 -0
- package/src/onboarding.ts +4 -2
- package/src/polling.ts +221 -15
- package/src/presentation.ts +14 -1
- package/src/progress-draft.ts +226 -0
- package/src/runtime.ts +23 -0
- package/src/secret-contract.ts +183 -0
- package/src/send.ts +346 -59
- package/src/stream-draft.ts +102 -41
- package/src/turn-adoption.ts +33 -0
- package/src/types.ts +9 -3
- package/src/typing.ts +77 -0
- package/src/update-journal.ts +302 -0
- package/src/webhook-queue.ts +205 -33
- package/src/webhook-runner.ts +65 -8
- package/src/webhook.ts +39 -18
- package/scripts/check-cycles.mjs +0 -38
- package/scripts/test-api.mjs +0 -169
- package/scripts/test-send.mjs +0 -162
- package/scripts/update-schema.mjs +0 -48
package/README.md
CHANGED
|
@@ -1,313 +1,303 @@
|
|
|
1
|
-
# openclaw-max
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
```
|
|
1
|
+
# openclaw-max — канал мессенджера MAX для OpenClaw
|
|
2
|
+
|
|
3
|
+
[English version](README_EN.md)
|
|
4
|
+
|
|
5
|
+
Плагин канала, который подключает ассистента [OpenClaw](https://openclaw.ai) к мессенджеру [MAX](https://max.ru) через MAX Bot API (`platform-api2.max.ru`). Люди пишут вашему боту в MAX — в личном диалоге или в группе, — и агент OpenClaw отвечает там же: с медиа, кнопками и голосом.
|
|
6
|
+
|
|
7
|
+
Плагин нужен тем, кто пользуется OpenClaw и хочет, чтобы ассистент был доступен в MAX. Понадобятся токен бота MAX (ботов создают организации на [business.max.ru](https://business.max.ru/self)) и собственный gateway OpenClaw.
|
|
8
|
+
|
|
9
|
+
Версия 0.8.0. Изменения: [CHANGELOG.md](https://github.com/aspalagin/openclaw-max/blob/main/CHANGELOG.md).
|
|
10
|
+
|
|
11
|
+
## Содержание
|
|
12
|
+
|
|
13
|
+
- [Поддерживаемые типы сообщений](#поддерживаемые-типы-сообщений)
|
|
14
|
+
- [Требования и совместимость](#требования-и-совместимость)
|
|
15
|
+
- [Установка](#установка)
|
|
16
|
+
- [Быстрый старт](#быстрый-старт)
|
|
17
|
+
- [Транспорты: webhook и long polling](#транспорты-webhook-и-long-polling)
|
|
18
|
+
- [Справочник настроек](#справочник-настроек)
|
|
19
|
+
- [Доступ и политики](#доступ-и-политики)
|
|
20
|
+
- [Действия инструмента `message` и где они разрешены](#действия-инструмента-message-и-где-они-разрешены)
|
|
21
|
+
- [Медиа и локальные файлы](#медиа-и-локальные-файлы)
|
|
22
|
+
- [Голосовые сообщения](#голосовые-сообщения)
|
|
23
|
+
- [Команды и меню](#команды-и-меню)
|
|
24
|
+
- [Статус хода](#статус-хода)
|
|
25
|
+
- [Несколько аккаунтов и наследование](#несколько-аккаунтов-и-наследование)
|
|
26
|
+
- [Прокси, адрес API и сертификаты Russian Trusted CA](#прокси-адрес-api-и-сертификаты-russian-trusted-ca)
|
|
27
|
+
- [Секреты и SecretRef](#секреты-и-secretref)
|
|
28
|
+
- [Надёжность](#надёжность)
|
|
29
|
+
- [Лимиты MAX API](#лимиты-max-api)
|
|
30
|
+
- [Безопасность](#безопасность)
|
|
31
|
+
- [Конфиденциальность](#конфиденциальность)
|
|
32
|
+
- [Устранение неполадок](#устранение-неполадок)
|
|
33
|
+
- [Обновление с 0.7](#обновление-с-07)
|
|
34
|
+
- [Разработка](#разработка)
|
|
35
|
+
- [Благодарности, авторы и лицензия](#благодарности-авторы-и-лицензия)
|
|
36
|
+
|
|
37
|
+
## Поддерживаемые типы сообщений
|
|
38
|
+
|
|
39
|
+
| Тип | Входящие (MAX → агент) | Исходящие (агент → MAX) |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| Текст | Да; упоминания распознаются по разметке MAX, `@username` и шаблонам упоминаний ядра | Да; markdown переводится в диалект MAX (`++underline++`) и делится на части по 4000 символов; если MAX отклоняет разметку, текст один раз отправляется заново без неё |
|
|
42
|
+
| Изображение | Скачивается и передаётся агенту как медиа | Да; несколько изображений и видео уходят альбомами до 12 штук; публичная https-ссылка на изображение передаётся в MAX ссылкой |
|
|
43
|
+
| Видео | Скачивается (лимит размера — `mediaMaxMb`) | Да |
|
|
44
|
+
| Аудио и голосовые | Скачиваются; если MAX прислал расшифровку, используется она, иначе запись распознаёт ядро OpenClaw | Да; `asVoice` / `audioAsVoice` отправляет вложение MAX `audio` |
|
|
45
|
+
| Файл | Скачивается | Да: из разрешённого локального пути, по URL или содержимым в `buffer` |
|
|
46
|
+
| Стикер | Код стикера и изображение | Действие `sticker` по коду |
|
|
47
|
+
| Контакт | Имя и телефоны из VCard, профиль MAX | `sendAttachment` с `type="contact"` |
|
|
48
|
+
| Геолокация | Координаты и ссылка на карту | `sendAttachment` с `type="location"` |
|
|
49
|
+
| Карточка ссылки (share) | Заголовок, описание и ссылка | — |
|
|
50
|
+
| Пересланное сообщение | Текст и вложения с исходным автором; собственный комментарий отправителя идёт первым | — |
|
|
51
|
+
| Ответ на сообщение | Цитата исходного сообщения (до 1000 символов) и его автор | Да, ответом MAX (`replyTo`) |
|
|
52
|
+
| Inline-клавиатура | Нажатия кнопок: callback, подтверждения, меню команд | 7 типов кнопок (callback, link, message, clipboard, open_app, request_contact, request_geo_location); блоки `presentation` OpenClaw отрисовываются текстом и клавиатурами |
|
|
53
|
+
| Изменённое сообщение | Обрабатывается заново как новое | Действие `edit`; при стриминге черновика правится одно сообщение |
|
|
54
|
+
| Удалённое сообщение | Игнорируется (запись в лог на уровне debug) | Действие `delete` |
|
|
55
|
+
| Закрепление | — | `pin` / `unpin` в группах и каналах; `delivery.pin` |
|
|
56
|
+
| Запуск бота (deep link) | Доходит до агента как `/start` или `/start <payload>`; как и сообщения, пропускается, если старше `maxEventAgeMinutes` | — |
|
|
57
|
+
| Индикатор набора, отметка о прочтении | — | `typing_on` на весь ход; `mark_seen` для входящих сообщений (`markSeen`) |
|
|
58
|
+
|
|
59
|
+
Реакции и опросы не поддерживаются.
|
|
60
|
+
|
|
61
|
+
## Требования и совместимость
|
|
62
|
+
|
|
63
|
+
- OpenClaw ≥ 2026.9.6 (`peerDependencies.openclaw`, `openclaw.compat.minGatewayVersion`). Более ранние версии не работают — это проверено на 2026.9.3–2026.9.5 проверкой типов и набором тестов. В 2026.9.5 нет `createLivePreviewLifecycle` в `openclaw/plugin-sdk/channel-outbound`, поэтому модуль плагина не загружается (на этой функции построен статус хода); в 2026.9.3 и 2026.9.4 вдобавок нет проверки отправителя при ответе на вопросы агента кнопками (`authorize` в `resolveOption`), защищённого хука индикатора набора для heartbeat и формата пояснения к плану. Чтобы пользоваться плагином, обновите OpenClaw.
|
|
64
|
+
- Node.js ≥ 22.
|
|
65
|
+
- MAX Bot API в том виде, в каком его описывают схема 0.0.33 и [dev.max.ru/docs-api](https://dev.max.ru/docs-api).
|
|
66
|
+
- Для режима webhook: публичный HTTPS-адрес на порту 443 с доверенным сертификатом, который ведёт на HTTP-сервер gateway.
|
|
67
|
+
|
|
68
|
+
**Что проверено.** Версия 0.8.0 работает в эксплуатации с 27 сентября 2026 года с OpenClaw 2026.9.6 в режиме webhook. Вживую проверены: старт и подписка webhook, статус канала (`mode`, `tokenStatus`), текст в личном чате, длинные ответы по частям, команды ядра, отправка инструментом `message`, включая файл из рабочего каталога агента и отказ на файл неразрешённого типа, и восстановление долговечной очереди после настоящего рестарта gateway — сообщение, которому ядро отказало во время завершения, дождалось следующего старта и получило ответ. Версии 0.7.x до этого работали в эксплуатации с текстом, медиа, кнопками, обоими транспортами и распознаванием входящих голосовых через webhook. Остальное новое в 0.8.0 покрыто автоматическими тестами и прошло независимое ревью кода, но вживую ещё не проверялось — в том числе HTTP-прокси и `apiBaseUrl`, разрешение SecretRef при старте gateway, статус хода, меню команд, голосовые ответы, пересланные сообщения, группы и long polling.
|
|
82
69
|
|
|
83
70
|
## Установка
|
|
84
71
|
|
|
85
|
-
|
|
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 (рекомендуется):
|
|
72
|
+
Из ClawHub:
|
|
96
73
|
|
|
97
74
|
```bash
|
|
98
75
|
openclaw plugins install clawhub:@aspalagin/openclaw-max
|
|
99
76
|
```
|
|
100
77
|
|
|
101
|
-
|
|
78
|
+
Из npm:
|
|
102
79
|
|
|
103
80
|
```bash
|
|
104
81
|
openclaw plugins install npm:@aspalagin/openclaw-max
|
|
105
82
|
```
|
|
106
83
|
|
|
107
|
-
|
|
84
|
+
> Указывайте имя со scope — `@aspalagin/openclaw-max`. Пакет npm без scope `openclaw-max` — другой проект (см. [Благодарности](#благодарности-авторы-и-лицензия)).
|
|
85
|
+
|
|
86
|
+
После установки перезапустите gateway, чтобы он загрузил плагин. Id плагина в OpenClaw — `openclaw-max`, id канала — `max`.
|
|
87
|
+
|
|
88
|
+
Обновление: `openclaw plugins update openclaw-max` для установки из npm или `openclaw plugins install clawhub:@aspalagin/openclaw-max --force` для установки из ClawHub; затем перезапустите gateway. Перед обновлением прочитайте раздел [Обновление с 0.7](#обновление-с-07).
|
|
89
|
+
|
|
90
|
+
## Быстрый старт
|
|
91
|
+
|
|
92
|
+
1. **Создайте бота.** На [business.max.ru](https://business.max.ru/self) (нужна зарегистрированная организация или ИП) откройте **Чат-боты → Создать**, дождитесь модерации и скопируйте токен в разделе **Чат-боты → Интеграция**.
|
|
93
|
+
2. **Сохраните токен** в файл, который может читать пользователь gateway, например `/home/you/.openclaw/secrets/max-bot-token` (права `0600`). Путь используется как есть: указывайте абсолютный путь; `~` не раскрывается. Нужен обычный файл: символическая ссылка не читается — как если бы файла не было.
|
|
94
|
+
3. **Настройте** `~/.openclaw/openclaw.json`:
|
|
95
|
+
|
|
96
|
+
```jsonc
|
|
97
|
+
{
|
|
98
|
+
"channels": {
|
|
99
|
+
"max": {
|
|
100
|
+
"enabled": true,
|
|
101
|
+
"tokenFile": "/home/you/.openclaw/secrets/max-bot-token",
|
|
102
|
+
"dmPolicy": "pairing",
|
|
103
|
+
// Webhook (MAX рекомендует его для рабочей эксплуатации); для long polling уберите оба ключа
|
|
104
|
+
"webhookUrl": "https://bot.example.com/max/webhook",
|
|
105
|
+
"webhookSecretFile": "/home/you/.openclaw/secrets/max-webhook-secret"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
4. **Перезапустите gateway**: `openclaw gateway restart`.
|
|
112
|
+
5. **Проверьте аккаунт**: `openclaw channels status` показывает аккаунт, `tokenSource`, `tokenStatus` и `mode`; `openclaw channels status --probe` дополнительно обращается к MAX API.
|
|
113
|
+
6. **Напишите боту** в MAX. При `dmPolicy: "pairing"` вы получите код сопряжения (pairing); подтвердите его командой `openclaw pairing approve max <code>` (`openclaw pairing list max` показывает ожидающие запросы с id пользователя-отправителя). Чтобы обойтись без сопряжения, добавьте свой id пользователя в `allowFrom`.
|
|
114
|
+
|
|
115
|
+
Токен можно задать и через `botToken` (строкой или [SecretRef](#секреты-и-secretref)) или переменной окружения `MAX_BOT_TOKEN` (только для аккаунта верхнего уровня). Порядок: `botToken`, затем `tokenFile`, затем `MAX_BOT_TOKEN`.
|
|
116
|
+
|
|
117
|
+
## Транспорты: webhook и long polling
|
|
118
|
+
|
|
119
|
+
**Для рабочей эксплуатации используйте webhook.** MAX описывает long polling как режим для разработки и тестирования, ограниченный по скорости и сроку хранения событий, и просит в рабочей эксплуатации использовать webhook ([GET /updates](https://dev.max.ru/docs-api/methods/GET/updates)). Авторы других плагинов также сообщают, что через long polling голосовые сообщения могут приходить пустыми или не приходить вовсе (в частности, с Android); MAX этого не документирует, и мы пока этого не подтвердили — через webhook голосовые приходят и распознаются.
|
|
120
|
+
|
|
121
|
+
`transport` выбирает режим: `webhook`, если задан `webhookUrl`, иначе `polling`. `transport: "webhook"` без `webhookUrl` — ошибка конфигурации.
|
|
122
|
+
|
|
123
|
+
### Webhook
|
|
124
|
+
|
|
125
|
+
- **Требования MAX:** HTTPS на порту 443 с сертификатом доверенного удостоверяющего центра; ответ `200` в течение 30 секунд; неудавшиеся доставки MAX повторяет, а после 8 часов сбоев снимает подписку. Каждый запрос несёт заголовок `X-Max-Bot-Api-Secret`. Пока подписка webhook активна, long polling не работает.
|
|
126
|
+
- **Маршрут:** плагин принимает webhook на собственном HTTP-сервере gateway (отдельный порт не нужен). Путь: `webhookPath`, иначе путь из `webhookUrl`, иначе `/max/webhook`. Поставьте перед gateway обратный прокси или туннель с действительным сертификатом и пробрасывайте только этот путь.
|
|
127
|
+
- **Секрет:** `webhookSecret`, иначе `webhookSecretFile`, иначе секрет генерируется один раз и хранится в файле состояния аккаунта. Формат, который требует MAX: 5–256 символов `A–Z a–z 0–9 _ -`. Секрет сравнивается за постоянное время до чтения тела запроса; при несовпадении — ответ `401`.
|
|
128
|
+
- **Подписка:** при старте плагин удаляет подписки этого бота на другие URL и подписывает `webhookUrl`. Каждые 12 минут он проверяет, что подписка на месте, и создаёт её заново, если MAX её снял. При остановке подписка сохраняется, поэтому события, пришедшие во время рестарта, MAX доставит повторно.
|
|
129
|
+
- **Обработка:** HTTP-обработчик проверяет секрет, тело и дубликаты, записывает событие (см. [Надёжность](#надёжность)) и отвечает; агент работает в задаче аккаунта — по порядку внутри чата, до 4 чатов параллельно.
|
|
130
|
+
- **Включение:** сначала разверните плагин и перезапустите gateway, затем добавьте `webhookUrl` и перезапустите ещё раз; убедитесь, что в логе есть `MAX webhook subscribed` и что сообщение доходит до агента.
|
|
131
|
+
- **Откат:** задайте `transport: "polling"` (или уберите `webhookUrl`) и перезапустите gateway; при старте polling подписка удаляется. Если плагин не запускается, удалите подписку вручную: `curl -X DELETE "<api>/subscriptions?url=<webhookUrl>" -H "Authorization: <token>"`, где `<api>` — ваш `apiBaseUrl`, а если он не задан — `https://platform-api2.max.ru`.
|
|
108
132
|
|
|
109
|
-
|
|
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
|
-
```
|
|
133
|
+
### Long polling
|
|
114
134
|
|
|
115
|
-
|
|
135
|
+
Плагин опрашивает `GET /updates` и после каждой пачки сохраняет маркер в файле состояния аккаунта, поэтому после рестарта продолжает с того же места. После ошибки он ждёт 2 с, удваивая паузу до 60 с со случайным разбросом (jitter), соблюдает `Retry-After`, а после `401` повторяет попытку раз в 5 минут. При старте активная подписка webhook этого бота удаляется с предупреждением (пока она есть, MAX не обслуживает `GET /updates`).
|
|
116
136
|
|
|
117
|
-
|
|
137
|
+
## Справочник настроек
|
|
118
138
|
|
|
119
|
-
|
|
120
|
-
{
|
|
121
|
-
"channels": {
|
|
122
|
-
"max": {
|
|
123
|
-
// Токен бота из business.max.ru → Чат-боты → Интеграция
|
|
124
|
-
"botToken": "ваш_токен_бота",
|
|
139
|
+
Все опции находятся в `channels.max`; те же ключи (кроме `accounts` и `commands`) принимаются в `channels.max.accounts.<id>`. «Собственные» опции относятся к одному боту и не наследуются именованными аккаунтами; всё остальное наследуется с уровня канала (см. [Несколько аккаунтов](#несколько-аккаунтов-и-наследование)).
|
|
125
140
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
141
|
+
| Опция | Тип | По умолчанию | Описание |
|
|
142
|
+
|---|---|---|---|
|
|
143
|
+
| `enabled` | логический | `true` | `false` на уровне канала выключает все аккаунты |
|
|
144
|
+
| `botToken` | строка или SecretRef | — | Токен бота. Собственная |
|
|
145
|
+
| `tokenFile` | строка | — | Абсолютный путь к обычному файлу с токеном. Собственная |
|
|
146
|
+
| `name` | строка | — | Отображаемое имя аккаунта. Собственная |
|
|
147
|
+
| `transport` | `polling` \| `webhook` | `webhook`, если задан `webhookUrl`, иначе `polling` | Собственная |
|
|
148
|
+
| `webhookUrl` | строка | — | Публичный HTTPS-адрес, на который MAX отправляет события. Собственная |
|
|
149
|
+
| `webhookSecret` | строка или SecretRef | генерируется | Секрет webhook. Собственная |
|
|
150
|
+
| `webhookSecretFile` | строка | — | Файл с секретом webhook (обычный файл, не символическая ссылка). Собственная |
|
|
151
|
+
| `webhookPath` | строка | путь из `webhookUrl`, иначе `/max/webhook` | Путь маршрута в gateway. Собственная |
|
|
152
|
+
| `webhookQueue.mode` | `durable` \| `memory` | `durable` | Записывать принятые события webhook в журнал на диске или держать только в памяти |
|
|
153
|
+
| `webhookQueue.maxPending` | целое | `5000` | Сколько принятых, но ещё не обработанных событий может ждать |
|
|
154
|
+
| `webhookQueue.overflow` | `reject` \| `drop` | `reject` | При переполнении: ответить `503`, чтобы MAX повторил доставку, или подтвердить событие и отбросить |
|
|
155
|
+
| `maxEventAgeMinutes` | целое ≥ 0 | `60` | Пропускать сообщения, правки, нажатия кнопок и запуски бота старше этого срока (по времени события в MAX); `0` — без ограничения |
|
|
156
|
+
| `dmPolicy` | `pairing` \| `allowlist` \| `open` \| `disabled` | `pairing` | Кто может писать в личных диалогах |
|
|
157
|
+
| `allowFrom` | массив id пользователей | `[]` | Разрешённые отправители личных сообщений; `"*"` — кто угодно (обязательно при `open`) |
|
|
158
|
+
| `groupPolicy` | `allowlist` \| `open` \| `disabled` | `allowlist` (или `channels.defaults.groupPolicy` ядра) | В каких группах бот отвечает |
|
|
159
|
+
| `groups` | объект с ключами — id чатов или `"*"` | `{}` | Разрешённые группы и их настройки, см. ниже |
|
|
160
|
+
| `groupAllowFrom` | массив id пользователей | — | Отправители, допущенные в разрешённых группах (если у группы нет своего `allowFrom`) |
|
|
161
|
+
| `mentionPatterns` | `{ mode: "allow" \| "deny", allowIn, denyIn }` | allow | Где в группах MAX действуют шаблоны упоминаний ядра |
|
|
162
|
+
| `actionScope` | `admitted` \| `current` \| `off` | `admitted` | В каких чатах может действовать инструмент `message` |
|
|
163
|
+
| `notify` | логический | как в MAX (с уведомлением) | Push-уведомления при отправке; `false` — тихая отправка (в каналах невозможна) |
|
|
164
|
+
| `disableLinkPreview` | логический | как в MAX (превью включены), у сводок инструментов — выключены | Превью ссылок при отправке; заданное значение действует и на сводки инструментов |
|
|
165
|
+
| `markSeen` | логический | `true` | Отмечать входящие сообщения прочитанными |
|
|
166
|
+
| `mediaMaxMb` | число | `20` | Лимит размера скачиваемых и загружаемых медиа, МБ |
|
|
167
|
+
| `mediaMaxCount` | целое | `12` | Сколько медиавложений скачивается из одного входящего сообщения, включая пересланные |
|
|
168
|
+
| `streamMode` | `off` \| `partial` \| `block` | `off` | `partial` — одно сообщение-черновик правится, пока ответ стримится; `block` — блочные ответы ядра |
|
|
169
|
+
| `streaming` | настройка стриминга ядра | выключено | `streaming.mode` (`off`, `partial`, `block`, `progress`) сильнее `streamMode`; `streaming.progress.*` настраивает [статус хода](#статус-хода) |
|
|
170
|
+
| `textChunkLimit` | целое | `4000` | Символов в одном исходящем сообщении; значение аккаунта в приоритете; всё, что больше лимита MAX в 4000, урезается до него |
|
|
171
|
+
| `responsePrefix` | строка | — | Префикс ответов агента, его применяет ядро OpenClaw (`"auto"` — имя агента) |
|
|
172
|
+
| `historyLimit` | целое ≥ 0 | умолчание ядра | Читает ядро OpenClaw: сколько последних ходов групповой сессии встроенный рантайм агента держит в промпте (нативные CLI-рантаймы ведут историю сами) |
|
|
173
|
+
| `dmHistoryLimit`, `dms.<userId>.historyLimit` | целое ≥ 0 | без ограничения | Читает ядро OpenClaw: то же для личных диалогов с сессией на канал (`session.dmScope`); значение для пользователя в приоритете |
|
|
174
|
+
| `actions` | объект `{ <action>: boolean }` | всё включено | `false` выключает действие инструмента `message` (`send`, `edit`, `delete`, `sticker`, `sendAttachment`, `pin`, `unpin`): оно скрыто из инструмента и отклоняется |
|
|
175
|
+
| `apiBaseUrl` | строка | `https://platform-api2.max.ru` | Базовый адрес Bot API; только `https`, `http` — лишь для loopback |
|
|
176
|
+
| `httpProxy` | строка или SecretRef | — | HTTP(S)-прокси для всего трафика MAX; `""` в аккаунте отключает унаследованный прокси |
|
|
177
|
+
| `logMessagePreview` | логический | `false` | Добавлять в debug-логи превью текста длиной 50 символов |
|
|
178
|
+
| `commands` | массив `{ name, description }` | — | Только на уровне канала: команды бота, которые регистрируются в MAX (до 32; имя ≤ 64 символов без `/`, описание ≤ 128) |
|
|
179
|
+
| `accounts` | объект | — | Только на уровне канала: именованные аккаунты |
|
|
129
180
|
|
|
130
|
-
|
|
131
|
-
// "allowlist" — только из allowFrom (по умолчанию)
|
|
132
|
-
// "open" — любой может писать
|
|
133
|
-
// "pairing" — новые контакты проходят pairing-код
|
|
134
|
-
"dmPolicy": "allowlist"
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
```
|
|
181
|
+
Настройки группы (`groups.<chatId>` или `groups["*"]`):
|
|
139
182
|
|
|
140
|
-
|
|
183
|
+
| Ключ | По умолчанию | Описание |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| `requireMention` | `true` | Отвечать, только если бота упомянули, ответили на его сообщение или нажали его кнопку |
|
|
186
|
+
| `allowFrom` | — | Отправители, допущенные в этой группе (вместо `groupAllowFrom`) |
|
|
187
|
+
| `tools` | — | Политика инструментов ядра для этой группы |
|
|
188
|
+
| `disableAudioPreflight` | `false` | Не распознавать голосовые без подписи ради поиска упоминания |
|
|
189
|
+
| `enabled` | `true` | `false` — игнорировать группу при любой `groupPolicy` |
|
|
190
|
+
| `systemPrompt` | — | Дополнительный системный промпт для ходов в этой группе |
|
|
191
|
+
| `skills` | все | Навыки, которые может загружать ход в этой группе; `[]` — никакие |
|
|
192
|
+
|
|
193
|
+
Ключи `markdown` (отрисовка таблиц), `blockStreaming` (используйте `streamMode` или `streaming.mode: "block"`) и `blockStreamingCoalesce` (ядро читает `streaming.block.coalesce`) схема принимает для совместимости с общим форматом конфигурации каналов, но **в MAX они ни на что не влияют**: ни плагин, ни ядро OpenClaw не читают их для этого канала. Они оставлены в схеме, чтобы существующие конфиги по-прежнему проходили проверку.
|
|
194
|
+
|
|
195
|
+
## Доступ и политики
|
|
196
|
+
|
|
197
|
+
**Личные диалоги** (`dmPolicy`):
|
|
198
|
+
|
|
199
|
+
- `pairing` (по умолчанию) — незнакомый отправитель получает код сопряжения; владелец подтверждает его командой `openclaw pairing approve max <code>`. Отправителям из `allowFrom` сопряжение не нужно.
|
|
200
|
+
- `allowlist` — только id пользователей из `allowFrom`.
|
|
201
|
+
- `open` — кто угодно; требует `allowFrom: ["*"]`.
|
|
202
|
+
- `disabled` — личные сообщения не принимаются.
|
|
203
|
+
|
|
204
|
+
**Группы** (`groupPolicy`):
|
|
205
|
+
|
|
206
|
+
- `allowlist` (по умолчанию) — только чаты, перечисленные в `groups` (или любой чат, если в `groups` есть запись `"*"`).
|
|
207
|
+
- `open` — любая группа, в которой состоит бот.
|
|
208
|
+
- `disabled` — сообщения из групп не принимаются.
|
|
141
209
|
|
|
142
|
-
|
|
143
|
-
openclaw gateway restart
|
|
144
|
-
```
|
|
210
|
+
В допущенной группе непустой `groups.<id>.allowFrom` (а если его нет — `groupAllowFrom`) ограничивает, кто может обращаться к боту: сообщения, нажатия кнопок, команды и голосовые для проверки упоминания от остальных участников игнорируются, их вложения не скачиваются. `"*"` разрешает всем; в id принимается префикс `max:`. Список `allowFrom` для личных сообщений на группы **не** распространяется.
|
|
145
211
|
|
|
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`).
|
|
212
|
+
**Упоминания.** При `requireMention` (по умолчанию `true`) бот отвечает в группе, когда его упомянули (`@username` или упоминанием MAX), когда кто-то ответил на его сообщение или нажал его кнопку либо когда текст совпал с шаблонами упоминаний, настроенными в ядре OpenClaw (`messages.groupChat.mentionPatterns` или `groupChat.mentionPatterns` агента). Шаблоны, которые ядро выводит из имени агента, в MAX не используются — только явно заданные. `channels.max.mentionPatterns` ограничивает, где действуют шаблоны: `{ "mode": "deny" }` отключает их для MAX, `allowIn` / `denyIn` перечисляют id групп. Голосовое без подписи в такой группе проверяется по шаблонам — по расшифровке MAX или после однократного распознавания ядром (только в допущенных группах и от допущенных отправителей).
|
|
224
213
|
|
|
225
|
-
|
|
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
|
-
```
|
|
214
|
+
**Подтверждения и вопросы.** Кнопки подтверждения (approval) могут нажимать только отправители, явно перечисленные в `allowFrom`; для вопросов `ask_user` достаточно записи `"*"` — но в группе такой ответ засчитывается, только если группа допущена и нажавший есть в её списке отправителей (`groups.<id>.allowFrom` / `groupAllowFrom`), когда список задан.
|
|
230
215
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
216
|
+
**Как узнать id пользователя.** `openclaw pairing list max` показывает id отправителя, который ждёт сопряжения. Id групповых чатов — отрицательные числа.
|
|
217
|
+
|
|
218
|
+
Именованный аккаунт, унаследовавший политику `open`, при старте пишет в лог предупреждение с путём опции, которую стоит задать. Помните об этом наследовании, когда добавляете второго бота.
|
|
219
|
+
|
|
220
|
+
## Действия инструмента `message` и где они разрешены
|
|
221
|
+
|
|
222
|
+
Действия: `send`, `sendAttachment`, `sticker`, `edit`, `delete`, `pin`, `unpin`.
|
|
223
|
+
|
|
224
|
+
Цели: `user:<id>` для личного диалога, числовой id чата для группы или канала (например, `-70000000000001`). `@username` и ссылки max.ru MAX Bot API не поддерживает, и такие цели отклоняются с понятной ошибкой. Положительное число без префикса, на которое MAX ответил `dialog.not.found`, один раз пробуется заново как id пользователя.
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
message(action="send", target="user:12345678", message="Hello")
|
|
228
|
+
message(action="send", target="CHAT_ID", message="Choose:",
|
|
229
|
+
buttons=[[{"text":"Yes","type":"callback","payload":"yes"},
|
|
230
|
+
{"text":"More","type":"message"}]])
|
|
231
|
+
message(action="sendAttachment", target="CHAT_ID", path="report.pdf", caption="Report")
|
|
232
|
+
message(action="sendAttachment", target="CHAT_ID", buffer="data:text/plain;base64,SGVsbG8=", filename="hello.txt")
|
|
233
|
+
message(action="send", target="CHAT_ID", path="reply.ogg", asVoice=true)
|
|
234
|
+
message(action="send", target="CHAT_ID", message="Quiet", silent=true)
|
|
235
|
+
message(action="sendAttachment", target="CHAT_ID", type="location", latitude="55.75", longitude="37.62")
|
|
236
|
+
message(action="sendAttachment", target="CHAT_ID", type="contact", contactName="Name", vcfPhone="+70000000000")
|
|
237
|
+
message(action="sticker", target="CHAT_ID", stickerId="CODE")
|
|
238
|
+
message(action="pin", target="CHAT_ID", messageId="MID")
|
|
242
239
|
```
|
|
243
240
|
|
|
244
|
-
|
|
241
|
+
- `attachments=[…]` отправляет несколько файлов: изображения и видео уходят альбомами до 12 штук, аудио и файлы — по одному. В результате есть `messageIds`; элементы, которые не удалось отправить, перечислены в `mediaErrors`.
|
|
242
|
+
- `silent=true` отправляет без push-уведомления; в каналах MAX уведомление приходит всегда.
|
|
243
|
+
- `pin=true` или `delivery.pin` закрепляет отправленное сообщение. В личных диалогах MAX закреплять нельзя: плагин пропускает вызов и возвращает `pinned: false` с причиной.
|
|
245
244
|
|
|
246
|
-
|
|
247
|
-
с новым кодом, оставаясь в polling-режиме. Старые версии плагина при
|
|
248
|
-
`webhookUrl` подписывались, но роут не регистрировали — бот глох.
|
|
249
|
-
2. Затем добавить в конфиг `transport`/`webhookUrl`/`webhookSecretFile`
|
|
250
|
-
(поля `transport` и `webhookSecretFile` знает только новая схема) и
|
|
251
|
-
перезапустить канал/gateway. Подписку создаёт сам плагин при старте.
|
|
252
|
-
3. Проверить: `GET /subscriptions` показывает ровно один URL, в логе
|
|
253
|
-
`MAX webhook subscribed`, сообщение боту доходит до агента.
|
|
245
|
+
**Где разрешены действия (`actionScope`).** В ходе, который начал не владелец, действия работают только в текущем чате и в чатах, которые допускает политика входящих: в диалогах с отправителями из `allowFrom` или прошедшими сопряжение и в группах из `groups` — а если у группы есть список отправителей, то только когда автор запроса в нём есть. Без ограничений действуют только вызовы, которые OpenClaw помечает как вызовы владельца (`senderIsOwner: true`): ходы самого владельца, `openclaw message` из CLI, чат администратора в Control UI. Любой другой вызов — в том числе запуск вне разговора, у которого признака владельца нет или он ложный, как бывает у heartbeat, субагентов, запусков по расписанию, плагинами и хуками, — ограничен допущенными чатами, а в группе со списком отправителей получает отказ: автора запроса, которого можно было бы проверить, нет. Доставки ядра (ответы агента, напоминания, объявления автоматизаций) идут не через инструмент `message` и не затронуты. `send` с `presentation` (карточки, кнопки), который ядро доставляет без обработчика действий плагина, проверяется так же, и на него тоже действует `actions.send: false`. Текущий чат засчитывается только аккаунту, через который пришёл ход: действие в нём через другой аккаунт MAX (`accountId`) проверяется по политике того аккаунта. Отказ — это ошибка инструмента, которая возникает раньше, чем что-либо изменится в MAX; если плагин не может определить чат сообщения, он тоже отказывает. Вне чата текущего хода `edit` и `delete` работают только с сообщениями, которые отправил сам бот (автор берётся из того же запроса, что и чат); в текущем чате и для владельца — как раньше. `send`, `sendAttachment` и `sticker` с `replyTo` (а также `send` с `presentation`) могут отвечать только на сообщение того чата, куда уходят (проверка — один запрос, и только когда `replyTo` задан); ответ на сообщение другого чата или сообщение, чат которого определить нельзя, получает отказ.
|
|
254
246
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
247
|
+
- `admitted` (по умолчанию) — как описано выше.
|
|
248
|
+
- `current` — ходы не владельца действуют только в своём чате.
|
|
249
|
+
- `off` — плагин ничего не проверяет (поведение 0.7).
|
|
258
250
|
|
|
259
|
-
|
|
260
|
-
удаляется при старте polling. Если плагин не стартует, снять подписку руками:
|
|
261
|
-
`curl -X DELETE "https://platform-api2.max.ru/subscriptions?url=<webhookUrl>" -H "Authorization: <token>"`.
|
|
251
|
+
Так инъекция промпта в одном чате не сможет править, удалять или публиковать сообщения в другом.
|
|
262
252
|
|
|
263
|
-
##
|
|
253
|
+
## Медиа и локальные файлы
|
|
264
254
|
|
|
265
|
-
|
|
255
|
+
- **Входящие** медиа скачиваются после проверок доступа, в пределах `mediaMaxMb` (по умолчанию 20 МБ) и `mediaMaxCount` (по умолчанию 12 на сообщение); о медиа, которые не загрузились, агент получает пометку. Файлы хранит медиахранилище ядра OpenClaw.
|
|
256
|
+
- **Исходящие локальные файлы** читаются только загрузчиком OpenClaw SDK и только из каталогов, которые ядро разрешает агенту: его рабочего каталога (workspace), media-каталогов gateway и того, что допускает политика файловой системы ядра. Пути за их пределами — в том числе через `..` или символические ссылки — завершаются ошибкой `Local media path is not under an allowed directory: …`. Чтобы отправить файл из другого места, сначала скопируйте его в рабочий каталог агента. Если политика ядра даёт агенту чтение с хоста, каталог не ограничивается, зато ядро проверяет тип по содержимому: отправляются изображения, аудио, видео, PDF, документы Office, архивы и текстовые документы (`.txt`, `.md`, `.csv`, `.json`, `.yaml`), остальное отклоняется ошибкой `Host-local media sends only allow …`. Отключить эти проверки нельзя.
|
|
257
|
+
- **Содержимое в вызове:** `buffer` (base64 или data URL) с `filename` и `contentType`; лимит размера проверяется до декодирования. Если заданы и путь, и `buffer`, используется путь.
|
|
258
|
+
- **Удалённые URL** скачиваются в память через загрузчик ядра с защитой от SSRF. Ссылка на изображение передаётся в MAX как есть, только если её хост — публичный https; иначе изображение скачивается и загружается в MAX.
|
|
266
259
|
|
|
267
|
-
|
|
268
|
-
2. Нажать **Старт** или отправить любое сообщение
|
|
269
|
-
3. Бот ответит, если ваш `user_id` в `allowFrom`
|
|
260
|
+
## Голосовые сообщения
|
|
270
261
|
|
|
271
|
-
|
|
262
|
+
**Входящие голосовые** передаются ядру OpenClaw как аудиомедиа. Если MAX прислал расшифровку, агент получает её как `[Voice transcript: …]`, и ядро не распознаёт запись повторно; иначе ядро распознаёт аудио провайдером, настроенным в `tools.media.audio`. Голосовое, которое не удалось скачать и у которого нет расшифровки, доходит до агента как `[Voice message: audio unavailable, no transcript]`.
|
|
272
263
|
|
|
273
|
-
|
|
264
|
+
**Голосовые ответы.** Канал сообщает ядру, что принимает для TTS аудиофайлы (`mp3`, `m4a`, `wav`, `ogg`, `opus`). Когда ответ помечен как голосовой — TTS ядра (например, `/tts`), директива `[[audio_as_voice]]` или `asVoice=true` у `send` / `sendAttachment`, — аудио уходит вложением MAX `audio`: отдельного типа «голосовое сообщение» в Bot API нет. Текст ответа доставляется один раз, отдельным сообщением. Если MAX отклоняет аудио, те же байты отправляются файлом.
|
|
274
265
|
|
|
275
|
-
|
|
276
|
-
openclaw logs --follow
|
|
277
|
-
# В логах будет: sender.user_id: 12345678
|
|
278
|
-
```
|
|
266
|
+
## Команды и меню
|
|
279
267
|
|
|
280
|
-
|
|
268
|
+
- `channels.max.commands` при старте аккаунта регистрирует в MAX список команд бота (`PATCH /me/commands`): до 32 команд.
|
|
269
|
+
- Сообщение, которое начинается с `/`, обрабатывается как команда OpenClaw.
|
|
270
|
+
- Команды, для которых ядро описывает варианты, — `/think`, `/fast`, `/reasoning`, `/verbose`, `/usage`, `/elevated`, `/trace`, `/activation`, `/send`, `/tts`, `/session`, `/subagents`, `/acp`, `/tools` — без аргумента отвечают меню из кнопок, текущий вариант отмечен ✓. Нажатие применяет команду так же, как ввод `/think high`. Меню и нажатия доступны только отправителям, которым разрешено выполнять команды.
|
|
271
|
+
- Запуск бота по deep link (`max.ru/<bot>?start=<payload>`) доходит до агента как `/start <payload>`.
|
|
281
272
|
|
|
282
|
-
|
|
283
|
-
MAX_BOT_TOKEN=xxx node scripts/test-api.mjs
|
|
284
|
-
# В разделе updates будет виден ваш user_id
|
|
285
|
-
```
|
|
273
|
+
## Статус хода
|
|
286
274
|
|
|
287
|
-
|
|
275
|
+
При стриминге прогресса ядра одно служебное сообщение показывает, чем занят агент: строку статуса, план, запросы подтверждения, а с `toolProgress` — и строки инструментов. Оно правится не чаще раза в секунду, отправляется без уведомления и удаляется после доставки ответа. По умолчанию статус выключен.
|
|
276
|
+
|
|
277
|
+
```jsonc
|
|
278
|
+
"channels": { "max": { "streaming": { "mode": "progress", "progress": { "toolProgress": true } } } }
|
|
279
|
+
```
|
|
288
280
|
|
|
289
|
-
|
|
290
|
-
- **open**: любой пользователь MAX может писать боту
|
|
291
|
-
- **pairing**: новый пользователь получает код, который нужно подтвердить у владельца
|
|
281
|
+
Остальные ключи `streaming.progress` (`label`, `labels`, `maxLines`, `maxLineChars`, `commandText`) — настройки ядра. Если статус не удалось отправить, останавливается только статус; на ход это не влияет.
|
|
292
282
|
|
|
293
|
-
##
|
|
283
|
+
## Несколько аккаунтов и наследование
|
|
294
284
|
|
|
295
|
-
|
|
285
|
+
Один gateway может обслуживать несколько ботов MAX. `channels.max` верхнего уровня — аккаунт по умолчанию; именованные аккаунты находятся в `accounts`:
|
|
296
286
|
|
|
297
287
|
```jsonc
|
|
298
288
|
{
|
|
299
289
|
"channels": {
|
|
300
290
|
"max": {
|
|
301
|
-
|
|
302
|
-
"
|
|
291
|
+
"tokenFile": "/home/you/.openclaw/secrets/max-main-token",
|
|
292
|
+
"dmPolicy": "allowlist",
|
|
303
293
|
"allowFrom": ["12345678"],
|
|
304
|
-
|
|
305
|
-
// Дополнительные аккаунты
|
|
294
|
+
"maxEventAgeMinutes": 30,
|
|
306
295
|
"accounts": {
|
|
307
|
-
"
|
|
308
|
-
"
|
|
309
|
-
"
|
|
310
|
-
"
|
|
296
|
+
"support": {
|
|
297
|
+
"tokenFile": "/home/you/.openclaw/secrets/max-support-token",
|
|
298
|
+
"dmPolicy": "open",
|
|
299
|
+
"allowFrom": ["*"]
|
|
300
|
+
// наследует maxEventAgeMinutes, groupPolicy, groups, … из channels.max
|
|
311
301
|
}
|
|
312
302
|
}
|
|
313
303
|
}
|
|
@@ -315,110 +305,188 @@ MAX_BOT_TOKEN=xxx node scripts/test-api.mjs
|
|
|
315
305
|
}
|
|
316
306
|
```
|
|
317
307
|
|
|
318
|
-
|
|
308
|
+
Именованный аккаунт берёт с уровня канала каждую опцию, которую не задал сам, включая политики доступа и списки; собственное значение аккаунта сильнее, а списки и `groups` заменяются целиком. Собственные опции не наследуются: `botToken`, `tokenFile`, `name`, `transport`, `webhookUrl`, `webhookSecret`, `webhookSecretFile`, `webhookPath`. `channels.max.enabled: false` выключает все аккаунты, `accounts.<id>.enabled: false` — один. `MAX_BOT_TOKEN` действует только для аккаунта верхнего уровня.
|
|
319
309
|
|
|
320
|
-
|
|
321
|
-
openclaw --account zaya send "Привет из второго бота"
|
|
322
|
-
```
|
|
310
|
+
Аккаунт в CLI выбирается ключом `--account`: `openclaw message send --channel max --account support --target user:87654321 --message "Hi"`.
|
|
323
311
|
|
|
324
|
-
##
|
|
312
|
+
## Прокси, адрес API и сертификаты Russian Trusted CA
|
|
313
|
+
|
|
314
|
+
- **`httpProxy`** (например, `http://proxy.example.com:3128`, можно с учётными данными) направляет весь трафик MAX этого аккаунта через HTTP(S)-прокси: вызовы Bot API, загрузку файлов, скачивание входящих вложений и удалённых медиа. Loopback-адреса идут мимо прокси. Учётные данные прокси никогда не попадают в логи и ошибки; в логе старта адрес выглядит как `http://***@host:port`.
|
|
315
|
+
- **`apiBaseUrl`** направляет плагин на другой адрес Bot API, например на тестовый стенд; префикс пути сохраняется. Принимается только `https` (`http` — лишь для loopback), поэтому токен никогда не передаётся открытым текстом.
|
|
316
|
+
- Неверное значение любой из этих опций останавливает аккаунт при старте ошибкой с именем опции.
|
|
317
|
+
- **Russian Trusted CA.** С июля 2026 года Bot API на `platform-api2.max.ru` использует сертификат, выпущенный Russian Trusted Sub CA Минцифры, а этого удостоверяющего центра нет в стандартном хранилище доверенных сертификатов Node.js. Плагин поставляется с сертификатами Russian Trusted Root CA и Sub CA (`src/russian-trusted-ca.ts`) и добавляет их к системным только для собственных соединений с MAX — в том числе внутри туннеля прокси. Общее хранилище доверенных сертификатов процесса не меняется, `NODE_EXTRA_CA_CERTS` не нужен.
|
|
318
|
+
|
|
319
|
+
## Секреты и SecretRef
|
|
320
|
+
|
|
321
|
+
`botToken`, `webhookSecret` и `httpProxy` принимают обычную строку или SecretRef OpenClaw:
|
|
325
322
|
|
|
326
|
-
|
|
323
|
+
```jsonc
|
|
324
|
+
"botToken": { "source": "env", "provider": "default", "id": "MAX_BOT_TOKEN" }
|
|
325
|
+
```
|
|
327
326
|
|
|
328
|
-
|
|
329
|
-
|
|
327
|
+
Провайдер должен быть описан в `secrets.providers` ядра. Пути `channels.max.botToken`, `webhookSecret`, `httpProxy` (и их варианты в `accounts.<id>`) видны командам `openclaw secrets configure`, `apply` и `audit`. Gateway разрешает ссылки до старта аккаунта. Аккаунт, у которого ссылка не разрешилась, не стартует: ошибка называет опцию и ссылку, но не значение, и отката на `tokenFile` или `MAX_BOT_TOKEN` не происходит; остальные аккаунты продолжают работать. `openclaw secrets audit` сообщает о токене, секрете webhook или прокси, записанных в `openclaw.json` обычной строкой.
|
|
328
|
+
|
|
329
|
+
`tokenFile` и `webhookSecretFile` по-прежнему поддерживаются. `openclaw channels status` показывает `tokenSource` (`config`, `file`, `env`, `none`) и `tokenStatus` (`available`, `configured_unavailable`, `missing`), не раскрывая токен.
|
|
330
|
+
|
|
331
|
+
## Надёжность
|
|
332
|
+
|
|
333
|
+
- **Долговечная очередь webhook.** Каждое принятое событие webhook записывается в `<stateDir>/max/inbox-<account>/` до того, как MAX получит `200`. После рестарта или падения первыми обрабатываются необработанные события, в исходном порядке внутри каждого чата. Событие считается завершённым, как только ядро приняло ход агента (прерванный ход ядро возобновляет само). Если событие не удалось записать или очередь заполнена (`webhookQueue.maxPending`), MAX получает `503` и повторяет доставку позже (`overflow: "drop"` вместо этого подтверждает событие и отбрасывает его). Если каталог состояния недоступен для записи, очередь с предупреждением переходит в память. Собственная очередь входящих событий SDK в OpenClaw 2026.9.x доступна только встроенным и официальным плагинам, поэтому плагин ведёт свой журнал по тому же контракту.
|
|
334
|
+
- **Дедупликация.** Ключи обработанных событий (`update_type:timestamp:mid`; у событий без сообщения вместо `mid` — чат и пользователь) хранятся 24 часа, до 5000 штук, и переживают рестарт, поэтому повторные доставки MAX отбрасываются. Long polling использует те же ключи и, как очередь webhook, отмечает событие обработанным, как только ядро приняло ход: после рестарта или падения посреди пачки — даже посреди хода — обработанные события не повторяются, а прерванный ход ядро возобновляет само, заново он не запускается.
|
|
335
|
+
- **Завершение gateway.** Пока gateway штатно останавливается или перезапускается, ядро не принимает новые ходы агента, а webhook и long polling продолжают получать события. Такое событие не теряется: плагин держит его в очереди и повторяет через 2, 5, 10 и далее каждые 30 секунд, а следующие события того же чата ждут за ним. Если gateway остановился раньше, событие остаётся в журнале на диске и обрабатывается после старта (long polling к тому же не сохраняет маркер пачки). Отказ ядра после того, как оно приняло ход, и другие ошибки обработки не повторяются. В очереди в памяти (`webhookQueue.mode: "memory"` или каталог состояния недоступен для записи) такое событие, как и раньше, пропускается с ошибкой в журнале.
|
|
336
|
+
- **Возраст событий.** Сообщения, правки, нажатия кнопок и запуски бота старше `maxEventAgeMinutes` (по умолчанию 60) пропускаются с предупреждением — после долгого простоя бот не отвечает на сообщения многочасовой давности. События реестра чатов обрабатываются всегда.
|
|
337
|
+
- **Ошибки доставки** передаются ядру: не ушло ничего — ответ считается неотправленным; видна часть — это частичная доставка с id видимых сообщений. Если часть текста не отправилась, следующие части не отправляются.
|
|
338
|
+
- **Длинные ответы** делятся по 4000 символов (или по меньшему `textChunkLimit`); при `streamMode: "partial"` первая часть заменяет черновик, остальные идут новыми сообщениями, кнопки — на последнем.
|
|
339
|
+
- **Ответ из нескольких частей.** Если ядро отдаёт ответ несколькими частями, в черновик стриминга или в ответ на нажатие кнопки (`POST /answers`) идёт только первая; остальные части, а также блоки `streamMode: "block"`, приходят новыми сообщениями, и ни одна часть не затирает другую.
|
|
340
|
+
- **Черновик стриминга** (`streamMode: "partial"`). Обновления черновика отправляются по одному, второго черновика не бывает; финал ждёт отправки черновика, которая уже идёт. Выводы инструментов (режим verbose) приходят отдельными сообщениями, а черновик продолжает стримиться. Черновик, который не стал ответом (`NO_REPLY`, `/stop`, ответ через инструмент `message` или ответ только из медиа), в конце хода удаляется; после ошибки обработки остаётся.
|
|
341
|
+
- **Повторы:** запросы с ответом `429` и сетевыми ошибками повторяются; `attachment.not.ready` после загрузки повторяется; отправка текста ограничена лимитом MAX — 2 сообщения в секунду на чат.
|
|
342
|
+
|
|
343
|
+
## Лимиты MAX API
|
|
344
|
+
|
|
345
|
+
По документации MAX Bot API:
|
|
346
|
+
|
|
347
|
+
| Лимит | Значение |
|
|
348
|
+
|---|---|
|
|
349
|
+
| Текст сообщения | 4000 символов (более длинный текст плагин делит) |
|
|
350
|
+
| Отправка | 2 сообщения в секунду на диалог, группу или канал (плагин ставит отправки в очередь) |
|
|
351
|
+
| Правка | Только собственные сообщения бота. В диалогах: сообщения с inline-клавиатурой — любой давности, остальные — в течение 7 дней. В групповых чатах и каналах — любой давности. Не больше 2 правок в секунду на чат |
|
|
352
|
+
| Удаление | Боту нужны права администратора с разрешением на удаление. В диалогах — только собственные сообщения бота, в группах и каналах — любые. Не больше 2 удалений в секунду на чат |
|
|
353
|
+
| Альбом | До 12 изображений и видео в одном сообщении; файлы и аудио — отдельно |
|
|
354
|
+
| Кнопки | Текст ≤ 128 символов, payload ≤ 1024 байт, ≤ 30 рядов, ≤ 210 кнопок |
|
|
355
|
+
| Команды бота | До 32 |
|
|
356
|
+
| Webhook | HTTPS на порту 443, доверенный сертификат, `200` в течение 30 с; подписка снимается после 8 часов неудачных доставок |
|
|
357
|
+
| Long polling | Не для рабочей эксплуатации; недоступен, пока есть подписка webhook |
|
|
358
|
+
|
|
359
|
+
## Безопасность
|
|
360
|
+
|
|
361
|
+
- **Контроль доступа по умолчанию:** личные сообщения требуют сопряжения, группы — списка разрешённых, списки отправителей в группах соблюдаются.
|
|
362
|
+
- **Где разрешены действия:** инструмент `message` не может действовать в чатах, которые не допускает политика входящих (см. [выше](#действия-инструмента-message-и-где-они-разрешены)).
|
|
363
|
+
- **Локальные файлы:** только из каталогов, которые OpenClaw разрешает агенту; выйти за их пределы через `..` и символические ссылки нельзя. Агенту с чтением с хоста ядро разрешает любые пути, но только проверенные по содержимому типы файлов.
|
|
364
|
+
- **Удалённые медиа:** скачиваются через SSRF-защиту ядра; приватные, loopback- и link-local-адреса, а также адреса сервисов метаданных не передаются в MAX ссылками.
|
|
365
|
+
- **Webhook:** секрет проверяется за постоянное время до чтения тела запроса.
|
|
366
|
+
- **Секреты:** поддерживается SecretRef; токен, секрет webhook и учётные данные прокси не пишутся в логи.
|
|
367
|
+
- **TLS:** дополнительным российским удостоверяющим центрам плагин доверяет только в собственных соединениях.
|
|
368
|
+
- **Логи:** по умолчанию без текста сообщений.
|
|
369
|
+
|
|
370
|
+
**Прокси и внутренние адреса.** Если задан `httpProxy`, имена хостов скачиваемых медиа разрешает прокси, а не плагин: литеральные приватные адреса и заблокированные имена по-прежнему отклоняются, но имя, которое DNS прокси разрешает во внутренний адрес, будет скачано из сети прокси. Поэтому прокси, стоящий во внутренней сети, даёт путь к адресам этой сети. Используйте прокси, у которого нет доступа к внутренним ресурсам.
|
|
371
|
+
|
|
372
|
+
**Известные ограничения.**
|
|
373
|
+
|
|
374
|
+
- Если во время работы gateway кончилось место на диске или диск стал доступен только для чтения, webhook отвечает `503` на каждое событие и бот не отвечает, пока место не освободится.
|
|
375
|
+
- В допущенной группе голосовое без подписи, которое MAX не расшифровал, отправляется на распознавание речи, чтобы найти упоминание бота. Распознавание платное и не ограничено по частоте; оно включается только при заданных шаблонах упоминаний (`groups.<id>.disableAudioPreflight` отключает его для группы).
|
|
376
|
+
- Кнопка, нагрузка которой начинается с `/`, при нажатии выполняется как команда от имени нажавшего и с его правами — так же, как в каналах ядра.
|
|
377
|
+
- Событие, обработка которого роняет процесс gateway (а не вызывает ошибку, которую ловит плагин), обрабатывается снова после каждого старта, пока не станет старше `maxEventAgeMinutes`; при `0` — без ограничения.
|
|
378
|
+
- `pin` и `unpin` проверяют чат, но не автора сообщения: в допущенном чате, где бот — администратор, они закрепляют и открепляют любое сообщение (в отличие от `edit` и `delete` вне текущего чата).
|
|
379
|
+
|
|
380
|
+
Об уязвимостях сообщайте приватно, как описано в [SECURITY.md](https://github.com/aspalagin/openclaw-max/blob/main/SECURITY.md). Поддерживаемая версия: 0.8.x.
|
|
381
|
+
|
|
382
|
+
## Конфиденциальность
|
|
383
|
+
|
|
384
|
+
Что плагин хранит на диске (`<stateDir>` — каталог состояния OpenClaw, по умолчанию `~/.openclaw`):
|
|
385
|
+
|
|
386
|
+
| Путь | Содержимое | Срок хранения |
|
|
387
|
+
|---|---|---|
|
|
388
|
+
| `<stateDir>/max/state-<account>.json` | Маркер long polling; реестр чатов (id чата, тип, название, когда бота добавили, удалили или остановили); сгенерированный секрет webhook, если он есть | Пока файл не удалят |
|
|
389
|
+
| `<stateDir>/max/inbox-<account>/` (каталог `0700`, файлы `0600`) | События webhook, ожидающие обработки, включая текст сообщений и метаданные вложений | Удаляются после обработки |
|
|
390
|
+
| `<stateDir>/max/inbox-<account>/completed.json` | Ключи обработанных событий (`update_type:timestamp:mid`), без содержимого | 24 часа, до 5000 ключей |
|
|
391
|
+
|
|
392
|
+
`webhookQueue.mode: "memory"` держит ожидающие события webhook только в памяти (файл ключей остаётся). Скачанные входящие медиа и историю разговоров хранит ядро OpenClaw, а не плагин.
|
|
393
|
+
|
|
394
|
+
Логи: тип сообщения, длина текста, id чата, сообщения и пользователя, ошибки и тайминги. Текст сообщений, подписи и расшифровки в логи не пишутся, если не включён `logMessagePreview: true` (превью в 50 символов на уровне debug). Токены, секреты и учётные данные прокси не пишутся в логи никогда.
|
|
395
|
+
|
|
396
|
+
Сетевые обращения плагина: MAX Bot API, его хосты загрузки и CDN (через `httpProxy`, если он задан) и хосты удалённых медиа, которые отправляет агент. Если у голосового нет расшифровки MAX, ядро OpenClaw отправляет аудио провайдеру распознавания речи, настроенному в `tools.media.audio`.
|
|
397
|
+
|
|
398
|
+
## Устранение неполадок
|
|
399
|
+
|
|
400
|
+
| Симптом | Вероятная причина и что делать |
|
|
401
|
+
|---|---|
|
|
402
|
+
| Бот не отвечает в личных сообщениях | `dmPolicy` — `pairing`, а отправитель не подтверждён (`openclaw pairing list max`), или `allowlist`, а отправителя нет в `allowFrom` |
|
|
403
|
+
| Бот не отвечает в группе | Чата нет в `groups` (`groupPolicy: allowlist`); бота не упомянули (`requireMention`); отправителя нет в `groups.<id>.allowFrom` / `groupAllowFrom`; бот не администратор группы — тогда MAX не доставляет ему сообщения группы (замечено при long polling) |
|
|
404
|
+
| `MAX bot token not configured: …` в статусе | Не задан ни `botToken`, ни `tokenFile`, ни `MAX_BOT_TOKEN`; в сообщении указан путь опции. `tokenFile`, которого нет, который пуст или является символической ссылкой, не читается (`tokenFile is missing, empty or not a regular file`); аккаунт по умолчанию тогда берёт `MAX_BOT_TOKEN`, если он задан |
|
|
405
|
+
| Аккаунт не стартует, в ошибке упоминается SecretRef | Ссылка не разрешилась; проверьте `secrets.providers` и переменную, файл или команду, на которые она указывает |
|
|
406
|
+
| Webhook: события не приходят | Проверьте публичный URL, сертификат и то, что обратный прокси пробрасывает путь; после 8 часов сбоев MAX снимает подписку — плагин восстановит её в течение 12 минут; пока подписка есть, long polling не работает |
|
|
407
|
+
| MAX получает `503` от webhook | Журнал не удалось записать (диск заполнен, нет прав) или достигнут `webhookQueue.maxPending` |
|
|
408
|
+
| Старые сообщения после простоя остаются без ответа | Они старше `maxEventAgeMinutes` (по умолчанию 60); задайте `0`, чтобы отвечать на сообщения любой давности |
|
|
409
|
+
| `Local media path is not under an allowed directory` | Файл лежит вне каталогов, разрешённых агенту; скопируйте его в рабочий каталог |
|
|
410
|
+
| `Host-local media sends only allow …` | У агента чтение с хоста, а тип файла ядро не распознало или не разрешает; отправьте файл разрешённого типа или передайте содержимое через `buffer` |
|
|
411
|
+
| Инструмент `message` получает отказ в другом чате | `actionScope`: политика входящих не допускает этот чат; добавьте чат в политику или измените `actionScope` |
|
|
412
|
+
| `MAX API does not resolve @username …` | Используйте `user:<id>` или числовой id чата |
|
|
413
|
+
| `pinned: false` в личном диалоге | В диалогах MAX закреплять нельзя |
|
|
414
|
+
| В сообщении видны `**` или `_` | MAX отклонил разметку, и плагин отправил текст заново без неё |
|
|
415
|
+
| Аккаунт останавливается при старте с ошибкой прокси или адреса API | `httpProxy` или `apiBaseUrl` заданы неверно, или `apiBaseUrl` использует `http` для хоста, который не loopback |
|
|
416
|
+
| `/think` отвечает кнопками, а не текстом | Это меню команд: чтобы задать значение сразу, отправьте `/think high` |
|
|
417
|
+
| Предупреждение о том, что аккаунт наследует открытую политику | Именованный аккаунт без своих `dmPolicy` / `groupPolicy` наследует `open`; задайте политику в `accounts.<id>` |
|
|
418
|
+
|
|
419
|
+
## Обновление с 0.7
|
|
420
|
+
|
|
421
|
+
Конфиг 0.7 работает без изменений. Что меняется в поведении и как вернуть поведение 0.7:
|
|
422
|
+
|
|
423
|
+
| Изменение в 0.8 | Поведение 0.7 |
|
|
424
|
+
|---|---|
|
|
425
|
+
| В ходе не владельца инструмент `message` действует только в текущем чате и в чатах, которые допускает политика входящих | `actionScope: "off"` (строже — `"current"`) |
|
|
426
|
+
| Вызов без `senderIsOwner: true` (heartbeat, субагент, запуск по расписанию без авторитета сообщений) ограничен допущенными чатами; в группе со списком отправителей — отказ | `actionScope: "off"` |
|
|
427
|
+
| Вне чата текущего хода `edit` и `delete` не трогают чужие сообщения — только сообщения бота | `actionScope: "off"` |
|
|
428
|
+
| `replyTo` отправки должен указывать на сообщение того же чата, куда она уходит; иначе отказ | `actionScope: "off"` |
|
|
429
|
+
| Локальные файлы читает защищённый загрузчик ядра: из разрешённых агенту каталогов, а при чтении с хоста — только проверенных типов | Переключателя нет: копируйте файлы в рабочий каталог агента |
|
|
430
|
+
| Сообщения, правки, нажатия кнопок и запуски бота старше 60 минут, включая повторные доставки MAX после простоя, остаются без ответа | `maxEventAgeMinutes: 0` |
|
|
431
|
+
| Webhook отвечает `200` после записи события на диск; `503` — если запись не удалась или очередь заполнена | `webhookQueue.overflow: "drop"`, `webhookQueue.mode: "memory"` |
|
|
432
|
+
| Ожидающие события webhook вместе с текстом сообщений хранятся в `<stateDir>/max/inbox-<account>/` до обработки | `webhookQueue.mode: "memory"` (файл ключей остаётся) |
|
|
433
|
+
| Именованные аккаунты наследуют все опции уровня канала, включая `dmPolicy`, `allowFrom`, `groupPolicy`, `groups` | Задайте политики в каждом аккаунте |
|
|
434
|
+
| `channels.max.enabled: false` выключает и именованные аккаунты | Оставьте канал включённым; чтобы работали только именованные аккаунты, не задавайте токен на верхнем уровне (`botToken`, `tokenFile`, `MAX_BOT_TOKEN`) |
|
|
435
|
+
| Непустой `groupAllowFrom` или `groups.<id>.allowFrom` ограничивает, кто может обращаться к боту в допущенной группе | Удалите списки или добавьте `"*"` |
|
|
436
|
+
| Из одного входящего сообщения скачивается не больше 12 медиа | Увеличьте `mediaMaxCount` |
|
|
437
|
+
| Пересланные сообщения запускают ход агента (раньше отбрасывались) | — |
|
|
438
|
+
| Сводки инструментов (режим verbose) приходят без превью ссылок | `disableLinkPreview: false` |
|
|
439
|
+
| `/think`, `/fast`, `/reasoning` и другие команды с меню без аргумента отвечают кнопками | — (с аргументом работают как раньше) |
|
|
440
|
+
| Заданные в ядре `messages.groupChat.mentionPatterns` теперь действуют в группах MAX | `mentionPatterns: { "mode": "deny" }` |
|
|
441
|
+
| Флаг `silent` ядра соблюдается; индикатор набора подчиняется `typingMode` ядра и держится весь ход | Настройки ядра |
|
|
442
|
+
| Если MAX отклонил разметку, ответ приходит обычным текстом (с видимыми `**`, `_`) | — |
|
|
443
|
+
| В debug-логах нет текста сообщений | `logMessagePreview: true` |
|
|
444
|
+
| `openclaw secrets audit` отмечает `botToken`, `webhookSecret` и `httpProxy`, записанные строкой | Перенесите их в SecretRef, `tokenFile` или `webhookSecretFile` |
|
|
445
|
+
| `streaming.mode` сильнее `streamMode` | Оставьте только один из ключей |
|
|
446
|
+
| Polling после ошибки ждёт 2–60 с (было 3 с), после `401` — 5 минут | — |
|
|
447
|
+
| Опубликованный пакет больше не содержит `scripts/` | Скрипты остаются в репозитории |
|
|
448
|
+
| Действуют `groups.<id>.enabled`, `groups.<id>.systemPrompt`, `groups.<id>.skills`, `actions` и `textChunkLimit` (в 0.7 схема их принимала, но они не читались) | Уберите эти ключи из конфига |
|
|
449
|
+
|
|
450
|
+
Полный список — в [журнале изменений](https://github.com/aspalagin/openclaw-max/blob/main/CHANGELOG.md).
|
|
330
451
|
|
|
331
|
-
|
|
452
|
+
## Разработка
|
|
332
453
|
|
|
333
454
|
```bash
|
|
455
|
+
git clone https://github.com/aspalagin/openclaw-max.git
|
|
334
456
|
cd openclaw-max
|
|
335
457
|
npm ci
|
|
336
|
-
npm run
|
|
458
|
+
npm run typecheck # запускать до build: build собирает и при ошибках типов
|
|
459
|
+
npm run build # dist/index.js, dist/setup-entry.js, dist/secret-contract-api.js, dist/src/*.js
|
|
337
460
|
```
|
|
338
461
|
|
|
339
|
-
|
|
340
|
-
запускайте `npm run typecheck`.
|
|
341
|
-
|
|
342
|
-
### Проверки (как в CI)
|
|
462
|
+
Проверки, как в CI:
|
|
343
463
|
|
|
344
464
|
```bash
|
|
345
465
|
npm run format:check # prettier
|
|
346
|
-
npm run lint # eslint
|
|
347
|
-
npm run check:cycles #
|
|
466
|
+
npm run lint # eslint
|
|
467
|
+
npm run check:cycles # циклы импортов между модулями src/
|
|
348
468
|
npm run typecheck
|
|
349
|
-
npm test # vitest, включая сверку со
|
|
469
|
+
npm test # vitest, включая сверку со снимком схемы MAX
|
|
470
|
+
npm audit --omit=dev --omit=peer # зависимости времени выполнения, которые входят в пакет
|
|
350
471
|
```
|
|
351
472
|
|
|
352
|
-
|
|
473
|
+
CI также запускает `typecheck` и тесты на `openclaw@latest` (с zod из lockfile и с минимальной поддерживаемой версией zod) и на `openclaw@beta`; сбой на `beta` попадает в отчёт, но сборку не проваливает.
|
|
353
474
|
|
|
354
|
-
`src/schema-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
Обновить снимок:
|
|
475
|
+
- `src/__fixtures__/max-schema-0.0.33.yaml` — снимок схемы MAX Bot API; `npm run schema:update [ref]` его обновляет.
|
|
476
|
+
- `npm run test:api` обращается к живому API с `MAX_BOT_TOKEN` (`GET /me`, `GET /updates`); вспомогательные скрипты из `scripts/` есть только в репозитории, в пакет они не входят.
|
|
477
|
+
- В тестах — только вымышленные токены и id.
|
|
358
478
|
|
|
359
|
-
|
|
360
|
-
npm run schema:update # последний коммит репозитория схемы
|
|
361
|
-
npm run schema:update -- <commit> # конкретный коммит, тег или ветка
|
|
362
|
-
npm test
|
|
363
|
-
```
|
|
479
|
+
Вопросы и предложения: [GitHub Issues](https://github.com/aspalagin/openclaw-max/issues). Правила участия: [CONTRIBUTING.md](https://github.com/aspalagin/openclaw-max/blob/main/CONTRIBUTING.md).
|
|
364
480
|
|
|
365
|
-
|
|
366
|
-
файла коммит и дату. Упавший тест сверки означает, что плагин отправляет или ждёт
|
|
367
|
-
то, чего в новой схеме нет: поправить код или тип, затем закоммитить снимок.
|
|
481
|
+
## Благодарности, авторы и лицензия
|
|
368
482
|
|
|
369
|
-
|
|
483
|
+
**Благодарности.** С 28 мая 2026 года (синхронизация с версией 0.5.0, коммит `d8d24bc`) плагин содержит код, производный от npm-пакета `openclaw-max` 0.5.0 Евгения Быстрова, опубликованного под лицензией MIT (Copyright (c) 2026 Evgeniy Bystrov): [github.com/evgeniyvbystrov/openclaw-max](https://github.com/evgeniyvbystrov/openclaw-max). Уведомление об авторских правах Евгения Быстрова сохранено в [LICENSE](LICENSE). Спасибо.
|
|
370
484
|
|
|
371
|
-
|
|
372
|
-
# Проверить бота: GET /me + GET /updates
|
|
373
|
-
MAX_BOT_TOKEN=xxx node scripts/test-api.mjs
|
|
485
|
+
**Авторы.**
|
|
374
486
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
```
|
|
487
|
+
- [Petlevoy](https://github.com/petlevoy) — основатель проекта.
|
|
488
|
+
- [Арсений Палагин](https://github.com/aspalagin) — сопровождающий.
|
|
378
489
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
```bash
|
|
382
|
-
# Полный цикл: отправить → подождать → отредактировать → подождать → удалить
|
|
383
|
-
MAX_BOT_TOKEN=xxx node scripts/test-send.mjs <chat_id> "Текст сообщения"
|
|
384
|
-
```
|
|
490
|
+
Разработка ведётся с помощью ИИ-агентов.
|
|
385
491
|
|
|
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
|
|
492
|
+
**Лицензия:** MIT, см. [LICENSE](LICENSE).
|