Files
fitcrm/docs/plan-telegram-bot-module.md
root fe07f3e13f feat(telegram-bot): add Telegram Bot Constructor module
Full-stack module for building Telegram chatbot scenarios in CRM.

Backend (NestJS):
- 6 Prisma models: TgBot, TgBotScenario, TgBotSubscriber, TgBotMessage,
  TgBotBroadcast, TgBotWebhook
- JSON scenario engine with 6 node types: message, condition, action,
  input, delay, referral
- Referral mechanics: deep links, invite tracking, friend routing
- Broadcasts with 10 audience filters, batch sending with rate limiting
- Outgoing webhooks: 9 event types, HMAC-SHA256 signing, retry
- CRM integration: create deals from bot scenarios
- Analytics: subscriber stats, referral metrics, funnel, daily growth
- 12 services, 4 BullMQ processors, 7 controllers, ~35 REST endpoints

Frontend (web-club-admin):
- 8 pages: bot list, dashboard, subscribers, broadcasts, scenario editor,
  webhooks, analytics
- Visual scenario editor with React Flow: 6 custom node types
- Sidebar: module-aware visibility (Hide, Don't Block)

Includes Powerhouse Gym referral scenario (24 nodes) and user docs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 10:40:03 +00:00

36 KiB
Raw Permalink Blame History

Модуль "Telegram Bot Constructor" — План разработки

1. Концепция

Универсальный конструктор чат-бот сценариев в Telegram, встроенный в CRM. Админ клуба через UI собирает цепочку шагов (ноды), сценарий сохраняется в JSON, движок бота выполняет его в рантайме. Первый кейс — реферальный бот Powerhouse Gym.

Принцип: конструктор покрывает любой сценарий — от простого приветствия до многошаговой реферальной программы с ветвлениями, условиями и интеграциями.


2. Архитектура JSON-сценария

2.1 Структура сценария

{
  "id": "uuid",
  "version": 1,
  "name": "Реферальный бот Powerhouse",
  "startNodeId": "node_welcome",
  "variables": {
    "invited_count": { "type": "number", "default": 0 },
    "selected_club": { "type": "string", "default": "" },
    "bonus_granted": { "type": "boolean", "default": false },
    "phone": { "type": "string", "default": "" },
    "referrer_chat_id": { "type": "string", "default": "" }
  },
  "nodes": { ... },
  "edges": [ ... ]
}

2.2 Типы нод

Тип Назначение
message Отправить текст/фото с кнопками (inline/reply)
condition Ветвление по переменной или выражению
action Выполнить действие (webhook, set variable, API)
input Ждать ввод от пользователя (текст, телефон)
delay Пауза / таймер / напоминание
referral Генерация реф-ссылки, подсчёт приглашённых
broadcast Точка входа для рассылки (не часть основного flow)

2.3 Пример ноды message

{
  "id": "node_welcome",
  "type": "message",
  "data": {
    "text": "👋 Привет! Это бот фитнес-клуба {{club_name}}.\n\nСейчас можем предложить вам **бесплатную персональную тренировку**!",
    "image": null,
    "buttons": [
      {
        "text": "🏋️ Как получить тренировку",
        "action": "goto",
        "target": "node_offer",
      },
      {
        "text": "📍 О клубе",
        "action": "goto",
        "target": "node_about_club",
      },
    ],
    "buttonsLayout": "inline", // "inline" | "reply" | "remove"
  },
  "next": null, // null = ждём нажатия кнопки
}

2.4 Пример ноды condition

{
  "id": "node_check_referrals",
  "type": "condition",
  "data": {
    "rules": [
      {
        "condition": "invited_count >= 2",
        "target": "node_bonus_granted",
      },
      {
        "condition": "invited_count == 1",
        "target": "node_one_more",
      },
    ],
    "default": "node_send_referral_link",
  },
}

2.5 Пример ноды action

{
  "id": "node_send_to_crm",
  "type": "action",
  "data": {
    "actions": [
      {
        "type": "webhook",
        "url": "{{webhook_url}}",
        "method": "POST",
        "body": {
          "event": "bot.lead.created",
          "phone": "{{phone}}",
          "club": "{{selected_club}}",
          "source": "telegram_bot",
          "referrer": "{{referrer_chat_id}}",
        },
      },
      {
        "type": "set_variable",
        "variable": "bonus_granted",
        "value": true,
      },
      {
        "type": "crm_deal",
        "pipeline": "{{default_pipeline}}",
        "fields": {
          "name": "TG Bot: {{first_name}}",
          "phone": "{{phone}}",
        },
      },
    ],
  },
  "next": "node_thank_you",
}

2.6 Пример ноды input

{
  "id": "node_get_phone",
  "type": "input",
  "data": {
    "prompt": "Оставьте номер телефона — менеджер предложит удобное время визита.",
    "inputType": "phone", // "text" | "phone" | "email" | "choice"
    "saveAs": "phone", // имя переменной
    "requestContact": true, // кнопка "Поделиться контактом"
    "validation": "^\\+?[0-9]{10,15}$",
    "errorMessage": "Введите корректный номер телефона",
  },
  "next": "node_send_to_crm",
}

2.7 Пример ноды delay

{
  "id": "node_reminder_30min",
  "type": "delay",
  "data": {
    "duration": "30m", // "10m", "1h", "24h", "3d"
    "cancelOn": "invited_count >= 2", // отмена при выполнении условия
    "message": {
      "text": "⏰ Пока никто из друзей не оставил заявку.\nПопробуйте отправить ссылку ещё одному другу!",
      "buttons": [
        { "text": "📤 Отправить ещё раз", "action": "goto", "target": "node_send_referral_link" },
      ],
    },
  },
  "next": "node_check_referrals",
}

2.8 Пример ноды referral

{
  "id": "node_referral_setup",
  "type": "referral",
  "data": {
    "generateLink": true,
    "linkPrefix": "https://t.me/{{bot_username}}?start=ref_",
    "trackVariable": "invited_count",
    "targetCount": 2,
    "onReach": "node_bonus_granted",
    "friendStartNode": "node_friend_welcome", // куда попадает друг
  },
  "next": "node_send_referral_link",
}

3. Модели данных (Prisma)

3.1 Конфигурация бота

model TgBot {
  id          String   @id @default(uuid())
  clubId      String
  club        Club     @relation(fields: [clubId], references: [id])
  name        String                          // "Реферальный бот Powerhouse"
  token       String                          // Bot API token (encrypted)
  username    String?                         // @bot_username
  isActive    Boolean  @default(false)
  webhookUrl  String?                         // Telegram webhook URL
  scenarioId  String?
  scenario    TgBotScenario? @relation(fields: [scenarioId], references: [id])
  settings    Json     @default("{}")         // { welcomeMessage, defaultLanguage, ... }
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  subscribers TgBotSubscriber[]
  messages    TgBotMessage[]
  broadcasts  TgBotBroadcast[]
  webhooks    TgBotWebhook[]

  @@index([clubId])
}

3.2 Сценарий

model TgBotScenario {
  id          String   @id @default(uuid())
  clubId      String
  club        Club     @relation(fields: [clubId], references: [id])
  botId       String?
  name        String
  description String?
  scenario    Json                            // Полный JSON сценария
  version     Int      @default(1)
  isPublished Boolean  @default(false)
  isDraft     Boolean  @default(true)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  bots        TgBot[]

  @@index([clubId])
}

3.3 Подписчики (ключевая таблица)

model TgBotSubscriber {
  id               String    @id @default(uuid())
  clubId           String
  club             Club      @relation(fields: [clubId], references: [id])
  botId            String
  bot              TgBot     @relation(fields: [botId], references: [id])
  chatId           String                     // Telegram chat ID
  telegramUserId   String?                    // Telegram user ID
  username         String?                    // @username
  firstName        String?
  lastName         String?
  phone            String?                    // номер телефона (если есть)
  isRegistered     Boolean   @default(false)  // зарегистрировался или нет
  bonusGranted     Boolean   @default(false)  // получал бонус или нет
  mailingConsent   Boolean   @default(false)  // давал согласие на рассылку
  isSubscribed     Boolean   @default(true)   // подписан сейчас или нет
  referrerId       String?                    // от кого пришёл (ID подписчика)
  referrer         TgBotSubscriber? @relation("Referrals", fields: [referrerId], references: [id])
  referrals        TgBotSubscriber[] @relation("Referrals")
  invitedCount     Int       @default(0)      // сколько пригласил
  source           String?                    // источник трафика (qr, social, website)
  currentNodeId    String?                    // текущая нода в сценарии
  variables        Json      @default("{}")   // переменные сессии бота
  tags             String[]  @default([])     // теги для сегментации
  registeredAt     DateTime?                  // дата регистрации в боте
  lastActiveAt     DateTime?
  blockedAt        DateTime?                  // когда заблокировал бота
  createdAt        DateTime  @default(now())
  updatedAt        DateTime  @updatedAt

  messages         TgBotMessage[]

  @@unique([botId, chatId])
  @@index([clubId])
  @@index([botId])
  @@index([referrerId])
  @@index([phone])
  @@index([isSubscribed, mailingConsent])
}

3.4 История сообщений

model TgBotMessage {
  id            String   @id @default(uuid())
  botId         String
  bot           TgBot    @relation(fields: [botId], references: [id])
  subscriberId  String
  subscriber    TgBotSubscriber @relation(fields: [subscriberId], references: [id])
  direction     TgMessageDirection            // IN | OUT
  nodeId        String?                       // из какой ноды сценария
  text          String?
  payload       Json?                         // кнопка, контакт, фото и т.д.
  telegramMsgId Int?                          // ID сообщения в Telegram
  createdAt     DateTime @default(now())

  @@index([botId, subscriberId])
  @@index([createdAt])
}

enum TgMessageDirection {
  IN
  OUT
}

3.5 Рассылки

model TgBotBroadcast {
  id            String   @id @default(uuid())
  clubId        String
  club          Club     @relation(fields: [clubId], references: [id])
  botId         String
  bot           TgBot    @relation(fields: [botId], references: [id])
  name          String
  message       Json                          // { text, image, buttons }
  filters       Json                          // фильтры аудитории (ниже)
  status        TgBroadcastStatus @default(DRAFT)
  scheduledAt   DateTime?                     // запланированная отправка
  startedAt     DateTime?
  completedAt   DateTime?
  totalCount    Int      @default(0)          // сколько подписчиков в выборке
  sentCount     Int      @default(0)
  failedCount   Int      @default(0)
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt

  @@index([clubId])
  @@index([botId])
}

enum TgBroadcastStatus {
  DRAFT
  SCHEDULED
  SENDING
  COMPLETED
  CANCELLED
}

Формат фильтров рассылки:

{
  "clubs": ["uuid1", "uuid2"], // фильтрация по клубам
  "source": ["qr", "website"], // от кого пришёл
  "isRegistered": true, // зарегистрировался
  "bonusGranted": false, // получал/не получал бонус
  "mailingConsent": true, // согласие на рассылку
  "isSubscribed": true, // подписан сейчас
  "invitedCountMin": 1, // сколько пригласил (от)
  "invitedCountMax": null, // сколько пригласил (до)
  "registeredAfter": "2026-01-01", // дата регистрации от
  "registeredBefore": "2026-03-01", // дата регистрации до
  "tags": ["vip", "active"], // по тегам
}

3.6 Вебхуки бота (уведомления на внешние URL)

model TgBotWebhook {
  id          String   @id @default(uuid())
  clubId      String
  botId       String
  bot         TgBot    @relation(fields: [botId], references: [id])
  url         String                          // URL для отправки
  secret      String                          // HMAC secret
  events      String[]                        // ["subscriber.created", "referral.completed", ...]
  isActive    Boolean  @default(true)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  @@index([botId])
}

События для вебхуков:

  • subscriber.created — новый подписчик
  • subscriber.blocked — заблокировал бота
  • subscriber.phone_shared — поделился телефоном
  • referral.invited — пригласил друга
  • referral.completed — условие реферала выполнено
  • bonus.granted — бонус начислен
  • lead.created — заявка создана (выбрал клуб)
  • broadcast.completed — рассылка завершена
  • scenario.completed — подписчик прошёл сценарий до конца

4. Архитектура модулей (API)

apps/api/src/modules/telegram-bot/
├── telegram-bot.module.ts          # NestJS модуль
├── telegram-bot.controller.ts      # REST API для управления ботами
├── scenario.controller.ts          # CRUD сценариев
├── subscriber.controller.ts        # Управление подписчиками
├── broadcast.controller.ts         # Рассылки
├── webhook-input.controller.ts     # Приём обновлений от Telegram API
├── dto/
│   ├── create-bot.dto.ts
│   ├── update-bot.dto.ts
│   ├── create-scenario.dto.ts
│   ├── update-scenario.dto.ts
│   ├── create-broadcast.dto.ts
│   ├── subscriber-filter.dto.ts
│   └── bot-webhook.dto.ts
├── services/
│   ├── bot-manager.service.ts      # CRUD ботов, регистрация webhook в TG
│   ├── scenario.service.ts         # CRUD и валидация сценариев JSON
│   ├── scenario-engine.service.ts  # 🔑 Движок: выполняет JSON-сценарий
│   ├── subscriber.service.ts       # CRUD подписчиков, фильтрация
│   ├── referral.service.ts         # Реферальная механика
│   ├── broadcast.service.ts        # Рассылки с фильтрами
│   ├── telegram-api.service.ts     # Обёртка над Telegram Bot API
│   └── bot-webhook.service.ts      # Отправка событий на внешние URL
├── processors/
│   ├── telegram-message.processor.ts  # BullMQ: обработка входящих
│   ├── broadcast.processor.ts         # BullMQ: отправка рассылок
│   ├── bot-webhook.processor.ts       # BullMQ: доставка вебхуков
│   └── delay-node.processor.ts        # BullMQ: отложенные ноды
└── interfaces/
    ├── scenario.interface.ts       # Типы JSON-сценария
    ├── node.interface.ts           # Типы нод
    └── engine-context.interface.ts # Контекст выполнения

5. Ключевые компоненты

5.1 Scenario Engine (ядро)

Движок получает входящее событие (сообщение, callback, контакт) и:

  1. Находит подписчика по chatId
  2. Загружает сценарий бота
  3. Определяет текущую ноду (subscriber.currentNodeId)
  4. Выполняет ноду → получает следующую
  5. Рекурсивно проходит action и condition ноды (без ожидания ввода)
  6. Останавливается на message (ждём кнопку) или input (ждём текст)
  7. Сохраняет currentNodeId + variables
Входящее сообщение
  → telegram-message.processor (BullMQ)
    → scenario-engine.service
      → resolve current node
      → execute node chain (action → condition → message)
      → send Telegram message
      → save state (currentNodeId, variables)
      → emit events (webhook)

5.2 Referral Service

  • Генерирует deep link: https://t.me/bot?start=ref_{subscriberId}
  • При /start ref_XXX — записывает referrerId новому подписчику
  • Инкрементит invitedCount у реферера
  • Проверяет условие (достиг targetCount) → переключает на onReach ноду
  • Отправляет реферу промежуточные уведомления ("Осталось ещё 1 человек")

5.3 Broadcast Service

  • Строит SQL-запрос по фильтрам из JSON
  • Создаёт BullMQ job'ы пачками (50 сообщений/сек — лимит TG API)
  • Rate limiter: 30 msg/sec per bot, очередь с задержкой
  • Обновляет sentCount/failedCount в реальном времени
  • Поддержка отмены рассылки (CANCELLED)

6. API Endpoints

Боты

POST   /telegram-bot/bots                    # Создать бота
GET    /telegram-bot/bots                    # Список ботов клуба
GET    /telegram-bot/bots/:id                # Получить бота
PATCH  /telegram-bot/bots/:id                # Обновить бота
DELETE /telegram-bot/bots/:id                # Удалить бота
POST   /telegram-bot/bots/:id/activate       # Активировать (set webhook)
POST   /telegram-bot/bots/:id/deactivate     # Деактивировать

Сценарии

POST   /telegram-bot/scenarios               # Создать сценарий
GET    /telegram-bot/scenarios               # Список сценариев
GET    /telegram-bot/scenarios/:id           # Получить сценарий (с JSON)
PATCH  /telegram-bot/scenarios/:id           # Обновить JSON сценария
POST   /telegram-bot/scenarios/:id/publish   # Опубликовать (isDraft=false)
POST   /telegram-bot/scenarios/:id/validate  # Валидация JSON (без сохранения)
POST   /telegram-bot/scenarios/:id/duplicate # Дублировать сценарий

Подписчики

GET    /telegram-bot/bots/:botId/subscribers           # Таблица подписчиков
GET    /telegram-bot/bots/:botId/subscribers/:id        # Карточка подписчика
GET    /telegram-bot/bots/:botId/subscribers/:id/messages  # История переписки
GET    /telegram-bot/bots/:botId/subscribers/stats      # Статистика
PATCH  /telegram-bot/bots/:botId/subscribers/:id        # Редактировать (теги, заметки)
POST   /telegram-bot/bots/:botId/subscribers/:id/reset  # Сбросить сценарий

Рассылки

POST   /telegram-bot/broadcasts                # Создать рассылку
GET    /telegram-bot/broadcasts                # Список рассылок
GET    /telegram-bot/broadcasts/:id            # Статус рассылки
PATCH  /telegram-bot/broadcasts/:id            # Обновить (DRAFT only)
POST   /telegram-bot/broadcasts/:id/send       # Запустить
POST   /telegram-bot/broadcasts/:id/cancel     # Отменить
POST   /telegram-bot/broadcasts/preview-count  # Превью кол-ва получателей

Вебхуки бота

POST   /telegram-bot/bots/:botId/webhooks       # Создать подписку
GET    /telegram-bot/bots/:botId/webhooks        # Список подписок
PATCH  /telegram-bot/bots/:botId/webhooks/:id    # Обновить
DELETE /telegram-bot/bots/:botId/webhooks/:id    # Удалить
POST   /telegram-bot/bots/:botId/webhooks/:id/test  # Тестовая отправка

Telegram Webhook Input (публичный)

POST   /telegram-bot/webhook/:botId             # Incoming updates от TG API

7. UI в админ-панелях

7.1 web-club-admin (Админ клуба)

Маршруты:

/telegram-bot                         # Список ботов
/telegram-bot/new                     # Создание бота
/telegram-bot/:id                     # Настройки бота
/telegram-bot/:id/scenario            # Редактор сценария
/telegram-bot/:id/subscribers         # Таблица подписчиков
/telegram-bot/:id/broadcasts          # Рассылки
/telegram-bot/:id/broadcasts/new      # Создание рассылки
/telegram-bot/:id/webhooks            # Настройка вебхуков
/telegram-bot/:id/analytics           # Аналитика бота

Экран "Редактор сценария" (ключевой):

  • Визуальный редактор нод (drag & drop) на основе React Flow
  • Панель свойств ноды справа
  • Превью текста сообщения с подстановкой переменных
  • Кнопки: Сохранить черновик / Опубликовать / Валидировать
  • Импорт/экспорт JSON

Экран "Таблица подписчиков":

  • Колонки: Chat ID, Имя, Телефон, Зарегистрирован, Бонус, Согласие, Подписан, Источник, Приглашено, Дата
  • Фильтры по всем колонкам
  • Экспорт в XLSX
  • Клик → карточка подписчика с историей переписки

Экран "Рассылки":

  • Форма: текст + кнопки + фильтры аудитории
  • Превью количества получателей
  • Статус отправки в реальном времени

7.2 web-platform-admin (Суперадмин)

  • Обзор всех ботов по всем клубам
  • Управление лимитами модуля (макс. подписчиков, рассылок/мес)
  • Мониторинг активности

8. Спринты разработки

Спринт 1: Фундамент (5-7 дней)

Цель: Базовая инфраструктура модуля, бот отвечает на /start

# Задача Слой
1 Prisma-модели: TgBot, TgBotScenario, TgBotSubscriber, TgBotMessage Schema
2 Миграция БД Schema
3 NestJS модуль telegram-bot со структурой API
4 TelegramApiService — обёртка над Telegram Bot API (sendMessage, setWebhook, getMe) API
5 BotManagerService — CRUD ботов, активация/деактивация API
6 webhook-input.controller.ts — приём updates от Telegram API
7 BullMQ очередь telegram-message + процессор API
8 SubscriberService — создание подписчика при /start API
9 REST контроллер ботов (CRUD) API
10 @RequireModule('telegram-bot') + enum в shared-types API
11 Seed: тестовый бот для dev-окружения API

Definition of Done: Бот зарегистрирован, принимает /start, создаёт подписчика в БД.


Спринт 2: Движок сценариев (7-10 дней)

Цель: JSON-сценарий выполняется в рантайме

# Задача Слой
1 TypeScript интерфейсы для JSON-сценария (все типы нод) Types
2 ScenarioService — CRUD сценариев, валидация JSON API
3 ScenarioEngineService — ядро: загрузка, resolve, execute node chain API
4 Обработка ноды message — отправка текста + кнопки API
5 Обработка ноды condition — eval выражений на переменных API
6 Обработка ноды action — set_variable, webhook API
7 Обработка ноды input — ожидание текста, телефона, requestContact API
8 Обработка callback_query (inline-кнопки) API
9 Сохранение состояния (currentNodeId, variables) в подписчике API
10 Подстановка переменных в текст {{var_name}} API
11 REST контроллер сценариев API
12 Тестовый JSON-сценарий приветствия (3-4 ноды) Test

Definition of Done: Простой сценарий (приветствие → кнопка → условие → ответ) работает в боте.


Спринт 3: Реферальная механика (5-7 дней)

Цель: Полный реферальный цикл из Miro-схемы

# Задача Слой
1 ReferralService — генерация deep links, трекинг API
2 Обработка ноды referral в движке API
3 Обработка /start ref_XXX — привязка реферера API
4 Инкремент invitedCount, уведомления реферу API
5 Ветка друга — отдельный startNode из friendStartNode API
6 Обработка ноды delay — BullMQ delayed jobs API
7 delay-node.processor.ts — отправка напоминаний по таймеру API
8 Отмена delay при выполнении условия (cancelOn) API
9 JSON-сценарий Powerhouse Gym (полный по Miro-схеме) Content
10 E2E тест реферального цикла Test

Definition of Done: Полный цикл: клиент → пригласил друга → друг зашёл → выбрал клуб → клиент получил бонус.


Спринт 4: Рассылки + Вебхуки (5-7 дней)

Цель: Отправка рассылок по фильтрам, события на внешние URL

# Задача Слой
1 Prisma-модели: TgBotBroadcast, TgBotWebhook Schema
2 Миграция БД Schema
3 BroadcastService — создание, фильтрация, запуск API
4 broadcast.processor.ts — пакетная отправка с rate limit API
5 Фильтры: клуб, источник, приглашено, дата, согласие, подписка API
6 preview-count endpoint — подсчёт аудитории без отправки API
7 BotWebhookService — CRUD подписок на события API
8 bot-webhook.processor.ts — доставка с HMAC, retry API
9 Эмиссия событий из движка и referral service API
10 REST контроллеры рассылок и вебхуков API

Definition of Done: Рассылка с фильтрами отправляется, события бота уходят на внешний URL.


Спринт 5: UI — Управление ботами (7-10 дней)

Цель: Базовый UI в web-club-admin

# Задача Слой
1 Sidebar item "Telegram-бот" (с RoleGate) UI
2 Страница списка ботов UI
3 Форма создания/редактирования бота (токен, имя) UI
4 Таблица подписчиков с фильтрами и пагинацией UI
5 Карточка подписчика + история переписки UI
6 Экспорт подписчиков в XLSX UI
7 Страница рассылок: список + форма создания UI
8 Фильтры аудитории с превью количества UI
9 Страница вебхуков: CRUD подписок UI
10 Дашборд аналитики: подписчики, конверсия, рефералы UI

Definition of Done: Админ клуба может создать бота, видеть подписчиков, отправить рассылку через UI.


Спринт 6: UI — Редактор сценариев (10-14 дней)

Цель: Визуальный конструктор сценариев

# Задача Слой
1 Интеграция React Flow (или @xyflow/react) UI
2 Кастомные ноды: message, condition, action, input, delay, referral UI
3 Drag & drop добавление нод UI
4 Панель свойств ноды (правая sidebar) UI
5 Редактор текста сообщения с переменными UI
6 Редактор кнопок (inline/reply) UI
7 Редактор условий (variable, operator, value) UI
8 Редактор действий (webhook, set_variable, crm_deal) UI
9 Конвертация React Flow ↔ JSON сценария UI
10 Валидация графа (нет висячих нод, есть startNode) UI
11 Превью / тест сценария (симулятор в UI) UI
12 Импорт/экспорт JSON UI
13 Черновик + публикация UI

Definition of Done: Админ собирает сценарий визуально, сохраняет, бот его выполняет.


Спринт 7: Интеграции + Полировка (5-7 дней)

Цель: Связь с CRM, аналитика, production-readiness

# Задача Слой
1 Action crm_deal — создание сделки в CRM из сценария API
2 Action send_email — отправка email API
3 Action google_sheets — запись в таблицу API
4 Аналитика: воронка по нодам (сколько дошло до каждой) API+UI
5 Аналитика: конверсия рефералов API+UI
6 UI в web-platform-admin: обзор ботов по всем клубам UI
7 Лимиты модуля: макс. подписчиков, рассылок/мес API
8 Логирование и мониторинг API
9 Документация API (Swagger) Docs
10 Seed: полный сценарий Powerhouse Gym Content

Definition of Done: Модуль production-ready, сценарий Powerhouse Gym работает end-to-end.


9. Технические решения

Вопрос Решение
Telegram Bot API HTTP напрямую (без grammY/Telegraf) — меньше зависимостей, полный контроль
Приём updates Webhook mode (не long polling) — через публичный endpoint
Rate limiting BullMQ rate limiter: 30 msg/sec per bot
Шифрование токена AES-256 через env-ключ, хранение в БД
JSON-валидация JSON Schema (ajv) для валидации сценария
Визуальный редактор @xyflow/react (React Flow) — де-факто стандарт
Eval условий Безопасный eval через new Function с whitelist переменных (без arbitrary code)
Multi-tenancy clubId на всех таблицах, RLS, фильтрация как в остальной системе
Модуль @RequireModule('telegram-bot'), ModuleId.TELEGRAM_BOT

10. Оценка

Спринт Дни Сложность
1. Фундамент 5-7 Средняя
2. Движок сценариев 7-10 Высокая
3. Реферальная механика 5-7 Высокая
4. Рассылки + Вебхуки 5-7 Средняя
5. UI — Управление 7-10 Средняя
6. UI — Редактор сценариев 10-14 Высокая
7. Интеграции + Полировка 5-7 Средняя
Итого 44-62