Quickstart
Same handlers, two processes: you own Telegram’s webhook, or Caspian’s gateway does. Telegram always needs a BotFather token. Hosted does not mint a bot. Self-host needs no Caspian API key.
1. Install
pip install caspian-sdkbun add caspian-sdkPython 3.10+. Discord socket extra: pip install 'caspian-sdk[discord]'. Slack Socket Mode: pip install 'caspian-sdk[slack-socket]'.
2. Write the handlers once
Keep rules in one module. The process file only does channels.add and the inbound loop. This is the same shape as the Telegram example: specific commands first, echo last.
from caspian import Button, Caspian, HandlerContext, Message, Thread
def register(cx: Caspian) -> None:
@cx.on_message({"channel": "telegram", "command": ["start", "help"]})
def on_help(thread: Thread, msg: Message, ctx: HandlerContext) -> None:
thread.post("send anything — I echo it.", actions=(
Button(label="docs", url="https://trycaspianai.com/docs/"),
))
@cx.on_message({"channel": "telegram"})
def on_echo(thread: Thread, msg: Message, ctx: HandlerContext) -> None:
if msg.text.strip():
thread.post(msg.text)import { Caspian } from "caspian-sdk"
export function register(cx: Caspian) {
cx.onMessage({ channel: "telegram", command: ["start", "help"] }, async (thread) => {
await thread.post("send anything — I echo it.", {
actions: [{ label: "docs", url: "https://trycaspianai.com/docs/" }],
})
})
cx.onMessage({ channel: "telegram" }, async (thread, msg) => {
if (msg.text.trim()) await thread.post(msg.text)
})
}3a. Self-host — no Caspian key
Caspian() with no arguments. Pass via="self-host" and your BotFather token. A public HTTPS URL (ngrok / cloudflared) is registered with Telegram via webhook_url. Poll and webhook cannot both be active — call deleteWebhook before poll.
export TELEGRAM_BOT_TOKEN='…' # BotFather → /newbot
export TELEGRAM_WEBHOOK_URL='https://…' # public HTTPS (Python self-host registers it)
export PORT=8080import os
from http.server import BaseHTTPRequestHandler, HTTPServer
from caspian import Caspian
from app import register
token = os.environ["TELEGRAM_BOT_TOKEN"]
webhook_url = os.environ["TELEGRAM_WEBHOOK_URL"]
cx = Caspian() # no api_key
register(cx)
cx.channels.add(
"telegram",
via="self-host",
bot_token=token,
webhook_url=webhook_url,
)
class Hook(BaseHTTPRequestHandler):
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length", "0")))
cx.handle("telegram", body, dict(self.headers))
self.send_response(200)
self.end_headers()
HTTPServer(("127.0.0.1", int(os.environ.get("PORT", "8080"))), Hook).serve_forever()
# no public URL: cx.poll("telegram") instead of the HTTP serverimport { Caspian } from "caspian-sdk"
import { register } from "./app.ts"
const token = process.env.TELEGRAM_BOT_TOKEN!
const port = Number(process.env.PORT ?? "8080")
const cx = new Caspian()
await cx.channels.add("telegram", { via: "self-host", bot_token: token })
register(cx)
Bun.serve({
port,
fetch: async (request) => {
if (request.method !== "POST") return new Response("ok")
const body = await request.text()
const headers: Record<string, string> = {}
request.headers.forEach((value, key) => { headers[key] = value })
void cx.handle("telegram", body, headers)
return Response.json({ ok: true })
},
})
// TypeScript add() does not register the webhook yet — setWebhook after start.
// no public URL: await cx.poll("telegram")3b. Hosted — Caspian key + via="hosted"
Construct the client with api_key, then channels.add(..., via="hosted"). This process never sees a Telegram Update: cx.run() polls GET /v1/events and feeds each payload to handle("gateway", …).
export TELEGRAM_BOT_TOKEN='…'
export CASPIAN_API_KEY='…'import os
from caspian import Caspian
from app import register
cx = Caspian(api_key=os.environ["CASPIAN_API_KEY"])
cx.channels.add("telegram", via="hosted", bot_token=os.environ["TELEGRAM_BOT_TOKEN"])
register(cx)
cx.run()import { Caspian } from "caspian-sdk"
import { register } from "./app.ts"
const cx = new Caspian()
await cx.channels.add("telegram", {
via: "hosted",
bot_token: process.env.TELEGRAM_BOT_TOKEN!,
})
register(cx)
await cx.run({ apiKey: process.env.CASPIAN_API_KEY! })4. Talk to it
Send /help in the chat. Ctrl+C stops either process.