Repository files navigation

KTU Bot ⚡

KTU Bot

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.

Better Stack Badge


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. ❤️


What Is This? 🤔

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.

What You Can Do

  • 🔍 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?"

Architecture Overview 🏗️

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.

ComponentTypeWhat It Does
BotGrammY Telegram botHandles all user interactions — commands, searches, conversations
Announcements Notify WorkerBackground workerMonitors for new announcements using BullMQ scheduled jobs and sends filtered alerts to users
Broadcasts WorkerBackground workerHandles queued broadcast message delivery
Data Sync WorkerBackground workerPeriodically syncs KTU data to local DB via BullMQ scheduled jobs to power full-text search
Attachment Delivery WorkerBackground workerDownloads and sends files asynchronously to prevent bot blocking
Bull Board ServiceMonitoring serviceWeb dashboard for real-time queue monitoring and job management
PostgreSQLDatabaseStores all data with Drizzle ORM for type-safe queries
RedisQueuePowers BullMQ jobs

Tip

Want to understand how it all works? Check out How It Works for the complete architecture breakdown with diagrams.

Quick Start 🚀

Prerequisites

Trust me. Docker is the easiest way to run anything within seconds 🙃

1. Clone the Repo

git clone https://github.com/devadathanmb/ktu-bot.git
cd ktu-bot

2. Configure Environment

Development environment files live in the env/dev/ directory. Each service/module has its own .env file.

Minimum required:

  • env/dev/bot.env — Set BOT_TOKEN and BOT_FILE_UPLOAD_CHANNEL_ID
  • Most files come prefilled with sensible defaults

Optional (for extra features):

  • env/dev/api.env — For UptimeRobot monitoring, file uploads, etc.
  • env/dev/llm.env — For AI-powered announcement filtering

Note

Most environment variables needed for the development setup come pre-configured in each .env file.

However, some configurations depend on external services and are left as placeholder values. Fill those in with actual credentials if you plan to use those features.

Important

For sensitive local secrets:

cp env/dev/.env.example env/dev/.env
# Add your personal API keys, tokens, or credentials here

This file is mounted last in Docker Compose, so values here override anything in env/dev/*.env files.

Warning

If you don't configure certain .env variables, those features simply won't work or the zod validations may get triggered. Review each file to see what's needed.

3. Run Everything

docker compose -f docker/compose/compose.dev.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.dev.yaml up --build

This starts all services with hot-reload enabled. Code changes trigger automatic restarts.

4. Run Only the Bot

If you don't need the workers:

docker compose -f docker/compose/compose.dev.yaml up ktu-bot-app --build

Tip

Database migrations are generated and run automatically via the ktu-bot-db-migrations service.

Once everything is up, talk to your bot in Telegram!

5. Run Individual Workers

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 --build

Tip

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 Deployment 🏭

Production uses a single .env file in the env/prod/ directory.

1. Configure Environment

cp env/prod/.env.example env/prod/.env
# Edit env/prod/.env and fill in all required values# Most values come pre-configured — just update anything specific to your deployment.

2. Start Monitoring (Optional but Recommended)

# Start Prometheus monitoring independently
docker compose -f docker/monitoring/compose.yaml up -d

This starts Prometheus on port 9090 with persistent storage. It runs independently from the application stack.

3. Start Application Services

docker compose -f docker/compose/compose.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.yaml up -d --build

4. Verify Health

curl -f http://localhost:3000/health

Notes

  • All services communicate over an internal Docker network
  • Database migrations run automatically on startup
  • Make sure all required API keys/tokens are provided
  • If some keys are missing, update the code to handle their absence gracefully

Tech Stack 🛠️

  • Language:TypeScript — Because type-safe code is always better?
  • Bot Framework:GrammY — Modern, type-safe Telegram bot framework
  • Database:PostgreSQL — Powerful relational DB with god knows how many features
  • ORM:Drizzle — Type-safe SQL queries and migrations
  • Job Queue:BullMQ — Reliable background job processing
  • HTTP Client:got — Modern fetch wrapper with in-memory caching

Contributing 🤝

Contributions are welcome! Whether it's bug fixes, new features, documentation improvements, or ideas — all are appreciated.

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

Need help getting started? Check out How It Works to understand the architecture.

Tip

New to Telegram Bot ecosystem? Check out this awesome getting started guide from GrammY.


Documentation 📚

License 🛡️

AGPL-3.0 — See LICENSE for details.

This means you can use, modify, and distribute this code freely, but you must:

  • Keep it open source
  • Share your changes under the same license
  • Give credit where it's due
  • If you run this software on a server and let users interact with it remotely, you must provide them with the source code

About

A telegram bot to view KTU exam results and notifications easily.

Topics

Resources

Stars

60 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

KTU Bot ⚡

KTU Bot

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.

Better Stack Badge


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. ❤️


What Is This? 🤔

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.

What You Can Do

  • 🔍 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?"

Architecture Overview 🏗️

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.

ComponentTypeWhat It Does
BotGrammY Telegram botHandles all user interactions — commands, searches, conversations
Announcements Notify WorkerBackground workerMonitors for new announcements using BullMQ scheduled jobs and sends filtered alerts to users
Broadcasts WorkerBackground workerHandles queued broadcast message delivery
Data Sync WorkerBackground workerPeriodically syncs KTU data to local DB via BullMQ scheduled jobs to power full-text search
Attachment Delivery WorkerBackground workerDownloads and sends files asynchronously to prevent bot blocking
Bull Board ServiceMonitoring serviceWeb dashboard for real-time queue monitoring and job management
PostgreSQLDatabaseStores all data with Drizzle ORM for type-safe queries
RedisQueuePowers BullMQ jobs

Tip

Want to understand how it all works? Check out How It Works for the complete architecture breakdown with diagrams.

Quick Start 🚀

Prerequisites

Trust me. Docker is the easiest way to run anything within seconds 🙃

1. Clone the Repo

git clone https://github.com/devadathanmb/ktu-bot.git
cd ktu-bot

2. Configure Environment

Development environment files live in the env/dev/ directory. Each service/module has its own .env file.

Minimum required:

  • env/dev/bot.env — Set BOT_TOKEN and BOT_FILE_UPLOAD_CHANNEL_ID
  • Most files come prefilled with sensible defaults

Optional (for extra features):

  • env/dev/api.env — For UptimeRobot monitoring, file uploads, etc.
  • env/dev/llm.env — For AI-powered announcement filtering

Note

Most environment variables needed for the development setup come pre-configured in each .env file.

However, some configurations depend on external services and are left as placeholder values. Fill those in with actual credentials if you plan to use those features.

Important

For sensitive local secrets:

cp env/dev/.env.example env/dev/.env
# Add your personal API keys, tokens, or credentials here

This file is mounted last in Docker Compose, so values here override anything in env/dev/*.env files.

Warning

If you don't configure certain .env variables, those features simply won't work or the zod validations may get triggered. Review each file to see what's needed.

3. Run Everything

docker compose -f docker/compose/compose.dev.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.dev.yaml up --build

This starts all services with hot-reload enabled. Code changes trigger automatic restarts.

4. Run Only the Bot

If you don't need the workers:

docker compose -f docker/compose/compose.dev.yaml up ktu-bot-app --build

Tip

Database migrations are generated and run automatically via the ktu-bot-db-migrations service.

Once everything is up, talk to your bot in Telegram!

5. Run Individual Workers

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 --build

Tip

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 Deployment 🏭

Production uses a single .env file in the env/prod/ directory.

1. Configure Environment

cp env/prod/.env.example env/prod/.env
# Edit env/prod/.env and fill in all required values# Most values come pre-configured — just update anything specific to your deployment.

2. Start Monitoring (Optional but Recommended)

# Start Prometheus monitoring independently
docker compose -f docker/monitoring/compose.yaml up -d

This starts Prometheus on port 9090 with persistent storage. It runs independently from the application stack.

3. Start Application Services

docker compose -f docker/compose/compose.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.yaml up -d --build

4. Verify Health

curl -f http://localhost:3000/health

Notes

  • All services communicate over an internal Docker network
  • Database migrations run automatically on startup
  • Make sure all required API keys/tokens are provided
  • If some keys are missing, update the code to handle their absence gracefully

Tech Stack 🛠️

  • Language:TypeScript — Because type-safe code is always better?
  • Bot Framework:GrammY — Modern, type-safe Telegram bot framework
  • Database:PostgreSQL — Powerful relational DB with god knows how many features
  • ORM:Drizzle — Type-safe SQL queries and migrations
  • Job Queue:BullMQ — Reliable background job processing
  • HTTP Client:got — Modern fetch wrapper with in-memory caching

Contributing 🤝

Contributions are welcome! Whether it's bug fixes, new features, documentation improvements, or ideas — all are appreciated.

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

Need help getting started? Check out How It Works to understand the architecture.

Tip

New to Telegram Bot ecosystem? Check out this awesome getting started guide from GrammY.


Documentation 📚

License 🛡️

AGPL-3.0 — See LICENSE for details.

This means you can use, modify, and distribute this code freely, but you must:

  • Keep it open source
  • Share your changes under the same license
  • Give credit where it's due
  • If you run this software on a server and let users interact with it remotely, you must provide them with the source code

About

A telegram bot to view KTU exam results and notifications easily.

Topics

Resources

Stars

60 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

KTU Bot ⚡

KTU Bot

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.

Better Stack Badge


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. ❤️


What Is This? 🤔

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.

What You Can Do

  • 🔍 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?"

Architecture Overview 🏗️

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.

ComponentTypeWhat It Does
BotGrammY Telegram botHandles all user interactions — commands, searches, conversations
Announcements Notify WorkerBackground workerMonitors for new announcements using BullMQ scheduled jobs and sends filtered alerts to users
Broadcasts WorkerBackground workerHandles queued broadcast message delivery
Data Sync WorkerBackground workerPeriodically syncs KTU data to local DB via BullMQ scheduled jobs to power full-text search
Attachment Delivery WorkerBackground workerDownloads and sends files asynchronously to prevent bot blocking
Bull Board ServiceMonitoring serviceWeb dashboard for real-time queue monitoring and job management
PostgreSQLDatabaseStores all data with Drizzle ORM for type-safe queries
RedisQueuePowers BullMQ jobs

Tip

Want to understand how it all works? Check out How It Works for the complete architecture breakdown with diagrams.

Quick Start 🚀

Prerequisites

Trust me. Docker is the easiest way to run anything within seconds 🙃

1. Clone the Repo

git clone https://github.com/devadathanmb/ktu-bot.git
cd ktu-bot

2. Configure Environment

Development environment files live in the env/dev/ directory. Each service/module has its own .env file.

Minimum required:

  • env/dev/bot.env — Set BOT_TOKEN and BOT_FILE_UPLOAD_CHANNEL_ID
  • Most files come prefilled with sensible defaults

Optional (for extra features):

  • env/dev/api.env — For UptimeRobot monitoring, file uploads, etc.
  • env/dev/llm.env — For AI-powered announcement filtering

Note

Most environment variables needed for the development setup come pre-configured in each .env file.

However, some configurations depend on external services and are left as placeholder values. Fill those in with actual credentials if you plan to use those features.

Important

For sensitive local secrets:

cp env/dev/.env.example env/dev/.env
# Add your personal API keys, tokens, or credentials here

This file is mounted last in Docker Compose, so values here override anything in env/dev/*.env files.

Warning

If you don't configure certain .env variables, those features simply won't work or the zod validations may get triggered. Review each file to see what's needed.

3. Run Everything

docker compose -f docker/compose/compose.dev.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.dev.yaml up --build

This starts all services with hot-reload enabled. Code changes trigger automatic restarts.

4. Run Only the Bot

If you don't need the workers:

docker compose -f docker/compose/compose.dev.yaml up ktu-bot-app --build

Tip

Database migrations are generated and run automatically via the ktu-bot-db-migrations service.

Once everything is up, talk to your bot in Telegram!

5. Run Individual Workers

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 --build

Tip

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 Deployment 🏭

Production uses a single .env file in the env/prod/ directory.

1. Configure Environment

cp env/prod/.env.example env/prod/.env
# Edit env/prod/.env and fill in all required values# Most values come pre-configured — just update anything specific to your deployment.

2. Start Monitoring (Optional but Recommended)

# Start Prometheus monitoring independently
docker compose -f docker/monitoring/compose.yaml up -d

This starts Prometheus on port 9090 with persistent storage. It runs independently from the application stack.

3. Start Application Services

docker compose -f docker/compose/compose.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.yaml up -d --build

4. Verify Health

curl -f http://localhost:3000/health

Notes

  • All services communicate over an internal Docker network
  • Database migrations run automatically on startup
  • Make sure all required API keys/tokens are provided
  • If some keys are missing, update the code to handle their absence gracefully

Tech Stack 🛠️

  • Language:TypeScript — Because type-safe code is always better?
  • Bot Framework:GrammY — Modern, type-safe Telegram bot framework
  • Database:PostgreSQL — Powerful relational DB with god knows how many features
  • ORM:Drizzle — Type-safe SQL queries and migrations
  • Job Queue:BullMQ — Reliable background job processing
  • HTTP Client:got — Modern fetch wrapper with in-memory caching

Contributing 🤝

Contributions are welcome! Whether it's bug fixes, new features, documentation improvements, or ideas — all are appreciated.

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

Need help getting started? Check out How It Works to understand the architecture.

Tip

New to Telegram Bot ecosystem? Check out this awesome getting started guide from GrammY.


Documentation 📚

License 🛡️

AGPL-3.0 — See LICENSE for details.

This means you can use, modify, and distribute this code freely, but you must:

  • Keep it open source
  • Share your changes under the same license
  • Give credit where it's due
  • If you run this software on a server and let users interact with it remotely, you must provide them with the source code

About

A telegram bot to view KTU exam results and notifications easily.

Topics

Resources

Stars

60 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

KTU Bot ⚡

KTU Bot

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.

Better Stack Badge


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. ❤️


What Is This? 🤔

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.

What You Can Do

  • 🔍 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?"

Architecture Overview 🏗️

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.

ComponentTypeWhat It Does
BotGrammY Telegram botHandles all user interactions — commands, searches, conversations
Announcements Notify WorkerBackground workerMonitors for new announcements using BullMQ scheduled jobs and sends filtered alerts to users
Broadcasts WorkerBackground workerHandles queued broadcast message delivery
Data Sync WorkerBackground workerPeriodically syncs KTU data to local DB via BullMQ scheduled jobs to power full-text search
Attachment Delivery WorkerBackground workerDownloads and sends files asynchronously to prevent bot blocking
Bull Board ServiceMonitoring serviceWeb dashboard for real-time queue monitoring and job management
PostgreSQLDatabaseStores all data with Drizzle ORM for type-safe queries
RedisQueuePowers BullMQ jobs

Tip

Want to understand how it all works? Check out How It Works for the complete architecture breakdown with diagrams.

Quick Start 🚀

Prerequisites

Trust me. Docker is the easiest way to run anything within seconds 🙃

1. Clone the Repo

git clone https://github.com/devadathanmb/ktu-bot.git
cd ktu-bot

2. Configure Environment

Development environment files live in the env/dev/ directory. Each service/module has its own .env file.

Minimum required:

  • env/dev/bot.env — Set BOT_TOKEN and BOT_FILE_UPLOAD_CHANNEL_ID
  • Most files come prefilled with sensible defaults

Optional (for extra features):

  • env/dev/api.env — For UptimeRobot monitoring, file uploads, etc.
  • env/dev/llm.env — For AI-powered announcement filtering

Note

Most environment variables needed for the development setup come pre-configured in each .env file.

However, some configurations depend on external services and are left as placeholder values. Fill those in with actual credentials if you plan to use those features.

Important

For sensitive local secrets:

cp env/dev/.env.example env/dev/.env
# Add your personal API keys, tokens, or credentials here

This file is mounted last in Docker Compose, so values here override anything in env/dev/*.env files.

Warning

If you don't configure certain .env variables, those features simply won't work or the zod validations may get triggered. Review each file to see what's needed.

3. Run Everything

docker compose -f docker/compose/compose.dev.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.dev.yaml up --build

This starts all services with hot-reload enabled. Code changes trigger automatic restarts.

4. Run Only the Bot

If you don't need the workers:

docker compose -f docker/compose/compose.dev.yaml up ktu-bot-app --build

Tip

Database migrations are generated and run automatically via the ktu-bot-db-migrations service.

Once everything is up, talk to your bot in Telegram!

5. Run Individual Workers

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 --build

Tip

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 Deployment 🏭

Production uses a single .env file in the env/prod/ directory.

1. Configure Environment

cp env/prod/.env.example env/prod/.env
# Edit env/prod/.env and fill in all required values# Most values come pre-configured — just update anything specific to your deployment.

2. Start Monitoring (Optional but Recommended)

# Start Prometheus monitoring independently
docker compose -f docker/monitoring/compose.yaml up -d

This starts Prometheus on port 9090 with persistent storage. It runs independently from the application stack.

3. Start Application Services

docker compose -f docker/compose/compose.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.yaml up -d --build

4. Verify Health

curl -f http://localhost:3000/health

Notes

  • All services communicate over an internal Docker network
  • Database migrations run automatically on startup
  • Make sure all required API keys/tokens are provided
  • If some keys are missing, update the code to handle their absence gracefully

Tech Stack 🛠️

  • Language:TypeScript — Because type-safe code is always better?
  • Bot Framework:GrammY — Modern, type-safe Telegram bot framework
  • Database:PostgreSQL — Powerful relational DB with god knows how many features
  • ORM:Drizzle — Type-safe SQL queries and migrations
  • Job Queue:BullMQ — Reliable background job processing
  • HTTP Client:got — Modern fetch wrapper with in-memory caching

Contributing 🤝

Contributions are welcome! Whether it's bug fixes, new features, documentation improvements, or ideas — all are appreciated.

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

Need help getting started? Check out How It Works to understand the architecture.

Tip

New to Telegram Bot ecosystem? Check out this awesome getting started guide from GrammY.


Documentation 📚

License 🛡️

AGPL-3.0 — See LICENSE for details.

This means you can use, modify, and distribute this code freely, but you must:

  • Keep it open source
  • Share your changes under the same license
  • Give credit where it's due
  • If you run this software on a server and let users interact with it remotely, you must provide them with the source code

About

A telegram bot to view KTU exam results and notifications easily.

Topics

Resources

Stars

60 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

KTU Bot ⚡

KTU Bot

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.

Better Stack Badge


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. ❤️


What Is This? 🤔

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.

What You Can Do

  • 🔍 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?"

Architecture Overview 🏗️

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.

ComponentTypeWhat It Does
BotGrammY Telegram botHandles all user interactions — commands, searches, conversations
Announcements Notify WorkerBackground workerMonitors for new announcements using BullMQ scheduled jobs and sends filtered alerts to users
Broadcasts WorkerBackground workerHandles queued broadcast message delivery
Data Sync WorkerBackground workerPeriodically syncs KTU data to local DB via BullMQ scheduled jobs to power full-text search
Attachment Delivery WorkerBackground workerDownloads and sends files asynchronously to prevent bot blocking
Bull Board ServiceMonitoring serviceWeb dashboard for real-time queue monitoring and job management
PostgreSQLDatabaseStores all data with Drizzle ORM for type-safe queries
RedisQueuePowers BullMQ jobs

Tip

Want to understand how it all works? Check out How It Works for the complete architecture breakdown with diagrams.

Quick Start 🚀

Prerequisites

Trust me. Docker is the easiest way to run anything within seconds 🙃

1. Clone the Repo

git clone https://github.com/devadathanmb/ktu-bot.git
cd ktu-bot

2. Configure Environment

Development environment files live in the env/dev/ directory. Each service/module has its own .env file.

Minimum required:

  • env/dev/bot.env — Set BOT_TOKEN and BOT_FILE_UPLOAD_CHANNEL_ID
  • Most files come prefilled with sensible defaults

Optional (for extra features):

  • env/dev/api.env — For UptimeRobot monitoring, file uploads, etc.
  • env/dev/llm.env — For AI-powered announcement filtering

Note

Most environment variables needed for the development setup come pre-configured in each .env file.

However, some configurations depend on external services and are left as placeholder values. Fill those in with actual credentials if you plan to use those features.

Important

For sensitive local secrets:

cp env/dev/.env.example env/dev/.env
# Add your personal API keys, tokens, or credentials here

This file is mounted last in Docker Compose, so values here override anything in env/dev/*.env files.

Warning

If you don't configure certain .env variables, those features simply won't work or the zod validations may get triggered. Review each file to see what's needed.

3. Run Everything

docker compose -f docker/compose/compose.dev.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.dev.yaml up --build

This starts all services with hot-reload enabled. Code changes trigger automatic restarts.

4. Run Only the Bot

If you don't need the workers:

docker compose -f docker/compose/compose.dev.yaml up ktu-bot-app --build

Tip

Database migrations are generated and run automatically via the ktu-bot-db-migrations service.

Once everything is up, talk to your bot in Telegram!

5. Run Individual Workers

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 --build

Tip

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 Deployment 🏭

Production uses a single .env file in the env/prod/ directory.

1. Configure Environment

cp env/prod/.env.example env/prod/.env
# Edit env/prod/.env and fill in all required values# Most values come pre-configured — just update anything specific to your deployment.

2. Start Monitoring (Optional but Recommended)

# Start Prometheus monitoring independently
docker compose -f docker/monitoring/compose.yaml up -d

This starts Prometheus on port 9090 with persistent storage. It runs independently from the application stack.

3. Start Application Services

docker compose -f docker/compose/compose.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.yaml up -d --build

4. Verify Health

curl -f http://localhost:3000/health

Notes

  • All services communicate over an internal Docker network
  • Database migrations run automatically on startup
  • Make sure all required API keys/tokens are provided
  • If some keys are missing, update the code to handle their absence gracefully

Tech Stack 🛠️

  • Language:TypeScript — Because type-safe code is always better?
  • Bot Framework:GrammY — Modern, type-safe Telegram bot framework
  • Database:PostgreSQL — Powerful relational DB with god knows how many features
  • ORM:Drizzle — Type-safe SQL queries and migrations
  • Job Queue:BullMQ — Reliable background job processing
  • HTTP Client:got — Modern fetch wrapper with in-memory caching

Contributing 🤝

Contributions are welcome! Whether it's bug fixes, new features, documentation improvements, or ideas — all are appreciated.

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

Need help getting started? Check out How It Works to understand the architecture.

Tip

New to Telegram Bot ecosystem? Check out this awesome getting started guide from GrammY.


Documentation 📚

License 🛡️

AGPL-3.0 — See LICENSE for details.

This means you can use, modify, and distribute this code freely, but you must:

  • Keep it open source
  • Share your changes under the same license
  • Give credit where it's due
  • If you run this software on a server and let users interact with it remotely, you must provide them with the source code

About

A telegram bot to view KTU exam results and notifications easily.

Topics

Resources

Stars

60 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

KTU Bot ⚡

KTU Bot

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.

Better Stack Badge


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. ❤️


What Is This? 🤔

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.

What You Can Do

  • 🔍 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?"

Architecture Overview 🏗️

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.

ComponentTypeWhat It Does
BotGrammY Telegram botHandles all user interactions — commands, searches, conversations
Announcements Notify WorkerBackground workerMonitors for new announcements using BullMQ scheduled jobs and sends filtered alerts to users
Broadcasts WorkerBackground workerHandles queued broadcast message delivery
Data Sync WorkerBackground workerPeriodically syncs KTU data to local DB via BullMQ scheduled jobs to power full-text search
Attachment Delivery WorkerBackground workerDownloads and sends files asynchronously to prevent bot blocking
Bull Board ServiceMonitoring serviceWeb dashboard for real-time queue monitoring and job management
PostgreSQLDatabaseStores all data with Drizzle ORM for type-safe queries
RedisQueuePowers BullMQ jobs

Tip

Want to understand how it all works? Check out How It Works for the complete architecture breakdown with diagrams.

Quick Start 🚀

Prerequisites

Trust me. Docker is the easiest way to run anything within seconds 🙃

1. Clone the Repo

git clone https://github.com/devadathanmb/ktu-bot.git
cd ktu-bot

2. Configure Environment

Development environment files live in the env/dev/ directory. Each service/module has its own .env file.

Minimum required:

  • env/dev/bot.env — Set BOT_TOKEN and BOT_FILE_UPLOAD_CHANNEL_ID
  • Most files come prefilled with sensible defaults

Optional (for extra features):

  • env/dev/api.env — For UptimeRobot monitoring, file uploads, etc.
  • env/dev/llm.env — For AI-powered announcement filtering

Note

Most environment variables needed for the development setup come pre-configured in each .env file.

However, some configurations depend on external services and are left as placeholder values. Fill those in with actual credentials if you plan to use those features.

Important

For sensitive local secrets:

cp env/dev/.env.example env/dev/.env
# Add your personal API keys, tokens, or credentials here

This file is mounted last in Docker Compose, so values here override anything in env/dev/*.env files.

Warning

If you don't configure certain .env variables, those features simply won't work or the zod validations may get triggered. Review each file to see what's needed.

3. Run Everything

docker compose -f docker/compose/compose.dev.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.dev.yaml up --build

This starts all services with hot-reload enabled. Code changes trigger automatic restarts.

4. Run Only the Bot

If you don't need the workers:

docker compose -f docker/compose/compose.dev.yaml up ktu-bot-app --build

Tip

Database migrations are generated and run automatically via the ktu-bot-db-migrations service.

Once everything is up, talk to your bot in Telegram!

5. Run Individual Workers

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 --build

Tip

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 Deployment 🏭

Production uses a single .env file in the env/prod/ directory.

1. Configure Environment

cp env/prod/.env.example env/prod/.env
# Edit env/prod/.env and fill in all required values# Most values come pre-configured — just update anything specific to your deployment.

2. Start Monitoring (Optional but Recommended)

# Start Prometheus monitoring independently
docker compose -f docker/monitoring/compose.yaml up -d

This starts Prometheus on port 9090 with persistent storage. It runs independently from the application stack.

3. Start Application Services

docker compose -f docker/compose/compose.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.yaml up -d --build

4. Verify Health

curl -f http://localhost:3000/health

Notes

  • All services communicate over an internal Docker network
  • Database migrations run automatically on startup
  • Make sure all required API keys/tokens are provided
  • If some keys are missing, update the code to handle their absence gracefully

Tech Stack 🛠️

  • Language:TypeScript — Because type-safe code is always better?
  • Bot Framework:GrammY — Modern, type-safe Telegram bot framework
  • Database:PostgreSQL — Powerful relational DB with god knows how many features
  • ORM:Drizzle — Type-safe SQL queries and migrations
  • Job Queue:BullMQ — Reliable background job processing
  • HTTP Client:got — Modern fetch wrapper with in-memory caching

Contributing 🤝

Contributions are welcome! Whether it's bug fixes, new features, documentation improvements, or ideas — all are appreciated.

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

Need help getting started? Check out How It Works to understand the architecture.

Tip

New to Telegram Bot ecosystem? Check out this awesome getting started guide from GrammY.


Documentation 📚

License 🛡️

AGPL-3.0 — See LICENSE for details.

This means you can use, modify, and distribute this code freely, but you must:

  • Keep it open source
  • Share your changes under the same license
  • Give credit where it's due
  • If you run this software on a server and let users interact with it remotely, you must provide them with the source code

About

A telegram bot to view KTU exam results and notifications easily.

Topics

Resources

Stars

60 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

KTU Bot ⚡

KTU Bot

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.

Better Stack Badge


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. ❤️


What Is This? 🤔

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.

What You Can Do

  • 🔍 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?"

Architecture Overview 🏗️

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.

ComponentTypeWhat It Does
BotGrammY Telegram botHandles all user interactions — commands, searches, conversations
Announcements Notify WorkerBackground workerMonitors for new announcements using BullMQ scheduled jobs and sends filtered alerts to users
Broadcasts WorkerBackground workerHandles queued broadcast message delivery
Data Sync WorkerBackground workerPeriodically syncs KTU data to local DB via BullMQ scheduled jobs to power full-text search
Attachment Delivery WorkerBackground workerDownloads and sends files asynchronously to prevent bot blocking
Bull Board ServiceMonitoring serviceWeb dashboard for real-time queue monitoring and job management
PostgreSQLDatabaseStores all data with Drizzle ORM for type-safe queries
RedisQueuePowers BullMQ jobs

Tip

Want to understand how it all works? Check out How It Works for the complete architecture breakdown with diagrams.

Quick Start 🚀

Prerequisites

Trust me. Docker is the easiest way to run anything within seconds 🙃

1. Clone the Repo

git clone https://github.com/devadathanmb/ktu-bot.git
cd ktu-bot

2. Configure Environment

Development environment files live in the env/dev/ directory. Each service/module has its own .env file.

Minimum required:

  • env/dev/bot.env — Set BOT_TOKEN and BOT_FILE_UPLOAD_CHANNEL_ID
  • Most files come prefilled with sensible defaults

Optional (for extra features):

  • env/dev/api.env — For UptimeRobot monitoring, file uploads, etc.
  • env/dev/llm.env — For AI-powered announcement filtering

Note

Most environment variables needed for the development setup come pre-configured in each .env file.

However, some configurations depend on external services and are left as placeholder values. Fill those in with actual credentials if you plan to use those features.

Important

For sensitive local secrets:

cp env/dev/.env.example env/dev/.env
# Add your personal API keys, tokens, or credentials here

This file is mounted last in Docker Compose, so values here override anything in env/dev/*.env files.

Warning

If you don't configure certain .env variables, those features simply won't work or the zod validations may get triggered. Review each file to see what's needed.

3. Run Everything

docker compose -f docker/compose/compose.dev.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.dev.yaml up --build

This starts all services with hot-reload enabled. Code changes trigger automatic restarts.

4. Run Only the Bot

If you don't need the workers:

docker compose -f docker/compose/compose.dev.yaml up ktu-bot-app --build

Tip

Database migrations are generated and run automatically via the ktu-bot-db-migrations service.

Once everything is up, talk to your bot in Telegram!

5. Run Individual Workers

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 --build

Tip

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 Deployment 🏭

Production uses a single .env file in the env/prod/ directory.

1. Configure Environment

cp env/prod/.env.example env/prod/.env
# Edit env/prod/.env and fill in all required values# Most values come pre-configured — just update anything specific to your deployment.

2. Start Monitoring (Optional but Recommended)

# Start Prometheus monitoring independently
docker compose -f docker/monitoring/compose.yaml up -d

This starts Prometheus on port 9090 with persistent storage. It runs independently from the application stack.

3. Start Application Services

docker compose -f docker/compose/compose.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.yaml up -d --build

4. Verify Health

curl -f http://localhost:3000/health

Notes

  • All services communicate over an internal Docker network
  • Database migrations run automatically on startup
  • Make sure all required API keys/tokens are provided
  • If some keys are missing, update the code to handle their absence gracefully

Tech Stack 🛠️

  • Language:TypeScript — Because type-safe code is always better?
  • Bot Framework:GrammY — Modern, type-safe Telegram bot framework
  • Database:PostgreSQL — Powerful relational DB with god knows how many features
  • ORM:Drizzle — Type-safe SQL queries and migrations
  • Job Queue:BullMQ — Reliable background job processing
  • HTTP Client:got — Modern fetch wrapper with in-memory caching

Contributing 🤝

Contributions are welcome! Whether it's bug fixes, new features, documentation improvements, or ideas — all are appreciated.

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

Need help getting started? Check out How It Works to understand the architecture.

Tip

New to Telegram Bot ecosystem? Check out this awesome getting started guide from GrammY.


Documentation 📚

License 🛡️

AGPL-3.0 — See LICENSE for details.

This means you can use, modify, and distribute this code freely, but you must:

  • Keep it open source
  • Share your changes under the same license
  • Give credit where it's due
  • If you run this software on a server and let users interact with it remotely, you must provide them with the source code

About

A telegram bot to view KTU exam results and notifications easily.

Topics

Resources

Stars

60 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

KTU Bot ⚡

KTU Bot

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.

Better Stack Badge


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. ❤️


What Is This? 🤔

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.

What You Can Do

  • 🔍 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?"

Architecture Overview 🏗️

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.

ComponentTypeWhat It Does
BotGrammY Telegram botHandles all user interactions — commands, searches, conversations
Announcements Notify WorkerBackground workerMonitors for new announcements using BullMQ scheduled jobs and sends filtered alerts to users
Broadcasts WorkerBackground workerHandles queued broadcast message delivery
Data Sync WorkerBackground workerPeriodically syncs KTU data to local DB via BullMQ scheduled jobs to power full-text search
Attachment Delivery WorkerBackground workerDownloads and sends files asynchronously to prevent bot blocking
Bull Board ServiceMonitoring serviceWeb dashboard for real-time queue monitoring and job management
PostgreSQLDatabaseStores all data with Drizzle ORM for type-safe queries
RedisQueuePowers BullMQ jobs

Tip

Want to understand how it all works? Check out How It Works for the complete architecture breakdown with diagrams.

Quick Start 🚀

Prerequisites

Trust me. Docker is the easiest way to run anything within seconds 🙃

1. Clone the Repo

git clone https://github.com/devadathanmb/ktu-bot.git
cd ktu-bot

2. Configure Environment

Development environment files live in the env/dev/ directory. Each service/module has its own .env file.

Minimum required:

  • env/dev/bot.env — Set BOT_TOKEN and BOT_FILE_UPLOAD_CHANNEL_ID
  • Most files come prefilled with sensible defaults

Optional (for extra features):

  • env/dev/api.env — For UptimeRobot monitoring, file uploads, etc.
  • env/dev/llm.env — For AI-powered announcement filtering

Note

Most environment variables needed for the development setup come pre-configured in each .env file.

However, some configurations depend on external services and are left as placeholder values. Fill those in with actual credentials if you plan to use those features.

Important

For sensitive local secrets:

cp env/dev/.env.example env/dev/.env
# Add your personal API keys, tokens, or credentials here

This file is mounted last in Docker Compose, so values here override anything in env/dev/*.env files.

Warning

If you don't configure certain .env variables, those features simply won't work or the zod validations may get triggered. Review each file to see what's needed.

3. Run Everything

docker compose -f docker/compose/compose.dev.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.dev.yaml up --build

This starts all services with hot-reload enabled. Code changes trigger automatic restarts.

4. Run Only the Bot

If you don't need the workers:

docker compose -f docker/compose/compose.dev.yaml up ktu-bot-app --build

Tip

Database migrations are generated and run automatically via the ktu-bot-db-migrations service.

Once everything is up, talk to your bot in Telegram!

5. Run Individual Workers

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 --build

Tip

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 Deployment 🏭

Production uses a single .env file in the env/prod/ directory.

1. Configure Environment

cp env/prod/.env.example env/prod/.env
# Edit env/prod/.env and fill in all required values# Most values come pre-configured — just update anything specific to your deployment.

2. Start Monitoring (Optional but Recommended)

# Start Prometheus monitoring independently
docker compose -f docker/monitoring/compose.yaml up -d

This starts Prometheus on port 9090 with persistent storage. It runs independently from the application stack.

3. Start Application Services

docker compose -f docker/compose/compose.yaml down -v --remove-orphans && \
docker compose -f docker/compose/compose.yaml up -d --build

4. Verify Health

curl -f http://localhost:3000/health

Notes

  • All services communicate over an internal Docker network
  • Database migrations run automatically on startup
  • Make sure all required API keys/tokens are provided
  • If some keys are missing, update the code to handle their absence gracefully

Tech Stack 🛠️

  • Language:TypeScript — Because type-safe code is always better?
  • Bot Framework:GrammY — Modern, type-safe Telegram bot framework
  • Database:PostgreSQL — Powerful relational DB with god knows how many features
  • ORM:Drizzle — Type-safe SQL queries and migrations
  • Job Queue:BullMQ — Reliable background job processing
  • HTTP Client:got — Modern fetch wrapper with in-memory caching

Contributing 🤝

Contributions are welcome! Whether it's bug fixes, new features, documentation improvements, or ideas — all are appreciated.

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

Need help getting started? Check out How It Works to understand the architecture.

Tip

New to Telegram Bot ecosystem? Check out this awesome getting started guide from GrammY.


Documentation 📚

License 🛡️

AGPL-3.0 — See LICENSE for details.

This means you can use, modify, and distribute this code freely, but you must:

  • Keep it open source
  • Share your changes under the same license
  • Give credit where it's due
  • If you run this software on a server and let users interact with it remotely, you must provide them with the source code

About

A telegram bot to view KTU exam results and notifications easily.

Topics

Resources

Stars

60 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages