vojo/apps/widget-telegram/README.md

176 lines
5.5 KiB
Markdown

# @vojo/widget-telegram
Vojo Telegram bridge management widget — mounts inside `/bots/telegram`
in the Vojo client. See [`docs/plans/bots_tab.md`](../../docs/plans/bots_tab.md)
Phase 3 for product context and the matrix-widget-api contract.
This is **not** a Telegram client. It's a small panel that drives the
mautrix-telegram bridge bot (`@telegrambot:vojo.chat`) by sending text
commands in the control DM and rendering the bot's text replies. M11
ships only the bootstrap + a `ping` button to verify the host handshake.
## Layout
```
src/
├── bootstrap.ts Parse URL params the host appends (matches BotWidgetEmbed.ts)
├── widget-api.ts Inline matrix-widget-api postMessage transport (no SDK)
├── App.tsx UI: bootstrap card, action buttons, transcript pane
├── main.tsx Entry: init bootstrap, render App or diagnostic
└── styles.css Theme-aware CSS variables
```
## Local development
**Don't touch the committed `config.json`.** Create `config.local.json` at
the project root once — gitignored, never deployed. The host's Vite dev
server overlays it on top of `/config.json` responses (see
`serveLocalConfigOverlay` in `vite.config.js`); prod builds ignore the
overlay entirely.
```bash
# one-time: install widget deps
cd apps/widget-telegram && npm install
# one-time: create config.local.json (gitignored) at the project root
cat > /home/ubuntu/projects/vojo/cinny/config.local.json <<'JSON'
{
"bots": [
{
"id": "telegram",
"experience": {
"type": "matrix-widget",
"url": "http://localhost:8081/"
}
}
]
}
JSON
```
The overlay merges `bots[]` by `id`, so just `{ id, experience }` is
enough — base bot's `mxid` and `name` are preserved. Top-level fields
not present in `config.local.json` are inherited from `config.json`.
Run both servers:
```bash
# terminal 1 — widget on :8081 with HMR
cd apps/widget-telegram && npm run dev
# terminal 2 — host SPA on :8080
cd /home/ubuntu/projects/vojo/cinny && npm start
```
Open `http://localhost:8080/bots/telegram`. Iframe loads cross-origin
from the widget dev server, HMR works, no proxy.
`http://localhost:*` URLs are accepted by the host's URL validator only
in dev builds (`import.meta.env.DEV` branch in
`src/app/features/bots/catalog.ts`); production builds drop the branch
via Vite's dead-code elimination, AND production-only enforces an origin
allowlist (`PROD_WIDGET_ORIGINS`) so prod can never embed `localhost` even
if config.json is poisoned.
Deploy is unchanged. `config.local.json` is gitignored, never shipped.
You don't need to revert anything before `Deploy to vojo.chat` — there
is nothing in tracked files that points at localhost.
Standalone preview of the widget bundle (no host, useful for visual
iteration):
```bash
cd apps/widget-telegram
npm run dev # vite dev server on :8081 — shows missing-params banner
# without host, expected.
npm run preview # serves the production build from dist/
```
## Build
```bash
npm run build
```
Outputs to `apps/widget-telegram/dist/`. Deploy by rsyncing `dist/*`
into `~/vojo/widgets/telegram/` on the production host (Caddy serves
this via the `widgets.vojo.chat` block). One parent `~/vojo/widgets/`
directory hosts every bot widget — adding a second one is `mkdir
~/vojo/widgets/<slug>/` plus a Caddy block, no docker-compose edit.
## Hosting (server-side, runbook)
1. DNS: `widgets.vojo.chat` A/AAAA → server. Verify with `dig`.
2. `~/vojo/docker-compose.yml` — Caddy `volumes:` adds (one parent mount,
future widgets reuse it):
```yaml
- ./widgets:/var/www/widgets
```
3. `~/vojo/caddy/Caddyfile` — append:
```
widgets.vojo.chat {
encode zstd gzip
header {
Content-Security-Policy "frame-ancestors https://vojo.chat https://localhost"
X-Content-Type-Options "nosniff"
Referrer-Policy "no-referrer"
Cache-Control "no-cache, no-store, must-revalidate"
-Server
}
handle_path /telegram/* {
root * /var/www/widgets/telegram
try_files {path} /index.html
file_server
}
handle {
respond "Not Found" 404
}
}
```
4. `mkdir -p ~/vojo/widgets/telegram` (placeholder so cert provisioning
has something to serve), then `docker compose up -d caddy` to apply.
5. Verify directly: `curl -I https://widgets.vojo.chat/telegram/index.html`
should return 200 and the `Content-Security-Policy` header.
## Updating the production /config.json
Once the widget is live at `https://widgets.vojo.chat/telegram/index.html`,
add to the host repo's `config.json`:
```json
"experience": {
"type": "matrix-widget",
"url": "https://widgets.vojo.chat/telegram/index.html"
}
```
## Capacitor (Android)
`capacitor.config.ts` already has a placeholder. Uncomment and set:
```ts
server: { allowNavigation: ['widgets.vojo.chat'] }
```
Without this, Android's WebView hijacks the cross-origin iframe URL into
`Intent.ACTION_VIEW` and the iframe stays blank. Rebuild the APK after.
## 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.state_event:m.room.member
```
Anything else is silently dropped by the host. To extend the surface,
update `BotWidgetDriver.ts` upstream — that requires a security review
per Phase 2 plan §M9.