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>
11 KiB
11 KiB
Доработки визуального редактора 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— уже есть проверка, ок - При повторном
/start—isSubscribed = 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 |