import type { ChatDTO, ContactDTO, GroupMetadataDTO, GroupParticipantDTO, LocationDTO, MessageDTO, MessageIdDTO, SerializedId, } from "../shared/dto"; /** * WA models contain a lot of internal properties and sometimes * circular references. Never JSON.stringify them directly. */ export function serialize(value: any): any { if (value == null) { return value; } if (Array.isArray(value)) { return value.map(serialize); } if (typeof value !== "object") { return value; } const result: Record = {}; for (const key of Object.keys(value)) { const v = value[key]; if ( typeof v === "string" || typeof v === "number" || typeof v === "boolean" || v === null ) { result[key] = v; } } return result; } function safeProperty(object: unknown, key: string): T | undefined { try { if (object && typeof object === "object") { return (object as any)[key]; } } catch { return undefined; } return undefined; } export function safeGet( object: unknown, key: string, ): T | undefined { if (object == null || typeof object !== "object") { return undefined; } try { return (object as any)[key]; } catch { return undefined; } } export function pick( source: any, keys: readonly string[], ): Partial { const result: Record = {}; for (const key of keys) { const value = safeGet(source, key); if (value !== undefined) { result[key] = serialize(value); } } return result as Partial; } export function serializeId(id: any): SerializedId | string | null { if (id == null) { return null; } if (typeof id === "string") { return id; } if (typeof id !== "object") { return String(id); } const result: SerializedId = {}; for (const key of [ "_serialized", "user", "server", "domain", "device", "agent", "fromMe", "remote", "participant", ]) { const value = safeGet(id, key); if (value !== undefined) { result[key] = serialize(value) as any; } } // Some WA objects expose useful fields that aren't // enumerable. if (!result._serialized && typeof safeGet(id, "toString") === "function") { try { const serialized = id.toString(); if (serialized && serialized !== "[object Object]") { result._serialized = serialized; } } catch {} } return Object.keys(result).length ? result : String(id); } export function serializeChat(chat: any): ChatDTO { const rawPreviewMessage = safeProperty(chat, "previewMessage"); return { id: serializeId(chat.id), accountLid: chat.accountLid, name: chat.name, formattedTitle: chat.formattedTitle, isGroup: chat.isGroup, isUser: chat.isUser, isReadOnly: chat.isReadOnly, archived: chat.archived, unreadCount: chat.unreadCount, previewMessage: rawPreviewMessage ? serializeMessage(rawPreviewMessage) : null, timestamp: chat.t, lastMessage: chat.lastMessage ? serialize(chat.lastMessage) : null, }; } export function serializeContact(contact: any): ContactDTO { return { id: serializeId(contact.id), accountLid: contact.accountLid ?? null, name: contact.name ?? null, pushname: contact.pushname ?? null, shortName: contact.shortName ?? null, formattedName: contact.formattedName ?? null, notifyName: contact.notifyName ?? null, number: contact.number ?? null, isMe: contact.isMe ?? false, isUser: contact.isUser ?? false, isBusiness: contact.isBusiness ?? false, isEnterprise: contact.isEnterprise ?? false, isGroup: contact.isGroup ?? false, isMyContact: contact.isMyContact ?? false, isBlocked: contact.isBlocked ?? false, profilePicThumbObj: contact.profilePicThumbObj ? serialize(contact.profilePicThumbObj) : undefined, }; } export function serializeGroupParticipant( participant: any, ): GroupParticipantDTO { return { id: serializeId(participant.id), isAdmin: participant.isAdmin ?? false, isSuperAdmin: participant.isSuperAdmin ?? false, isSuperParticipant: participant.isSuperParticipant ?? false, isBusiness: participant.isBusiness ?? false, }; } export function serializeGroupMetadata(metadata: any): GroupMetadataDTO { return { id: serializeId(metadata.id), subject: metadata.subject ?? null, subjectOwner: serializeId(metadata.subjectOwner), subjectTime: metadata.subjectTime ?? null, creation: metadata.creation ?? null, owner: serializeId(metadata.owner), participants: Array.isArray(metadata.participants) ? (metadata.participants .map(serializeGroupParticipant) .filter(Boolean) as GroupParticipantDTO[]) : [], size: metadata.size ?? metadata.participants?.length ?? 0, restrict: metadata.restrict ?? false, announce: metadata.announce ?? false, isCommunity: metadata.isCommunity ?? false, isCommunityAnnounce: metadata.isCommunityAnnounce ?? false, }; } export function serializeMessageId(id: any): MessageIdDTO | null { if (!id) { return null; } return { id: id.id ?? null, _serialized: id._serialized ?? null, fromMe: id.fromMe ?? false, remote: serializeId(id.remote), participant: serializeId(id.participant), }; } export function serializeLocation(location: any): LocationDTO | null { if (!location) { return null; } return { latitude: location.latitude ?? null, longitude: location.longitude ?? null, description: location.description ?? null, address: location.address ?? null, url: location.url ?? null, name: location.name ?? null, }; } export function serializeMessageLink(link: any): any { return { link: link?.link ?? null, isSuspicious: link?.isSuspicious ?? false, }; } export function serializeMessage(message: any): MessageDTO { const quotedMsgId = safeProperty(message, "quotedMsgId"); const hasQuoted = !!quotedMsgId; let quotedMsg = null; if (hasQuoted) { const rawQuoted = safeProperty(message, "quotedMsg"); if (rawQuoted) { quotedMsg = serializeMessage(rawQuoted); } } return { id: serializeMessageId(message.id)!, chatId: message.chatId ?? message.id?.remote?._serialized ?? null, from: serializeId(message.from), to: serializeId(message.to), author: serializeId(message.author), fromMe: message.id?.fromMe ?? message.fromMe ?? false, body: message.body ?? null, caption: message.caption ?? null, type: message.type ?? null, subtype: message.subtype ?? null, timestamp: message.timestamp ?? null, ack: message.ack ?? null, hasMedia: message.hasMedia ?? false, mediaKey: message.mediaKey ?? null, isForwarded: message.isForwarded ?? false, forwardingScore: message.forwardingScore ?? 0, isStarred: message.isStarred ?? false, broadcast: message.broadcast ?? false, notifyName: message.notifyName ?? null, mentionedIds: Array.isArray(message.mentionedIds) ? message.mentionedIds.map(serializeId) : [], quotedMsgId: serializeMessageId(quotedMsgId), quotedMsg, links: Array.isArray(message.links) ? message.links.map(serializeMessageLink) : [], location: serializeLocation(message.location), vCard: message.vCard ?? null, vCards: Array.isArray(message.vCards) ? message.vCards : [], filename: message.filename ?? null, mimetype: message.mimetype ?? null, size: message.size ?? null, duration: message.duration ?? null, }; }