Skip to main content

Telegram Send

@magistr/telegram-sendv2026.09.24.1· 12d agoMODELS
01README

Send messages, photos, documents, and videos to Telegram chats and channels via the Telegram Bot API.

Methods:

  • getMe — verify the bot token and fetch bot identity (smoke test)
  • sendMessage — text with optional MarkdownV2 / HTML formatting
  • sendPhoto — image by URL, Telegram file_id, or local file path
  • sendDocument — arbitrary file (PDF, ZIP, …) up to 50 MB
  • sendVideo — video by URL, Telegram file_id, or local file path, with optional width/height so Telegram sizes the player correctly
  • sendRichMessage — Bot API 10.2 block-based rich message (passthrough), with local-file attachments uploaded multipart
  • getFile — download a file the bot received, emitted as base64
  • setWebhook / getWebhookInfo / deleteWebhook — manage the bot's webhook registration
  • sendMessage also accepts replyMarkup (e.g. inline keyboards)

Also ships the @magistr/telegram-webhook serve webhook scheme: it verifies Telegram's static X-Telegram-Bot-Api-Secret-Token header in constant time, so Telegram can call a swamp serve webhook directly, and surfaces flat callback / document / magnet views on the update for workflows.

The botToken (and optional webhookSecret) are sensitive globalArguments routed to a vault automatically. Set defaultChatId on the model instance to avoid repeating the chat target on every call.

A natural complement to @magistr/telegram-import, which goes the other direction — importing Telegram channel exports into Obsidian.

02Release Notes

2026.09.24.1

Added

  • Ported from the homelab in-repo copy, which had drifted ahead of this package and was shadowing it on swamp serve: getFile (download a received file as base64), setWebhook / getWebhookInfo / deleteWebhook, sendRichMessage (the port deferred since 2026.08.01.1 as telegram-send-hardening-richmessage-port), replyMarkup on sendMessage, and the optional sensitive webhookSecret global argument.
  • The @magistr/telegram-webhook serve webhook scheme now ships in this package (extensions/webhooks/telegram_webhook.ts). It was never published before, so a registry install of this package could not serve /hooks/telegram.

Changed

  • Every Bot API and file-download request now goes through one token-redacting redactedFetch, including the new getFile download URL (/file/bot<token>/...) and the sendRichMessage multipart upload, which bypassed redaction in the homelab copy.
  • richMessage / files JSON arguments fail with a named error (richMessage is not valid JSON: ...) before any request is sent.
  • Upgrade 2026.09.19.2 -> 2026.09.24.1 carries attributes over unchanged; webhookSecret defaults to empty.
03Models1
@magistr/telegram/sendv2026.09.24.1extensions/models/telegram_send.ts
fn getMe()
Call getMe to verify the bot token and fetch bot identity. Use as a smoke-test.
fn getFile(fileId: string, fileName?: string, mimeType?: string)
Download a file the bot received (e.g. a document sent to the bot) by
ArgumentTypeDescription
fileIdstringTelegram file_id (from message.document.file_id / a webhook update)
fileName?stringOriginal file name to record (Telegram's getFile does not return it)
mimeType?stringOriginal MIME type to record
fn setWebhook(url: string, dropPendingUpdates: boolean, allowedUpdates: array)
Register a webhook URL so Telegram POSTs updates to it (replacing
ArgumentTypeDescription
urlstringHTTPS URL Telegram should POST updates to
dropPendingUpdatesbooleanDiscard updates queued before the webhook was set
allowedUpdatesarrayUpdate types to receive (empty = Telegram default: all but
fn getWebhookInfo()
Fetch the bot's current webhook registration (URL, pending updates,
fn deleteWebhook(dropPendingUpdates: boolean)
Remove the bot's webhook (Telegram reverts to getUpdates). Use to
ArgumentTypeDescription
dropPendingUpdatesboolean
fn sendMessage(text: string, disableWebPagePreview?: boolean, disableNotification?: boolean, replyToMessageId?: number)
Send a text message to a chat or channel.
ArgumentTypeDescription
textstringMessage text (1-4096 characters)
disableWebPagePreview?boolean
disableNotification?boolean
replyToMessageId?number
fn sendPhoto(chatId?: string, caption?: string, disableNotification?: boolean)
Send a photo. `photo` may be an https URL, a Telegram file_id, or a local file path.
ArgumentTypeDescription
chatId?string
caption?string
disableNotification?boolean
fn sendRichMessage(chatId?: string, disableNotification?: boolean)
Send a Rich Message (Bot API 10.2 block-based 'article' formatting: headings, paragraphs, tables, quotations, details, dividers, photo blocks). Pass the full InputRichMessage as `richMessage` JSON ({text?, parse_mode?, entities?, blocks?[], media?[]}); local-file media goes in `files` as {attachName: localPath}, uploaded via multipart and referenced in blocks/media as `attach://<attachName>`. Thin passthrough so it tracks the evolving API without hard-coding block internals.
ArgumentTypeDescription
chatId?string
disableNotification?boolean
fn sendDocument(chatId?: string, caption?: string, disableNotification?: boolean)
Send a document/file. `document` may be an https URL, a Telegram file_id, or a local file path.
ArgumentTypeDescription
chatId?string
caption?string
disableNotification?boolean
fn sendVideo(chatId?: string, caption?: string, width?: number, height?: number, disableNotification?: boolean)
Send a video. `video` may be an https URL, a Telegram file_id, or a local file path.
ArgumentTypeDescription
chatId?string
caption?string
width?numberVideo width
height?numberVideo height
disableNotification?boolean

Resources

botInfo(infinite)— Bot identity returned by getMe
sentMessage(infinite)— Result of a send* call
webhookInfo(infinite)— Result of setWebhook / deleteWebhook / getWebhookInfo
downloadedFile(infinite)— A file downloaded from Telegram via getFile (base64)
04Previous Versions7
2026.09.19.2

2026.09.19.2

Changed

  • Repo-wide maintenance release: version bump to republish the current source. No schema change.
2026.09.17.1

2026.09.17.1

Changed

  • Repo-wide maintenance release: version bump to republish the current source. No schema change.
2026.08.20.1

2026.08.20.1

Added

  • sendVideo — send a video by https URL, Telegram file_id, or local file path (multipart upload), with optional width/height so Telegram sizes the player correctly instead of guessing the aspect ratio. Mirrors sendDocument exactly: same local-vs-remote branch via isLocalPath, the same sentMessage resource mapping, and the same token-redacting error path.

    This closes a drift rather than inventing a feature: the method had been running in the homelab's own in-repo copy of the model (the printer-timelapse workflow calls it) but was never carried back into this published package, so registry consumers could not send video at all.

  • Fixture fixtures/sendVideo.json plus two tests — a contract test pinning the sentMessage mapping, and one asserting width/height actually reach the wire and are omitted when not supplied. The fixture is doc-derived like every other file in fixtures/; no live capture (see PROVENANCE.md).

  • Test helper withEnvelopeCapturing — records each request's decoded JSON body so a test can assert what went ON THE WIRE, not just what came back. The existing withEnvelope discards the request, which would have let a dropped width pass unnoticed.

Still missing

  • sendRichMessage remains un-ported from the in-repo copy — tracked as telegram-send-hardening-richmessage-port. It is a much larger surface (block-based article formatting plus multipart attach:// media) and is deliberately left to its own change.

Modified 1 models

2026.08.19.1
2026.08.01.1

2026.08.01.1

Security hardening: closes the HIGH bot-token credential-leak tracked below as a "Known gap" in the Unreleased entry (filed and planned as the issue-lifecycle model telegram-send-hardening-richmessage-port).

  • Added a module-private redactToken(message, token) pure helper to telegram_send.ts. It replaces the live /bot<token>/ URL segment with /bot<redacted>/, then applies a generic /bot[^/]+/ regex backstop so any /bot.../ path segment is scrubbed even if the token reaches the message reformatted (re-cased, percent-encoded, or otherwise transformed) rather than byte-for-byte. message is accepted as unknown and safely coerced to a string — a fetch rejection is not guaranteed to be an Error with a string .message (it may be a DOMException, a thrown string, or an arbitrary non-Error value) — so no unsanitized shape can pass through unredacted.
  • telegramJson and telegramMultipart now wrap their fetch() call in try/catch: a network-layer rejection (DNS failure, TLS error, connection reset) is caught and rethrown with its message redacted via redactToken, preserving the original rejection as cause for downstream diagnostics. Only the fetch() call itself is wrapped — the ok:false API-error throw (never carries the token, pinned GREEN and covered by property test c) is untouched.
  • Behavior-preserving otherwise: legitimate sends and the ok:false API-error path are unchanged.
  • Tests: flipped the two adversarial suite's former "HONEST GAP pin" tests (telegram_send_adversarial_test.ts, telegramJson/getMe and telegramMultipart/sendPhoto) to assert the fetch-rejection message is now redacted (contains /bot<redacted>/, excludes the raw token, preserves .cause) instead of asserting verbatim propagation. Added direct redactToken unit tests: exact-token redaction, token-free passthrough, the generic backstop for a reformatted token, and non-Error/DOMException/ thrown-string/plain-object coercion (including a case where a non-Error value's own string form embeds the token). All 72 suite tests green; property suite green at FC_NUM_RUNS=5000.
  • README.md: updated the Security note — the token-in-URL fetch-rejection gap is now redacted rather than an open gap.
  • quality.yaml: the byte-frozen-source justification no longer applies (source is modified); ratchet re-measured live.
  • Deferred, tracked separately: porting sendRichMessage from the homelab dev copy is OUT OF SCOPE for this security fix (its homelab source-of-truth is not in this read-only snapshot, so folding it in would be a blind, unverifiable port). It remains tracked by the issue-lifecycle model telegram-send-hardening-richmessage-port as a follow-up.
2026.07.16.2

Release 2026.07.16.2 — align model versions with manifests

Maintenance release across the @magistr extensions. For most packages this carries no functional change: the only edit is the model's version: field, brought back in line with its manifest version so the published model type version and the package version no longer drift.

Functional changes in this release are limited to:

  • anime-cron: normalizeTitle now strips a ": subtitle" suffix and a trailing parenthesized year before comparison, fixing dedup false-misses where the torrent title carries a subtitle or year that the AniList romaji does not.

  • arckit: first publish. Standalone ArcKit port — a 12-phase architecture governance state machine with 65 bundled templates, driven by a bundled skill.

Also tracks three extensions (kaiten, observability-agent, music-library) that previously existed only as untracked working-tree directories, recovered from stashes.

2026.05.13.1
05Stats
A
100 / 100
Downloads
31
Archive size
18.7 KB
  • Has README or module doc2/2earned
  • README has a code example1/1earned
  • README is substantive1/1earned
  • Most symbols documented1/1earned
  • No slow types (deprecated)1/1earned
  • Dependencies pass trust audit2/2earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License declared1/1earned
  • Verified public repository2/2earned
06Platforms
07Labels