Skip to content

ketans7/RiderIntel

Repository files navigation

Rider Intelligence Bot

A Telegram bot for UK Deliveroo riders. Send a screenshot of an order offer, get back an accept / borderline / skip verdict grounded in the real cycling time from your current location through every pickup and dropoff.

Local-first: OCR + a local VLM read the screenshot, Mapbox computes the cycling route, SQLite stores everything for later analysis.

Why this exists

When a Deliveroo order offer pops up, a rider gets a few seconds to accept or reject — while sitting on a bike, often mid-ride. The offer screen shows the payout and the addresses, and nothing else: no distance, no time estimate, no hint that the "£5.53, two drops" job is actually a 90-minute slog across town that nets less than £4/hour.

Riders develop gut feel, but gut feel fails exactly where it matters most: stacked orders with multiple pickups and drops, unfamiliar postcodes, and peak-hour traps where restaurant waits eat the margin. Every bad accept locks you out of better orders for the next half hour or more.

This project closes that information gap in the only window that matters — the decision moment:

  1. Screenshot the offer, send it to the bot. OCR (plus a local vision model as fallback) extracts the payout and every pickup/dropoff address.
  2. The bot computes the real trip. Mapbox routes the actual cycling path from where you are right now, through every stop, and adds realistic per-stop and peak-hour friction.
  3. You get a verdict in seconds — accept / borderline / skip — with the effective £/hour and a per-leg time breakdown, so you can sanity-check it at a glance.

Beyond the instant verdict, every order, route, outcome, and post-delivery survey answer is logged. Over time that becomes a personal dataset of which restaurants make you wait, which buildings swallow ten hidden minutes, and which areas actually pay — feeding a /suggest command that recommends where to position for better orders, and an operator dashboard for digging into the data.

Everything runs locally on your own machine: screenshots never leave it, the only cloud calls are routing lookups. No accounts, no SaaS, no fees.

See PROJECT_PLAN.md for the architecture and design decisions.


Quick start (native, Apple Silicon — MLX path)

# 1. clone, install deps. --extra mlx pulls mlx-vlm + torch + timm.
uv sync --extra mlx

# 2. copy .env template and fill in tokens
cp .env.example .env
$EDITOR .env
#   TELEGRAM_BOT_TOKEN=...     (from @BotFather)
#   MAPBOX_TOKEN=...           (from mapbox.com/account/access-tokens)
#   VISION_PROVIDER=mlx_vlm
#   MLX_VLM_MODEL=/abs/path/to/qwen7b   (or an mlx-community repo id)

# 3. run the bot in the foreground for testing
uv run python -m rider_bot

# 4. (separately) run the dashboard
uv run python -m dashboard           # http://localhost:8000

# 4. or run in the background with auto-restart on rerun
./scripts/run.sh

Background mode

scripts/run.sh launches and supervises both the bot and the dashboard as native macOS processes (no Docker). Each writes a PID file under data/ and a daily-rotating log under data/logs/.

./scripts/run.sh              # start both bot + dashboard
./scripts/run.sh bot          # start bot only
./scripts/run.sh dashboard    # start dashboard only (http://localhost:8000)
./scripts/run.sh status       # what's running
./scripts/run.sh stop         # stop both

Re-running start for a service stops the existing instance first, so a single command is enough to "restart". Logs:

  • data/logs/bot.log
  • data/logs/dashboard.log

Dashboard

Read-only operator dashboard at http://localhost:8000. Built from the "RiderIntel" design handoff:

  • OPERATE — Overview · Live feed · Riders
  • ANALYSE — Spatial · Surveys · Decisions
  • SYSTEM — Pipeline

The dashboard renders the React/Babel prototype as-is; the FastAPI server injects a window.RI payload built from the bot's SQLite DB on each page load. /api/data returns the same JSON for clients that want it raw.

Run standalone (no Docker, useful for fast frontend iteration):

uv run python -m dashboard

Configuration (.env)

Var Default Notes
TELEGRAM_BOT_TOKEN Required. From @BotFather.
MAPBOX_TOKEN Required if ROUTING_PROVIDER=mapbox.
GOOGLE_MAPS_API_KEY Required if ROUTING_PROVIDER=google.
ROUTING_PROVIDER mapbox mapbox or google. Same provider used for geocoding.
PARSER hybrid ocr, vlm, or hybrid (OCR primary, VLM on low confidence).
VISION_PROVIDER ollama mlx_vlm, ollama, or anthropic.
MLX_VLM_MODEL mlx-community/Qwen2.5-VL-3B-Instruct-4bit Apple Silicon only.
OLLAMA_MODEL qwen3-vl:4b Only if vision provider is ollama.
OCR_CONFIDENCE_FLOOR 0.7 Below this, hybrid falls back to the VLM.
LOG_LEVEL INFO

What gets stored

SQLite at data/bot.db. Tables:

  • user — one row per Telegram user, with first-seen / last-seen
  • userlocation — every shared location, full GPS precision
  • order — every screenshot, with parsed fields, route, verdict, timings
  • feedback — 👍/👎 on the verdict itself
  • deliveryoutcome — did the rider accept, complete, was the time right, any issues
  • postcodecache — postcode → lat/lng cache (never re-geocoded)
  • rawevent — every Telegram update raw JSON, append-only, for forensic replay

/forget from inside Telegram wipes all of the above for the sender.


Bot commands

Command What
/start Welcome + workflow hint
/help List commands
/stats Your last 7 days
/forget Delete all your stored data

Anything else (photo, location share, button tap) is handled automatically.


Logs

  • stdout: streaming JSON (every event), readable by tail -f data/logs/run.log
  • data/logs/bot.log: same content, rotated daily, 14 days kept
  • The bot also stores raw Telegram updates in the rawevent table — those are the structured equivalent and the place to query for analytics

Layout

src/rider_bot/
├── __main__.py        — entry point
├── bot.py             — Telegram handlers
├── parser/            — OCR + VLM parsers (mlx_vlm, ollama, anthropic) + hybrid
├── routing.py         — Mapbox + Google routing clients
├── geocoding.py       — Mapbox + Google geocoding clients
├── scoring.py         — verdict logic + card formatter
├── models.py          — SQLModel tables
├── db.py              — session + helpers
├── survey.py          — 23-question post-delivery survey catalogue
└── prompts.py         — VLM prompt + Anthropic tool schema

dashboard/
├── __main__.py        — uvicorn entry point
├── server.py          — FastAPI: /api/data + index.html with injected payload
└── static/            — React/Babel prototype (RiderIntel design handoff)

tests/                 — unit tests (scoring)
scripts/               — utilities
   ├── run.sh          — background launcher with PID file
   ├── test_ocr.py     — OCR-only smoke test
   ├── test_pipeline.py — full parse→geocode→route→score on fixtures
   └── benchmark_parsers.py   — (planned)
data/
   ├── bot.db          — SQLite, gitignored
   ├── screenshots/    — saved images, gitignored
   └── logs/           — rotating daily logs, gitignored


Dev

uv run pytest                  # unit tests
uv run python scripts/test_pipeline.py  # full pipeline on fixtures
uv run python -m rider_bot     # run the bot

About

Telegram bot that tells UK delivery riders in seconds whether a Deliveroo order offer is worth taking — real cycling-time routing, local OCR/VLM parsing, and an operator dashboard.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors