Files
openclaw/src/plugins/runtime/runtime-web-channel-plugin.ts
2026-04-03 13:08:39 +01:00

290 lines
11 KiB
TypeScript

import { createJiti } from "jiti";
type WebChannelHeavyRuntimeModule = typeof import("@openclaw/whatsapp/runtime-api.js");
type WebChannelLightRuntimeModule = typeof import("@openclaw/whatsapp/light-runtime-api.js");
import {
getDefaultLocalRoots as getDefaultLocalRootsImpl,
loadWebMedia as loadWebMediaImpl,
loadWebMediaRaw as loadWebMediaRawImpl,
optimizeImageToJpeg as optimizeImageToJpegImpl,
} from "../../media/web-media.js";
import {
loadPluginBoundaryModuleWithJiti,
resolvePluginRuntimeModulePath,
resolvePluginRuntimeRecord,
} from "./runtime-plugin-boundary.js";
const WEB_CHANNEL_PLUGIN_ID = "whatsapp";
type WebChannelPluginRecord = {
origin: string;
rootDir?: string;
source: string;
};
let cachedHeavyModulePath: string | null = null;
let cachedHeavyModule: WebChannelHeavyRuntimeModule | null = null;
let cachedLightModulePath: string | null = null;
let cachedLightModule: WebChannelLightRuntimeModule | null = null;
const jitiLoaders = new Map<boolean, ReturnType<typeof createJiti>>();
function resolveWebChannelPluginRecord(): WebChannelPluginRecord {
return resolvePluginRuntimeRecord(WEB_CHANNEL_PLUGIN_ID, () => {
throw new Error(
`web channel plugin runtime is unavailable: missing plugin '${WEB_CHANNEL_PLUGIN_ID}'`,
);
}) as WebChannelPluginRecord;
}
function resolveWebChannelRuntimeModulePath(
record: WebChannelPluginRecord,
entryBaseName: "light-runtime-api" | "runtime-api",
): string {
const modulePath = resolvePluginRuntimeModulePath(record, entryBaseName, () => {
throw new Error(
`web channel plugin runtime is unavailable: missing ${entryBaseName} for plugin '${WEB_CHANNEL_PLUGIN_ID}'`,
);
});
if (!modulePath) {
throw new Error(
`web channel plugin runtime is unavailable: missing ${entryBaseName} for plugin '${WEB_CHANNEL_PLUGIN_ID}'`,
);
}
return modulePath;
}
function loadCurrentHeavyModuleSync(): WebChannelHeavyRuntimeModule {
const modulePath = resolveWebChannelRuntimeModulePath(
resolveWebChannelPluginRecord(),
"runtime-api",
);
return loadPluginBoundaryModuleWithJiti<WebChannelHeavyRuntimeModule>(modulePath, jitiLoaders);
}
function loadWebChannelLightModule(): WebChannelLightRuntimeModule {
const modulePath = resolveWebChannelRuntimeModulePath(
resolveWebChannelPluginRecord(),
"light-runtime-api",
);
if (cachedLightModule && cachedLightModulePath === modulePath) {
return cachedLightModule;
}
const loaded = loadPluginBoundaryModuleWithJiti<WebChannelLightRuntimeModule>(
modulePath,
jitiLoaders,
);
cachedLightModulePath = modulePath;
cachedLightModule = loaded;
return loaded;
}
async function loadWebChannelHeavyModule(): Promise<WebChannelHeavyRuntimeModule> {
const record = resolveWebChannelPluginRecord();
const modulePath = resolveWebChannelRuntimeModulePath(record, "runtime-api");
if (cachedHeavyModule && cachedHeavyModulePath === modulePath) {
return cachedHeavyModule;
}
const loaded = loadPluginBoundaryModuleWithJiti<WebChannelHeavyRuntimeModule>(
modulePath,
jitiLoaders,
);
cachedHeavyModulePath = modulePath;
cachedHeavyModule = loaded;
return loaded;
}
function getLightExport<K extends keyof WebChannelLightRuntimeModule>(
exportName: K,
): NonNullable<WebChannelLightRuntimeModule[K]> {
const loaded = loadWebChannelLightModule();
const value = loaded[exportName];
if (value == null) {
throw new Error(`web channel plugin runtime is missing export '${String(exportName)}'`);
}
return value as NonNullable<WebChannelLightRuntimeModule[K]>;
}
async function getHeavyExport<K extends keyof WebChannelHeavyRuntimeModule>(
exportName: K,
): Promise<NonNullable<WebChannelHeavyRuntimeModule[K]>> {
const loaded = await loadWebChannelHeavyModule();
const value = loaded[exportName];
if (value == null) {
throw new Error(`web channel plugin runtime is missing export '${String(exportName)}'`);
}
return value as NonNullable<WebChannelHeavyRuntimeModule[K]>;
}
export function getActiveWebListener(
...args: Parameters<WebChannelLightRuntimeModule["getActiveWebListener"]>
): ReturnType<WebChannelLightRuntimeModule["getActiveWebListener"]> {
return getLightExport("getActiveWebListener")(...args);
}
export function getWebAuthAgeMs(
...args: Parameters<WebChannelLightRuntimeModule["getWebAuthAgeMs"]>
): ReturnType<WebChannelLightRuntimeModule["getWebAuthAgeMs"]> {
return getLightExport("getWebAuthAgeMs")(...args);
}
export function logWebSelfId(
...args: Parameters<WebChannelLightRuntimeModule["logWebSelfId"]>
): ReturnType<WebChannelLightRuntimeModule["logWebSelfId"]> {
return getLightExport("logWebSelfId")(...args);
}
export function loginWeb(
...args: Parameters<WebChannelHeavyRuntimeModule["loginWeb"]>
): ReturnType<WebChannelHeavyRuntimeModule["loginWeb"]> {
return loadWebChannelHeavyModule().then((loaded) => loaded.loginWeb(...args));
}
export function logoutWeb(
...args: Parameters<WebChannelLightRuntimeModule["logoutWeb"]>
): ReturnType<WebChannelLightRuntimeModule["logoutWeb"]> {
return getLightExport("logoutWeb")(...args);
}
export function readWebSelfId(
...args: Parameters<WebChannelLightRuntimeModule["readWebSelfId"]>
): ReturnType<WebChannelLightRuntimeModule["readWebSelfId"]> {
return getLightExport("readWebSelfId")(...args);
}
export function webAuthExists(
...args: Parameters<WebChannelLightRuntimeModule["webAuthExists"]>
): ReturnType<WebChannelLightRuntimeModule["webAuthExists"]> {
return getLightExport("webAuthExists")(...args);
}
export function sendWebChannelMessage(
...args: Parameters<WebChannelHeavyRuntimeModule["sendMessageWhatsApp"]>
): ReturnType<WebChannelHeavyRuntimeModule["sendMessageWhatsApp"]> {
return loadWebChannelHeavyModule().then((loaded) => loaded.sendMessageWhatsApp(...args));
}
export function sendWebChannelPoll(
...args: Parameters<WebChannelHeavyRuntimeModule["sendPollWhatsApp"]>
): ReturnType<WebChannelHeavyRuntimeModule["sendPollWhatsApp"]> {
return loadWebChannelHeavyModule().then((loaded) => loaded.sendPollWhatsApp(...args));
}
export function sendWebChannelReaction(
...args: Parameters<WebChannelHeavyRuntimeModule["sendReactionWhatsApp"]>
): ReturnType<WebChannelHeavyRuntimeModule["sendReactionWhatsApp"]> {
return loadWebChannelHeavyModule().then((loaded) => loaded.sendReactionWhatsApp(...args));
}
export function createRuntimeWebChannelLoginTool(
...args: Parameters<WebChannelLightRuntimeModule["createWhatsAppLoginTool"]>
): ReturnType<WebChannelLightRuntimeModule["createWhatsAppLoginTool"]> {
return getLightExport("createWhatsAppLoginTool")(...args);
}
export function createWebChannelSocket(
...args: Parameters<WebChannelHeavyRuntimeModule["createWaSocket"]>
): ReturnType<WebChannelHeavyRuntimeModule["createWaSocket"]> {
return loadWebChannelHeavyModule().then((loaded) => loaded.createWaSocket(...args));
}
export function formatError(
...args: Parameters<WebChannelLightRuntimeModule["formatError"]>
): ReturnType<WebChannelLightRuntimeModule["formatError"]> {
return getLightExport("formatError")(...args);
}
export function getStatusCode(
...args: Parameters<WebChannelLightRuntimeModule["getStatusCode"]>
): ReturnType<WebChannelLightRuntimeModule["getStatusCode"]> {
return getLightExport("getStatusCode")(...args);
}
export function pickWebChannel(
...args: Parameters<WebChannelLightRuntimeModule["pickWebChannel"]>
): ReturnType<WebChannelLightRuntimeModule["pickWebChannel"]> {
return getLightExport("pickWebChannel")(...args);
}
export function resolveWebChannelAuthDir(): WebChannelLightRuntimeModule["WA_WEB_AUTH_DIR"] {
return getLightExport("WA_WEB_AUTH_DIR");
}
export async function handleWebChannelAction(
...args: Parameters<WebChannelHeavyRuntimeModule["handleWhatsAppAction"]>
): ReturnType<WebChannelHeavyRuntimeModule["handleWhatsAppAction"]> {
return (await getHeavyExport("handleWhatsAppAction"))(...args);
}
export async function loadWebMedia(
...args: Parameters<typeof loadWebMediaImpl>
): ReturnType<typeof loadWebMediaImpl> {
return await loadWebMediaImpl(...args);
}
export async function loadWebMediaRaw(
...args: Parameters<typeof loadWebMediaRawImpl>
): ReturnType<typeof loadWebMediaRawImpl> {
return await loadWebMediaRawImpl(...args);
}
export function monitorWebChannel(
...args: Parameters<WebChannelHeavyRuntimeModule["monitorWebChannel"]>
): ReturnType<WebChannelHeavyRuntimeModule["monitorWebChannel"]> {
return loadWebChannelHeavyModule().then((loaded) => loaded.monitorWebChannel(...args));
}
export async function monitorWebInbox(
...args: Parameters<WebChannelHeavyRuntimeModule["monitorWebInbox"]>
): ReturnType<WebChannelHeavyRuntimeModule["monitorWebInbox"]> {
return (await getHeavyExport("monitorWebInbox"))(...args);
}
export async function optimizeImageToJpeg(
...args: Parameters<typeof optimizeImageToJpegImpl>
): ReturnType<typeof optimizeImageToJpegImpl> {
return await optimizeImageToJpegImpl(...args);
}
export async function runWebHeartbeatOnce(
...args: Parameters<WebChannelHeavyRuntimeModule["runWebHeartbeatOnce"]>
): ReturnType<WebChannelHeavyRuntimeModule["runWebHeartbeatOnce"]> {
return (await getHeavyExport("runWebHeartbeatOnce"))(...args);
}
export async function startWebLoginWithQr(
...args: Parameters<WebChannelHeavyRuntimeModule["startWebLoginWithQr"]>
): ReturnType<WebChannelHeavyRuntimeModule["startWebLoginWithQr"]> {
return (await getHeavyExport("startWebLoginWithQr"))(...args);
}
export async function waitForWebChannelConnection(
...args: Parameters<WebChannelHeavyRuntimeModule["waitForWaConnection"]>
): ReturnType<WebChannelHeavyRuntimeModule["waitForWaConnection"]> {
return (await getHeavyExport("waitForWaConnection"))(...args);
}
export async function waitForWebLogin(
...args: Parameters<WebChannelHeavyRuntimeModule["waitForWebLogin"]>
): ReturnType<WebChannelHeavyRuntimeModule["waitForWebLogin"]> {
return (await getHeavyExport("waitForWebLogin"))(...args);
}
export const extractMediaPlaceholder = (
...args: Parameters<WebChannelHeavyRuntimeModule["extractMediaPlaceholder"]>
) => loadCurrentHeavyModuleSync().extractMediaPlaceholder(...args);
export const extractText = (...args: Parameters<WebChannelHeavyRuntimeModule["extractText"]>) =>
loadCurrentHeavyModuleSync().extractText(...args);
export function getDefaultLocalRoots(
...args: Parameters<typeof getDefaultLocalRootsImpl>
): ReturnType<typeof getDefaultLocalRootsImpl> {
return getDefaultLocalRootsImpl(...args);
}
export function resolveHeartbeatRecipients(
...args: Parameters<WebChannelHeavyRuntimeModule["resolveHeartbeatRecipients"]>
): ReturnType<WebChannelHeavyRuntimeModule["resolveHeartbeatRecipients"]> {
return loadCurrentHeavyModuleSync().resolveHeartbeatRecipients(...args);
}