zaytsv-bot-graph-mcp 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zaytsv-bot-graph-mcp",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "MCP server to build and publish Telegram, MAX and Instagram bot funnels/automations in the zaytsv /bots service. Zero dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": { "zaytsv-bot-graph-mcp": "src/index.mjs" },
|
|
@@ -39,10 +39,12 @@
|
|
|
39
39
|
|
|
40
40
|
#### Instagram (только для IG-ботов)
|
|
41
41
|
IG-боты не поддерживают команды (`/start`). Вход — через взаимодействие с контентом или директ:
|
|
42
|
-
- `TRIGGER_IG_DM` — `{ "isRoot": true }` — входящее сообщение в Instagram Direct. Это дефолтный триггер нового IG-графа (бэкенд сеет его при создании).
|
|
43
|
-
- `TRIGGER_IG_COMMENT` — `{ "isRoot": true }` — комментарий к посту или Reel бота.
|
|
44
|
-
- `TRIGGER_IG_STORY_REPLY` — `{ "isRoot": true }` — ответ на историю бота.
|
|
45
|
-
- `TRIGGER_IG_STORY_MENTION` — `{ "isRoot": true }` — упоминание бота в истории подписчика.
|
|
42
|
+
- `TRIGGER_IG_DM` — `{ "isRoot": true, "keywords": "хочу, каталог" }` — входящее сообщение в Instagram Direct. Это дефолтный триггер нового IG-графа (бэкенд сеет его при создании). `keywords` (опц.) — список через запятую или с новой строки; **регистронезависимо, совпадение по вхождению (contains)**; пусто = любое сообщение.
|
|
43
|
+
- `TRIGGER_IG_COMMENT` — `{ "isRoot": true, "keywords": "купить, цена" }` — комментарий к посту или Reel бота. `keywords` (опц.) — список через запятую или с новой строки; регистронезависимый contains; пусто = любой комментарий.
|
|
44
|
+
- `TRIGGER_IG_STORY_REPLY` — `{ "isRoot": true, "keywords": "хочу, вопрос" }` — ответ на историю бота. `keywords` (опц.) — список через запятую или с новой строки; регистронезависимый contains; пусто = любой ответ.
|
|
45
|
+
- `TRIGGER_IG_STORY_MENTION` — `{ "isRoot": true }` — упоминание бота в истории подписчика. **Текста нет → фильтрация по ключевым словам не применяется**; срабатывает на каждое упоминание независимо от содержимого истории.
|
|
46
|
+
|
|
47
|
+
> **Разные слова — разные сценарии:** добавь **несколько триггеров одного типа** с разными `keywords`; бэкенд берёт **первый совпавший по порядку узлов**. Триггер-«ловушку» с пустыми `keywords` (ловит всё) ставь **последним** — иначе он заблокирует все нижестоящие ключевые слова.
|
|
46
48
|
|
|
47
49
|
Для всех IG-триггеров реакция бота отправляется через Instagram Messaging API в течение **24-часового окна** после последнего входящего действия пользователя.
|
|
48
50
|
|
|
@@ -89,6 +89,12 @@ Instagram доставляет сообщения только в течение
|
|
|
89
89
|
|
|
90
90
|
`inputKind == null` (не задан) — допустимо (дефолт = текст).
|
|
91
91
|
|
|
92
|
+
### Фильтрация по ключевым словам (`keywords`) — только информация, не ошибка
|
|
93
|
+
|
|
94
|
+
`TRIGGER_IG_COMMENT`, `TRIGGER_IG_DM`, `TRIGGER_IG_STORY_REPLY` поддерживают опциональное поле `keywords` в config. Значение — произвольная строка (список через запятую или с новой строки). Бэкенд делает **регистронезависимый contains** (вхождение): срабатывает, если входящий текст содержит хотя бы одно из ключевых слов/фраз (по вхождению, без границ слова). Пустое или отсутствующее `keywords` = совпадение с любым событием. Никаких жёстких кодов ошибок для `keywords` нет — любая строка валидна.
|
|
95
|
+
|
|
96
|
+
`TRIGGER_IG_STORY_MENTION` фильтрацию по ключевым словам **не поддерживает**: Instagram не передаёт текст упоминания боту, поэтому `keywords` игнорируется (валидатор выдаёт предупреждение, не ошибку).
|
|
97
|
+
|
|
92
98
|
---
|
|
93
99
|
|
|
94
100
|
## Локальная проверка
|
|
@@ -98,4 +104,4 @@ Instagram доставляет сообщения только в течение
|
|
|
98
104
|
```bash
|
|
99
105
|
node validate.mjs <import.json> --platform=INSTAGRAM
|
|
100
106
|
```
|
|
101
|
-
Добавляет проверки `IG_NODE_UNSUPPORTED`, `IG_DELAY_OVER_24H`, `IG_INPUT_UNSUPPORTED` поверх всех
|
|
107
|
+
Добавляет проверки `IG_NODE_UNSUPPORTED`, `IG_DELAY_OVER_24H`, `IG_INPUT_UNSUPPORTED` поверх всех обычных, а также предупреждение (WARNING) для `TRIGGER_IG_STORY_MENTION` с заполненным `keywords` (поле игнорируется).
|
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
|
|
6
6
|
import { readFileSync } from "node:fs";
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
// Разобрать --platform=... (регистронезависимо)
|
|
8
|
+
// Разобрать аргументы порядконезависимо: первый не-флаг = путь; --platform=... в любом месте
|
|
9
|
+
let path = "";
|
|
12
10
|
let platform = "TELEGRAM";
|
|
13
|
-
for (const arg of process.argv.slice(
|
|
11
|
+
for (const arg of process.argv.slice(2)) {
|
|
14
12
|
const m = arg.match(/^--platform=([A-Za-z]+)$/);
|
|
15
|
-
if (m) platform = m[1].toUpperCase();
|
|
13
|
+
if (m) { platform = m[1].toUpperCase(); }
|
|
14
|
+
else if (!arg.startsWith("--") && !path) { path = arg; }
|
|
16
15
|
}
|
|
16
|
+
if (!path) { console.error("Usage: node validate.mjs <import.json> [--platform=TELEGRAM|MAX|INSTAGRAM]"); process.exit(2); }
|
|
17
17
|
if (!["TELEGRAM", "MAX", "INSTAGRAM"].includes(platform)) {
|
|
18
18
|
console.error(`❌ Неизвестная платформа: ${platform}. Допустимые: TELEGRAM, MAX, INSTAGRAM`);
|
|
19
19
|
process.exit(2);
|
|
@@ -314,9 +314,9 @@ if (platform === "INSTAGRAM") {
|
|
|
314
314
|
// только FIXED считаем конкретно; TOMORROW/UNTIL — всегда > 24h (Long.MAX_VALUE)
|
|
315
315
|
if (!c || c.kind !== "FIXED") return Infinity;
|
|
316
316
|
// legacy: durationSec
|
|
317
|
-
if (typeof c.durationSec === "number"
|
|
317
|
+
if (typeof c.durationSec === "number") return c.durationSec;
|
|
318
318
|
// new format: duration + unit
|
|
319
|
-
if (typeof c.duration === "number"
|
|
319
|
+
if (typeof c.duration === "number") {
|
|
320
320
|
const u = String(c.unit || "");
|
|
321
321
|
if (u === "MINUTES") return c.duration * 60;
|
|
322
322
|
if (u === "HOURS") return c.duration * 3600;
|
|
@@ -353,6 +353,11 @@ if (platform === "INSTAGRAM") {
|
|
|
353
353
|
errors.push(`IG_DELAY_OVER_24H: ${who} — задержка больше 24ч недопустима для Instagram (24-часовое окно доставки)`);
|
|
354
354
|
}
|
|
355
355
|
}
|
|
356
|
+
|
|
357
|
+
// 4) TRIGGER_IG_STORY_MENTION: keywords игнорируется — текста упоминания нет
|
|
358
|
+
if (type === "TRIGGER_IG_STORY_MENTION" && !blank(c.keywords)) {
|
|
359
|
+
warns.push(`${who}: TRIGGER_IG_STORY_MENTION не фильтруется по ключевым словам (у упоминания нет текста) — keywords игнорируется`);
|
|
360
|
+
}
|
|
356
361
|
}
|
|
357
362
|
}
|
|
358
363
|
|
package/src/index.mjs
CHANGED
|
@@ -22,7 +22,7 @@ import os from "node:os";
|
|
|
22
22
|
import fs from "node:fs";
|
|
23
23
|
import path from "node:path";
|
|
24
24
|
|
|
25
|
-
const VERSION = "0.7.
|
|
25
|
+
const VERSION = "0.7.2";
|
|
26
26
|
const BASE = (process.env.ZAYTSV_BASE_URL || "https://zaytsv.ru").replace(/\/+$/, "");
|
|
27
27
|
const CONFIG_DIR = path.join(os.homedir(), ".zaytsv-bot-graph");
|
|
28
28
|
const TOKEN_FILE = path.join(CONFIG_DIR, "token");
|
|
@@ -116,7 +116,7 @@ const TOOLS = [
|
|
|
116
116
|
{ 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"] } },
|
|
117
117
|
{ name: "rename_graph", description: "Переименовать сценарий (работает и для опубликованных — имя не влияет на исполнение).", inputSchema: { type: "object", properties: { graphId: { type: "string" }, name: { type: "string" } }, required: ["graphId", "name"] } },
|
|
118
118
|
{ name: "clone_graph", description: "Склонировать граф в новый DRAFT «… (copy)» — безопасно итерировать поверх опубликованного.", inputSchema: { type: "object", properties: { graphId: { type: "string" } }, required: ["graphId"] } },
|
|
119
|
-
{ name: "copy_graph", description: "Скопировать граф в ДРУГОГО бота (в т.ч. на другую
|
|
119
|
+
{ name: "copy_graph", description: "Скопировать граф в ДРУГОГО бота (в т.ч. на другую платформу). Возвращает {graphId, sourcePlatform, targetPlatform, notes[]}. notes[] помечают, что адаптировано (severity=TRANSFORM, напр. вопрос-контакт → ввод телефона текстом), что требует ручной правки (MANUAL, напр. условие SUBSCRIBED в MAX) и особенности платформы (INFO). Авто-адаптация узлов реализована для Telegram⇄MAX; при копировании в/из Instagram-бота граф копируется без трансформаций — несовместимые узлы будут отмечены при публикации (IG-allowlist). preview=true — только проверка совместимости, без копирования. Тот же бот запрещён (для дублирования есть clone_graph).", inputSchema: { type: "object", properties: { graphId: { type: "string" }, targetBotId: { type: "string", description: "id бота-получателя (см. list_bots)" }, preview: { type: "boolean", description: "true = только отчёт о совместимости, ничего не сохраняется" } }, required: ["graphId", "targetBotId"] } },
|
|
120
120
|
{ name: "delete_graph", description: "Удалить граф. Активный (опубликованный и назначенный боту) удалить нельзя — будет 409; сначала переключи активный через set_active_graph.", inputSchema: { type: "object", properties: { graphId: { type: "string" } }, required: ["graphId"] } },
|
|
121
121
|
{ name: "set_active_graph", description: "Назначить, какой опубликованный граф активен у бота (переключение живого сценария без перепубликации).", inputSchema: { type: "object", properties: { botId: { type: "string" }, graphId: { type: "string" } }, required: ["botId", "graphId"] } },
|
|
122
122
|
];
|