diff --git a/src/server/main.ts b/src/server/main.ts index 1798a22..d0eed26 100644 --- a/src/server/main.ts +++ b/src/server/main.ts @@ -11,7 +11,8 @@ const wss = new WebSocketServer({ port: PORT, }); -let whatsapp: WebSocket | undefined; +const bridgeSockets = new Set(); +const clientSockets = new Set(); const pending = new Map< string, @@ -26,11 +27,24 @@ function requestId() { return crypto.randomUUID(); } +function getWhatsApp(): WebSocket | undefined { + for (const ws of bridgeSockets) { + if (ws.readyState === WebSocket.OPEN) { + return ws; + } + + bridgeSockets.delete(ws); + } + + return undefined; +} + function call( method: BridgeRequest["method"], params?: unknown, ): Promise { - if (!whatsapp || whatsapp.readyState !== WebSocket.OPEN) { + const wa = getWhatsApp(); + if (!wa || wa.readyState !== WebSocket.OPEN) { throw new Error("WhatsApp Web is not connected"); } @@ -49,7 +63,7 @@ function call( timer, }); - whatsapp!.send( + wa!.send( JSON.stringify({ type: "request", id, @@ -94,6 +108,11 @@ function handleWhatsAppMessage(data: string) { // console.log("[WhatsApp event]", message.event, message.data); if (message.event === "message") { + // forward to clients + clientSockets.forEach((ws) => { + ws.send(JSON.stringify(message)); + }); + const m = message.data as MessageDTO; insertMessage({ @@ -109,15 +128,14 @@ function handleWhatsAppMessage(data: string) { } wss.on("connection", (ws) => { - console.log("[WS] client connected"); + console.log("[WS] socket connected"); - /* - * First connection from Tampermonkey is the - * WhatsApp bridge. - */ - if (!whatsapp && !ws.url.includes("?client=1")) { - whatsapp = ws; + const url = new URL(ws.url ?? "", "http://localhost"); + const isBridge = + !url.searchParams.get("client") || url.searchParams.get("bridge") === "1"; + if (isBridge) { + bridgeSockets.add(ws); console.log("[WS] WhatsApp bridge attached"); ws.on("message", (data) => { @@ -125,32 +143,23 @@ wss.on("connection", (ws) => { }); ws.on("close", () => { - if (whatsapp === ws) { - whatsapp = undefined; - } - - console.log("[WS] WhatsApp disconnected"); + bridgeSockets.delete(ws); + console.log("[WS] WhatsApp bridge disconnected"); }); + ws.on("error", () => { + bridgeSockets.delete(ws); + }); return; } - /* - * This is an external application/client. - * - * Example: - * my backend - * CLI - * React application - * another service - */ + clientSockets.add(ws); ws.on("message", async (data) => { let request: any; try { request = JSON.parse(data.toString()) as BridgeRequest; - if (request.type !== "request") { return; } @@ -214,8 +223,13 @@ wss.on("connection", (ws) => { }); ws.on("close", () => { + clientSockets.delete(ws); console.log("[WS] external client disconnected"); }); + + ws.on("error", () => { + clientSockets.delete(ws); + }); }); initDb(); diff --git a/src/userscript/index.ts b/src/userscript/index.ts index 629c0d5..0f1c9a8 100644 --- a/src/userscript/index.ts +++ b/src/userscript/index.ts @@ -7,7 +7,7 @@ import { serializeMessage, } from "./serializer"; -const WS_URL = "wss://wa-bridge.rul.sh/ws"; +const WS_URL = "wss://wa-bridge.rul.sh/ws?bridge=1"; let socket: WebSocket | undefined;