Claude
Skills
Sign in
← Back

jobsearch-telegram

Included with Lifetime
$97 forever

Poll Telegram for job search messages β€” apply to jobs, search for roles, check status, all via chat

General

What this skill does


# Job Search Telegram Polling

Poll Telegram for incoming messages and route them to the appropriate Proficiently skill. Runs headlessly via `/loop 1m /proficiently:jobsearch-telegram`.

## First-Time Setup

Before this skill can run, the user must create a Telegram bot and configure it. If `DATA_DIR/telegram-config.md` does not exist, walk the user through setup:

### 1. Create a Telegram Bot

Tell the user:

> **Let's set up your Telegram bot.**
>
> 1. Open Telegram and search for **@BotFather**
> 2. Send `/newbot`
> 3. Choose a name (e.g., "My Job Search Assistant")
> 4. Choose a username (must end in `bot`, e.g., `my_jobsearch_bot`)
> 5. BotFather will give you a **bot token** β€” copy it and paste it here
>
> Then send your bot a message (anything) so I can find your chat ID.

### 2. Get the Chat ID

Once the user provides the bot token, fetch their chat ID:

```bash
curl -s "https://api.telegram.org/bot{TOKEN}/getUpdates"
```

Extract `message.chat.id` from the first result. If no results, remind the user to send a message to the bot first, then retry.

### 3. Save Config

Write `DATA_DIR/telegram-config.md`:

```markdown
# Telegram Config

- Bot token: {TOKEN}
- Chat ID: {CHAT_ID}
- Bot username: @{USERNAME}
```

### 4. Verify

Send a test message:

```bash
curl -s -X POST "https://api.telegram.org/bot{TOKEN}/sendMessage" \
  -H "Content-Type: application/json" \
  -d '{"chat_id": "{CHAT_ID}", "text": "πŸ‘‹ Job search bot connected! Send me a job URL to apply, or say \"search\" to find jobs."}'
```

If successful, tell the user setup is complete and they can start the loop with `/loop 1m /proficiently:jobsearch-telegram`.

---

## Config & State Files

Resolve the data directory using `shared/references/data-directory.md`.

**Config** β€” `DATA_DIR/telegram-config.md` (created during setup, contains bot token + chat ID). **Never commit this file to git.** Read this first on every poll cycle to get credentials.

**State** β€” `DATA_DIR/telegram-state.md` (tracks polling position). Create if missing:

```markdown
# Telegram State

## Polling
- last_update_id: 0

## Pending Confirmations
<!-- Format: [msg_id: X] type/stage β€” description β€” waiting since DATE
     For apply confirmations, also store: job_url, form_url, field_mapping (JSON) -->

(none)

## Recent Actions
<!-- Last 20 actions taken -->
```

---

## Workflow

### Step 1: Load Context

1. Read `DATA_DIR/telegram-config.md` β€” if missing, run First-Time Setup above and stop
2. Read `DATA_DIR/telegram-state.md` β€” if missing, create from template above
3. Read these if they exist: `DATA_DIR/job-history.md`, `DATA_DIR/application-data.md`, `DATA_DIR/preferences.md`

### Step 2: Poll for Messages

```bash
curl -s "https://api.telegram.org/bot{TOKEN}/getUpdates?offset={LAST_UPDATE_ID+1}&timeout=5"
```

If no new messages β†’ exit silently. Do not log, do not send anything.

### Step 3: Classify Each Message

Parse each message and classify:

| Message Type | Detection | Route |
|---|---|---|
| Job URL | Contains `greenhouse.io`, `lever.co`, `myworkdayjobs.com`, `ashbyhq.com`, or other job board URL | Step 4a: Apply |
| "apply last" / "apply" | Text matches `apply` (with optional `last`/`current`) | Step 4a: Apply |
| "search for ..." | Text starts with `search`, `find`, `look for` | Step 4b: Search |
| "tailor resume for ..." | Text mentions `tailor`/`resume` + context | Step 4c: Tailor |
| "status" / "what's open" | Text asks about application status | Step 4d: Status |
| "help" | Text is exactly `help` or `?` | Step 4e: Help |
| Confirmation reply | **Threaded reply** to a pending confirmation message, OR standalone confirm word (`yes`/`y`/`go`/`no`/`cancel`) when pending confirmations exist | Step 5: Confirm |
| Plain text | Anything else | Step 6: Note |

### Step 4a: Handle Job URL / Apply Request

1. Extract the URL or resolve "last"/"current"
2. Check if a job folder already exists in `DATA_DIR/jobs/` for this URL
3. Send acknowledgment to Telegram:
   ```
   🎯 Got it β€” applying to [URL or "most recent job"].
   I'll scan the form, tailor your resume, and propose answers. Stand by...
   ```
4. **Execute the apply workflow** from `skills/apply/SKILL.md`:
   - Follow Steps 0-6 (prerequisites β†’ navigate β†’ scout β†’ generate materials β†’ scan fields β†’ propose answers)
   - Instead of using AskUserQuestion for approval, **send the Step 6 proposal summary to Telegram** and add to Pending Confirmations
   - Store in the pending confirmation: `job_url`, `form_url` (the direct ATS form URL navigated to), and `field_mapping` (the full approved field→value JSON)
   - Wait for user confirmation via Telegram (will arrive as a reply in a future poll cycle)

5. When field-approval confirmation arrives (Step 5), re-navigate to `form_url`, fill all fields, then send a **second confirmation** (submit approval) with a screenshot description and ask: `"Everything looks good β€” submit?"`
   - Store this as a new pending confirmation with `stage: "submit-approval"`

6. When submit-approval arrives, click Submit, then log the application (Step 9 of the apply skill).

**Sending the proposal:** Use the send message helper (Step 8) with the full field summary. Keep it under 4000 chars. If longer, split into: (1) auto-fill fields, (2) proposed answers, (3) needs input.

**Two-phase confirmation flow:**
- Phase 1 (`stage: field-approval`): User approves the field→value mapping
- Phase 2 (`stage: submit-approval`): User approves the final form before clicking Submit
- Never skip phase 2 β€” submitting a job application is irreversible

### Step 4b: Handle Search Request

1. Extract search keywords from the message
2. Send acknowledgment: `πŸ” Searching for: [keywords]...`
3. Execute the job-search workflow from `skills/job-search/SKILL.md`
4. Send results summary to Telegram:
   ```
   πŸ” Found X matches for "[keywords]":

   1. [Role] at [Company] β€” [fit score]
      [URL]
   2. ...

   Reply with a number to apply, or "apply 1" / "apply 3" etc.
   ```
5. Add to Pending Confirmations with the job list so replies can be matched

### Step 4c: Handle Tailor Request

1. Extract job reference (URL, "last", or job name)
2. Send acknowledgment: `πŸ“ Tailoring resume for [job]...`
3. Execute the tailor-resume workflow from `skills/tailor-resume/SKILL.md`
4. Send result to Telegram with key changes made
5. Note the file path where the tailored resume was saved

### Step 4d: Handle Status Query

Compile from `DATA_DIR/job-history.md` and `DATA_DIR/jobs/*/applied.md`:

```
πŸ“‹ Job Search Status

Applied (X):
- [Role] at [Company] β€” [date] β€” [status]
- ...

Saved but not applied (Y):
- [Role] at [Company] β€” [date saved]
- ...

Pending your confirmation:
- [any pending apply proposals]
```

### Step 4e: Handle Help Request

Send:

```
πŸ‘‹ Here's what you can do:

<b>Apply</b>
β€’ Send a job URL β†’ I'll apply for you
β€’ "apply last" β†’ continue with the most recent job

<b>Search</b>
β€’ "search [keywords]" β†’ find matching jobs
β€’ "find AI product jobs" β†’ same thing

<b>Resume</b>
β€’ "tailor resume for [job URL or name]"

<b>Status</b>
β€’ "status" β†’ see all applications and what's pending

<b>Other</b>
β€’ "help" β†’ this message
β€’ Any other text is saved as a note
```

### Step 5: Handle Confirmation Reply

A confirmation reply is either:
- A **threaded reply** (Telegram's native reply feature): match via `reply_to_message.message_id` to a pending confirmation
- A **standalone message** containing only a confirm/reject word (`yes`, `y`, `go`, `send it`, πŸ‘, `no`, `skip`, `cancel`, ❌) when pending confirmations exist

**Disambiguation when standalone:**
- If exactly one pending confirmation exists β†’ apply it to that confirmation
- If multiple pending confirmations exist β†’ respond with a numbered list of what's pending and ask which one they mean:
  ```
  You have X things waiting. Which one?
  1. [description of pending 1]
  2. [description of pending 2]
  Reply with a number.
  ```

**Processing:**
1. Look up the pending confirmation in `telegram-state.md

Related in General