Skip to content

Repository files navigation

tgbotmau

Shields.io-compatible MAU badges for Telegram bots, with daily history tracking and a small web UI. Live at tgbotmau.quoi.dev.

For example, the live badge of @BotFather:

@BotFather MAU

What it does

  • Renders MAU <count> badges in every shields.io style (flat, flat-square, plastic, for-the-badge, social) with the Telegram logo and brand color. Output is byte-identical to shields.io (verified by tests against real shields.io responses).
  • Discovers MAU by scraping the public t.me/<username> page: the Start Bot button proves the username is a bot, the N monthly users line provides the count.
  • Falls back to MTProto (bot_active_users of the User object) for bots whose MAU is hidden on the web, using a pool of bot tokens:
    • first contact per account uses contacts.resolveUsername (heavily rate-limited by Telegram, ~150/day/account — budgeted and persisted);
    • the result (user_id + per-account access_hash) is stored, so all later reads use the cheap users.getFullUser;
    • FLOOD_WAIT penalties are tracked per account and per method (independent limits) and survive restarts;
    • user-facing requests queue through a hard per-account limiter (1 req/5 s by default), background refreshes are additionally slowed to 1 req/60 s per account.
  • Remembers every requested username (including "not a bot" / "not found") for CACHE_TTL (12 h), and re-scrapes all known bots on that cadence, recording one MAU history point per bot per UTC day. Bots that hide their MAU are retried slowly (UPDATER_NO_MAU_BATCH oldest per window). All updater state lives in PostgreSQL, so restarts resume seamlessly.
  • Exports Prometheus metrics: badge render time, t.me scrape time/errors, MTProto request time/errors (resolve vs getFullUser separately), and per-account connection state (1=UP / 0=DOWN).

HTTP API

Endpoint Description
GET /api/bot/{username}/mau/badge?style=flat SVG badge (Cache-Control: max-age=3600, configurable). Unknown MAU renders ???.
GET /api/bot/{username}/mau JSON: {"username","status","mau":null|N,"mau_source","checked_at"}
GET /api/bot/{username}/mau/history?range=1m|3m|6m|1y|all JSON daily history points
GET /api/openapi.yaml OpenAPI 3 spec, rendered with the deployment's real cache settings
GET /api-docs Swagger UI for the spec (self-hosted, no CDN)
GET /metrics Prometheus metrics
GET /healthz Liveness probe
GET /readyz Readiness probe (checks the database)
GET / Web UI (React, embedded into the binary)

Embedding a badge for your bot (@BotFather here as an example):

[![@BotFather MAU](https://tgbotmau.quoi.dev/api/bot/BotFather/mau/badge?style=flat "@BotFather MAU")](https://tgbotmau.quoi.dev/?bot=BotFather)
<a href="https://tgbotmau.quoi.dev/?bot=BotFather" target="_blank"><img alt="@BotFather MAU" title="@BotFather MAU" src="https://tgbotmau.quoi.dev/api/bot/BotFather/mau/badge?style=flat"></a>

Fair use: please don't use the API for mass username enumeration or bulk discovery. Every previously unseen bot may cost a strictly limited Telegram MTProto request (ResolveUsername has a small daily quota), so bulk scanning drains a shared budget and degrades the service for everyone. The API is meant for looking up the MAU of bots you already know — typically your own.

Architecture

cmd/tgbotmau        wiring: config -> store -> scrapers -> service -> http
internal/badge      shields.io-compatible SVG renderer (byte-identical port
                    of badge-maker, anafanafo font metrics included)
internal/tme        t.me page scraper (rate-limited, fixture-tested)
internal/mtproto    bot-account pool over gotd/td: quota ledger, per-method
                    flood waits, persisted peers, auto-reconnect
internal/service    domain logic: cache -> t.me -> MTProto, singleflight
                    deduplication, history recording, auto-updater
internal/store      PostgreSQL 18 (uuidv7 PKs), embedded goose migrations
internal/httpapi    handlers, metrics middleware, embedded SPA serving
internal/metrics    every exported Prometheus metric in one place
web                 Astro SSG frontend (React islands, DaisyUI, Recharts,
                    Swagger UI): the page shell is pre-rendered at build
                    time, hydrated on load, embedded via go:embed

Concurrency notes: any number of concurrent requests for one username (users + auto-updater alike) collapse into a single upstream scrape via singleflight; the scrape runs on a detached context so a cancelled requester never fails the other waiters, while every waiter is released as soon as its own context ends. A failed MTProto lookup never overwrites a previously known MAU and never fabricates history points. All rate limits and quotas are enforced per account with persisted state, so restarts never overshoot Telegram's limits.

Run exactly one instance per database: the MTProto resolve quota and flood waits are mirrored in process memory, so multiple replicas would overspend Telegram's limits.

Running

docker compose up --build   # app on :8080, PostgreSQL 18 included

Or run the published image against your own PostgreSQL 18+:

docker run -d --name tgbotmau -p 8080:8080 \
  -e DATABASE_URL=postgres://user:password@your-postgres:5432/tgbotmau \
  ghcr.io/kivapple/tgbotmau:latest

Or manually: build the frontend, then the binary (the frontend is embedded):

cd web && pnpm install --frozen-lockfile && pnpm run build && cd ..
go build -o tgbotmau ./cmd/tgbotmau
DATABASE_URL=postgres://localhost/tgbotmau ./tgbotmau

Configuration is environment-only; see .env.example for every knob. Without BOT_TOKENS the service runs in t.me-only mode.

Development

go test ./...                                  # unit tests
TEST_DATABASE_URL=postgres://localhost/tgbotmau_test go test ./internal/store  # DB tests
cd web && pnpm dev                             # astro dev server (proxies /api to :8080)

Credits & license

This codebase was written with extensive use of AI coding tools, working under the close architectural direction, review and quality control of an experienced software engineer.

Licensed under the MIT License.

About

Telegram bot MAU history tracker and sheilds.io-like badge generator

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages