diff --git a/conversation-layer-agent/src/index.ts b/conversation-layer-agent/src/index.ts index b791493..8db03b2 100644 --- a/conversation-layer-agent/src/index.ts +++ b/conversation-layer-agent/src/index.ts @@ -6,12 +6,37 @@ import dotenv from 'dotenv'; dotenv.config(); +interface Participant { + id: string; + name: string; + isMe: boolean; + isAdmin?: boolean; +} + +interface Msg { + id: string; + from: string; + to: string; + ts: number; + type: 'chat' | 'image' | 'audio' | 'sticker' | 'doc'; + text?: string; + mediaUrl?: string; + mentions?: string[]; + meta: { + ack: number; + hasReaction: boolean; + isQuoted: boolean; + }; +} + interface Conversation { chatId: string; - messages: { text: string }[]; + title: string; + isGroup: boolean; + unreadCount: number; + participants: Participant[]; + messages: Msg[]; createdAt: number; - updatedAt: number; - messageCount: number; } const PORT = Number(process.env.PORT) || 8001; @@ -76,7 +101,7 @@ app.get('/', (req, res) => {

Conversation Layer Agent

This service answers questions about the repository.

Send a POST request to / with a JSON body containing {"conversation": {...}}

-

Example: {"conversation": {"chatId": "123@c.us", "messages": [{"text": "hello"}]}}

+

Example: {"conversation": {"chatId": "123@c.us", "title": "Alice", "isGroup": false, "unreadCount": 0, "participants": [{"id": "123@c.us", "name": "Alice", "isMe": false}], "messages": [{"id": "msg1", "from": "123@c.us", "to": "me@c.us", "ts": 0, "type": "chat", "text": "hello", "meta": {"ack": 0, "hasReaction": false, "isQuoted": false}}], "createdAt": 0 }}

It will respond with a JSON object containing {"reply": "the answer"}

Repository info: ${repoInfo}

diff --git a/whatsapp-router/src/store/conversation.ts b/whatsapp-router/src/store/conversation.ts index 18d938c..6c23e83 100644 --- a/whatsapp-router/src/store/conversation.ts +++ b/whatsapp-router/src/store/conversation.ts @@ -1,5 +1,5 @@ import axios from 'axios'; -import { WhatsAppMessage, Conversation } from '../types'; +import { WhatsAppMessage, Conversation, Msg, Participant } from '../types'; const conversations = new Map(); @@ -19,6 +19,24 @@ async function loadMessages( return msgs; } +function mapMessage(m: WhatsAppMessage): Msg { + return { + id: m.id, + from: m.from, + to: m.to, + 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: (m as any).ack || 0, + hasReaction: (m as any).hasReaction || false, + isQuoted: !!(m as any).quotedMsg, + }, + }; +} + export async function getConversation( chatId: string, openWaUrl: string @@ -41,15 +59,61 @@ export async function buildConversation( openWaUrl: string ): Promise { console.log(`[conversationStore] Building conversation for ${chatId}`); - const messages = await loadMessages(chatId, openWaUrl); + 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; + participantsMap.set(c.id, { + id: c.id, + name: c.pushname || c.name || '', + isMe: c.isMe, + }); + } + if (isGroup && chat?.groupMetadata?.participants) { + for (const p of chat.groupMetadata.participants as any[]) { + const c = p.contact || {}; + const id = c.id || p.id; + participantsMap.set(id, { + id, + name: c.pushname || c.name || '', + isMe: c.isMe || false, + isAdmin: p.isAdmin || p.isSuperAdmin, + }); + } + } + + for (const m of rawMessages) { + const s = m.sender; + if (s && !participantsMap.has(s.id)) { + participantsMap.set(s.id, { + id: s.id, + name: s.pushname || s.name || '', + isMe: s.isMe, + }); + } + } + + const messages: Msg[] = rawMessages.slice(-20).map(mapMessage); + messages.sort((a, b) => a.ts - b.ts); + const conv: Conversation = { chatId, + title, + isGroup, + unreadCount, + participants: Array.from(participantsMap.values()), messages, createdAt: conversations.get(chatId)?.createdAt || now, - updatedAt: now, - messageCount: messages.length, }; + conversations.set(chatId, conv); return conv; } @@ -66,8 +130,16 @@ export async function addMessageToConversation( ): Promise { console.log(`[conversationStore] Adding message to ${chatId}`); const conv = await getConversation(chatId, openWaUrl); - conv.messages.push(msg); - conv.messageCount = conv.messages.length; - conv.updatedAt = Date.now(); + const mapped = mapMessage(msg); + conv.messages.push(mapped); + if (conv.messages.length > 20) conv.messages.shift(); + const s = msg.sender; + if (s && !conv.participants.some((p) => p.id === s.id)) { + conv.participants.push({ + id: s.id, + name: s.pushname || s.name || '', + isMe: s.isMe, + }); + } return conv; } diff --git a/whatsapp-router/src/types.ts b/whatsapp-router/src/types.ts index 5152240..4bbdedb 100644 --- a/whatsapp-router/src/types.ts +++ b/whatsapp-router/src/types.ts @@ -193,10 +193,35 @@ export interface WhatsAppMessage { text: string; } +export interface Participant { + id: string; + name: string; + isMe: boolean; + isAdmin?: boolean; +} + +export interface Msg { + id: string; + from: string; + to: string; + ts: number; + type: 'chat' | 'image' | 'audio' | 'sticker' | 'doc'; + text?: string; + mediaUrl?: string; + mentions?: string[]; + meta: { + ack: number; + hasReaction: boolean; + isQuoted: boolean; + }; +} + export interface Conversation { chatId: string; - messages: WhatsAppMessage[]; + title: string; + isGroup: boolean; + unreadCount: number; + participants: Participant[]; + messages: Msg[]; createdAt: number; - updatedAt: number; - messageCount: number; }