Skip to content

AgentShekel/hh-bot

Repository files navigation

Русский · English

hh-bot

A Telegram bot that automates job search and applications on hh.ru with LLM-based relevance scoring and auto-generated cover letters.

What it does

  • Search vacancies via configurable filters using browser automation (Playwright).
  • Pre-filter by an optional title blacklist (empty by default — you add title fragments of role types that are off-target for you), skipping obvious mismatches before spending LLM tokens.
  • Score each vacancy via any OpenAI-compatible LLM (Groq, DeepSeek, OpenRouter, Z.AI, Together, OpenAI, local Ollama, etc.). The analyzer prompt is generic — it scores fit against the candidate profile from prompts/analyzer_summary.txt and ships with an inline guide on how to tighten scoring (role-type rules, anti-domains, base scores) for your own profile.
  • Route by score (thresholds configurable):
    • SCORE_AUTO_APPLY (default 90) → auto-apply with a generated cover letter — only when AUTO_APPLY_ENABLED=true (off by default).
    • middle band → sent to Telegram for manual decision (buttons: «Apply / Skip / Open on hh.ru»).
    • < SCORE_AUTO_SKIP (default 10) → auto-skip.
  • Cover letter generation uses your prompts/candidate.txt profile, goes through an adversarial self-critique pass (regenerates on fabricated numbers / HR clichés / off-tone phrasing).
  • Apply by link: paste an hh.ru vacancy URL into the Telegram chat and the bot fetches it, scores it, drafts a cover letter, and shows it with Send / Rewrite / Cancel buttons — no need to wait for the autopilot to find it. Handles regional subdomains and tracking query params; refuses to double-apply.
  • Autopilot: background search loop (paced to avoid hh.ru anti-bot throttling) + a periodic summary posted to your Telegram chat.
  • Inbox monitor (messages_loop): checks hh.ru negotiations every 5 min, tags incoming as TEST_TASK / QUESTION / MESSAGE, pings you in Telegram.
  • Company blacklist (whole-word match), employer rating floor, «remote-only» filter, cross-city deduplication.
  • Telegram channel monitor (optional): reads public job channels via their web mirror (t.me/s/<channel>, no API keys), extracts and scores posts, and pings relevant ones to your Telegram — runs alongside hh.ru.

Disclaimer

Using automation on hh.ru may violate the user agreement and result in account suspension. Use at your own risk. The bot is a pet project for a single user — multi-tenant deploy is not intended and the code has not undergone legal review.

Stack

  • Python 3.10+ (requires Browser | None syntax)
  • aiogram 3.x — Telegram bot
  • playwright — hh.ru browser automation
  • httpx[socks] — HTTP client with SOCKS5 proxy support
  • python-dotenv — config
  • sqlite3 (Python stdlib) — storage

Installation

git clone https://github.com/<YOUR_USERNAME>/hh-bot.git
cd hh-bot

python -m venv .venv
# Windows:
.venv\Scripts\activate
# macOS/Linux:
source .venv/bin/activate

pip install -r requirements.txt
playwright install chromium
playwright install msedge   # for interactive login — visible Edge window

Configuration

Copy .env.example to .env and fill in values:

cp .env.example .env

Minimal .env:

TELEGRAM_BOT_TOKEN=...           # get from @BotFather
TELEGRAM_ADMIN_ID=...            # your Telegram user_id (ask @userinfobot)
HH_LOGIN=+79001234567            # phone number for hh.ru

LLM_BASE_URL=https://api.groq.com/openai/v1
LLM_API_KEY=...
LLM_MODEL=llama-3.3-70b-versatile

RESUME_FILE=resume.txt

Resume

Place resume.txt in the project root — a plain-text file with your résumé. The bot uses it as candidate context when scoring vacancies and generating cover letters.

LLM provider

Any provider with an OpenAI-compatible Chat Completions API is supported. Examples in .env.example. Easiest is Groq (free tier):

  1. Sign up at https://console.groq.com
  2. Create an API key
  3. Set LLM_API_KEY

Proxy (optional)

If your LLM provider is blocked on your network:

LLM_PROXY=socks5://user:pass@host:port

Run

python main.py

On first run the bot will prompt for authorization. In Telegram, send:

/login

A visible Edge browser window will open — log in to hh.ru manually (captcha, SMS, whatever). The bot will pick up the session and switch to headless mode.

Bot commands

Command What it does
/start Status, command list
/login Open the browser for interactive hh.ru login
/addfilter Add a search filter (title, keywords, city, salary, experience)
/filters List active filters
/search Run a manual search across all filters
/autopilot Trigger one autopilot cycle manually
/recheck Re-score un-scored vacancies
/stats Application statistics

Architecture

hh-bot/
├── main.py                       # Entry point: bot + autopilot
├── config.py                     # Config from .env
├── ai/
│   ├── llm_client.py             # Universal OpenAI-compatible client
│   ├── analyzer.py               # Vacancy relevance scoring (role-type +
│   │                             #   AI-focus + anti-domain gates)
│   ├── vacancy_analyzer.py       # Deep structured JD analysis for
│   │                             #   cover-letter targeting
│   └── cover_letter.py           # Cover letter generation + adversarial
│                                 #   self-critique
├── bot/
│   ├── handlers.py               # Telegram commands and callbacks
│   ├── autopilot.py              # Background search + filter + apply loop
│   ├── messages_loop.py          # hh.ru negotiations inbox monitor
│   └── keyboards.py              # Inline keyboards
├── parser/
│   ├── hh_client.py              # Playwright client for hh.ru
│   └── tg_client.py              # Reads TG channels via t.me/s/ web mirror
├── scripts/
│   └── tg_pull_test.py           # Manual smoke test for TG channel parser
├── prompts/
│   ├── candidate.example.txt     # Template — copy to candidate.txt
│   ├── analyzer_summary.example.txt
│   └── candidate.txt             # YOUR private profile (gitignored)
├── data/
│   └── tg_channels.example.yaml  # Template TG channel list
└── db/
    ├── models.py                 # SQLite schema
    └── storage.py                # CRUD

Security

  • Access to all commands is restricted by TELEGRAM_ADMIN_ID — the bot is single-user.
  • hh.ru cookies are stored in hh_cookies.json (in .gitignore).
  • Vacancy descriptions are isolated with markers in the LLM prompt — partial protection against prompt injection via vacancy text.
  • All secrets via .env, nothing is hardcoded.

Telegram channel monitor (optional)

The bot can also ingest job posts from public Telegram channels (@datasciencejobs, @workinai, @prog_jobs, etc.) alongside hh.ru. Channels are read through their public web mirror (https://t.me/s/<channel>) — no API keys, no my.telegram.org, no login or session files.

Setup:

  1. Copy data/tg_channels.example.yaml to data/tg_channels.yaml, list the channels you want and tune role_filter per channel to your profile.
  2. (Optional) Test it: python -m parser.tg_client test — prints the latest posts from each channel without running the bot.
  3. Start the bot — the monitor runs automatically and every TG_PULL_INTERVAL (default 30 min) pushes relevant posts to your Telegram with «Open in Telegram / Draft letter / Skip» buttons.

The monitor only notifies (on Telegram you apply by messaging a recruiter yourself), so it runs even in MANUAL_MODE. If t.me is blocked on your network, set TG_HTTP_PROXY in .env.

Known limitations

  • hh.ru selectors are hardcoded — UI redesigns may require updates to parser/hh_client.py.
  • City grid in _resolve_area covers only the 15 largest cities. Others are searched without region filter (with a warning in logs).
  • DB is syncsqlite3 blocks the event loop at large volumes. For a single user this is fine.
  • No tests on business logic.
  • TG channel monitor only notifies — the autopilot does not apply for TG-sourced vacancies (on Telegram you apply by messaging a recruiter yourself); they are surfaced for manual review and letter drafting.

License

PolyForm Noncommercial License 1.0.0 — full text in LICENSE.

In short:

  • Allowed: read the code, clone, fork, study, experiment, use for personal non-commercial purposes, in coursework and research, in hobby projects.
  • Allowed for organizations: charitable, educational, research, government, healthcare, environmental.
  • Not allowed: using the code in commercial products, reselling, building paid services or features on it, monetizing in any way.
  • On distribution: preserve the license text and the author's copyright.

If you need commercial use — contact me for a separate commercial license.

About

AI-assisted job-search autopilot for hh.ru: Playwright search, LLM relevance scoring, cover-letter generation, and a Telegram bot with multi-channel monitoring

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages