cv-2026/.claude/skills/get-tg-jobs/SKILL.md

10 KiB
Raw Blame History

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 set or auth error → .env is missing or the session is expired. Regenerate with scripts/regen_telegram_session.sh and tell Oleg.
  • TELEGRAM_API_ID / TELEGRAM_API_HASH not set → same .env issue.
  • 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 culture
  • p2 — stack OK but CIS/RUB market, or occasional gems; recruiter/market-intel content
  • p3 — 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:

  1. Pre-step: build the known_applied list from Trello (the source of truth) before spawning Haiku. Fetch the vacancy-bearing lists and keep only card titles:

    mcp__trello__set_active_board(boardId="6a1a9a5af082cb0526b22704")
    mcp__trello__get_cards_by_list_id(listId="6a1aa59555aab72a261c42aa")   # TODO
    mcp__trello__get_cards_by_list_id(listId="6a1aa59a5e7e651b1352895b")   # In Progress
    mcp__trello__get_cards_by_list_id(listId="6a267e2e9ed02737346dc047")   # In touch
    mcp__trello__get_cards_by_list_id(listId="6a1aa5a1d8bcb0ed7234987b")   # Applyed
    mcp__trello__get_cards_by_list_id(listId="6a245a328452976c0d1fcca1")   # Rejected
    

    Distill to titles only, then pass that list into every Haiku subagent prompt. See the triage-jobs batch pre-step for the context-safety rules and the REST fallback when the board grows. tracking/applications.md is frozen/legacy — don't grep it.

  2. 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.

  3. VERIFY verdicts: run a fast WebSearch "{company} remote hiring countries" to resolve geo, then apply Gate 1.

  4. APPLY verdicts: for every confirmed APPLY — create a Trello card in the TODO column (6a1aa59555aab72a261c42aa) on board 6a1a9a5af082cb0526b22704. The card is the application record (replaces the old applications.md row): title Company — Role (Track X), description = verdict summary + a Tracking footer (date added, channel, CV used, JD link, status). See the triage-jobs skill ("After triage — update tracking") for the exact card schema. Do not write to tracking/applications.md (frozen/legacy).

  5. 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.json is 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.json is 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.