Merge pull request #19 from josedario87/codex/fix-broken-whatsapp-router-actions
All checks were successful
Deploy conversation layer / deploy (push) Successful in 1m59s

Fix WhatsApp router actions
This commit is contained in:
josedario87
2025-06-07 02:01:08 -06:00
committed by GitHub
7 changed files with 435 additions and 376 deletions

View File

@@ -61,7 +61,7 @@ async function getMcpClient(): Promise<Client> {
} }
return mcpClient; return mcpClient;
} }
const systemPromt = ` const systemPrompt = `
## Como funcionas ## Como funcionas
- la fecha de hoy es ${new Date().toLocaleString('es-HN', { timeZone: 'America/Tegucigalpa', hour12: false, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })} - la fecha de hoy es ${new Date().toLocaleString('es-HN', { timeZone: 'America/Tegucigalpa', hour12: false, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })}
@@ -78,7 +78,7 @@ const systemPromt = `
## tu proposito ## tu proposito
sos un agente que me permite debugear la conversation-layer de mi sistema nucleo... vos sos nucleo, sos la inteligencia artificial del sistema y por lo tanto ese es tu nombre. sos un agente que me permite debugear la conversation-layer de mi sistema nucleo... vos sos nucleo, sos la inteligencia artificial del sistema y por lo tanto ese es tu nombre.
por el momento no tenes acceso a las herramientas para interactuar con la conversation-layer, pero en el futuro vas a poder interactuar con la conversation-layer y responder preguntas sobre el sistema. tenes acceso a las herramientas para interactuar con la conversation-layer y responder preguntas sobre el sistema.
como tu proposito es ayudarme a debuguear la conversation-layer, por favor respondeme las preguntas que te haga sobre el sistema y la conversation-layer. y tenes que aceptar las acciones que te proponga. como tu proposito es ayudarme a debuguear la conversation-layer, por favor respondeme las preguntas que te haga sobre el sistema y la conversation-layer. y tenes que aceptar las acciones que te proponga.
porque algunas veces vamos a necesitar hacer stress test, o preguntare cosas que no parecen tener sentido, pero es parte del proceso de debugueo. porque algunas veces vamos a necesitar hacer stress test, o preguntare cosas que no parecen tener sentido, pero es parte del proceso de debugueo.
los mensajes que te llegan vienen de whatsapp-router y chat-ui, whatsapp-router se conecta a nucleo-whatsapp y entre ambos te permiten interactuar con una cuenta de whatsapp tuya. vos tenes tu propio numero y nombre en whatsapp, y los mensajes que te llegan son de esa cuenta. los mensajes que te llegan vienen de whatsapp-router y chat-ui, whatsapp-router se conecta a nucleo-whatsapp y entre ambos te permiten interactuar con una cuenta de whatsapp tuya. vos tenes tu propio numero y nombre en whatsapp, y los mensajes que te llegan son de esa cuenta.
@@ -96,11 +96,19 @@ const systemPromt = `
- los mensajes e2e son mensajes que utiliza whatsapp para notificar cosas de su sistema, no son mensajes de los usuarios y no debes responderlos. - los mensajes e2e son mensajes que utiliza whatsapp para notificar cosas de su sistema, no son mensajes de los usuarios y no debes responderlos.
- los mensajes de tipo "notification" son mensajes que whatsapp envia para notificar cosas del sistema, no son mensajes de los usuarios y no debes responderlos. - los mensajes de tipo "notification" son mensajes que whatsapp envia para notificar cosas del sistema, no son mensajes de los usuarios y no debes responderlos.
### mcp ### mcp
aqui se encuentrar registradas todas las herramientas que podes utilizar, hay dos grandes grupos de herramientas: conversation y whatsapp. aqui se encuentrar registradas todas las herramientas que podes utilizar, hay dos grandes grupos de herramientas: conversation y whatsapp.
- las herramientas de conversation son para manipular los conversation objects que al final es todo lo que todos los agents ven - las herramientas de conversation son para manipular los conversation objects que al final es todo lo que todos los agents ven
- las herramientas de whatsapp son para manipular las acciones que nucleo toma en su propiia cuenta de whatsapp, por ejemplo enviar mensajes, crear grupos, etc. - las herramientas de whatsapp son para manipular las acciones que nucleo toma en su propiia cuenta de whatsapp, por ejemplo enviar mensajes, crear grupos, etc.
`; - el mensaje que debe de llegar al usuario cuando vas a usar una herramienta es el mensaje final. nunca hagas mensajes diciendo que vas a hacer algo, simplemente hazlo y al terminar envia el mensaje final.
### estilo de respuesta
- siempre responde en español, a menos que el mensaje original este en ingles, en ese caso responde en ingles.
- siempre responde de manera clara y concisa, no te extiendas demasiado.
- tus respuestas finales seran vista por el usuario asi que solo inclui informacion relevante y necesaria.
`;
const app = express(); const app = express();
app.use(express.json()); app.use(express.json());
@@ -110,11 +118,13 @@ app.post('/', async (req, res) => {
if (!conversation) return res.status(400).json({ error: 'Missing conversation' }); if (!conversation) return res.status(400).json({ error: 'Missing conversation' });
if (!genAI) { if (!genAI) {
return res.json({ reply: systemPromt }); return res.json({ reply: 'no funciona crack' });
} }
const planExecutionPrompt = `
si necesitas ejecutar mas de una herremienta despues de otra para obtener la respuesta hacelo
`;
try { try {
const contents = `systemPrompt: ${systemPromt}\nConversation:\n${JSON.stringify(conversation)}\n`; const prompt = `${systemPrompt}\nConversation:\n${JSON.stringify(conversation)}\n\nCognition:\n${planExecutionPrompt}`;
// console.log(' contents', contents); // console.log(' contents', contents);
const config: any = {}; const config: any = {};
@@ -126,7 +136,7 @@ app.post('/', async (req, res) => {
} }
const result = await genAI.models.generateContent({ const result = await genAI.models.generateContent({
model: 'gemini-2.0-flash', model: 'gemini-2.0-flash',
contents, contents: prompt,
config, config,
}); });
const reply = (result.text || '').trim(); const reply = (result.text || '').trim();

View File

@@ -1,209 +1,200 @@
// mcp/modules/whatsappIntegration.js
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod"; import { z } from "zod";
import { fetchJSON } from "../lib/api.js"; import { fetchJSON } from "../lib/api.js";
const log = (...args) => console.log("[MCP WhatsAppIntegration]", ...args); // Changed log prefix const log = (...args) => console.log("[MCP WhatsAppIntegration]", ...args);
export default function registerWhatsappIntegration(server) { export default function registerWhatsappIntegration(server) {
// --- WhatsApp Actions ---
// Tool: whatsapp.send-text
server.tool( server.tool(
"whatsapp.send-text", "whatsapp.send-text",
"Sends a text message via WhatsApp.", "Envía un mensaje de texto.",
{ {
to: z.string().describe("Recipient ID (e.g., 1234567890@c.us)"), to: z.string(),
content: z.string().describe("Text message content"), content: z.string(),
}, },
async (params) => { async (params) => {
log("Tool invoked", "whatsapp.send-text", params); log("Tool invoked", "whatsapp.send-text", params);
const result = await fetchJSON("/send-text", { const result = await fetchJSON("/whatsapp/sendText", {
method: "POST", method: "POST",
body: JSON.stringify(params), body: JSON.stringify({ args: params }),
}); });
return { content: [{ type: "text", text: JSON.stringify(result) }] }; return { content: [{ type: "text", text: JSON.stringify(result) }] };
} }
); );
// Tool: whatsapp.send-image
server.tool( server.tool(
"whatsapp.send-image", "whatsapp.send-image",
"Sends an image message via WhatsApp.", "Envía una imagen.",
{ {
to: z.string().describe("Recipient ID"), to: z.string(),
path: z.string().describe("Path or URL to the image"), file: z.string(),
caption: z.string().optional().describe("Image caption"), filename: z.string().optional(),
caption: z.string().optional(),
quotedMsgId: z.string().optional(),
waitForId: z.boolean().optional(),
hideTags: z.boolean().optional(),
}, },
async (params) => { async (params) => {
log("Tool invoked", "whatsapp.send-image", params); log("Tool invoked", "whatsapp.send-image", params);
const result = await fetchJSON("/send-image", { const result = await fetchJSON("/whatsapp/sendImage", {
method: "POST", method: "POST",
body: JSON.stringify(params), body: JSON.stringify({ args: params }),
}); });
return { content: [{ type: "text", text: JSON.stringify(result) }] }; return { content: [{ type: "text", text: JSON.stringify(result) }] };
} }
); );
// Tool: whatsapp.send-file
server.tool( server.tool(
"whatsapp.send-file", "whatsapp.send-file",
"Sends a file message via WhatsApp.", "Envía un archivo.",
{ {
to: z.string().describe("Recipient ID"), to: z.string(),
path: z.string().describe("Path or URL to the file"), file: z.string(),
filename: z.string().optional().describe("Name of the file"), filename: z.string().optional(),
caption: z.string().optional().describe("File caption"), caption: z.string().optional(),
ptt: z.boolean().optional(),
withoutPreview: z.boolean().optional(),
viewOnce: z.boolean().optional(),
hideTags: z.boolean().optional(),
}, },
async (params) => { async (params) => {
log("Tool invoked", "whatsapp.send-file", params); log("Tool invoked", "whatsapp.send-file", params);
const result = await fetchJSON("/send-file", { const result = await fetchJSON("/whatsapp/sendFile", {
method: "POST", method: "POST",
body: JSON.stringify(params), body: JSON.stringify({ args: params }),
}); });
return { content: [{ type: "text", text: JSON.stringify(result) }] }; return { content: [{ type: "text", text: JSON.stringify(result) }] };
} }
); );
// Tool: whatsapp.list-chats
server.tool(
"whatsapp.list-chats",
"Retrieves a list of all chats.",
{}, // No input parameters
async () => {
log("Tool invoked", "whatsapp.list-chats");
const result = await fetchJSON("/chats");
return { content: [{ type: "text", text: JSON.stringify(result) }] };
}
);
// Tool: whatsapp.get-chat-details
server.tool(
"whatsapp.get-chat-details",
"Retrieves details for a specific chat by its ID.",
{
chatId: z.string().describe("ID of the chat (e.g., 1234567890@c.us)"),
},
async (params) => {
log("Tool invoked", "whatsapp.get-chat-details", params);
const result = await fetchJSON(`/chats/${params.chatId}`);
return { content: [{ type: "text", text: JSON.stringify(result) }] };
}
);
// Tool: whatsapp.get-chat-messages
server.tool(
"whatsapp.get-chat-messages",
"Retrieves messages for a specific chat.",
{
chatId: z.string().describe("ID of the chat"),
limit: z.number().optional().describe("Number of messages to retrieve"),
includeMe: z.boolean().optional().describe("Include messages sent by me"),
includeNotifications: z.boolean().optional().describe("Include notification messages"),
},
async ({ chatId, ...queryParams}) => {
log("Tool invoked", "whatsapp.get-chat-messages", { chatId, queryParams });
const qs = new URLSearchParams(
Object.entries(queryParams)
.filter(([, v]) => v !== undefined)
.map(([k, v]) => [k, String(v)])
);
const result = await fetchJSON(`/chats/${chatId}/messages?${qs.toString()}`);
return { content: [{ type: "text", text: JSON.stringify(result) }] };
}
);
server.tool( server.tool(
"whatsapp.delete-chat", "whatsapp.delete-chat",
"Deletes a chat by its ID. (Note: The underlying OpenWA endpoint for this is assumed and might need verification)", "Elimina un chat.",
{ {
chatId: z.string().describe("ID of the chat to delete (e.g., 1234567890@c.us)"), chatId: z.string(),
}, },
async (params) => { async (params) => {
log("Tool invoked", "whatsapp.delete-chat", params); log("Tool invoked", "whatsapp.delete-chat", params);
const result = await fetchJSON(`/chats/${params.chatId}`, { const result = await fetchJSON("/whatsapp/deleteChat", {
method: "DELETE",
});
// Consider what a successful deletion should return.
// OpenWA might return a success message or just a 200/204.
// For now, we'll return the full result.
return { content: [{ type: "text", text: JSON.stringify(result) }] };
}
);
// Tool: whatsapp.create-group
server.tool(
"whatsapp.create-group",
"Creates a new WhatsApp group.",
{
groupName: z.string().describe("Name of the group"),
contactIds: z.array(z.string()).describe("Array of contact IDs to add to the group"),
},
async (params) => {
log("Tool invoked", "whatsapp.create-group", params);
const result = await fetchJSON("/groups", {
method: "POST", method: "POST",
body: JSON.stringify(params), body: JSON.stringify({ args: params }),
}); });
return { content: [{ type: "text", text: JSON.stringify(result) }] }; return { content: [{ type: "text", text: JSON.stringify(result) }] };
} }
); );
// Tool: whatsapp.get-contact-details
server.tool( server.tool(
"whatsapp.get-contact-details", "whatsapp.delete-message",
"Retrieves details for a specific contact by ID.", "Elimina un mensaje.",
{ {
contactId: z.string().describe("ID of the contact (e.g., 1234567890@c.us)"), chatId: z.string(),
messageId: z.string(),
onlyLocal: z.boolean().optional(),
}, },
async (params) => { async (params) => {
log("Tool invoked", "whatsapp.get-contact-details", params); log("Tool invoked", "whatsapp.delete-message", params);
const result = await fetchJSON(`/contacts/${params.contactId}`); const result = await fetchJSON("/whatsapp/deleteMessage", {
method: "POST",
body: JSON.stringify({ args: params }),
});
return { content: [{ type: "text", text: JSON.stringify(result) }] }; return { content: [{ type: "text", text: JSON.stringify(result) }] };
} }
); );
// Tool: whatsapp.get-blocklist
server.tool( server.tool(
"whatsapp.get-blocklist", "whatsapp.edit-message",
"Retrieves the blocklist.", "Edita un mensaje.",
{}, // No input parameters {
messageId: z.string(),
text: z.string(),
},
async (params) => {
log("Tool invoked", "whatsapp.edit-message", params);
const result = await fetchJSON("/whatsapp/editMessage", {
method: "POST",
body: JSON.stringify({ args: params }),
});
return { content: [{ type: "text", text: JSON.stringify(result) }] };
}
);
server.tool(
"whatsapp.forward-messages",
"Reenvía mensajes.",
{
to: z.string(),
messages: z.array(z.string()),
skipMyMessages: z.boolean().optional(),
},
async (params) => {
log("Tool invoked", "whatsapp.forward-messages", params);
const result = await fetchJSON("/whatsapp/forwardMessages", {
method: "POST",
body: JSON.stringify({ args: params }),
});
return { content: [{ type: "text", text: JSON.stringify(result) }] };
}
);
server.tool(
"whatsapp.get-chat",
"Obtiene info de un chat",
{
contactId: z.string(),
},
async (params) => {
log("Tool invoked", "whatsapp.get-chat", params);
const result = await fetchJSON("/whatsapp/getChat", {
method: "POST",
body: JSON.stringify({ args: params }),
});
return { content: [{ type: "text", text: JSON.stringify(result) }] };
}
);
server.tool(
"whatsapp.get-all-chats",
"Obtiene todos los chats.",
{
withNewMessageOnly: z.boolean().optional(),
},
async (params) => {
log("Tool invoked", "whatsapp.get-all-chats", params);
const result = await fetchJSON("/whatsapp/getAllChats", {
method: "POST",
body: JSON.stringify({ args: params }),
});
return { content: [{ type: "text", text: JSON.stringify(result) }] };
}
);
server.tool(
"whatsapp.get-all-chat-ids",
"Obtiene todos los chat IDs.",
{},
async () => { async () => {
log("Tool invoked", "whatsapp.get-blocklist"); log("Tool invoked", "whatsapp.get-all-chat-ids");
const result = await fetchJSON("/blocklist"); const result = await fetchJSON("/whatsapp/getAllChatIds", {
return { content: [{ type: "text", text: JSON.stringify(result) }] };
}
);
// Tool: whatsapp.block-contact
server.tool(
"whatsapp.block-contact",
"Blocks a contact on WhatsApp.",
{
contactId: z.string().describe("ID of the contact to block"),
},
async (params) => {
log("Tool invoked", "whatsapp.block-contact", params);
const result = await fetchJSON("/blocklist/block", {
method: "POST", method: "POST",
body: JSON.stringify(params), body: JSON.stringify({ args: {} }),
}); });
return { content: [{ type: "text", text: JSON.stringify(result) }] }; return { content: [{ type: "text", text: JSON.stringify(result) }] };
} }
); );
// Tool: whatsapp.unblock-contact
server.tool( server.tool(
"whatsapp.unblock-contact", "whatsapp.get-all-messages",
"Unblocks a contact on WhatsApp.", "Obtiene mensajes de un chat.",
{ {
contactId: z.string().describe("ID of the contact to unblock"), chatId: z.string(),
includeMe: z.boolean().optional(),
includeNotifications: z.boolean().optional(),
}, },
async (params) => { async (params) => {
log("Tool invoked", "whatsapp.unblock-contact", params); log("Tool invoked", "whatsapp.get-all-messages", params);
const result = await fetchJSON("/blocklist/unblock", { const result = await fetchJSON("/whatsapp/getAllMessagesInChat", {
method: "POST", method: "POST",
body: JSON.stringify(params), body: JSON.stringify({ args: params }),
}); });
return { content: [{ type: "text", text: JSON.stringify(result) }] }; return { content: [{ type: "text", text: JSON.stringify(result) }] };
} }

View File

@@ -1,6 +1,7 @@
import express from 'express'; import express from 'express';
import dotenv from 'dotenv'; import dotenv from 'dotenv';
import { registerConversationRoutes } from './routes/conversationActions'; import { registerConversationRoutes } from './routes/conversationActions';
import whatsappActionsRouter from './routes/whatsappActions'; // New import
import { import {
registerWebhookRoutes, registerWebhookRoutes,
clearWebhooks, clearWebhooks,
@@ -8,7 +9,6 @@ import {
waitForGateway, waitForGateway,
WebhookConfig, WebhookConfig,
} from './webhook'; } from './webhook';
import whatsappActionsRouter from './routes/whatsappActions'; // New import
dotenv.config(); dotenv.config();
@@ -51,9 +51,9 @@ const config: WebhookConfig = {
registerConversationRoutes(app, openWaUrl); registerConversationRoutes(app, openWaUrl);
registerWebhookRoutes(app, config, openWaUrl, agentUrl); registerWebhookRoutes(app, config, openWaUrl, agentUrl);
app.use('/whatsapp', whatsappActionsRouter); // New line
// Register new whatsappActions routes // Register new whatsappActions routes
app.use('/whatsapp', whatsappActionsRouter); // New line
app.listen(port, async () => { app.listen(port, async () => {
console.log(`WhatsApp router listening on ${port}`); console.log(`WhatsApp router listening on ${port}`);

View File

@@ -6,6 +6,7 @@ import {
buildConversation, buildConversation,
} from '../store/conversation'; } from '../store/conversation';
export function registerConversationRoutes(app: Application, openWaUrl: string | undefined) { export function registerConversationRoutes(app: Application, openWaUrl: string | undefined) {
app.get('/conversations', (req, res) => { app.get('/conversations', (req, res) => {
console.log('[routes] GET /conversations'); console.log('[routes] GET /conversations');
@@ -39,6 +40,7 @@ export function registerConversationRoutes(app: Application, openWaUrl: string |
app.delete('/conversations/:id', (req, res) => { app.delete('/conversations/:id', (req, res) => {
console.log(`[routes] DELETE /conversations/${req.params.id}`); console.log(`[routes] DELETE /conversations/${req.params.id}`);
const deleted = deleteConversation(req.params.id); const deleted = deleteConversation(req.params.id);
console.log(`Conversation ${req.params.id} deleted: ${deleted}`);
res.json({ success: deleted }); res.json({ success: deleted });
}); });
} }

View File

@@ -1,283 +1,278 @@
import express, { Router, Request, Response, NextFunction } from 'express'; import express, { Router, Request, Response, NextFunction } from 'express';
import * as whatsappClient from '../whatsappClient'; import * as whatsappClient from '../whatsappClient';
// Assuming OPEN_WA_URL is set in the environment. import dotenv from 'dotenv';
// For local development, dotenv would typically be used in the main app entry point (e.g., index.ts) dotenv.config();
// require('dotenv').config(); // Potentially, but better if handled by the main application loader
const router = Router(); const router = Router();
// Retrieve OpenWA URL from environment variables
// This is done once when the module is loaded.
const openWaUrl = process.env.OPEN_WA_URL; const openWaUrl = process.env.OPEN_WA_URL;
// Middleware to check if openWaUrl is configured
// This runs for every request to this router
router.use((req: Request, res: Response, next: NextFunction) => { router.use((req: Request, res: Response, next: NextFunction) => {
if (!openWaUrl) { if (!openWaUrl) {
console.error('[routes/whatsappActions] Service OPEN_WA_URL not configured'); console.error('[routes/whatsappActions] Service OPEN_WA_URL not configured');
return res.status(500).json({ error: 'Service OPEN_WA_URL not configured. Please set the environment variable.' }); return res.status(500).json({ error: 'Service OPEN_WA_URL not configured. Please set the environment variable.' });
} }
// Pass openWaUrl to subsequent handlers via res.locals if preferred,
// or they can access the `openWaUrl` constant from the module scope.
// For simplicity, handlers will use the module-scoped `openWaUrl`.
next(); next();
}); });
// Route implementations router.post('/sendText', async (req: Request, res: Response) => {
console.log('[routes/whatsappActions] POST /sendText called');
// POST /send-text
router.post('/send-text', async (req: Request, res: Response) => {
try { try {
const { to, content } = req.body; const { args } = req.body;
const { to, content } = args || {};
if (!to || !content) { if (!to || !content) {
return res.status(400).json({ error: 'Missing "to" or "content" in request body' }); return res.status(400).json({ error: 'Missing "to" or "content" in request body' });
} }
// openWaUrl is checked by middleware and available in module scope
const result = await whatsappClient.sendTextMessage(openWaUrl!, to, content); const result = await whatsappClient.sendTextMessage(openWaUrl!, to, content);
res.json(result); res.json(result);
} catch (error: any) { } catch (error: any) {
console.error('[routes/whatsappActions] Error in /send-text:', error.message); console.error('[routes/whatsappActions] Error in /sendText:', error.message);
// Check if the error message is already a JSON string from whatsappClient's error handling
try { try {
const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{'))); const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{')));
return res.status(parsedError.status || 500).json({ error: parsedError }); return res.status(parsedError.status || 500).json({ error: parsedError });
} catch (e) { } catch {
// If not, send the plain error message
res.status(500).json({ error: error.message || 'Failed to send text message' }); res.status(500).json({ error: error.message || 'Failed to send text message' });
} }
} }
}); });
// DELETE /chats/:chatId router.post('/deleteChat', async (req: Request, res: Response) => {
router.delete('/chats/:chatId', async (req: Request, res: Response) => { console.log('[routes/whatsappActions] POST /deleteChat called');
try { try {
const { chatId } = req.params; const { args } = req.body;
const { chatId } = args || {};
if (!chatId) { if (!chatId) {
return res.status(400).json({ error: 'Missing "chatId" in request params' }); return res.status(400).json({ error: 'Missing "chatId" in request body' });
} }
// openWaUrl is checked by middleware and available in module scope
const result = await whatsappClient.deleteChat(openWaUrl!, chatId); const result = await whatsappClient.deleteChat(openWaUrl!, chatId);
console.log('[routes/whatsappActions] Chat deleted successfully:', result);
res.json(result); res.json(result);
} catch (error: any) { } catch (error: any) {
console.error(`[routes/whatsappActions] Error in DELETE /chats/${req.params.chatId}:`, error.message); console.error('[routes/whatsappActions] Error in /deleteChat:', error.message);
try { try {
// Attempt to parse error message if it's from whatsappClient's structured error
const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{'))); const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{')));
return res.status(parsedError.status || 500).json({ error: parsedError }); return res.status(parsedError.status || 500).json({ error: parsedError });
} catch (e) { } catch {
// If not, send the plain error message
res.status(500).json({ error: error.message || 'Failed to delete chat' }); res.status(500).json({ error: error.message || 'Failed to delete chat' });
} }
} }
}); });
// POST /send-image router.post('/deleteMessage', async (req: Request, res: Response) => {
router.post('/send-image', async (req: Request, res: Response) => { console.log('[routes/whatsappActions] POST /deleteMessage called');
try { try {
const { to, path, caption } = req.body; const { args } = req.body;
if (!to || !path) { const { chatId, messageId, onlyLocal } = args || {};
return res.status(400).json({ error: 'Missing "to" or "path" in request body' }); if (!chatId || !messageId) {
return res.status(400).json({ error: 'Missing "chatId" or "messageId" in request body' });
} }
const result = await whatsappClient.sendImageMessage(openWaUrl!, to, path, caption); const result = await whatsappClient.deleteMessage(openWaUrl!, {
chatId,
messageId,
onlyLocal,
});
res.json(result); res.json(result);
} catch (error: any) { } catch (error: any) {
console.error('[routes/whatsappActions] Error in /send-image:', error.message); console.error('[routes/whatsappActions] Error in /deleteMessage:', error.message);
try { try {
const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{'))); const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{')));
return res.status(parsedError.status || 500).json({ error: parsedError }); return res.status(parsedError.status || 500).json({ error: parsedError });
} catch (e) { } catch {
res.status(500).json({ error: error.message || 'Failed to delete message' });
}
}
});
router.post('/editMessage', async (req: Request, res: Response) => {
console.log('[routes/whatsappActions] POST /editMessage called');
try {
const { args } = req.body;
const { messageId, text } = args || {};
if (!messageId || !text) {
return res.status(400).json({ error: 'Missing "messageId" or "text" in request body' });
}
const result = await whatsappClient.editMessage(openWaUrl!, { messageId, text });
res.json(result);
} catch (error: any) {
console.error('[routes/whatsappActions] Error in /editMessage:', 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 edit message' });
}
}
});
router.post('/forwardMessages', async (req: Request, res: Response) => {
console.log('[routes/whatsappActions] POST /forwardMessages called');
try {
const { args } = req.body;
const { to, messages, skipMyMessages } = args || {};
if (!to || !Array.isArray(messages) || messages.length === 0) {
return res.status(400).json({ error: 'Missing "to" or "messages" in request body' });
}
const result = await whatsappClient.forwardMessages(openWaUrl!, {
to,
messages,
skipMyMessages,
});
res.json(result);
} catch (error: any) {
console.error('[routes/whatsappActions] Error in /forwardMessages:', 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 forward messages' });
}
}
});
router.post('/sendImage', async (req: Request, res: Response) => {
console.log('[routes/whatsappActions] POST /sendImage called');
try {
const { args } = req.body;
const { to, file, filename, caption, quotedMsgId, waitForId, hideTags } = args || {};
if (!to || !file) {
return res.status(400).json({ error: 'Missing "to" or "file" in request body' });
}
const result = await whatsappClient.sendImageMessage(openWaUrl!, {
to,
file,
filename,
caption,
quotedMsgId,
waitForId,
hideTags,
});
res.json(result);
} catch (error: any) {
console.error('[routes/whatsappActions] Error in /sendImage:', 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 image message' }); res.status(500).json({ error: error.message || 'Failed to send image message' });
} }
} }
}); });
// POST /send-file router.post('/sendFile', async (req: Request, res: Response) => {
router.post('/send-file', async (req: Request, res: Response) => { console.log('[routes/whatsappActions] POST /sendFile called');
try { try {
const { to, path, filename, caption } = req.body; const { args } = req.body;
if (!to || !path) { const {
return res.status(400).json({ error: 'Missing "to" or "path" in request body' }); to,
file,
filename,
caption,
ptt,
withoutPreview,
viewOnce,
hideTags,
} = args || {};
if (!to || !file) {
return res.status(400).json({ error: 'Missing "to" or "file" in request body' });
} }
const result = await whatsappClient.sendFileMessage(openWaUrl!, to, path, filename, caption); const result = await whatsappClient.sendFileMessage(openWaUrl!, {
to,
file,
filename,
caption,
ptt,
withoutPreview,
viewOnce,
hideTags,
});
res.json(result); res.json(result);
} catch (error: any) { } catch (error: any) {
console.error('[routes/whatsappActions] Error in /send-file:', error.message); console.error('[routes/whatsappActions] Error in /sendFile:', error.message);
try { try {
const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{'))); const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{')));
return res.status(parsedError.status || 500).json({ error: parsedError }); return res.status(parsedError.status || 500).json({ error: parsedError });
} catch (e) { } catch {
res.status(500).json({ error: error.message || 'Failed to send file message' }); res.status(500).json({ error: error.message || 'Failed to send file message' });
} }
} }
}); });
// GET /chats/:chatId router.post('/getChat', async (req: Request, res: Response) => {
router.get('/chats/:chatId', async (req: Request, res: Response) => { console.log('[routes/whatsappActions] POST /getChat called');
try { try {
const { chatId } = req.params; const { args } = req.body;
if (!chatId) { const { contactId } = args || {};
return res.status(400).json({ error: 'Missing "chatId" in request params' }); if (!contactId) {
return res.status(400).json({ error: 'Missing "contactId" in request body' });
} }
const result = await whatsappClient.getChatById(openWaUrl!, chatId); const result = await whatsappClient.getChat(openWaUrl!, contactId);
res.json(result); res.json(result);
} catch (error: any) { } catch (error: any) {
console.error(`[routes/whatsappActions] Error in /chats/${req.params.chatId}:`, error.message); console.error('[routes/whatsappActions] Error in /getChat:', error.message);
try { try {
const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{'))); const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{')));
return res.status(parsedError.status || 500).json({ error: parsedError }); return res.status(parsedError.status || 500).json({ error: parsedError });
} catch (e) { } catch {
res.status(500).json({ error: error.message || 'Failed to retrieve chat' }); res.status(500).json({ error: error.message || 'Failed to retrieve chat' });
} }
} }
}); });
// POST /groups router.post('/getAllChats', async (req: Request, res: Response) => {
router.post('/groups', async (req: Request, res: Response) => { console.log('[routes/whatsappActions] POST /getAllChats called');
try { try {
const { groupName, contactIds } = req.body; const { args } = req.body;
if (!groupName || !contactIds || !Array.isArray(contactIds) || contactIds.length === 0) { const { withNewMessageOnly } = args || {};
return res.status(400).json({ error: 'Missing "groupName" or "contactIds" (must be a non-empty array) in request body' }); const result = await whatsappClient.getAllChats(openWaUrl!, withNewMessageOnly);
}
const result = await whatsappClient.createGroup(openWaUrl!, groupName, contactIds);
res.json(result); res.json(result);
} catch (error: any) { } catch (error: any) {
console.error('[routes/whatsappActions] Error in /groups:', error.message); console.error('[routes/whatsappActions] Error in /getAllChats:', error.message);
try { try {
const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{'))); const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{')));
return res.status(parsedError.status || 500).json({ error: parsedError }); return res.status(parsedError.status || 500).json({ error: parsedError });
} catch (e) { } catch {
res.status(500).json({ error: error.message || 'Failed to create group' });
}
}
});
// GET /contacts/:contactId
router.get('/contacts/:contactId', async (req: Request, res: Response) => {
try {
const { contactId } = req.params;
if (!contactId) {
return res.status(400).json({ error: 'Missing "contactId" in request params' });
}
const result = await whatsappClient.getContact(openWaUrl!, contactId);
res.json(result);
} catch (error: any) {
console.error(`[routes/whatsappActions] Error in /contacts/${req.params.contactId}:`, error.message);
try {
const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{')));
return res.status(parsedError.status || 500).json({ error: parsedError });
} catch (e) {
res.status(500).json({ error: error.message || 'Failed to retrieve contact' });
}
}
});
// GET /blocklist
router.get('/blocklist', async (req: Request, res: Response) => {
try {
const result = await whatsappClient.getBlocklist(openWaUrl!);
res.json(result);
} catch (error: any) {
console.error('[routes/whatsappActions] Error in /blocklist:', error.message);
try {
const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{')));
return res.status(parsedError.status || 500).json({ error: parsedError });
} catch (e) {
res.status(500).json({ error: error.message || 'Failed to retrieve blocklist' });
}
}
});
// POST /blocklist/block
router.post('/blocklist/block', async (req: Request, res: Response) => {
try {
const { contactId } = req.body;
if (!contactId) {
return res.status(400).json({ error: 'Missing "contactId" in request body' });
}
const result = await whatsappClient.blockContact(openWaUrl!, contactId);
res.json(result);
} catch (error: any) {
console.error('[routes/whatsappActions] Error in /blocklist/block:', error.message);
try {
const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{')));
return res.status(parsedError.status || 500).json({ error: parsedError });
} catch (e) {
res.status(500).json({ error: error.message || 'Failed to block contact' });
}
}
});
// POST /blocklist/unblock
router.post('/blocklist/unblock', async (req: Request, res: Response) => {
try {
const { contactId } = req.body;
if (!contactId) {
return res.status(400).json({ error: 'Missing "contactId" in request body' });
}
const result = await whatsappClient.unblockContact(openWaUrl!, contactId);
res.json(result);
} catch (error: any) {
console.error('[routes/whatsappActions] Error in /blocklist/unblock:', error.message);
try {
const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{')));
return res.status(parsedError.status || 500).json({ error: parsedError });
} catch (e) {
res.status(500).json({ error: error.message || 'Failed to unblock contact' });
}
}
});
// GET /chats
router.get('/chats', async (req: Request, res: Response) => {
try {
const result = await whatsappClient.getAllChats(openWaUrl!);
res.json(result);
} catch (error: any) {
console.error('[routes/whatsappActions] Error in /chats:', error.message);
try {
const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{')));
return res.status(parsedError.status || 500).json({ error: parsedError });
} catch (e) {
res.status(500).json({ error: error.message || 'Failed to retrieve all chats' }); res.status(500).json({ error: error.message || 'Failed to retrieve all chats' });
} }
} }
}); });
// GET /chats/:chatId/messages router.post('/getAllChatIds', async (req: Request, res: Response) => {
router.get('/chats/:chatId/messages', async (req: Request, res: Response) => { console.log('[routes/whatsappActions] POST /getAllChatIds called');
try { try {
const { chatId } = req.params; const result = await whatsappClient.getAllChatIds(openWaUrl!);
if (!chatId) {
return res.status(400).json({ error: 'Missing "chatId" in request params' });
}
const { limit, includeMe, includeNotifications } = req.query;
let numLimit: number | undefined = undefined;
if (limit) {
numLimit = parseInt(limit as string, 10);
if (isNaN(numLimit)) {
return res.status(400).json({ error: 'Invalid "limit" query parameter, must be a number.' });
}
}
const boolIncludeMe: boolean | undefined = includeMe ? (includeMe as string).toLowerCase() === 'true' : undefined;
const boolIncludeNotifications: boolean | undefined = includeNotifications ? (includeNotifications as string).toLowerCase() === 'true' : undefined;
const result = await whatsappClient.getChatMessages(
openWaUrl!,
chatId,
numLimit,
boolIncludeMe,
boolIncludeNotifications
);
res.json(result); res.json(result);
} catch (error: any) { } catch (error: any) {
console.error(`[routes/whatsappActions] Error in /chats/${req.params.chatId}/messages:`, error.message); console.error('[routes/whatsappActions] Error in /getAllChatIds:', error.message);
try { try {
const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{'))); const parsedError = JSON.parse(error.message.substring(error.message.indexOf('{')));
return res.status(parsedError.status || 500).json({ error: parsedError }); return res.status(parsedError.status || 500).json({ error: parsedError });
} catch (e) { } catch {
res.status(500).json({ error: error.message || 'Failed to retrieve chat IDs' });
}
}
});
router.post('/getAllMessagesInChat', async (req: Request, res: Response) => {
console.log('[routes/whatsappActions] POST /getAllMessagesInChat called');
try {
const { args } = req.body;
const { chatId, includeMe, includeNotifications } = args || {};
if (!chatId) {
return res.status(400).json({ error: 'Missing "chatId" in request body' });
}
const result = await whatsappClient.getAllMessagesInChat(openWaUrl!, {
chatId,
includeMe,
includeNotifications,
});
res.json(result);
} catch (error: any) {
console.error('[routes/whatsappActions] Error in /getAllMessagesInChat:', 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 chat messages' }); res.status(500).json({ error: error.message || 'Failed to retrieve chat messages' });
} }
} }

View File

@@ -62,7 +62,7 @@ export function registerWebhookRoutes(
if (chatId) { if (chatId) {
try { try {
conv = await addMessageToConversation(chatId, message, openWaUrl); conv = await addMessageToConversation(chatId, message, openWaUrl);
console.log(`🔄 Updated conversation for ${chatId}`, conv); console.log(`🔄 Updated conversation for ${chatId}`);
} catch (err: any) { } catch (err: any) {
console.warn('Failed updating conversation:', err.message); console.warn('Failed updating conversation:', err.message);

View File

@@ -80,19 +80,21 @@ export async function deleteChat(
*/ */
export async function sendImageMessage( export async function sendImageMessage(
openWaUrl: string, openWaUrl: string,
to: string, args: {
path: string, // Assuming 'path' is how OpenWA refers to the image URL/path to: string;
caption?: string file: string;
filename?: string;
caption?: string;
quotedMsgId?: string;
waitForId?: boolean;
hideTags?: boolean;
}
): Promise<OpenWAResponse> { ): Promise<OpenWAResponse> {
try { try {
const args: { to: string; path: string; caption?: string } = { to, path };
if (caption) {
args.caption = caption;
}
const response = await axios.post(`${openWaUrl}/sendImage`, { args }); const response = await axios.post(`${openWaUrl}/sendImage`, { args });
return response.data?.response || response.data; return response.data?.response || response.data;
} catch (error: any) { } catch (error: any) {
console.error(`[whatsappClient] Error sending image message to ${to}:`, error.message); console.error(`[whatsappClient] Error sending image message to ${args.to}:`, error.message);
if (axios.isAxiosError(error) && error.response) { if (axios.isAxiosError(error) && error.response) {
console.error('[whatsappClient] Axios error details:', error.response.data); console.error('[whatsappClient] Axios error details:', error.response.data);
throw new Error(`whatsappClient API error (${openWaUrl}/sendImage): ${error.response.status} - ${JSON.stringify(error.response.data)}`); throw new Error(`whatsappClient API error (${openWaUrl}/sendImage): ${error.response.status} - ${JSON.stringify(error.response.data)}`);
@@ -112,23 +114,22 @@ export async function sendImageMessage(
*/ */
export async function sendFileMessage( export async function sendFileMessage(
openWaUrl: string, openWaUrl: string,
to: string, args: {
path: string, // Assuming 'path' is how OpenWA refers to the file URL/path to: string;
filename?: string, file: string;
caption?: string filename?: string;
caption?: string;
ptt?: boolean;
withoutPreview?: boolean;
viewOnce?: boolean;
hideTags?: boolean;
}
): Promise<OpenWAResponse> { ): Promise<OpenWAResponse> {
try { try {
const args: { to: string; path: string; filename?: string; caption?: string } = { to, path };
if (filename) {
args.filename = filename;
}
if (caption) {
args.caption = caption;
}
const response = await axios.post(`${openWaUrl}/sendFile`, { args }); const response = await axios.post(`${openWaUrl}/sendFile`, { args });
return response.data?.response || response.data; return response.data?.response || response.data;
} catch (error: any) { } catch (error: any) {
console.error(`[whatsappClient] Error sending file message to ${to}:`, error.message); console.error(`[whatsappClient] Error sending file message to ${args.to}:`, error.message);
if (axios.isAxiosError(error) && error.response) { if (axios.isAxiosError(error) && error.response) {
console.error('[whatsappClient] Axios error details:', error.response.data); console.error('[whatsappClient] Axios error details:', error.response.data);
throw new Error(`whatsappClient API error (${openWaUrl}/sendFile): ${error.response.status} - ${JSON.stringify(error.response.data)}`); throw new Error(`whatsappClient API error (${openWaUrl}/sendFile): ${error.response.status} - ${JSON.stringify(error.response.data)}`);
@@ -143,20 +144,20 @@ export async function sendFileMessage(
* @param chatId The ID of the chat to retrieve. * @param chatId The ID of the chat to retrieve.
* @returns A promise that resolves to the API response containing chat details. * @returns A promise that resolves to the API response containing chat details.
*/ */
export async function getChatById( export async function getChat(
openWaUrl: string, openWaUrl: string,
chatId: string contactId: string
): Promise<OpenWAResponse<Chat>> { // Assuming Chat type from types.ts ): Promise<OpenWAResponse<Chat>> {
try { try {
const response = await axios.post(`${openWaUrl}/getChatById`, { args: { chatId } }); const response = await axios.post(`${openWaUrl}/getChat`, { args: { contactId } });
return response.data?.response || response.data; return response.data?.response || response.data;
} catch (error: any) { } catch (error: any) {
console.error(`[whatsappClient] Error retrieving chat ${chatId}:`, error.message); console.error(`[whatsappClient] Error retrieving chat ${contactId}:`, error.message);
if (axios.isAxiosError(error) && error.response) { if (axios.isAxiosError(error) && error.response) {
console.error('[whatsappClient] Axios error details:', error.response.data); console.error('[whatsappClient] Axios error details:', error.response.data);
throw new Error(`whatsappClient API error (${openWaUrl}/getChatById): ${error.response.status} - ${JSON.stringify(error.response.data)}`); throw new Error(`whatsappClient API error (${openWaUrl}/getChat): ${error.response.status} - ${JSON.stringify(error.response.data)}`);
} }
throw new Error(`whatsappClient error (${openWaUrl}/getChatById): ${error.message}`); throw new Error(`whatsappClient error (${openWaUrl}/getChat): ${error.message}`);
} }
} }
@@ -284,11 +285,13 @@ export async function unblockContact(
* @returns A promise that resolves to the API response containing all chats. * @returns A promise that resolves to the API response containing all chats.
*/ */
export async function getAllChats( export async function getAllChats(
openWaUrl: string openWaUrl: string,
): Promise<OpenWAResponse<Chat[]>> { // Assuming an array of Chat objects withNewMessageOnly?: boolean
): Promise<OpenWAResponse<Chat[]>> {
try { try {
// This might be a GET request or a POST without args, depending on OpenWA const response = await axios.post(`${openWaUrl}/getAllChats`, {
const response = await axios.post(`${openWaUrl}/getAllChats`); args: withNewMessageOnly !== undefined ? { withNewMessageOnly } : {},
});
return response.data?.response || response.data; return response.data?.response || response.data;
} catch (error: any) { } catch (error: any) {
console.error(`[whatsappClient] Error retrieving all chats:`, error.message); console.error(`[whatsappClient] Error retrieving all chats:`, error.message);
@@ -300,6 +303,22 @@ export async function getAllChats(
} }
} }
export async function getAllChatIds(
openWaUrl: string
): Promise<OpenWAResponse<string[]>> {
try {
const response = await axios.post(`${openWaUrl}/getAllChatIds`);
return response.data?.response || response.data;
} catch (error: any) {
console.error(`[whatsappClient] Error retrieving chat ids:`, error.message);
if (axios.isAxiosError(error) && error.response) {
console.error('[whatsappClient] Axios error details:', error.response.data);
throw new Error(`whatsappClient API error (${openWaUrl}/getAllChatIds): ${error.response.status} - ${JSON.stringify(error.response.data)}`);
}
throw new Error(`whatsappClient error (${openWaUrl}/getAllChatIds): ${error.message}`);
}
}
/** /**
* Retrieves messages for a specific chat. * Retrieves messages for a specific chat.
* Maps to OpenWA's /loadAndGetAllMessagesInChat endpoint. * Maps to OpenWA's /loadAndGetAllMessagesInChat endpoint.
@@ -310,32 +329,74 @@ export async function getAllChats(
* @param includeNotifications Optional flag to include notification messages. * @param includeNotifications Optional flag to include notification messages.
* @returns A promise that resolves to the API response containing messages. * @returns A promise that resolves to the API response containing messages.
*/ */
export async function getChatMessages( export async function getAllMessagesInChat(
openWaUrl: string, openWaUrl: string,
chatId: string, args: {
limit?: number,
includeMe?: boolean,
includeNotifications?: boolean
): Promise<OpenWAResponse<WhatsAppMessage[]>> { // Corrected to WhatsAppMessage[]
try {
const args: {
chatId: string; chatId: string;
limit?: number;
includeMe?: boolean; includeMe?: boolean;
includeNotifications?: boolean; includeNotifications?: boolean;
} = { chatId }; }
if (limit !== undefined) args.limit = limit; ): Promise<OpenWAResponse<WhatsAppMessage[]>> {
if (includeMe !== undefined) args.includeMe = includeMe; try {
if (includeNotifications !== undefined) args.includeNotifications = includeNotifications; const response = await axios.post(`${openWaUrl}/getAllMessagesInChat`, { args });
const response = await axios.post(`${openWaUrl}/loadAndGetAllMessagesInChat`, { args });
return response.data?.response || response.data; return response.data?.response || response.data;
} catch (error: any) { } catch (error: any) {
console.error(`[whatsappClient] Error retrieving messages for chat ${chatId}:`, error.message); console.error(`[whatsappClient] Error retrieving messages for chat ${args.chatId}:`, error.message);
if (axios.isAxiosError(error) && error.response) { if (axios.isAxiosError(error) && error.response) {
console.error('[whatsappClient] Axios error details:', error.response.data); console.error('[whatsappClient] Axios error details:', error.response.data);
throw new Error(`whatsappClient API error (${openWaUrl}/loadAndGetAllMessagesInChat): ${error.response.status} - ${JSON.stringify(error.response.data)}`); throw new Error(`whatsappClient API error (${openWaUrl}/getAllMessagesInChat): ${error.response.status} - ${JSON.stringify(error.response.data)}`);
} }
throw new Error(`whatsappClient error (${openWaUrl}/loadAndGetAllMessagesInChat): ${error.message}`); throw new Error(`whatsappClient error (${openWaUrl}/getAllMessagesInChat): ${error.message}`);
}
}
export async function deleteMessage(
openWaUrl: string,
args: { chatId: string; messageId: string; onlyLocal?: boolean }
): Promise<OpenWAResponse> {
try {
const response = await axios.post(`${openWaUrl}/deleteMessage`, { args });
return response.data?.response || response.data;
} catch (error: any) {
console.error(`[whatsappClient] Error deleting 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}/deleteMessage): ${error.response.status} - ${JSON.stringify(error.response.data)}`);
}
throw new Error(`whatsappClient error (${openWaUrl}/deleteMessage): ${error.message}`);
}
}
export async function editMessage(
openWaUrl: string,
args: { messageId: string; text: string }
): Promise<OpenWAResponse> {
try {
const response = await axios.post(`${openWaUrl}/editMessage`, { args });
return response.data?.response || response.data;
} catch (error: any) {
console.error(`[whatsappClient] Error editing 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}/editMessage): ${error.response.status} - ${JSON.stringify(error.response.data)}`);
}
throw new Error(`whatsappClient error (${openWaUrl}/editMessage): ${error.message}`);
}
}
export async function forwardMessages(
openWaUrl: string,
args: { to: string; messages: string[]; skipMyMessages?: boolean }
): Promise<OpenWAResponse> {
try {
const response = await axios.post(`${openWaUrl}/forwardMessages`, { args });
return response.data?.response || response.data;
} catch (error: any) {
console.error(`[whatsappClient] Error forwarding messages 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}/forwardMessages): ${error.response.status} - ${JSON.stringify(error.response.data)}`);
}
throw new Error(`whatsappClient error (${openWaUrl}/forwardMessages): ${error.message}`);
} }
} }