# @vojo/widget-whatsapp

Vojo WhatsApp bridge management widget — mounts inside `/bots/whatsapp`
in the Vojo client. Drives the mautrix-whatsapp bridge bot
(`@whatsappbot:vojo.chat`) by sending bridgev2 commands in the control DM
and rendering the bot's text replies into a typed login flow.

This is **not** a WhatsApp client — Vojo continues using the Matrix room
the bridge writes to. The widget is a panel that handles authentication
(QR scan or pairing code) and surfaces session status.

## Layout

```
src/
├── bootstrap.ts                         Parse URL params the host appends (mirrors BotWidgetEmbed.ts)
├── widget-api.ts                        Inline matrix-widget-api postMessage transport (no SDK)
├── App.tsx                              UI: login forms, QR / pairing-code panels, transcript pane
├── main.tsx                             Entry: init bootstrap, render App or diagnostic
├── styles.css                           Theme-aware CSS (Vojo Dawn palette)
├── state.ts                             Login state machine + hydrate-from-timeline
├── i18n/                                Russian primary + English fallback
└── bridge-protocol/
    ├── types.ts                         LoginEvent discriminated union
    ├── parser.ts                        Dispatch shim
    └── dialects/
        └── bridgev2_v0264.ts            Regex table pinned to mautrix-whatsapp v0.26.4
```

## Login flows

WhatsApp's mautrix bridge ships TWO login flows (see
`pkg/connector/login.go::GetLoginFlows`):

1. **QR** (`!wa login qr`) — bridge emits a rotating `m.image` whose body
   is the raw whatsmeow handshake payload (`<ref>,<noise>,<identity>,<adv>`,
   four base64 fields). The widget renders it as a QR matrix client-side.
   Whatsmeow `qrIntervals = [60s, 20s, 20s, 20s, 20s, 20s]` — first QR
   lasts 60 seconds, then five rotations of 20 seconds each. Total active
   window: 2 minutes 40 seconds. Each rotation arrives as an `m.replace`
   edit of the original event; the state machine matches on the original
   id and repaints the matrix.

2. **Pairing code** (`!wa login phone`) — alternative for users whose
   camera doesn't work or who prefer typing. The user enters a phone
   number; the bridge replies with two notices:
   - `Input the pairing code in the WhatsApp mobile app to log in`
   - The 8-character code itself (`XXXX-XXXX`, custom base32 alphabet).
   The widget renders the code prominently and the user enters it in
   WhatsApp → Settings → Linked devices → Link with phone number.

There is **no 2FA cloud-password step** — multidevice handshake is
single-factor. The state machine has no `awaiting_password` arm.

## Capability contract

The widget requests EXACTLY this set (matches the host's
`BotWidgetDriver.getBotWidgetCapabilities`):

```
org.matrix.msc2762.timeline:<roomId>
org.matrix.msc2762.send.event:m.room.message#m.text
org.matrix.msc2762.receive.event:m.room.message#m.text
org.matrix.msc2762.receive.event:m.room.message#m.notice
org.matrix.msc2762.receive.event:m.room.message#m.image
org.matrix.msc2762.receive.event:m.room.redaction
org.matrix.msc2762.receive.state_event:m.room.member
```

Anything else is silently dropped by the host. The capability set is
identical to the Telegram widget's M13 expansion — the host driver
already supports `m.image` + `m.room.redaction`.

## Local development

```bash
cd apps/widget-whatsapp && npm install
cd /home/ubuntu/projects/vojo/cinny && cat > config.local.json <<'JSON'
{
  "bots": [
    { "id": "whatsapp", "experience": { "type": "matrix-widget", "url": "http://localhost:8083/" } }
  ]
}
JSON
```

Run both servers:

```bash
# terminal 1 — widget on :8083 with HMR
cd apps/widget-whatsapp && npm run dev

# terminal 2 — host SPA on :8080
cd /home/ubuntu/projects/vojo/cinny && npm start
```

Open `http://localhost:8080/bots/whatsapp`. The host's URL validator
accepts `http://localhost:*` only in dev builds.

## Build

```bash
npm run build
```

Outputs to `apps/widget-whatsapp/dist/`. Deploy by rsyncing `dist/*` into
`~/vojo/widgets/whatsapp/` on the production host. The VSCode task
`Deploy widgets` already includes the third subshell — running it from
the host root pushes all three widgets in sequence.

## Capacitor (Android)

`capacitor.config.ts` already allows `widgets.vojo.chat` for the existing
TG / Discord widgets — no extra entry needed for WhatsApp.

## Hosting (server-side)

Same Caddy `widgets.vojo.chat` block as the other widgets — add a third
`handle_path /whatsapp/* { … }` block alongside `/telegram/*` and
`/discord/*`. Then `mkdir -p ~/vojo/widgets/whatsapp` on the server, run
the deploy task, and verify with
`curl -I https://widgets.vojo.chat/whatsapp/index.html`.

## Source-of-truth pointers

- mautrix-whatsapp connector: <https://github.com/mautrix/whatsapp/blob/main/pkg/connector/login.go>
- mautrix-whatsapp connector (post-login session events):
  <https://github.com/mautrix/whatsapp/blob/main/pkg/connector/handlewhatsapp.go>
- whatsmeow QR format: <https://github.com/tulir/whatsmeow/blob/main/pair.go> (`makeQRData`)
- whatsmeow pairing-code: <https://github.com/tulir/whatsmeow/blob/main/pair-code.go> (`PairPhone`)
- bridgev2 commands layer (shared with mautrix-telegram):
  <https://github.com/mautrix/go/blob/main/bridgev2/commands/login.go>

The dialect file `src/bridge-protocol/dialects/bridgev2_v0264.ts` has
inline upstream pointers per regex; when the bridge image is upgraded,
spot-check those pointers and either confirm the wording is still valid
or drop a sibling dialect file with new regexes.
