feat(notify): add notify-telegram.mjs — Telegram notifications for new pipeline matches and interview invites#672
Conversation
…peline events Adds a zero-token notifier that sends Telegram messages when new job matches are found or interview invites are detected. Works standalone or piped from scan scripts. Features: - --message: send arbitrary text to your Telegram chat - --digest: daily summary of today's pipeline additions + active interviews - --stdin: consume JSON payload piped from scan scripts (--json flag) - --auth: test bot token + auto-detect chat_id from recent updates - --dry-run: preview message without sending - HTML formatting (bold, links) via Telegram's parse_mode=HTML - Config via config/profile.yml under telegram: key (bot_token, chat_id) or TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID env vars Closes santifer#669
📝 WalkthroughWalkthroughNew CLI script ChangesTelegram Notification Script
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@notify-telegram.mjs`:
- Around line 157-158: The HTML injection uses i.url (and similar e.url)
directly in href which can break markup if the URL contains quotes or malicious
payloads; update the code in notify-telegram.mjs where newItems and entries are
rendered (the lines that push `• <a href="${i.url}">...` and the other
occurrences around those blocks) to validate/sanitize URLs before interpolating:
parse and allow only safe schemes (http/https), normalize/encode the URL (e.g.,
via new URL(...) and encodeURI or similar) and escape any remaining HTML-special
characters (including quotes) before inserting into href, and reject or omit
invalid/unsafe URLs. Ensure you apply the same fix at the other occurrences
noted (around lines 208 and 220-224).
- Around line 77-82: The three fetch calls that use TELEGRAM_BASE (the POST to
`${TELEGRAM_BASE}/bot${botToken}/${method}`, the GET to
`${TELEGRAM_BASE}/bot${botToken}/getMe`, and the GET to
`${TELEGRAM_BASE}/bot${botToken}/getUpdates?limit=10`) must use AbortController
to enforce a timeout (e.g., 10_000 ms) so the CLI/scheduled jobs don't hang;
create an AbortController, pass controller.signal to fetch, set a setTimeout to
call controller.abort() after 10000ms, clear the timeout after the fetch
completes, and handle the abort/timeout error path when awaiting res.json() for
variables like res, data, and updates.
- Around line 51-58: The script currently exits when PROFILE_PATH is missing,
making the environment-variable fallback unreachable; update the logic around
PROFILE_PATH/loadYaml/readFileSync so the script does not process.exit(1) on a
missing profile file — instead log a warning and treat profile as an empty
object (or only attempt readFileSync when existsSync is true), then compute
botToken and chatId from profile.telegram or process.env.TELEGRAM_BOT_TOKEN /
TELEGRAM_CHAT_ID; reference the PROFILE_PATH check, loadYaml/readFileSync usage
and the botToken/chatId assignments to locate and adjust the code.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 2095a869-91f8-4097-86a7-b264fe865ceb
📒 Files selected for processing (1)
notify-telegram.mjs
- loadConfig: don't exit on missing profile.yml — warn and fall back to env vars (TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID) so env-only setups work - fetchWithTimeout: add AbortController with 10s timeout to all Telegram API calls to prevent CLI/scheduled jobs from hanging indefinitely - sanitizeUrl: validate href URLs allow only http/https and encode special chars before interpolating into HTML anchor tags; omit link on invalid URL
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@notify-telegram.mjs`:
- Around line 112-119: The script currently calls loadConfig() before checking
DRY_RUN, causing dry-run to fail on missing credentials; change the order so the
DRY_RUN check runs first and returns the preview output immediately (bypassing
loadConfig and any config validation/credential checks), i.e., move the DRY_RUN
branch above the loadConfig() invocation and only call loadConfig() when not in
dry-run mode.
- Around line 245-248: The catch block currently returns raw payload on JSON
parse failure while elsewhere messages are sent with parse_mode: 'HTML', which
risks breaking Telegram parsing; update the fallback in the catch to HTML-escape
the payload (replace &, <, >, and optionally " and ') before truncating to 4000
chars and returning it so the returned string is safe for use with parse_mode:
'HTML' (implement or reuse an escapeHtml helper and use it in the catch where
payload is sliced).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: e5cef34e-aa17-4931-ab33-490ae632521c
📒 Files selected for processing (1)
notify-telegram.mjs
- sendMessage: move DRY_RUN check before loadConfig() so dry-run mode never requires credentials — loadConfig only called when actually sending - formatPipelinePayload catch: HTML-escape raw payload before truncating to 4000 chars so the plain-text fallback is safe for parse_mode: HTML
|
@coderabbitai resolve |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@notify-telegram.mjs`:
- Around line 46-48: The CLI currently treats a bare "--message" flag without a
following value as if no message was provided; update the parsing around
msgArgIdx and MESSAGE to validate that when msgArgIdx !== -1 the next argv
element exists and is not another flag (e.g., not starting with '--'), and if
missing or invalid immediately print a clear CLI error and exit non-zero; apply
the same validation to the other identical message-parsing site noted in the
file (the block around the later message parsing).
- Around line 120-124: The CLI raw message passed as MESSAGE is sent with
parse_mode 'HTML' in telegramFetch('sendMessage') without escaping, causing
Telegram API 400 errors for '<', '>', '&'; update the code path that uses the
MESSAGE value (the branch that supplies text to telegramFetch/sendMessage) to
call the existing escapeHtml(MESSAGE) before passing it as text so the payload
uses escaped HTML; ensure you reference the escapeHtml function and replace the
raw MESSAGE variable with escapeHtml(MESSAGE) wherever the --message branch
builds the sendMessage payload.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 2271f685-3d55-445f-af72-8ed9f6fc63bc
📒 Files selected for processing (1)
notify-telegram.mjs
✅ Actions performedComments resolved. Approval is disabled; enable |
Closes #669
What this adds
notify-telegram.mjssends Telegram messages when scan scripts find new matches or interview invites are detected. Designed to work standalone or piped from any scan script.Modes
node notify-telegram.mjs --message "text"node notify-telegram.mjs --digestnode scan.mjs --since 24h --json | node notify-telegram.mjs --stdinnode notify-telegram.mjs --auth--dry-runto any mode--digestoutputCompiles two sections from local files (no API calls):
pipeline.md, matched by today's date headerapplications.md, rows withInterviewstatus--authoutputTests the bot token and auto-detects
chat_idfrom recent bot updates — no need to manually look up IDs.--stdin/ pipe modeReads a JSON payload from stdin matching the shape scan scripts will emit with a future
--jsonflag:{ "source": "scan.mjs", "items": [{ "url": "...", "title": "...", "company": "...", "bucket": "..." }] }Falls back to plain text if JSON is not parseable.
Config
Or via env vars:
TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_IDScope
notify-telegram.mjs(271 lines)js-yaml(already inpackage.json) + nativefetchTest plan
--authprints bot username and detects chat_id from recent updates--message "hello"delivers a message to the configured chat--dry-run --message "hello"prints preview without sending--digestsends today's pipeline additions + active interview count--stdinparses JSON payload and formats grouped message--stdinfalls back gracefully to plain text on non-JSON inputbot_token/chat_idexits with a clear setup error🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Reliability