A Telegram bot that automatically imports bank email receipts via IMAP, categorises transactions, tracks budgets across multiple currencies, and delivers a visual spending dashboard — all inside Telegram.
| Feature | Details |
|---|---|
| Auto Email Import | Polls your inbox via IMAP at a configurable interval for bank receipt emails |
| Smart Categorisation | Auto-tags merchants into categories: Food & Dining, Groceries, Transport, Shopping, Entertainment, Health, Utilities, Subscriptions, Travel |
| Multi-Currency Support | Detects currency from email (e.g. SGD, DKK, USD) and groups transactions by currency |
| Visual Dashboard | Generates a 4-panel PNG dashboard: KPI cards, category pie chart, daily bar chart, monthly trend |
| Real-time Alerts | Instant Telegram notification with retry logic for every new transaction |
| Budget Limits | Set per-category or global monthly budgets; see % used with colour-coded indicators |
| Multi-Bank Patterns | Built-in regex patterns for Trust Bank SG, Chase, Bank of America, Wells Fargo, Citi, plus a generic fallback |
budget-tracker-bot/
├── main.py ← Entry point: bot + email polling scheduler
├── requirements.txt ← Python dependencies
├── .env ← Your credentials (not committed — see .env.example)
├── .env.example ← Template for environment variables
├── .gitignore
├── data/
│ └── budget.db ← SQLite database (auto-created on first run)
├── logs/
│ └── bot.log ← Runtime logs (auto-created)
└── src/
├── bot.py ← Telegram command handlers & notification logic
├── database.py ← SQLite schema, queries & migrations
├── email_parser.py ← IMAP client + bank email regex parsing
└── dashboard.py ← Matplotlib chart generator
git clone <your-repo-url>
cd budget-tracker-bot
pip install -r requirements.txtPython 3.10+ is required. Dependencies:
| Package | Purpose |
|---|---|
python-telegram-bot==20.7 |
Telegram Bot API (async) |
apscheduler==3.10.4 |
Email polling scheduler |
matplotlib==3.8.2 |
Dashboard chart generation |
pillow==10.2.0 |
Image support for matplotlib |
beautifulsoup4==4.12.3 |
HTML email body parsing |
lxml==5.1.0 |
HTML parser backend |
python-dotenv==1.0.0 |
.env file loading |
aiofiles==23.2.1 |
Async file I/O |
cp .env.example .envEdit .env with your values:
| Variable | Required | Description | Example |
|---|---|---|---|
TELEGRAM_BOT_TOKEN |
Yes | Token from @BotFather | 123456:ABC-DEF... |
TELEGRAM_CHAT_ID |
Yes | Your Telegram user ID (get from @userinfobot) | 123456789 |
EMAIL_ADDRESS |
Yes | Email address to poll for bank receipts | you@gmail.com |
EMAIL_PASSWORD |
Yes | App password (not your main password — see below) | abcd efgh ijkl mnop |
IMAP_SERVER |
Yes | Your email provider's IMAP server | imap.gmail.com |
IMAP_PORT |
No | IMAP port (default: 993) |
993 |
BANK_EMAIL_SENDER |
Yes | Sender address your bank uses for receipts | alerts@chase.com |
EMAIL_CHECK_INTERVAL |
No | Minutes between email checks (default: 5) |
5 |
DEFAULT_MONTHLY_BUDGET |
No | Default monthly budget (default: 3000) |
3000 |
CURRENCY_SYMBOL |
No | Fallback currency symbol (default: $) |
$ |
Gmail requires an App Password, not your main account password.
- Go to myaccount.google.com/security
- Enable 2-Step Verification if not already enabled
- Navigate to App Passwords → Select app: Mail → Generate
- Copy the 16-character password and use it as
EMAIL_PASSWORDin your.env
- Use
imap-mail.outlook.comasIMAP_SERVER - You may need to enable IMAP access in Outlook settings
- Use an App Password if 2FA is enabled
Set IMAP_SERVER and IMAP_PORT to your provider's IMAP settings. Most providers use port 993 with SSL.
python main.pyOn startup the bot will:
- Initialise the SQLite database (
data/budget.db) - Start listening for Telegram commands
- Immediately check emails, then repeat every
EMAIL_CHECK_INTERVALminutes - Send you a Telegram notification for each new transaction found
| Command | Description |
|---|---|
/start |
Welcome message with command overview |
/help |
Detailed command reference |
/today |
Today's transactions, grouped by currency |
/month |
Monthly summary with category breakdown per currency |
/budget |
View all budgets and usage with colour-coded indicators |
/budget Food 500 |
Set the Food category limit to 500/month |
/budget 3000 |
Set the global monthly budget to 3000 |
/dashboard |
Generate and send a visual spending dashboard image |
Inline buttons for Dashboard and This Month appear below transaction messages for quick navigation.
Edit src/email_parser.py and add an entry to the BANK_PATTERNS list before the Generic fallback:
{
"name": "My Bank",
"currency_default": "USD", # fallback currency
"amount": r"(?:charged|spent)\s*\$(?P<amount>[\d,]+\.\d{2})", # must have (?P<amount>...)
"merchant": r"at\s+(?P<merchant>[A-Za-z0-9 &',.\-]+?)(?:\s*on|\n)", # must have (?P<merchant>...)
"date": r"on\s+(?P<date>\d{2}/\d{2}/\d{4})", # must have (?P<date>...)
},Tips:
- Forward a real bank email to yourself and inspect the text
- Use regex101.com to build and test your patterns
- Named groups
amount,merchant, anddateare required - To capture currency from the email text (e.g.
SGD,EUR), add a(?P<currency>[A-Z]{3})group to the amount pattern instead of usingcurrency_default
The parser auto-detects these date formats:
MM/DD/YYYY, MM/DD/YY, MM-DD-YYYY, Month DD YYYY, Month DD, YYYY, DD Mon YYYY
The /dashboard command generates a 4-panel dark-themed image:
| Panel | Content |
|---|---|
| KPI Cards | Total Spent (per currency if multi-currency), Remaining, Budget, % Used, Transaction Count |
| Category Pie Chart | Spending breakdown by category (and currency when applicable) |
| Daily Bar Chart | Spending per day this month (stacked by currency if multi-currency) |
| Monthly Trend | 6-month line chart with one line per currency |
-
Email Polling —
main.pyuses APScheduler to run an IMAP check every N minutes. It searches for emails from your configuredBANK_EMAIL_SENDER. -
Parsing — Each email body is extracted (HTML emails are converted to text via BeautifulSoup). The text is matched against
BANK_PATTERNSin order until a pattern successfully extracts an amount, merchant, and date. -
Categorisation — The merchant name is matched against keyword lists in
CATEGORY_RULESto auto-assign a category (Food & Dining, Groceries, etc.). Unmatched merchants are labelled "Uncategorized". -
Storage — Transactions are stored in SQLite with a
UNIQUEconstraint onemail_uidto prevent duplicates across restarts. -
Notification — Each new transaction triggers a Telegram message with merchant, amount, currency, category, and date. Notifications retry up to 3 times on timeout.
-
Dashboard — Charts are generated by matplotlib and sent as a PNG image via the Telegram Bot API.
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir -r requirements.txt
CMD ["python", "main.py"]docker build -t budget-bot .
docker run -d --env-file .env --name budget-bot budget-bot- Never commit
.env— it contains your bot token, email credentials, and chat ID. It is already in.gitignore. - Use App Passwords for Gmail/Outlook, never your main account password.
- The bot only sends notifications to the
TELEGRAM_CHAT_IDconfigured in.env. - The SQLite database and log files are stored locally in
data/andlogs/.
MIT