Skip to content

WhatsApp

shaerware edited this page Nov 2, 2026 · 8 revisions

WhatsApp (Боты)

⭐ КП из WhatsApp-диалога. Клиент просит КП — ассистент сам собирает черновик по каталогу, менеджер утверждает его командой «ок 18» со служебного WhatsApp-номера или кнопкой в Telegram. Подробно: KP.

Управление мультиинстансными WhatsApp ботами с независимыми настройками AI, TTS и воронкой продаж. Поддерживаются два способа подключения:

Способ provider Что нужно Кому
Meta Cloud API cloud Номер, заведённый в WhatsApp Business API, приложение в Meta, публичный HTTPS-вебхук Официальный путь, шаблоны, рассылки
Свой провайдер (QR) bridge Обычный телефон с WhatsApp — линкуется по QR, как WhatsApp Web Быстрый старт без Meta и без платных посредников

Второй вариант — собственный мост на нашем хосте; он заменяет сторонние сервисы вроде Wazzup / Green API / Radist. Переписка не уходит третьей стороне.

Скриншот

WhatsApp

Концепция

Система поддерживает несколько WhatsApp ботов одновременно. Каждый бот:

  • Запускается как отдельный субпроцесс Python (аналогично Telegram ботам)
  • Использует WhatsApp Cloud API (Meta Business Platform)
  • Имеет независимый LLM бэкенд, персону и системный промпт
  • Может иметь свою воронку продаж (квиз, сегменты, агентские промпты)
  • Поддерживает интерактивные сообщения (quick reply, list messages)
  • Управляется (start/stop/restart) из админ-панели

Свой провайдер — подключение телефона по QR

Вариант без Meta и без посредников. Сервис services/whatsapp-bridge/ (Node + Baileys) держит связь с телефоном по протоколу multi-device — ровно так же, как WhatsApp Web, — и отдаёт боту обычный REST + вебхуки.

телефон ──QR──► whatsapp-bridge (:8005) ──вебхук──► whatsapp_bot (:8003)
                       ▲                                    │
                       └────────── REST: отправка ──────────┘

Запуск моста

# Docker (креды в volume — иначе после рестарта снова потребуется QR)
docker compose --profile whatsapp-bridge up -d

# или локально
cd services/whatsapp-bridge
npm install
WHATSAPP_BRIDGE_TOKEN=$(openssl rand -hex 24) npm start

В .env проекта пропишите тот же токен:

WHATSAPP_BRIDGE_TOKEN=<тот же токен>       # общий секрет; им же подписываются вебхуки
WHATSAPP_BRIDGE_URL=http://127.0.0.1:8005  # где живёт мост
WHATSAPP_BRIDGE_CALLBACK_HOST=127.0.0.1    # куда мост стучится за входящими

Привязка телефона

  1. Создайте инстанс, в поле «Способ подключения» выберите «Свой провайдер (телефон по QR)»
  2. Нажмите «Подключить телефон» в шапке инстанса
  3. На телефоне: Настройки → Связанные устройства → Привязка устройства
  4. Наведите камеру на QR из модалки

Статус связи виден там же: Не запущена → Подключение… → Ожидает сканирования → Подключено. Креды сохраняются на диске, поэтому после перезапуска мост подцепляется молча, без нового QR. Кнопка «Отвязать телефон» стирает креды — следующее подключение потребует сканирования заново.

Чем отличается от Cloud API

Что Cloud API Свой провайдер
Кнопки и списки Нативные интерактивные элементы Эмулируются нумерованным текстом; ответ «2» или название пункта распознаётся автоматически
Шаблоны, 24-часовое окно Есть Нет — привязанный телефон просто пишет сообщения
Групповые чаты — Игнорируются, чтобы ассистент не влезал во все группы телефона
Голосовые и фото Обрабатываются Скачиваются мостом, но в STT пока не заведены — бот просит написать текстом
Статус протокола Официальный Неофициальный (как у всех QR-провайдеров)

Воронка продаж, FAQ-навигация и LLM-роутер работают одинаково на обоих провайдерах: BridgeClient реализует тот же интерфейс, что и Cloud-клиент.

Что важно знать

  • Отдельная симка для прода. Протокол неофициальный: номер, ведущий себя как рассыльщик, могут заблокировать. Держите ассистента реактивным (отвечает на входящие), не превращайте в рассылку.
  • Один телефон — одна сессия. Если открыть WhatsApp Web с того же аккаунта, наша связь может быть вытеснена; мост это распознаёт и остаётся выключенным, а не отбирает слот обратно.
  • Volume с кредами обязателен в Docker, иначе каждый рестарт = новый QR.

API моста

Все маршруты, кроме /health, требуют заголовок X-Bridge-Token.

Метод Путь Назначение
GET /health Живость + состояние всех сессий
POST /sessions/:id/start Открыть сокет, тело {webhook_url}
GET /sessions/:id Состояние + QR (data-URL, пока идёт привязка)
POST /sessions/:id/stop Закрыть сокет, креды сохранить
POST /sessions/:id/logout Отвязать телефон и стереть креды
POST /sessions/:id/messages Отправка текста и медиа
POST /sessions/:id/read Синие галочки
POST /sessions/:id/presence Индикатор «печатает»
GET /sessions/:id/media/:messageId Скачать вложение из входящего

Входящие уходят боту на POST /bridge/webhook с подписью X-Bridge-Signature: sha256=<hex> (HMAC-SHA256 по сырому телу), 3 ретрая с backoff. Мёртвый потребитель не роняет сокет WhatsApp.

Подробности — services/whatsapp-bridge/README.md.

Предварительная настройка (Cloud API)

Шаг 1: Создание приложения в Meta

  1. Зарегистрируйтесь на Meta for Developers
  2. Создайте Business App → добавьте продукт WhatsApp
  3. Получите Phone Number ID и WhatsApp Business Account ID (WABA ID)
  4. Сгенерируйте Permanent Access Token (System User → Generate Token)
  5. Задайте Verify Token (произвольная строка для верификации webhook)
  6. Скопируйте App Secret из настроек приложения

Шаг 2: Настройка Webhook

WhatsApp требует публичный HTTPS URL для вебхука:

  1. Используйте ngrok или Cloudflare Tunnel:
    ngrok http 8003  # порт webhook бота
  2. В Meta Developer Portal → WhatsApp → Configuration:
    • Callback URL: https://your-tunnel.ngrok.io/webhook
    • Verify Token: тот же, что в настройках бота
    • Подписка на: messages

Подтверждение ответов

Для любого WhatsApp-номера можно включить режим, в котором ассистент не отвечает клиенту сам: ответы копятся в очереди на странице CRM → вкладка Подтверждение, а клиент сразу получает только автоответ о приёме. Там же, во вкладке Мониторинг, видно, работает ли бот, на связи ли телефон и есть ли диалоги без ответа. Подробнее — CRM.

Список ботов

Левая панель отображает все инстансы:

Элемент Описание
Индикатор Зелёный = запущен, серый = остановлен
Название Имя бота
Enabled Включён ли бот
Действия Start / Stop / Restart / Delete

Создание бота

  1. Нажмите "Добавить бота" в админ-панели
  2. Введите название
  3. Заполните данные WhatsApp Cloud API (см. выше)
  4. Настройте AI параметры
  5. Нажмите "Сохранить"

Из названия автоматически генерируется slug-ID (например, "Support Bot" → support-bot).

Настройки бота

Конфигурация организована по вкладкам:

Основные (Settings)

Параметр Описание
Название Отображаемое имя инстанса
Способ подключения cloud (Meta Cloud API) или bridge (свой провайдер, телефон по QR)
Адрес моста Только для bridge. Пусто — берётся из WHATSAPP_BRIDGE_URL
Phone Number ID ID номера телефона из Meta Business
WABA ID WhatsApp Business Account ID
Access Token Постоянный токен доступа (маскируется)
Verify Token Токен верификации вебхука
App Secret Секрет приложения для проверки подписи вебхука
Webhook Port Порт для приёма вебхуков (по умолчанию: 8003)
Enabled Включить/отключить бот

AI настройки

Параметр Описание
LLM Backend vllm, gemini или cloud:{provider_id}
System Prompt Кастомный системный промпт
LLM Params JSON с параметрами: temperature, max_tokens, top_p

TTS настройки

Параметр Описание
TTS Enabled Включить голосовые ответы
Engine xtts, piper или openvoice
Voice ID голоса (anna, marina)
Preset Опциональный пресет TTS

При включённом TTS бот отправляет аудио-сообщения вместе с текстовыми ответами.

Контроль доступа (Access)

Параметр Описание
Allowed Phones Список разрешённых номеров телефонов (пустой = все допущены)
Blocked Phones Список заблокированных номеров

Rate Limiting

Параметр Описание По умолчанию
Rate Limit Count Максимум сообщений за период 5
Rate Limit Hours Период в часах 5

Воронка продаж

WhatsApp бот поддерживает полную воронку продаж, аналогичную Telegram:

Интерактивные сообщения

WhatsApp использует два типа интерактивных элементов:

Тип Ограничения Использование
Quick Reply ≤3 кнопки, заголовок ≤20 символов Простые ответы да/нет, навигация
List Message ≤10 секций, ≤10 строк Квиз-вопросы, каталоги, FAQ-разделы

На своём провайдере интерактивных элементов нет — их не умеет отрисовать привязанный телефон. Меню уходит нумерованным текстом, а ответ пользователя («2», «2)», или само название пункта) мапится обратно в тот же reply_id, поэтому воронка и FAQ работают без изменений. Число вне диапазона меню (например «нужно 5 штук») намеренно считается обычным сообщением, а не нажатием. Ограничение на 3 кнопки здесь тоже не действует — печатается весь список.

Квиз и сегментация

  • Вопросы квиза отображаются как list messages
  • Сегментация (DIY/Basic/Custom) импортируется из модуля Telegram (telegram_bot.sales.segments)
  • Логика determine_segment(), GPU_AUDIT, calculate_quote() — общая, без дублирования

Пути сегментов

Сегмент Описание
DIY GPU-аудит, ссылка на GitHub
Basic Презентация ценности, демо, оформление заказа
Custom 5-шаговый discovery, расчёт стоимости, альтернативы

Платежи

WhatsApp не поддерживает встроенные платежи (как Telegram Stars), поэтому:

  • YooMoney: ссылка на оплату в тексте сообщения
  • Контактная информация для связи с менеджером

FAQ

FAQ-разделы идентичны Telegram:

  • Продукт (5 вопросов): what_is, offline, security, vs_cloud, cloud_models
  • Установка (3 вопроса): hardware, install, integrations
  • Цены и поддержка (3 вопроса): price, support, free_trial

Навигация: faq:cat_* → раздел, faq:{key} → ответ, faq:back_* → назад к разделу.

Особенности WhatsApp

Ограничение Решение
Нет URL-кнопок Ссылки в тексте сообщения
Нет редактирования сообщений Новое сообщение на каждое взаимодействие
List = single-select Последовательный выбор с кнопками "Ещё"/"Готово"
*bold* вместо **bold** Адаптированные шаблоны текстов

Управление ботом

Запуск / Остановка

Кнопка Действие Эффект на auto_start
Start Запустить субпроцесс auto_start = true
Stop Остановить (SIGTERM → 5с → SIGKILL) auto_start = false
Restart Stop + Start Без изменений

Auto-Start

Боты с auto_start = true автоматически запускаются при старте оркестратора. Флаг устанавливается при первом запуске и снимается при остановке.

Process Watcher и Auto-Restart

Периодический task bot-process-watcher (каждые 30с) обнаруживает завершённые бот-процессы и через EventBus:

  1. Логирует — событие process_died в Audit (канал, exit_code, uptime)
  2. Перезапускает — автоматически при crash (exit_code ≠ 0) с экспоненциальным backoff:
    • 3 попытки: 10с → 20с → 30с
    • Счётчик рестартов сбрасывается после стабильной работы >60с
    • После исчерпания попыток — только аудит, статус "failed"
  3. Пропускает — graceful stop (exit_code = 0) не перезапускается

Подробнее о событиях: Audit#process_died

Логи

  • Просмотр последних 100 строк лога бота
  • Лог-файл: logs/whatsapp_bot_{instance_id}.log

Архитектура субпроцесса

POST /admin/whatsapp/instances/{id}/start
  → WhatsAppManager.start_bot(id)
    → Спавнит: python -m whatsapp_bot
    → ENV: WA_INSTANCE_ID={id}, WA_INTERNAL_TOKEN={jwt}
    → Логи: logs/whatsapp_bot_{id}.log

Внутри субпроцесса:

  1. Бот загружает конфиг через API оркестратора (используя WA_INTERNAL_TOKEN)
  2. Запускает webhook-сервер на указанном порту
  3. Принимает входящие сообщения от WhatsApp Cloud API
  4. Маршрутизирует: приветствия → воронка, FAQ → ответы, остальное → LLM

Обработка сообщений

Incoming webhook → Signature verification (X-Hub-Signature-256)
  → Text message:
    1. Проверка приветствия (9 триггерных слов) → Welcome buttons
    2. Проверка funnel_state (свободный ввод: custom_step_1, diy_gpu_custom)
    3. Fallback → LLM через orchestrator API
  → Interactive reply:
    → Routing по prefix:action формату:
      sales:* → handlers/sales/router.py
      faq:*   → FAQ навигация
      tz:*    → TZ генератор
      nav:*   → Общая навигация

База данных воронки

Каждый WhatsApp бот хранит данные воронки в отдельной SQLite БД:

  • Файл: data/wa_sales_{instance_id}.db
  • Таблицы: users, events, custom_discovery
  • Ключ пользователя: номер телефона (TEXT)
  • Колонка funnel_state для отслеживания состояния свободного ввода

API эндпоинты

Управление инстансами

Метод Endpoint Описание
GET /admin/whatsapp/instances Список всех инстансов
POST /admin/whatsapp/instances Создать инстанс
GET /admin/whatsapp/instances/{id} Конфиг инстанса
PUT /admin/whatsapp/instances/{id} Обновить инстанс
DELETE /admin/whatsapp/instances/{id} Удалить инстанс
POST /admin/whatsapp/instances/{id}/start Запустить бота
POST /admin/whatsapp/instances/{id}/stop Остановить бота
POST /admin/whatsapp/instances/{id}/restart Перезапустить бота
GET /admin/whatsapp/instances/{id}/status Статус бота
GET /admin/whatsapp/instances/{id}/logs Последние N строк лога

Свой провайдер: привязка телефона

Метод Endpoint Описание
POST /admin/whatsapp/instances/{id}/bridge/start Открыть сессию, вернуть QR
GET /admin/whatsapp/instances/{id}/bridge/status Состояние связи + QR
POST /admin/whatsapp/instances/{id}/bridge/stop Закрыть сокет, креды сохранить
POST /admin/whatsapp/instances/{id}/bridge/logout Отвязать телефон, стереть креды

Требуют channels:edit (кроме status — channels:view). Если мост не поднят, эндпоинты отвечают 503 с адресом, по которому его искали.

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

Метод Endpoint Описание
GET /webhook (на порту webhook_port) Верификация вебхука (challenge)
POST /webhook (на порту webhook_port) Приём входящих сообщений

RBAC

  • Admin — видит и управляет всеми ботами в workspace
  • User/Web — только свои боты (owner_id)
  • Guest — только чтение

Все CRUD-эндпоинты и action-эндпоинты (start/stop/restart/status/logs) фильтруются по workspace_id из JWT. Системные методы (get_auto_start_instances, get_instance_with_token) не фильтруются — работают без JWT-контекста.

Миграция

python scripts/migrate_whatsapp.py           # создаёт таблицу whatsapp_instances
python scripts/migrate_whatsapp_provider.py  # добавляет provider / bridge_url / bridge_token

Вторая миграция идемпотентна и пришпиливает все существующие инстансы к provider='cloud' — транспорт под работающим ботом не меняется.


← Telegram | GSM →

Clone this wiki locally