Воронка-бот на YAML, который никогда не спит

Стратегия роста клиента была Telegram-нативной — каждый новый пользователь приходил через бота, который отправлял приветственную последовательность, рассказывал о сообществе и подталкивал к платному тарифу. Проблема была в том, что каждый раз, когда маркетинг хотел поменять воронку — другой порядок сообщений, новые картинки, скорректированные задержки, — им нужен был разработчик, чтобы изменить код, протестировать и задеплоить. Итерации воронки, которые должны были занимать час, занимали неделю. Для команды, ведущей кампании на быстро меняющемся бразильском рынке крипто-образования, такая задержка убивала возможность экспериментировать.

Стек

  • Python
  • Telegram
  • PostgreSQL
  • Docker

как мы построили

Мы пересобрали весь бот привлечения вокруг YAML-управляемого движка воронки. Маркетинговая команда задаёт последовательности сообщений в конфигурационном файле: каждый шаг указывает тип сообщения (текст, фото, видео, стикер), содержимое, задержку до следующего шага и опциональные условные ветвления по действиям пользователя. Хотите отправить другую последовательность пользователям, пришедшим с конкретного канала? Добавьте условие. Хотите вставить 24-часовое напоминание для тех, кто не ответил? Добавьте узел задержки. Никаких изменений кода, никаких деплоев — отредактировал YAML, перезагрузил конфиг — и воронка в проде. Сам бот работает на dispatcher-архитектуре aiogram 3, где каждый шаг воронки зарегистрирован как async-обработчик, разрешающий следующее действие из конфига в рантайме.

Проверка подписки на каналы встроена — бот проверяет, вступил ли пользователь в нужные каналы, прежде чем продвинуть его дальше по воронке, и шлёт напоминания, если нет. Для реактивации APScheduler крутит систему напоминаний, триггерящую сообщения пользователям, застрявшим на конкретных стадиях воронки. Планировщик сохраняет состояние своих задач в PostgreSQL, что важнее, чем кажется, — без персистентности каждый перезапуск бота терял бы тысячи запланированных напоминаний.

крайние случаи и состояние

Что сделало проект обманчиво сложным — это крайние случаи. YAML-управляемые воронки выглядят элегантно на бумаге, но как только с ними начинают взаимодействовать живые пользователи, всё становится мутно. Пользователь шлёт случайное сообщение посреди последовательности — воронка двигается, сбрасывается или игнорирует это? Бот рестартует во время деплоя — какие пользователи были в середине воронки и где именно? Мы пришли к state machine, отслеживающей позицию каждого пользователя в воронке и проставляющей таймстемпы на каждый переход, с хранением в PostgreSQL. При рестарте бот восстанавливает ожидающие действия из состояния базы, а не полагается только на in-memory задачи планировщика.

аналитика и шлифовка

Аналитика воронки оказалась той фичей, которую клиент ценит больше всего, — и мы её чуть не построили. В исходной YAML-схеме не было ни идентификаторов шагов, ни хуков отслеживания — она проектировалась для доставки контента, а не для измерений. Когда клиент спросил «где отваливаются пользователи?», нам пришлось ретроактивно добавить step ID, логирование событий на каждый переход и построить отчёт drop-off, показывающий конверсию между шагами. Одна деталь доставила реальные неприятности: португальский контент с активным использованием эмодзи периодически ломал парсинг сообщений из-за крайних случаев кодировки в markdown-форматтере aiogram. Мы переключились на HTML parse mode во всех сообщениях воронки, и это решило вопрос чисто.

результаты

Бот теперь обрабатывает несколько сотен новых пользователей в день без ручного вмешательства. Маркетинг провёл более двадцати итераций воронки за месяцы после запуска — то, что было бы невозможно при старом подходе с зависимостью от кода. Только напоминания о реактивации, по оценкам, вернули 15–20% пользователей, которые иначе ушли бы в молчание после первого сообщения. Вывод здесь в том, что настоящим продуктом был не бот — а слой конфигурации. Возможность редактировать воронку без разработчиков превратила статичный инструмент в живую систему, которая улучшается по своему графику, а не по нашему.