initial commit

This commit is contained in:
2026-09-14 17:15:00 +08:00
commit c78b75297f
19 changed files with 2421 additions and 0 deletions
+298
View File
@@ -0,0 +1,298 @@
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 {
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,
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,
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.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,
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,
};
}