# jeffy-sajan/ai-job-hunter architecture

> Architecture diagram of the GitHub repository jeffy-sajan/ai-job-hunter, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-08-07). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/jeffy-sajan/ai-job-hunter
- Repository: https://github.com/jeffy-sajan/ai-job-hunter

## Overview

#### Purpose and execution
AI Job Hunter is a scheduled Python batch pipeline that discovers entry-level Python/tech roles, ranks them against a configurable candidate profile, deduplicates results, and alerts a Telegram chat. The local entry point is `main.py`; scheduled execution is defined in `.github/workflows/job-hunter.yml`.

#### Pipeline orchestration
`main.py` coordinates source collection, fallback behavior, matching, persistence, and notifications. It aggregates normalized job records from scrapers; when primary collection fails or yields no results, it can use mock data from `scraper/mock.py`. This is a one-run-at-a-time batch workflow rather than a web service.

#### Job-source acquisition boundary
Source adapters isolate each provider’s API, HTML, or feed format behind fetch functions:
- `scraper/remoteok.py` for RemoteOK API data
- `scraper/naukri.py` for Naukri search scraping
- `scraper/indeed.py` for Indeed acquisition
- Additional/backup adapters: `scraper/google_jobs.py`, `scraper/rss_jobs.py`, `scraper/infopark.py`, and `scraper/technopark.py`

These modules form the external-network boundary and are susceptible to provider blocking, markup changes, and unavailable feeds.

#### Candidate profile and eligibility filtering
Profile inputs and defaults are centralized in `config.py`. Resume-oriented skill extraction/normalization is handled by `matcher/resume_profile.py`, including environment-provided skill overrides for CI use. `matcher/job_filters.py` applies role and entry-level eligibility rules, defining the domain boundary for MCA/CS-oriented roles and fresher/intern or 0–1-year opportunities.

#### Relevance scoring
`matcher/scorer.py` converts job/profile overlap into a match score used to decide which eligible jobs are worth alerting. The scoring stage consumes the normalized source record plus configured or extracted skills, separating source-specific parsing from candidate-specific decision logic.

#### Persistence and duplicate control
`database/db.py` owns SQLite access and the seen-job state stored under `data/jobs.db`. The pipeline queries/inserts jobs before notification so repeated scheduled runs do not resend prior listings. SQLite is the only durable state layer; GitHub Actions runners may require persistence handling outside the ephemeral runner if cross-run deduplication is expected.

#### Notification delivery and runtime infrastructure
`notifier/telegram.py` sends selected new matches through the Telegram Bot API using bot token and chat ID environment configuration. `config.py` supplies runtime configuration, while `.github/workflows/job-hunter.yml` supplies the recurring six-hour GitHub Actions runtime and secret injection. Local runs use the same Python process and can load `.env`-based credentials.

Main entry points: [`main.py`](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/main.py) (Batch orchestrator), [`database/db.py`](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/database/db.py) (SQLite seen-job store), [`matcher/resume_profile.py`](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/matcher/resume_profile.py) (Resume profile), [`matcher/scorer.py`](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/matcher/scorer.py) (Match scorer)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_runtime["Execution"]
  node_schedule["GitHub Actions schedule<br/>workflow<br/>[job-hunter.yml]"]
  node_main["Batch orchestrator<br/>Python entry point<br/>[main.py]"]
  node_config["Runtime config<br/>configuration module<br/>[config.py]"]
end

subgraph group_sources["Job Sources"]
  node_remoteok["RemoteOK adapter<br/>source adapter<br/>[remoteok.py]"]
  node_naukri["Naukri adapter<br/>source adapter<br/>[naukri.py]"]
  node_indeed["Indeed adapter<br/>source adapter<br/>[indeed.py]"]
  node_backup_sources["Backup source adapters<br/>[google_jobs.py]"]
  node_mock["Mock fallback jobs<br/>fallback source<br/>[mock.py]"]
end

subgraph group_matching["Candidate Matching"]
  node_resume_profile["Resume profile<br/>skill extraction<br/>[resume_profile.py]"]
  node_filters["Eligibility filters<br/>domain rules<br/>[job_filters.py]"]
  node_scorer["Match scorer<br/>ranking logic<br/>[scorer.py]"]
end

subgraph group_state["State &amp; Delivery"]
  node_db[("SQLite seen-job store<br/>persistence<br/>[db.py]")]
  node_jobs_db[("Job state database<br/>SQLite database<br/>[jobs.db]")]
  node_telegram["Telegram notifier<br/>notification adapter<br/>[telegram.py]"]
  node_telegram_api["Telegram Bot API<br/>external service"]
end

node_schedule -->|"scheduled run"| node_main
node_config -->|"settings"| node_main
node_main -->|"collects jobs"| node_remoteok
node_main -->|"collects jobs"| node_naukri
node_main -->|"collects jobs"| node_indeed
node_main -->|"collects jobs"| node_backup_sources
node_main -.->|"fallback"| node_mock
node_config -->|"profile defaults"| node_resume_profile
node_main -->|"builds profile"| node_resume_profile
node_main -->|"normalized jobs"| node_filters
node_resume_profile -->|"skills"| node_scorer
node_filters -->|"eligible jobs"| node_scorer
node_main -->|"checks and records"| node_db
node_db -->|"SQLite access"| node_jobs_db
node_scorer -->|"new qualifying matches"| node_telegram
node_db -->|"only unseen jobs"| node_telegram
node_telegram -->|"Bot API message"| node_telegram_api

click node_schedule "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/.github/workflows/job-hunter.yml"
click node_main "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/main.py"
click node_config "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/config.py"
click node_remoteok "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/scraper/remoteok.py"
click node_naukri "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/scraper/naukri.py"
click node_indeed "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/scraper/indeed.py"
click node_backup_sources "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/scraper/google_jobs.py"
click node_mock "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/scraper/mock.py"
click node_resume_profile "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/matcher/resume_profile.py"
click node_filters "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/matcher/job_filters.py"
click node_scorer "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/matcher/scorer.py"
click node_db "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/database/db.py"
click node_jobs_db "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/data/jobs.db"
click node_telegram "https://github.com/jeffy-sajan/ai-job-hunter/blob/main/notifier/telegram.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_schedule,node_main,node_config toneBlue
class node_remoteok,node_naukri,node_indeed,node_backup_sources,node_mock toneAmber
class node_resume_profile,node_filters,node_scorer toneMint
class node_db,node_jobs_db,node_telegram,node_telegram_api toneRose
```

## Components

### Execution

- [GitHub Actions schedule](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/.github/workflows/job-hunter.yml): workflow · Runs the pipeline every six hours and injects secrets. · `.github/workflows/job-hunter.yml`
- [Batch orchestrator](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/main.py): Python entry point · Coordinates collection, fallback, matching, deduplication, and alerts. · `main.py`
- [Runtime config](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/config.py): configuration module · Centralizes profile defaults and runtime settings. · `config.py`

### Job Sources

Provider adapters isolate external formats.

- [RemoteOK adapter](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/scraper/remoteok.py): source adapter · `scraper/remoteok.py`
- [Naukri adapter](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/scraper/naukri.py): source adapter · `scraper/naukri.py`
- [Indeed adapter](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/scraper/indeed.py): source adapter · `scraper/indeed.py`
- [Backup source adapters](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/scraper/google_jobs.py): source adapters · Google Jobs, RSS, Infopark, and Technopark acquisition. · `scraper/google_jobs.py`
- [Mock fallback jobs](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/scraper/mock.py): fallback source · Used when primary collection fails or is empty. · `scraper/mock.py`

### Candidate Matching

- [Resume profile](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/matcher/resume_profile.py): skill extraction · Normalizes candidate skills and CI environment overrides. · `matcher/resume_profile.py`
- [Eligibility filters](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/matcher/job_filters.py): domain rules · Keeps MCA/CS-oriented entry-level roles. · `matcher/job_filters.py`
- [Match scorer](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/matcher/scorer.py): ranking logic · Scores eligible jobs by profile-skill overlap. · `matcher/scorer.py`

### State & Delivery

- [SQLite seen-job store](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/database/db.py): persistence · Checks and records jobs before notification. · `database/db.py`
- [Job state database](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/data/jobs.db): SQLite database · Durable deduplication state; runner persistence is required across ephemeral runs. · `data/jobs.db`
- [Telegram notifier](https://github.com/jeffy-sajan/ai-job-hunter/blob/main/notifier/telegram.py): notification adapter · Sends selected new matches through the Bot API. · `notifier/telegram.py`
- **Telegram Bot API**: external service

## Connections

- GitHub Actions schedule → Batch orchestrator: scheduled run
- Runtime config → Batch orchestrator: settings
- Batch orchestrator → RemoteOK adapter: collects jobs
- Batch orchestrator → Naukri adapter: collects jobs
- Batch orchestrator → Indeed adapter: collects jobs
- Batch orchestrator → Backup source adapters: collects jobs
- Batch orchestrator → Mock fallback jobs: fallback
- Runtime config → Resume profile: profile defaults
- Batch orchestrator → Resume profile: builds profile
- Batch orchestrator → Eligibility filters: normalized jobs
- Resume profile → Match scorer: skills
- Eligibility filters → Match scorer: eligible jobs
- Batch orchestrator → SQLite seen-job store: checks and records
- SQLite seen-job store → Job state database: SQLite access
- Match scorer → Telegram notifier: new qualifying matches
- SQLite seen-job store → Telegram notifier: only unseen jobs
- Telegram notifier → Telegram Bot API: Bot API message
