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