@retensy/mcp 0.35.0 → 0.37.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/README.md
CHANGED
|
@@ -69,7 +69,7 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
69
69
|
|
|
70
70
|
| Платформа | Онбординг | Триггеры входа | Ограничения |
|
|
71
71
|
|---|---|---|---|
|
|
72
|
-
| **Telegram** | Токен бота (BotFather) → `create_bot` | `/start`, команды, callback, текст,
|
|
72
|
+
| **Telegram** | Токен бота (BotFather) → `create_bot` | `/start`, команды, callback, текст, рассылки, **Telegram Business** (бот отвечает от имени аккаунта владельца: `business_message`, условие `BUSINESS`) | Полный функционал |
|
|
73
73
|
| **MAX** | Токен бота (MasterBot в MAX) → `create_bot` | Команды, callback, текст | Без SUBSCRIBED/reply-клавиатур (мягкие предупреждения) |
|
|
74
74
|
| **Instagram** | Вход через Instagram (OAuth) в кабинете `/bots/instagram` (без токена) — нужен Instagram-аккаунт Business или Creator | Комментарий/Direct/Ответ на историю/Упоминание | Ограниченный набор узлов; DELAY ≤ 24ч; ASK_QUESTION только TEXT/EMAIL/PHONE/NUMBER/CONTACT (CONTACT = ручной ввод номера); без рассылок |
|
|
75
75
|
|
|
@@ -209,7 +209,7 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
209
209
|
| `graph_versions_list(graphId, [version], [compareTo])` | история версий сценария (снимки при публикации, правках живого графа, ручные); с `version:N` — сравнение с текущим графом или другой версией (`compareTo`). Право `read` |
|
|
210
210
|
| `graph_version_rollback(graphId, version)` | откат сценария на версию = публикация снимка (проверки как у `edit_graph_live`, текущее состояние сохраняется отдельной версией). Право `write:bots` |
|
|
211
211
|
| `import_funnel(botId, name, graphFile\|graph)` | всё за раз: create → update → dry-run → publish |
|
|
212
|
-
| `list_templates()` | готовые шаблоны воронок |
|
|
212
|
+
| `list_templates([niche], [channel], [query])` | готовые шаблоны воронок (~30, ниша и каналы в карточке; фильтры по нише, каналу, тексту); те же открыты публично на `/templates` |
|
|
213
213
|
| `create_graph_from_template(botId, templateId, name)` | граф из шаблона (DRAFT) |
|
|
214
214
|
| `clone_graph(graphId)` | копия графа в новый DRAFT |
|
|
215
215
|
| `rename_graph(graphId, name)` | переименовать сценарий |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retensy/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
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
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"scripts": {
|
|
22
22
|
"start": "node src/index.mjs",
|
|
23
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/agent-write-tools.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 && node scripts/platform-decisions.test.mjs && node scripts/token-scopes.test.mjs && node scripts/secondary-graphs.test.mjs && node scripts/graph-versions.test.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/agent-write-tools.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 && node scripts/platform-decisions.test.mjs && node scripts/token-scopes.test.mjs && node scripts/secondary-graphs.test.mjs && node scripts/graph-versions.test.mjs && node scripts/templates-filter.test.mjs",
|
|
25
25
|
"validate": "node skills/build-bot-funnel/validate.mjs"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
@@ -9,6 +9,7 @@ description: Собрать воронку (сценарий) бота для с
|
|
|
9
9
|
|
|
10
10
|
**Различия платформ (кратко):**
|
|
11
11
|
- **Telegram** — полный функционал: `/start` и другие команды, все типы узлов, SUBSCRIBED, кнопки-контакты, рассылки.
|
|
12
|
+
- **Telegram Business** — тот же Telegram-бот может отвечать клиентам от имени личного/бизнес-аккаунта владельца (подключает владелец в настройках Telegram; сообщения идут в отдельной сессии, триггер `business_message`, условие `BUSINESS`; подробности в schema.md).
|
|
12
13
|
- **MAX** — те же узлы, но без SUBSCRIBED/reply-клавиатур (мягкие предупреждения при публикации, не блокируют).
|
|
13
14
|
- **Instagram** — ограниченный набор узлов; вход только через комментарий/директ/историю (нет `/start`); нет рассылок; DELAY не более 24ч; ASK_QUESTION только TEXT/EMAIL/PHONE/NUMBER/CONTACT (CONTACT = ручной ввод номера, кнопки «Поделиться номером» в IG нет); подключение только в браузере — вход через Instagram (OAuth) на странице `/bots/instagram`, нужен Instagram-аккаунт Business/Creator; без токена бота.
|
|
14
15
|
|
|
@@ -30,7 +31,7 @@ description: Собрать воронку (сценарий) бота для с
|
|
|
30
31
|
- **Не добавляй узлы `END`** — ветка завершается сама на узле без исходящих рёбер; явный «конец сценария» убран из редактора.
|
|
31
32
|
- `SEND_MESSAGE` умеет быть и сообщением, и **вопросом** (`awaitReply:true` + `saveTo`/`inputKind`/`validator`). У кнопок-ссылок (`kind:"URL"`) есть флаг `track:true` — на такой шаг ссылается условие `LINK_CLICKED`.
|
|
32
33
|
- **Медиа** (фото/видео/аудио/документ/кружок/галерея): загрузи файл инструментом `upload_file` (локальный `path` или `url` для перезаливки) → получишь публичный URL → вставь его в медиа-карточку `SEND_MESSAGE` (`url`, у `gallery` — `urls[]`) или в `SEND_PHOTO.photoUrl`. Уже загруженное — `list_files`. Бинарь в графе не хранится, только ссылки. Подробности — в schema.md.
|
|
33
|
-
- `CONDITION` умеет: теги, переменные, UTM-метки, имя/email/телефон из профиля, @username, подписку на канал (`SUBSCRIBED`), клик по ссылке шага (`LINK_CLICKED`), дату/время/день недели. Полная таблица `kind`/`op` — в schema.md.
|
|
34
|
+
- `CONDITION` умеет: теги, переменные, UTM-метки, имя/email/телефон из профиля, @username, подписку на канал (`SUBSCRIBED`), клик по ссылке шага (`LINK_CLICKED`), канал Telegram Business (`BUSINESS`), дату/время/день недели. Полная таблица `kind`/`op` — в schema.md.
|
|
34
35
|
- Пакет `ACTIONS` — до 30 действий (метки, профиль, HTTP, уведомления, интеграции GetCourse/amoCRM/Битрикс24/Google Sheets/Я.Метрика, приём оплат, модерация группы). Список — в schema.md. **Интеграции РАБОТАЮТ** (это не заглушки), но каждой нужен `connectionId` — возьми его через **`list_integrations`**; без него действие упадёт «не выбрано подключение». Для Google Таблиц вместо `connectionId` нужен `googleEmail` (аккаунт подключается в вебе).
|
|
35
36
|
- `AI_REPLY` («AI-агент») — два режима, поле `mode` (`"simple"`/не задан, либо `"agent"`). Платный узел в обоих режимах (`PREMIUM_NODE_FORBIDDEN` на бесплатном тарифе) и недоступен в Instagram-графах (нет в IG-allowlist). Тратит месячный AI-бюджет тарифа — он в токенах, не в рублях.
|
|
36
37
|
- **`mode` не задан или `"simple"`** — ответ модели по `systemPrompt`/`userPromptTemplate` (обязателен, иначе выход `error`). Исчерпание бюджета — не ошибка: узел шлёт `quotaFallbackText` (если задан) и идёт дальше по `next`, как обычно.
|
|
@@ -60,7 +61,7 @@ description: Собрать воронку (сценарий) бота для с
|
|
|
60
61
|
5. **Залей и опубликуй через MCP `bot-graph`** (если он подключён и пользователь просит публикацию):
|
|
61
62
|
- `list_bots` → выбрать `botId` (или `create_graph` в существующем боте).
|
|
62
63
|
Конфликт токена (тот же Telegram-токен подключён ещё в BotHelp/Salebot/своём скрипте): `active:false` + `pausedReason:"TOKEN_CONFLICT"` — вебхук другого сервиса, бот на паузе (отвечать не будет); `pausedChannels` — выключены отдельные каналы; `conflictWarning:"TOKEN_CONFLICT_POLLING"` — бот работает, но часть сообщений забирает другая программа. Во всех случаях попроси пользователя отключить бота в том сервисе (или `/revoke` в @BotFather → `bot_change_token`), при паузе — потом `bot_resume`. Справка: `/docs#token-conflict`.
|
|
63
|
-
- `create_graph(botId, name)` → получить `graphId`. Либо стартуй с готовой основы: `list_templates` → `create_graph_from_template(botId, templateId, name)`.
|
|
64
|
+
- `create_graph(botId, name)` → получить `graphId`. Либо стартуй с готовой основы: `list_templates` (фильтры `niche`, `channel`, `query`; около 30 шаблонов под русские ниши: салон, клиника, недвижимость, школа, магазин, ресторан, авто, юрист, отзывы и др.) → `create_graph_from_template(botId, templateId, name)`. Подходящий шаблон почти всегда быстрее и надёжнее сборки с нуля: правь только тексты и ссылки. Шаблоны не содержат платных узлов (`AI_REPLY`, `CALL_WEBHOOK`), поэтому работают и на бесплатном тарифе.
|
|
64
65
|
- `update_graph(graphId, nodes, edges, canvasMeta)` → залить узлы/рёбра.
|
|
65
66
|
- `dry_run(graphId, kind:"command", value:"start")` → прогнать стартовую ветку, проверить `runStatus`. `dry_run` ничего не делает снаружи: внешние запросы, CRM, таблицы, письма, уведомления, счета и ответы ИИ пропускаются (у шага `skipped: "dry-run"`), переменные из их ответов остаются пустыми — это проверяется только живым прогоном.
|
|
66
67
|
- `publish_graph(graphId)` → при отказе инструмент вернёт ошибку `HTTP 422` со ВСЕМИ причинами построчно (`code@nodeId: message`) — разобрать, починить узлы, обновить, опубликовать снова.
|
|
@@ -60,15 +60,17 @@
|
|
|
60
60
|
`callback_query`, `channel_post`, `edited_channel_post`, `chat_member`, `chat_join_request`,
|
|
61
61
|
`my_chat_member`, `message_reaction`, `message_reaction_count`, `chat_boost`,
|
|
62
62
|
`removed_chat_boost`, `poll`, `poll_answer`, `inline_query`, `chosen_inline_result`,
|
|
63
|
-
`shipping_query`, `pre_checkout_query
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
`shipping_query`, `pre_checkout_query`, а также Telegram Business: `business_message`,
|
|
64
|
+
`edited_business_message`, `deleted_business_messages` (см. «Telegram Business» ниже).
|
|
65
|
+
**НЕ работают** (Telegram их шлёт, но рантайм не разбирает): `business_connection` (подключения
|
|
66
|
+
бэкенд ведёт сам, в сценарий они не попадают), `chat_shared`, `users_shared`,
|
|
67
|
+
`write_access_allowed`, `purchased_paid_media` — такой триггер не сработает.
|
|
66
68
|
`filter` (все условия по И, пустое = не задано):
|
|
67
69
|
`{"status":"member"|"left"|"kicked"|…}` — только там, где есть `member.status`
|
|
68
70
|
(`chat_member`, `my_chat_member`); `{"chatId":"-1001234567890"}` — конкретный чат/канал
|
|
69
71
|
(сравнение строкой, id каналов не влезают в int), доступен у 12 событий с `chat`;
|
|
70
72
|
`{"text":"купить","textMode":"contains"|"equals"}` — только `message`/`edited_message`/
|
|
71
|
-
`channel_post`/`edited_channel_post`. У `poll`/`poll_answer`/`inline_query`/
|
|
73
|
+
`channel_post`/`edited_channel_post`/`business_message`/`edited_business_message`. У `poll`/`poll_answer`/`inline_query`/
|
|
72
74
|
`chosen_inline_result`/`shipping_query`/`pre_checkout_query` чата нет → фильтров тоже.
|
|
73
75
|
Бот должен быть админом канала/группы, иначе события оттуда не придут.
|
|
74
76
|
- `TRIGGER_ANY_UPDATE` — `{}`, ловит ЛЮБОЙ апдейт. Приоритет самый низкий: срабатывает, только
|
|
@@ -447,6 +449,7 @@ IG-боты не поддерживают команды (`/start`). Вход
|
|
|
447
449
|
| `USERNAME` | `EQUALS`, `CONTAINS` | `value` | @username пользователя Telegram |
|
|
448
450
|
| `SUBSCRIBED` | `SUBSCRIBED`, `NOT_SUBSCRIBED` | `key` — **числовой** id канала/группы (напр. `-1001234567890`); узнать числовой id подключённых каналов: `list_channels(botId)` | подписан ли пользователь на канал бота |
|
|
449
451
|
| `LINK_CLICKED` | `CLICKED`, `NOT_CLICKED` | `key` — **`id` узла-шага** с отслеживаемой URL-кнопкой (тот же UUID, что у `SEND_MESSAGE`) | кликал ли пользователь по ссылке этого шага |
|
|
452
|
+
| `BUSINESS` | `IS`, `IS_NOT` | ничего не нужно | сообщение пришло через Telegram Business (сессия клиента бизнес-аккаунта) или нет |
|
|
450
453
|
| `CURRENT_DATE` | `BEFORE`, `AFTER`, `EQUALS` | `value` — дата `YYYY-MM-DD` | сегодняшнюю дату (МСК) |
|
|
451
454
|
| `CURRENT_TIME` | `BETWEEN` | `value`, `value2` — время `HH:mm` | текущее время в интервале (через полночь — если `value`>`value2`) |
|
|
452
455
|
| `DAY_OF_WEEK` | `IN` | `days` — массив из `MON`,`TUE`,`WED`,`THU`,`FRI`,`SAT`,`SUN` | день недели (МСК) |
|
|
@@ -455,6 +458,8 @@ IG-боты не поддерживают команды (`/start`). Вход
|
|
|
455
458
|
|
|
456
459
|
**`SUBSCRIBED`** работает только если бот **админ** в канале/группе и канал «привязан» (бот узнаёт о членстве через хук `my_chat_member` — добавь бота в канал админом). `key` должен парситься в число, иначе условие = `false`. Профильные поля (`NAME`/`EMAIL`/`PHONE`) и UTM заполняются по ходу воронки (`ASK_QUESTION`→`saveTo`, диплинк-клик с UTM).
|
|
457
460
|
|
|
461
|
+
**Telegram Business** (только Telegram-боты). Владелец подключает бота сам: Настройки Telegram → Telegram для бизнеса → Чат-боты (нужен Premium) и даёт право «Отвечать на сообщения» (`can_reply`). Статус подключения виден в кабинете: Настройки бота → «Telegram Business». После этого `business_message` клиента запускает сценарий как обычное сообщение: работают `TRIGGER_COMMAND`/`TRIGGER_TEXT`/ИИ-агент/вопросы/задержки, а все ответы уходят от имени аккаунта владельца (`business_connection_id`). Клиент Business живёт в **отдельной сессии** (не склеивается с его личным чатом с ботом): переменные и метки двух диалогов не общие. Бот игнорирует сообщения самого владельца и своих ботов, а при отключённом подключении или без `can_reply` сценарий не запускается. Отличить такие чаты: условие `BUSINESS` или триггер `TRIGGER_TG_EVENT` с `event:"business_message"` (срабатывает только для бизнес-сообщений). Не поддерживается в бизнес-чатах: рассылки и ответы оператора из «Диалогов», удаление сообщений, оплата Stars; отвечать можно только в чатах, где клиент писал за последние 24 часа (ограничение Telegram).
|
|
462
|
+
|
|
458
463
|
**`LINK_CLICKED`** проверяет факт клика по URL-кнопке конкретного шага. Чтобы условие работало: у нужного `SEND_MESSAGE` хотя бы одна кнопка `kind:"URL"` с `"track": true`, а в условии `key` = `id` этого узла-шага. Клик фиксируется через публичный редирект бота, поэтому условие имеет смысл ставить **после** `DELAY`/`ASK_QUESTION` (дай пользователю время кликнуть).
|
|
459
464
|
|
|
460
465
|
> **Платформа MAX.** Боты конструктора умеют работать и в мессенджере MAX. Там не поддерживаются `SUBSCRIBED`/`NOT_SUBSCRIBED` (нет членства в каналах) и reply-клавиатуры; публикация такого графа на MAX-бот вернёт **мягкие предупреждения** (не блокирует). Для Telegram-ботов всё работает как описано.
|
|
@@ -537,3 +542,7 @@ node validate.mjs graph.json --platform=INSTAGRAM
|
|
|
537
542
|
|
|
538
543
|
## Подстановки в тексте
|
|
539
544
|
`{{from.first_name}}`, `{{from.username}}`, `{{var.<имя>}}`, либо `{Имя}` как плейсхолдер. Имена переменных/меток: `[a-z_][a-z0-9_]{0,63}` (var) и `[a-z0-9_-]{1,64}` (tag).
|
|
545
|
+
|
|
546
|
+
## Готовые шаблоны (list_templates)
|
|
547
|
+
|
|
548
|
+
Шаблоны лежат в бэкенде как JSON `{id, name, description, icon, niche, channels, en:{name,description}, graph:{name,nodes,edges}}` и проходят тот же `GraphValidator`, что и ваши графы. Это обычный граф формата выше, без новых типов узлов: id узлов `tpl_*`, кнопки `CALLBACK` с пустым `value` и рёбрами `btn_N`, вопросы с рёбрами `valid`/`invalid`. Платных узлов (`AI_REPLY`, `CALL_WEBHOOK`) в нише-шаблонах нет, кроме универсального `ai-agent` (у него `hasAi:true`). Ниша: beauty, medical, realty, education, ecommerce, fitness, restaurant, auto, legal, reviews, universal. Каналы: TELEGRAM, MAX, VK.
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
- `TRIGGER_TEXT`: `matchMode` ∈ {ANY,EQUALS,CONTAINS,REGEX}; для не-ANY нужен `value`.
|
|
46
46
|
- `ASK_QUESTION`: `promptText` обязателен (`ASK_NO_PROMPT`); `saveTo` ∈ `[a-z_][a-z0-9_]{0,63}` (`ASK_BAD_SAVE_TO`); при `validator:"REGEX"` — валидный `regex` (`ASK_BAD_REGEX`).
|
|
47
47
|
- `BRANCH`: ≥1 case (`BRANCH_NO_CASES`); вне `abTest` — непустое валидное `expression` (`BRANCH_EMPTY_EXPR`/`BRANCH_BAD_EXPR`) и подключённое ребро `case_<id>` (`BRANCH_CASE_UNCONNECTED`; `disabled:true` — если ветка намеренно пустая). В `abTest:true` рёбра нужны только у первых двух case.
|
|
48
|
-
- `CONDITION`: `match` ∈ {ALL, ANY} (`CONDITION_BAD_MATCH`); ≥1 элемент в `conditions` (`CONDITION_EMPTY`). Для `kind:"TAG"` — `value` ∈ `[a-z0-9_-]{1,64}` (`CONDITION_BAD_TAG`); для `kind:"VARIABLE"` — `key` ∈ `[a-z_][a-z0-9_]{0,63}` (`CONDITION_BAD_KEY`). Остальные `kind` (UTM/NAME/EMAIL/PHONE/USERNAME/SUBSCRIBED/LINK_CLICKED/CURRENT_*/DAY_OF_WEEK) бэкенд проверяет в рантайме — некорректный `kind`/`op`, нечисловой `key` у `SUBSCRIBED` или `key` у `LINK_CLICKED` без отслеживаемой кнопки молча дают `false` (выход `no`), публикацию не блокируют. Список `kind`/`op` — в schema.md.
|
|
48
|
+
- `CONDITION`: `match` ∈ {ALL, ANY} (`CONDITION_BAD_MATCH`); ≥1 элемент в `conditions` (`CONDITION_EMPTY`). Для `kind:"TAG"` — `value` ∈ `[a-z0-9_-]{1,64}` (`CONDITION_BAD_TAG`); для `kind:"VARIABLE"` — `key` ∈ `[a-z_][a-z0-9_]{0,63}` (`CONDITION_BAD_KEY`). Остальные `kind` (UTM/NAME/EMAIL/PHONE/USERNAME/SUBSCRIBED/LINK_CLICKED/BUSINESS/CURRENT_*/DAY_OF_WEEK) бэкенд проверяет в рантайме — некорректный `kind`/`op`, нечисловой `key` у `SUBSCRIBED` или `key` у `LINK_CLICKED` без отслеживаемой кнопки молча дают `false` (выход `no`), публикацию не блокируют. Список `kind`/`op` — в schema.md.
|
|
49
49
|
- `CALL_WEBHOOK`: `url` обязателен (`WEBHOOK_NO_URL`), http(s):// (`WEBHOOK_BAD_SCHEME`).
|
|
50
50
|
- `DELAY`: `kind` ∈ {FIXED,UNTIL,TOMORROW} (`DELAY_BAD_KIND`). `FIXED` — `durationSec>0` (секунды) ИЛИ `duration>0` (+`unit` ∈ {MINUTES,HOURS,DAYS}), иначе `DELAY_BAD_DURATION`. `TOMORROW` — `time` = `HH:mm` (`DELAY_BAD_TIME`). `UNTIL` — `isoTimestamp`: ISO-8601 или шаблон `{{var.x}}` (`DELAY_BAD_TIMESTAMP`; рантайм читает только его, не `isoDate`+`time`); опц. `offset` вида `-24h`/`-30m`/`+1d` (`DELAY_BAD_OFFSET`) и `timezone` (`DELAY_BAD_TIMEZONE`).
|
|
51
51
|
- `SET_VARIABLE`: `key` ∈ `[a-z_][a-z0-9_]{0,63}` (`VAR_BAD_KEY`). `ADD_TAG`/`REMOVE_TAG`: `tag` ∈ `[a-z0-9_-]{1,64}` (`TAG_BAD_NAME`).
|
|
@@ -59,6 +59,7 @@ const COND_OPS = {
|
|
|
59
59
|
USERNAME: ["EQUALS", "CONTAINS"],
|
|
60
60
|
SUBSCRIBED: ["SUBSCRIBED", "NOT_SUBSCRIBED"],
|
|
61
61
|
LINK_CLICKED: ["CLICKED", "NOT_CLICKED"],
|
|
62
|
+
BUSINESS: ["IS", "IS_NOT"],
|
|
62
63
|
CURRENT_DATE: ["BEFORE", "AFTER", "EQUALS"],
|
|
63
64
|
CURRENT_TIME: ["BETWEEN"],
|
|
64
65
|
DAY_OF_WEEK: ["IN"],
|
|
@@ -223,16 +224,16 @@ for (const n of nodes) {
|
|
|
223
224
|
"edited_channel_post", "chat_member", "chat_join_request", "my_chat_member",
|
|
224
225
|
"message_reaction", "message_reaction_count", "chat_boost", "removed_chat_boost",
|
|
225
226
|
"poll", "poll_answer", "inline_query", "chosen_inline_result", "shipping_query",
|
|
226
|
-
"pre_checkout_query"]);
|
|
227
|
-
const DEAD = new Set(["business_connection", "
|
|
228
|
-
"deleted_business_messages", "chat_shared", "users_shared", "write_access_allowed",
|
|
227
|
+
"pre_checkout_query", "business_message", "edited_business_message", "deleted_business_messages"]);
|
|
228
|
+
const DEAD = new Set(["business_connection", "chat_shared", "users_shared", "write_access_allowed",
|
|
229
229
|
"purchased_paid_media"]);
|
|
230
230
|
const ev = String(c.event || "");
|
|
231
231
|
if (blank(ev)) errors.push(`TG_EVENT_NO_EVENT: ${who} — нужен event (напр. chat_member, message_reaction).`);
|
|
232
232
|
else if (DEAD.has(ev)) errors.push(`TG_EVENT_UNSUPPORTED: ${who} — событие «${ev}» рантайм не разбирает, триггер не сработает.`);
|
|
233
233
|
else if (!LIVE.has(ev)) warns.push(`${who}: событие «${ev}» не из списка поддержанных — проверь, что рантайм его размечает.`);
|
|
234
234
|
// Фильтр по тексту есть только у событий с текстом сообщения.
|
|
235
|
-
const TEXTY = new Set(["message", "edited_message", "channel_post", "edited_channel_post"
|
|
235
|
+
const TEXTY = new Set(["message", "edited_message", "channel_post", "edited_channel_post",
|
|
236
|
+
"business_message", "edited_business_message"]);
|
|
236
237
|
const f = c.filter && typeof c.filter === "object" ? c.filter : {};
|
|
237
238
|
if (!blank(f.text) && !TEXTY.has(ev)) warns.push(`${who}: фильтр по тексту у события «${ev}» игнорируется — текста в апдейте нет.`);
|
|
238
239
|
if (!blank(f.status) && ev !== "chat_member" && ev !== "my_chat_member") warns.push(`${who}: фильтр по статусу работает только у chat_member/my_chat_member.`);
|
package/src/index.mjs
CHANGED
|
@@ -1056,7 +1056,7 @@ const TOOLS = [
|
|
|
1056
1056
|
{ name: "graph_version_rollback", description: "Откатить сценарий на версию (POST /api/bots/graphs/{graphId}/versions/{version}/rollback). Откат = публикация снимка: у ЖИВОГО графа бот переходит на ту версию сразу, и она проходит те же проверки, что edit_graph_live (валидатор, платные блоки, лимит блоков, платформа) — при ошибках HTTP 422 со всеми code@nodeId, граф НЕ изменён. Состояние ДО отката сервер сам сохраняет отдельной версией (savedCurrentAs), результат — версией reason ROLLBACK (newVersion), так что откат обратим тем же инструментом. Узлы получают ТЕ ЖЕ id, что были в снимке. Это действие на живом боте: перед вызовом покажи пользователю graph_versions_list (version:N — что изменится) и получи согласие. Нужно право write:bots.", inputSchema: { type: "object", properties: { graphId: { type: "string" }, version: { type: "integer", description: "Номер версии из graph_versions_list" } }, required: ["graphId", "version"] } },
|
|
1057
1057
|
{ name: "unpublish_graph", description: "Снять ДОПОЛНИТЕЛЬНЫЙ сценарий с публикации (POST /api/bots/graphs/{graphId}/unpublish): он снова черновик, лендинги и ссылки на него ведут в основной сценарий, подписчики в нём возвращаются в основной, его задержки отменяются. Основной сценарий так не снимается — HTTP 409.", inputSchema: { type: "object", properties: { graphId: { type: "string" } }, required: ["graphId"] } },
|
|
1058
1058
|
{ name: "import_funnel", description: "Всё за раз: создать граф, залить узлы/рёбра, (опц.) dry-run /start, опубликовать. Граф можно передать инлайном (graph) или файлом (graphFile).", inputSchema: { type: "object", properties: { botId: { type: "string" }, name: { type: "string" }, graph: { type: "object" }, graphFile: { type: "string", description: "Путь к локальному JSON графа вместо инлайн graph; поддерживается ~" }, dryRun: { type: "boolean" }, publish: { type: "boolean" } }, required: ["botId"] } },
|
|
1059
|
-
{ name: "list_templates", description: "Список готовых шаблонов воронок (id, имя,
|
|
1059
|
+
{ name: "list_templates", description: "Список готовых шаблонов воронок (id, имя, описание, ниша, каналы, nameEn/descriptionEn, hasAi). Около 30 шаблонов под русские ниши (запись в салон и клинику, квалификация в недвижимости, лид-магнит школы, заказ в магазине, бронь в ресторане, сбор отзывов и др.) плюс универсальные. Можно сузить: niche (beauty, medical, realty, education, ecommerce, fitness, restaurant, auto, legal, reviews, universal), channel (TELEGRAM, MAX, VK), query (подстрока в названии и описании). Те же шаблоны открыты публично на /templates.", inputSchema: { type: "object", properties: { niche: { type: "string" }, channel: { type: "string" }, query: { type: "string" } } } },
|
|
1060
1060
|
{ name: "create_graph_from_template", description: "Создать граф (DRAFT) из шаблона (см. list_templates). Возвращает граф с id — дальше правь через update_graph.", inputSchema: { type: "object", properties: { botId: { type: "string" }, templateId: { type: "string" }, name: { type: "string" } }, required: ["botId", "templateId"] } },
|
|
1061
1061
|
{ name: "rename_graph", description: "Переименовать сценарий (работает и для опубликованных — имя не влияет на исполнение).", inputSchema: { type: "object", properties: { graphId: { type: "string" }, name: { type: "string" } }, required: ["graphId", "name"] } },
|
|
1062
1062
|
{ name: "clone_graph", description: "Склонировать граф в новый DRAFT «… (copy)» — безопасно итерировать поверх опубликованного. Клон сценария-вебхука получает собственный путь и секрет вебхука.", inputSchema: { type: "object", properties: { graphId: { type: "string" } }, required: ["graphId"] } },
|
|
@@ -1443,7 +1443,15 @@ async function handleCall(params) {
|
|
|
1443
1443
|
}
|
|
1444
1444
|
return okResult({ graphId, steps });
|
|
1445
1445
|
}
|
|
1446
|
-
case "list_templates":
|
|
1446
|
+
case "list_templates": {
|
|
1447
|
+
const all = await api("/api/bots/graph-templates");
|
|
1448
|
+
if (!Array.isArray(all) || !(a.niche || a.channel || a.query)) return okResult(all);
|
|
1449
|
+
const q = String(a.query || "").trim().toLowerCase();
|
|
1450
|
+
return okResult(all.filter((t) =>
|
|
1451
|
+
(!a.niche || t.niche === a.niche)
|
|
1452
|
+
&& (!a.channel || (Array.isArray(t.channels) && t.channels.includes(String(a.channel).toUpperCase())))
|
|
1453
|
+
&& (!q || `${t.name} ${t.description} ${t.nameEn || ""} ${t.descriptionEn || ""}`.toLowerCase().includes(q))));
|
|
1454
|
+
}
|
|
1447
1455
|
case "create_graph_from_template":
|
|
1448
1456
|
return okResult(await api(`/api/bots/${a.botId}/graphs/from-template`, { method: "POST", body: { templateId: a.templateId, name: a.name } }));
|
|
1449
1457
|
case "rename_graph":
|