Channels

Telegram

Webhook or poll inbound. A BotFather token is always required — hosted does not mint a bot. Self-host needs no Caspian API key. Same handlers in both modes (/help, /send, /reply, buttons, stream, …).

Self-host webhook

export TELEGRAM_BOT_TOKEN='…'
export TELEGRAM_WEBHOOK_URL='https://…'   # ngrok / cloudflared (Python registers it)
export TELEGRAM_WEBHOOK_SECRET='…'        # optional
export PORT=8080
bot.py
cx = Caspian()   # no Caspian key
cx.channels.add(
    "telegram",
    via="self-host",
    bot_token=token,
    webhook_url=webhook_url,
    webhook_secret=webhook_secret,
)
# POST → cx.handle("telegram", body, headers)
# no public URL: cx.poll("telegram")  — deleteWebhook first
bot.ts
const cx = new Caspian()
await cx.channels.add("telegram", {
  via: "self-host",
  bot_token: token,
  webhook_secret,   // optional
})
// POST → await cx.handle("telegram", body, headers)
// TypeScript add() does not setWebhook yet — register the URL after start.
// no public URL: await cx.poll("telegram")

Hosted

export TELEGRAM_BOT_TOKEN='…'
export CASPIAN_API_KEY='…'
cx = Caspian(api_key=api_key)
cx.channels.add("telegram", via="hosted", bot_token=token)
cx.run()   # GET /v1/events → handle("gateway", …)
const cx = new Caspian()
await cx.channels.add("telegram", { via: "hosted", bot_token: token })
await cx.run({ apiKey })   // GET /v1/events → handle("gateway", …)

What it can do

Receive, reply, send, media, buttons, edit, delete, react, typing, pin, forward, threading, membership. send_blocks has no native Telegram blocks — it renders as text + keyboard.

Handlers in the example

Specific commands first, echo last. /reply calls thread.reply(msg.message_id, …) (quote). /send calls thread.send(…) (standalone, not a reply). Everything else uses thread.post. Buttons use on_action. Pin is groups/channels only.