69 lines
2.4 KiB
TypeScript
69 lines
2.4 KiB
TypeScript
// Parse the URL params the Phase 2 bot widget host appends when loading
|
|
// experience.url. Source of truth on the host side:
|
|
// src/app/features/bots/BotWidgetEmbed.ts (getBotWidgetUrl).
|
|
// Keep this in sync if the host adds params.
|
|
//
|
|
// Identical shape to apps/widget-telegram/src/bootstrap.ts on purpose —
|
|
// the host emits the same param set for every bot. Differences between
|
|
// telegram and discord live in the bridge protocol, not in bootstrap.
|
|
|
|
export type WidgetBootstrap = {
|
|
widgetId: string;
|
|
parentUrl: string;
|
|
parentOrigin: string;
|
|
roomId: string;
|
|
userId: string;
|
|
botId: string;
|
|
botMxid: string;
|
|
/** Bridge command prefix (e.g. `!discord`). Always non-empty — the host
|
|
* validator (catalog.ts) defaults missing values to `!tg` and rejects
|
|
* malformed overrides, so the discord bot's /config.json entry MUST set
|
|
* `experience.commandPrefix: "!discord"` to override the default. The
|
|
* widget prepends `<commandPrefix> ` to every outbound command. */
|
|
commandPrefix: string;
|
|
theme: 'light' | 'dark';
|
|
clientLanguage: string;
|
|
};
|
|
|
|
export type BootstrapResult =
|
|
| { ok: true; bootstrap: WidgetBootstrap }
|
|
| { ok: false; missing: string[] };
|
|
|
|
const REQUIRED = ['widgetId', 'parentUrl', 'roomId', 'userId', 'botMxid', 'commandPrefix'] as const;
|
|
|
|
export const readBootstrap = (search: string): BootstrapResult => {
|
|
const params = new URLSearchParams(search);
|
|
const get = (k: string) => params.get(k) ?? '';
|
|
|
|
const missing = REQUIRED.filter((k) => !params.get(k));
|
|
if (missing.length > 0) return { ok: false, missing: [...missing] };
|
|
|
|
// Origin is what the widget validates against on incoming postMessage —
|
|
// see widget-api.ts. Falling back to '*' would defeat the security
|
|
// boundary, so a malformed parentUrl bails out as a missing-param error.
|
|
let parentOrigin: string;
|
|
try {
|
|
parentOrigin = new URL(get('parentUrl')).origin;
|
|
} catch {
|
|
return { ok: false, missing: ['parentUrl'] };
|
|
}
|
|
|
|
const themeRaw = get('theme');
|
|
const theme: 'light' | 'dark' = themeRaw === 'dark' ? 'dark' : 'light';
|
|
|
|
return {
|
|
ok: true,
|
|
bootstrap: {
|
|
widgetId: get('widgetId'),
|
|
parentUrl: get('parentUrl'),
|
|
parentOrigin,
|
|
roomId: get('roomId'),
|
|
userId: get('userId'),
|
|
botId: get('botId'),
|
|
botMxid: get('botMxid'),
|
|
commandPrefix: get('commandPrefix'),
|
|
theme,
|
|
clientLanguage: get('clientLanguage'),
|
|
},
|
|
};
|
|
};
|