Telegram bot for splitting restaurant bills from receipt photos.
This bot solves a very common post-dinner problem: one or two people pay the full bill, everyone ordered different things, and then the group has to figure out who owes what.
The project accepts a receipt image, runs OCR, extracts line items, asks users to confirm or fix the parse, tracks who ate what and who already paid, and calculates who owes whom in the end.
Restaurant bill splitting is messy in real life:
- one or two people pay the whole check
- several people ordered different dishes and drinks
- someone shared food
- someone already transferred money
- the group still needs a clear final answer: who owes whom
This bot is built to take that chaos and turn it into a structured settlement flow.
- A user sends a receipt photo.
- The bot runs OCR and extracts raw receipt text.
- The parser builds line items and the total.
- Users confirm or fix the parsed items.
- Participants mark who ate what and who already paid.
- The bot calculates balances and minimal transfers.
flowchart LR
A[Receipt Photo] --> B[OCR]
B --> C[Receipt Parser]
C --> D[User Confirmation]
D --> E[Participants and Payments]
E --> F[Settlement Result]
- OCR-based receipt parsing
- Manual confirmation and correction of parsed items
- Split by item or split evenly
- Optional LLM-assisted refinement
- Polling mode for local development
- Webhook mode for production
- Quality and observability scripts for parser/OCR iterations
Main modules:
app/bot.py— aiogram bot flow and Telegram interaction logicapp/webhook.py— webhook runtime built onaiohttpapp/ocr.py,app/ocr_layout.py,app/ocr_normalizer.py— OCR extraction and normalizationapp/receipt_parser.py,app/receipt_layout_parser.py— rule-based receipt parsingapp/llm.py,app/llm_refiner.py— optional LLM parse/refine layerapp/storage.py— in-memory session storageapp/split_calc.py— balances and minimal transfer calculationscripts/— OCR integration checks, quality reports, replay and export toolstests/— unit tests for parser and quality-related logic
Create a virtual environment and install dependencies:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtCreate a local env file:
cp .env.example .envFill at least:
BOT_TOKENOCR_PROVIDERYANDEX_FOLDER_IDandYANDEX_VISION_API_KEYif you use Yandex OCROPENAI_API_KEYif you want LLM-assisted refinement
Run locally in polling mode:
python3 -m app.botOr with make:
make install
make local-runRecommended for local development:
make local-runRecommended for production behind HTTPS reverse proxy:
make webhook-runRequired webhook variables:
WEBHOOK_BASE_URLWEBHOOK_PATHWEBHOOK_SECRETWEBHOOK_HOSTWEBHOOK_PORT
Core:
BOT_TOKENTIP_PERCENT
OCR:
OCR_PROVIDERYANDEX_FOLDER_IDYANDEX_VISION_API_KEY
LLM:
OPENAI_API_KEYOPENAI_MODELOPENAI_PROXY_URLLLM_MAX_CALLS_PER_RECEIPTLLM_OCR_EXCERPT_CHARSLLM_GROUNDING_STRICTLLM_MAX_UNSUPPORTED_RATIOLLM_MAX_NOVEL_TOKEN_RATIO
Quality alerts:
QUALITY_ALERT_ENABLEDQUALITY_ALERT_WEEKDAYQUALITY_ALERT_HOURQUALITY_ALERT_TZQUALITY_ALERT_MIN_NEW_FAILSQUALITY_ALERT_CHAT_IDQUALITY_ALERT_BOT_TOKENQUALITY_ALERT_PROJECT_NAMEQUALITY_ALERT_PROXY_URL
Webhook:
WEBHOOK_BASE_URLWEBHOOK_PATHWEBHOOK_SECRETWEBHOOK_HOSTWEBHOOK_PORT
See the full template in .env.example.
Unit tests:
make unit-testParser regression on sanitized synthetic fixtures:
make parser-testYandex OCR integration test on your own image:
make ocr-test IMAGE=/abs/path/to/receipt.jpgOr with multiple files:
python3 scripts/test_yandex_ocr.py --image /abs/path/receipt1.jpg --image /abs/path/receipt2.jpgThe public repository intentionally does not ship real receipt photos.
At confirmation time the bot:
- shows parsed items
- reports quality status:
good,warning, orlow_confidence - asks the user to confirm, fix, or rescan if needed
After confirmation, users can:
- split by item
- split evenly
- record payments
- get final balances and minimal transfers
The bot writes diagnostic logs to:
logs/receipt_sessions.jsonllogs/receipt_user_edits.jsonllogs/ocr_sessions/<receipt_session_id>.txtlogs/quality_reports/alert_state.json
Useful commands:
python3 scripts/quality_report.py --days 7
python3 scripts/receipt_auto_check.py --days 7 --limit 20
python3 scripts/export_receipt_dataset.py --days 30 --status low_confidence --limit 20
./scripts/run_weekly_quality_report.shTo generate local weekly automation templates for your own machine:
./scripts/install_weekly_quality_automation.shThe repo includes generic Linux VM deployment scaffolding:
ops/deploy/systemd/restaurant-split-bot.serviceops/deploy/nginx/restaurant-split-bot.confops/deploy/scripts/bootstrap_vm.shops/deploy/scripts/deploy_vm.sh
Example bootstrap flow:
cd /opt/restaurant-split-bot
chmod +x ops/deploy/scripts/bootstrap_vm.sh ops/deploy/scripts/deploy_vm.sh
DOMAIN=bot.example.com RUN_USER=ubuntu APP_DIR=/opt/restaurant-split-bot ./ops/deploy/scripts/bootstrap_vm.sh
sudo certbot --nginx -d bot.example.com
APP_DIR=/opt/restaurant-split-bot BRANCH=main SERVICE_NAME=restaurant-split-bot ./ops/deploy/scripts/deploy_vm.shThe GitHub Actions workflow .github/workflows/deploy.yml can be used as a starting point for SSH-based deployment.
app/ bot runtime, OCR, parser, split logic
tests/ unit tests
scripts/ integration and observability tools
ops/deploy/ generic deployment scaffolding
fixtures/ sanitized synthetic parser fixtures
docs/ public notes and roadmap
- Contributing guide: CONTRIBUTING.md
- Security policy: SECURITY.md
- Release checklist: docs/RELEASE_CHECKLIST.md
- No real credentials should be committed. Use
.env. - This public repo intentionally excludes private ops/runbooks and real receipt datasets.
- If you want production rollout, adapt
ops/deploy/to your own infrastructure.
MIT