Как я научил нейросеть искать велопокатушки за меня
Анонсы любительских велозаездов в России живут в сотнях телеграм-чатов: «в субботу катим от метро, темп бодрый, кто с нами — плюсуйте». Найти, куда поехать в выходные, — значит вручную листать десятки чатов. Я собрал конвейер, который сам находит эти чаты, читает анонсы через LLM и складывает их в единый календарь — dodofo.ru. Рассказываю, как это устроено и что получилось: 192 источника, сотни заездов Москвы и Питера, и всё это крутится на двух дешёвых VPS.
Боль
Я велосипедист. Каждую пятницу у меня один и тот же ритуал: открыть десяток телеграм-чатов и понять, куда можно выехать в выходные. Проблема в том, что велодвижуха в России почти целиком живёт в Telegram, и у неё нет ни одной точки входа:
- у каждого клуба и района свой чат — «Вело-ЮЗАО», «Строгино», рандоннёры, шоссеры, гравийщики;
- анонс — это простыня текста в свободном формате: где-то есть дата и точка старта, где-то «как обычно, от той же беседки»;
- маршрут — отдельной ссылкой на сторонний сервис: чтобы просто взглянуть на трек, надо куда-то переходить, и так по каждому заезду;
- главный вопрос — «а мне туда вообще можно?» — решается вручную: сидишь, тыкаешь по анонсам и трекам и прикидываешь, где покатаешься комфортно, а где либо не вывезешь, либо будешь абузой для группы;
- и это ещё не всё: надо понять, как добраться до старта, и есть ли вообще заезд поближе к дому;
- а через два дня анонс тонет под сотней сообщений «а какая резина у тебя?».
Агрегаторы типа афиш существуют для гранфондо и больших марафонов, но 90% реальной жизни — это клубные покатушки на 20–100 человек, которые нигде, кроме своего чата, не анонсируются. В Telegram есть и каналы-агрегаторы покатушек, но у них та же беда, только с другой стороны: чтобы туда попасть, организатор должен специально написать админу канала. Большинство оргов этого просто не делает — им и в своём чате хорошо.
Идея: пусть анонсы читает машина
Руками это не собрать: source-база — сотни чатов, формат — произвольный текст. Но это идеальная задача для LLM: «вот пост из чата, скажи, это анонс заезда? Если да — вытащи дату, время, точку старта, дистанцию, темп, дисциплину».
Так появился dodofo.ru — календарь велозаездов, который наполняется автоматически. Человек в конвейере остался только один — я, в роли модератора.
Шаг 1. Найти чаты — тоже через Telegram
Первая проблема: откуда взять список источников? Гуглить «велочат Бутово» бесполезно — половина чатов нигде, кроме самого Telegram, не засвечена.
Решение: у Telegram есть внутренний поиск по публичным чатам и каналам (метод contacts.search в MTProto API). Я написал утилиту, которая гоняет по нему матрицу запросов «велотермины × география»: вело/шоссе/гравел/МТБ/бревет × города Подмосковья и Ленобласти, потом вторым заходом — × районы Москвы и Питера. Получилось порядка тысячи запросов (с паузами — Telegram строго следит за флудом).
Дальше ручная курация: выкинуть мото и самокаты, магазины, прокаты и веломастерские, чаты других регионов. После фильтра в базе осталось 192 живых источника — от многотысячных каналов до районного чата на полсотни человек.
Шаг 2. Конвейер: прочитать, понять, опубликовать
Дальше работает конвейер из четырёх стадий:
- Скрейп. Коллектор по MTProto-сессии читает новые посты из всех источников.
- Пре-фильтр. Дешёвый локальный классификатор отсеивает очевидный мусор — обсуждения, мемы, продажу колёс. Это важно для экономики: LLM-вызовы стоят денег, и жечь их на сообщение «кто-нибудь едет завтра?» жалко. До нейросети доходит только малая часть потока.
- LLM-разбор. Кандидатов читает Claude и возвращает структуру: дата, время, точка старта, маршрут, дистанция, темп, дисциплина, требования. Параллельность ограничена, очередь переживает и флуд-вейты Telegram, и лимиты API.
- Дедупликация. Самое неожиданно сложное место. Один и тот же заезд анонсируют в пяти чатах, с разными формулировками и опечатками. Дедуп детерминированный: окно по дате, сигналы «ссылка на один маршрут» и «совпадение гео+времени», название — только вспомогательный модификатор. Слить два разных заезда страшнее, чем показать дубль, поэтому явно разное время старта — жёсткий блокер слияния.
И одно принципиальное правило: уважение желания кататься своей компашкой. Если в анонсе написано «только для своих», «не для репоста», «инфа для чата» — такой заезд в календарь не попадает. Публичный календарь — для тех, кто рад новым людям, а не способ притащить толпу туда, где её не ждут.
Результат — карточка заезда с нормальной структурой вместо простыни текста.
Шаг 3. Архитектура: два сервиса в двух странах
Техническая деталь, которая может пригодиться другим: система разрезана на два сервиса.
- Коллектор (скрейп + LLM-разбор) живёт на VPS в Европе — оттуда без танцев доступны и Telegram API, и API нейросетей.
- Сайт и основная база живут на хостинге в России — быстрый отклик для аудитории и задел под 152-ФЗ (впереди пользовательские аккаунты).
Между ними — узкий внутренний контракт: коллектор копит результаты в буферной базе и пушит готовые структуры по HTTP с идемпотентностью (повторная доставка не создаёт дублей). Если один сервис лежит — второй продолжает работать.
Стек скучный и дешёвый: Go на бэке, Vue 3 на фронте, PostgreSQL + PostGIS, nginx, Docker, GitLab CI. Два маленьких VPS суммарно дешевле подписки на пару SaaS-сервисов.
Что получилось
- 192 источника — чаты и каналы Москвы, Подмосковья, Питера и Ленобласти;
- сотни заездов в календаре с начала года — от вечерних покатушек до бреветов на 200 км (зимой, как выяснилось, велочаты массово катаются на лыжах — конвейер честно тащит и это);
- у каждого заезда — своя страница с ЧПУ вида /velozaezdy/moskva/2026-07-19/nazvanie, страницы городов, дисциплин и организаторов — календарь заодно работает как SEO-каталог;
- фильтры: город, дисциплина (шоссе/гравел/МТБ/бревет), дата.
Грабли, на которые я уже наступил
- «Как обычно, от беседки». Часть анонсов понятна только своим: без даты, без точки. LLM тут честно пасует — такие посты уходят в скип. Лечится только контекстом источника (домашний город чата, история прошлых заездов).
- Афиши-картинки. Многие организаторы постят красивую афишу, а в тексте — пусто. Учу конвейер разбирать картинки через vision-модель: гейт срабатывает, когда текст поста ни о чём не говорит.
- Экономика LLM. Наивный вариант «шли всё в нейросеть» умножает счёт на порядок. Пре-фильтр до LLM + дешёвая модель для рутины + кэширование промптов — и разбор поста стоит копейки.
- Дедуп без жалости. Сначала я боялся агрессивного слияния дублей. Потом посмотрел на выдачу с тремя копиями одного заезда и перестал бояться: лучше одно слитое событие с несколькими источниками, чем три карточки-близнеца.
Что дальше
Календарь — это первая, «read-only» часть продукта. Дальше много различных планов по развитию сервиса, но пока не готов с ними делиться на широкую аудиторию.
Сейчас мне важнее другое: полезен ли такой календарь вам?
Если вы катаете в Москве, Подмосковье, Питере или Ленобласти — загляните на dodofo.ru и скажите, чего не хватает. Если знаете живой велочат, которого нет в выдаче, — напишите в комментариях, добавлю в источники. И отдельно интересно: какой город добавлять следующим?