176 lines
5.5 KiB
Markdown
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.
|