feat(scheduler): add telegram as a delivery channel#85
Open
tiuro wants to merge 1 commit intoghostwright:mainfrom
Open
feat(scheduler): add telegram as a delivery channel#85tiuro wants to merge 1 commit intoghostwright:mainfrom
tiuro wants to merge 1 commit intoghostwright:mainfrom
Conversation
Adds Telegram as a third delivery channel for the scheduler, alongside the existing Slack and "none" options. Uses raw Bot API fetch() rather than injecting a TelegramChannel into DeliveryContext, which keeps the blast radius to two files and avoids the executor -> service -> index wiring cascade. Design choices: 1. Raw fetch, not Telegraf. channels/telegram.ts already owns the Telegraf long-polling instance. Calling bot.telegram.sendMessage() from the scheduler would share the same Telegraf client across two concurrency contexts. Raw fetch against the sendMessage endpoint sidesteps this entirely. 2. Env reuse. No new env vars. The two already-required vars (TELEGRAM_BOT_TOKEN, OWNER_TELEGRAM_USER_ID) are read directly in the delivery function rather than threaded through DeliveryContext. 3. Target format. "owner" (resolves to OWNER_TELEGRAM_USER_ID) or any numeric chat_id (positive user ids, negative group ids). isValidTelegramTarget regex-validates at creation time. 4. 4096-char defensive truncation with trailing indicator. Telegram hard-limits messages at 4096 chars. 5. No parse_mode. Scheduler task outputs are generally plain text. Avoiding MarkdownV2 escaping keeps delivery code deterministic. Changes: - src/scheduler/types.ts: add "telegram" to channel enum, isValidTelegramTarget() helper, update target description. - src/scheduler/delivery.ts: add deliverTelegram() function, wire into deliverResult(), extend DeliveryOutcome union with two dropped:telegram_* variants. Production validation: Running in production on 2 Phantom instances (GPU host + TrueNAS) since 2026-04-21. Scheduler fires morning-brief + weekly jobs via telegram, delivery_status stamped "delivered" in scheduled_jobs rows. Zero conflict with Telegraf polling instance. Tests skipped in the forker's environment (no bun toolchain locally); patch validated in production instead. Unit tests for deliverTelegram (mock fetch, target variants, env-missing cases) are straightforward follow-ups happy to add in a revision if preferred.
Member
|
Lets add some tests and screenshots if you can. Thank youu |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Telegram as a third delivery channel for the scheduler, alongside the existing Slack and
"none"options. Uses raw Bot APIfetch()rather than injecting a TelegramChannel into DeliveryContext, which keeps the blast radius to two files and avoids the executor → service → index wiring cascade.Motivation
Running two Phantom instances (one on a GPU host, one on a TrueNAS box) where Telegram is the primary channel. Slack was introducing multi-workspace friction for our setup; Telegram via the existing bot config "just works". Been stable in production for ~2 weeks across both instances with zero delivery incidents.
Design choices
Raw fetch, not Telegraf.
channels/telegram.tsalready owns the Telegraf long-polling instance. Callingbot.telegram.sendMessage()from the scheduler would share the same Telegraf client across two concurrency contexts. Raw fetch against thesendMessageendpoint sidesteps this entirely. It also means the scheduler path has zero coupling to the channels layer for delivery.Env reuse. No new env vars. The two already-required vars (
TELEGRAM_BOT_TOKEN,OWNER_TELEGRAM_USER_ID) are read directly in the delivery function rather than threaded through DeliveryContext, which would otherwise require changes inexecutor.ts+service.ts+index.ts.Target format.
"owner"(symbolic, resolves toOWNER_TELEGRAM_USER_ID) or any numeric chat_id (positive user ids, negative group ids).isValidTelegramTargetregex-validates at creation time, matching the existing Slack validator pattern.4096-char defensive truncation with a trailing indicator. Telegram hard-limits messages at 4096 chars, so operators see content was cut rather than chasing a silent API error.
No parse_mode. Scheduler task outputs are generally plain text. Avoiding MarkdownV2 escaping keeps the delivery code trivial and deterministic.
Changes
src/scheduler/types.ts— add"telegram"toJobDeliverySchema.channelenum,isValidTelegramTarget()helper, update target descriptionsrc/scheduler/delivery.ts— adddeliverTelegram()function, wire intodeliverResult(), extendDeliveryOutcomeunion with twodropped:telegram_*variantsTest plan
channel=telegram, target=owner;delivery_statusstamped"delivered"inscheduled_jobsrows, zero conflict with the Telegraf polling instance.src/scheduler/__tests__/delivery-telegram.test.tscovering happy path (target=owner, explicit numeric target), missing-token env, missing-owner env, API 4xx response, and network error in a follow-up commit if the maintainers want that before merge.