A Telegram group bot built with TypeScript, Bun, Effect, and Telly.
SuperSeriousBot has grown with its groups for years. It combines AI, media, search, reminders, social tools, and group history in one bot.
- AI: native rich replies, semantic search, image and video generation, custom songs, summaries, and transcription
- Native Telegram UI: rich tables, lists, details, charts, dates, links, and command dashboards
- Group memory: semantic
/search, citations, member personas, and group lore - Media:
/set,/get,/dl, automatic reel downloads, memes, and quotes - Social:
/summon,/habit,/highlight, reactions, mentions, andsedcorrections - Utilities: reminders, scheduled AI tasks, football alerts, weather, books, games, and translation
- Operations: durable Telly inboxes and jobs, polling or webhooks, command quotas, and failure records
Run /help to see the commands enabled by the configured API keys.
/ask always enables OpenRouter web search, including after /model ask changes.
OpenRouter uses native search when the selected model supports it and falls back
to Exa otherwise. Search charges are separate from model token charges.
AI answers, summaries, and scheduled AI results share the policy in
src/app/rich.ts. Optimize for a phone, not a document:
- Answer first; short prose for simple questions, labeled bullets for alternatives.
- Use bold sparingly for key facts. Highlight backgrounds depend on the Telegram theme and can make white text unreadable; use them only when explicitly requested.
- Keep comparisons narrow. Tables, headings, code, and spoilers have their uses, but are not default decoration. Preserve requested detail rather than forcing every answer into a fixed template.
- Keep currency, units, and one-way/return qualifiers clear. Avoid unsolicited conversions. Put source links beside supported claims and separate brief caveats.
Do not repair model output with regex replacements: that can corrupt code, math,
URLs, or deliberately requested formatting. Telegram owns Markdown rendering;
we send it unchanged through Telly. This is a model instruction, not a guarantee
of compliance. Keep prompt-policy checks separate from delivery/rendering tests.
The illustrative fare fixture under tests/fixtures/phone-answer.json is not
current travel advice and uses placeholder source URLs.
/tl uses Google Translate's batch web endpoint through google-translate-api-x, without an API key. Use /tl Bonjour for English, /tl fr - Good morning for a specific language, or reply to a message with /tl [language]. Provider outages and rate limits are reported separately from unknown languages.
Requirements:
- Bun 1.4
ffmpegyt-dlp
git clone https://github.com/obviyus/SuperSeriousBot
cd SuperSeriousBot
cp .env.example .env
bun install --frozen-lockfile
bun run devRequired environment values:
TELEGRAM_TOKEN: token from @BotFatherQUOTE_CHANNEL_ID: private channel used to archive quotesTURSO_DATABASE_URL: LibSQL or Turso database URLTURSO_AUTH_TOKEN: database token
Optional integrations:
OPENROUTER_API_KEY: AI, image, video, transcription, summaries, search, and cronOPENROUTER_BASE_URL: optional OpenRouter-compatible endpoint for local testingBASED_PROVIDER:local(default) ornanogptfor/basedBASED_MODEL: model ID for/basedBASED_BASE_URL: OpenAI-compatible endpoint for thelocalprovider (include/v1)KIE_API_KEY: song generationCOBALT_URL: media downloadsNANO_GPT_API_KEY: URL/YouTube extraction and/basedwhenBASED_PROVIDER=nanogptGOODREADS_API_KEY,WOLFRAM_APP_ID,WEATHERAPI_API_KEY, andWAQI_API_KEY
/based [query] streams a text answer from the configured model. Reply to a
text message to include it as context. It has its own chat/user whitelist and a
40-request daily limit, like /ask. Enable it for a group through /settings.
It needs no OpenRouter key and does not use /model ask or /thinking settings.
Admins can use /model based <model-id> to save a global model override. /model
shows the current selection, and /model all <model-id> also includes /based.
Changes apply to the next request and survive restarts. BASED_MODEL is the default
until an override is saved; /model does not change the configured provider.
Set BASED_PROVIDER=nanogpt and BASED_MODEL to a NanoGPT catalog ID to use hosted
inference. It uses the existing NANO_GPT_API_KEY and NanoGPT's fixed API endpoint;
BASED_BASE_URL applies only to local. NanoGPT requests use reasoning_effort=none;
local requests retain their chat-template thinking control.
Use /ask for supported images. /based rejects media. Requests stop after two minutes; responses
are capped at 1,024 tokens. There is no fallback to another provider on failure.
For the local provider, use a private Tailscale HTTPS route to its loopback listener, as with Cobalt. Allow only the bot host to reach that route. Keep the endpoint and model ID in the app environment; do not expose the unauthenticated model API publicly.
Operations:
ADMINS: space-separated Telegram user IDsSEARCH_EXCLUDED_USERS: space-separated usernames whose messages stay indexed but never reach/searchanswer, persona, or lore modelsUPDATER:pollingorwebhookWEBHOOK_URL: public base URL in webhook modePORT: webhook server port, default8443LOGGING_CHANNEL_ID: optional Telegram failure logTELLY_STATE_DIRECTORY: local inbox and job database directory, default./dbTELEGRAM_API_ROOT: custom Bot API root for local Test Server harnesses
Evaluate search with a private JSON file containing [{ "chatId": -100123, "question": "Who is the biggest tech nerd here?" }]:
bun run search:evaluate /path/to/private-questions.jsonThis reads chat data and calls the configured AI service without sending Telegram messages or writing search events. Each answer gets a fresh review against its complete context. Results are printed as JSON lines. Keep question files and reports private.
cp .env.example ssgbot.env
docker compose up --buildThe image installs ffmpeg and yt-dlp. The db volume stores Telly's durable inbox and scheduled jobs.
bun run checkThe test suite drives the real Telly handler against its hermetic Bot API fake. It also checks compatibility with the schema produced by all 33 Python migrations.
The local Test Server harness uses leased QA credentials from the Telly skill:
bun run test:e2e
TELEGRAM_E2E_POLLING_RECOVERY=1 bun run test:e2e
TELEGRAM_E2E_GROUP=1 bun run test:e2e
TELEGRAM_E2E_STATIC_RICH=1 TELEGRAM_E2E_GROUP=1 bun run test:e2e
TELEGRAM_E2E_FORMATTING=1 bun run test:e2e
TELEGRAM_E2E_WEBHOOK=1 bun run test:e2eThe vendored Telly bundle is built from upstream commit a311a8239993c0e8aba63149b02a3b88f4309fa7
(polling recovery fix). The polling-recovery
Test Server scenario injects six local getUpdates 502 responses and proves a real
user's /ping is answered by the original process after the outage. It records
the injected fault timeline alongside Telegram event evidence; it does not
claim Telegram itself produced those errors.
Set TELEGRAM_E2E_SKILL_DIR and TELEGRAM_E2E_CONVEX_PROJECT_DIR when the leased
Test Server skill and broker checkout are not at the harness's default paths.
The formatting scenario checks the shared policy reaches the provider and a deterministic fare answer becomes a native rich-message edit on the Test Server. It proves delivery/rendering, not live-model compliance or visual contrast in every client.
Operator commands:
bun run operator usage --days 30 --status failed
bun run operator search-index --chat-id -1001234567890
bun run operator search-memory --chat-id -1001234567890Before deploying /model based to an existing database, run
bun run operator migrate-model-settings with that database's environment. This
adds the nullable model-setting field and preserves existing settings. Run it
separately before replacing the bot.
Before deploying the incremental search indexer to an existing database, run
bun run operator migrate-query-schema with that database's environment.
The migration adds the generation column to existing search progress and replaces
the source-change triggers in one transaction. It preserves existing rows and the
previous indexer's append notifications, and is safe to run again. Run it separately
before replacing the running bot; startup does not upgrade existing progress.
Indexing saves progress separately for each chat and embedding configuration. Unchanged chats do no indexing work. New messages extend the unfinished windows and speaker groups. Older imports, source edits or deletes, and author changes request the existing full-history pass. Existing embedding ranges retain their current reuse behavior. Each pass claims a generation and captures a fixed message boundary. Later appends remain pending without invalidating that prefix. Backfills, edits, deletes, author changes, and newer workers invalidate the claim. A failed or superseded pass cannot advance progress.
- Telly owns Telegram transport, updates, routing, persistence, and jobs.
- Vercel AI SDK owns text, structured output, streaming, embeddings, images, and video.
- Effect owns typed effects, services, interruption, concurrency, and shutdown.
- Bun owns packages, tests, development, and the production runtime.
- Turso stores bot and search data.
