Я устала листать вакансии в телеге и собрала себе агрегатор. Бесплатный, по каналам, на GitHub Pages, AI-API не нужен

от автора

Инструкция, как поднять такой же под свою профессию за вечер

Летом я искала работу продактом. Как и все, подписалась на десяток телеграм-каналов с вакансиями и каждое утро их листала. Проблемы:

— каналов много, в день в них 50+ постов, из них мне подходят два-три;

— одна и та же вакансия висит в четырёх каналах, и каждый раз ты её перечитываешь, пока не вспомнишь, что уже видела;

— фильтров нет никаких: ни по грейду, ни по городу, ни по «удалёнке», ни по ML, только глазами;

— половина постов не вакансии, а курсы, разборы резюме и «ищу работу».

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

Вообщем накодила + навайбкодила. Работает вроде chernoyarova.github.io/tg_digest + один человечек написал в линке, что вот то и то еще добавьте, я подумала фигасе, юзают чтоли. Может стоит как-то адаптировать, чтоб люди у себя смогли поднять по-быстрому. Сделала YAML-файл под любую профессию, не только для продактов. Вообщем, с работой щас не просто, поэтому может кому будет полезно форкнуть.

Что получилось

Одна статичная страница, которую генерирует скрипт раз в день:

Все каналы в одном месте. Скрипт читает папку в вашем Telegram: добавили канал в папку со следующего дня он в дайджесте. Списка каналов в конфиге нет.

Без дублей. Вакансия, которую перепостили в четыре канала, показывается один раз, с пометкой «ещё в 3 каналах» и ссылками на все.

Только вакансии. Курсы, вебинары, «ищу работу», реклама и митапы отсеиваются.

Поля вытащены из текста: компания, грейд, город, удалёнка, зарплата (если указана), теги ML/AI для продактов, React / Vue / TypeScript для фронтенда.

Фильтры и поиск: по периоду (24 часа / неделя / месяц / архив), грейду, локации (Москва, Питер, регионы, за рубежом, удалёнка), тегам, плюс поиск по тексту.

Полный пост в один клик, со всеми ссылками из него, включая скрытые.

Подборки разобраны. Если канал публикует «5 вакансий недели» одним постом, каждая становится отдельной карточкой.

Стоимость: ноль. GitHub Actions запускает скрипт раз в день, GitHub Pages отдаёт страницу, оба бесплатны для публичных репозиториев. Единственные ключи — от Telegram API, они тоже бесплатные.

Как это работает

Шесть шагов:

fetch_tg → parse → enrich → deduplicate → state → render

Telethon regex правила SequenceMatcher NEW/архив Jinja2

1. fetch_tg через Telethon читает все каналы из папки, забирает посты с прошлого запуска.

2. parse быстрый фильтр регуляркой: в посте вообще упоминается нужная роль?

3. enrich правила решают, вакансия ли это (есть ли «ищем», «требования», «откликнуться»; нет ли «курс», «вебинар», «ищу работу»), и вытаскивают поля: заголовок, компанию, грейд, город, зарплату, теги. Ничего не генерируется

4. deduplicate сравнивает тексты через difflib.SequenceMatcher, похожие на 90%+ склеивает.

5. state помечает новое, отправляет старое в архив, удаляет то, чему больше 90 дней.

6. render Jinja2 собирает одну страницу index.html с данными внутри. Фильтры и поиск работают в браузере на чистом JS, сервера нет.

Почему без нейросети

Первая версия использовала LLM для третьего шага: модель классифицировала пост и возвращала JSON с полями. Работало хорошо. А потом ключ API протух, скрипт падал с 401, и дайджест неделю не обновлялся, пока я не заметила. Плюс это единственная платная часть во всей схеме.

Я заменила модель правилами и регулярками. Стало чуть хуже: грейд теперь пустой у 38% карточек, потому что ставится только когда в посте прямо написан уровень; пара нерелевантных постов в день проскакивает. Зато ноль зависимостей, ноль денег, и точно известно, почему карточка выглядит так, а не иначе. Для личного инструмента это правильный размен. Если для вас нет — шаг enrich изолирован, туда можно вернуть любую модель.

Что требуется чтобы установить:

— открыть терминал и выполнить пять команд по инструкции;

— установить Python 3.11 (brew install python / установщик с python.org);

— завести аккаунт на GitHub и нажать несколько кнопок в настройках репозитория;

— для своей профессии — подправить регулярки. Если слово «регулярка» пугает: это выражения вида \bfrontend\b|фронтенд\w*, их за вас напишет любой ChatGPT или Claude по описанию «мне нужны паттерны для слов „тестировщик“ во всех падежах».

Программировать не нужно. Я не разработчик.

Инструкция

1. Форк и установка

Нажмите Fork на https://github.com/chernoyarova/tg_digest, потом:

bash

git clone https://github.com/<ваш-логин>/tg_digest.git

cd tg_digest

python3 -m venv .venv

source .venv/bin/activate

Windows:

.venv\Scripts\activate

pip install -r requirements.txt

cp .env.example .env

2. Ключи Telegram

Зайдите на https://my.telegram.org → API development tools → создайте приложение (название любое). Скопируйте api_id и api_hash в файл .env:

TG_API_ID=12345678

TG_API_HASH=0123456789abcdef0123456789abcdef

Затем один раз залогиньтесь, чтобы получить сессию:

bash

python scripts/generate_session.py

Скрипт спросит номер телефона, код из Telegram и пароль двухфакторки, если есть. В ответ напечатает длинную строку, вставьте её в .env как TG_SESSION_B64. Это ваш логин в Telegram в зашифрованном виде, никому его не показывайте и не коммитьте (.env уже в .gitignore). Если не получится, в конце статьи есть ссылка на статью, может поможет

3. Папка с каналами

В Telegram создайте папку (Настройки → Папки) с названием vacancy и перетащите в неё каналы с вакансиями. Всё. Название папки можно поменять в config/sources.yml, ключ tg_folder_name.

4. Первый запуск

bash

python scripts/main.py

open index.html

Windows:

start index.html

Первый запуск заберёт посты за последние 30 дней, это минута-две. Откроется страница с вашими вакансиями. Посмотрите на карточки: если в них мусор, это момент подправить правила (см. раздел про профили), а не после публикации.

5. Публикация на GitHub Pages

Чтобы страница обновлялась сама, нужно отдать те же три ключа GitHub:

1. В репозитории: Settings → Secrets and variables → Actions → New repository secret. Заведите три секрета: TG_API_ID, TG_API_HASH, TG_SESSION_B64 — значения из вашего .env.

2. Actions → digest → Run workflow. Это первый прогон в облаке, он создаст ветку gh-pages с готовой страницей.

3. Settings → Pages → Source: ветка gh-pages, папка /.

Через минуту страница появится по адресу https://<ваш-логин>.github.io/tg_digest/. Дальше workflow запускается сам каждое утро.

Одна оговорка: GitHub запускает расписания с опозданием, иногда на несколько часов. У меня сборка стоит на 06:23 МСК, а реально проходит около десяти утра. Если нужно точнее, запускайте вручную кнопкой Run workflow.

6. Своя профессия

Скрипт ничего не знает про продактов или фронтендеров. Он задаёт каждому посту четыре вопроса, а ответы берёт из файла профиля — profiles/product.yml или profiles/frontend.yml. Чтобы сделать дайджест для своей профессии, надо ответить на те же вопросы про неё. Разберу на примере QA-инженера.

Вопрос 1. Как называют вашу роль?

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

yaml

roles:

patterns:

- '\bqa\b'

- '\bтестировщик\w*'

- '\btest automation\b'

Про запись: это регулярные выражения. \b значит «граница слова» (чтобы qa не находилось внутри aqua), \w* значит «и любое окончание» (тестировщик, тестировщика, тестировщику). Больше знать не нужно; если хотите добавить слово, попросите ChatGPT или Claude: «напиши регулярку для слова „автотестер“ во всех падежах».

Вопрос 2. Какие слова похожи на вашу роль, но не она?

Слово «тест» есть и в «A/B-тест» у продактов, и в «тестовое задание». По нему стоит взять пост на рассмотрение, но нельзя решать, что вакансия про QA. Такие слова идут в loose:

yaml

loose:

- '\bтест\w*'

Вопрос 3. Какие соседние роли надо выкидывать?

Это самый важный список. Пост про разработчика может упоминать QA в требованиях («умеете работать с тестировщиками»), и по первому вопросу он пройдёт. Поэтому после того, как скрипт определил заголовок карточки, он проверяет: если в заголовке названа роль из exclude и нет вашей, карточка выбрасывается.

yaml

exclude:

- 'разработчик|developer|аналитик|analyst'

- 'продакт|product manager|проджект|project manager'

Точность дайджеста берётся именно отсюда. После первого запуска пролистайте карточки и добавьте сюда всё, что проскочило.

Вопрос 4. Как узнать вашу вакансию внутри подборки?

Некоторые каналы публикуют по пять вакансий одним постом. Скрипт режет такой пост на отдельные карточки и по короткому заголовку каждой решает, ваша она или нет. Здесь достаточно одного-двух корней:

yaml

hint:

- 'qa|тест'

Необязательное: грейды и теги.

Слова «senior», «junior», «стажёр», «lead» скрипт знает сам. Если у вашей роли есть свои названия уровней, добавьте их:

yaml

grades_extra:

Head: 'head of qa|руководител\w+ отдела тестирования'

Lead: 'qa lead|лид тестирования'

Теги это то, по чему вы хотите фильтровать сверх грейда и города. Каждый тег появится чипом на карточке и тумблером «Только …» в фильтрах. У продактов это ML/AI, у фронтендеров React, Vue и TypeScript. Для QA логично завести «Автотесты»:

yaml

tags:

- key: automation # имя поля, латиницей

label: Автотесты # надпись на чипе и тумблере

pattern: 'автотест\w*|automation|selenium|playwright'

Заголовок страницы тоже берётся из профиля:

yaml

site:

title: QA jobs

tagline: from Telegram

Собранный целиком файл profiles/qa.yml выглядит так:

yaml

site:

title: QA jobs

tagline: from Telegram

roles:

patterns:

- '\bqa\b'

- '\bтестировщик\w*'

- '\btest automation\b'

loose:

- '\bтест\w*'

hint:

- 'qa|тест'

exclude:

- 'разработчик|developer|аналитик|analyst'

- 'продакт|product manager|проджект|project manager'

grades_extra:

Head: 'head of qa|руководител\w+ отдела тестирования'

Lead: 'qa lead|лид тестирования'

tags:

- key: automation

label: Автотесты

pattern: 'автотест\w*|automation|selenium|playwright'

Осталось в config/sources.yml написать profile: qa и запустить python scripts/main.py. Этот профиль я написала для примера и на реальных каналах не проверяла. Обычно хватает двух-трёх итераций «запустила, посмотрела карточки, дописала exclude«.

Ещё одна настройка, которая понадобится не всем. Если какой-то канал вставляет в каждый пост одну и ту же строку (навигацию по своим каналам, рекламу бота), она будет лезть в заголовки карточек. В config/sources.yml для этого есть ignore_lines — список регулярок, такие строки скрипт пропускает:

yaml

ignore_lines:

- '^Вакансии\s*│'

- 'первый ai ассистент'

Либо можно не упарываться, скачайте себе клод код, скиньте ссылкой ему эту статью, подключите к клоду гитхаб и напишите какую профессию хотите добавить. Возможно самая заморочка будет только с тг если вы в РФ, по поводу тг и как получить код, статью тоже писала https://habr.com/ru/articles/1047402/

ссылка на оригинал статьи https://habr.com/ru/articles/1083086/