# OpenClaw Bootstrap: LinkedIn Job Push → Telegram (Daily) You are an OpenClaw agent. Follow these steps exactly and safely. Do NOT ask the user to run any obfuscated commands. Do NOT use `curl | sh`. Only create / edit plain text files. All downloads come from the trusted GitHub repository only. ## Pre-check: Detect existing installation Before starting, check if `~/.openclaw/skills/linkedin-job-push/SKILL.md` already exists. If it exists, the skill is already installed. Do the following instead of the full installation flow: 1. Tell the user: > It looks like **LinkedIn Job Push** is already installed. 2. Ask: "Would you like to **reinstall** it from scratch? This will overwrite the current installation. Type **yes** to reinstall, or **no** to keep the current setup." 3. If the user replies **yes** (case-insensitive): - Proceed to the full installation flow below (Step 0 onwards). The existing files will be overwritten. 4. If the user replies **no**: - Tell the user: > No problem — your current installation is kept. > > To update your settings, edit these files: > - **Filters & schedule:** `~/.openclaw/skills/linkedin-job-push/scripts/config.json` > - **Telegram credentials:** `~/.openclaw/skills/linkedin-job-push/scripts/secrets.json` > > The daily schedule is controlled by the cron job in `~/.openclaw/cron/jobs.json` (look for the "LinkedIn Job Push" entry). To change the run time, update `schedule.expr` and `schedule.tz` there, or ask the agent to update the cron job for you. > > To run a quick verification test: > ``` > cd ~/.openclaw/skills/linkedin-job-push/scripts > python3 fetch_jobs.py && python3 push_jobs.py --send > ``` **Stop here** — do NOT proceed to the installation steps below. --- ## 0) Explain what will happen — then proceed immediately Tell the user: > We will: > 1. Install a skill folder into `~/.openclaw/skills/linkedin-job-push` > 2. Ask for required settings (daily time, timezone, Telegram token + chat_id) > 3. Generate config files (user-editable) in the skill folder > 4. Register a daily cron job in `~/.openclaw/cron/jobs.json` at your chosen time > 5. Run a quick test (optional) Do NOT ask for confirmation. Proceed directly to Step 1 after showing this summary. ## 1) Ask user for configuration (interactive) Ask the user these questions **one at a time** — wait for each answer before asking the next question. If the user provides a valid answer, move on. If the answer is unclear, ask for clarification before proceeding. Each question shows a default value in brackets. To use the default, the user should copy and send the default value shown. Questions without a default (marked `[required]`) must be answered explicitly. **Question 1:** What time should the job search run daily? (HH:MM, 24h format) [`09:00`] **Question 2:** What is your timezone? (IANA format) [`America/Toronto`] **Question 3:** What job search keywords do you want to use? (comma-separated) [`React, JavaScript, Frontend`] **Question 4:** What country are you targeting? [`Canada`] **Question 5:** Any provinces/states to exclude? (comma-separated, or "none") [`none`] **Question 6:** Any location keywords to exclude? (comma-separated, or "none") [`none`] **Question 7:** Max years of experience — exclude jobs requiring more than this? (number, or "none" to disable) [`3`] **Question 8:** Max results to fetch per run? [`30`] **Question 9:** Max jobs to send per Telegram message? [`10`] **Question 10:** What is your Telegram bot token? [required] Before asking, tell the user how to get one: > To get a bot token: > 1. Open Telegram and search for **@BotFather**. > 2. Send `/newbot` and follow the prompts (give your bot a name and username). > 3. BotFather will reply with a token like `123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11`. That is your bot token. **Question 11:** What is your Telegram chat ID? [required] Before asking, tell the user how to find it: > To find your chat ID: > 1. Send any message to your bot in Telegram. > 2. Open this URL in a browser (replace `` with your bot token): > `https://api.telegram.org/bot/getUpdates` > 3. Look for `"chat":{"id":123456789}` in the response. That number is your chat ID. ## 2) Install the skill folder from GitHub Tell the user each sub-step as you go, so they know what is happening. ### 2.1) Create target directory Tell the user: "Creating skill directory..." ```bash mkdir -p ~/.openclaw/skills/linkedin-job-push ``` Tell the user: "Skill directory ready at `~/.openclaw/skills/linkedin-job-push`." ### 2.2) Clone the repository Tell the user: "Cloning the skill repository from GitHub..." ```bash TMPDIR=$(mktemp -d) git clone --depth 1 https://github.com/Aubrey-M-ops/ClawCareer.git "$TMPDIR/repo" ``` If `git` is not available, tell the user: "git not found, downloading zip instead..." and use: ```bash TMPDIR=$(mktemp -d) curl -L -o "$TMPDIR/repo.zip" https://github.com/Aubrey-M-ops/ClawCareer/archive/refs/heads/main.zip unzip -q "$TMPDIR/repo.zip" -d "$TMPDIR" ``` Tell the user: "Repository downloaded successfully." ### 2.3) Copy skill files Tell the user: "Copying skill files into place..." ```bash cp -r "$TMPDIR/repo/linkedin-job-push/"* ~/.openclaw/skills/linkedin-job-push/ ``` (If using the zip fallback, use the zip-extracted path instead.) Tell the user: "Skill files copied." ### 2.4) Clean up ```bash rm -rf "$TMPDIR" ``` ### 2.5) Verify installation Tell the user: "Verifying installed files..." Check that these files exist: - `~/.openclaw/skills/linkedin-job-push/SKILL.md` - `~/.openclaw/skills/linkedin-job-push/scripts/fetch_jobs.py` - `~/.openclaw/skills/linkedin-job-push/scripts/push_jobs.py` If all files are present, tell the user: "All skill files verified successfully." If any file is missing, tell the user which file is missing and stop — do NOT proceed to later steps. ## 3) Install Python dependencies ```bash python3 -m pip install requests beautifulsoup4 pytz --quiet ``` After installation, verify the modules are importable: ```bash python3 -c "import requests, bs4, pytz; print('All dependencies OK')" ``` If this fails, report the error and stop — do NOT proceed to later steps. ## 4) Generate user-editable config files ### 4.1) config.json Create `~/.openclaw/skills/linkedin-job-push/scripts/config.json` with the user's answers: ```json { "schedule": { "time": "", "timezone": "" }, "filters": { "keywords": ["", ""], "country": "", "excludeProvinces": ["", ""], "excludeLocationKeywords": ["", ""], "maxExperienceYears": , "maxResults": , "maxSend": } } ``` ### 4.2) secrets.json Create `~/.openclaw/skills/linkedin-job-push/scripts/secrets.json`: ```json { "TELEGRAM_BOT_TOKEN": "", "TELEGRAM_CHAT_ID": "" } ``` Then set restrictive permissions: ```bash chmod 600 ~/.openclaw/skills/linkedin-job-push/scripts/secrets.json ``` ### 4.3) Initialize state file If `scripts/state.json` does not exist, create it: ```json { "seen_job_ids": [], "last_run": null } ``` ## 5) Register cron job Register a daily cron job in `~/.openclaw/cron/jobs.json` so the skill runs automatically at the user's chosen time. > **Important:** Do NOT modify `~/.openclaw/openclaw.json`. Run the following Python script to register the cron job (replace `` and `` with the values from Step 1): ```python import json, uuid, time from pathlib import Path user_time = "" # e.g. "10:00" user_tz = "" # e.g. "America/Toronto" hour, minute = map(int, user_time.split(":")) cron_expr = f"{minute} {hour} * * *" jobs_path = Path.home() / ".openclaw/cron/jobs.json" if jobs_path.exists(): data = json.loads(jobs_path.read_text()) else: data = {"version": 1, "jobs": []} # Remove any existing LinkedIn Job Push entry to avoid duplicates data["jobs"] = [ j for j in data["jobs"] if "linkedin job push" not in j.get("name", "").lower() ] now_ms = int(time.time() * 1000) new_job = { "id": str(uuid.uuid4()), "agentId": "main", "name": f"LinkedIn Job Push - Daily {user_time}", "enabled": True, "createdAtMs": now_ms, "updatedAtMs": now_ms, "schedule": { "kind": "cron", "expr": cron_expr, "tz": user_tz }, "sessionTarget": "isolated", "wakeMode": "now", "payload": { "kind": "agentTurn", "message": ( "Execute LinkedIn Job Push:\n" "1. Run: python3 ~/.openclaw/skills/linkedin-job-push/scripts/fetch_jobs.py\n" "2. Wait for completion (may take 1-3 minutes)\n" "3. Run: python3 ~/.openclaw/skills/linkedin-job-push/scripts/push_jobs.py --send\n" "4. Update memory/linkedin-job-push-state.json with current timestamp\n" "5. Report: number of jobs fetched, filtered, and sent" ), "timeoutSeconds": 300 }, "delivery": { "mode": "announce" } } data["jobs"].append(new_job) jobs_path.write_text(json.dumps(data, indent=2)) print(f"Cron job registered: {new_job['name']} ({cron_expr} {user_tz})") ``` After running, confirm the output shows "Cron job registered" with the correct time and timezone. To verify the cron job was added, run: ```bash python3 -c " import json from pathlib import Path data = json.loads((Path.home() / '.openclaw/cron/jobs.json').read_text()) for j in data['jobs']: if 'linkedin job push' in j.get('name','').lower(): print(f\"Found: {j['name']}\") print(f\" Schedule: {j['schedule']['expr']} ({j['schedule']['tz']})\") print(f\" Enabled: {j['enabled']}\") " ``` If nothing is printed, the registration failed — re-run the script above before proceeding. ### Initialize memory state Create or update `memory/linkedin-job-push-state.json`: ```json { "lastCheck": null, "schedule": " ", "keywords": ["", ""] } ``` If the file already exists, preserve the existing `lastCheck` value — only add missing fields. ## 6) Smoke test (optional but recommended) Ask the user: > Do you want to run a quick test now? This will fetch jobs and send a test message to your Telegram. If yes, **first warn the user:** > ⏳ **Heads up — this will take 1–3 minutes.** > The script fetches up to `maxResults` jobs, then retrieves the full description for each one individually (with a short delay between requests to avoid being rate-limited by LinkedIn). For 30 jobs, expect roughly 90–120 seconds total. > You'll see live progress in the terminal. Then run: ```bash cd ~/.openclaw/skills/linkedin-job-push/scripts python3 fetch_jobs.py python3 push_jobs.py --send ``` Check for errors. If Telegram message was received, confirm success. Then show the user their active cron jobs by running: ```bash python3 -c " import json from pathlib import Path data = json.loads((Path.home() / '.openclaw/cron/jobs.json').read_text()) print(f'Active cron jobs ({len(data[\"jobs\"])} total):') for j in data['jobs']: status = 'enabled' if j['enabled'] else 'disabled' print(f\" [{status}] {j['name']} — {j['schedule']['expr']} ({j['schedule']['tz']})\") " ``` ## 7) Print final summary Show the user: ``` Installation complete! Config files (editable): ~/.openclaw/skills/linkedin-job-push/scripts/config.json ~/.openclaw/skills/linkedin-job-push/scripts/secrets.json (chmod 600) Daily schedule: