Каналы
WhatsApp
WhatsApp-адаптер работает поверх официального WhatsApp Business Cloud API от Meta (graph.facebook.com/v18.0). Входящие сообщения приходят через Meta webhooks, исходящие отправляются REST-вызовами в Cloud API.
Плагины каналов читают активную runtime-конфигурацию через app.channelService. Текущая реализация EnvChannelService работает поверх env и отдаёт один дефолтный WhatsApp-канал и один дефолтный Telegram-канал; интерфейс готов к будущей multi-tenant реализации через БД.
Архитектура
src/channels/whatsapp/
├── index.ts # Service factory (WhatsAppService)
├── plugin.ts # Fastify plugin (routes)
├── connection.ts # Cloud API client, webhook parsing, token lifecycle
├── handler.ts # Transport normalization + AgentTask creation
├── sender.ts # Rate-limited outbound sender
├── queue.ts # BullMQ inbound/outbound workers
├── auth-state.ts # OAuth token persistence in Redis
├── conversation-repository.ts # Prisma conversation store
└── types.ts # Config and message types
Конфигурация
| Переменная | По умолчанию | Описание |
|---|---|---|
WHATSAPP_APP_ID | — | ID Meta-приложения (используется для refresh токена) |
WHATSAPP_APP_SECRET | — | Secret Meta-приложения (используется для refresh токена) |
WHATSAPP_PHONE_NUMBER_ID | — | Phone number ID из Meta Business dashboard |
WHATSAPP_ACCESS_TOKEN | — | Начальный OAuth access token |
WHATSAPP_WEBHOOK_VERIFY_TOKEN | — | Verify-токен webhook (задаётся в Meta dashboard) |
WA_PHONE_NUMBER | +380000000000 | Номер телефона отправителя (используется как префикс ключей в Redis) |
WA_RATE_LIMIT_PER_MIN | 60 | Лимит исходящих сообщений в минуту |
Настройка
- Создайте Meta-приложение с включённым продуктом WhatsApp Business.
- Добавьте номер телефона и запишите Phone Number ID из dashboard.
- Сгенерируйте long-lived access token и пропишите в
WHATSAPP_ACCESS_TOKEN. - Задайте
WHATSAPP_WEBHOOK_VERIFY_TOKENи укажите URL webhook в Meta dashboard:- Verify endpoint:
GET /api/v1/whatsapp/webhook - Events endpoint:
POST /api/v1/whatsapp/webhook
- Verify endpoint:
- Пропишите остальные env-переменные и запустите сервер.
Жизненный цикл соединения
- Сервер стартует — вызывается
WhatsAppConnection.connect(). - Сохранённый токен загружается из Redis. Если его нет — туда записывается настроенный
WHATSAPP_ACCESS_TOKEN. - Если срок жизни сохранённого токена истекает в течение 7 дней, он обменивается на новый long-lived через Meta Graph API.
- Раз в 6 часов запускается периодическая проверка, которая обновляет токены заранее.
- При остановке сервера планировщик refresh-а чистится.
Сканирование QR-кода не нужно — вся аутентификация через OAuth-токены.
Входящие
- Meta делает POST на
/api/v1/whatsapp/webhook. - Payload парсится в
connection.ts(handleWebhookPayload). - Каждое сообщение извлекается и раздаётся зарегистрированным обработчикам.
- Обработчик кладёт сообщение в очередь
wa-inbound(BullMQ). - Воркер:
- находит или создаёт пользователя и разговор по WhatsApp JID;
- сохраняет входящее сообщение;
- создаёт
AgentTask; - ставит задачу в общую очередь
agent-tasks.
Маршрут сразу возвращает { received: true }; обработка асинхронная, чтобы уложиться в таймаут ответа Meta.
Общий agent-tasks воркер делает inject_profile, RAG, генерацию, confidence-проверки, persona escalation, обход матрицы доверия и HITL-маршрутизацию одинаково для WhatsApp и Telegram.
Исходящие
- Одобренное или авто-пропущенное сообщение ставится в очередь
wa-outbound. - Воркер вызывает
WhatsAppSender.send(). - Rate limiter (token bucket) ждёт свободную ёмкость.
- Сообщение отправляется через
POST /{phoneNumberId}/messagesCloud API.
Эндпоинты
| Метод | Путь | Описание |
|---|---|---|
GET | /api/v1/whatsapp/status | Статус подключения к Cloud API |
GET | /api/v1/whatsapp/webhook | Верификация webhook (hub.challenge) |
POST | /api/v1/whatsapp/webhook | Приём входящих сообщений от Meta |
Форматирование сообщений
Sender поддерживает нативное форматирование WhatsApp:
*bold*_italic_~strikethrough~`код`и```code block```
Rate limiting
На каждый процесс-отправитель применяется алгоритм token bucket. Настраивается через WA_RATE_LIMIT_PER_MIN (по умолчанию 60 сообщений/мин).
Хранилище токенов
OAuth access-токены хранятся в Redis под префиксом <sessionKeyPrefix><phoneNumber>:. На каждый токен два ключа:
| Суффикс ключа | Значение |
|---|---|
access_token | Текущая строка access-токена |
token_expires_at | Unix-ms timestamp истечения (пустая строка, если неизвестно) |
Токены обновляются заранее, когда до истечения остаётся меньше 7 дней. Чтобы очистить сохранённые токены (например, после отзыва доступа), вызовите clearTokenState(redis, keyPrefix) из auth-state.ts.
Telegram
Telegram-адаптер использует Grammy — фреймворк для Telegram-ботов на Node.js.
Архитектура
src/channels/telegram/
├── index.ts # Service factory
├── plugin.ts # Fastify plugin (routes)
├── bot.ts # Grammy bot initialization
├── handler.ts # Transport normalization + AgentTask creation
├── sender.ts # Outbound message formatting
├── queue.ts # BullMQ inbound/outbound workers
├── conversation-repository.ts # Prisma conversation store
└── types.ts # Config and message types
Конфигурация
| Переменная | По умолчанию | Описание |
|---|---|---|
TG_BOT_TOKEN | — | Токен Telegram-бота от @BotFather |
TG_RATE_LIMIT_PER_MIN | 30 | Лимит сообщений в минуту |
Настройка
- Создайте бота через @BotFather в Telegram.
- Скопируйте токен бота.
- Пропишите
TG_BOT_TOKENв.env. - Перезапустите сервер.
Текущий плагин запускает бота в режиме long polling и отдаёт эндпоинт статуса.
Поток сообщений
Входящие
- Grammy получает сообщение через long polling.
- Сообщение попадает в очередь
tg-inbound(BullMQ). - Воркер:
- находит или создаёт пользователя и разговор по Telegram user ID;
- сохраняет входящее сообщение;
- создаёт
AgentTask; - ставит задачу в общую очередь
agent-tasks.
Дальше общий agent-tasks воркер решает — auto-send, создать HITL approval или отправить на persona escalation.
Исходящие
- Одобренное сообщение ставится в очередь
tg-outbound. - Воркер форматирует его в Telegram MarkdownV2/HTML.
- Отправляет через Grammy Bot API.
- Rate limiter придерживает темп.
Эндпоинты
| Метод | Путь | Описание |
|---|---|---|
GET | /api/v1/telegram/status | Статус бота |
Форматирование сообщений
Telegram поддерживает:
**bold**_italic_`inline code`- Блоки кода;
- HTML-форматирование как фолбэк.
Отслеживание разговоров
Каждый Telegram-пользователь трекается по tg_user_id. Conversation repository при первом контакте создаёт записи пользователя и разговора — так же как в WhatsApp-адаптере.