regresado a la vida el router
All checks were successful
Deploy conversation layer / deploy (push) Successful in 20s

This commit is contained in:
2025-06-05 23:05:59 -06:00
parent 9a6dfa9471
commit 4c4052bb64
2 changed files with 66 additions and 77 deletions

View File

@@ -6,7 +6,7 @@ dotenv.config();
export type Handler = string | ((conv: Conversation) => Promise<string>); export type Handler = string | ((conv: Conversation) => Promise<string>);
export const chatHandlers: Record<string, Handler> = { export const chatHandlers: Record<string, Handler> = {
'50498554225@c.us': process.env.CONVERSATION_AGENT_URL || 'http://conversation-layer-agent:8001', '50498554225@c.us': process.env.CONVERSATION_AGENT_URL || 'http://localhost:8001',
'120363401804322608@g.us' : 'http://planilla-agent:8012' '120363401804322608@g.us' : 'http://planilla-agent:8012'
// Add other mappings like: // Add other mappings like:
// '50496210031@c.us': 'http://llm-agent:8000' // '50496210031@c.us': 'http://llm-agent:8000'

View File

@@ -1,84 +1,17 @@
import axios from 'axios'; import axios from 'axios';
import { WhatsAppMessage, Conversation, Msg, Participant } from '../types'; import { WhatsAppMessage, Conversation, Msg, Participant } from '../types';
/**
* Inmemory cache of conversations indexed by chatId.
*/
const conversations = new Map<string, Conversation>(); const conversations = new Map<string, Conversation>();
// ──────────────────────────── Helpers ─────────────────────────────
// ─────────────────────────────────────────────────────── internal helpers ────
/**
* Fetches *all* messages for a chat using OpenWA and returns them asis.
*/
async function fetchChatMessages(chatId: string, openWaUrl: string): Promise<WhatsAppMessage[]> { async function fetchChatMessages(chatId: string, openWaUrl: string): Promise<WhatsAppMessage[]> {
const { data } = await axios.post(`${openWaUrl}/loadAndGetAllMessagesInChat`, { const { data } = await axios.post(`${openWaUrl}/loadAndGetAllMessagesInChat`, {
args: { chatId, includeMe: true, includeNotifications: true }, args: { chatId, includeMe: true, includeNotifications: true },
}); });
return data?.response ?? data ?? []; return data?.response ?? data ?? [];
} }
/**
* Builds a complete {@link Conversation} object from raw WA messages.
*/
export async function buildConversation(chatId: string, openWaUrl: string): Promise<Conversation> {
const raw = await fetchChatMessages(chatId, openWaUrl);
const chatMeta = raw[0]?.chat;
const convo: Conversation = {
chatId,
title: chatMeta?.formattedTitle ?? chatMeta?.name ?? chatId,
isGroup: Boolean(chatMeta?.isGroup),
unreadCount: chatMeta?.unreadCount ?? 0,
participants: buildParticipants(raw),
messages: raw.slice(-100).map(toMsg).sort((a, b) => a.ts - b.ts),
createdAt: Date.now(),
};
conversations.set(chatId, convo);
return convo;
}
/**
* Collects unique participants from every message (and group metadata if any).
*/
function buildParticipants(messages: WhatsAppMessage[]): Participant[] {
const map = new Map<string, Participant>();
for (const m of messages) {
const s: any = m.sender ?? m.chat?.contact ?? null;
if (!s) continue;
if (!map.has(s.id)) {
map.set(s.id, {
id: s.id,
name: s.pushname || s.name || '',
isMe: Boolean(s.isMe),
isAdmin: Boolean(s.isAdmin || s.isSuperAdmin),
});
}
}
return [...map.values()];
}
function ensureParticipant(list: Participant[], sender: any): void {
if (!sender) return;
if (list.some((p) => p.id === sender.id)) return;
list.push({
id: sender.id,
name: sender.pushname || sender.name || '',
isMe: Boolean(sender.isMe),
});
}
/**
* Normalises a raw WhatsApp message to the lightweight {@link Msg} format.
*/
function toMsg(m: WhatsAppMessage): Msg { function toMsg(m: WhatsAppMessage): Msg {
const anyMsg = m as any; const anyMsg = m as any;
return { return {
@@ -88,7 +21,7 @@ function toMsg(m: WhatsAppMessage): Msg {
ts: anyMsg.timestamp ?? anyMsg.t ?? Date.now(), ts: anyMsg.timestamp ?? anyMsg.t ?? Date.now(),
type: anyMsg.type ?? 'chat', type: anyMsg.type ?? 'chat',
text: anyMsg.text ?? anyMsg.caption ?? anyMsg.body ?? '', text: anyMsg.text ?? anyMsg.caption ?? anyMsg.body ?? '',
mediaUrl: anyMsg.cloudUrl ?? anyMsg.clientUrl ?? undefined, mediaUrl: anyMsg.cloudUrl ?? anyMsg.clientUrl,
mentions: anyMsg.mentionedJidList ?? [], mentions: anyMsg.mentionedJidList ?? [],
meta: { meta: {
ack: anyMsg.ack ?? 0, ack: anyMsg.ack ?? 0,
@@ -98,7 +31,65 @@ function toMsg(m: WhatsAppMessage): Msg {
}; };
} }
// ─────────────────────────────────────────────────────────── public API ────── function buildParticipants(messages: WhatsAppMessage[], chat?: any): Participant[] {
const map = new Map<string, Participant>();
if (chat?.contact) {
const c = chat.contact;
map.set(c.id, {
id: c.id,
name: c.pushname || c.name || '',
isMe: Boolean(c.isMe),
});
}
if (chat?.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, {
id,
name: c.pushname || c.name || '',
isMe: Boolean(c.isMe),
isAdmin: Boolean(p.isAdmin || p.isSuperAdmin),
});
}
}
for (const m of messages) {
const s = m.sender;
if (s && !map.has(s.id)) {
map.set(s.id, {
id: s.id,
name: s.pushname || s.name || '',
isMe: Boolean(s.isMe),
});
}
}
return [...map.values()];
}
// ──────────────────────────── Public API ─────────────────────────────
export async function buildConversation(chatId: string, openWaUrl: string): Promise<Conversation> {
const raw = await fetchChatMessages(chatId, openWaUrl);
const chat = raw[0]?.chat;
const now = Date.now();
const conversation: Conversation = {
chatId,
title: chat?.formattedTitle ?? chat?.name ?? chatId,
isGroup: Boolean(chat?.isGroup),
unreadCount: chat?.unreadCount ?? 0,
participants: buildParticipants(raw, chat),
messages: raw.slice(-20).map(toMsg).sort((a, b) => a.ts - b.ts),
createdAt: conversations.get(chatId)?.createdAt || now,
};
conversations.set(chatId, conversation);
return conversation;
}
export async function getConversation(chatId: string, openWaUrl: string): Promise<Conversation> { export async function getConversation(chatId: string, openWaUrl: string): Promise<Conversation> {
let conv = conversations.get(chatId); let conv = conversations.get(chatId);
@@ -116,13 +107,11 @@ export function deleteConversation(chatId: string): boolean {
export async function addMessageToConversation( export async function addMessageToConversation(
chatId: string, chatId: string,
waMsg: WhatsAppMessage, msg: WhatsAppMessage,
openWaUrl: string, openWaUrl: string
): Promise<Conversation> { ): Promise<Conversation> {
console.log(`[conversationStore] Adding message to ${chatId}`);
const conv = await getConversation(chatId, openWaUrl); const conv = await getConversation(chatId, openWaUrl);
const mapped = mapMessage(msg); const mapped = toMsg(msg);
// avoid duplicates if multiple webhook events deliver the same message
if (!conv.messages.some((m) => m.id === mapped.id)) { if (!conv.messages.some((m) => m.id === mapped.id)) {
conv.messages.push(mapped); conv.messages.push(mapped);
if (conv.messages.length > 20) conv.messages.shift(); if (conv.messages.length > 20) conv.messages.shift();
@@ -132,7 +121,7 @@ export async function addMessageToConversation(
conv.participants.push({ conv.participants.push({
id: s.id, id: s.id,
name: s.pushname || s.name || '', name: s.pushname || s.name || '',
isMe: s.isMe, isMe: Boolean(s.isMe),
}); });
} }
return conv; return conv;