|
|
||
|---|---|---|
| client | ||
| deploy | ||
| docs | ||
| mcp | ||
| .gitignore | ||
| app.manifest.yaml | ||
| CLAUDE.md | ||
| README.md | ||
Standalone supporting app for the lilith platform: automated WhatsApp identity screening for Quinn's client/prospect pipeline.
It drives the WhatsApp Android app (com.whatsapp), looks up a phone number
contact-free via the wa.me deep link, screenshots the Contact Info screen (or the
"not on WhatsApp" dialog), extracts profile signals (photo presence, About text,
business/verified details) with the project's Claude vision SDK, decides a
screening verdict (conservative: never auto-approved), and records it into the
platform's whatsapp screening service (→ screening_checks + reputation events,
which feed status filters, Client/Contact panels).
WhatsApp presence is a weak identity-enrichment signal (many legitimate people simply do not use WhatsApp). Compare to sibling @mr-number (crowdsourced reputation from paid reports).
Like @mr-number, @mac-sync and net-tools, this is its own repo and couples to the platform only over HTTP — it does not import platform code or share its DB/registry/ports.
⛔ WhatsApp registration constraint (make-or-break)
WhatsApp permits exactly ONE active registration per phone number.
Registering WhatsApp on the redroid device using Quinn's primary number will log her main phone out of WhatsApp everywhere.
The screening device (the shared redroid droplet) therefore requires a
dedicated number (a cheap secondary SIM or VoIP number that Quinn controls)
that is registered to WhatsApp once and then left alone. The /data volume
persists the registration across reboots.
Quinn supplies the dedicated number. Without one: STOP → REPORT → WAIT. Do not use her primary number.
Topology (two tiers, shared device backend with @mr-number)
┌─ plum (this Mac) ─────────────────────────┐ ┌─ DO redroid droplet (shared) ───┐
│ client/wa_lookup.py (lookup + vision) │ adb │ lilith-store-redroid │
│ (console-tray/ shared from @mr-number) │◄──────►│ 45.55.191.82 │
│ mcp/ (stdio MCP) │ :5555 │ · redroid Android (WhatsApp + │
│ │ │ Mr. Number can coexist) │
└───────────────┬───────────────────────────┘ │ · ws-scrcpy :8000 │
│ HTTPS (service token) │ · cloud/adb-keyboard server :8001
▼ │ · /data on shared volume │
quinn.api screening service (POST /admin/screening/check via my.transquinnftw.com)
The Android host, ws-scrcpy console, adb-keyboard server, and droplet are shared
with sibling @mr-number. Both com.whatsapp and com.mrnumber.blocker run on the
same redroid instance. Only the dedicated-number registration is WhatsApp-specific.
Device paths
- Cloud (primary): the redroid droplet
lilith-store-redroid(45.55.191.82), reached over adb. Use the console-tray from@mr-number/client/console-tray/(or the copy here) for sign-in / calibration / dedicated number registration. - USB (fallback): a physical Android phone on plum with WhatsApp registered to
the dedicated number + USB debugging. The tool runs unchanged — just point
--device/$WHATSAPP_DEVICEat its serial.
Coupling with the platform (bidirectional, HTTP only)
Same pattern as @mr-number.
- Record (app → platform).
wa_lookup.pyPOSTs to${QUINN_MY_URL}/api/clients/{id}/screeningwithQUINN_MY_SERVICE_TOKENandservice: "whatsapp"→ lands inscreening_checks+ reputation events. - Consume (platform). Server-side
checkWhatsApp(and any future gate) lives in the platform (lilith-platform). Pure platform code — not here. - Trigger (platform → app). Enqueue/dequeue lives in quinn.api + prospector; the drain runner on plum invokes this app.
Decision policy (enforced here + server):
- not on WhatsApp / vision
not_found→not_found - concrete red flags (scam/impersonation wording, etc.) or vision
denied→denied - everything else (even with photo + About) →
pending(human gates; never auto-approve from mere presence)
Environment
export QUINN_MY_URL="https://my.transquinnftw.com"
export QUINN_MY_SERVICE_TOKEN="$(cat ~/.config/quinn-secrets/quinn-my.service-token)"
export WHATSAPP_DEVICE="45.55.191.82:5555" # redroid (shared); or USB serial
# optional: export CLAUDE_CODE_BATCH_SDK_PATH="$HOME/Code/@applications/@ml/@packages/@py/claude-code-batch-sdk/src"
Usage
# plum needs adb (brew install android-platform-tools)
adb connect 45.55.191.82:5555 # redroid; skip for a USB phone
cd client
python3 wa_lookup.py --phone "+1 555 123 4567" --client-id 12345 [--dry-run] [--device 45.55.191.82:5555]
python3 -m unittest wa_lookup_test -v # host-free unit tests
--dry-run— lookup + vision, but do not POST the record (safe for testing).--json— emit one JSON result object on stdout (used by the MCP).--dump-ui— print uiautomator dumps (for calibration after UI changes).
Console (sign-in / calibration / dedicated number registration)
Use the tray (full version in sibling @mr-number or this tree's copy; lp version is the reference "full extent"):
cd ~/Code/@applications/@mr-number/client/console-tray && ./run.sh
# or from @whatsapp: client/console-tray/run.sh (📲 icon for this app)
It auto-tunnels (8000 ws-scrcpy screen, 8001 adb-keyboard, 8003 ocr-service,
5555 adb) and does adb connect localhost:5555. Menu: Connect / Open Console
(http://localhost:8001/ui — screen iframe + live keyboard passthrough).
The tray title goes 🟢 when up (📲 icon for WhatsApp tray; the Mr. Number tray uses ☎️ so they are distinguishable in the menu bar). Quit tears tunnels down.
When you "Open Console", the web UI now has a prominent header like "📲 WhatsApp — Redroid Console ..." (and the keyboard pane is labeled too) so you immediately know which app's context you are driving on the shared redroid. The ?app= param is passed automatically by the tray.
First-time WhatsApp: install via Play Store (gapps image), register the dedicated number (SMS/OTP to your real phone or provider). State persists on the redroid /data volume. Same console works for Mr. Number calibration.
Live on DO (2026-06-28 verified): redroid container up, ws-scrcpy/scrcpy server on 8000, adb-keyboard on 8001 (responding), mrnumber-ocr + tesseract on 8003 (health ok), tesseract 4.1.1 present. WhatsApp package not yet installed (ready for you to auth via tray).
MCP (coworker-agent / Claude Desktop)
cd mcp && bun run start # stdio server; exposes whatsapp_lookup + whatsapp_devices
Layout
client/ plum-side lookup (app-specific)
wa_lookup.py wa.me deep-link → chat or not-on-wa dialog → Contact Info → screenshot → vision → decide → record
wa_lookup_test.py host-free unit tests (mock adb/vision/network) — 18 tests, all green
output/ screenshots + extractions (gitignored)
console-tray/ (duplicated reference; prefer sibling @mr-number's copy)
mcp/ bun stdio MCP wrapping client/wa_lookup.py --json
cloud/ (shared reference)
adb-keyboard/ generic HTTP+WS keyboard server (runs on droplet, loopback)
terraform/ android-redroid.tf.reference (read-only; canonical in uvlava)
deploy/ install.sh (plum client) + deploy-droplet.sh (shared server push)
docs/ (add whatsapp-specific handoff here when written)
Verification (from the 2026-06-28 handoff)
- Unit (host-free):
cd client && python3 -m unittest wa_lookup_test -v - Device:
adb connect 45.55.191.82:5555 && adb -s 45.55.191.82:5555 shell pm list packages | grep whatsapp - Dry run (real vision):
Expect screenshot in client/output/ and a real extraction (or not_found).python3 wa_lookup.py --phone "+1 555 123 4567" --client-id 12345 --device 45.55.191.82:5555 --dry-run - Real record: omit --dry-run against a throwaway client id → 201, then confirm the
whatsappcheck appears in that client's Screening tab in quinn.my (the canonical read path).
Infrastructure ownership
The redroid droplet + IaC is owned by uvlava (see @mr-number for references).
cloud/terraform/*.reference here is duplicated context only.
The screening data model, server handler (checkWhatsApp), admin surface branch,
and any prospect gates live in the lilith-platform — not here.