🏗️ СПРИНТ: NextBot ↔ Chatwoot Потоковая Синхронизация

Проект: EastPay v2 Pro
Приоритет: 🔴 CRITICAL — Production-grade, финансовые сделки
Дата старта: 2026-03-05
Статус: 🔄 В работе — Phase 1 выполняется


⚠️ ИЗОЛЯЦИЯ ПРОЕКТА

NextBot аккаунт содержит НЕСКОЛЬКО проектов!
Этот спринт работает ТОЛЬКО с проектом:

  • Название: EastPay
  • Project ID (agent_hash): 276a96dd-9dea-41f6-8828-3666a84d85c1
  • Домен: app.nextbot.kz

Все dialog_id, сценарии и вебхуки принадлежат ТОЛЬКО этому проекту.
Никогда не подставляйте ID из других проектов — утечка данных клиентов!


📋 Executive Summary

Production-grade двунаправленная синхронизация между NextBot (ИИ-агент) и Chatwoot (операторский интерфейс) через n8n. Система обслуживает финансовые сделки — каждое потерянное сообщение = потерянный клиент = потерянные деньги.

Принципы проектирования

Принцип Реализация
Zero message loss Каждое сообщение записывается в Supabase перед обработкой
Idempotency Уникальный message_id предотвращает дублирование
Graceful degradation При сбое одной системы остальные продолжают работать
Loop prevention 5 уровней защиты от зацикливания
Audit trail Полный лог всех операций
Project isolation Все данные привязаны к NextBot Project ID 276a96dd

🎯 Прогресс спринта

Фаза Задача Статус
1 T1: Supabase — таблицы channel_mapping + sync_message_log
1 T2: Chatwoot — API Inbox
1 T3: Chatwoot — Global Webhook
1 T4: Smoke Test всех API
2 T5: n8n Workflow 1 «NextBot → Chatwoot» ✅ (v4 published)
2 T6: NextBot — сценарии синхронизации (1-4) ✅ (все 4 теста 200 OK)
2 T7: Integration Test NB → CW
3 T8: n8n Workflow 2 «Chatwoot → NextBot» ✅ (TG removed, NextBot only)
3 T9: NextBot — настройки оператора
3 T10: Integration Test CW → NB
4 T11: E2E Full Cycle Test
4 T12: Production Checklist

Легенда: ⬜ Todo · 🔄 В работе · ✅ Готово · ❌ Заблокировано


Phase 1: Foundation (Infrastructure)

T1: Supabase — Создание таблиц

Приоритет: 🔴 Blocker | Зависимости: нет | Статус: ✅ Выполнено

Ядро всей синхронизации. Без маппинга ID между системами ни один поток не работает.

Таблица channel_mapping:

CREATE TABLE public.channel_mapping (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  
  -- NextBot Project Isolation
  -- ⚠️ CRITICAL: Ensures we only process EastPay project data
  nextbot_project_id TEXT NOT NULL DEFAULT '276a96dd-9dea-41f6-8828-3666a84d85c1',
  
  -- Telegram
  telegram_chat_id BIGINT NOT NULL,
  
  -- NextBot
  nextbot_dialog_id BIGINT NOT NULL,
  
  -- Chatwoot
  chatwoot_contact_id INTEGER,
  chatwoot_conversation_id INTEGER,
  chatwoot_source_id TEXT,
  
  -- Client metadata (cache)
  user_name TEXT,
  user_phone TEXT,
  telegram_username TEXT,
  
  -- State management
  is_active BOOLEAN DEFAULT true,
  operator_active BOOLEAN DEFAULT false,
  last_synced_at TIMESTAMPTZ DEFAULT now(),
  
  -- Timestamps
  created_at TIMESTAMPTZ DEFAULT now(),
  updated_at TIMESTAMPTZ DEFAULT now(),
  
  -- Constraint: only EastPay project
  CONSTRAINT chk_eastpay_project CHECK (
    nextbot_project_id = '276a96dd-9dea-41f6-8828-3666a84d85c1'
  )
);

CREATE UNIQUE INDEX idx_cm_nextbot_dialog 
  ON public.channel_mapping(nextbot_dialog_id);
CREATE INDEX idx_cm_telegram_chat 
  ON public.channel_mapping(telegram_chat_id);
CREATE INDEX idx_cm_cw_conversation 
  ON public.channel_mapping(chatwoot_conversation_id) 
  WHERE chatwoot_conversation_id IS NOT NULL;
CREATE INDEX idx_cm_active 
  ON public.channel_mapping(is_active) 
  WHERE is_active = true;

CREATE OR REPLACE FUNCTION update_channel_mapping_timestamp()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = now();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_channel_mapping_updated
  BEFORE UPDATE ON public.channel_mapping
  FOR EACH ROW
  EXECUTE FUNCTION update_channel_mapping_timestamp();

ALTER TABLE public.channel_mapping ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Service role full access" 
  ON public.channel_mapping FOR ALL 
  USING (true) WITH CHECK (true);

Таблица sync_message_log (идемпотентность + аудит):

CREATE TABLE public.sync_message_log (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  message_id TEXT NOT NULL,
  source TEXT NOT NULL,          -- 'nextbot', 'chatwoot', 'telegram'
  direction TEXT NOT NULL,       -- 'nb_to_cw', 'cw_to_nb', 'cw_to_tg'
  channel_mapping_id UUID REFERENCES public.channel_mapping(id),
  content_preview TEXT,
  message_type TEXT,
  status TEXT DEFAULT 'sent',
  error_message TEXT,
  created_at TIMESTAMPTZ DEFAULT now()
);

CREATE UNIQUE INDEX idx_sml_message_unique 
  ON public.sync_message_log(message_id, direction);
CREATE INDEX idx_sml_created_at 
  ON public.sync_message_log(created_at);

ALTER TABLE public.sync_message_log ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Service role full access" 
  ON public.sync_message_log FOR ALL 
  USING (true) WITH CHECK (true);

Чеклист:


T2: Chatwoot — Создать API Inbox

Приоритет: 🔴 Blocker | Зависимости: нет | Статус: ✅ Выполнено

curl -X POST "https://chatcrm.eastpay.online/api/v1/accounts/3/inboxes" \
  -H "api_access_token: FksZYfbqjb9RXmiEgAQqQ6Ev" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "EastPay Telegram AI",
    "channel": {
      "type": "api",
      "webhook_url": "https://n8n.flowzzy.com/webhook/chatwoot-operator-reply"
    }
  }'

Чеклист:


T3: Chatwoot — Настроить Global Webhook

Приоритет: 🔴 Blocker | Зависимости: T2 | Статус:

В Chatwoot UI: Settings → Integrations → Webhooks → Add new webhook

Поле Значение
URL https://n8n.flowzzy.com/webhook/chatwoot-events
Name n8n Sync Bridge
message_created ✅ Обязательно
conversation_status_changed ✅ Обязательно

Чеклист:


T4: Smoke Test — проверка фундамента

Приоритет: 🟡 Required | Зависимости: T1, T2, T3 | Статус: ✅ Выполнено (частично, кроме T3)

Чеклист:


Phase 2: NextBot → Chatwoot (Forward Sync)

T5: n8n — Workflow 1 «NB→CW Message Sync»

Приоритет: 🔴 Critical | Зависимости: T4 | Статус: ✅ Выполнено (n8n ID: qWlkQJiTqU1IzJQx)

Главный workflow. Без него менеджеры не видят диалоги.

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

[Webhook Trigger] → [Switch by event] → [Supabase: Find mapping]
                                              │
                                     ┌────────┴────────┐
                                   Found           Not Found
                                     │                 │
                                     │         [CW: Search/Create Contact]
                                     │                 │
                                     │         [CW: Create Conversation]
                                     │                 │
                                     │         [Supabase: Save mapping]
                                     │                 │
                                     ├─────────────────┘
                                     │
                              [Idempotency Check]
                                     │
                              [CW: Send Message]
                                     │
                              [Log to Supabase]
                                     │
                                  [Done ✅]

Спецификация нод:

# Нода Type Ключевые параметры
1 Webhook Trigger webhook Path: nextbot-message-sync, POST, Response: 200
2 Switch: Event switch Field: eventclient_message / agent_message
3 Supabase: Find supabase channel_mapping WHERE nextbot_dialog_id = dialog_id
4 IF: Exists if Check mapping found
5a CW: Search Contact httpRequest GET /contacts/search?q=tg_{{user_id}}
5b CW: Create Contact httpRequest POST /contacts (inbox_id, identifier, name, phone)
6 CW: Create Conversation httpRequest POST /conversations (с source_id!)
7 Supabase: Save Mapping supabase INSERT channel_mapping
8 Idempotency Check supabase SELECT sync_message_log WHERE message_id + direction
9 CW: Send Message httpRequest POST /conversations/{id}/messages
10 Log supabase INSERT sync_message_log

Payload от NextBot (webhook из сценария):

{
  "event": "client_message",
  "dialog_id": 10177062,
  "text": "Хочу обменять 5000 USDT",
  "user_id": "tg_123456789",
  "user_name": "Иван Петров",
  "user_phone": "+79001234567",
  "telegram_username": "ivan_petrov",
  "timestamp": "2026-03-05T14:05:00Z",
  "message_id": "uuid-from-nextbot"
}

Маппинг event → CW message_type:

NextBot event CW message_type CW private
client_message incoming (0) false
agent_message outgoing (1) false
function_executed outgoing (1) true

Чеклист:


T6: NextBot — Сценарии синхронизации (5 штук)

Приоритет: 🔴 Critical | Зависимости: T5 | Статус:

⚠️ Все действия СТРОГО в проекте «EastPay» (ID: 276a96dd-9dea-41f6-8828-3666a84d85c1)
📖 Полная инструкция: docs/ai-agent/nextbot-chatwoot-scenarios.md

Где: NextBot → (проект EastPay) → Scenarios → Create

Все сценарии используют действие Custom API → POST → https://n8n.flowzzy.com/webhook/nextbot-message-sync

# Сценарий Триггер (Condition) Event в JSON Обязательный
1 CW Sync: Старт диалога Dialog start dialog_start ✅ да
2 CW Sync: Входящее от клиента New client message client_message ✅ да
3 CW Sync: Ответ агента New agent message agent_message ✅ да
4 CW Sync: Ошибка отправки Message send error send_error ✅ да
5 CW Sync: Менеджер в NextBot Manager message manager_message ⚠️ опц.

Сценарий 5 опционален — может вызвать петлю. Создавать только после тестирования 1-4.

Пример JSON Body для Custom API (Сценарий 2):

{
  "event": "client_message",
  "dialog_id": "{{dialog_id}}",
  "text": "{{message_text}}",
  "user_id": "tg_{{user_id}}",
  "user_name": "{{user_name}}",
  "user_phone": "{{user_phone}}",
  "telegram_username": "{{telegram_username}}",
  "timestamp": "{{current_datetime}}",
  "message_id": "client_{{dialog_id}}_{{timestamp}}"
}

⚠️ Имена переменных ({{dialog_id}}, {{message_text}} и т.д.) — проверьте в NextBot UI (автокомплит в поле Body).

Чеклист:


T7: Integration Test — NextBot → Chatwoot

Приоритет: 🟡 Required | Зависимости: T5, T6 | Статус:


Phase 3: Chatwoot → NextBot (Reverse Sync)

T8: n8n — Workflow 2 «CW→NB Operator Reply»

Приоритет: 🔴 Critical | Зависимости: T7 | Статус:

⚠️ АРХИТЕКТУРНОЕ ПРАВИЛО: НИКОГДА не слать в Telegram напрямую!
Все сообщения клиенту ОБЯЗАНЫ идти через NextBot webhook (forwarded_output).
Иначе AI-агент не видит историю, автозасыпание не работает, контекст теряется.

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

[Webhook Trigger] → [Switch: event type]
                          │
              ┌───────────┴───────────┐
        message_created     conversation_status_changed
              │                       │
     [FILTER: sender.type    [IF: resolved → deactivate]
      === "user" &&          [IF: open → reactivate]
      !private &&
      message_type === 1]
              │
        ┌─────┴─────┐
      PASS        REJECT → STOP
        │
  [Supabase: Get mapping]
        │
  ┌─────┴─────┐
  │           │
Found      Not Found → Log Error
  │
  ├── [NextBot Webhook: forwarded_output] ── клиент получает в TG
  │       ↑ NextBot сам отправит через бот, запишет в историю,
  │         AI увидит контекст, автозасыпание сработает
  │
  └── [Log to sync_message_log]

⚠️ ФИЛЬТР — самая важная защита от петель!

  • sender.type === "user" → только от реальных менеджеров
  • private === false → не пересылать private notes
  • message_type === 1 → только outgoing (ответы менеджера)

Ошибка = бесконечная петля = DDoS на все системы!

🔑 Почему ТОЛЬКО через NextBot, а не через Telegram API:

  1. NextBot записывает сообщение в историю → AI-агент видит полный контекст
  2. Автозасыпание AI-агента срабатывает (он знает, что менеджер пишет)
  3. Сценарии и функции NextBot продолжают работать корректно
  4. Один токен бота — один хозяин (NextBot), мы не вмешиваемся

Спецификация нод:

# Нода Ключевые параметры
1 Webhook Trigger Path: chatwoot-events, POST
2 Switch: Event event: message_created → A, conversation_status_changed → B
3 FILTER sender.type === "user" AND private === false AND message_type === 1
4 Supabase: Get Mapping WHERE chatwoot_conversation_id = conversation.id
5 NextBot: forwarded_output POST webhook_url, body: {dialog_id, text, message_type: "forwarded_output"}
6 Log INSERT sync_message_log

Flow B — Conversation Status:

CW Status Действие
resolved UPDATE channel_mapping SET is_active = false
open (reopen) UPDATE channel_mapping SET is_active = true

Чеклист:


T9: NextBot — Настройки оператора + Обратный поток

Приоритет: 🟡 Required | Зависимости: T7 | Статус:

⚠️ Проект EastPay (ID: 276a96dd-9dea-41f6-8828-3666a84d85c1)
📖 Полная инструкция: docs/ai-agent/nextbot-chatwoot-scenarios.md → раздел «Настройки оператора»

Часть A: Настройки Agent Settings

Где: NextBot → Проект EastPay → Agent Settings → Управление активностью

Настройка Значение Почему
Автоматическое засыпание ✅ Вкл ИИ замолкает при вмешательстве менеджера
Игнор первого сообщения менеджера ❌ Выкл Каждое сообщение = интенция взять контроль
Авто-возобновление 30 минут Менеджер ушёл → ИИ продолжит
Буфер сообщений 10 сек Собирает короткие реплики клиента в один запрос

Часть B: Обратный поток (Chatwoot → NextBot)

Когда менеджер пишет из Chatwoot, n8n WF-2 отправляет сообщение в NextBot через webhook:

POST https://app.nextbot.kz/api/webhooks/v1/276a96dd-.../41a585...
{
  "dialog_id": <из channel_mapping>,
  "text": "<текст менеджера>",
  "message_type": "forwarded_output"
}

Чеклист:


T10: Integration Test — Chatwoot → NextBot + TG

Приоритет: 🟡 Required | Зависимости: T8, T9 | Статус:


Phase 4: Hardening & Production

T11: E2E Full Cycle Test

Приоритет: 🔴 Critical | Зависимости: T10 | Статус:

Полный сценарий шаг за шагом:

Нагрузочный тест:


T12: Production Checklist

Приоритет: 🔴 Critical | Зависимости: T11 | Статус:


📊 Risk Register

Риск Влияние Митигация
Петля сообщений 🔴 5 фильтров + message_type: notification
Потеря сообщений при сбое n8n 🔴 Retry 3x + Error Workflow + sync_message_log
Чужие dialog_id из другого проекта 🔴 CHECK CONSTRAINT в БД + nextbot_project_id
Chatwoot API rate limit 🟡 Wait node + экспоненциальный backoff
NextBot webhook медленный 🟢 Async отправка, timeout 10 сек
Клиент удалил chat 🟡 Try-catch на sendMessage
Несколько менеджеров одновременно 🟢 CW Conversation Assignee

🔐 Учётные данные

Параметр Значение Статус
CHATWOOT_BASE_URL https://chatcrm.eastpay.online
CHATWOOT_ACCOUNT_ID 3
CHATWOOT_API_TOKEN FksZY....env)
CHATWOOT_INBOX_ID 3
NEXTBOT_PROJECT_NAME EastPay
NEXTBOT_PROJECT_ID 276a96dd-9dea-41f6-8828-3666a84d85c1
NEXTBOT_WEBHOOK_URL https://app.nextbot.kz/api/webhooks/v1/276a96dd.../41a585...
TELEGRAM_BOT_TOKEN 8385176510:AAEF6....env)
N8N_WEBHOOK_BASE https://n8n.flowzzy.com
SUPABASE_URL https://cvzsgjksswowqgfxvrsb.supabase.co

Критический путь: T1 → T4 → T5 → T7 → T8 → T10 → T11 → T12
Параллельно: T1+T2 | T5+T6 | T8+T9
NextBot Project: EastPay (ID: 276a96dd-9dea-41f6-8828-3666a84d85c1)
Обновлено: 2026-03-05