306 lines
7.5 KiB
TypeScript
306 lines
7.5 KiB
TypeScript
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<string, unknown> = {};
|
|
|
|
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<T>(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<T = unknown>(
|
|
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<T extends object>(
|
|
source: any,
|
|
keys: readonly string[],
|
|
): Partial<T> {
|
|
const result: Record<string, unknown> = {};
|
|
|
|
for (const key of keys) {
|
|
const value = safeGet(source, key);
|
|
|
|
if (value !== undefined) {
|
|
result[key] = serialize(value);
|
|
}
|
|
}
|
|
|
|
return result as Partial<T>;
|
|
}
|
|
|
|
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<any>(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<any>(message, "quotedMsgId");
|
|
const hasQuoted = !!quotedMsgId;
|
|
let quotedMsg = null;
|
|
|
|
if (hasQuoted) {
|
|
const rawQuoted = safeProperty<any>(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,
|
|
};
|
|
}
|