A production-grade Frappe app providing Telegram bot integration, smart notification routing, daily digests, and workflow approvals for the Bespo QMS platform.
- 📬 Telegram Notification Engine — Instant alerts via Telegram Webhook for all ERP events.
- 💬 Interactive Bot Menu — In-Telegram
/startmenu for quick access to pending approvals and personal digest. - ✅ One-Click Workflow Approvals — Approve or reject documents directly from Telegram inline buttons.
- 📅 Daily Digest — Personalized user summary of pending tasks, open tenders, and CPO expiries.
- 🔔 CPO Expiry Alerts — Proactive daily checks for expiring Conditional Purchase Orders.
- 📈 SLA Escalation Engine — Hourly escalation processor for unattended critical alerts.
- 🔒 DND / OOO Routing — Respect quiet hours, delegate out-of-office notifications to backup approvers.
- 🧩 Telegram Chat Profiles — Register individual users, groups, supergroups, and channels as notification destinations.
- 🤖 Auto Group Registration — Bot auto-registers itself in Telegram groups when added.
The app follows the standard Frappe modular architecture:
| File | Responsibility |
|---|---|
api.py |
Whitelisted HTTP endpoints (Webhook, PIN verification, Admin dashboard) |
events.py |
Document event hooks (on_submit, on_update, after_insert) |
tasks.py |
Scheduled background jobs (Daily, Hourly, Weekly Cron) |
utils.py |
Core Telegram update processing, menu routing, workflow callbacks |
hooks.py |
Frappe framework registration for all events, schedules, and fixtures |
- Frappe Framework
v14.xorv15.x - Python
>=3.10 - MariaDB (via Frappe bench)
- Redis (for background queues and PIN cache)
- A configured Telegram Bot Token (from @BotFather)
cd /path/to/your/frappe-bench
bench get-app https://github.com/nathanabay/bespo_notifications --branch mainbench --site your-site.local install-app bespo_notificationsbench --site your-site.local migrate- Search for "Bespo Notification Settings" in the Frappe desk.
- Enter your Bot Token from Telegram BotFather.
- Click Register Webhook to bind the bot to your server's public URL.
- Each user can link their Telegram account by generating a PIN from their User Profile.
| Field | Type | Description |
|---|---|---|
custom_telegram_chat_id |
Data | Linked Telegram Chat ID |
custom_receive_telegram |
Check | Enable/disable Telegram alerts |
tg_notify_workflow |
Check | Receive workflow button alerts |
tg_notify_daily_digest |
Check | Receive daily digest |
tg_notify_cpo_expiry |
Check | Receive CPO expiry alerts |
tg_notify_tender_alerts |
Check | Receive tender lifecycle alerts |
tg_notify_system_alerts |
Check | Forward ERP Notification Logs |
dnd_start_time / dnd_end_time |
Time | Do-Not-Disturb window |
is_out_of_office |
Check | OOO flag |
delegated_approver |
Link → User | Who to route approvals to when OOO |
The app registers a webhook endpoint at:
https://<your-domain>/api/method/bespo_notifications.api.telegram_webhook
This must be publicly accessible for Telegram to push updates instantly. Set it via Bespo Notification Settings → Register Webhook.
The app supports running multiple Telegram bots simultaneously with full tenant isolation.
Each Telegram Chat Profile links to ONE specific bot via the linked_bot field. This enables:
- Tenant Isolation - Different departments/teams use different bots
- Per-Bot Analytics - Track delivery metrics per bot
- Independent Webhooks - Each bot has its own webhook configuration
- Bot-Scoped Routing - Notifications route through the correct bot
| Component | File | Purpose |
|---|---|---|
| Bot Runner | bot/main.py |
run_all_bots() - Starts multiple bots concurrently |
| Bot Factory | bot/application.py |
PTB v20+ factory with per-bot Redis persistence |
| Circuit Breaker | circuit_breaker.py |
Per-bot failure handling |
| Rate Limiting | utils.py |
check_bot_rate_limit() per bot |
| Health Monitoring | tasks.py |
check_bot_health() cron job |
# Run all enabled bots
bench execute bespo_notifications.bespo_notifications.bot.main.run_all_bots
# Run specific bots
bench execute bespo_notifications.bespo_notifications.bot.main.run_all_bots --bots "Bot A","Bot B"Each bot is configured via the Telegram Bot DocType:
- Bot token (encrypted)
- Custom API URL (for self-hosted Telegram API)
- Proxy settings
- Chat ID allowlist (optional restriction)
- Health monitoring thresholds
| DocType | Type | Purpose |
|---|---|---|
Bespo Notification Settings |
Single | Global bot token, webhook URL, and feature flags |
Telegram Bot |
Standard | Multi-bot configuration (tokens, proxies, allowlists) |
Telegram Chat Profile |
Standard | Registered Telegram destinations (users, groups, channels) |
Telegram Notification Rule |
Standard | Configurable routing rules per DocType and event |
Telegram Group Topics |
Child Table | Thread/topic entries for supergroups |
MIT — See license.txt
The app implements a Dead Letter Queue for failed notifications. Failed notifications are retried up to 5 times with exponential backoff before being permanently marked as failed.
- Retry Schedule: 30s, 60s, 120s (exponential backoff)
- Max Retries: 5 per notification
- Hard Bounces: Notifications that fail due to invalid chat IDs are immediately marked as hard bounces and not retried
- Email Fallback: After DLQ exhaustion, an email fallback is triggered to notify the user
# View failed notification logs
bench --site <site> execute frappe.desk.query_report.run \
-- report_name="Notification Log" \
-- filters='{"status":"Failed"}'
# Run DLQ processor manually
bench --site <site> execute bespo_notifications.bespo_notifications.tasks.process_dead_letter_queue| Field | Description |
|---|---|
is_hard_bounce |
Permanently failed (invalid recipient) |
error_trace |
Full traceback of the failure |
retry_count |
Number of retry attempts made |
- Secret Token Validation: All webhooks require
X-Telegram-Bot-Api-Secret-Tokenheader - IP Allowlisting: Only Telegram server IPs are allowed (configurable via
allow_localhost_in_webhook) - Rate Limiting: Global (5000 req/min) and per-bot (1000 req/min) limits
- Payload Size Limits: 64KB max to prevent DoS
- Multi-Tenant Isolation: Each bot is isolated via
linked_botfield on Chat Profiles - No Cross-Bot Notifications: Approvers must have a linked bot to receive notifications
- PIN Verification: Users must verify via time-limited PIN codes
- Rate Limiting: Verify attempts limited to 5 per hour
- Path Traversal Protection: Private file access uses symlink resolution
- CSRF Bypass: Only for webhook endpoint (Telegram cannot include CSRF tokens)
- SQL Injection: All queries use parameterized filters
- Audit Trail: All Telegram actions are logged for accountability
| Action | Required Permission |
|---|---|
| View dashboard | System Manager |
| Link Telegram account | User (self) |
| Delete webhook | Telegram Bot (delete) |
| Set webhook | Telegram Bot (write) |
| View pending approvals | User (assigned) |
- Backup database
- Review breaking changes in release notes
- Test in staging environment
- Verify webhook registration:
bench --site <site> execute bespo_notifications.api.telegram_webhook - Test PIN verification flow
- Verify notification routing for all categories
- Test approval workflow end-to-end
- Check DLQ processor is running:
bench --site <site> list Scheduled Jobs - Verify circuit breaker health checks
- Test rate limiting by sending rapid callbacks
- Verify multi-bot isolation if using multiple bots
- Check error logs for any warnings:
bench --site <site> show-logs
- User can link Telegram account via PIN
- Approver receives notification for new request
- Approver can approve/reject via inline buttons
- Submitter receives status updates
- Document status syncs to ERPNext
- DND routing respects quiet hours
- OOO delegation forwards notifications
- Daily digest generates and sends
- CPO expiry alerts trigger correctly