9.4 KiB
| name | description |
|---|---|
| get-tg-jobs | Full autonomous end-to-end job pipeline. Fetches fresh vacancies from Oleg's Telegram "Jobs" folder, handles new/pending channels, runs the full triage cycle (triage-jobs), pushes APPLY verdicts to Trello, then reports results in the chat session AND sends a Telegram notification to Oleg via telegram-helper. Trigger on "забери из Jobs", "что нового в каналах", "запусти pipeline", or similar. |
get-tg-jobs
Autonomous end-to-end pipeline: fetch → curate new channels → triage → Trello → report. Does everything without stopping for confirmation. Only halts if a hard error occurs (auth failure, script crash, etc.).
Trigger only when Oleg explicitly asks — e.g. "забери свежее из Jobs", "что нового в каналах", "запусти pipeline". No background polling.
Notification targets
All pipeline reports go to the "New cool job" Telegram group. The telegram-helper account (samuishechka) is a member of that group and sends messages there.
| Target | chat_id | When |
|---|---|---|
| "New cool job" group | -4783500394 |
All reports — success and errors |
Tool to use: mcp__telegram-helper__send_message(chat_id=-4783500394, message="...", parse_mode="html")
This section is the canonical reference for notification targets. Update here if the group changes.
Step 1 — Run the pipeline
~/.local/bin/uv run scripts/list_telegram_channels.py \
| ~/.local/bin/uv run scripts/fetch_telegram_jobs.py -
Run from the project root. The script prints a per-channel summary to stdout — echo it to the session so Oleg can see raw progress.
If the script fails:
TELEGRAM_SESSION_STRING not setor auth error →.envis missing or the session is expired. Regenerate withscripts/regen_telegram_session.shand tell Oleg.TELEGRAM_API_ID / TELEGRAM_API_HASH not set→ same.envissue.Could not find folder "Jobs"→ the folder was renamed or the wrong session is in.env. Must be the usulsu session, not samuishechka.- Any other exception → paste the error and stop. Don't retry blindly.
Step 2 — Check for pending (new) channels
test -f tracking/telegram_pending_channels.json && echo PENDING || echo OK
If OK: skip to Step 3.
If PENDING: curate each new channel using the procedure below, then rerun Step 1.
New-channel curation procedure
For each channel in telegram_pending_channels.json:
2a — Read keyword frequency scan
jq -r 'to_entries[] | "\n=== \(.key) ===\nmessages_scanned: \(.value.messages_scanned)\ntruncated: \(.value.truncated)\ntop keywords:\n\(.value.keyword_counts_from_other_channels | to_entries | sort_by(-.value) | .[:20] | .[] | " \(.key): \(.value)")"' \
tracking/telegram_pending_channels.json
2b — Sample messages
jq -r '.channels["<channel>"].messages[:6] | .[] | "── \(.date[0:16])\n\(.text[0:400])\n"' \
tracking/telegram_inbox.json
Look for: hashtag patterns, language, post structure (single role / digest / chat), recurring noise.
2c — Decide lang, priority, filter shape
Priority:
p1— strong stack hits (JS/TS/React/Node/AI/LLM) AND global-remote culturep2— stack OK but CIS/RUB market, or occasional gems; recruiter/market-intel contentp3— wrong stack, off-market, noise, dead channels
Base priority on the best vacancy in the sample, not the average.
Filter shape:
| Channel pattern | Filter |
|---|---|
Consistent #vacancy + #remote hashtags |
AND-of-OR hashtag filter |
| Vacancy text without consistent hashtags | Positive stack include + Oleg-stack excludes |
| Low-volume personal/curated digest | Trust-all (no include/exclude) |
| Mixes resumes and vacancies | Trust-all |
| Mostly noise but worth keeping | Strict positive filter |
Standard Oleg-stack excludes: ["kafka", "golang", "kotlin", "android", "swift"]
For *_jobs channels add resume excludes: ["#резюме", "#resume", "#cv", "#ищуработу"]
Positive stack include: ["javascript", "typescript", " react", "node.js", "nodejs", "fullstack", "full-stack", "tech lead", "techlead", "ai engineer", " llm"]
2d — Add entry to telegram_channels.json
Insert in correct priority block (p1 → p2 → p3, alphabetically within each group):
"<channel_id>": {
"lang": "ru" | "en" | "mixed",
"priority": "p1" | "p2" | "p3",
"include": [["kw1","kw2"], ["kw3"]], // optional
"exclude": ["kw4", "kw5"] // optional
}
2e — Rerun and confirm clean
~/.local/bin/uv run scripts/list_telegram_channels.py \
| ~/.local/bin/uv run scripts/fetch_telegram_jobs.py -
test -f tracking/telegram_pending_channels.json && echo "STILL PENDING" || echo "Clean"
2f — Validate the filter
jq -r '.channels["<channel>"] | "kept \(.kept)/\(.seen)" + (.messages | map("\n── \(.date[0:16])\n\(.text[0:300])") | join(""))' \
tracking/telegram_inbox.json
If kept == 0 and a filter is set — verify the filter isn't too strict before accepting zero as correct.
Step 3 — Run full triage
Follow the complete triage-jobs skill procedure on tracking/telegram_inbox.json.
Key points from that skill:
-
Pre-step: build
known_appliedlist before spawning Haiku:grep -E '^\|' tracking/applications.md | tail -n +2 | awk -F'|' '{print $3}' | sort -uAlso fetch Trello card titles from all BestJob board lists. Pass the merged list into every Haiku subagent prompt.
-
Stratify by priority: all p1, p2, p3 go through Haiku first pass. Main session reviews only APPLY and VERIFY verdicts from Haiku. SKIP verdicts from Haiku accepted without re-review.
-
VERIFY verdicts: run a fast
WebSearch "{company} remote hiring countries"to resolve geo, then apply Gate 1. -
APPLY verdicts: for every confirmed APPLY —
- Add a row to
tracking/applications.mdwith statustodo - Create a Trello card in the TODO column (
6a1aa59555aab72a261c42aa) on board6a1a9a5af082cb0526b22704with: company, role, track, verdict summary, JD link
- Add a row to
-
QUICK-APPLY verdicts: note them explicitly in the session report; do not auto-add to Trello. Ask Oleg if he wants to proceed.
Full triage playbook: base/reference/vacancy-filter-and-triage-2026.md. Full batch instructions: .claude/skills/triage-jobs/SKILL.md.
Step 4 — Report in session
After triage is complete, output a consolidated summary to the chat:
Pipeline done — <date>
Fetch: N messages from X channels (Y with results)
[⚠️ Truncated: channel_name (kept K/500, p2/ru)]
Triage results:
✅ APPLY: N roles → added to Trello
🔍 VERIFY: N roles → resolved / still open
⚡ QUICK-APPLY: N roles (ask Oleg before adding)
❌ SKIP: N roles
APPLY roles:
- Company A — Role Title (Track A) → [Trello card]
- Company B — Role Title (Track B) → [Trello card]
New channels curated: N [or: none]
Step 5 — Send Telegram notification
After the session report, send a summary to the "New cool job" group via telegram-helper.
Success message format
mcp__telegram-helper__send_message(
chat_id=-4783500394,
parse_mode="html",
message="""<b>Jobs pipeline done</b> · 2026-06-07
✅ APPLY: N роли → Trello
🔍 VERIFY: N (нужен ресёрч)
⚡ Quick-apply: N (нужно ок)
❌ SKIP: N
📥 Всего: N сообщений из X каналов
<b>APPLY:</b>
– <a href="https://trello.com/c/...">Company A — Role (Track A)</a>
– <a href="https://trello.com/c/...">Company B — Role (Track B)</a>"""
)
Rules:
- Always include Trello links for APPLY roles — это главная ценность уведомления.
- If QUICK-APPLY roles exist, list them with direct JD links (not Trello — для быстрого решения):
⚡ Quick-apply (нужно ок): – Company X — Role → wantapply.com/... – Company Y — Role → linkedin.com/jobs/... - Keep VERIFY as a count only (no list) — they need more research, not immediate action.
- If zero APPLY and zero QUICK-APPLY: still send the message — "всё просмотрено, ничего релевантного в этот раз".
Error message format
If the pipeline failed at any step and cannot complete — send a short error notification to the same group:
mcp__telegram-helper__send_message(
chat_id=-4783500394,
parse_mode="html",
message="""<b>⚠️ Pipeline error</b> · 2026-06-07
Упало на шаге: <step name>
Причина: <one-line error summary>
Что сделано до ошибки: <brief — e.g. "fetch OK, 12 msgs, triage не запустился">
Что нужно: <action — e.g. "обнови TELEGRAM_SESSION_STRING в .env">"""
)
Send the error notification even if only partial work was done. Better to have an incomplete notification than silence when something breaks.
Sanity notes
telegram_state.jsonis not in git — per-machine cursor. If lost, next run fetches 30 days back per channel (slow but correct).- To reset a single channel's cursor for filter re-validation:
jq 'del(.["<channel>"])' tracking/telegram_state.json > /tmp/s.json && mv /tmp/s.json tracking/telegram_state.json telegram_inbox.jsonis overwritten every run — it's the current snapshot only.- The pipeline reads the live "Jobs" folder — new channels Oleg added in Telegram are automatically included in the next run.