Merge pull request #20 from josedario87/codex/implement-nucleo-whatsapp-api-functions
All checks were successful
Deploy conversation layer / deploy (push) Successful in 2m4s
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:
@@ -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) }] };
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user