Files
fitcrm/docs/plan-tgbot-visual-editor-v2.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

11 KiB
Raw Permalink Blame History

Доработки визуального редактора Telegram-бот — План

Итерация 1: Критичные фиксы + Backend (2-3 дня)

1.1. Обработка /stop (п.4, п.9)

Backend:

  • В telegram-incoming.processor.ts — при получении /stop:
    • Пометить подписчика isSubscribed = false, blockedAt = now()
    • Отправить прощальное сообщение (настраивается в settings бота)
    • НЕ отправлять больше сообщений (проверка в engine перед sendMessage)
  • В scenario-engine.service.ts — перед каждым sendMessage проверять isSubscribed
  • В broadcast.processor.ts — уже есть проверка, ок
  • При повторном /startisSubscribed = true, blockedAt = null (уже работает)

Аналитика:

  • В stats добавить unsubscribed (blockedAt != null, отдельно от telegram-блокировки)
  • Отображать на дашборде: подписаны / отписались (/stop) / заблокировали (telegram)

1.2. Защита от перехода по своей реф-ссылке (п.10)

Backend:

  • В telegram-incoming.processor.ts — при /start ref_XXX:
    • Если ref_XXX === текущий subscriberId → не создавать реферал
    • Отправить сообщение: "Это ваша реферальная ссылка — отправьте её другу!"
    • Показать кнопку "Поделиться ссылкой"

1.3. Сохранение позиций блоков (п.8 второй)

Backend:

  • Добавить поле layoutPositions Json? в модель TgBotScenario
    • Формат: { "nodeId": { "x": 100, "y": 200 }, ... }
  • API: при сохранении сценария принимать positions отдельно от scenario
  • Загружать позиции при открытии и применять к React Flow

Frontend:

  • При перемещении ноды — сохранять node.position в локальный state
  • При нажатии "Сохранить" — отправлять и scenario и positions
  • При загрузке — если есть positions, применять их вместо авто-layout

Итерация 2: Визуальный редактор v2 — Layout + Кнопки (3-5 дней)

2.1. Рендеринг сверху вниз (п.1)

Изменения:

  • React Flow direction: 'TB' (top-to-bottom) вместо LR
  • Handles: Position.Top (target) и Position.Bottom (source)
  • scenarioToFlow — пересчёт layout вертикально (дерево сверху вниз)
  • Dagre layout library для автоматического расположения (@dagrejs/dagre)

2.2. Кнопки как визуальные элементы (п.1)

Архитектура:

  • Кнопки рендерятся внутри message-ноды как кликабельные блоки
  • Каждая кнопка имеет свой выходной handle (source)
  • Из каждой кнопки идёт связь вниз к целевой ноде
  • Кнопки расположены горизонтально на одном уровне под сообщением

UI ноды message:

┌──────────────────────────┐
│ 📝 Сообщение             │
│ Привет! Выбери действие: │
│                          │
│ [+ Кнопка]               │
│ ┌─────────┐ ┌─────────┐  │
│ │ Узнать  │ │ О клубах│  │
│ │    ●    │ │    ●    │  │  ← выходные handles
│ └─────────┘ └─────────┘  │
└──────────────────────────┘

Для каждой кнопки:

  • Текст (редактируемый inline)
  • Тип: goto / url / share_contact
  • Кнопки [X] удалить, [↑↓] переместить
  • Drag handle для связи с целевой нодой

2.3. Управление кнопками (п.1)

  • [+ Кнопка] — добавить новую кнопку в сообщение
  • Клик по кнопке → inline-редактирование текста
  • Правый клик / иконка → popup: тип (goto/url/contact), target
  • — удалить кнопку
  • Drag & drop для сортировки кнопок внутри ноды

Итерация 3: Визуальные редакторы свойств (3-5 дней)

3.1. Редактор сообщений в стиле Telegram (п.3)

Компонент TelegramMessageEditor:

  • WYSIWYG-подобный редактор текста
  • Toolbar: Bold, Italic, Strikethrough, Code, ||Spoiler||
  • Генерирует HTML: <b>, <i>, <s>, <code>, <tg-spoiler>
  • Emoji picker (популярные emoji для бизнеса)
  • Превью сообщения в стиле Telegram (пузырь)
  • Подстановка переменных: кнопка {{x}} → выпадающий список переменных сценария

3.2. Картинки в сообщениях (п.2)

  • Кнопка "Добавить картинку" в ноде message
  • Upload изображения → сохранение на сервере (или URL)
  • Превью картинки в ноде
  • Кнопка [X] для удаления картинки
  • Backend: sendPhoto вместо sendMessage когда есть image

3.3. Визуальный блок «Действие» (п.5)

Вместо JSON — UI форма:

┌──────────────────────────────┐
│ ⚡ Действие                   │
│                              │
│ [+ Добавить действие]        │
│                              │
│ 1. 📌 Установить переменную  │
│    bonus_granted = true  [X] │
│                              │
│ 2. 🎁 Начислить бонус    [X] │
│                              │
│ 3. 📋 Создать сделку CRM [X] │
│    Имя: {{user_name}}        │
│    Тел: {{phone}}            │
└──────────────────────────────┘

Каждый тип действия — своя мини-форма:

  • set_variable → dropdown переменных + input значения
  • grant_bonus → просто галочка, без параметров
  • set_consent → toggle вкл/выкл
  • webhook → URL + метод + body (JSON)
  • crm_deal → поля: имя, телефон, pipeline (dropdown)

3.4. Визуальный блок «Условие» (п.7)

Вместо JSON — UI с ветвлениями:

┌────────────────────────────────┐
│ 🔀 Условие                     │
│                                │
│ [+ Добавить правило]           │
│                                │
│ Если [invited_count ▼] [>= ▼] │
│      [2        ]  → ●         │  ← handle к целевой ноде
│                                │
│ Если [invited_count ▼] [== ▼] │
│      [1        ]  → ●         │
│                                │
│ Иначе            → ●          │  ← default handle
└────────────────────────────────┘

Каждое правило:

  • Dropdown: выбор переменной (из variables сценария)
  • Dropdown: оператор (==, !=, >=, <=, >, <)
  • Input: значение
  • Выходной handle справа/снизу

3.5. Визуальное условие в задержке (п.8 первый)

Блок delay с визуальным cancelOn:

┌───────────────────────────┐
│ ⏰ Задержка: 30m           │
│                           │
│ Отменить если:            │
│ [invited_count ▼] [>= ▼] │
│ [2               ]        │
│                           │
│ Напоминание: "Пока..."   │
└───────────────────────────┘

Зависимости и порядок

Итерация 1 (backend фиксы)
  ├── /stop обработка
  ├── Защита от своей реф-ссылки
  └── Сохранение позиций
      │
Итерация 2 (layout + кнопки)
  ├── Dagre layout (TB)
  ├── Кнопки как handles
  └── CRUD кнопок в ноде
      │
Итерация 3 (визуальные редакторы)
  ├── Telegram message editor
  ├── Картинки
  ├── Action builder
  ├── Condition builder
  └── Delay condition builder

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

Задача Библиотека/подход
TB layout @dagrejs/dagre — автоматическое расположение графа
Кнопки как handles React Flow dynamic handles (Handle с уникальными id per button)
Текстовый редактор tiptap (lightweight) или custom contentEditable с toolbar
Emoji picker emoji-mart или встроенный через Unicode
Картинки Upload на /api/upload → URL, или base64 inline
Позиции JSON поле в TgBotScenario, sync при drag-end

Оценка

Итерация Дни Приоритет
1. Backend фиксы 2-3 Высокий
2. Layout + кнопки 3-5 Высокий
3. Визуальные редакторы 3-5 Средний
Итого 8-13