Getting started

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-sdk
bun add caspian-sdk

Python 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.

app.py
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)
app.ts
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=8080
bot.py
import 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 server
bot.ts
import { 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='…'
hosted.py
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()
hosted.ts
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.

Next

Handling events →
Filters, commands, buttons, overlap, acknowledgement.
Hosted & self-host →
handle, poll, listen, run.
Telegram →
Webhook vs poll vs hosted.