diff options
| author | Elvis Claros Castro <elvis@claros.ar> | 2026-09-26 19:26:33 -0300 |
|---|---|---|
| committer | Elvis Claros Castro <elvis@claros.ar> | 2026-09-26 19:26:33 -0300 |
| commit | d80640d01eeb84907e36ff4af33add9081eda17c (patch) | |
| tree | 27698b34e06e28d111c2196700015d9c82f78274 /src/serialize.js | |
| download | wa-cli-d80640d01eeb84907e36ff4af33add9081eda17c.tar.gz wa-cli-d80640d01eeb84907e36ff4af33add9081eda17c.zip | |
Diffstat (limited to 'src/serialize.js')
| -rw-r--r-- | src/serialize.js | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/src/serialize.js b/src/serialize.js new file mode 100644 index 0000000..5f22674 --- /dev/null +++ b/src/serialize.js @@ -0,0 +1,158 @@ +// whatsapp-web.js objects -> plain, stable JSON. +// +// This shape is the "contract" consumed by the formats (table, eml, mbox) and by any +// external integration (hooks, the Maildir bridge). Changing it breaks consumers: adding +// fields is fine, renaming is not. + +const iso = (ts) => (ts ? new Date(ts * 1000).toISOString() : null); + +// names: (id) => display name | undefined +export function message(m, { names = () => undefined, me } = {}) { + const d = m._data || {}; + const chatId = typeof m.id.remote === 'string' ? m.id.remote : m.id.remote?._serialized; + const sender = m.fromMe ? (me?.id ?? m.from) : (m.author || m.from); + const out = { + id: m.id._serialized ?? m.id.$1, + stanzaId: m.id.id, + chatId, + chatName: names(chatId) ?? null, + fromMe: m.fromMe, + from: m.from, + to: m.to, + author: m.author || null, + sender, + senderName: m.fromMe + ? (me?.name ?? null) + : (names(sender) || d.notifyName || sender?.split('@')[0] || null), + timestamp: m.timestamp, + date: iso(m.timestamp), + type: m.type, + body: m.body ?? '', + hasMedia: m.hasMedia, + media: m.hasMedia + ? { mimetype: d.mimetype ?? null, filename: d.filename ?? null, size: d.size ?? null } + : null, + hasQuotedMsg: m.hasQuotedMsg, + quotedStanzaId: d.quotedStanzaID ?? null, + quotedParticipant: serializedId(d.quotedParticipant), + isForwarded: Boolean(m.isForwarded), + isStarred: Boolean(m.isStarred), + isStatus: Boolean(m.isStatus), + ack: m.ack ?? null, + mentionedIds: (m.mentionedIds || []).map(serializedId), + links: (m.links || []).map((l) => l.link), + }; + if (m.location) { + out.location = { + latitude: Number(m.location.latitude), + longitude: Number(m.location.longitude), + name: m.location.name ?? m.location.description ?? null, + address: m.location.address ?? null, + url: m.location.url ?? null, + }; + } + if (m.vCards?.length) out.vCards = m.vCards; + if (m.type === 'poll_creation') { + out.poll = { name: m.pollName, options: (m.pollOptions || []).map((o) => o.name ?? o) }; + } + return out; +} + +function serializedId(v) { + if (v == null) return null; + return typeof v === 'string' ? v : (v._serialized ?? null); +} + +export function chat(c, opts = {}) { + const out = { + id: c.id._serialized, + name: c.name || c.id.user, + isGroup: Boolean(c.isGroup), + unreadCount: c.unreadCount ?? 0, + timestamp: c.timestamp ?? null, + date: iso(c.timestamp), + archived: Boolean(c.archived), + pinned: Boolean(c.pinned), + isMuted: Boolean(c.isMuted), + muteExpiration: c.muteExpiration ?? 0, + isReadOnly: Boolean(c.isReadOnly), + lastMessage: c.lastMessage ? message(c.lastMessage, opts) : null, + }; + if (c.isGroup && c.groupMetadata) { + out.participantCount = c.groupMetadata.participants?.length ?? null; + } + return out; +} + +export function group(c, { names = () => undefined } = {}) { + const md = c.groupMetadata || {}; + return { + id: c.id._serialized, + name: c.name, + description: md.desc ?? null, + owner: serializedId(md.owner), + createdAt: md.creation ? iso(md.creation) : null, + announce: Boolean(md.announce), + restrict: Boolean(md.restrict), + participants: (md.participants || []).map((p) => { + const id = serializedId(p.id); + return { id, name: names(id) ?? null, isAdmin: Boolean(p.isAdmin), isSuperAdmin: Boolean(p.isSuperAdmin) }; + }), + }; +} + +export function contact(c) { + return { + id: c.id._serialized, + number: c.number ?? c.id.user, + name: c.name ?? null, + pushname: c.pushname ?? null, + shortName: c.shortName ?? null, + displayName: displayName(c), + isMyContact: Boolean(c.isMyContact), + isBusiness: Boolean(c.isBusiness), + isGroup: Boolean(c.isGroup), + isBlocked: Boolean(c.isBlocked), + }; +} + +export function displayName(c) { + return c.name || c.pushname || c.shortName || c.verifiedName || c.number || c.id?.user || null; +} + +export function reaction(r) { + return { + id: serializedId(r.id), + messageId: serializedId(r.msgId), + chatId: r.msgId?.remote ? serializedId(r.msgId.remote) : null, + senderId: r.senderId, + reaction: r.reaction, + timestamp: r.timestamp, + date: iso(r.timestamp), + }; +} + +export function groupNotification(n) { + return { + id: serializedId(n.id), + chatId: n.chatId, + type: n.type, + author: n.author, + recipientIds: n.recipientIds, + body: n.body, + timestamp: n.timestamp, + date: iso(n.timestamp), + }; +} + +export function call(c) { + return { + id: c.id, + from: c.from ?? null, + fromMe: Boolean(c.fromMe), + isVideo: Boolean(c.isVideo), + isGroup: Boolean(c.isGroup), + timestamp: c.timestamp, + date: iso(c.timestamp), + }; +} |