No description
Find a file
Natalie 023496d993 chore(redroid-whatsapp): update self-path to @ct/@applications after reorg
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 11:41:56 -04:00
client refactor: thin onto shared redroid packages; box moves to @redroid app 2026-06-28 15:06:02 -04:00
deploy fix(security): pass forge PyPI creds via 0600 temp netrc, not creds-in-URL index 2026-06-28 15:12:31 -04:00
docs chore: detach @whatsapp from mr-number worktree into independent repo 2026-06-28 10:40:36 -04:00
mcp refactor: thin onto shared redroid packages; box moves to @redroid app 2026-06-28 15:06:02 -04:00
.gitignore chore: detach @whatsapp from mr-number worktree into independent repo 2026-06-28 10:40:36 -04:00
app.manifest.yaml chore(redroid-whatsapp): update self-path to @ct/@applications after reorg 2026-06-29 11:41:56 -04:00
CLAUDE.md refactor: thin onto shared redroid packages; box moves to @redroid app 2026-06-28 15:06:02 -04:00
README.md docs: document the improved webui with per-app titles/headers so it is obvious which console you opened 2026-06-28 10:55:59 -04:00

@whatsapp

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_DEVICE at its serial.

Coupling with the platform (bidirectional, HTTP only)

Same pattern as @mr-number.

  1. Record (app → platform). wa_lookup.py POSTs to ${QUINN_MY_URL}/api/clients/{id}/screening with QUINN_MY_SERVICE_TOKEN and service: "whatsapp" → lands in screening_checks + reputation events.
  2. Consume (platform). Server-side checkWhatsApp (and any future gate) lives in the platform (lilith-platform). Pure platform code — not here.
  3. 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_foundnot_found
  • concrete red flags (scam/impersonation wording, etc.) or vision denieddenied
  • 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)

  1. Unit (host-free):
    cd client && python3 -m unittest wa_lookup_test -v
    
  2. Device: adb connect 45.55.191.82:5555 && adb -s 45.55.191.82:5555 shell pm list packages | grep whatsapp
  3. Dry run (real vision):
    python3 wa_lookup.py --phone "+1 555 123 4567" --client-id 12345 --device 45.55.191.82:5555 --dry-run
    
    Expect screenshot in client/output/ and a real extraction (or not_found).
  4. Real record: omit --dry-run against a throwaway client id → 201, then confirm the whatsapp check 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.