From 78faf2b07069719d20d88a82eb6d5eb09f4e750d Mon Sep 17 00:00:00 2001 From: Khairul Hidayat Date: Wed, 16 Sep 2026 05:07:21 +0800 Subject: [PATCH] feat: add getMedia --- dist/whatsapp-bridge.user.js | 5 ++++- dist/whatsapp-bridge.user.js.map | 4 ++-- esbuild.mjs | 2 +- src/client/whatsapp.ts | 4 ++++ src/shared/protocol.ts | 3 ++- src/userscript/index.ts | 6 ++++++ src/userscript/types.d.ts | 6 ++++++ 7 files changed, 25 insertions(+), 5 deletions(-) diff --git a/dist/whatsapp-bridge.user.js b/dist/whatsapp-bridge.user.js index a7c010a..7857233 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.4 +// @version 0.0.5 // @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 @@ -308,6 +308,9 @@ ); return serialize(result); } + case "getMedia": + const media = await WPP.chat.downloadMedia(request.params).then(WPP.util.blobToBase64); + return media; default: throw new Error(`Unknown method: ${request.method}`); } diff --git a/dist/whatsapp-bridge.user.js.map b/dist/whatsapp-bridge.user.js.map index ffc54fe..72fa78b 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 {\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\n ? serializeMessage(rawPreviewMessage)\n : 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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", + "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\n ? serializeMessage(rawPreviewMessage)\n : 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 case \"getMedia\":\n const media = await WPP.chat\n .downloadMedia(request.params)\n .then(WPP.util.blobToBase64);\n return media;\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 93ecc13..571e99b 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.4 +// @version 0.0.5 // @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/client/whatsapp.ts b/src/client/whatsapp.ts index e45a9f0..677b2f0 100644 --- a/src/client/whatsapp.ts +++ b/src/client/whatsapp.ts @@ -150,4 +150,8 @@ export class WhatsAppClient { sendText(params: SendTextParams) { return this.request("sendText", params); } + + getMedia(msgId: string) { + return this.request("getMedia", msgId); + } } diff --git a/src/shared/protocol.ts b/src/shared/protocol.ts index fe6a34f..e721d41 100644 --- a/src/shared/protocol.ts +++ b/src/shared/protocol.ts @@ -68,4 +68,5 @@ export type BridgeRequest = | RpcRequest<"listGroups"> | RpcRequest<"getMessages", GetMessagesParams> | RpcRequest<"searchMessages", SearchMessagesParams> - | RpcRequest<"sendText", SendTextParams>; + | RpcRequest<"sendText", SendTextParams> + | RpcRequest<"getMedia", string>; diff --git a/src/userscript/index.ts b/src/userscript/index.ts index 0f1c9a8..c7a954c 100644 --- a/src/userscript/index.ts +++ b/src/userscript/index.ts @@ -108,6 +108,12 @@ async function handleRequest(request: BridgeRequest) { return serialize(result); } + case "getMedia": + const media = await WPP.chat + .downloadMedia(request.params) + .then(WPP.util.blobToBase64); + return media; + default: throw new Error(`Unknown method: ${(request as any).method}`); } diff --git a/src/userscript/types.d.ts b/src/userscript/types.d.ts index 8133c48..d3dde89 100644 --- a/src/userscript/types.d.ts +++ b/src/userscript/types.d.ts @@ -48,6 +48,8 @@ declare const WPP: { }, ): Promise; + downloadMedia(msgId: string): Promise; + on(event: string, callback: (...args: any[]) => void): void; }; @@ -60,5 +62,9 @@ declare const WPP: { getAllGroups(): Promise; }; + util: { + blobToBase64: (blob: Blob) => Promise; + }; + on(event: string, callback: (...args: any[]) => void): void; };