diff --git a/dist/whatsapp-bridge.user.js b/dist/whatsapp-bridge.user.js index f9f2eac..3908b91 100644 --- a/dist/whatsapp-bridge.user.js +++ b/dist/whatsapp-bridge.user.js @@ -226,7 +226,7 @@ } // src/userscript/index.ts - var WS_URL = "ws://127.0.0.1:8787"; + var WS_URL = "wss://wa-bridge.rul.sh/ws"; var socket; function send(message) { if (socket?.readyState !== WebSocket.OPEN) { diff --git a/dist/whatsapp-bridge.user.js.map b/dist/whatsapp-bridge.user.js.map index 7c5f379..508974e 100644 --- a/dist/whatsapp-bridge.user.js.map +++ b/dist/whatsapp-bridge.user.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../src/userscript/serializer.ts", "../src/userscript/index.ts"], - "sourcesContent": ["import type {\r\n ChatDTO,\r\n ContactDTO,\r\n GroupMetadataDTO,\r\n GroupParticipantDTO,\r\n LocationDTO,\r\n MessageDTO,\r\n MessageIdDTO,\r\n SerializedId,\r\n} from \"../shared/dto\";\r\n\r\n/**\r\n * WA models contain a lot of internal properties and sometimes\r\n * circular references. Never JSON.stringify them directly.\r\n */\r\nexport function serialize(value: any): any {\r\n if (value == null) {\r\n return value;\r\n }\r\n\r\n if (Array.isArray(value)) {\r\n return value.map(serialize);\r\n }\r\n\r\n if (typeof value !== \"object\") {\r\n return value;\r\n }\r\n\r\n const result: Record = {};\r\n\r\n for (const key of Object.keys(value)) {\r\n const v = value[key];\r\n\r\n if (\r\n typeof v === \"string\" ||\r\n typeof v === \"number\" ||\r\n typeof v === \"boolean\" ||\r\n v === null\r\n ) {\r\n result[key] = v;\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction safeProperty(object: unknown, key: string): T | undefined {\r\n try {\r\n if (object && typeof object === \"object\") {\r\n return (object as any)[key];\r\n }\r\n } catch {\r\n return undefined;\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\nexport function safeGet(\r\n object: unknown,\r\n key: string,\r\n): T | undefined {\r\n if (object == null || typeof object !== \"object\") {\r\n return undefined;\r\n }\r\n\r\n try {\r\n return (object as any)[key];\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\nexport function pick(\r\n source: any,\r\n keys: readonly string[],\r\n): Partial {\r\n const result: Record = {};\r\n\r\n for (const key of keys) {\r\n const value = safeGet(source, key);\r\n\r\n if (value !== undefined) {\r\n result[key] = serialize(value);\r\n }\r\n }\r\n\r\n return result as Partial;\r\n}\r\n\r\nexport function serializeId(id: any): SerializedId | string | null {\r\n if (id == null) {\r\n return null;\r\n }\r\n\r\n if (typeof id === \"string\") {\r\n return id;\r\n }\r\n\r\n if (typeof id !== \"object\") {\r\n return String(id);\r\n }\r\n\r\n const result: SerializedId = {};\r\n\r\n for (const key of [\r\n \"_serialized\",\r\n \"user\",\r\n \"server\",\r\n \"domain\",\r\n \"device\",\r\n \"agent\",\r\n \"fromMe\",\r\n \"remote\",\r\n \"participant\",\r\n ]) {\r\n const value = safeGet(id, key);\r\n\r\n if (value !== undefined) {\r\n result[key] = serialize(value) as any;\r\n }\r\n }\r\n\r\n // Some WA objects expose useful fields that aren't\r\n // enumerable.\r\n if (!result._serialized && typeof safeGet(id, \"toString\") === \"function\") {\r\n try {\r\n const serialized = id.toString();\r\n\r\n if (serialized && serialized !== \"[object Object]\") {\r\n result._serialized = serialized;\r\n }\r\n } catch {}\r\n }\r\n\r\n return Object.keys(result).length ? result : String(id);\r\n}\r\n\r\nexport function serializeChat(chat: any): ChatDTO {\r\n return {\r\n id: serializeId(chat.id),\r\n accountLid: chat.accountLid,\r\n name: chat.name,\r\n formattedTitle: chat.formattedTitle,\r\n\r\n isGroup: chat.isGroup,\r\n isUser: chat.isUser,\r\n isReadOnly: chat.isReadOnly,\r\n archived: chat.archived,\r\n unreadCount: chat.unreadCount,\r\n\r\n timestamp: chat.t,\r\n lastMessage: chat.lastMessage ? serialize(chat.lastMessage) : null,\r\n };\r\n}\r\n\r\nexport function serializeContact(contact: any): ContactDTO {\r\n return {\r\n id: serializeId(contact.id),\r\n\r\n accountLid: contact.accountLid ?? null,\r\n name: contact.name ?? null,\r\n pushname: contact.pushname ?? null,\r\n shortName: contact.shortName ?? null,\r\n formattedName: contact.formattedName ?? null,\r\n number: contact.number ?? null,\r\n\r\n isMe: contact.isMe ?? false,\r\n isUser: contact.isUser ?? false,\r\n isBusiness: contact.isBusiness ?? false,\r\n isEnterprise: contact.isEnterprise ?? false,\r\n isGroup: contact.isGroup ?? false,\r\n isMyContact: contact.isMyContact ?? false,\r\n isBlocked: contact.isBlocked ?? false,\r\n\r\n profilePicThumbObj: contact.profilePicThumbObj\r\n ? serialize(contact.profilePicThumbObj)\r\n : undefined,\r\n };\r\n}\r\n\r\nexport function serializeGroupParticipant(\r\n participant: any,\r\n): GroupParticipantDTO {\r\n return {\r\n id: serializeId(participant.id),\r\n\r\n isAdmin: participant.isAdmin ?? false,\r\n isSuperAdmin: participant.isSuperAdmin ?? false,\r\n isSuperParticipant: participant.isSuperParticipant ?? false,\r\n isBusiness: participant.isBusiness ?? false,\r\n };\r\n}\r\n\r\nexport function serializeGroupMetadata(metadata: any): GroupMetadataDTO {\r\n return {\r\n id: serializeId(metadata.id),\r\n\r\n subject: metadata.subject ?? null,\r\n subjectOwner: serializeId(metadata.subjectOwner),\r\n subjectTime: metadata.subjectTime ?? null,\r\n creation: metadata.creation ?? null,\r\n owner: serializeId(metadata.owner),\r\n\r\n participants: Array.isArray(metadata.participants)\r\n ? (metadata.participants\r\n .map(serializeGroupParticipant)\r\n .filter(Boolean) as GroupParticipantDTO[])\r\n : [],\r\n\r\n size: metadata.size ?? metadata.participants?.length ?? 0,\r\n restrict: metadata.restrict ?? false,\r\n announce: metadata.announce ?? false,\r\n isCommunity: metadata.isCommunity ?? false,\r\n isCommunityAnnounce: metadata.isCommunityAnnounce ?? false,\r\n };\r\n}\r\nexport function serializeMessageId(id: any): MessageIdDTO | null {\r\n if (!id) {\r\n return null;\r\n }\r\n\r\n return {\r\n id: id.id ?? null,\r\n _serialized: id._serialized ?? null,\r\n fromMe: id.fromMe ?? false,\r\n remote: serializeId(id.remote),\r\n participant: serializeId(id.participant),\r\n };\r\n}\r\nexport function serializeLocation(location: any): LocationDTO | null {\r\n if (!location) {\r\n return null;\r\n }\r\n\r\n return {\r\n latitude: location.latitude ?? null,\r\n longitude: location.longitude ?? null,\r\n description: location.description ?? null,\r\n address: location.address ?? null,\r\n url: location.url ?? null,\r\n name: location.name ?? null,\r\n };\r\n}\r\nexport function serializeMessageLink(link: any): any {\r\n return {\r\n link: link?.link ?? null,\r\n isSuspicious: link?.isSuspicious ?? false,\r\n };\r\n}\r\nexport function serializeMessage(message: any): MessageDTO {\r\n const quotedMsgId = safeProperty(message, \"quotedMsgId\");\r\n const hasQuoted = !!quotedMsgId;\r\n let quotedMsg = null;\r\n\r\n if (hasQuoted) {\r\n const rawQuoted = safeProperty(message, \"quotedMsg\");\r\n if (rawQuoted) {\r\n quotedMsg = serializeMessage(rawQuoted);\r\n }\r\n }\r\n\r\n return {\r\n id: serializeMessageId(message.id)!,\r\n chatId: message.chatId ?? message.id?.remote?._serialized ?? null,\r\n from: serializeId(message.from),\r\n to: serializeId(message.to),\r\n author: serializeId(message.author),\r\n fromMe: message.fromMe ?? false,\r\n body: message.body ?? null,\r\n caption: message.caption ?? null,\r\n type: message.type ?? null,\r\n subtype: message.subtype ?? null,\r\n timestamp: message.timestamp ?? null,\r\n ack: message.ack ?? null,\r\n hasMedia: message.hasMedia ?? false,\r\n mediaKey: message.mediaKey ?? null,\r\n isForwarded: message.isForwarded ?? false,\r\n forwardingScore: message.forwardingScore ?? 0,\r\n isStarred: message.isStarred ?? false,\r\n broadcast: message.broadcast ?? false,\r\n mentionedIds: Array.isArray(message.mentionedIds)\r\n ? message.mentionedIds.map(serializeId)\r\n : [],\r\n quotedMsgId: serializeMessageId(quotedMsgId),\r\n quotedMsg,\r\n links: Array.isArray(message.links)\r\n ? message.links.map(serializeMessageLink)\r\n : [],\r\n location: serializeLocation(message.location),\r\n vCard: message.vCard ?? null,\r\n vCards: Array.isArray(message.vCards) ? message.vCards : [],\r\n filename: message.filename ?? null,\r\n mimetype: message.mimetype ?? null,\r\n size: message.size ?? null,\r\n duration: message.duration ?? null,\r\n };\r\n}\r\n", "import type { BridgeRequest } from \"../shared/protocol\";\r\nimport {\r\n serialize,\r\n serializeChat,\r\n serializeContact,\r\n serializeGroupMetadata,\r\n serializeMessage,\r\n} from \"./serializer\";\r\n\r\nconst WS_URL = \"ws://127.0.0.1:8787\";\r\n\r\nlet socket: WebSocket | undefined;\r\n\r\nfunction send(message: unknown) {\r\n if (socket?.readyState !== WebSocket.OPEN) {\r\n throw new Error(\"WebSocket is not connected\");\r\n }\r\n\r\n socket.send(JSON.stringify(message));\r\n}\r\n\r\nfunction reply(id: string, result: unknown) {\r\n send({\r\n type: \"response\",\r\n id,\r\n ok: true,\r\n result: serialize(result),\r\n });\r\n}\r\n\r\nfunction error(id: string, err: unknown) {\r\n send({\r\n type: \"response\",\r\n id,\r\n ok: false,\r\n error: {\r\n code: \"ERROR\",\r\n message: err instanceof Error ? err.message : String(err),\r\n },\r\n });\r\n}\r\n\r\nasync function handleRequest(request: BridgeRequest) {\r\n switch (request.method) {\r\n case \"status\":\r\n return {\r\n authenticated: WPP.conn.isAuthenticated(),\r\n ready: WPP.isReady,\r\n };\r\n\r\n case \"listChats\": {\r\n const chats = await WPP.chat.list({\r\n ...(request.params?.limit != null\r\n ? { count: request.params.limit }\r\n : {}),\r\n ...(request.params?.groupsOnly ? { onlyGroups: true } : {}),\r\n ...(request.params?.usersOnly ? { onlyUsers: true } : {}),\r\n });\r\n\r\n return chats.filter(Boolean).map(serializeChat);\r\n }\r\n\r\n case \"listContacts\": {\r\n const contacts = await WPP.contact.getAllContacts();\r\n\r\n const result = request.params?.limit\r\n ? contacts.slice(0, request.params.limit)\r\n : contacts;\r\n\r\n return result.filter(Boolean).map(serializeContact);\r\n }\r\n\r\n case \"listGroups\": {\r\n const groups = await WPP.group.getAllGroups();\r\n\r\n return groups.filter(Boolean).map(serializeGroupMetadata);\r\n }\r\n\r\n case \"getMessages\": {\r\n const messages = await WPP.chat.getMessages(request.params.chatId, {\r\n count: request.params.count ?? 50,\r\n direction: request.params.direction,\r\n id: request.params.id,\r\n });\r\n\r\n return messages.filter(Boolean).map(serializeMessage);\r\n }\r\n\r\n case \"searchMessages\":\r\n return searchMessages(\r\n request.params.query,\r\n request.params.chatId,\r\n request.params.limit ?? 100,\r\n );\r\n\r\n case \"sendText\": {\r\n const result = await WPP.chat.sendTextMessage(\r\n request.params.chatId,\r\n request.params.text,\r\n {\r\n delay: request.params.options?.delay,\r\n quotedMsg: request.params.options?.quotedMsg,\r\n mentionedList: request.params.options?.mentionedList,\r\n waitForAck: request.params.options?.waitForAck,\r\n },\r\n );\r\n\r\n return serialize(result);\r\n }\r\n\r\n default:\r\n throw new Error(`Unknown method: ${(request as any).method}`);\r\n }\r\n}\r\n\r\n/**\r\n * WA-JS doesn't need a separate \"global search\" API for our\r\n * first implementation. Search the messages loaded from chats.\r\n *\r\n * Later we can optimize this using WhatsApp's internal message\r\n * store/index if necessary.\r\n */\r\nasync function searchMessages(query: string, chatId?: string, limit = 100) {\r\n const normalized = query.toLowerCase();\r\n\r\n let chats;\r\n\r\n if (chatId) {\r\n const chat = WPP.chat.get(chatId);\r\n chats = chat ? [chat] : [];\r\n } else {\r\n chats = await WPP.chat.list();\r\n }\r\n\r\n const results: any[] = [];\r\n\r\n for (const chat of chats) {\r\n if (results.length >= limit) {\r\n break;\r\n }\r\n\r\n const id = chat?.id?._serialized ?? chat?.id ?? chat?.wid?._serialized;\r\n\r\n if (!id) {\r\n continue;\r\n }\r\n\r\n try {\r\n const messages = await WPP.chat.getMessages(id, {\r\n count: 100,\r\n });\r\n\r\n for (const message of messages) {\r\n const body = String(message.body ?? message.caption ?? \"\");\r\n\r\n if (body.toLowerCase().includes(normalized)) {\r\n results.push({\r\n chatId: id,\r\n message: serialize(message),\r\n });\r\n\r\n if (results.length >= limit) {\r\n break;\r\n }\r\n }\r\n }\r\n } catch (err) {\r\n console.warn(\"Failed searching chat\", id, err);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\nfunction connect() {\r\n if (\r\n socket &&\r\n (socket.readyState === WebSocket.OPEN ||\r\n socket.readyState === WebSocket.CONNECTING)\r\n ) {\r\n return;\r\n }\r\n\r\n socket = new WebSocket(WS_URL);\r\n\r\n socket.onopen = () => {\r\n console.log(\"[WA Bridge] connected\");\r\n\r\n send({\r\n type: \"event\",\r\n event: \"ready\",\r\n data: {\r\n authenticated: WPP.conn.isAuthenticated(),\r\n },\r\n });\r\n };\r\n\r\n socket.onmessage = async (event) => {\r\n try {\r\n const request = JSON.parse(event.data) as BridgeRequest;\r\n\r\n if (request.type !== \"request\") {\r\n return;\r\n }\r\n\r\n try {\r\n const result = await handleRequest(request);\r\n\r\n reply(request.id, JSON.stringify(result));\r\n } catch (err) {\r\n error(request.id, err);\r\n }\r\n } catch (err) {\r\n console.error(\"[WA Bridge] invalid message\", err);\r\n }\r\n };\r\n\r\n socket.onclose = (event) => {\r\n console.log(\"[WA Bridge] disconnected\", {\r\n code: event.code,\r\n reason: event.reason,\r\n wasClean: event.wasClean,\r\n });\r\n\r\n socket = undefined;\r\n setTimeout(connect, 3000);\r\n };\r\n\r\n socket.onerror = (event) => {\r\n console.error(\"[WA Bridge] websocket error\", event);\r\n };\r\n}\r\n\r\nfunction start() {\r\n WPP.loader.onReady(() => {\r\n console.log(\"[WA Bridge] WA-JS ready\");\r\n\r\n connect();\r\n\r\n /*\r\n * Forward new-message events to Node.\r\n */\r\n WPP.chat.on(\"chat.new_message\", (message: unknown) => {\r\n if (socket?.readyState !== WebSocket.OPEN) {\r\n return;\r\n }\r\n\r\n send({\r\n type: \"event\",\r\n event: \"message\",\r\n data: serialize(message),\r\n });\r\n });\r\n });\r\n}\r\n\r\nstart();\r\n"], + "sourcesContent": ["import type {\r\n ChatDTO,\r\n ContactDTO,\r\n GroupMetadataDTO,\r\n GroupParticipantDTO,\r\n LocationDTO,\r\n MessageDTO,\r\n MessageIdDTO,\r\n SerializedId,\r\n} from \"../shared/dto\";\r\n\r\n/**\r\n * WA models contain a lot of internal properties and sometimes\r\n * circular references. Never JSON.stringify them directly.\r\n */\r\nexport function serialize(value: any): any {\r\n if (value == null) {\r\n return value;\r\n }\r\n\r\n if (Array.isArray(value)) {\r\n return value.map(serialize);\r\n }\r\n\r\n if (typeof value !== \"object\") {\r\n return value;\r\n }\r\n\r\n const result: Record = {};\r\n\r\n for (const key of Object.keys(value)) {\r\n const v = value[key];\r\n\r\n if (\r\n typeof v === \"string\" ||\r\n typeof v === \"number\" ||\r\n typeof v === \"boolean\" ||\r\n v === null\r\n ) {\r\n result[key] = v;\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction safeProperty(object: unknown, key: string): T | undefined {\r\n try {\r\n if (object && typeof object === \"object\") {\r\n return (object as any)[key];\r\n }\r\n } catch {\r\n return undefined;\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\nexport function safeGet(\r\n object: unknown,\r\n key: string,\r\n): T | undefined {\r\n if (object == null || typeof object !== \"object\") {\r\n return undefined;\r\n }\r\n\r\n try {\r\n return (object as any)[key];\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\nexport function pick(\r\n source: any,\r\n keys: readonly string[],\r\n): Partial {\r\n const result: Record = {};\r\n\r\n for (const key of keys) {\r\n const value = safeGet(source, key);\r\n\r\n if (value !== undefined) {\r\n result[key] = serialize(value);\r\n }\r\n }\r\n\r\n return result as Partial;\r\n}\r\n\r\nexport function serializeId(id: any): SerializedId | string | null {\r\n if (id == null) {\r\n return null;\r\n }\r\n\r\n if (typeof id === \"string\") {\r\n return id;\r\n }\r\n\r\n if (typeof id !== \"object\") {\r\n return String(id);\r\n }\r\n\r\n const result: SerializedId = {};\r\n\r\n for (const key of [\r\n \"_serialized\",\r\n \"user\",\r\n \"server\",\r\n \"domain\",\r\n \"device\",\r\n \"agent\",\r\n \"fromMe\",\r\n \"remote\",\r\n \"participant\",\r\n ]) {\r\n const value = safeGet(id, key);\r\n\r\n if (value !== undefined) {\r\n result[key] = serialize(value) as any;\r\n }\r\n }\r\n\r\n // Some WA objects expose useful fields that aren't\r\n // enumerable.\r\n if (!result._serialized && typeof safeGet(id, \"toString\") === \"function\") {\r\n try {\r\n const serialized = id.toString();\r\n\r\n if (serialized && serialized !== \"[object Object]\") {\r\n result._serialized = serialized;\r\n }\r\n } catch {}\r\n }\r\n\r\n return Object.keys(result).length ? result : String(id);\r\n}\r\n\r\nexport function serializeChat(chat: any): ChatDTO {\r\n return {\r\n id: serializeId(chat.id),\r\n accountLid: chat.accountLid,\r\n name: chat.name,\r\n formattedTitle: chat.formattedTitle,\r\n\r\n isGroup: chat.isGroup,\r\n isUser: chat.isUser,\r\n isReadOnly: chat.isReadOnly,\r\n archived: chat.archived,\r\n unreadCount: chat.unreadCount,\r\n\r\n timestamp: chat.t,\r\n lastMessage: chat.lastMessage ? serialize(chat.lastMessage) : null,\r\n };\r\n}\r\n\r\nexport function serializeContact(contact: any): ContactDTO {\r\n return {\r\n id: serializeId(contact.id),\r\n\r\n accountLid: contact.accountLid ?? null,\r\n name: contact.name ?? null,\r\n pushname: contact.pushname ?? null,\r\n shortName: contact.shortName ?? null,\r\n formattedName: contact.formattedName ?? null,\r\n number: contact.number ?? null,\r\n\r\n isMe: contact.isMe ?? false,\r\n isUser: contact.isUser ?? false,\r\n isBusiness: contact.isBusiness ?? false,\r\n isEnterprise: contact.isEnterprise ?? false,\r\n isGroup: contact.isGroup ?? false,\r\n isMyContact: contact.isMyContact ?? false,\r\n isBlocked: contact.isBlocked ?? false,\r\n\r\n profilePicThumbObj: contact.profilePicThumbObj\r\n ? serialize(contact.profilePicThumbObj)\r\n : undefined,\r\n };\r\n}\r\n\r\nexport function serializeGroupParticipant(\r\n participant: any,\r\n): GroupParticipantDTO {\r\n return {\r\n id: serializeId(participant.id),\r\n\r\n isAdmin: participant.isAdmin ?? false,\r\n isSuperAdmin: participant.isSuperAdmin ?? false,\r\n isSuperParticipant: participant.isSuperParticipant ?? false,\r\n isBusiness: participant.isBusiness ?? false,\r\n };\r\n}\r\n\r\nexport function serializeGroupMetadata(metadata: any): GroupMetadataDTO {\r\n return {\r\n id: serializeId(metadata.id),\r\n\r\n subject: metadata.subject ?? null,\r\n subjectOwner: serializeId(metadata.subjectOwner),\r\n subjectTime: metadata.subjectTime ?? null,\r\n creation: metadata.creation ?? null,\r\n owner: serializeId(metadata.owner),\r\n\r\n participants: Array.isArray(metadata.participants)\r\n ? (metadata.participants\r\n .map(serializeGroupParticipant)\r\n .filter(Boolean) as GroupParticipantDTO[])\r\n : [],\r\n\r\n size: metadata.size ?? metadata.participants?.length ?? 0,\r\n restrict: metadata.restrict ?? false,\r\n announce: metadata.announce ?? false,\r\n isCommunity: metadata.isCommunity ?? false,\r\n isCommunityAnnounce: metadata.isCommunityAnnounce ?? false,\r\n };\r\n}\r\nexport function serializeMessageId(id: any): MessageIdDTO | null {\r\n if (!id) {\r\n return null;\r\n }\r\n\r\n return {\r\n id: id.id ?? null,\r\n _serialized: id._serialized ?? null,\r\n fromMe: id.fromMe ?? false,\r\n remote: serializeId(id.remote),\r\n participant: serializeId(id.participant),\r\n };\r\n}\r\nexport function serializeLocation(location: any): LocationDTO | null {\r\n if (!location) {\r\n return null;\r\n }\r\n\r\n return {\r\n latitude: location.latitude ?? null,\r\n longitude: location.longitude ?? null,\r\n description: location.description ?? null,\r\n address: location.address ?? null,\r\n url: location.url ?? null,\r\n name: location.name ?? null,\r\n };\r\n}\r\nexport function serializeMessageLink(link: any): any {\r\n return {\r\n link: link?.link ?? null,\r\n isSuspicious: link?.isSuspicious ?? false,\r\n };\r\n}\r\nexport function serializeMessage(message: any): MessageDTO {\r\n const quotedMsgId = safeProperty(message, \"quotedMsgId\");\r\n const hasQuoted = !!quotedMsgId;\r\n let quotedMsg = null;\r\n\r\n if (hasQuoted) {\r\n const rawQuoted = safeProperty(message, \"quotedMsg\");\r\n if (rawQuoted) {\r\n quotedMsg = serializeMessage(rawQuoted);\r\n }\r\n }\r\n\r\n return {\r\n id: serializeMessageId(message.id)!,\r\n chatId: message.chatId ?? message.id?.remote?._serialized ?? null,\r\n from: serializeId(message.from),\r\n to: serializeId(message.to),\r\n author: serializeId(message.author),\r\n fromMe: message.fromMe ?? false,\r\n body: message.body ?? null,\r\n caption: message.caption ?? null,\r\n type: message.type ?? null,\r\n subtype: message.subtype ?? null,\r\n timestamp: message.timestamp ?? null,\r\n ack: message.ack ?? null,\r\n hasMedia: message.hasMedia ?? false,\r\n mediaKey: message.mediaKey ?? null,\r\n isForwarded: message.isForwarded ?? false,\r\n forwardingScore: message.forwardingScore ?? 0,\r\n isStarred: message.isStarred ?? false,\r\n broadcast: message.broadcast ?? false,\r\n mentionedIds: Array.isArray(message.mentionedIds)\r\n ? message.mentionedIds.map(serializeId)\r\n : [],\r\n quotedMsgId: serializeMessageId(quotedMsgId),\r\n quotedMsg,\r\n links: Array.isArray(message.links)\r\n ? message.links.map(serializeMessageLink)\r\n : [],\r\n location: serializeLocation(message.location),\r\n vCard: message.vCard ?? null,\r\n vCards: Array.isArray(message.vCards) ? message.vCards : [],\r\n filename: message.filename ?? null,\r\n mimetype: message.mimetype ?? null,\r\n size: message.size ?? null,\r\n duration: message.duration ?? null,\r\n };\r\n}\r\n", "import type { BridgeRequest } from \"../shared/protocol\";\r\nimport {\r\n serialize,\r\n serializeChat,\r\n serializeContact,\r\n serializeGroupMetadata,\r\n serializeMessage,\r\n} from \"./serializer\";\r\n\r\nconst WS_URL = \"wss://wa-bridge.rul.sh/ws\";\r\n\r\nlet socket: WebSocket | undefined;\r\n\r\nfunction send(message: unknown) {\r\n if (socket?.readyState !== WebSocket.OPEN) {\r\n throw new Error(\"WebSocket is not connected\");\r\n }\r\n\r\n socket.send(JSON.stringify(message));\r\n}\r\n\r\nfunction reply(id: string, result: unknown) {\r\n send({\r\n type: \"response\",\r\n id,\r\n ok: true,\r\n result: serialize(result),\r\n });\r\n}\r\n\r\nfunction error(id: string, err: unknown) {\r\n send({\r\n type: \"response\",\r\n id,\r\n ok: false,\r\n error: {\r\n code: \"ERROR\",\r\n message: err instanceof Error ? err.message : String(err),\r\n },\r\n });\r\n}\r\n\r\nasync function handleRequest(request: BridgeRequest) {\r\n switch (request.method) {\r\n case \"status\":\r\n return {\r\n authenticated: WPP.conn.isAuthenticated(),\r\n ready: WPP.isReady,\r\n };\r\n\r\n case \"listChats\": {\r\n const chats = await WPP.chat.list({\r\n ...(request.params?.limit != null\r\n ? { count: request.params.limit }\r\n : {}),\r\n ...(request.params?.groupsOnly ? { onlyGroups: true } : {}),\r\n ...(request.params?.usersOnly ? { onlyUsers: true } : {}),\r\n });\r\n\r\n return chats.filter(Boolean).map(serializeChat);\r\n }\r\n\r\n case \"listContacts\": {\r\n const contacts = await WPP.contact.getAllContacts();\r\n\r\n const result = request.params?.limit\r\n ? contacts.slice(0, request.params.limit)\r\n : contacts;\r\n\r\n return result.filter(Boolean).map(serializeContact);\r\n }\r\n\r\n case \"listGroups\": {\r\n const groups = await WPP.group.getAllGroups();\r\n\r\n return groups.filter(Boolean).map(serializeGroupMetadata);\r\n }\r\n\r\n case \"getMessages\": {\r\n const messages = await WPP.chat.getMessages(request.params.chatId, {\r\n count: request.params.count ?? 50,\r\n direction: request.params.direction,\r\n id: request.params.id,\r\n });\r\n\r\n return messages.filter(Boolean).map(serializeMessage);\r\n }\r\n\r\n case \"searchMessages\":\r\n return searchMessages(\r\n request.params.query,\r\n request.params.chatId,\r\n request.params.limit ?? 100,\r\n );\r\n\r\n case \"sendText\": {\r\n const result = await WPP.chat.sendTextMessage(\r\n request.params.chatId,\r\n request.params.text,\r\n {\r\n delay: request.params.options?.delay,\r\n quotedMsg: request.params.options?.quotedMsg,\r\n mentionedList: request.params.options?.mentionedList,\r\n waitForAck: request.params.options?.waitForAck,\r\n },\r\n );\r\n\r\n return serialize(result);\r\n }\r\n\r\n default:\r\n throw new Error(`Unknown method: ${(request as any).method}`);\r\n }\r\n}\r\n\r\n/**\r\n * WA-JS doesn't need a separate \"global search\" API for our\r\n * first implementation. Search the messages loaded from chats.\r\n *\r\n * Later we can optimize this using WhatsApp's internal message\r\n * store/index if necessary.\r\n */\r\nasync function searchMessages(query: string, chatId?: string, limit = 100) {\r\n const normalized = query.toLowerCase();\r\n\r\n let chats;\r\n\r\n if (chatId) {\r\n const chat = WPP.chat.get(chatId);\r\n chats = chat ? [chat] : [];\r\n } else {\r\n chats = await WPP.chat.list();\r\n }\r\n\r\n const results: any[] = [];\r\n\r\n for (const chat of chats) {\r\n if (results.length >= limit) {\r\n break;\r\n }\r\n\r\n const id = chat?.id?._serialized ?? chat?.id ?? chat?.wid?._serialized;\r\n\r\n if (!id) {\r\n continue;\r\n }\r\n\r\n try {\r\n const messages = await WPP.chat.getMessages(id, {\r\n count: 100,\r\n });\r\n\r\n for (const message of messages) {\r\n const body = String(message.body ?? message.caption ?? \"\");\r\n\r\n if (body.toLowerCase().includes(normalized)) {\r\n results.push({\r\n chatId: id,\r\n message: serialize(message),\r\n });\r\n\r\n if (results.length >= limit) {\r\n break;\r\n }\r\n }\r\n }\r\n } catch (err) {\r\n console.warn(\"Failed searching chat\", id, err);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\nfunction connect() {\r\n if (\r\n socket &&\r\n (socket.readyState === WebSocket.OPEN ||\r\n socket.readyState === WebSocket.CONNECTING)\r\n ) {\r\n return;\r\n }\r\n\r\n socket = new WebSocket(WS_URL);\r\n\r\n socket.onopen = () => {\r\n console.log(\"[WA Bridge] connected\");\r\n\r\n send({\r\n type: \"event\",\r\n event: \"ready\",\r\n data: {\r\n authenticated: WPP.conn.isAuthenticated(),\r\n },\r\n });\r\n };\r\n\r\n socket.onmessage = async (event) => {\r\n try {\r\n const request = JSON.parse(event.data) as BridgeRequest;\r\n\r\n if (request.type !== \"request\") {\r\n return;\r\n }\r\n\r\n try {\r\n const result = await handleRequest(request);\r\n\r\n reply(request.id, JSON.stringify(result));\r\n } catch (err) {\r\n error(request.id, err);\r\n }\r\n } catch (err) {\r\n console.error(\"[WA Bridge] invalid message\", err);\r\n }\r\n };\r\n\r\n socket.onclose = (event) => {\r\n console.log(\"[WA Bridge] disconnected\", {\r\n code: event.code,\r\n reason: event.reason,\r\n wasClean: event.wasClean,\r\n });\r\n\r\n socket = undefined;\r\n setTimeout(connect, 3000);\r\n };\r\n\r\n socket.onerror = (event) => {\r\n console.error(\"[WA Bridge] websocket error\", event);\r\n };\r\n}\r\n\r\nfunction start() {\r\n WPP.loader.onReady(() => {\r\n console.log(\"[WA Bridge] WA-JS ready\");\r\n\r\n connect();\r\n\r\n /*\r\n * Forward new-message events to Node.\r\n */\r\n WPP.chat.on(\"chat.new_message\", (message: unknown) => {\r\n if (socket?.readyState !== WebSocket.OPEN) {\r\n return;\r\n }\r\n\r\n send({\r\n type: \"event\",\r\n event: \"message\",\r\n data: serialize(message),\r\n });\r\n });\r\n });\r\n}\r\n\r\nstart();\r\n"], "mappings": "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", "names": [] }