A Rose-style Telegram group-management bot with an AI /ask command for students.
TypeScript · grammY · Vercel serverless · Neon Postgres · OpenRouter.
About "MissRose's source code": Rose (@MissRose_bot) is closed-source and cannot be legally extracted or reverse-engineered from her servers. Her author's own open-source codebase (
PaulSonOfLars/tgbot) is an archived Python app that can't run on Vercel serverless anyway. Athena is a clean-room reimplementation of Rose-style behavior — no copied code — designed from day one for Vercel webhooks.
AI & study tools
/ask <question>— AI answers via OpenRouter; reply to any message with /ask to have it answered. Per-user daily limit (admins exempt)./setpersona <text>— per-group AI customization: admins define how/askanswers in their group only (language, curriculum, format)./personato view,/resetpersonato clear./remind 1h30m|18:30|2026-09-01 <text>— reminders (scheduler runs every minute) •/reminders•/delremind/exam <YYYY-MM-DD> <name>— daily countdown posted to the chat •/exams/quiz <topic>— AI-generated 5-question MCQ quiz with inline buttons and a scoreboard •/quizstop/summarize(reply) — summarize a long message or a linked article/recap— AI recap of today's chat •/resources— auto-indexed links shared in the group/draw <description>— AI image generation via pollinations.ai (free, no key)/chart <question or data>— precise, data-accurate charts: the AI extracts the real values from your question, and code renders the chart exactly (bar/line/pie, true labels & numbers — never a hallucinated drawing)./askautomatically attaches a chart when an answer is numeric. Shares the/askdaily quota.
Group management (Rose-style)
- Welcome & goodbye messages with fillings
{first} {last} {fullname} {username} {id} {chatname} {count} - Rules (
/setrules,/rules) - Warnings with configurable limit/action (
/warn,/warnings,/resetwarn,/warnlimit N,/warnaction ban|kick|mute) + inline "remove warning" button - Locks (
/lock photo url forward …,/unlock,/locks) incl.botsauto-ban andall - Purge (
/purgeas reply deletes a range,/spurgesilent,/del) - Notes saved by admins, retrieved with
#nameor/get name - Keyword filters with automatic replies (
/filter,/stop,/filters) - Antiflood with temporary mute (
/antiflood on|off|N) - Info tools (
/info,/id,/admins,/report) - Housekeeping (
/pin,/unpin,/cleanservice on|off,/help,/about)
Every setting is stored per chat id — nothing leaks between groups. Each group can have its
own welcome text, rules, warn policy, locks, notes, filters, and — via /setpersona — its own
AI answer style. Example:
/setpersona Answer in Sinhala first, then English.
Focus on A/L Biology syllabus. Always end with one exam-style practice question.
- Locally:
npm run devruns the scheduler every 60 s alongside polling. - On Vercel:
vercel.jsonships a daily cron (Hobby plan limit). For minute-level reminders on Hobby, point any external pinger (e.g. cron-job.org) atPOST /api/cronwith headerAuthorization: Bearer <CRON_SECRET>every minute. On Pro, change the vercel.json cron schedule to* * * * *. - Clock-based times (
18:30) use the server timezone — UTC on Vercel.
-
Bot token — create a bot with @BotFather, copy the token.
-
Database (Neon Postgres) — easiest from inside Vercel: project → Storage → Create Database → Neon → pick a region near your students → Create. Vercel injects
POSTGRES_URLautomatically. (Or create directly at neon.tech and paste the connection string asDATABASE_URL.) -
AI — create an API key at openrouter.ai/keys.
-
Clone this repo and install:
npm install cp .env.example .env # fill in your values
Long-polling runner (no webhook needed):
npm run devAdd the bot to a test group, promote it to admin (delete messages + ban users rights recommended).
npm i -g vercel
vercel link
vercel env add TELEGRAM_BOT_TOKEN # repeat for every variable in .env.example
vercel env add WEBHOOK_SECRET # invent a random string (A-Za-z0-9_-)
vercel env add OPENROUTER_API_KEY
vercel env add CRON_SECRET
vercel deploy --prod
# Then: Vercel dashboard → Storage → Create Database → Neon
# (links POSTGRES_URL to the project and redeploys)Then point Telegram at your deployment:
npm run set-webhook -- https://<your-app>.vercel.appEvery request is authenticated via the webhook secret token header — requests without it get a 401.
| Variable | Required | Purpose |
|---|---|---|
TELEGRAM_BOT_TOKEN |
✅ | From @BotFather |
WEBHOOK_SECRET |
prod | Secret sent back on every webhook call |
POSTGRES_URL |
✅ prod | Neon Postgres — injected automatically by Vercel's Storage → Neon integration |
OPENROUTER_API_KEY |
✅ | Primary AI key |
OPENROUTER_API_KEYS |
– | Extra OpenRouter keys, comma-separated — rotated automatically when one is rate-limited (each key has its own daily quota) |
OPENROUTER_MODEL |
– | Preferred free model. Free slugs rotate often — browse openrouter.ai/models?max_price=0. Default: minimax/minimax-m2.7:free |
OPENROUTER_MODEL_FALLBACK |
– | Tried automatically when the primary is rate-limited or returns junk |
POLLINATIONS_MODEL |
– | Image model for /draw (default flux) |
ASK_DAILY_LIMIT |
– | Per-user /ask calls per day (default 10) |
CRON_SECRET |
prod | Protects /api/cron (reminders, exam countdowns) |
USE_LOCAL_STORE |
– | 1 = store data in a local JSON file instead of Postgres (dev) |
Free OpenRouter keys allow ~50 requests/day unless you've purchased ≥10 credits (then 1000/day). The per-user quota protects your key from being drained.
/ask is the single entry point. Every prompt is auto-routed:
- Needs fresh web facts or exact computation → Groq compound model (built-in web search + python code execution)
- Numeric comparisons/trends in the answer → a precise chart is rendered and attached automatically
- Everything else → the always-free OpenRouter chain: your keys (
OPENROUTER_API_KEY,OPENROUTER_API_KEY_2.._4, rotated when one is rate-limited or rejected) × free models (primary → fallback → up to 4 auto-discovered:freemodels, cached 1 h) → keyless Pollinations text API as the final safety net.
The model itself can also request a reroute (ROUTE:web / ROUTE:python) mid-answer.
Junk answers ("User Safety: safe", leaked reasoning) are filtered everywhere.
Note OpenRouter's free cap (~50/day) is per key — N keys ≈ N × quota; credits raise
a key to 1,000/day.
Telegram ──webhook──▶ api/webhook.ts (secret check) ──▶ src/bot.ts (grammY singleton)
├─ command modules (src/modules/*)
├─ member events (welcome/goodbye/bot-lock)
└─ enforcement pipeline (guards):
cleanservice → locks → antiflood → filters → #notes
│
Neon Postgres ◀────┘ (settings/warns/notes/filters/quota)
OpenRouter ◀────── (/ask)
Settings are loaded once per update and persisted only when mutated. Filter lists are cached in-memory per instance for 60s.
npm run typecheck # tsc --noEmit
npm test # vitest (pure logic: templates, locks, filters, quotas, AI parsing)- Bot ignores commands in a group — make sure it was added after deployment and that privacy mode allows commands (or just make it admin).
- "I need permission to …" — promote the bot with Delete messages and Ban users rights.
- Empty/odd /ask answers — try another
OPENROUTER_MODEL; free models rotate, some leak reasoning or safety fragments (already filtered), and the fallback model kicks in automatically.
Built by Januth Nimnal · januth.dev