Skip to content
CivicDataLabPublic

About

Accessible grievance chatbot (Telegram, Telugu/English)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

24 Commits

Folders and files

Repository files navigation

PI4C

An accessible grievance chatbot. Residents report accessibility barriers, civic problems and disaster/climate impacts to a Telegram bot in Telugu or English, and every report is structured, triaged, grouped with repeat reports of the same problem, and made available through a REST API for downstream systems.


Why this exists

Reporting a broken ramp, an open manhole or a flooded street usually means finding the right office, filling in a form in English and describing the location precisely. For people with disabilities, caregivers and residents who are more comfortable in Telugu, that is often where the report ends.

This project removes that barrier by meeting residents in a messaging app they already use. The bot asks one simple question at a time, accepts taps or typed numbers instead of requiring precise touch input, lets residents type a landmark instead of dragging a map pin, and reads the whole report back for confirmation before submitting it.


How it works

  1. A resident messages the bot on Telegram.
  2. telegram.py receives the update (long polling in development, a webhook when deployed) and dispatch.py hands it on, ignoring duplicate deliveries and handling one message per resident at a time.
  3. flow.py walks the resident through the report: language, category, location, sub-category, description, photo, who is affected, then a read-back to confirm or edit.
  4. On confirmation the report is saved to SQLite, with any photos stored in the database.
  5. classify.py triages it with keyword rules (barrier type, department, severity) and cluster.py groups it with nearby open reports of the same problem.
  6. Downstream systems and dashboards read reports, clusters and statistics from the REST API.

The conversation flow is channel-neutral (app/messenger.py); Telegram is the only adapter today.


Repository structure

Path Type Description
app/main.py File FastAPI app: the read API and the Telegram webhook endpoint
app/telegram_cli.py File Bot commands: poll, set-webhook, delete-webhook, whoami, set-profile
app/telegram.py File Telegram Bot API adapter: parse updates, send messages, download photos
app/messenger.py File Channel-neutral Incoming message type and Messenger interface
app/dispatch.py File Duplicate filtering, per-resident locking, hourly cleanup of abandoned photo folders
app/flow.py File Conversation state machine, from language choice to submitted report
app/places.py File Typed place-name search (OpenStreetMap Nominatim), limited to the city
app/pipeline.py File Post-submission processing: classify, then cluster
app/classify.py File Keyword-rules triage: barrier type, department, severity
app/cluster.py File Groups repeat reports of the same physical problem
app/geo.py File Ward lookup and distances
app/taxonomy.py File Loads categories, sub-categories and departments from data/
app/i18n.py File Language registry
app/messages_te.py File Every Telugu string the bot sends, with English glosses, for review with OPDs
app/messages_en.py File English strings (same names as the Telugu file)
app/audit.py File Stage-wise interaction log and the HMAC reporter_hash
app/auth.py File The WRITE_ACCESS switch for endpoints that change data
app/models.py File SQLAlchemy tables
app/schemas.py File API request/response models, the contract downstream consumers build against
app/db.py File SQLite engine and table creation
app/config.py File Settings and .env loader
app/fake_inbound.py File Builds fake Telegram updates for the tests
app/api/ Directory REST endpoints: reports, clusters, stats, reference data
data/taxonomy.json File Categories and sub-categories (Telugu labels, default department, municipal subject)
data/departments.json File Placeholder department list
data/wards.geojson File Optional ward polygons; empty for now, so reports have no ward
docs/schema.md File Database schema reference
tests/ Directory API, conversation flow, dashboard and unit tests
pyproject.toml File Project metadata and Python dependencies (managed with uv)
uv.lock File Locked dependency versions, generated by uv
.env File Local secrets (gitignored, copied from .env.example)
.env.example File Variable names with placeholder values

Quick start

Prerequisites

  • uv: no separate Python install needed; uv downloads Python 3.12 or newer automatically if it isn't on your machine.
# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
  • A Telegram bot token: in Telegram, message @BotFather, send /newbot and copy the token it gives you.

1. Clone the repository

git clone https://github.com/CivicDataLab/PI4C
cd PI4C

2. Install dependencies

uv sync

This creates .venv/, installs Python if needed, and installs the locked dependencies from uv.lock. Check that everything works with:

uv run pytest

3. Configure environment variables

# Windows
copy .env.example .env

# macOS / Linux
cp .env.example .env

Then edit .env. It is gitignored; keep real tokens and keys out of .env.example, the README and source files.

Variable Required Description
TELEGRAM_BOT_TOKEN Yes Bot token from @BotFather
WRITE_ACCESS No true lets anyone change reports and see conversation logs; false (the default) blocks that for everyone. Reading is always open
TELEGRAM_WEBHOOK_SECRET When deployed Random string Telegram sends with every webhook call. Without it, anyone who finds the webhook URL can submit fake reports
DATABASE_URL No Another SQLite file, as sqlite:///.... Defaults to data/pi4c.db

4. Run the application

Telegram bot (local development, no public URL needed):

uv run python -m app.telegram_cli poll

Open your bot in Telegram and send /start.

Read API (runs separately):

uv run uvicorn app.main:app --reload

Interactive docs are at http://localhost:8000/docs. Import http://localhost:8000/openapi.json into Postman to try the endpoints.

Webhook mode (deployed server): set TELEGRAM_WEBHOOK_SECRET, run uvicorn behind HTTPS, then register the webhook:

uv run python -m app.telegram_cli set-webhook https://<host>/telegram/webhook

Polling and a webhook can't run at the same time; poll deletes the webhook when it starts.


Components

Conversation (app/flow.py)

Step Resident sees Accepts
LANGUAGE తెలుగు / English (first contact and every /start) tap
MENU 3 buttons: Accessibility / General Civic / Disaster-Climate tap, or type 1–3
LOCATION Live location, Choose a location and Type the place name keyboard buttons device location, live location, a place picked via the attach (paperclip) menu → Location, or a typed place name
LOCATION_PICK up to 5 matching places as a numbered list, plus "None of these" tap, type the number, or type another name to search again
SUBCATEGORY numbered list + buttons tap, or type the number
DESCRIBE free text prompt typed text, or a photo with a caption (voice notes are refused)
PHOTO optional, "No photo" button photo or skip
AFFECTED self (PwD) / caregiver / general tap
READBACK summary + Confirm / Edit / Cancel Edit jumps to one step, then back here

/start, "మెనూ" or "hi" restarts; /cancel or "రద్దు" cancels. Sessions expire after 30 minutes of silence. Only private chats are handled; group messages are ignored.

pwd_marker is true when the reporter is a person with disability or a caregiver, or the category is accessibility.

Bot profile: the description, "About" text and /start /language /cancel menu live in BOT_DESCRIPTION, BOT_SHORT_DESCRIPTION and BOT_COMMANDS in app/messages_en.py and app/messages_te.py. After editing them, publish with uv run python -m app.telegram_cli set-profile. English is the default; Telugu is shown to users whose Telegram app is set to Telugu.

API (app/api/)

All endpoints are under /api/v1 and need no key. Read endpoints are open to anyone. Write endpoints (changing reports, the conversation log) follow WRITE_ACCESS: open to anyone when it's true, closed to everyone when it's false.

Endpoint Access Description
GET /reports public Filters: category, subcategory, barrier_type, ward, department_id, severity, status, pwd_marker, needs_review, cluster_id, created_from/to, updated_since, bbox; paginated
GET /reports/{id} public Full record, including media links
PATCH /reports/{id} write access status, status_note, department_id, severity, municipal_subject, needs_review
GET /reports/{id}/media/{media_id} public Photo (from the database)
GET /reports/{id}/log write access Stage-wise interaction log
GET /reports.geojson public Same filters, as GeoJSON points
GET /reports.csv public Same filters, every matching report as CSV (Excel / Power BI); UTF-8 with BOM
GET /clusters, /clusters/{id} public Distinct problems with report counts
GET /stats/summary public Headline numbers: total, open, resolved, PwD, needs review, high/critical, with photo
GET /stats/counts?group_by=… public Counts grouped by any of category, status, severity, ward, department_id, pwd_marker… (repeat group_by)
GET /stats/timeseries?interval=day|week|month public Reports per period (UTC), optional group_by split
GET /stats/by-ward, /by-category, /by-department public Grouped counts
GET /metrics public Completion rate, read-back confirm/edit, review load, classifier mix
GET /taxonomy, /departments, /wards public Reference data

All /stats/* endpoints and /reports.csv take the same filters as /reports.

External dashboards. Server-side tools (Power BI web connector, Grafana Infinity, Metabase, scripts) can call the read endpoints directly, with no key. Web pages on any site can call it from the browser too: CORS allows every origin.


Configuration you'll want to edit

  • data/departments.json: placeholder department list. handles describes what each department deals with.
  • data/taxonomy.json: categories and sub-categories (Telugu labels, default department, municipal subject).
  • data/wards.geojson: optional ward polygons (properties.ward or properties.name); reports get ward set from their location. Empty for now.
  • app/messages_te.py: every Telugu string the bot sends, with English glosses, for review with OPDs and native speakers.

Development

Run the tests:

uv run pytest

Check which bot the token belongs to:

uv run python -m app.telegram_cli whoami

About

Accessible grievance chatbot (Telegram, Telugu/English)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages