Merge pull request #20 from josedario87/codex/implement-nucleo-whatsapp-api-functions
All checks were successful
Deploy conversation layer / deploy (push) Successful in 2m4s

Add new WhatsApp API routes and MCP tools
This commit is contained in:
josedario87
2025-06-07 11:15:16 -06:00
committed by GitHub
3 changed files with 307 additions and 0 deletions

View File

@@ -199,4 +199,106 @@ export default function registerWhatsappIntegration(server) {
return { content: [{ type: "text", text: JSON.stringify(result) }] }; 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) }] };
}
);
} }

View File

@@ -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; export default router;

View File

@@ -400,3 +400,87 @@ export async function forwardMessages(
throw new Error(`whatsappClient error (${openWaUrl}/forwardMessages): ${error.message}`); throw new Error(`whatsappClient error (${openWaUrl}/forwardMessages): ${error.message}`);
} }
} }
export async function getAllContacts(
openWaUrl: string
): Promise<OpenWAResponse<Contact[]>> {
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<OpenWAResponse<WhatsAppMessage[]>> {
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<OpenWAResponse> {
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<OpenWAResponse> {
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<OpenWAResponse> {
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}`);
}
}