3 дня, два сервера и один разработчик: дневник запуска сети Telegram-каналов с вакансиями

Три дня назад я решил собрать сеть локальных Telegram-каналов с вакансиями по городам — вместо очередного кейса «как мы выросли» будет честный дневник запуска: что уже работает, что сломалось в первый же день и почему.Подписчиков пока меньше 100 в каждом канале — это не история успеха, а технический разбор того, с чем реально сталкиваешься, когда за три дня нужно от идеи дойти до работающей автоматизации.

Идея

hh.ru и «Работа России» — гигантские, но безликие агрегаторы. Локальный Telegram-канал конкретного города — противоположность: живые вакансии, прямой контакт с работодателем, без анкет на 20 полей. Вопрос был не «нужно ли это», а «как за разумное время собрать рабочий MVP, который потом можно масштабировать без переписывания кода под каждый новый город».

Архитектура: два сервиса, а не один — с первого дня

Первая версия плана предполагала монолит. От него пришлось отказаться ещё до старта разработки по банальной причине: бот должен физически работать на зарубежном VPS (доступность Telegram API из России нестабильна), а персональные данные работодателей по 152-ФЗ обязаны храниться на сервере в России. Решение — два независимых сервиса:
- Backend (Express + MariaDB, сервер в РФ) — парсит открытый API [trudvsem.ru](https://opendata.trudvsem.ru/api/v1/vacancies) («Работа России») и хранит всё, включая персональные данные.
- Bot (grammY, иностранный VPS) — «тонкий клиент»: ничего не хранит на диске, только пересылает данные в backend по HTTPS с подписью запроса (HMAC), как только пользователь подтверждает заявку.

День 1–2: грабли с API «Работы России»

Логичное на первый взгляд предположение — что можно передать в API город и получить вакансии именно из него — не подтвердилось в первый же день тестирования. У trudvsem.ru есть только полнотекстовый поиск по названию/описанию вакансии и фильтр по региону целиком (область/край). Для городов вроде Москвы это совпадает случайно (город = субъект РФ), но для условного Домодедово или Анапы — нет: область в API называется «Московская область», а вовсе не «Домодедово». Версия парсера с фильтром по региону в итоге просто ничего не находила ни для одного «неглавного» города — пришлось вернуться к более простому и честному полнотекстовому поиску с известными ограничениями точности.

День 3: систематика мелких, но обидных багов

- Контакты в готовом посте иногда превращались в `[object Object]` — поле с контактом в ответе API у части источников оказалось вложенным объектом, а не строкой.
- Каждый рестарт сервиса bot (а на третий день их было немало) неожиданно приводил к лишней публикации — оказалось, я сам добавил «публикацию сразу при старте», не подумав, что systemd перезапускает процесс при любом сбое.

- Сетевой таймаут при обращении к trudvsem.ru падал без единой попытки повтора — добавил retry с экспоненциальной задержкой, обычная гигиена для внешних интеграций, которую легко забыть на старте.

Ничего из этого не является открытием — это ровно тот набор шероховатостей, который вылезает в первые дни любого интеграционного проекта, если делать его быстро и в одиночку.

Локализация персональных данных — не формальность

С самого начала было решено разносить бота и backend физически по разным юрисдикциям — но при ближайшем разборе 242-ФЗ выяснилось, что даже здесь нет единого мнения юристов о том, что именно считается «первичным сбором» персональных данных. Практический компромисс на данном этапе: бот не хранит на диске ничего, весь ввод пользователя уходит в backend в момент подтверждения заявки, а сама база физически находится в российском ЦОД.

Что дальше

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