ahora tambien enruta mensajes enviados por chatUI
This commit is contained in:
@@ -7,7 +7,11 @@ export type Handler = string | ((conv: Conversation) => Promise<string>);
|
||||
|
||||
export const chatHandlers: Record<string, Handler> = {
|
||||
'50498554225@c.us': process.env.CONVERSATION_AGENT_URL || 'http://conversation-layer-agent:8001',
|
||||
'120363401804322608@g.us' : process.env.PLANILLA_AGENT_URL ||'http://planilla-agent:8012'
|
||||
'120363401804322608@g.us' : process.env.PLANILLA_AGENT_URL ||'http://planilla-agent:8012',
|
||||
'planilla-UI' : process.env.PLANILLA_AGENT_URL ||'http://planilla-agent:8012'
|
||||
|
||||
//map any conversation that follow this pattern, planilla
|
||||
|
||||
// Add other mappings like:
|
||||
// '50496210031@c.us': 'http://llm-agent:8000'
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import express from 'express';
|
||||
import dotenv from 'dotenv';
|
||||
import { registerConversationRoutes } from './routes/conversationActions';
|
||||
import { registerChatUIRoutes } from './routes/chatUI_Actions';
|
||||
import whatsappActionsRouter from './routes/whatsappActions';
|
||||
import { registerLogSse } from './sse/logSse';
|
||||
import cors from 'cors';
|
||||
@@ -68,6 +69,7 @@ const config: WebhookConfig = {
|
||||
|
||||
registerConversationRoutes(app, openWaUrl);
|
||||
registerWebhookRoutes(app, config, openWaUrl, agentUrl);
|
||||
registerChatUIRoutes(app, openWaUrl);
|
||||
app.use('/whatsapp', whatsappActionsRouter); // New line
|
||||
|
||||
// Register new whatsappActions routes
|
||||
|
||||
304
whatsapp-router/src/routes/chatUI_Actions.ts
Normal file
304
whatsapp-router/src/routes/chatUI_Actions.ts
Normal file
@@ -0,0 +1,304 @@
|
||||
import { Application } from 'express';
|
||||
import express from 'express';
|
||||
import { WhatsAppMessage, Conversation, Msg } from '../types';
|
||||
import { addMessageToConversation } from '../store/conversation';
|
||||
import { mapWhatsAppMessageToMsg } from '../messageProcessor';
|
||||
import { getHandler } from '../chatHandlers';
|
||||
import axios from 'axios';
|
||||
|
||||
interface UIMessage {
|
||||
chatId: string;
|
||||
id: string;
|
||||
from: string;
|
||||
to: string;
|
||||
ts: number;
|
||||
type: 'chat';
|
||||
text: string;
|
||||
mediaUrl: string | null;
|
||||
mentions: string[] | null;
|
||||
meta: {
|
||||
ack: number;
|
||||
hasReaction: boolean;
|
||||
isQuoted: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const conversations = new Map<string, Conversation>();
|
||||
|
||||
|
||||
export const processIncomingMessageForConversation = (
|
||||
conversation: Conversation,
|
||||
message: WhatsAppMessage
|
||||
): void => {
|
||||
const mappedMsg = mapWhatsAppMessageToMsg(message);
|
||||
|
||||
// Avoid duplicates if multiple webhook events deliver the same message
|
||||
if (!conversation.messages.some((m) => m.id === mappedMsg.id)) {
|
||||
conversation.messages.push(mappedMsg);
|
||||
// if (conversation.messages.length > 20) {
|
||||
// conversation.messages.shift(); // Keep only the last 20 messages
|
||||
// }
|
||||
}
|
||||
|
||||
// Add new participants if necessary
|
||||
const sender = message.sender;
|
||||
if (sender && !conversation.participants.some((p) => p.id === sender.id)) {
|
||||
conversation.participants.push({
|
||||
id: sender.id,
|
||||
name: sender.pushname || sender.name || '',
|
||||
isMe: sender.isMe,
|
||||
// isAdmin property is not available here, it's part of group metadata
|
||||
});
|
||||
}
|
||||
// The conversation object is modified directly
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export function registerChatUIRoutes(app: Application, openWaUrl: string | undefined) {
|
||||
app.post('/chatUI/sendMessage', async (req: express.Request, res: express.Response) => {
|
||||
console.log(`[routes] POST /chatUI/sendMessage`, 'v2.0');
|
||||
|
||||
try {
|
||||
if (!req.body || !req.body.data) {
|
||||
throw new Error('Invalid request format');
|
||||
}
|
||||
|
||||
const uiMessage: UIMessage = req.body.data;
|
||||
|
||||
if (!uiMessage.text || !uiMessage.chatId) {
|
||||
throw new Error('Missing required fields: text and chatId');
|
||||
}
|
||||
|
||||
// convertimos el mensaje de la UI a un mensaje de whatsapp
|
||||
const message: WhatsAppMessage = {
|
||||
id: uiMessage.id,
|
||||
body: uiMessage.text,
|
||||
text: uiMessage.text,
|
||||
type: uiMessage.type,
|
||||
from: uiMessage.from,
|
||||
to: uiMessage.to,
|
||||
chatId: uiMessage.chatId,
|
||||
timestamp: uiMessage.ts * 1000, // Convertir a milisegundos
|
||||
fromMe: true,
|
||||
viewed: false,
|
||||
t: uiMessage.ts * 1000,
|
||||
notifyName: 'User',
|
||||
author: null,
|
||||
invis: false,
|
||||
isNewMsg: true,
|
||||
star: false,
|
||||
kicNotified: false,
|
||||
recvFresh: true,
|
||||
isFromTemplate: false,
|
||||
pollInvalidated: false,
|
||||
isSentCagPollCreation: false,
|
||||
latestEditMsgKey: null,
|
||||
latestEditSenderTimestampMs: null,
|
||||
mentionedJidList: uiMessage.mentions || [],
|
||||
groupMentions: [],
|
||||
isEventCanceled: false,
|
||||
eventInvalidated: false,
|
||||
isVcardOverMmsDocument: false,
|
||||
labels: [],
|
||||
hasReaction: uiMessage.meta.hasReaction,
|
||||
ephemeralDuration: 0,
|
||||
ephemeralSettingTimestamp: 0,
|
||||
disappearingModeInitiator: '',
|
||||
disappearingModeTrigger: '',
|
||||
viewMode: '',
|
||||
productHeaderImageRejected: false,
|
||||
lastPlaybackProgress: 0,
|
||||
isDynamicReplyButtonsMsg: false,
|
||||
isCarouselCard: false,
|
||||
parentMsgId: null,
|
||||
callSilenceReason: null,
|
||||
isVideoCall: false,
|
||||
callDuration: null,
|
||||
callParticipants: null,
|
||||
isMdHistoryMsg: false,
|
||||
stickerSentTs: 0,
|
||||
isAvatar: false,
|
||||
lastUpdateFromServerTs: 0,
|
||||
invokedBotWid: null,
|
||||
bizBotType: null,
|
||||
botResponseTargetId: null,
|
||||
botPluginType: null,
|
||||
botPluginReferenceIndex: null,
|
||||
botPluginSearchProvider: null,
|
||||
botPluginSearchUrl: null,
|
||||
botPluginSearchQuery: null,
|
||||
botPluginMaybeParent: false,
|
||||
botReelPluginThumbnailCdnUrl: null,
|
||||
botMessageDisclaimerText: null,
|
||||
botMsgBodyType: null,
|
||||
reportingTokenInfo: null,
|
||||
requiresDirectConnection: false,
|
||||
bizContentPlaceholderType: null,
|
||||
hostedBizEncStateMismatch: false,
|
||||
senderOrRecipientAccountTypeHosted: false,
|
||||
placeholderCreatedWhenAccountIsHosted: false,
|
||||
device: 0,
|
||||
local: true,
|
||||
mId: uiMessage.id,
|
||||
senderId: uiMessage.from,
|
||||
content: uiMessage.text,
|
||||
isGroupMsg: false,
|
||||
isQuotedMsgAvailable: uiMessage.meta.isQuoted,
|
||||
isMedia: !!uiMessage.mediaUrl,
|
||||
mediaData: uiMessage.mediaUrl ? { url: uiMessage.mediaUrl } : {},
|
||||
isOnline: false,
|
||||
sender: {
|
||||
id: uiMessage.from,
|
||||
name: 'User',
|
||||
shortName: 'User',
|
||||
pushname: 'User',
|
||||
type: 'user',
|
||||
isBusiness: false,
|
||||
isEnterprise: false,
|
||||
isSmb: false,
|
||||
isContactSyncCompleted: 1,
|
||||
disappearingModeDuration: 0,
|
||||
disappearingModeSettingTimestamp: 0,
|
||||
textStatusLastUpdateTime: 0,
|
||||
syncToAddressbook: false,
|
||||
formattedName: 'User',
|
||||
isMe: true,
|
||||
isMyContact: false,
|
||||
isPSA: false,
|
||||
isUser: true,
|
||||
isVerified: false,
|
||||
isWAContact: true,
|
||||
msgs: []
|
||||
},
|
||||
chat: {
|
||||
id: uiMessage.chatId,
|
||||
name: 'Chat',
|
||||
isGroup: false,
|
||||
participantsCount: 2,
|
||||
formattedTitle: 'Chat',
|
||||
pendingMsgs: false,
|
||||
t: uiMessage.ts * 1000,
|
||||
unreadCount: 0,
|
||||
unreadDividerOffset: 0,
|
||||
archive: false,
|
||||
isReadOnly: false,
|
||||
isLocked: false,
|
||||
muteExpiration: 0,
|
||||
isAutoMuted: false,
|
||||
notSpam: true,
|
||||
pin: 0,
|
||||
ephemeralDuration: 0,
|
||||
ephemeralSettingTimestamp: 0,
|
||||
disappearingModeInitiator: '',
|
||||
disappearingModeTrigger: '',
|
||||
createdLocally: false,
|
||||
unreadMentionsOfMe: [],
|
||||
unreadMentionCount: 0,
|
||||
hasUnreadMention: false,
|
||||
archiveAtMentionViewedInDrawer: false,
|
||||
hasChatBeenOpened: true,
|
||||
tcToken: {},
|
||||
tcTokenTimestamp: 0,
|
||||
tcTokenSenderTimestamp: 0,
|
||||
endOfHistoryTransferType: 0,
|
||||
pendingInitialLoading: false,
|
||||
unreadEditTimestampMs: 0,
|
||||
celebrationAnimationLastPlayed: 0,
|
||||
hasRequestedWelcomeMsg: false,
|
||||
canSend: true,
|
||||
groupMetadata: {},
|
||||
isOnline: false,
|
||||
contact: {
|
||||
id: uiMessage.chatId,
|
||||
name: 'User',
|
||||
shortName: 'User',
|
||||
pushname: 'User',
|
||||
type: 'user',
|
||||
isBusiness: false,
|
||||
isEnterprise: false,
|
||||
isSmb: false,
|
||||
isContactSyncCompleted: 1,
|
||||
disappearingModeDuration: 0,
|
||||
disappearingModeSettingTimestamp: 0,
|
||||
textStatusLastUpdateTime: 0,
|
||||
syncToAddressbook: false,
|
||||
formattedName: 'User',
|
||||
isMe: true,
|
||||
isMyContact: false,
|
||||
isPSA: false,
|
||||
isUser: true,
|
||||
isVerified: false,
|
||||
isWAContact: true,
|
||||
msgs: []
|
||||
},
|
||||
msgs: []
|
||||
}
|
||||
};
|
||||
|
||||
// nosotros mismos tenemos que buscar la conversacion in la memoria, conversations. si no existe, crearla
|
||||
let conversation = conversations.get(uiMessage.chatId);
|
||||
if (!conversation) {
|
||||
conversation = {
|
||||
chatId: uiMessage.chatId,
|
||||
title: 'Chat',
|
||||
isGroup: false,
|
||||
unreadCount: 0,
|
||||
participants: [],
|
||||
messages: [],
|
||||
createdAt: Date.now()
|
||||
}
|
||||
conversations.set(uiMessage.chatId, conversation);
|
||||
}
|
||||
processIncomingMessageForConversation(conversation, message);
|
||||
|
||||
|
||||
const handler = getHandler(uiMessage.chatId);
|
||||
if (!handler) throw new Error('No handler configured');
|
||||
let reply: string;
|
||||
if (typeof handler === 'string') {
|
||||
console.log(`🔗 Calling agent at ${handler} for conversation ${uiMessage.chatId}\n`);
|
||||
|
||||
const agentRes = await axios.post(handler, { conversation });
|
||||
reply = agentRes.data.reply || agentRes.data;
|
||||
} else {
|
||||
reply = await handler(conversation);
|
||||
}
|
||||
|
||||
const replyMsg: Msg = {
|
||||
id: uiMessage.id,
|
||||
from: uiMessage.to,
|
||||
to: uiMessage.from,
|
||||
ts: uiMessage.ts,
|
||||
type: 'chat',
|
||||
text: reply,
|
||||
mediaUrl: undefined,
|
||||
mentions: [],
|
||||
meta: {
|
||||
ack: 0,
|
||||
hasReaction: false,
|
||||
isQuoted: false
|
||||
}
|
||||
};
|
||||
conversation.messages.push(replyMsg);
|
||||
console.log('conversation', conversation);
|
||||
|
||||
|
||||
// Devolver la conversación completa
|
||||
/* it needs to be able to be parsed by the UI
|
||||
const response = await axios.post(routerUrl, {data:messagePayload});
|
||||
console.log('Message sent, raw response:', response.data); // Log raw response
|
||||
|
||||
const conversation = response.data;
|
||||
*/
|
||||
res.json(conversation);
|
||||
|
||||
} catch (err: any) {
|
||||
console.error('Error processing UI message:', err.message);
|
||||
res.status(400).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -43,4 +43,5 @@ export function registerConversationRoutes(app: Application, openWaUrl: string |
|
||||
console.log(`Conversation ${req.params.id} deleted: ${deleted}`);
|
||||
res.json({ success: deleted });
|
||||
});
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user