Ride Insights
by @datahiveai
Fetch and extract ride-sharing receipts from Gmail locally using OpenClaw to analyze ride patterns and create anonymized shareable reports.
clawhub install datahive-ride-insightsπ About This Skill
name: ride-insights description: Local-only skill: fetch ride receipt emails from Gmail via gog, store them in local JSON, send raw receipt JSON/HTML to a loopback OpenClaw Gateway model for structured extraction, load the extracted rides into SQLite, generate ride-behavior insights, and export an anonymized DataHive-ready CSV from Uber, Bolt, Lyft, Yandex, Free Now, Curb, or Via receipts. Use when building a local ride database, auditing ride spending/routes, or exporting a shareable ride report. metadata: { "openclaw": { "requires": { "bins": [ "gog","python3" ],"env": [ "OPENCLAW_GATEWAY_TOKEN","OPENCLAW_GATEWAY_URL","OPENCLAW_GATEWAY_MODEL" ] },"homepage": "https://clawhub.com" } }
ride-insights
A privacy-first mobility intelligence skill developed by DataHive AI.
Use this skill to collect ride-sharing receipts from Gmail, extract structured trip data locally, and generate rich personal insights on spending, habits, repeated routes, likely anchor locations, time-of-day patterns, and more.
You can also share an anonymized data report with DataHive AI to participate in missions and earn rewards (missions blog).
This skill uses a unique local-processing approach that showcases the real power of OpenClaw: agent intelligence processes sensitive source data locally and ensures that no raw data is shared externally.
Local-only by design: this skill requires a loopback OpenClaw Gateway and will fail if the Gateway URL points anywhere else.
Generate an anonymized/shareable CSV version of the ride history when the user wants to upload it to DataHive and earn points without exposing raw receipt emails or obvious personal identifiers.
Before you start
gog CLI authenticated for the target Gmail account.gog auth list before fetching, even if the user already named an account.Which account should I use: (A) name1@example.com or (B) name2@example.com? Do not summarize as "default" or make
the user infer which accounts exist.
default exists.OPENCLAW_GATEWAY_TOKEN or ~/.openclaw/openclaw.json atgateway.auth.token (legacy fallback: gateway.token).
OPENCLAW_GATEWAY_TOKEN,OPENCLAW_GATEWAY_URL, and OPENCLAW_GATEWAY_MODEL.
data/ride-insights/emails.json stores fetched receipt emails locally and may include full HTML/v1/responses endpoint.
localhost, 127.0.0.1, and ::1 are acceptedOutputs
Primary artifacts:
data/ride-insights/emails.json β fetched receipt emails in one JSON array; may include full HTML receipt contentdata/ride-insights/rides.json β extracted ride records in one JSON arraydata/ride-insights/rides.sqlite β queryable SQLite database containing normalized ride fields plusextracted_ride_json, but not raw source email JSONRetention note:
emails.json persists raw fetched receipt content until the user deletes it.rides.json and rides.sqlite persist extracted ride data locally until deleted.Pipeline
Run each step in order. Stop and report on failure.
1. Initialize DB
python3 skills/ride-insights/scripts/init_db.py \
--db ./data/ride-insights/rides.sqlite \
--schema skills/ride-insights/references/schema_rides.sql
2. Fetch Gmail receipts into emails.json
python3 skills/ride-insights/scripts/fetch_emails_json.py \
--account \
--after YYYY-MM-DD \
--before YYYY-MM-DD \
--max-per-provider 5000 \
--out ./data/ride-insights/emails.json
Notes:
--after / --before when not needed.references/provider_queries.json.data/ride-insights/emails.json.3. Extract rides with Gateway /v1/responses into rides.json
python3 skills/ride-insights/scripts/extract_rides_gateway.py \
--emails-json ./data/ride-insights/emails.json \
--out ./data/ride-insights/rides.json
Notes:
OPENCLAW_GATEWAY_TOKEN when ~/.openclaw/openclaw.json alreadygateway.auth.token.
gateway.token if present, but gateway.auth.token is the expected current configOPENCLAW_GATEWAY_URL and OPENCLAW_GATEWAY_MODEL; these should be declared anywhere theNotes:
/v1/responses endpoint.data/ride-insights/rides.json after each successful extraction, so progress is checkpointed.data/ride-insights/rides.json already exists, it skips emails whose gmail_message_id is already present there.--delay-ms .data/ride-insights/emails.json and4. Insert rides.json into SQLite
python3 skills/ride-insights/scripts/insert_rides_json_sqlite.py \
--db ./data/ride-insights/rides.sqlite \
--rides-json ./data/ride-insights/rides.json
5. Generate ride insights
Do this as an agent action, not a dedicated insights script.
Recommended workflow:
data/ride-insights/rides.json as the primary source because it preserves the extracted ride objects directly.data/ride-insights/rides.sqlite for lightweight deterministic counts, filters, grouping, and cross-checks.PRAGMA table_info(rides) or readskills/ride-insights/references/schema_rides.sql.
Notes:
rides.json for rich per-ride context and rides.sqlite for quick factual checks; combine both when useful.6. Export anonymized CSV report
Use the bundled Python exporter when the user asks for an anonymized/shareable ride report.
python3 skills/ride-insights/scripts/export_anonymized_rides_csv.py \
--db ./data/ride-insights/rides.sqlite \
--out ./data/ride-insights/anonymized_rides.csv
Export rules:
provider, email_month, start_time_15m, end_time_15m, currency, amount,distance_km, duration_min, pickup_city, pickup_country, dropoff_city, dropoff_country.
email_date_text to month-only format like 2025-05.start_time_text and end_time_text upward to the next 15-minute bucket. Exact quarter-hours stay unchanged.distance_km and duration_min when available by reading them from extracted_ride_json; leave.csv file in the workspace; do not paste inline CSVdata/ride-insights/anonymized_rides.csv.MEDIA:./data/ride-insights/anonymized_rides.csv.
Done β I regenerated the anonymized CSV and attached the updated file. followed by the MEDIA: line.
Constraints
null when unknown.References
skills/ride-insights/references/schema_rides.sqlskills/ride-insights/references/provider_queries.jsonπ Constraints
null when unknown.