A battle-tested, fully open source & libre Telegram bot that served 30,000+ users/month at its peak
Fast lookups • Full-text search • Smart announcement subscriptions • Real-time notifications
Everything the official website should've been, but isn't.
No ads. No tracking. 100% libre and will always remain so.
Important
This bot just got a major rewrite. This branch (grammy-rewrite) contains the new architecture built on GrammY. The legacy implementation lives in the prod branch.
Read the story: Why I rewrote this entire thing
Note
This project is currently in autopilot/maintenance mode. Core functionality depends on public KTU endpoints that can change without notice. If you want to help maintain, extend, or fork it, you're more than welcome.
KTU Bot is a Telegram bot that helps students do everything they could (and should) do on the official KTU website: check announcements, timetables, academic calendars, results, and more. The official site is notoriously clunky and frequently crashes when you actually need it, so this bot taps into their public APIs to deliver a reliable experience the website can't.
What started as a quick 50-line script to check my own results eventually became a lifeline for tens of thousands of students. It turned into the default go-to during results season, sparked a wave of similar tools, and carved out its own identity.
- Full-text search across announcements, academic calendars, and exam timetables. Find what you need right from the chat
- Browse historical data: announcements, exam timetables, academic calendars, and syllabi, all in one place
- Smart subscriptions: get only the announcements that matter to you using filters (course, type), delivered the moment they arrive
- Results lookup (currently broken, not the bot's fault, read why)
Tip
Check out the Commonly Asked Questions for answers to common questions like "Why isn't results working?" and "Will the bot keep working?"
The bot is built as independent services: the main bot, background workers for notifications and data syncing, and supporting databases. If one worker crashes, the bot keeps running.
| Component | Type | What It Does |
|---|---|---|
| Bot | GrammY Telegram bot | Handles all user interactions: commands, searches, conversations |
| Announcements Notify Worker | Background worker | Monitors for new announcements using BullMQ scheduled jobs and sends filtered alerts to users |
| Broadcasts Worker | Background worker | Handles queued broadcast message delivery |
| Data Sync Worker | Background worker | Periodically syncs KTU data to local DB via BullMQ scheduled jobs to power full-text search |
| Attachment Delivery Worker | Background worker | Downloads and sends files asynchronously to prevent bot blocking |
| Bull Board Service | Monitoring service | Web dashboard for real-time queue monitoring and job management |
| PostgreSQL | Database | Stores all data with Drizzle ORM for type-safe queries |
| Redis | Queue | Powers BullMQ jobs |
- Docker + Docker Compose
- Node.js (if running locally); this project uses
pnpm - qpdf for splitting oversized PDF downloads when running locally; the Docker image includes it
- A Telegram bot token from @BotFather
Docker is the recommended way to run the stack.
git clone https://github.com/devadathanmb/ktu-bot.git
cd ktu-botDevelopment defaults live in env/dev/*.env (one file per service or shared module, tracked in git). Each file comes with working defaults; fill in the secrets for the features you use.
Minimum required:
env/dev/bot.env: setBOT_TOKENandBOT_FILE_UPLOAD_CHANNEL_ID
Optional (for extra features):
env/dev/api.env: uptime monitoring keys (UptimeRobot, Better Uptime)env/dev/llm.env: Groq API key for AI-powered announcement filtering
Note
env/dev/*.env files are pre-configured with working defaults. Placeholders like API keys are left blank; fill in only the ones for features you need.
Important
For local secrets:
cp env/dev/.env.example env/dev/.env
# Add your personal API keys, tokens, or credentials hereThis file is mounted last in Docker Compose, so values here override anything in env/dev/*.env files. It is gitignored. Never commit it.
Warning
Missing required variables fail fast at startup via config validation. If a service refuses to start, check its .env file first.
docker compose -f docker/compose/compose.dev.yaml up --buildThis starts all services with hot-reload enabled. Code changes trigger automatic restarts.
If you don't need the workers:
docker compose -f docker/compose/compose.dev.yaml up ktu-bot-app --buildTip
Database migrations are generated and run automatically via the ktu-bot-db-migrations service.
Once everything is up, talk to your bot in Telegram!
Need just the notification worker? No problem:
# Announcements notify worker
docker compose -f docker/compose/compose.dev.yaml up announcements-notify-worker --build
# Data sync worker
docker compose -f docker/compose/compose.dev.yaml up data-sync-worker --build
# Broadcasts worker
docker compose -f docker/compose/compose.dev.yaml up broadcasts-worker --build
# Attachment delivery worker
docker compose -f docker/compose/compose.dev.yaml up attachment-delivery-worker --buildTip
Each service exposes a health check endpoint (e.g., http://localhost:3000/health)
There's also a dedicated bull-board-service running on port 3010 that provides a Bull Board UI for monitoring background workers and queues. Access it at http://localhost:3010
Production config is a gitignored .env at env/prod/.env (created from the tracked template below), plus a small env/prod/attachment-delivery-worker.env for that worker's port.
cp env/prod/.env.example env/prod/.env
# Edit env/prod/.env and fill in all blank valuesPorts and schedules come prefilled (notify every 2 minutes, data sync every 30); the remaining blanks are deployment-specific values: secrets, hosts, timeouts, and logging.
# Start Prometheus monitoring independently
docker compose -f docker/monitoring/compose.yaml up -dThis starts Prometheus on port 9090 with persistent storage. It runs independently from the application stack.
docker compose -f docker/compose/compose.yaml up -d --buildcurl -f http://localhost:3000/health- All services communicate over an internal Docker network
- Database migrations run automatically on startup
env/prod/.envis gitignored. Create it fromenv/prod/.env.exampleon the deploy host (seeenv/dev/*.envfor per-service defaults)- Missing required variables fail startup validation, so verify the
.envbefore deploying
- Language: TypeScript
- Bot Framework: GrammY, type-safe Telegram bot framework
- Database: PostgreSQL with full-text search
- ORM: Drizzle, type-safe SQL queries and migrations
- Job Queue: BullMQ with Redis, background job processing
- HTTP Client: got with in-memory caching
Contributions are welcome, whether bug fixes, new features, docs, or ideas.
Found a bug? Have an idea? Open an issue. When reporting bugs, please include:
- What you were trying to do?
- What happened instead?
- Steps to reproduce (if reproducible)
Tip
New to Telegram Bot ecosystem? Check out this awesome getting started guide from GrammY.
AGPL-3.0: See LICENSE for details.
