Features Β· Installation Β· Quickstart Β· Metrics Β· Dashboard Β· Privacy
#telegram #telegram-bot #analytics #python #sqlite #flask #chatbot #privacy-first #zero-dependencies #DAU #funnel-analysis #data-retention #open-source #MIT
Track users, messages, events, funnels, and ratings in your Telegram bot. Get in-bot Markdown reports and an optional Flask web dashboard with live charts.
The core library has zero third-party runtime dependencies β pure Python stdlib. The dashboard is an optional extra requiring Flask.
Live demo & docs: GitHub Pages site
| Feature | Description | |
|---|---|---|
| β‘ | Non-blocking writes | All tracking calls enqueue instantly. A background writer thread batches and commits β never block your bot's event loop. |
| π | Privacy by default | Usernames off, raw JSON off, text truncated, optional HMAC-SHA256 user ID hashing with per-DB salt. |
| π | 22 metric functions | DAU/WAU/MAU/YAU, new users, accumulation, hourly heatmap, message analysis, funnels, ratings, reviews. |
| π‘οΈ | SQL injection proof | Every query uses bound parameters. Read-only mode=ro connections for all reads. Custom sql_query allows only SELECT/WITH. |
| π¨ | Flask dashboard | Optional web UI with card-grid layout, client-side Chart.js graphs, hashed-password auth, read-only JSON API. |
| ποΈ | GDPR-ready retention | Automatic data purging with configurable retention_days. Cross-surface consistency guaranteed. |
| π€ | Multi-bot isolation | Multiple Analytics instances with different bot_id write to one DB. Each metric is bot-scoped. |
| π | Markdown reports | Five report categories delivered directly in chat. Admin-gated allowlist access. |
| π¦ | Zero dependencies | Core library uses only Python stdlib. pip install . and you're done. |
# Core library only (zero third-party deps)
pip install .
# With the web dashboard
pip install '.[dashboard]'
# For development
pip install -e '.[dashboard,dev]'from analytics_for_telegram_bot import Analytics, Config
config = Config(
db_path="analytics.db",
bot_id="my_bot",
admin_ids=[123456789], # Telegram user IDs allowed to see reports
)
ana = Analytics(config)
# /start handler β track a new user
ana.track_update(update)
# Track custom events
ana.event("purchase", event_type="shop", user_id=user_id)
ana.rating(5, user_id)
ana.review("Great bot!", user_id)
ana.funnel_step("checkout", "payment", user_id)
# Get a Markdown report in chat
from analytics_for_telegram_bot.reports import report, is_admin
if is_admin(user_id, config.admin_ids):
text = report("users", ana, period_start="2024-01-01")
bot.send_message(user_id, text)
ana.close() # flushes all pending writesfrom analytics_for_telegram_bot import Analytics, Config
config = Config(db_path="analytics.db", bot_id="my_bot")
ana = Analytics(config)
# In any async handler β track_update is non-blocking and thread-safe
async def on_message(update):
ana.track_update(update.dict()) # pass a dict, not the framework object
# The writer thread handles persistence asynchronously.
# Call ana.close() on shutdown to flush.All metrics open a read-only connection (mode=ro), work after close(),
concurrently with the writer thread, and return safe empty values on DB errors.
| Method | Returns |
|---|---|
analyze_total() |
Total users, messages, updates, averages |
analyze_new_user() |
New users per day |
analyze_user_accumulation() |
Cumulative growth (running total) |
analyze_dau() |
Daily Active Users per day |
analyze_wau() |
Weekly Active Users |
analyze_mau() |
Monthly Active Users |
analyze_yau() |
Yearly Active Users |
analyze_hour_activity() |
24Γ7 matrix (hour Γ day of week) |
analyze_messages_number(message_type=) |
Messages per day (filter by type) |
analyze_messages(limit=) |
Top messages (text + count) |
analyze_messages_type() |
Distribution by message type |
analyze_update_type() |
Distribution by update type |
analyze_chat_type() |
Distribution by chat type |
analyze_language() |
Distribution by user language |
analyze_bots_users() |
User counts across all bots in DB |
| Method | Returns |
|---|---|
analyze_events(limit=) |
Event distribution (name β count) |
analyze_events_number(event_type=) |
Events per day (filter by type) |
analyze_events_type() |
Distribution by event type |
analyze_events_funnel(steps=) |
Event funnel (counts per step) |
analyze_assessment() |
Rating distribution (1β5) |
analyze_review(limit=) |
Text reviews (latest-first) |
analyze_funnel(steps=, source=, funnel=) |
Unified funnel: events or updates |
rows = ana.sql_query(
"SELECT event, COUNT(*) AS c FROM events WHERE bot_id = :bid GROUP BY event ORDER BY c DESC",
params={"bid": "my_bot"},
limit=100,
)
# -> [{"event": "click", "c": 42}, ...]Only SELECT / WITH statements are allowed. Parameters are always bound,
never interpolated. DML/DDL is rejected with ValueError.
ana.event("click", event_type="ui", user_id=uid, value=1.0, props={"color": "red"})
ana.rating(4, user_id)
ana.review("Could be faster", user_id)
ana.funnel_step("signup", "email_verified", user_id)# After: pip install '.[dashboard]'
python -m analytics_for_telegram_bot dashboard --db analytics.db
# Optional: --host 0.0.0.0 --port 8787 --bot-id my_botThe dashboard provides:
- Card-grid UI with live charts powered by vendored Chart.js (no CDN)
- Read-only JSON API:
GET /api/fetch?m=<metric>&period_start=...&period_end=... - HTTP Basic Auth with hashed password (constant-time verify, 401 without redirect)
- Auto-polling every 5 seconds to keep values current
- Multi-bot support: auto-detects bot or pins via
--bot-id
Set a password hash via Config(dashboard_password_hash=...):
from analytics_for_telegram_bot.dashboard.auth import hash_password
hash_str = hash_password("your_password")Without a password, the dashboard is open only when bound to localhost.
from analytics_for_telegram_bot.reports import report, is_admin
if is_admin(user_id, config.admin_ids):
text = report("users", ana, period_start="2024-01-01", period_end="2024-01-31")
bot.send_message(chat_id, text)Five report categories: total, users, messages, events, ratings.
Each returns a Markdown string with tables, lists, and summaries.
config = Config(
db_path="analytics.db",
bot_id="my_bot",
retention_days=90, # delete data older than 90 days
auto_purge=True, # purge at init and close automatically
)
ana = Analytics(config)
# Or purge manually
deleted = ana.purge() # returns number of rows deleted- Purges all 5 data tables:
updates,events,ratings,funnel_steps,users userstable purged bylast_seen(active old users are preserved)retention_days=Nonedisables purge entirely (no-op)- Cross-surface consistency: metrics, reports, and dashboard all see the same trimmed data
| Setting | Default | Description |
|---|---|---|
collect_username |
False |
Telegram usernames are not collected |
collect_raw |
False |
Full update JSON is not stored |
hash_user_id |
False |
When True: HMAC-SHA256 with per-DB random salt |
collect_text |
True |
Message text stored, truncated to text_max_len |
text_max_len |
200 |
Max characters of message text retained |
collect_language |
True |
language_code from Telegram user object |
Additional security guarantees:
- All metric queries are parameterized SQL, scoped by
bot_id - Read-only connections (
mode=ro) for all read operations sql_queryonly allowsSELECT/WITH, runs on a read-only connection- DB errors translated to safe empty values (no SQL/stack leaks)
- Writer-thread logs are sanitized (no raw exception text, no bound values)
Config(
db_path="analytics.db", # path to SQLite
bot_id="my_bot", # bot identifier
# Privacy
collect_text=True,
text_max_len=200,
collect_command_text_always=True,
collect_username=False,
collect_language=True,
collect_raw=False,
hash_user_id=False,
# Retention
retention_days=None, # int or None
auto_purge=False,
# Access
admin_ids=[123456],
dashboard_password_hash=None, # bcrypt-style hash
dashboard_host="127.0.0.1",
dashboard_port=8787,
# Writer tuning
batch_size=100,
flush_interval=0.1,
queue_maxsize=0, # 0 = unbounded
# Display
tz_display="UTC",
) ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your Telegram Bot β
β (telebot / aiogram / python-telegram-bot) β
ββββββββββββ¬ββββββββββββββββββββ¬βββββββββββββββββββββ¬βββββββββββ
β β β
track_update() event() rating()
funnel_step() review()
β β β
βΌ βΌ βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β In-Memory Queue β
β (non-blocking, thread-safe enqueue) β
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β batch + flush_interval
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Background Writer Thread β
β (single daemon thread, sole write connection) β
β INSERT OR IGNORE dedup Β· error rollback Β· no data loss β
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SQLite Database β
β βββββββββββ ββββββββββ βββββββββββ ββββββββββββββ β
β β updates β β events β β ratings β β funnel_stepβ + users β
β βββββββββββ ββββββββββ βββββββββββ ββββββββββββββ β
ββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββ¬βββββββββββ
β mode=ro (read-only) β mode=ro
βΌ βΌ
ββββββββββββββββ βββββββββββββββββββββ βββββββββββββββββββ
β Metrics API β β Markdown Reports β β Flask Dashboard β
β (22 funcs) β β (5 categories) β β (Chart.js + API)β
ββββββββββββββββ βββββββββββββββββββββ βββββββββββββββββββ
Multiple Analytics instances with different bot_id can write to the same
SQLite database. Each metric is scoped by bot_id. The dashboard auto-detects
the bot with the most updates rows, or you can pin it via --bot-id /
?bot_id= query param.
| Check | Result |
|---|---|
| pytest | 254 tests, all passing |
| ruff | clean |
| mypy | clean (15 files) |
| E2E smoke test | passed (data ingestion, all metrics, sql_query, dashboard) |
# Run the full test suite
pip install -e '.[dashboard,dev]'
pytest tests/ -v
ruff check analytics_for_telegram_bot
mypy analytics_for_telegram_botanalytics_for_telegram_bot/
βββ analytics.py # Public facade: track_update, event, rating, review, funnel_step
βββ config.py # Config dataclass with validation
βββ db.py # SQLite connection + schema bootstrap
βββ parser.py # Telegram update parser (dict -> ParsedUpdate)
βββ privacy.py # User ID hashing, text resolution, salt management
βββ retention.py # Data purge logic
βββ writer.py # Background writer thread (batch + flush)
βββ metrics.py # 22 metric fetchers + REGISTRY (dashboard allow-list)
βββ repository.py # Parameterized SQL queries (read-only)
βββ reports.py # Markdown report generators
βββ __main__.py # CLI entry point (dashboard subcommand)
βββ dashboard/
βββ app.py # Flask app factory (auth, routes, API)
βββ auth.py # Password hashing + constant-time verify
βββ static/ # Vendored Chart.js + card-grid frontend
This repo includes GitHub Actions workflows:
- CI (
.github/workflows/ci.yml) β runs ruff, mypy, and pytest on Python 3.10β3.13 - Deploy Pages (
.github/workflows/deploy-pages.yml) β auto-deploys the landing page on every push tomaster - Release (
.github/workflows/release.yml) β creates a GitHub Release with built wheels when av*tag is pushed
# Trigger a release
git tag v0.1.0
git push origin v0.1.0MIT Β© BOSSincrypto
Made with β‘ for the Telegram bot community
#telegram #telegram-bot #analytics #python #sqlite #flask #chatbot #privacy-first #zero-dependencies #DAU #funnel-analysis #data-retention #open-source #MIT