@retensy/mcp 0.21.1 → 0.23.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/.mcp.json +12 -12
- package/LICENSE +21 -21
- package/README.md +41 -11
- package/package.json +54 -54
- package/skills/build-bot-funnel/SKILL.md +4 -3
- package/skills/build-bot-funnel/reference/schema.md +6 -4
- package/skills/build-bot-funnel/reference/validation.md +8 -0
- package/skills/build-bot-funnel/validate.mjs +2 -2
- package/src/index.mjs +2165 -1682
package/.mcp.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
{
|
|
2
|
-
"mcpServers": {
|
|
3
|
-
"retensy-mcp": {
|
|
4
|
-
"command": "node",
|
|
5
|
-
"args": ["${CLAUDE_PLUGIN_ROOT}/src/index.mjs"],
|
|
6
|
-
"env": {
|
|
7
|
-
"RETENSY_BASE_URL": "https://bots.retensy.com",
|
|
8
|
-
"RETENSY_MCP_TOKEN": "${RETENSY_MCP_TOKEN:-}"
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"retensy-mcp": {
|
|
4
|
+
"command": "node",
|
|
5
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/src/index.mjs"],
|
|
6
|
+
"env": {
|
|
7
|
+
"RETENSY_BASE_URL": "https://bots.retensy.com",
|
|
8
|
+
"RETENSY_MCP_TOKEN": "${RETENSY_MCP_TOKEN:-}"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 retensy
|
|
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.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 retensy
|
|
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
CHANGED
|
@@ -9,8 +9,12 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
9
9
|
|
|
10
10
|
- 🤖 **Сценарии ботов**: `list_bots`, `list_graphs`, `list_channels`, `get_graph`, `create_graph`, `update_graph`, `edit_graph_live`, `patch_graph`, `dry_run`, `publish_graph`, `import_funnel`, `list_templates`, `create_graph_from_template`, `clone_graph`, `copy_graph`, `rename_graph`, `set_active_graph`, `delete_graph`, `upload_file`, `list_files`, `delete_file`, `graph_analytics`, `list_bot_users`, `list_links` (+ `setup`/`set_token`).
|
|
11
11
|
- 🔌 **Боты и сервисы**: `create_bot` (Telegram/MAX по токену), `bot_stop`/`bot_resume`, `connect_integration`/`disconnect_integration` (amoCRM, Битрикс24, GetCourse, Я.Метрика, ЮKassa; Google Таблицы — ссылкой на вход Google; креды хранятся зашифрованными и не возвращаются), `integration_catalog`/`integration_status`/`integration_test` (каталог Integration Core и живая проверка подключения), `integration_ingress_url` (URL ингресса «Внешних событий» — Tilda, JivoSite, WooCommerce, amoCRM, Битрикс24, GetCourse, CloudPayments, Robokassa, Т-Банк, Prodamus — для `TRIGGER_WEBHOOK`), `channel_post` (пост в канал Telegram/MAX).
|
|
12
|
-
- 📣 **Рассылки**: `broadcast_preview` (размер аудитории), `broadcast_send` (сейчас или по расписанию, сразу по нескольким ботам; сообщения или запуск
|
|
13
|
-
-
|
|
12
|
+
- 📣 **Рассылки**: `broadcast_preview` (размер аудитории), `broadcast_send` (сейчас или по расписанию, сразу по нескольким ботам; сообщения или запуск сценария; полный фильтр аудитории `audienceFilter`, дедуп `dedup` и распределённый запуск `stagger` по ботам), `broadcast_list`/`broadcast_get`/`broadcast_cancel`, повторяющиеся (`broadcast_recurring`), черновики (`broadcast_drafts`, `broadcast_duplicate`), мульти-бот группы (`broadcast_group` — preview/list/get/pause/resume/cancel) + скилл `send-broadcast`.
|
|
13
|
+
- 👥 **Аудитория подписчиков**: `audience_search`/`audience_fields` — кросс-бот поиск и фильтр (теги, даты, статус, условия по переменным), `audience_tag_edit`/`audience_variable_edit` — массовое редактирование тегов/переменных, `audience_export` — потоковая выгрузка выборки в CSV/XLSX, `dialog_search` — поиск по «Диалогам» (включая текст сообщений).
|
|
14
|
+
- 📤 **Миграция подписчиков** из других конструкторов: `migration_presets`/`migration_preview`/`migration_commit` — generic CSV column-mapper для BotHelp/SaleBot/Senler/Leadteh/Smartsender/ManyChat (переносятся только подписчики — ни один из них не публикует документированный формат экспорта воронки).
|
|
15
|
+
- 📅 **Запись на слоты**: `booking_calendar_*` (буферы до/после брони, горизонт записи, типы встреч своей длительности, форма клиента, публичная страница `/book/<slug>`), `booking_confirm`/`booking_reject` — ручное подтверждение брони (`confirmationMode:"MANUAL"`).
|
|
16
|
+
- 📰 **Публикации и VK**: `publications_usage` — месячная квота (общая для Telegram/MAX/Instagram/VK), `vk_status`/`vk_connect`/`vk_disconnect`/`vk_post` — подключение VK-сообщества токеном и публикация на стену.
|
|
17
|
+
- 🔗 **Ссылка вместо отказа**: что нельзя сделать через API (вход через Google/Instagram, оплата тарифа, вход в аккаунт) — инструмент возвращает прямую ссылку и одну строку, что сделать.
|
|
14
18
|
- 📝 **Статьи блога** (тот же токен `zmcp_…`): `article_publish`, `article_update`, `article_list`, `article_get` — публикация статей в Markdown (как README на GitHub) в раздел **/articles**.
|
|
15
19
|
- 🧠 **ИИ-агенты и база знаний**: `agent_list`/`agent_get`/`agent_create`/`agent_update`/`agent_publish`/`agent_health`/`agent_test_chat`/`agent_unanswered`, `kb_docs`/`kb_add_qa`/`kb_add_text`/`kb_add_site`/`kb_reindex` — настройка ИИ-агента и его базы знаний (раздел «ИИ-агенты»).
|
|
16
20
|
- 📎 **Медиа**: `upload_file` грузит фото/видео/документы в библиотеку **/bots/files** (до 50 МБ) и возвращает публичный URL — его вставляешь в медиа-карточку сценария.
|
|
@@ -24,7 +28,7 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
24
28
|
|---|---|---|---|
|
|
25
29
|
| **Telegram** | Токен бота (BotFather) → `create_bot` | `/start`, команды, callback, текст, рассылки | Полный функционал |
|
|
26
30
|
| **MAX** | Токен бота (MasterBot в MAX) → `create_bot` | Команды, callback, текст | Без SUBSCRIBED/reply-клавиатур (мягкие предупреждения) |
|
|
27
|
-
| **Instagram**
|
|
31
|
+
| **Instagram** | Вход через Instagram (OAuth) в кабинете `/bots/instagram` (без токена) — нужен Instagram-аккаунт Business или Creator | Комментарий/Direct/Ответ на историю/Упоминание | Ограниченный набор узлов; DELAY ≤ 24ч; ASK_QUESTION только TEXT/EMAIL/PHONE/NUMBER/CONTACT (CONTACT = ручной ввод номера); без рассылок |
|
|
28
32
|
|
|
29
33
|
---
|
|
30
34
|
|
|
@@ -97,13 +101,13 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
97
101
|
| `setup` | статус авторизации + пошаговая инструкция подключения |
|
|
98
102
|
| `set_token` | сохранить присланный токен `zmcp_…` (без env/рестарта) |
|
|
99
103
|
| `list_bots` | список ботов |
|
|
100
|
-
| `create_bot(platform, token?, name?)` | подключить бота Telegram/MAX по токену; `WEB` — чат-виджет для сайта без токена → botId, key, snippet (Instagram → ссылка на кабинет) |
|
|
104
|
+
| `create_bot(platform, token?, name?)` | подключить бота Telegram/MAX по токену; `WEB` — чат-виджет для сайта без токена → botId, key, snippet (Instagram → ссылка на кабинет `/bots/instagram`, вход через Instagram) |
|
|
101
105
|
| `web_widget_snippet(botId)` | код вставки чат-виджета на сайт |
|
|
102
106
|
| `bot_stop(botId)` / `bot_resume(botId)` | остановить / запустить бота |
|
|
103
107
|
| `list_graphs(botId)` | графы (сценарии) бота |
|
|
104
108
|
| `list_channels(botId)` | каналы/группы, подключённые к боту (chatId для условия SUBSCRIBED) |
|
|
105
109
|
| `list_integrations()` | подключённые сервисы (amoCRM, Битрикс24, GetCourse, Я.Метрика, ЮKassa): `id` = `connectionId` для действий сценария |
|
|
106
|
-
| `connect_integration(provider, creds?, title?, connectionId?)` | подключить/обновить сервис; без кредов — какие поля нужны; Google Таблицы → ссылка входа Google, Instagram → ссылка на кабинет |
|
|
110
|
+
| `connect_integration(provider, creds?, title?, connectionId?)` | подключить/обновить сервис; без кредов — какие поля нужны; Google Таблицы → ссылка входа Google, Instagram → ссылка на кабинет `/bots/instagram` (вход через Instagram) |
|
|
107
111
|
| `disconnect_integration(connectionId)` | удалить подключение |
|
|
108
112
|
| `integration_catalog()` | каталог Integration Core: поля подключения (`configSchema`) и действия (`actions[].kind`) сервисов |
|
|
109
113
|
| `integration_status(connectionId)` | статус подключения: `OK` / `NEEDS_REAUTH` / `ERROR` / `UNKNOWN`, последняя проверка и ошибка |
|
|
@@ -134,7 +138,7 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
134
138
|
| `site_create(title, slug?, template?)` | новый сайт из блоков → `id`; `template` (`starter`/`blank`/`mini-landing`) — сразу черновик из шаблона; число сайтов ограничено тарифом — сверх лимита HTTP 402 с причиной и ссылкой на смену тарифа |
|
|
135
139
|
| `site_get(siteId, saveToFile?)` | модель сайта (`revision`, `draft`, `versions[]` публикаций) |
|
|
136
140
|
| `site_schema()` | JSON Schema модели и операций — читать перед правкой |
|
|
137
|
-
| `site_edit(siteId, ops[]?, revision?, init?)` | правка операциями (`init`: `starter`/`blank`/`mini-landing`; только `init` — черновик из шаблона), всё или ничего: страницы и папки, блоки, Zero-элементы, код блока (`get/set/add_block_code`), дизайны и их кадры, шаблоны (`add_template`), тема,
|
|
141
|
+
| `site_edit(siteId, ops[]?, revision?, init?)` | правка операциями (`init`: `starter`/`blank`/`mini-landing`; только `init` — черновик из шаблона), всё или ничего: страницы и папки, блоки, Zero-элементы, код блока (`get/set/add_block_code`), дизайны и их кадры, шаблоны (`add_template`), тема, попапы; пиксели аналитики (GA4/GTM/Meta/VK/Яндекс.Метрика) — тоже сюда, `set_settings {analytics: {...}}` (отдельного инструмента нет — те же `settings.analytics` модели сайта, валидируются форматом id на сервере) |
|
|
138
142
|
| `site_templates(category?, full?)` | библиотека шаблонов блоков для `add_template` |
|
|
139
143
|
| `site_publish(siteId)` | опубликовать черновик → `url` |
|
|
140
144
|
| `site_rollback(siteId, revision)` | вернуть прошлую публикацию |
|
|
@@ -145,11 +149,12 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
145
149
|
| `broadcast_list(botId?, group?, page?, size?)` | рассылки + счётчики разделов (черновики/запланированные/отправленные/повторы) |
|
|
146
150
|
| `broadcast_get(broadcastId)` | рассылка целиком: статус, счётчики, сообщения, причины ошибок |
|
|
147
151
|
| `broadcast_preview(botIds, tagsAll?, tagsNone?)` | сколько подписчиков получат рассылку |
|
|
148
|
-
| `broadcast_send(name, botIds, messages \| graphId, tagsAll
|
|
149
|
-
| `broadcast_cancel(broadcastId)` | отменить запланированную/идущую |
|
|
150
|
-
| `broadcast_recurring(action, …)` | повторяющиеся рассылки: list / create (DAILY·MONTHLY·YEARLY) / stop |
|
|
152
|
+
| `broadcast_send(name, botIds, messages \| graphId, tagsAll?/tagsNone? или audienceFilter?, scheduledAt?, draftId?, dedup?, stagger?)` | отправить сейчас / запланировать; по нескольким ботам; или запуск сценария; `dedup`/`stagger` → мульти-бот группа (`broadcast_group`) вместо прямых рассылок |
|
|
153
|
+
| `broadcast_cancel(broadcastId)` | отменить запланированную/идущую (для группы — `broadcast_group action:"cancel"`) |
|
|
154
|
+
| `broadcast_recurring(action, …)` | повторяющиеся рассылки: list / create (DAILY·MONTHLY·YEARLY, те же `audienceFilter`/`dedup`/`stagger`) / stop |
|
|
151
155
|
| `broadcast_drafts(action, …)` | черновики: list / get / create / update / delete |
|
|
152
156
|
| `broadcast_duplicate(broadcastId` или `draftId)` | копия рассылки или черновика — новый черновик «… (копия)» |
|
|
157
|
+
| `broadcast_group(action, …)` | мульти-бот группа с дедупом/распределённым запуском: `preview` (read-only оценка) / `list` / `get` / `pause` / `resume` / `cancel` (каскадом на уже запущенные боты) |
|
|
153
158
|
| `article_list()` | свои статьи блога (id, slug, title, просмотры) |
|
|
154
159
|
| `article_get(slug)` | статья по slug (Markdown content, excerpt, обложка) |
|
|
155
160
|
| `article_publish(content, title?, cover?, excerpt?)` | новая статья (Markdown; title из `# ...`, если не задан; обложка из `cover`-URL или 1-й картинки → OG; `excerpt` явно или авто) → id, slug, URL |
|
|
@@ -194,7 +199,32 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
194
199
|
| `booking_calendar_create(name, zone?, slotMinutes?, hours?, exceptions?, botId?)` | новый календарь: окна `{day:1–7, from:"HH:mm", to:"HH:mm"}`, исключения `{date, from?, to?}` |
|
|
195
200
|
| `booking_calendar_update(calendarId, …поля)` / `booking_calendar_delete(calendarId, confirm:true)` | правка (поля накладываются на текущие) / удаление |
|
|
196
201
|
| `booking_slots(calendarId, from?, to?, limit?)` | свободные слоты `[{at, label}]`, даты ГГГГ-ММ-ДД в зоне календаря |
|
|
197
|
-
| `booking_list(calendarId, from?)` / `booking_create(calendarId, slotAt, name?, phone?)` / `booking_cancel(calendarId, bookingId)` | брони: список / ручная запись (занятый слот — ошибка «занят») / отмена |
|
|
202
|
+
| `booking_list(calendarId, from?, pendingOnly?)` / `booking_create(calendarId, slotAt, name?, phone?, email?, comment?, customAnswers?, meetingTypeId?)` / `booking_cancel(calendarId, bookingId)` | брони: список (или только ждущие подтверждения) / ручная запись (занятый слот — ошибка «занят») / отмена |
|
|
203
|
+
| `booking_confirm(calendarId, bookingId)` / `booking_reject(calendarId, bookingId)` | подтвердить/отклонить бронь при `confirmationMode:"MANUAL"` (прочие поля календаря: `breaks`, `meetingTypes`, буферы, `horizonDays`, `intakeFields`, `notifyOwner`, `slug`+`enabled` — публичная ссылка `/book/<slug>` — в `booking_calendar_create`/`_update`) |
|
|
204
|
+
| `audience_fields(botId, search?, limit?)` | ключи переменных (с выведенным типом) и теги бота — для построителя фильтра |
|
|
205
|
+
| `audience_search(botId?, botIds?, filter?, q?, sort?, dir?, page?, size?)` | фильтр+сортировка подписчиков: один бот или кросс-бот (без `botId`); `filter` — полный `AudienceFilter` (теги any/all/none, диапазоны дат, `blocked`, `variableConditions`) |
|
|
206
|
+
| `audience_tag_edit(botId, chatIds, add?, remove?)` / `audience_variable_edit(botId, chatIds, set?, unset?)` | массово добавить/снять теги / установить/снять переменные у выбранных подписчиков |
|
|
207
|
+
| `audience_export(botId?, botIds?, filter?, q?, format, savePath?)` | потоковая выгрузка ТЕКУЩЕЙ отфильтрованной выборки, `csv`\|`xlsx` (xlsx требует `savePath` — бинарный формат), потолок 100000 строк |
|
|
208
|
+
| `dialog_search(q, limit?)` | кросс-бот поиск по «Диалогам» — имя/username/id/текст сообщения |
|
|
209
|
+
| `publications_usage(botId)` | месячная квота публикаций владельца (общая для Telegram/MAX/Instagram/VK) |
|
|
210
|
+
| `vk_status()` / `vk_connect(communityId, accessToken)` / `vk_disconnect(accountId)` | подключённые VK-сообщества / подключить токеном сообщества (проверяется живым вызовом VK) / отключить |
|
|
211
|
+
| `vk_post(accountId, text?, mediaUrl?)` | пост на стену VK (только текст/фото — без видео), списывает ту же квоту публикаций |
|
|
212
|
+
| `migration_presets()` | каталог источников миграции подписчиков (BotHelp/SaleBot/Senler/Leadteh/Smartsender/ManyChat/универсальный CSV) |
|
|
213
|
+
| `migration_preview(botId, path, platform, mapping?)` | пробный прогон CSV (ничего не пишет): без `mapping` — определение заголовков/подсказка сопоставления, с `mapping` — полный dry-run (новые/существующие, превышение лимита) |
|
|
214
|
+
| `migration_commit(botId, path, platform, mapping)` | реальный импорт подписчиков тем же `mapping`, что подтвердил `migration_preview` |
|
|
215
|
+
| `chain_list()` / `chain_get(chainId)` | цепочки публикаций («Публикации» → «Цепочки», автопостинг source → destinations[]) / одна цепочка целиком |
|
|
216
|
+
| `chain_create(chain)` / `chain_update(chainId, chain)` | создать / изменить цепочку целиком (`chain`: `name`, `source` TELEGRAM\|VK\|RSS, `destinations[]` TELEGRAM\|MAX\|VK\|INSTAGRAM, `steps[]` FILTER\|TEXT_TRANSFORM\|AI_ADAPT\|UTM\|DELAY, `approvalMode?`, `utmDefaults?`); HTTP 409 — прямой цикл с другой цепочкой |
|
|
217
|
+
| `chain_delete(chainId, confirm:true)` | удалить цепочку (теряется история заданий) |
|
|
218
|
+
| `chain_enable(chainId)` / `chain_pause(chainId)` | включить / поставить на паузу приём новых постов источника |
|
|
219
|
+
| `chain_runs(chainId, page?, size?)` | история заданий цепочки, новые сверху (бэкенд без постраничной выдачи — `page`/`size`, если переданы, режут список в MCP) |
|
|
220
|
+
| `chain_approve(jobId)` / `chain_reject(jobId)` | согласовать / отклонить задание, ждущее подтверждения (`approvalMode` AI_ONLY/ALL) |
|
|
221
|
+
| `chain_retry(jobId)` | повторить проваленное задание вручную, минуя бэкофф |
|
|
222
|
+
| `site_analytics(siteId, from?, to?)` | KPI+тренд сайта: просмотры, клики (только с `goalName`), заявки, топ-страницы/источники (по умолчанию последние 30 дней) |
|
|
223
|
+
| `site_analytics_events(siteId, from?, to?, page?, size?)` | сырые события сайта постранично (`PAGE_VIEW`\|`CLICK`\|`FORM_SUBMIT`, UTM) |
|
|
224
|
+
| `bot_subscriber_analytics(botId, from, to)` | новые подписчики/активность бота по дням (дашборд аналитики, диапазон до 92 дней) |
|
|
225
|
+
| `scenario_timeseries(graphId, from, to)` | дневной ряд прогонов сценария `{ok, failed, other}` (по узлам — `graph_analytics`) |
|
|
226
|
+
| `agent_tool_test(agentId, toolId, params?)` | проверить инструмент агента (`AMO_LOOKUP`/`SHEETS_LOOKUP`/`HTTP_API`) напрямую, без модели — бюджет ИИ не тратится |
|
|
227
|
+
| `site_lead_file_download(siteId, leadId, fileId)` | метаданные вложения заявки + ссылка на скачивание (сам файл через MCP не передаётся — HEAD-запрос, не GET) |
|
|
198
228
|
|
|
199
229
|
### Рассылки
|
|
200
230
|
|
|
@@ -230,7 +260,7 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
230
260
|
|---|---|
|
|
231
261
|
| нет токена / токен отозван | `/bots/mcp-tokens` — создать токен и прислать агенту |
|
|
232
262
|
| Google Таблицы (`connect_integration`) | одноразовая ссылка согласия Google (OAuth) |
|
|
233
|
-
| Instagram (`create_bot`, `connect_integration`) | `/bots/
|
|
263
|
+
| Instagram (`create_bot`, `connect_integration`) | `/bots/instagram` — подключается входом через Instagram (OAuth); нужен Instagram-аккаунт Business/Creator |
|
|
234
264
|
| лимит тарифа, рассылки на бесплатном (HTTP 402) | `upgradeUrl` из ответа или `/bots/subscription` |
|
|
235
265
|
| свой домен сайта | DNS у регистратора: A-запись на `dnsTarget` из `site_domains` |
|
|
236
266
|
|
package/package.json
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@retensy/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server to build and publish Telegram and MAX bot funnels/automations, broadcasts, service integrations, block-based websites (incl. free-layout Zero blocks, designs, templates, custom domains, form leads) and Markdown blog articles in the Retensy Bots service. Zero dependencies.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"retensy-mcp": "src/index.mjs"
|
|
8
|
-
},
|
|
9
|
-
"main": "src/index.mjs",
|
|
10
|
-
"publishConfig": {
|
|
11
|
-
"access": "public"
|
|
12
|
-
},
|
|
13
|
-
"files": [
|
|
14
|
-
"src",
|
|
15
|
-
"skills",
|
|
16
|
-
".mcp.json",
|
|
17
|
-
".claude-plugin",
|
|
18
|
-
"README.md",
|
|
19
|
-
"LICENSE"
|
|
20
|
-
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"start": "node src/index.mjs",
|
|
23
|
-
"check": "node --check src/index.mjs",
|
|
24
|
-
"test": "node scripts/smoke.mjs && node scripts/telemetry.test.mjs && node scripts/live-edit-422.test.mjs && node scripts/broadcast-connect.test.mjs && node scripts/integration-core.test.mjs && node scripts/site-init.test.mjs && node scripts/ai-agents.test.mjs && node scripts/battery-tools.test.mjs && node scripts/parity-tools.test.mjs && node scripts/parity-2.test.mjs && node scripts/validate-graph.test.mjs && node scripts/call-scenario.test.mjs",
|
|
25
|
-
"validate": "node skills/build-bot-funnel/validate.mjs"
|
|
26
|
-
},
|
|
27
|
-
"engines": {
|
|
28
|
-
"node": ">=18"
|
|
29
|
-
},
|
|
30
|
-
"keywords": [
|
|
31
|
-
"mcp",
|
|
32
|
-
"telegram",
|
|
33
|
-
"max",
|
|
34
|
-
"instagram",
|
|
35
|
-
"bot",
|
|
36
|
-
"funnel",
|
|
37
|
-
"broadcast",
|
|
38
|
-
"automation",
|
|
39
|
-
"articles",
|
|
40
|
-
"website-builder",
|
|
41
|
-
"landing-page",
|
|
42
|
-
"blog",
|
|
43
|
-
"markdown",
|
|
44
|
-
"retensy",
|
|
45
|
-
"claude-code",
|
|
46
|
-
"model-context-protocol"
|
|
47
|
-
],
|
|
48
|
-
"repository": {
|
|
49
|
-
"type": "git",
|
|
50
|
-
"url": "git+https://github.com/retensy/retensy-mcp.git"
|
|
51
|
-
},
|
|
52
|
-
"homepage": "https://bots.retensy.com/bots",
|
|
53
|
-
"license": "MIT"
|
|
54
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@retensy/mcp",
|
|
3
|
+
"version": "0.23.0",
|
|
4
|
+
"description": "MCP server to build and publish Telegram and MAX bot funnels/automations, broadcasts, service integrations, block-based websites (incl. free-layout Zero blocks, designs, templates, custom domains, form leads) and Markdown blog articles in the Retensy Bots service. Zero dependencies.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"retensy-mcp": "src/index.mjs"
|
|
8
|
+
},
|
|
9
|
+
"main": "src/index.mjs",
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"skills",
|
|
16
|
+
".mcp.json",
|
|
17
|
+
".claude-plugin",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"start": "node src/index.mjs",
|
|
23
|
+
"check": "node --check src/index.mjs",
|
|
24
|
+
"test": "node scripts/smoke.mjs && node scripts/telemetry.test.mjs && node scripts/live-edit-422.test.mjs && node scripts/broadcast-connect.test.mjs && node scripts/integration-core.test.mjs && node scripts/site-init.test.mjs && node scripts/ai-agents.test.mjs && node scripts/battery-tools.test.mjs && node scripts/parity-tools.test.mjs && node scripts/parity-2.test.mjs && node scripts/validate-graph.test.mjs && node scripts/call-scenario.test.mjs && node scripts/audience-publications.test.mjs && node scripts/chains-analytics.test.mjs",
|
|
25
|
+
"validate": "node skills/build-bot-funnel/validate.mjs"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"mcp",
|
|
32
|
+
"telegram",
|
|
33
|
+
"max",
|
|
34
|
+
"instagram",
|
|
35
|
+
"bot",
|
|
36
|
+
"funnel",
|
|
37
|
+
"broadcast",
|
|
38
|
+
"automation",
|
|
39
|
+
"articles",
|
|
40
|
+
"website-builder",
|
|
41
|
+
"landing-page",
|
|
42
|
+
"blog",
|
|
43
|
+
"markdown",
|
|
44
|
+
"retensy",
|
|
45
|
+
"claude-code",
|
|
46
|
+
"model-context-protocol"
|
|
47
|
+
],
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/retensy/retensy-mcp.git"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://bots.retensy.com/bots",
|
|
53
|
+
"license": "MIT"
|
|
54
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: build-bot-funnel
|
|
3
|
-
description: Собрать воронку (сценарий) бота для сервиса /bots из текстового описания — сгенерировать валидный граф формата retensy-bot-graph, проверить его и (через MCP bot-graph) залить в бота, прогнать dry-run и опубликовать. Поддерживает Telegram и
|
|
3
|
+
description: Собрать воронку (сценарий) бота для сервиса /bots из текстового описания — сгенерировать валидный граф формата retensy-bot-graph, проверить его и (через MCP bot-graph) залить в бота, прогнать dry-run и опубликовать. Поддерживает Telegram, MAX и Instagram (IG-бот подключается только в браузере через вход Instagram на /bots/instagram, без токена). Использовать, когда пользователь описывает воронку/прогрев/сценарий бота словами и просит «собрать», «сделать граф», «залить в бота», «опубликовать сценарий».
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Сборка воронки бота (retensy /bots)
|
|
7
7
|
|
|
8
|
-
Превращает **текстовое описание воронки** в валидный граф `retensy-bot-graph` и (опционально) публикует его через MCP-сервер `bot-graph`. Поддерживаются платформы **Telegram и
|
|
8
|
+
Превращает **текстовое описание воронки** в валидный граф `retensy-bot-graph` и (опционально) публикует его через MCP-сервер `bot-graph`. Поддерживаются платформы **Telegram, MAX и Instagram** — у каждой свои триггеры и ограничения, описанные в schema.md.
|
|
9
9
|
|
|
10
10
|
**Различия платформ (кратко):**
|
|
11
11
|
- **Telegram** — полный функционал: `/start` и другие команды, все типы узлов, SUBSCRIBED, кнопки-контакты, рассылки.
|
|
12
12
|
- **MAX** — те же узлы, но без SUBSCRIBED/reply-клавиатур (мягкие предупреждения при публикации, не блокируют).
|
|
13
|
-
- **Instagram**
|
|
13
|
+
- **Instagram** — ограниченный набор узлов; вход только через комментарий/директ/историю (нет `/start`); нет рассылок; DELAY не более 24ч; ASK_QUESTION только TEXT/EMAIL/PHONE/NUMBER/CONTACT (CONTACT = ручной ввод номера, кнопки «Поделиться номером» в IG нет); подключение только в браузере — вход через Instagram (OAuth) на странице `/bots/instagram`, нужен Instagram-аккаунт Business/Creator; без токена бота.
|
|
14
14
|
|
|
15
15
|
## Когда применять
|
|
16
16
|
Пользователь описывает сценарий бота словами: приветствие → подписка → вопрос с кнопками → ветки → задержки → вебинар и т.п. Либо просит залить/опубликовать готовую воронку.
|
|
@@ -41,6 +41,7 @@ description: Собрать воронку (сценарий) бота для с
|
|
|
41
41
|
- **`TRIGGER_PAYMENT` («оплата прошла»)** — если счёт выставлял бот, подписчик определяется САМ (счёт помнит его в metadata платежа): можно сразу слать сообщение и вешать метку, разбирать ответ кассы не нужно. Если оплата пришла мимо бота — прогон headless, получателя задавай через `config.target`. Ставь фильтр `minAmount`/`descriptionContains`, иначе сценарий сработает на любую оплату в кассе.
|
|
42
42
|
- **`TRIGGER_SCHEDULE` («По расписанию»)** — `{cron:"0 9 * * 1-5", timezone}`: headless-прогон без чата (отправка — только с `config.target`), для сводок, эскалаций, SLA. Журнал — `scenario_runs`.
|
|
43
43
|
- **Запись на слоты и напоминание**: календарь создаётся `booking_calendar_create`; в `ACTIONS` — `booking_slots` (список в `{{var.slots}}`) → вопрос → `booking_book` (ветка `taken` — слот занят) → `DELAY UNTIL {{var.slot_at}}` с `offset:"-24h"` → напоминание; `booking_cancel` освобождает слот. **Подписки**: `invite_link_create`/`invite_link_revoke`, `subscription_extend`/`subscription_check`, `yookassa_charge_saved`. **Передача оператору** — `agent_chat` (бот замолкает до «Вернуть боту»). Поля — в schema.md.
|
|
44
|
+
- **Переиспользуемые подсценарии — `call_scenario`** (действие в `ACTIONS`, ставь ПОСЛЕДНИМ в блоке): `graphId` — сценарий ЭТОГО ЖЕ бота (`list_graphs`), `mode: "goto"` (по умолчанию, уйти без возврата) или `"call"` (вызвать и вернуться — после `END`/конца цели движок продолжает вызывающего с места после блока). Общая глубина `goto`+`call` в одной цепочке — не больше 5 переходов/вызовов подряд. Коды ошибок и рантайм-поведение — в validation.md.
|
|
44
45
|
- **Статус прогона `PARTIAL`** («завершён с ошибками») — сценарий дошёл до конца, но действие упало (шаг `ok:false`, `error` «КОД: …»). Проверяй `bot_user_runs`/`scenario_runs`, а ответы CRM/HTTP — `integration_calls`.
|
|
45
46
|
- **`TRIGGER_SITE_FORM` («Заявка с сайта»)** — `{siteId, formId}`: посетитель отправил форму на ОПУБЛИКОВАННОМ сайте владельца (`site_list` → id сайта, id формы — из `site_get`). Пустой/отсутствующий `formId` = любая форма сайта. Поля формы — `{{var.<имя поля>}}`; имя, почта, телефон всегда доступны как `{{var.name}}`, `{{var.email}}`, `{{var.phone}}` (если поле названо иначе — находятся по подписи), UTM — `{{var.utm_source}}` и т.п. Чата с посетителем нет: узлы отправки работают только с явным `config.target`, CRM/таблицы/вебхуки — как обычно. Обычно это сценарий без бота (источник WEBHOOK, как вебхук-сценарий) — ему `TRIGGER_WEBHOOK` не нужен; можно и в сценарии бота. Чужой сайт → `SITE_FORM_FOREIGN_SITE`, без сайта → `SITE_FORM_NO_SITE`. Одна заявка — один запуск; `TRIGGER_ANY_UPDATE` на заявки не срабатывает.
|
|
46
47
|
|
|
@@ -179,7 +179,11 @@ IG-боты не поддерживают команды (`/start`). Вход
|
|
|
179
179
|
- **передача оператору**: `agent_chat` (без полей) — «позвать человека»: диалог встаёт на паузу (бот и ИИ молчат, входящие копятся в «Диалогах»), владельцу уходит уведомление; без бота уведомлений действие помечается `NOT_CONFIGURED`, пауза всё равно включается. Вернуть боту — кнопка в «Диалогах» или MCP `dialog_handoff {active:false}`. В dry-run и headless-прогонах пропускается.
|
|
180
180
|
- **срок ответа оператора (SLA)**: `sla_check` — опц. `maxLevel` (0 — без потолка), `timezone` (для `sla_at`, по умолч. Europe/Moscow). Ставь первым в блоке `ACTIONS` после `DELAY` на срок ответа, ниже в том же блоке — эскалация (`add_tag`, `notify`). Оператор не писал в чат после `agent_chat` → `sla_breached`=`1`, `sla_level` +1, `sla_at` — ISO со смещением, действия блока выполняются; ответил / диалог у бота / тот же таймер повторно → `sla_breached`=`0`, остальные действия блока пропускаются. Новая передача — уровни с 1. Ветвиться дальше — `CONDITION` по `{{var.sla_level}}`.
|
|
181
181
|
- **запись на слоты** (календарь — `booking_calendar_create`/`booking_calendar_list`): `booking_slots` — `calendarId`, опц. `days` (1–62, по умолч. 7), `limit` (1–50, по умолч. 10), `saveTo` (по умолч. `slots`) → переменные `slots` (нумерованный список для сообщения), `slots_json`, `slots_count`; `booking_book` — `calendarId`, `slotAt` (по умолч. `{{var.slot_at}}`; можно номер из списка «2», ISO-время или подпись слота), опц. `slotsVar`, `name`/`phone` (по умолч. `{{var.name}}`/`{{var.phone}}`) → `booking_id`, `slot_at` (ISO с зоной — для напоминания `DELAY UNTIL {{var.slot_at}} offset -24h`), `slot_label`, `booking_status`; занятый слот → выход блока **`taken`** (если ребро нарисовано, иначе `error`); `booking_cancel` — `bookingId` (по умолч. `{{var.booking_id}}`), освобождает слот.
|
|
182
|
-
- **переход в другой сценарий**: `call_scenario` — `graphId` (сценарий ЭТОГО ЖЕ бота того же владельца, id из `list_graphs`; черновик тоже можно), опц. `nodeId` (стартовый блок цели; по умолч. триггер `/start` → первый триггер → первый блок без входящих рёбер — так стартует подсценарий без триггера), `variables: [{ "key":"plan", "value":"{{var.x}}" }]` (ключи `[a-z][a-z0-9_]{0,63}`, служебные `_*` запрещены — `ACTION_BAD_KEY`, в рантайме пропускаются, значения — шаблоны; сохраняются подписчику ДО перехода), `mode` —
|
|
182
|
+
- **переход в другой сценарий**: `call_scenario` — `graphId` (сценарий ЭТОГО ЖЕ бота того же владельца, id из `list_graphs`; черновик тоже можно), опц. `nodeId` (стартовый блок цели; по умолч. триггер `/start` → первый триггер → первый блок без входящих рёбер — так стартует подсценарий без триггера), `variables: [{ "key":"plan", "value":"{{var.x}}" }]` (ключи `[a-z][a-z0-9_]{0,63}`, служебные `_*` запрещены — `ACTION_BAD_KEY`, в рантайме пропускаются, значения — шаблоны; сохраняются подписчику ДО перехода), `mode` — `"goto"` (по умолч.) или `"call"`. Ставь ПОСЛЕДНИМ в блоке для обоих режимов (`CALL_SCENARIO_NOT_LAST`), не на себя (`CALL_SCENARIO_SELF`), только `goto`/`call` (иначе `CALL_SCENARIO_BAD_MODE`), без цели — `CALL_SCENARIO_NO_TARGET`, чужой/несуществующий/вебхук-сценарий — `CALL_SCENARIO_FORBIDDEN` при публикации.
|
|
183
|
+
- `mode:"goto"` (по умолчанию) — подписчик уходит в целевой сценарий отдельным прогоном, текущий прогон завершается `OK`; ответы на вопросы и кнопки цели доходят до неё, хотя активный сценарий бота другой (`session.currentGraphId`). Возврата нет — для «вернуться» нужно либо `mode:"call"`, либо свой `call_scenario` обратно в конце цели (сработает, только если между ними был ответ подписчика/задержка, иначе `CALL_SCENARIO_RECURSION`).
|
|
184
|
+
- `mode:"call"` — «вызвать и вернуться»: перед переходом движок кладёт возвратный кадр (вызывающий graphId + узел, на который вести после — следующий по ребру `next` этого же блока, либо ничего, если ребра нет) в `session.callStack` (персистентно, переживает рестарт сервера). Когда цель доходит до `END` или до конца графа (рёбер больше нет), движок снимает кадр и продолжает вызывающего С ЭТОГО МЕСТА новым прогоном — удобно для переиспользуемых подсценариев («спросить телефон», «проверить оплату») без копипасты.
|
|
185
|
+
- Общий счётчик глубины/цикла — ОБЩИЙ для `goto` и `call` в одной цепочке: не больше 5 переходов/вызовов подряд (`CALL_SCENARIO_DEPTH`), повтор графа, уже бывшего в цепочке — `CALL_SCENARIO_RECURSION` (для `call` цепочка — это персистентный `callStack`, не только текущий тик).
|
|
186
|
+
- В рантайме отказ = шаг `ok=false`, ошибка `CODE: call_scenario — …`, блок в `error` (нет ребра — `next`): `CALL_SCENARIO_RECURSION`, `CALL_SCENARIO_DEPTH`, `CALL_SCENARIO_NOT_FOUND`, `CALL_SCENARIO_FORBIDDEN`, `CALL_SCENARIO_NO_ENTRY`, `CALL_SCENARIO_NO_SUBSCRIBER` (headless-вебхук), `NOT_CONFIGURED` (бэкенд в режиме развязанных сценариев — переход недоступен). Сбой более раннего действия блока не прячется переходом: шаг `ok=false` с кодом того сбоя, прогон `PARTIAL`, переход/вызов при этом выполняется. Цель-черновик исполняется как есть — правки черновика сразу видят подписчики, уже перешедшие в него.
|
|
183
187
|
- **контакт заявки**: `lead_link_contact` — `phone`/`email` (по умолч. `{{var.phone}}`/`{{var.email}}`) → склейка с единым контактом владельца по телефону E.164 или email, `{{var.contact_id}}`.
|
|
184
188
|
- **платный доступ / подписки** (бот должен быть админом группы/канала): `invite_link_create` — персональная ссылка на одно вступление: опц. `expireHours` (по умолч. 24), `name`, `saveTo` (по умолч. `invite_link`), `send` (false — не отправлять), `text`; `invite_link_revoke` — `link` (по умолч. `{{var.invite_link}}`); `subscription_extend` — `period` (`1mo`, `30d`, `1y`, `2w`, `12h`), опц. `var` (по умолч. `sub_until`), `timezone` — продлевает от max(сейчас, текущий срок); `subscription_check` — опц. `var`, `saveTo` (по умолч. `sub_active`) → `true`/`false`; `yookassa_charge_saved` — рекуррентное списание по сохранённому способу оплаты: `connectionId` (ЮKassa), `amount`, `description`, опц. `paymentMethodId` (по умолч. `{{var.yk_payment_method_id}}`), `periodKey` (по умолч. `{{var.sub_until}}` — защита от двойного списания за период). Доступ продлевает не списание, а сценарий `TRIGGER_PAYMENT` → `subscription_extend`.
|
|
185
189
|
- **Meta Conversions API** (Integration Core): `meta_capi_event` — `connectionId`, `eventName` (Purchase/Lead/…), опц. `dealId`, `stage`, `eventId`, `email`/`phone` (сырые — хешируются SHA-256), `value`, `currency`, `actionSource`. Поля других провайдеров — в `integration_catalog` (`actions[].inputs`).
|
|
@@ -405,10 +409,8 @@ IG-боты не поддерживают команды (`/start`). Вход
|
|
|
405
409
|
|
|
406
410
|
## Платформа Instagram
|
|
407
411
|
|
|
408
|
-
> **Сейчас Instagram выключен в сервисе:** IG-бота не подключить, страница `/bots/instagram` ведёт на `/bots`. Раздел оставлен на будущее включение.
|
|
409
|
-
|
|
410
412
|
IG-боты подключаются через OAuth на странице **`/bots/instagram`** (раздел «Подключения» → карточка
|
|
411
|
-
Instagram; прежний раздел «Инструменты роста» / `/growth` расформирован и редиректит) — **без вставки токена вручную**; у IG нет персонального бот-токена. После OAuth бот получает доступ к Messaging API через
|
|
413
|
+
Instagram; прежний раздел «Инструменты роста» / `/growth` расформирован и редиректит) — **без вставки токена вручную**; у IG нет персонального бот-токена. Подключение открывается только в браузере: вход через Instagram (`www.instagram.com/oauth/authorize`), затем выбор аккаунта. Нужен Instagram-аккаунт типа **Business или Creator** — обычный личный профиль подключить нельзя. После OAuth бот получает доступ к Messaging API через этот аккаунт.
|
|
412
414
|
|
|
413
415
|
### Разрешённые типы узлов для IG-ботов
|
|
414
416
|
|
|
@@ -47,6 +47,14 @@
|
|
|
47
47
|
- `SCHEDULE`: `isoDate` = `YYYY-MM-DD`, ISO-8601 дата-время или шаблон `{{var.x}}` (`SCHEDULE_BAD_DATE`); `time` = `HH:mm` — обязателен при голой дате (`SCHEDULE_BAD_TIME`); опц. `offset` (`SCHEDULE_BAD_OFFSET`), `timezone` (`SCHEDULE_BAD_TIMEZONE`).
|
|
48
48
|
- `TRIGGER_SCHEDULE`: `cron` ровно из 5 полей, не чаще раза в минуту (`SCHEDULE_TRIGGER_BAD_CRON`); `timezone` — известная зона (`SCHEDULE_TRIGGER_BAD_TIMEZONE`).
|
|
49
49
|
- `ACTIONS`: непустой `actions[]` (`ACTIONS_EMPTY`); каждый `kind` — из допустимого списка (`ACTION_UNKNOWN_KIND`, список — в schema.md); per-kind: `tag` (`ACTION_BAD_TAG`), `set_field.key` (`ACTION_BAD_KEY`), `url` у `external_request`/`subscriber_webhook` (`ACTION_BAD_URL`), `calendarId` у `booking_slots`/`booking_book` (`ACTION_BOOKING_NO_CALENDAR`) и их `saveTo` (`ACTION_BAD_KEY`). Действия Integration Core (`actions[].kind` из `integration_catalog`, например `meta_capi_event`) бэкенд узнаёт по реестру провайдеров; `validate.mjs` их оффлайн не знает и выдаёт предупреждение, а не ошибку.
|
|
50
|
+
- `ACTIONS` с действием `call_scenario` (переход/вызов другого сценария того же бота того же владельца), зеркало `GraphValidator.validateScenarioCalls`:
|
|
51
|
+
- должно быть **последним** действием в блоке, для обоих `mode` (`CALL_SCENARIO_NOT_LAST`);
|
|
52
|
+
- `graphId` обязателен — сценарий этого же бота, `list_graphs` (`CALL_SCENARIO_NO_TARGET`); не сам текущий граф (`CALL_SCENARIO_SELF`, сравнение без учёта регистра);
|
|
53
|
+
- `mode` — `"goto"` (по умолчанию) или `"call"`, другое значение → `CALL_SCENARIO_BAD_MODE`;
|
|
54
|
+
- `variables[].key` ∈ `[a-z][a-z0-9_]{0,63}`, служебные `_*` запрещены → `ACTION_BAD_KEY`;
|
|
55
|
+
- чужой/несуществующий/вебхук-сценарий-цель → `CALL_SCENARIO_FORBIDDEN` (проверяется при публикации, не офлайн-валидатором).
|
|
56
|
+
- В рантайме (оба `mode` делят один и тот же счётчик глубины/цикла): не больше **5** переходов/вызовов подряд в одной цепочке (`CALL_SCENARIO_DEPTH`); граф, уже бывший в этой цепочке — `CALL_SCENARIO_RECURSION` (для `mode:"call"` цепочка — персистентный `session.callStack`, не только текущий тик); прочие рантайм-коды: `CALL_SCENARIO_NOT_FOUND`, `CALL_SCENARIO_NO_ENTRY`, `CALL_SCENARIO_NO_SUBSCRIBER` (headless-вебхук), `NOT_CONFIGURED` (бэкенд в режиме развязанных сценариев).
|
|
57
|
+
- `mode:"goto"` — текущий прогон завершается `OK`, подписчик продолжает в цели отдельным прогоном, возврата нет. `mode:"call"` — перед переходом в `session.callStack` кладётся кадр (вызывающий граф + узел после блока по ребру `next`, если есть); когда цель доходит до `END`/конца графа, кадр снимается и вызывающий продолжается с этого места новым прогоном.
|
|
50
58
|
- `ACTIONS` с действием `issue_invoice` (счёт на кассу владельца):
|
|
51
59
|
- `connectionId` обязателен (`YK_NO_CONNECTION`) — подключение с `provider=YOOKASSA` из `list_integrations`;
|
|
52
60
|
- `amount` обязателен (`YK_NO_AMOUNT`), число > 0 или шаблон `{{var.x}}` (`YK_BAD_AMOUNT`);
|
|
@@ -403,8 +403,8 @@ for (const n of nodes) {
|
|
|
403
403
|
// Зеркало GraphValidator.validateScenarioCalls. Владение целью (сценарий этого же бота) проверит publish_graph.
|
|
404
404
|
if (i !== c.actions.length - 1)
|
|
405
405
|
errors.push(`CALL_SCENARIO_NOT_LAST: ${who} — переход в сценарий должен быть последним действием в блоке.`);
|
|
406
|
-
if (a.mode != null && String(a.mode)
|
|
407
|
-
errors.push(`CALL_SCENARIO_BAD_MODE: ${who} —
|
|
406
|
+
if (a.mode != null && !["goto", "call"].includes(String(a.mode)))
|
|
407
|
+
errors.push(`CALL_SCENARIO_BAD_MODE: ${who} — mode: «goto» (перейти) или «call» (вызвать и вернуться).`);
|
|
408
408
|
(Array.isArray(a.variables) ? a.variables : []).forEach((v, j) => {
|
|
409
409
|
const key = String(v?.key ?? "");
|
|
410
410
|
if (!VAR_RE.test(key) || key.startsWith("_"))
|