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

## 1. Концепция

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

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

---

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

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

```jsonc
{
  "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`

```jsonc
{
  "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`

```jsonc
{
  "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`

```jsonc
{
  "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`

```jsonc
{
  "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`

```jsonc
{
  "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`

```jsonc
{
  "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 Конфигурация бота

```prisma
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 Сценарий

```prisma
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 Подписчики (ключевая таблица)

```prisma
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 История сообщений

```prisma
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 Рассылки

```prisma
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
}
```

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

```jsonc
{
  "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)

```prisma
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** |             |
