Skip to content

Repository files navigation

prayer-bot 🙏

A serverless Telegram bot that provides Muslim prayer times and sends notifications when prayers are approaching.

The worldwide global bot is the flagship, actively developed version and the focus of future work. It serves prayer times anywhere from a shared location and adds a Telegram Mini App, Qibla direction, Gregorian--Hijri calendar, Islamic occasions, a rolling calendar feed, and configurable reminders. It has its own engineering and architecture guide covering the runtime, Mini App, reminder pipeline, database schemas, and GCP deployment. Its prayer calculations, high-latitude rules, Qibla bearing, Gregorian--Hijri conversion, Islamic occasions, and rolling calendar are documented on the public calculation methodology site, with versioned LaTeX source maintained in this repository.

The original per-city bots documented in the architecture below predate the global bot and remain available in maintenance mode.

wakatime Report card

Currently Available Cities

City Bot
Kazan @kazan_prayer_bot
Innopolis @innopolis_prayer_bot

Architecture 🏗️

The project is a set of stateless GCP Cloud Functions written in Go, backed by Supabase Postgres for state and GCS for prayer-schedule CSV files. A single codebase serves many bots (one per city); everything is keyed by bot_id and the per-bot config (token, owner, timezone) is loaded from the base64-encoded APP_CONFIG environment variable.

System overview

flowchart LR
  subgraph clients [External triggers]
    TG[Telegram users]
    SCH[Cloud Scheduler]
    UP[Prayer CSV upload]
  end

  subgraph functions [GCP Cloud Functions]
    D["dispatcher — HTTP webhook"]
    R["reminder — HTTP / cron"]
    L["loader — GCS CloudEvent"]
  end

  PG[(Supabase Postgres)]
  GCS[(GCS data bucket)]

  TG -->|webhook update| D
  D -->|replies| TG
  SCH -->|periodic POST| R
  R -->|reminders| TG
  UP --> GCS
  GCS -->|object finalized| L

  D --> PG
  R --> PG
  L --> PG
Loading

Each function is an independent Go module under serverless/ that depends on a shared root module for cross-cutting code.

Component Trigger Responsibility Code
dispatcher Telegram webhook (HTTP POST) Authenticates the request by secret header, resolves/creates the chat, and routes commands & inline callbacks serverless/dispatcher
reminder Cloud Scheduler (HTTP POST) For every bot, evaluates each subscriber against the reminder rules and sends due notifications serverless/reminder
loader GCS object-finalized (CloudEvent) Parses an uploaded <bot_id>.csv schedule and upserts it into Postgres serverless/loader
domain Shared models & value types (Chat, PrayerDay, Reminder, Duration, errors) domain
config Decodes APP_CONFIG into a per-bot config map config
internal/db pgx-based Postgres repository shared by all functions internal/db
log Thin structured-logging wrapper over log/slog log

Infrastructure (functions, scheduler, buckets, IAM) is defined as Terraform in infra/gcp/. Each directory has its own README.md with details.

Repository layout

.
├── domain/          # shared models & value types (root module)
├── config/          # APP_CONFIG loader
├── log/             # slog wrapper
├── internal/db/     # Postgres repository (pgx)
├── serverless/
│   ├── dispatcher/  # Telegram webhook handler   (own go.mod)
│   ├── reminder/    # scheduled reminder sender   (own go.mod)
│   └── loader/      # CSV schedule loader         (own go.mod)
├── migrations/      # Goose SQL migrations
├── infra/gcp/       # Terraform (Cloud Functions, Scheduler, GCS)
└── _scripts/        # local helper scripts

Reminder flow

The reminder function is the heart of the system. On each tick it fans out over bots and, within a bot, over subscribers (bounded to maxConcurrentReminderSends), evaluating three independent reminder types. State is stored per chat in the reminder JSONB column so a reminder is sent at most once, and stale reminders are skipped after downtime.

flowchart TD
  A[Scheduler POST] --> B[for each bot]
  B --> C[GetSubscribers]
  C --> D[GetChatsByIDs + GetPrayerDay today/next]
  D --> E["fan out per chat (≤ maxConcurrentReminderSends)"]
  E --> F{evaluate reminder types}
  F -->|Tomorrow| G[send next-day schedule]
  F -->|Soon| H["send upcoming prayer (or jamaat poll in groups)"]
  F -->|Arrive| I[send prayer-arrived notice]
  G --> J[UpdateReminder: message id + last_at]
  H --> J
  I --> J
Loading

Data model

Two tables, both multi-tenant via a composite primary key that starts with bot_id. The flexible reminder configuration is stored as JSONB on chats.

erDiagram
  chats {
    bigint      bot_id        PK
    bigint      chat_id       PK
    text        language_code
    text        state
    jsonb       reminder
    boolean     subscribed
    timestamptz subscribed_at
    timestamptz created_at
  }
  prayers {
    bigint      bot_id       PK
    date        prayer_date  PK
    timestamptz fajr
    timestamptz shuruq
    timestamptz dhuhr
    timestamptz asr
    timestamptz maghrib
    timestamptz isha
  }
Loading

chats and prayers are linked logically by bot_id (and by date at read time); there is no foreign key, since the two tables are populated by different functions.

Deployment

GitHub secrets (per environment):

Secret Purpose
APP_CONFIG Bot config JSON
GCP_PROJECT_ID GCP project ID
GCP_SA_KEY GCP deploy service account JSON
GCP_TFSTATE_BUCKET GCS bucket for Terraform state
SUPABASE_DB_URL Supabase transaction pooler URL (port 6543) — runtime DATABASE_URL on functions
SUPABASE_DB_DIRECT_URL Supabase direct Postgres URL (port 5432) — Goose schema migrations

Automatic deploys

Trigger Environment What runs
Pull request → main dev lint → validate → plan → Goose migrate → Terraform apply → webhooks → profiles
Push / merge to main prod same full chain

Manual deploy (hotfixes): Actions → Deploy to GCP → Run workflow → pick dev or prod and optionally a branch.


Configuration 🛠️

Bot configuration is managed through environment variables.

Below is an example of an APP_CONFIG value containing all bot information:

{
  "648252": {
    "bot_id": 648252,          // Bot ID
    "owner_id": 1385434843,    // Bot owner ID
    "location": "Europe/Moscow", // Timezone of the city
    "token": "oa7GmLW3fncbOE0MTfV0mKxH/F37cShhxgZ1mjl614w", // Telegram token
    "secret": "Noe&uPcwjaAxjqJU_JP4C^g2V7ZDQX" // Secret key to verify requests
  },
  ...
}
  • To find your owner ID, use ID bot
  • Bot ID is the first number before : in the bot token
    • TOKEN: 123456789:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
    • Bot ID: 123456789

Bot Features 🤖

User Commands 📝

Command Description
today Get today's prayer times
date Get prayer times for a specific date
next Find out the next prayer time
subscribe Subscribe to daily reminders
unsubscribe Unsubscribe from daily reminders
remind Set reminder offset for the next prayer
language Change the bot language
help Show help message
bug Report a problem to bot owner
feedback Send feedback to bot owner

Admin Commands 📝

Command Description
admin Show admin help message
stats View bot usage statistics
announce Send message to all users
reply Reply to user's bug/feedback message

References 📚


How to Contribute 🤝

[1] Add a City

You do:

  1. Get prayer times for a city in CSV format
  2. Make a pull request (or open an issue) with the new file

I do:

  1. Create a new Telegram bot
  2. Upload the city file to the GCS data bucket

[2] Add a Language

You do:

  1. Create translation text for the following files:

I do:

  1. Deploy a new version of the code

[3] Code Contributions

Found a bug? Want to add a new feature? Just open an issue or submit a pull request.


Development Roadmap 🚀

V1 ✅

  • Support date format for /prayersdate command with leading zeros and delimiters (. / -)
  • Implement subscriptions & notifications
  • Update text messages to be more user-friendly

V2 ✅

  • Store prayer times in memory to reduce database requests
  • Add response endpoint for admin to address feedback & bug messages
  • Add Jumu'ah prayer reminders on Fridays

V3 ✅

  • Add time keyboard to /date command
  • Remove selection message for /date & /lang after user interaction or timeout
  • Terminate other active channels when user sends new commands
  • Add feature to delete old prayer time message when a new one is sent
  • Enable admins to broadcast messages to all subscribers
  • Add feature to get subscriber count for admins
  • Write more robust tests for core features

V4 ✅

  • Add multi-language support (AR, RU, TT, TR, UZ)
  • Implement script messages in the bot
  • Set user script before command if not set
  • Use script commands in notifications
  • Fix prayer timetables for other languages

V5 ✅

  • Refactor code for better readability and maintainability
  • Enhance logging to be more informative
  • Enable using multiple bots with the same codebase

V6 ✅

  • Migrate to serverless architecture
  • Automate deployment using Terraform
  • Add support for multiple cities
  • Add Spanish & French language support
  • Add /stats command for bot usage statistics

V7 ✅

  • Add jamaat gathering feature for group chats

V8 🔄

  • Add support for all major world cities

Visualization 🖥️

cd infra/gcp
terraform plan -out plan.out
terraform show -json plan.out > plan.json
docker run --rm -it -p 9000:9000 -v "$(pwd)/plan.json:/src/plan.json" im2nguyen/rover:latest -planJSONPath=plan.json

About

Worldwide serverless Telegram prayer-times bot — location-based times, Mini App, Qibla, Hijri calendar, Islamic occasions, and smart reminders. Also hosts the original per-city bots.

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages