diff --git a/whatsapp-router/src/routes/conversations.ts b/whatsapp-router/src/routes/conversations.ts index 18a7efe..4c350a4 100644 --- a/whatsapp-router/src/routes/conversations.ts +++ b/whatsapp-router/src/routes/conversations.ts @@ -41,4 +41,4 @@ export function registerConversationRoutes(app: Application, openWaUrl: string | const deleted = deleteConversation(req.params.id); res.json({ success: deleted }); }); -} +} \ No newline at end of file diff --git a/whatsapp-router/src/store/conversation.ts b/whatsapp-router/src/store/conversation.ts index 65a998e..a517459 100644 --- a/whatsapp-router/src/store/conversation.ts +++ b/whatsapp-router/src/store/conversation.ts @@ -3,81 +3,108 @@ import { WhatsAppMessage, Conversation, Msg, Participant } from '../types'; const conversations = new Map(); -// ──────────────────────────── Helpers ───────────────────────────── - -async function fetchChatMessages(chatId: string, openWaUrl: string): Promise { +async function loadMessages( + chatId: string, + openWaUrl: string +): Promise { + console.log(`[conversationStore] Loading messages for ${chatId}`); const { data } = await axios.post(`${openWaUrl}/loadAndGetAllMessagesInChat`, { - args: { chatId, includeMe: true, includeNotifications: true }, + args: { + chatId, + includeMe: true, + includeNotifications: true, + }, }); - return data?.response ?? data ?? []; + const msgs: WhatsAppMessage[] = data?.response || data || []; + return msgs; } -function toMsg(m: WhatsAppMessage): Msg { - const anyMsg = m as any; +function mapMessage(m: WhatsAppMessage): Msg { return { id: m.id, from: m.from, to: m.to, - ts: anyMsg.timestamp ?? anyMsg.t ?? Date.now(), - type: anyMsg.type ?? 'chat', - text: anyMsg.text ?? anyMsg.caption ?? anyMsg.body ?? '', - mediaUrl: anyMsg.cloudUrl ?? anyMsg.clientUrl, - mentions: anyMsg.mentionedJidList ?? [], + ts: (m as any).timestamp || (m as any).t, + type: ((m as any).type as any) || 'chat', + text: (m as any).text || (m as any).caption || (m as any).body, + mediaUrl: (m as any).cloudUrl || (m as any).clientUrl, + mentions: ((m as any).mentionedJidList as any) || [], meta: { - ack: anyMsg.ack ?? 0, - hasReaction: Boolean(anyMsg.hasReaction), - isQuoted: Boolean(anyMsg.quotedMsg), + ack: (m as any).ack || 0, + hasReaction: (m as any).hasReaction || false, + isQuoted: !!(m as any).quotedMsg, }, }; } -function buildParticipants(messages: WhatsAppMessage[], chat?: any): Participant[] { - const map = new Map(); +export async function getConversation( + chatId: string, + openWaUrl: string +): Promise { + console.log(`[conversationStore] Retrieving conversation for ${chatId}`); + let conv = conversations.get(chatId); + if (!conv) { + conv = await buildConversation(chatId, openWaUrl); + } + return conv; +} +export function listConversations(): Conversation[] { + console.log('[conversationStore] Listing conversations'); + return Array.from(conversations.values()); +} + +export async function buildConversation( + chatId: string, + openWaUrl: string +): Promise { + console.log(`[conversationStore] Building conversation for ${chatId}`); + const rawMessages = await loadMessages(chatId, openWaUrl); + const now = Date.now(); + + const first = rawMessages[0]; + const chat = first?.chat; + const title = chat?.formattedTitle || chat?.name || chatId; + const isGroup = chat?.isGroup || false; + const unreadCount = chat?.unreadCount || 0; + + const participantsMap = new Map(); if (chat?.contact) { const c = chat.contact; - map.set(c.id, { + participantsMap.set(c.id, { id: c.id, name: c.pushname || c.name || '', - isMe: Boolean(c.isMe), + isMe: c.isMe, }); } - - if (chat?.isGroup && chat.groupMetadata?.participants) { + if (isGroup && chat?.groupMetadata?.participants) { for (const p of chat.groupMetadata.participants as any[]) { const c = p.contact || {}; const id = c.id || p.id; - map.set(id, { + participantsMap.set(id, { id, name: c.pushname || c.name || '', - isMe: Boolean(c.isMe), - isAdmin: Boolean(p.isAdmin || p.isSuperAdmin), + isMe: c.isMe || false, + isAdmin: p.isAdmin || p.isSuperAdmin, }); } } - for (const m of messages) { + for (const m of rawMessages) { const s = m.sender; - if (s && !map.has(s.id)) { - map.set(s.id, { + if (s && !participantsMap.has(s.id)) { + participantsMap.set(s.id, { id: s.id, name: s.pushname || s.name || '', - isMe: Boolean(s.isMe), + isMe: s.isMe, }); } } - return [...map.values()]; -} + const messages: Msg[] = rawMessages.slice(-20).map(mapMessage); + messages.sort((a, b) => a.ts - b.ts); -// ──────────────────────────── Public API ───────────────────────────── - -export async function buildConversation(chatId: string, openWaUrl: string): Promise { - const raw = await fetchChatMessages(chatId, openWaUrl); - const chat = raw[0]?.chat; - const now = Date.now(); - - const conversation: Conversation = { + const conv: Conversation = { chatId, title, isGroup, @@ -87,21 +114,12 @@ export async function buildConversation(chatId: string, openWaUrl: string): Prom createdAt: conversations.get(chatId)?.createdAt || now, }; - conversations.set(chatId, conversation); - return conversation; -} - -export async function getConversation(chatId: string, openWaUrl: string): Promise { - let conv = conversations.get(chatId); - if (!conv) conv = await buildConversation(chatId, openWaUrl); + conversations.set(chatId, conv); return conv; } -export function listConversations(): Conversation[] { - return [...conversations.values()]; -} - export function deleteConversation(chatId: string): boolean { + console.log(`[conversationStore] Deleting conversation ${chatId}`); return conversations.delete(chatId); } @@ -110,8 +128,10 @@ export async function addMessageToConversation( msg: WhatsAppMessage, openWaUrl: string ): Promise { + console.log(`[conversationStore] Adding message to ${chatId}`); const conv = await getConversation(chatId, openWaUrl); - const mapped = toMsg(msg); + const mapped = mapMessage(msg); + // avoid duplicates if multiple webhook events deliver the same message if (!conv.messages.some((m) => m.id === mapped.id)) { conv.messages.push(mapped); if (conv.messages.length > 20) conv.messages.shift(); @@ -121,9 +141,9 @@ export async function addMessageToConversation( conv.participants.push({ id: s.id, name: s.pushname || s.name || '', - isMe: Boolean(s.isMe), + isMe: s.isMe, }); } return conv; -} +} \ No newline at end of file