From 2a999a1cb7014de64600691c64405a65b550931a Mon Sep 17 00:00:00 2001 From: josedario87 <71241187+josedario87@users.noreply.github.com> Date: Sat, 7 Jun 2025 10:52:21 -0600 Subject: [PATCH] feat: support additional WhatsApp API functions --- mcp/modules/whatsappIntegration.js | 102 +++++++++++++++ whatsapp-router/src/routes/whatsappActions.ts | 121 ++++++++++++++++++ whatsapp-router/src/whatsappClient.ts | 84 ++++++++++++ 3 files changed, 307 insertions(+) diff --git a/mcp/modules/whatsappIntegration.js b/mcp/modules/whatsappIntegration.js index 801bab5..07b839e 100644 --- a/mcp/modules/whatsappIntegration.js +++ b/mcp/modules/whatsappIntegration.js @@ -199,4 +199,106 @@ export default function registerWhatsappIntegration(server) { return { content: [{ type: "text", text: JSON.stringify(result) }] }; } ); + + server.tool( + "whatsapp.get-all-contacts", + "Obtiene todos los contactos.", + {}, + async () => { + log("Tool invoked", "whatsapp.get-all-contacts"); + const result = await fetchJSON("/whatsapp/getAllContacts", { + method: "POST", + body: JSON.stringify({ args: {} }), + }); + return { content: [{ type: "text", text: JSON.stringify(result) }] }; + } + ); + + server.tool( + "whatsapp.get-contact", + "Obtiene la información de un contacto.", + { + contactId: z.string(), + }, + async (params) => { + log("Tool invoked", "whatsapp.get-contact", params); + const result = await fetchJSON("/whatsapp/getContact", { + method: "POST", + body: JSON.stringify({ args: params }), + }); + return { content: [{ type: "text", text: JSON.stringify(result) }] }; + } + ); + + server.tool( + "whatsapp.load-earlier-messages", + "Carga mensajes antiguos hasta una fecha dada.", + { + contactId: z.string(), + timestamp: z.number(), + }, + async (params) => { + log("Tool invoked", "whatsapp.load-earlier-messages", params); + const result = await fetchJSON("/whatsapp/loadEarlierMessagesTillDate", { + method: "POST", + body: JSON.stringify({ args: params }), + }); + return { content: [{ type: "text", text: JSON.stringify(result) }] }; + } + ); + + server.tool( + "whatsapp.react", + "Reacciona a un mensaje.", + { + messageId: z.string(), + emoji: z.string(), + }, + async (params) => { + log("Tool invoked", "whatsapp.react", params); + const result = await fetchJSON("/whatsapp/react", { + method: "POST", + body: JSON.stringify({ args: params }), + }); + return { content: [{ type: "text", text: JSON.stringify(result) }] }; + } + ); + + server.tool( + "whatsapp.reply", + "Responde a un mensaje.", + { + to: z.string(), + content: z.string(), + quotedMsgId: z.string(), + sendSeen: z.boolean().optional(), + }, + async (params) => { + log("Tool invoked", "whatsapp.reply", params); + const result = await fetchJSON("/whatsapp/reply", { + method: "POST", + body: JSON.stringify({ args: params }), + }); + return { content: [{ type: "text", text: JSON.stringify(result) }] }; + } + ); + + server.tool( + "whatsapp.send-text-with-mentions", + "Envía un texto mencionando contactos.", + { + to: z.string(), + content: z.string(), + hideTags: z.boolean().optional(), + mentions: z.array(z.string()).optional(), + }, + async (params) => { + log("Tool invoked", "whatsapp.send-text-with-mentions", params); + const result = await fetchJSON("/whatsapp/sendTextWithMentions", { + method: "POST", + body: JSON.stringify({ args: params }), + }); + return { content: [{ type: "text", text: JSON.stringify(result) }] }; + } + ); } diff --git a/whatsapp-router/src/routes/whatsappActions.ts b/whatsapp-router/src/routes/whatsappActions.ts index e83910c..3e4eabf 100644 --- a/whatsapp-router/src/routes/whatsappActions.ts +++ b/whatsapp-router/src/routes/whatsappActions.ts @@ -278,4 +278,125 @@ router.post('/getAllMessagesInChat', async (req: Request, res: Response) => { } }); +router.post('/getAllContacts', async (_req: Request, res: Response) => { + console.log('[routes/whatsappActions] POST /getAllContacts called'); + try { + const result = await whatsappClient.getAllContacts(openWaUrl!); + res.json(result); + } catch (error: any) { + console.error('[routes/whatsappActions] Error in /getAllContacts:', error.message); + try { + const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{'))); + return res.status(parsedError.status || 500).json({ error: parsedError }); + } catch { + res.status(500).json({ error: error.message || 'Failed to retrieve contacts' }); + } + } +}); + +router.post('/getContact', async (req: Request, res: Response) => { + console.log('[routes/whatsappActions] POST /getContact called'); + try { + const { args } = req.body; + const { contactId } = args || {}; + if (!contactId) { + return res.status(400).json({ error: 'Missing "contactId" in request body' }); + } + const result = await whatsappClient.getContact(openWaUrl!, contactId); + res.json(result); + } catch (error: any) { + console.error('[routes/whatsappActions] Error in /getContact:', error.message); + try { + const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{'))); + return res.status(parsedError.status || 500).json({ error: parsedError }); + } catch { + res.status(500).json({ error: error.message || 'Failed to retrieve contact' }); + } + } +}); + +router.post('/loadEarlierMessagesTillDate', async (req: Request, res: Response) => { + console.log('[routes/whatsappActions] POST /loadEarlierMessagesTillDate called'); + try { + const { args } = req.body; + const { contactId, timestamp } = args || {}; + if (!contactId || typeof timestamp !== 'number') { + return res.status(400).json({ error: 'Missing "contactId" or "timestamp" in request body' }); + } + const result = await whatsappClient.loadEarlierMessagesTillDate(openWaUrl!, { contactId, timestamp }); + res.json(result); + } catch (error: any) { + console.error('[routes/whatsappActions] Error in /loadEarlierMessagesTillDate:', error.message); + try { + const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{'))); + return res.status(parsedError.status || 500).json({ error: parsedError }); + } catch { + res.status(500).json({ error: error.message || 'Failed to load messages' }); + } + } +}); + +router.post('/react', async (req: Request, res: Response) => { + console.log('[routes/whatsappActions] POST /react called'); + try { + const { args } = req.body; + const { messageId, emoji } = args || {}; + if (!messageId || !emoji) { + return res.status(400).json({ error: 'Missing "messageId" or "emoji" in request body' }); + } + const result = await whatsappClient.react(openWaUrl!, { messageId, emoji }); + res.json(result); + } catch (error: any) { + console.error('[routes/whatsappActions] Error in /react:', error.message); + try { + const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{'))); + return res.status(parsedError.status || 500).json({ error: parsedError }); + } catch { + res.status(500).json({ error: error.message || 'Failed to react to message' }); + } + } +}); + +router.post('/reply', async (req: Request, res: Response) => { + console.log('[routes/whatsappActions] POST /reply called'); + try { + const { args } = req.body; + const { to, content, quotedMsgId, sendSeen } = args || {}; + if (!to || !content || !quotedMsgId) { + return res.status(400).json({ error: 'Missing "to", "content" or "quotedMsgId" in request body' }); + } + const result = await whatsappClient.reply(openWaUrl!, { to, content, quotedMsgId, sendSeen }); + res.json(result); + } catch (error: any) { + console.error('[routes/whatsappActions] Error in /reply:', error.message); + try { + const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{'))); + return res.status(parsedError.status || 500).json({ error: parsedError }); + } catch { + res.status(500).json({ error: error.message || 'Failed to send reply' }); + } + } +}); + +router.post('/sendTextWithMentions', async (req: Request, res: Response) => { + console.log('[routes/whatsappActions] POST /sendTextWithMentions called'); + try { + const { args } = req.body; + const { to, content, hideTags, mentions } = args || {}; + if (!to || !content) { + return res.status(400).json({ error: 'Missing "to" or "content" in request body' }); + } + const result = await whatsappClient.sendTextWithMentions(openWaUrl!, { to, content, hideTags, mentions }); + res.json(result); + } catch (error: any) { + console.error('[routes/whatsappActions] Error in /sendTextWithMentions:', error.message); + try { + const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{'))); + return res.status(parsedError.status || 500).json({ error: parsedError }); + } catch { + res.status(500).json({ error: error.message || 'Failed to send text with mentions' }); + } + } +}); + export default router; diff --git a/whatsapp-router/src/whatsappClient.ts b/whatsapp-router/src/whatsappClient.ts index cae96e8..8165348 100644 --- a/whatsapp-router/src/whatsappClient.ts +++ b/whatsapp-router/src/whatsappClient.ts @@ -400,3 +400,87 @@ export async function forwardMessages( throw new Error(`whatsappClient error (${openWaUrl}/forwardMessages): ${error.message}`); } } + +export async function getAllContacts( + openWaUrl: string +): Promise> { + try { + const response = await axios.post(`${openWaUrl}/getAllContacts`); + return response.data?.response || response.data; + } catch (error: any) { + console.error('[whatsappClient] Error retrieving all contacts:', error.message); + if (axios.isAxiosError(error) && error.response) { + console.error('[whatsappClient] Axios error details:', error.response.data); + throw new Error(`whatsappClient API error (${openWaUrl}/getAllContacts): ${error.response.status} - ${JSON.stringify(error.response.data)}`); + } + throw new Error(`whatsappClient error (${openWaUrl}/getAllContacts): ${error.message}`); + } +} + +export async function loadEarlierMessagesTillDate( + openWaUrl: string, + args: { contactId: string; timestamp: number } +): Promise> { + try { + const response = await axios.post(`${openWaUrl}/loadEarlierMessagesTillDate`, { args }); + return response.data?.response || response.data; + } catch (error: any) { + console.error(`[whatsappClient] Error loading messages for ${args.contactId}:`, error.message); + if (axios.isAxiosError(error) && error.response) { + console.error('[whatsappClient] Axios error details:', error.response.data); + throw new Error(`whatsappClient API error (${openWaUrl}/loadEarlierMessagesTillDate): ${error.response.status} - ${JSON.stringify(error.response.data)}`); + } + throw new Error(`whatsappClient error (${openWaUrl}/loadEarlierMessagesTillDate): ${error.message}`); + } +} + +export async function react( + openWaUrl: string, + args: { messageId: string; emoji: string } +): Promise { + try { + const response = await axios.post(`${openWaUrl}/react`, { args }); + return response.data?.response || response.data; + } catch (error: any) { + console.error(`[whatsappClient] Error reacting to message ${args.messageId}:`, error.message); + if (axios.isAxiosError(error) && error.response) { + console.error('[whatsappClient] Axios error details:', error.response.data); + throw new Error(`whatsappClient API error (${openWaUrl}/react): ${error.response.status} - ${JSON.stringify(error.response.data)}`); + } + throw new Error(`whatsappClient error (${openWaUrl}/react): ${error.message}`); + } +} + +export async function reply( + openWaUrl: string, + args: { to: string; content: string; quotedMsgId: string; sendSeen?: boolean } +): Promise { + try { + const response = await axios.post(`${openWaUrl}/reply`, { args }); + return response.data?.response || response.data; + } catch (error: any) { + console.error(`[whatsappClient] Error replying to ${args.to}:`, error.message); + if (axios.isAxiosError(error) && error.response) { + console.error('[whatsappClient] Axios error details:', error.response.data); + throw new Error(`whatsappClient API error (${openWaUrl}/reply): ${error.response.status} - ${JSON.stringify(error.response.data)}`); + } + throw new Error(`whatsappClient error (${openWaUrl}/reply): ${error.message}`); + } +} + +export async function sendTextWithMentions( + openWaUrl: string, + args: { to: string; content: string; hideTags?: boolean; mentions?: string[] } +): Promise { + try { + const response = await axios.post(`${openWaUrl}/sendTextWithMentions`, { args }); + return response.data?.response || response.data; + } catch (error: any) { + console.error(`[whatsappClient] Error sending text with mentions to ${args.to}:`, error.message); + if (axios.isAxiosError(error) && error.response) { + console.error('[whatsappClient] Axios error details:', error.response.data); + throw new Error(`whatsappClient API error (${openWaUrl}/sendTextWithMentions): ${error.response.status} - ${JSON.stringify(error.response.data)}`); + } + throw new Error(`whatsappClient error (${openWaUrl}/sendTextWithMentions): ${error.message}`); + } +}