A minimalist personal expense tracking bot that uses Telegram as the interface and LLM-powered natural language understanding.
- Add or subtract cash:
add cash 500or-200 cash - Specify dates:
add cash 1000 on 12.8.25(defaults to today) - Check balance:
current balance
- Add expenses naturally:
500 fruitsorfruits 500 - Automatic categorization via LLM with confirmation
- Specify dates:
500 batteries on 15.10.25 - Modify or delete by replying to any message:
change to 400,delete, orchange category to Food
- Time-based breakdowns:
expenses this monthorexpenses from 1 oct to 31 oct— includes pie chart - Category-specific queries:
food expenses this month - View all categories:
show categories
Stack:
- Rust application
- Telegram Bot API for messaging
- LLM with function calling (Groq as inference provider)
- Remote Turso database (libsql)
- Pie chart generation via plotters
Design:
- Message-based context tracking (no conversation state)
- Immediate commits with easy corrections
- Auto-categorization with user category caching
- Natural language date parsing
- Allowlist-based access control
- Rust 1.89.0+
- Telegram bot token (via @BotFather)
- Turso database
- Groq API key
Create a .env file:
TELEGRAM_BOT_TOKEN=your_bot_token
ERROR_BOT_TOKEN=your_error_bot_token
TURSO_AUTH_TOKEN=your_turso_token
GROQ_API_KEY=your_groq_key
TELEGRAM_ERROR_CHANNEL_ID=your_error_channel_idThe app reads config.json at startup (override with CONFIG_FILE env var):
{
"log_level": "info",
"db_url": "libsql://your-db.turso.io",
"model_name": "openai/gpt-oss-20b",
"admin_id": 123456789
}admin_id is your Telegram user ID — message @userinfobot to find it. Use config.prod.json for production.
Create the following table in your Turso database (in addition to the expenses and cash_transactions tables):
CREATE TABLE allowlist (user_id INTEGER PRIMARY KEY, status TEXT NOT NULL DEFAULT 'pending');The bot uses an allowlist. By default no one has access until approved by the admin.
- When an unknown user messages the bot, they are silently queued as
pendingand you receive a notification in your error channel with their user ID - From your Telegram account, send
/approve <user_id>to the bot to grant access - Send
/suspend <user_id>to revoke access - As admin, you bypass the allowlist entirely
# Install dependencies
cargo build
# Run the bot
cargo runBuild and run:
docker build -t cash-tracker .
docker run -d --env-file .env cash-trackerThe application is designed to run on any VPS:
# Build release binary
cargo build --release
# Run with environment variables
./target/release/cash-trackerEnsure all environment variables and config.prod.json are properly configured in your production environment.
MIT