@retensy/mcp 0.35.0 → 0.36.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
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retensy/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.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": {
|
|
@@ -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,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-ботов всё работает как описано.
|
|
@@ -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.`);
|