@smthrs/telegram: bots in serverless functions, workflow tasks, or tests, with no long-lived polling process (this page’s first half).@smthrs/integrations/telegram: workflows that durably wait on Telegram, listening for messages and approving gated steps from the chat via inline buttons or a Mini App (Approve from the chat).
fetch instead of Telegraf so Vercel Functions, Cloudflare Workers, and
Sandbox tasks can call the Bot API without a local polling loop.
Install
Send messages
createTelegramClient retries 429, 5xx, and transient network/read
failures: 429 honors Telegram’s retry_after, others use exponential
backoff. Permanent 4xx responses are not retried.
Verify webhooks
normalizeTelegramUpdate handles message, edited_message, channel_post,
and edited_channel_post, ignoring bot-authored messages by default.
isTelegramChatAllowed fails closed (Eliza’s access-control shape): blank
TELEGRAM_ALLOWED_CHATS allows all chats, a JSON array of ids restricts to
those, and malformed values block all chats until fixed.
Test with a fake
Approve from the chat
<Telegram.Approval> turns a workflow approval into inline Approve/Reject
buttons, resolved from the button press via the durable long-poll source: no
web server, no gateway wiring. The decision matches core
<Approval>’s shape:
{ approved, note, decidedBy, decidedAt }.
Register the two internal node schemas with telegramApprovalSchemas, then
use the component:
mode="select" with options;
the decision becomes { selected, notes }.
The run only receives the press if a Telegram source is polling next to your
workflow:
WaitForEvent wakes on the first callback query for the chat: run one
interactive approval per chat at a time, or pass threadId (a forum topic) to
isolate concurrent approvals. Any chat member can press a button, so keep
approvals private or allowlisted.
Mini App approvals
For a richer decision (read a diff, pick an option, add a note), add a Mini App button:miniApp opens a web_app page in the chat alongside the plain
buttons:
initData string your backend must verify before
trusting the user; never trust initDataUnsafe.
secret = HMAC_SHA256(key="WebAppData", message=botToken). Data is authentic when
hex(HMAC_SHA256(key=secret, message=dataCheckString)) equals hash, where
dataCheckString is every field except hash, formatted key=value, sorted,
and newline-joined. It runs on Web Crypto, working in both the Smithers
runtime and a Cloudflare Worker. verifyTelegramWebAppInitDataSignature
verifies the newer Ed25519 signature from a third party with only the
numeric bot id.
A runnable reference (a static Mini App page plus a Worker endpoint that verifies
initData for real) ships in apps/telegram-site: site/approve.html and
src/handleApprove.ts.