Все записи
7 мин

Телеграм-бот агрегатор заказов на межгород за день — потому что инфраструктура уже была

Building in Publictelegram-ботывайбкодинг

Идея пришла за рулём, а к вечеру уже был работающий телеграм-бот, который собирает заказы на межгород из кучи Telegram-групп и шлёт мне только те города, что я выбрал. И весь фокус тут не в том что я за день что-то «закодил» (код, как обычно, писал Claude Code под моим управлением), а в том что я не стал делать новый проект, а воткнул всю эту историю как новый домен внутри уже существующего LeadRadar, где Telethon, очереди, AI-парсинг и паттерн «бот плюс мини-апп» уже были написаны и просто переиспользовались. То есть за день вышло D0-D4 по моему процессу, 103 теста из 103 зелёные, AI-экстракция заказов на gpt-4o-mini, и всё это потому что 90% фундамента уже стояло.

Ниже расскажу как именно, без прикрас, что переиспользовал, на чём спорил с GPT и что осознанно выкинул в следующую версию.

Откуда вообще взялась идея

Я сейчас катаю в такси, и пока крутишь баранку весь день, наслушаешься всякого. И вот говорят, есть много групп в телеграме, где люди выкладывают заказы на межгород: кому-то надо из одного города в другой, кто-то готов везти. Групп этих десятки, в каждой свой поток сообщений, и сидеть мониторить их руками — это занятие для человека без жизни. И я думаю: а что если снова сделать бота, который будет собирать нужные города из всех этих групп? Дословно у меня в голове так и крутилось.

Слово «снова» тут ключевое, потому что у меня уже есть LeadRadar — система, которая мониторит Telegram и через AI фильтрует, что мне нужно, а что мусор. И вот заказы на межгород — это ведь ровно та же механика, только сущность другая. Не лид, а заказ. Не «купи квартиру», а «надо из города А в город Б».

Главный вопрос: новый проект или нет

Когда приходит такая идея, первое искушение — открыть пустую папку и начать новый проект с нуля. Красиво же, чистый лист, свой репозиторий, своё имя. И я честно прикинул этот вариант, но потом сам себе ответил: «Так конечно домен. Типа система-то одна, точки выхода разные просто (боты)». То есть ядро одно, а ботов на нём можно повесить сколько угодно, каждый под свою задачу.

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

Структурно вышло так: есть `app/core` с общим ядром, и есть `app/domains/`, где лежат отдельные домены — leads, alerts и теперь новый rides. Каждый домен это своя сущность и свой бот, но Telethon-инфраструктура, очереди, паттерн AI-парсинга у них общие. Тот же доменный подход я недавно гонял и в Картаре, когда добавлял нумерологию отдельным доменом, а не новым проектом. Я Claude Code так прямо и сказал: переиспользуй паттерны короче. Не выдумывай велосипед, бери что есть.

Что переиспользовалось буквально

Самый наглядный пример — выбор городов. В домене Alerts у меня уже была таблица `cities` и интерфейс в мини-аппе, где юзер тыкает города, которые ему интересны. И для нового бота я не стал заводить новую таблицу, новый домен ссылается на ту же таблицу `cities` через внешний ключ. То есть один справочник городов на всю систему, а не три копии, которые потом разъедутся.

Интерфейс мини-аппа для выбора городов тоже сделали по образу и подобию того, что было в Alerts. Юзер заходит, отмечает нужные города, и ему начинают капать совпадения. Никакого нового дизайна с нуля, никакого «давайте придумаем как это должно выглядеть», паттерн есть, копируем, адаптируем.

Вот это и есть честный ответ на вопрос «как ты один успеваешь делать столько проектов». Да я и не делаю столько проектов. Я делаю одну систему и навешиваю на неё новые точки выхода, переиспользуя 90% того, что уже написано и работает.

Где пришлось реально подумать — match_mode

Не всё было копипастой, был один момент, на котором я завис и устроил по нему дебаты с GPT. Вопрос звучал так: когда юзер выбирает город — это город, ИЗ которого он хочет заказы, или город, В который? Для таксиста это принципиально. Я ведь стою в своём городе и хочу заказы отсюда, увезти кого-то туда, куда мне по пути.

Вариантов было несколько: матчить только по городу отправления, только по городу назначения, или по обоим сразу. И тут легко себя обмануть и сделать «по обоим, чтобы наверняка», но это размывает смысл. В итоге решили явно: по умолчанию `match_mode = FROM`, то есть выбранный город это город, ИЗ которого идут заказы. Это самый частый кейс для того, кто стоит на месте и ищет попутный груз или пассажира. А остальные режимы можно докрутить потом, если будет спрос.

Я специально это проговариваю, потому что вот такие решения, где нет «правильного» ответа из учебника, а есть только понимание реального сценария, нейросеть сама за тебя не примет. Она предложит варианты, разложит плюсы-минусы, но выбор за тобой, потому что только ты знаешь, как оно работает на земле.

AI-экстракция: как из каши сообщений достать заказ

Сообщения в этих группах — это не аккуратные формы, а живой текст: «Срочно надо завтра утром, двое плюс чемоданы, недорого». И вот из этой каши надо вытащить структуру: откуда, куда, когда. Эту работу делает AI-экстракция на gpt-4o-mini в JSON-режиме, модель дешёвая, а для такой задачи её за глаза. Если запрос падает, идут ретраи с задержками 1 секунда, 5 секунд, 30 секунд, чтобы не долбить API в стену при временном сбое.

Отдельная боль любого агрегатора — дубли. Одно и то же объявление человек кидает в пять групп сразу, и без защиты юзер получит пять одинаковых уведомлений и проклянёт меня. Сделали мягкий дедуп через Redis: `SET NX` с TTL 45 минут. То есть первое уникальное сообщение проходит, а его копии в течение 45 минут отсекаются. Просто и работает, без построения сложной системы похожести текстов, которая мне на старте даром не нужна.

Это, кстати, про принцип, которому я стараюсь следовать: ии для автоматизации процессов хорош ровно там, где задача расплывчатая (вытащить смысл из живого текста), а всё остальное пусть делает тупая надёжная инфраструктура, Redis, ключи, TTL. Не надо AI там, где справится обычный код.

Завели тестовую культуру, да

Признаюсь в стыдном: в этом проекте раньше папки `tests/` вообще не было. Вообще. Жил без тестов, на «вроде работает». И вот на этом домене решил: всё, заводим культуру. Так и сказал — заводим культуру, да. И по итогу D0-D4 закрылись с 103 тестами из 103 зелёных, тем же методом STC, по которому я раньше собирал другого телеграм-бота с нуля за неделю с 411 тестами.

Звучит как мелочь, но для соло-разработчика тесты это не про красоту, а про сон. Когда ты один и через месяц вернёшься к этому коду, ты ни черта не вспомнишь, что тут как работает. А зелёные тесты — это страховка, что когда нейросеть полезет что-то менять в соседнем домене, она не сломает молча этот. Тесты проверяют поведение, а не наличие файликов: пришло сообщение, распарсилось, совпало по городу, ушло уведомление. Если эта цепочка зелёная, мне спокойно.

Если интересно, как у меня вообще выживает вся эта Telegram-инфраструктура с аккаунтами и группами, я про это отдельно писал — про баны аккаунтов и 180 групп, там вся подноготная.

Что осознанно выкинул в V1.1

И вот тут важный момент про дисциплину. Хотелось же сразу сделать богато: и приоритеты заказов, и кнопку «взял заказ», чтобы человек прямо в боте мог застолбить заявку, и оплату прикрутить. Но я себя одёрнул. V1 — это только агрегация и выбор города. Точка. Приходят совпадения по выбранным городам, и всё.

Приоритеты, фича «взял заказ», оплата — всё это уехало в V1.1, потому что пока я даже не знаю, есть ли в этом толк вообще. Делаю-то я это в первую очередь для себя, буду катать и смотреть, реально ли оно экономит мне время и приносит заказы. А дальше, если будет толк, можно и распространять, и допиливать. Можно, конечно, было сразу пилить под продажу, но зачем, если я её как минимум для себя сделать хотел бы?

Что я из этого вынес

Главный вывод дня даже не про код, а про подход. За день получился рабочий телеграм-бот с нейросетью под капотом не потому что я гений скорости, а потому что 90% инфраструктуры уже стояло и было оплачено прошлым трудом. Спека, дебаты с GPT по спорному решению, реализация D0-D4 по процессу с тестами — всё это легло на готовый фундамент.

Когда вы один и без команды, ваш главный актив — это не скорость набора кода (его всё равно пишет нейросеть), а накопленная инфраструктура и насмотренность, какой кусок куда переиспользовать. Новый бот, который раньше был бы проектом на неделю, стал доменом на день. Вот и делайте выводы.