← Portfolio JR Julia Reprintseva RU

CLAUDE CODE · TELEGRAM · LAUNCHD

Let Claude Code send you job posts every day — with a draft reply

Once a day, with no terminal open: it checks your Telegram channels, picks what matches your keywords, drafts a cover letter and sends it to your own Saved Messages. Nothing reaches an employer without you looking at it first.

Telegram folder of channels → filter by keywords + format → Claude Code drafts a reply → Saved Messages, once a day

What it does

A script runs on a schedule (on macOS via launchd) and opens a headless Claude Code session with a precisely defined task: look into a chosen Telegram folder, take the last day of messages from the channels in it, keep only the ones that match what you are looking for, and send the result to your own Saved Messages.

One message per matching job post: role and company, work format and salary (if stated), a link to the original post, the contacts to apply to, and a draft cover letter written from your own profile. The letter is marked as a draft — you send it yourself, if you decide to apply.

What you need first

The easiest way is not to retype these steps by hand but to open this page in a conversation with your own Claude Code and ask: “set up the same thing for me, but for my folder and my keywords.” It will repeat all of this itself, ask for the missing details and adapt it to your machine.

Steps

01Find your folder ID

Ask Claude Code in a normal chat: “show me my Telegram folders” — it will call list_folders and return a list with ids. Note the id of the folder you need (in the examples below it is 169).

02Decide on your filter

Two independent conditions: keywords for the role, and the work format. For example:

ParameterExample value
Keywordseditor, smm, writer, producer, journalist, copywriter
Work formatremote, work from home, hybrid, distributed

A job post passes the filter only if at least one word from each group appears in it.

03Write a short profile for the letters (optional)

If you want not just a digest but ready drafts of replies, give Claude Code 3–5 sentences about yourself: your role, key experience, achievements, contacts. It will use only these facts and will not invent anything.

04Create the script

A file, for example ~/.claude/scripts/vacancy-digest.sh — the places to replace are marked “# ← replace”:

vacancy-digest.sh
#!/bin/bash
# A daily digest of Telegram job posts + draft replies.
# Everything goes to Saved Messages only — nothing is sent to employers.

export PATH="$HOME/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"

FOLDER_ID=169                        # ← replace with your id from step 01
SINCE_DATE=$(date -v-1d +%Y-%m-%d)   # on Linux: date -d yesterday +%Y-%m-%d

KEYWORDS="editor, smm, writer, producer, journalist, copywriter"  # ← replace
FORMAT="remote, work from home, wfh, hybrid, distributed"       # ← replace

PROFILE="First Last — role/specialisation. 3-5 facts from your experience. Contacts: email, telegram, phone."  # ← replace

PROMPT="Check the Telegram folder (folder_id=$FOLDER_ID via mcp__telegram__get_folder) and find new job posts from the last day.

1. From the folder take only chats with type == \"Channel\".
2. For each channel call mcp__telegram__list_messages with from_date=\"$SINCE_DATE\", limit around 30.
3. Select messages containing at least one word from this list: $KEYWORDS — and mentioning the format: $FORMAT.
4. If nothing was found, send nothing and stop.
5. For each job post get a link to it via mcp__telegram__get_message_link.
6. Get your own chat_id via mcp__telegram__get_me (chat_id \"me\" = Saved Messages).
7. Write a short (4-6 sentences) cover letter in the first person based on the profile — real facts only. The letter must begin with “I am applying for [this role](link to the post) JOB TITLE” (the words “this role” being a markdown link to the post link from step 5), and near the end contain the phrase “my CV is attached”:
$PROFILE
8. Via mcp__telegram__send_message (with parse_mode=\"md\", so that the link on “this role” renders as clickable) send ONE message per job post to the \"me\" chat: role/company, format/salary, link to the post, contacts, and the draft letter marked “not sent — check before sending”.

Never write or send anything to employers directly — only to your own Saved Messages."

claude -p "$PROMPT" \
  --allowedTools "mcp__telegram__get_folder,mcp__telegram__list_messages,mcp__telegram__get_me,mcp__telegram__send_message,mcp__telegram__get_message_link" \
  --model claude-sonnet-5
terminal
chmod +x ~/.claude/scripts/vacancy-digest.sh

05Describe the schedule for launchd

The file ~/Library/LaunchAgents/com.NAME.vacancy-digest.plist (replace NAME and the path to the script):

com.NAME.vacancy-digest.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.NAME.vacancy-digest</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/Users/NAME/.claude/scripts/vacancy-digest.sh</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key><integer>11</integer>
        <key>Minute</key><integer>0</integer>
    </dict>
    <key>StandardOutPath</key>
    <string>/Users/NAME/.claude/logs/vacancy-digest.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/NAME/.claude/logs/vacancy-digest.err.log</string>
    <key>RunAtLoad</key><false/>
</dict>
</plist>

Hour/Minute is the run time in your local time zone. The computer has to be on at that moment (off overnight and on in the morning is fine — just set the time with some margin).

06Register it and check

terminal
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.NAME.vacancy-digest.plist
launchctl enable gui/$(id -u)/com.NAME.vacancy-digest

# a test run right now, without waiting for the schedule:
launchctl kickstart -p gui/$(id -u)/com.NAME.vacancy-digest

If everything is set up correctly, within 1–2 minutes (checking several channels takes a while) job cards will appear in your Saved Messages — or nothing, if nothing matching came up in the last day.

Change the time or turn it off

ActionCommand
Change the timeedit Hour/Minute in the plist, then bootout + bootstrap + enable again
Stop it completelylaunchctl bootout gui/$(id -u)/com.NAME.vacancy-digest
See the logscat ~/.claude/logs/vacancy-digest.log

About safety

important

The script is allowed exactly five Telegram tools (--allowedTools) — reading folders and messages, getting a link to a post, and sending a message to yourself. It has no access to any chats other than your own Saved Messages, and no way to write to anyone else.

Draft letters are drafts: Claude Code writes them only from the facts you gave in your profile, and marks every message clearly as unsent. Whether to apply, whether to edit the letter — that is always your call.