Мы периодически летаем на Бали, и с каждым годом наша компания растёт. Сначала летали вдвоём с женой, потом втроём с дочкой, а в этом году полетит вообще 4 взрослых + 1 ребёнок.
В декабре 2025, устав от уральской зимы, я открыл сервис по поиску дешёвых авиабилетов. Билеты по маршруту Екатеринбург–Москва–Бали стоили дорого. Подписался на уведомления о ценах и стал ждать.
Через две недели приходит пуш: «Твои билеты подешевели!» Открываю, смотрю цену… а там не учитывался детский билет. Решил, что я сам накосячил. Удалил маршрут, создал новый — тщательно проверил все фильтры. Через неделю история повторяется. Цена приходит без учёта части заданных параметров. Обидно.
Пуши не помогли, начал мониторить вручную. Мне важно:
Диапазон вылета 25 февраля – 15 марта (хочу попасть на балийский Новый год 19 марта)
Длительность пребывания 27–29 дней (чтобы не продлевать визу VOA и не переплачивать за жильё)
У всех агрегаторов есть «гибкие даты», но я никогда не видел в них пользы. Вбиваю вылет 25.02–15.03, хочу быть в стране 27–29 дней → возврат должен быть 24.03–15.04.
Что я, ожидаемо, вижу:
«Вылет 15.03, возврат 24.03» — 9 дней. Мало.
«Вылет 25.02, возврат 15.04» — 49 дней. Много.
Единственный способ найти билеты с пребыванием на Бали 27–29 дней — это искать руками. Спустя неделю ручной мониторинг начал бесить.
Я тимлид, но в последнее время отошёл от написания кода. Давно хотел проверить, может ли AI написать настоящий продукт. Поставил себе челлендж: весь код пишут нейронки, я ни строчки не пишу сам.
Промпт в ChatGPT: «Напиши скрипт на JavaScript, который ходит в OpenAPI агрегатора и ищет билеты». AI за 5 минут накидал код. Запускаю — работает! Сравниваю цену из скрипта с ценой на сайте.
Цены разные. Существенно.
Спрашиваю у ChatGPT. Поясняет: OpenAPI отдаёт кэшированные данные. То есть какой-то пользователь искал такой же маршрут, результат записался в кэш, и теперь отдаётся мне. При этом на сайте цена уже другая.
Грустно. Надо переделывать.
Раз уж через OpenAPI цены не актуальные, а актуальные цены получает реальный пользователь, то я выбрал эмулировать поведение реального пользователя через автотесты.
Промпт AI: «Напиши скрипт с использованием Puppeteer, где он: открывает сайт, устанавливает фильтры, сортирует по цене, делает скриншот (пруф) и сохраняет результат в SQLite.
AI накидал базовую реализацию. Запустил — работает! Цены актуальные.
Чтобы перебрать варианты с длительностью 27–29 дней, генерирую все возможные пары дат по маршруту:
Вылет 25.02 + 27 дней = возврат 24.03
Вылет 25.02 + 28 дней = возврат 25.03
Вылет 26.02 + 27 дней = возврат 25.03
… и так далее
Математика:
Диапазон дат вылета: 18 дней
Вариантов длительности: 3 (27, 28, 29)
Авиакомпании: 2 (Etihad и Emirates)
Итого: 108 комбинаций. Проверка запускалась каждый час на локальной машине, чтобы не упустить выгодные билеты. Скрипт работал по 40 минут на все комбинации.
Через 4 часа глянул логи — ошибки. Система защиты агрегатора забанила мой IP.
Я подождал снятия блокировки и вместе с AI подобрал:
Адекватные тайминги между действиями
Эмуляцию действия пользователя
Задержку между проверками
|
Категория |
Механизм |
Назначение |
|---|---|---|
|
Анти-детект |
|
Скрыть признак автоматизации |
|
|
Имитация настоящего Chrome | |
|
Fake plugins |
Подмена списка плагинов | |
|
Задержки |
|
Случайная пауза перед загрузкой |
|
|
Пауза после каждого фильтра | |
|
|
Пауза между batch URL | |
|
Клики |
|
Эмуляция клика по чекбоксу |
|
|
Клик по label элемента | |
|
Drag & Drop |
MouseDown → Move → Up |
Полная эмуляция перетаскивания слайдера |
|
Расчет координат |
Преобразование значения в пиксели | |
|
setTimeout chain |
Задержки между событиями мыши | |
|
Ожидания |
|
Ожидание загрузки всех источников цен |
|
|
Ожидание появления DOM элементов | |
|
Adaptive retry |
Повторные попытки при изменениях |
Сработало — блокировок больше нет. Скрипт крутился локально, я проверял результаты руками.
Подходить к ноутбуку каждый час — не вариант. Создал новый промпт для AI: «Добавь отправку уведомлений в Telegram». За вечер бот был готов. Оповещения приходят на телефон, блокировок нет. Ноутбук пашет 24/7.
Появилась потребность купить билеты в Питер для родственника на 3 недели. Да еще и бюджет на билеты был ограничен. За час скрипт нашёл билеты вдвое дешевле бюджета. Родственник купил, улетел довольный, скоро уже вернётся домой.
Тогда до меня дошло: такая проблема не только у меня.
Нашёл десяток ботов в Telegram и сервисы с подписками. Изучил, все работают через OpenAPI. То есть отдают кэшированные цены.
Рассказал о своём боте коллегам. Люди заинтересовались, нагрузка начала расти, а текущая архитектура не масштабируется. Пользователи создали маршруты на 400 комбинаций, которые прогонялись 5+ часов, а это долго. Можно упустить дешевый билет.
Нужно переделывать с нуля, но уже проектировать масштабируемую систему.
Новая система должна отвечать требованиям:
VPS и прокси
Производительность — проверять 1000+ комбинаций в час
Тарификация комбинаций
Админка как система мониторинга
Система уведомлений — не должна заспамить пользователей
UX — простой интерфейс для создания маршрутов
Оплата тарифов
Чтобы не убивать свою локальную машину нашёл VPS за 800 ₽/мес, залил код. AI помог настроить окружение и PM2.
Прокси: всего купил 17 резидентских прокси на 1300 ₽/мес, настроил ротацию прокси под авторизационные куки.
Запустил проверки на VPS сразу в 3 потока из-за чего VPS стал уходить в перегрузку (CPU+RAM под 200%). Решил оптимизировать алгоритм проверки цен на билеты.
Узким местом оказались автотесты. Пришло время от них отказаться и реализовать поиск билетов по API. Повысить производительность удалось путём реверс-инжиниринга фронтового API агрегатора.
В DevTools нашёл основные запросы, скормил нейронке. В паре с AI исследовал основные поля, загрузку данные и прочее. Через пару дней удалось написать алгоритм получения цен по API.
В результате на проверку 400 комбинаций стало уходить всего 32 минуты (быстрее в 10 раз). Запас прочности — до 1000 комбинаций/час можно достичь увеличением числа прокси и кук.
Но надо грамотно выстроить тарификацию — количество маршрутов и комбинаций на пользователя. Чем больше комбинаций создает пользователь, тем больше требуется прокси, зависимость линейная.
Для тарификации решил идти по пути подписок — всего 2 модели для пользователей. Такие модели отвечают на все запросы пользователей, и даже в бесплатной версии можно спокойно пользоваться ботом.
Для платной подписки уже доступно 350 комбинаций максимум, для чего требуется 2 прокси. 1 прокси обходится мне в 70 ₽. Чистая маржа - 59 ₽ с пользователя с платной подпиской и то она уходит на VPS.
Количество пользователей постепенно росло. Для проактивного отслеживания основных метрик создал админку. Всё было написал AI. К тому моменту перешёл на AI-агента Claude Code с моделью Claude Opus 4.6 — это, конечно, зверь.
В админке отображается:
длительность проверок цен
количество комбинаций по маршрутам
CTR, retention, DAU/WAU/MAU
информация по маршрутам пользователей
С самого начала я логировал некоторые события нажатия кнопок в боте. Никакой глубокой цели в этом не было — просто чтобы после релиза новой фичи быстро понять, всё ли работает. Логи нигде не хранились, смотрел в моменте.
Почему бы не сохранять часть этой информации в базу? Всё равно уже собираю DAU/WAU/MAU, а добавить несколько полей в базу — не проблема. Добавил события на создание маршрута, визуализировал, и тут нашлась проблема в воронке создания маршрута.
Схема работы системы логирования
В воронке увидел, что часть пользователей не завершают создание маршрута и спотыкаются на этапе ввода дат. Именно в этот момент срабатывала валидация и сообщала, что комбинаций слишком много.
Фатальная ошибка. Изначально в бесплатном тарифе было ограничение — 20 комбинаций для маршрутов с гибкими датами. Пытаясь побудить пользователей взять платную подписку, я просто не давал клиенту "родиться" и пользователь уходил даже не создав свой первый маршрут.
Я увеличил лимит в бесплатном тарифе до 50 комбинаций. Узкое место в воронке исчезло — процент успешно созданных маршрутов вырос, а отток на этом шаге снизился.
Вывод: даже простая аналитика действий пользователя помогает находить узкие места, о которых не догадываешься на этапе проектирования.
Теперь предстояло выстроить адекватную систему уведомлений.
Первая версия системы уведомлений присылала вообще все результаты проверок. А проверки выполняются каждый час. Один пользователь получил 15 уведомлений за 2 часа (много маршрутов) и попросил их отключить.
Во второй версии уведомления только если цена снижалась, но динамика снижения цен разная. Бывало, что каждая проверка с каждым разом находила билеты всё дешевле и дешевле и это спамило пользователей.
Переделывал еще несколько раз. Сейчас у меня сложный скоринговый алгоритм, учитывающий:
процент снижения
частоту изменений
время суток
историю уведомлений пользователя
среднюю цену
Это, если честно, тянет на отдельную статью. Но для затравки скину схему работы текущей системы уведомлений.
Система уведомлений
С последней системой уведомлений заспам пользователей уменьшился и ключевые метрики улучшились:
CTR: 11.4% (каждое 9-е уведомление ведёт к открытию)
Retention: ~20% (показатель низковат, но в целом для подобного продукта это хороший показатель)
Мой принцип: бот должен быть простым. Только поиск билетов. Никакого лишнего функционала. Бот не хранит даже имя пользователя, только chat_id.
AI реализовал:
создание маршрутов через диалог
тепловую карту цен (какой час/день выгоднее)
график цен
историю найденных предложений
Для приёма платежей подключил ЮKassa через BotFather. По умолчанию была доступна только оплата картам, ЮMoney, SberPay.
Но: BotFather не умеет в СБП. Кто в 2026 оплачивает не через СБП?!
Пришлось через API ЮKassa делать оплату самостоятельно. AI справился за день. Промпт в скрытом. Этот и подобные промпты я храню в репозитории для истории.
Промпт**Задача**: Мигрировать оплату подписки с Telegram Payments API на прямую интеграцию ЮКасса API **Контекст**: - Текущий код: `handlers/subscriptionHandlers.js` использует токены через BotFather - Проблема: нет поддержки СБП (доступен в личном кабинете ЮКассы) - Решение: прямое API ЮКассы с webhook **Что сделать**: 1. Переписать `handlers/subscriptionHandlers.js`: - Заменить `sendInvoice` на создание платежа через ЮКасса API - Генерировать URL оплаты, отправлять пользователю - Обрабатывать статусы платежей 2. Добавить webhook в `web/server.js`: - Принимать уведомления от ЮКассы о статусе платежа - Валидировать подпись запроса - Обновлять статус подписки в БД 3. Дополнить `.env.example`: - Добавить переменные для ключей ЮКассы с примерами *** 1. **Структура данных**: в файле [database.js](../../config/database.js) 2. **База данных**: тут: [database.js](../../config/database.js) 3. **Тарифные планы**: в БД в таблице subscription_types или в документации в [subs.md](../subs.md) 4. **Текущий флоу**: посмотри в файле [subscriptionHandlers.js](../../handlers/subscriptionHandlers.js) 5. Старый функционал оплаты сохранять не надо
Схема алгоритма оплаты.
В версии 3.0 было два типа маршрутов: фиксированные даты и гибкие (диапазон + дни). Но я бы сильно хотел один раз вбить свой полный маршрут — Екатеринбург → Москва (1д) → Бали(27-29д) → Москва(1д) → Екатеринбург и просто получать оповещение о полной цене.
Можно, конечно, создать два маршрута, но стыковать остановку придётся вручную. А ведь здесь бот может проявить себя на полную.
Сам промпт писал в Claude Sonnet, дабы не съедать много токенов. Перед этим, конечно, сам накидал драфт, затем скормил уже Opus.
Opus в режиме планирования выел все токены за 4-часовой лимит. С корректировкой плана ушло 3 сессии.
Потом приступил к реализации.
Я выпал. Opus реализовал всё корректно. Написал тесты (включая интеграционные). Мне оставалось только запустить и наслаждаться.
Использую Opus только для сложных задач, где страшно сломать обратную совместимость. Подписка на два аккаунта спасает — при лимите переключаешься и просишь продолжить. Магия.
Запустил фичу локально, получил общую цену маршрута ~600 к. В проде же бот выдал цену за отдельные участки: Москва (80 к) + Бали (350 к) = 430 к. Не понял почему суммы отличаются и пустил в релиз.
На следующий день решил проверить, что именно нашёл бот:
Москва → Бали: 239 к
Бали → Москва: 341 к
Сумма: 580 к
Открываю сервис-агрегатор, вбиваю те же даты сам, но ищу не два отдельных билета в один конец, а указываю дату возврата (билет туда-обратно). Вижу цену значительно ниже — около 435 к.
Такая же проблема с билетами Екатеринбург – Москва: бот нашёл 35 427 ₽ (туда) + 47 155 ₽ (обратно) = 82 582 ₽. С датой возврата на сайте: 80 000 ₽.
ОТКРЫТИЕ: сумма двух one-way билетов НЕ равна цене round-trip. Разница может быть огромной.
Попросил Opus переписать алгоритм следующим промптом.
Промпт для исправления составных маршрутовСейчас в файле @TripOptimizer есть функция generateBatchItems. Она генерирует комбинации мо ногам трип. Если я создаю такой трип: 🗺️ SVX → MOW → DPS → MOW → SVX 📅 25.02.2026 – 15.03.2026 1️⃣ Екатеринбург → Москва: 1-1 дн. | 👥4взр+1дет | ✈️Любая | 🧳багаж | 🔄любые пер. 2️⃣ Москва → Денпасар (Бали): 28-29 дн. | 👥4взр+1дет | ✈️Etihad Airways | 🧳багаж | 🔄до 1 пер.(5ч) 3️⃣ Денпасар (Бали) → Москва: 1-1 дн. | 👥4взр+1дет | ✈️Etihad Airways | 🧳багаж | 🔄до 1 пер.(5ч) 4️⃣ Москва → Екатеринбург | 👥4взр+1дет | ✈️Любая | 🧳багаж | 🔄любые пер. 🔍 78 проверок (API-вызовов) то это 78 комбинаций на выходе. И потом будет найдено 4 билета. Найденные цены такие: 1. 25.02 — 35427 ₽ 2. 26.02 — 239982 ₽ 3. 27.03 — 341662 ₽ 4. 28.03 — 47155 ₽ Билеты в один конец все. То есть отдельно билеты идет от екб до москвы, потом с Москвы до Бали, потом с бали до москвы, потом с москвы до екатеринбурга. НО если я на сайте смотрю билеты не в один конец, то суммарная цена ниже. К примеру, на те же даты я ищу билеты с Москвы до Бали и еще указываю дату возврата (26.02-27.03), то я получаю цену 435893. Но если я просуммирую два отдельных билета в один конец на те же даты, то это будет 239982 (26.02 до бали из москвы)+341662(27.03 до москвы из бали), что существенно выше. Такая же проблема по билетам екатеринбург москва. Бот нашёл билеты на 25.02 из Екб в Мск и на 28.03 из мск в екб по цене 35427 + 47155, но если я указываю дату туда-обратно, то получаю цену 80000. В общем моё, предложение: если в трипе указаны ноги где совпадают пары вылет-прилет, то надо генерировать еще дополнительно комбинации с датой возврата. То есть в моем примере будет 77 комбинаций + сколько-то комбинаций для билетов екб-мск-екб и мск-бали-мск. Важно: не факт, что билеты с датой возврата будут дешевле, поэтому считать самый дешевый вариант в функции findBestCombination надо, перебирая все комбинации Внеси исправления и посчитай мне количество комбинаций в таком случае.
Новая логика: если в маршруте есть пары A→B и B→A, бот ищет два отдельных one-way билета и один round-trip билет и выбирает самый дешёвый.
Результат: пользователи начали находить билеты на 30-40% дешевле, чем раньше.
Открыл другой популярный сервис, вбил маршрут с гибкими датами — цена 492 000 ₽.
Тот же сервис в соседней вкладке, те же даты, но явно указал, что нужен билет с датой возврата (round-trip) — цена 352 000 ₽. На 35% дешевле! Тот же рейс, те же даты.
Пруфы
185 активных пользователей (за последние 2 недели)
77 созданных маршрутов
3 билета куплено (пользователи подтвердили)
5 платных подписок (995 ₽/мес — окупают часть расходов)
~20% средняя экономия от средней цены
Лучшее предложение: билет за 52% от средней цены агрегатора
Технический стек:
JavaScript (без фреймворков)
SQLite
Puppeteer (получение кук)
PM2 (управление процессами)
Telegram Bot API
ЮKassa (платежи через СБП и карты)
Расходы vs Доходы:
Расходы: 2100 ₽/мес (VPS 800 ₽ + прокси 1300 ₽)
Доходы: 995 ₽/мес (5 подписок × 199 ₽)
До полной окупаемости: ещё 6 подписок
Время разработки:
Версия 1.0–2.5: 1 неделя (вечера)
Версия 3.0: 2 недели (вечера + выходные)
Версия 3.1: 3 дня
Процент кода от AI: ~90%
Мой вклад: промпты, тестирование, проектирование архитектуры.
AI может написать настоящий продукт. Не игрушку, не MVP на коленке, а рабочий SaaS с монетизацией.
Реверс-инжиниринг API с AI-ассистентом — это весело. Скармливаешь данные из DevTools, AI описывает логику и пишет код.
Несмотря на это, нужно развиваться в промпт-инжениринге. На мой взгляд, тут все как в продуктовой разработке - чем лучше сформулирована задача, тем дешевле будет разработка. Пишу корявые промпты — трачу токены на то, чтобы допилить напильником, но если вдумчиво написать промпт, то получаю результат с первого раза.
Я продолжаю разработку бота, придерживаясь своих принципов:
Бот решает одну задачу — ищет билеты. Никакого feature bloat.
Своевременные уведомления — без спама.
Весь код пишет AI — челлендж для себя и проверка возможностей моделей 2026.
Ближайшие планы:
Полная окупаемость (ещё 6 подписок)
Улучшить систему уведомлений (сейчас CTR 11.4%, хочу 15%+)
Возможно, web-app хотя бы для создания маршрутов — это явно удобнее, но я скептически отношусь к web-app.
Долгосрочные планы:
Надеюсь, найду силы написать отдельную статью про систему умных уведомлений
Масштабирование на 500+ пользователей
P.S. Если интересно, как устроена система уведомлений (алгоритм баланса спама и полезности) или хотите увидеть промпты для Claude Opus — дайте знать, будет для меня дополнительной мотивацией.
Источник


