Платформа для запуска рекламы: от приложения для себя до веб-сервиса
Эту платформу я сделал для управления кампаниями в собственных рекламных проектах. Началась она в 2024 году как десктопное приложение на Tkinter для себя. Потом выросла в полноценный продукт: больше 80 функций, Telegram-бот и рабочее веб-приложение на Next.js и FastAPI. В веб-версии есть права доступа по ролям и шифрованное хранилище токенов. С мая по сентябрь 2026 года в репозитории появилось 826 коммитов. Сейчас у десктопа 13 вкладок, у веб-версии 228 точек API. Расход двух рекламных сетей попадает в трекер по часовому поясу площадки. Отчёты прослеживают клик до конкретного объявления и сводят воронку «регистрация → первая оплата» по приложению, стране и офферу — предложению партнёрской сети.
Запуск одной кампании — 2 часа ручной работы
В закупке рекламы запуск одной кампании — это 9 связанных шагов: составить название по схеме (магазин, продукт, страна, специалист, ниша), подобрать посадочную страницу и офферы в трекере Keitaro, сделать короткую ссылку через Cutt.ly, прописать OG-теги для превью ссылки, привязать пиксель Facebook и токен доступа, создать пост на странице в Facebook, добавить первые комментарии.
На каждом шаге легко ошибиться: имя ссылки совпадает с прошлой кампанией, указан не тот пиксель, забыта UTM-метка — и учёт конверсий ломается, а бюджет тратится впустую. Когда в день запускается 5–10 рекламных наборов, на копирование и вставку уходит больше времени, чем на работу с креативами.
Нужен был инструмент, который знает принятую схему названий и нужные интеграции и растёт вместе с задачами команды.
Четыре версии и три интерфейса
Приложение на Tkinter для macOS, сначала для себя
Первая версия написана на Python и Tkinter, с родным меню правки macOS (отменить, вырезать, копировать, вставить). Это был личный рабочий инструмент: одна форма с автоподбором посадочных страниц и офферов, проверкой полей, сохранением шаблонов и локальной историей в SQLite. В нём было около 2000 строк на Python, и он собирался в приложение .app.
Telegram-бот для команды: работа прямо в чате
Бот написан на python-telegram-bot 20 и ведёт пошаговый сценарий: задаёт вопросы по очереди, а ответы выбираются кнопками прямо в переписке. Бизнес-логика общая с десктопной версией. Специалист по рекламе создаёт кампанию с телефона, прямо из чата.
Рабочее веб-приложение с правами доступа по ролям
Веб-версия написана с нуля как полноценный сервис: Next.js 14 (App Router, TypeScript, Tailwind, shadcn/ui) + FastAPI (async SQLAlchemy 2 + Pydantic v2) + PostgreSQL 16 + Celery + Redis 7.
Шифрованное хранилище токенов на Fernet (AES-128-CBC + HMAC-SHA256): токен попадает в браузер, только если пользователь повторно ввёл пароль. Вход по JWT, пароли хранятся в виде хешей bcrypt. Каждое действие записывается в журнал в базе. Сервис развёрнут через Docker Compose на сервере Vultr, адрес сервера скрыт за Cloudflare.
Лето 2026: версии выровнены, данные приведены в порядок
С июня по сентябрь — 178 коммитов. Первая часть работы — выровнять десктоп и веб-версию, чтобы в обеих были одинаковые возможности. Было шесть кругов проверки (с 38-го по 43-й), 24 коммита с 6 по 14 июня. Каждый круг устроен так: параллельная проверка по группам вкладок, сводный список расхождений, затем исправления. Правило для исправлений — «один файл — один исполнитель»: два параллельных редактора одного файла незаметно откатывают работу друг друга. Правки одного круга пришлось откатить. Проверка типов и сборка прошли, страница отдавала код 200, но в браузере падала каждая страница после входа. Причина: компонент кнопки передавал обёртке Radix двух потомков, а она принимает ровно одного. Через два дня правки вернули, и теперь перед каждой выкладкой все страницы заранее отрисовываются.
Вторая часть — данные. Расход рекламных сетей попадает в трекер по часовому поясу площадки: для Meta — по поясу каждого рекламного кабинета, для Unity Ads почасовые данные складываются в сутки по времени Лос-Анджелеса, по которым считает трекер. Поверх этого появились разметка клика до объявления по fbclid (идентификатору клика, который Facebook добавляет к ссылке), сводки «регистрация → первая оплата» по приложению, стране и офферу, выгрузка базы офферов партнёрских сетей и движок правил, который предлагает решения по связкам «кампания трекера и её кампании в Meta».
Десять главных функций из 80 с лишним
Таблица с правкой прямо в ячейках, автонумерация постов, полоса прогресса у каждой строки и итог по каждой: успех или ошибка.
Fernet (AES-128-CBC + HMAC-SHA256), главный ключ в переменных окружения, отдельная роль vault-admin. Без повторного ввода пароля токен с сервера не уходит.
«Вы обычно берёте этот магазин с этим продуктом», подсказки по времени («14:00 — ваше обычное время публикации»).
Форма сама сохраняется в базу. Если браузер или приложение упали, черновик восстанавливается из последнего сохранения. Есть кнопка «очистить черновик».
Рамка поля становится красной или зелёной, при совпадении кнопка блокируется. В пакетном режиме имя правится само: добавляется -2, -3 или случайное слово.
Группировка по стране и магазину, применение в одно нажатие, предпросмотр перед применением, импорт и экспорт в JSON, шаблоном можно поделиться с командой.
Каждое изменение записывается со временем. Поиск и фильтры (кампания, пост, объявления, ошибки), фильтр по датам, выгрузка в CSV или JSON, переход к кампании одним нажатием.
Интервал копирования задаётся в настройках, копию можно сделать вручную кнопкой в меню, время последнего сохранения видно в строке состояния.
Предупреждение, если создаётся кампания с тем же магазином, продуктом и страной, что у существующей; рядом показывается сравнение с прошлой версией.
Cmd+1 … Cmd+0 переключают вкладки, Cmd+H открывает историю.
Это моя личная разработка, внутренний инструмент для собственной работы. Я показываю её как пример того, что умею собирать в одиночку: продукт с тремя интерфейсами, выкладка в рабочую среду, права доступа по ролям, около 80 проработанных функций. К сентябрю 2026 года у десктопа 13 вкладок, у веб-версии 33 экрана, служебных скриптов 85. В клиентских проектах я уделяю деталям то же внимание, а инструменты подбираю под вашу задачу.
На чём это собрано
- Next.js 14 (App Router)
- React 18 + TypeScript
- Tailwind CSS + shadcn/ui
- Компоненты Radix UI
- react-hook-form и проверка форм на Zod
- Sonner для всплывающих уведомлений
- FastAPI 0.115 (async)
- SQLAlchemy 2 (async) и миграции Alembic
- Pydantic v2 для всех схем
- JWT (jose, HS256, срок жизни 24 часа)
- bcrypt для паролей
- Fernet (AES + HMAC) для хранилища токенов
- PostgreSQL 16 (pgcrypto, uuid-ossp)
- Celery 5 для фоновых задач
- Redis 7 как брокер очередей и кэш
- SQLite в десктопной и Telegram-версиях
- ClickHouse трекера: данные по каждому клику для ранних правил
- Docker Compose, несколько сервисов
- Nginx (TLS, заголовки безопасности, закрытые карты исходников)
- Cloudflare DNS/CDN, адрес сервера скрыт
- Vultr VPS, Ubuntu 24.04
- Автоматические сертификаты Let's Encrypt
- Задачи по расписанию на сервере трекера: расход каждые 15 минут, отчёт по правилам раз в сутки
- Python 3 + Tkinter
- Родное меню правки macOS
- SQLite для локальной базы
- py2app для сборки .app
- python-telegram-bot 20 (async)
- Пошаговые сценарии диалога
- Кнопки в сообщениях и обработка нажатий
- Бот аналитики: карточки кампаний, пауза и запуск, решения движка правил, контроль состояния
- Keitaro 11.10 API (кампании, посадочные страницы, офферы, расходы)
- Facebook Graph API и Marketing API (посты, комментарии, страницы, кампании)
- Meta Conversions API через собственный сервис-ретранслятор событий
- Unity Ads Statistics API (расход и установки по часам)
- Cutt.ly API (короткие ссылки)
- API партнёрских сетей для сверки продаж
- Приём вебхуков со статусами
- Pytest: 221 тест в 10 файлах, 83 из них добавлены летом 2026
- Проверка движка правил на 90 днях истории без заглядывания в будущее
- Контроль состояния: 22 проверки в 7 группах каждые 30 минут
- Журнал действий в базе
- Единая обработка ошибок
- Структурированные журналы
Что изменилось в работе
в 8 раз быстрее, с защитой от дублей и проверкой всех полей
кампаний за один запуск, каждая настраивается отдельно прямо в таблице
за 10 этапов, от первой рабочей версии до полноценного сервиса
фильтр префиксов fbclid отбрасывал 492 из 672 кликов в сутки, и продажи уходили в Meta без идентификатора клика; проверено на живых журналах: 180/672 → 672/672
кликов по одной ссылке в комментарии размечаются до объявления: 13,5% дописывает Meta, 21,6% даёт собственный разбор fbclid; четыре кампании с одинаковым названием больше не сливаются в одну
сервис коротких ссылок отвечал ошибкой на запрос со своим доменом, и программа повторяла запрос 14 раз с растущей паузой; теперь она сразу переходит на домен по умолчанию
Что это дало в целом: платформа стала единым источником данных по всем рекламным запускам команды. Telegram-бот нужен специалистам в дороге, веб-приложение — для офиса и совместной работы, десктоп — тем, кому удобнее приложение на macOS. Все три версии используют общий код бизнес-логики.
Вывод для клиентских проектов: если бизнес-логика вынесена отдельно и не зависит от интерфейса, новый интерфейс — мобильное приложение, интеграцию со Slack, голосового помощника — можно добавить поверх той же серверной части, не переписывая логику.
Лето 2026: расход и сводки. Расход Meta записывается в трекер по часовому поясу каждого рекламного кабинета. Раньше кабинеты одного профиля работали в двух часовых поясах, сутки съезжали примерно на 10 часов, и часть расхода попадала на соседний день. Флаг уникальных кликов при записи убрал завышение на 10–15%. Запись идёт полной суммой за день, поэтому повторный запуск безопасен: 96 прогонов в сутки. Вчерашний день досчитывается после полуночи, пока сеть досылает поздние часы. Сводки «регистрация → первая оплата» по кампаниям второй сети собираются так: расход и установки берутся из самой сети, регистрации, оплаты и выручка — из трекера. Данные склеиваются по шестисимвольному идентификатору кампании в её названии. В сводке — цена установки, доли по шагам воронки, ROI и точка безубыточности. Доля оплат по регистрациям старше 6 часов показала, что один и тот же оффер конвертирует по-разному в разных приложениях, поэтому сначала смотрят на приложение, потом на оффер. Оплаты доходят за 24–48 часов, поэтому доля за первые сутки помечена в отчёте как заниженная.
Атрибуция клика до объявления. Фильтр префиксов fbclid в сборщике событий отбрасывал 492 из 672 кликов в сутки: продажа уходила в Conversions API без идентификатора клика, и из 4 продаж за день кабинет видел 2. Проверка по семейству префикса подняла долю до 672/672. Затем список префиксов убран целиком: такой список устаревает при следующей смене формата. 48 продаж за 7 дней отправлены заново с прежним идентификатором события, принято 48 из 48. Несколько кампаний показывают один пост с одной ссылкой, и трекер сливал их клики вместе. Поле adid внутри fbclid совпало с настоящим идентификатором объявления в 0 из 137 проверенных кликов. Поэтому таблица соответствия ведётся своя: она заполняется из кликов, к которым Meta сама дописывает идентификаторы. Итог: 262 пары, 22 объявления, ни одного конфликта, независимая сверка 361 из 361. Справочник «объявление → группа → кампания» по 10 кабинетам (912 объявлений, 760 групп, 244 кампании) собран без нарушений целостности. Повторный прогон сборщика с той же отметкой времени ничего не меняет, а раньше каждый прогон удваивал счётчики. Бот аналитики удваивал показатели трекера на одноимённых кампаниях и брал продажи только из одного потока: 10 продаж при 13 в трекере. Это давало ложный сигнал на остановку. Теперь суммируются все потоки, и отчёт сходится с трекером до цента. 34 теста на реальных образцах работают без подключения к сети.
Движок правил. Пять решений по связкам «кампания трекера × кампании Meta», от ранней остановки до масштабирования. Движок предлагает, решает человек: в трекер и кабинеты движок ничего не записывает. Данные идут из трёх источников. Трекер — единственный источник денег и воронки. Meta даёт ранг, текущий расход и состояние кабинета. Партнёрская сеть служит предохранителем через сверку продаж: если сеть показывает больше продаж, чем трекер, остановка запрещена. Пороги выведены из данных за 92 дня по всем кампаниям и совпали с правилами, которыми специалист пользовался на глаз: это точки, где ROI меняет знак, по цене уникального клика и цене перехода. Восстановление по отдельным кликам сошлось с отчётом трекера в 1 704 из 1 704 ячеек «день × кампания». Проверка на 90 днях истории без заглядывания в будущее нашла ошибку: первая версия предлагала остановить главную связку, которая потом принесла 139% прибыли аккаунта. Добавлен предохранитель «связку в плюсе остановить нельзя». Минимум продаж для решения по цене продажи (CPA) поднят с 3 до 8. Правило «худшая зона цен» теперь требует минимум 2 продажи: раньше оно выносило 9 из 17 решений при нуле продаж. Эффект правил на 90 днях — 0,8–1,8% расхода, и отчёт говорит об этом прямо. 49 тестов, ежедневный отчёт в Telegram. Карточки бота считают решение тем же движком и показывают его причину.
Бот аналитики и хранилище. Профили Meta добавляются и переключаются прямо из чата. Токен проверяется и сразу удаляется из переписки. Пауза и запуск кампаний работают под любым из токенов пользователя. Если запрос упирается в лимиты Meta, он повторяется с паузами 1, 2 и 5 секунд. Хранилище токенов веб-версии: проверка срока и прав, показ токена после повторного ввода пароля, архив с восстановлением, автоблокировка, журнал устройств входа. Оффер с пустым адресом или адресом-заглушкой создать нельзя: это правило появилось после случая, когда 33 оффера вели на заглушку.
Похожие кейсы
Играбельная реклама для мобильной игры: установок на 73% больше, каждая на 38% дешевле
Мини-уровень на HTML5 длиной 5–10 секунд с настоящей игровой механикой, собран на Cocos2d-js. Из одного кода…
Своя CRM: почта, заказы, звонки и споры по платежам на одном экране
Общий почтовый ящик, свежие данные из двух магазинов Shopify, отгрузки, звонки и возвраты собраны в одной…
Настольная программа собирает промо-страницу из описания или по ссылке
Программа на Python и tkinter собирает страницу из шаблона на Bootstrap или Astro и описания, а может взять…
Начните с аудита за 5 000 ₽: письменный отчёт и расчёт окупаемости
Расскажу, что автоматизировать в первую очередь, когда это окупится и нужен ли вашей задаче ИИ вообще. Иногда он не нужен.
Или просто задайте вопрос. В рабочее время отвечаю в течение 2 часов