import express from 'express'; import axios from 'axios'; import dotenv from 'dotenv'; // Load environment variables dotenv.config(); const API_URL = process.env.API_URL; const INSTANCE_NAME = process.env.INSTANCE_NAME; const API_KEY = process.env.API_KEY; const WEBHOOK_URL = process.env.WEBHOOK_URL; const COMMUNITY_ID = process.env.COMMUNITY_ID; if (!API_URL || !INSTANCE_NAME || !API_KEY || !WEBHOOK_URL || !COMMUNITY_ID) { throw new Error('Required environment variables are not defined.'); } // Store community and group information const linkedGroups: Set = new Set(); // Create an Express app const app = express(); app.use(express.json()); // Fetch all groups and identify the community and linked groups async function fetchGroups() { try { const response = await axios.get(`${API_URL}/group/fetchAllGroups/${INSTANCE_NAME}?getParticipants=false`, { headers: { apikey: API_KEY, }, }); console.log('API Response:', response.data); // Log the full response // Check if the response is an array of groups if (Array.isArray(response.data)) { for (const group of response.data) { if (group.id === COMMUNITY_ID) { // This is the community console.log(`Community ID: ${COMMUNITY_ID}`); } else if (group.linkedParent === COMMUNITY_ID) { // This is a group linked to the community linkedGroups.add(group.id); console.log(`Linked Group ID: ${group.id}`); } } } else { console.error('Unexpected response format:', response.data); } } catch (error) { console.error('Error fetching groups:', error.response ? error.response.data : error.message); } } // Add the bot to a new group async function addBotToGroup(groupId: string) { try { await axios.post(`${API_URL}/group/invite`, { groupId, participants: ['your-bot-phone-number'], // Replace with your bot's phone number }, { headers: { 'Content-Type': 'application/json', apikey: API_KEY, }, }); console.log(`Bot added to group: ${groupId}`); } catch (error) { console.error('Error adding bot to group:', error); } } // Webhook endpoint app.post('/webhook', (req, res) => { const event = req.body; console.log('Received event:', JSON.stringify(event, null, 2)); // Log the full event // Handle the event if (event.event === 'messages.upsert') { const messageData = event.data; // Ignore messages sent by the bot itself // if (messageData.key.fromMe) { // console.log('Ignoring message sent by the bot itself'); // return res.status(200).send('OK'); // } // Handle the message handleMessage(messageData); } else if (event.event === 'group_create' && event.data.parentGroupId === COMMUNITY_ID) { // A new group was created in the community const groupId = event.data.groupId; linkedGroups.add(groupId); addBotToGroup(groupId); } res.status(200).send('OK'); }); // Handle incoming messages function handleMessage(messageData: any) { // console.log('Incoming message data:', JSON.stringify(messageData, null, 2)); // Log the full message data const body = messageData.message?.conversation; const sender = messageData.key.remoteJid; const groupId = messageData.key.remoteJid; // Group ID for community messages // Check if the message is from a group linked to the community if (groupId && linkedGroups.has(groupId) && body) { // Parse the command if (body.startsWith('/tarea')) { // Remove the "/tarea" prefix and trim any extra spaces const command = body.replace('/tarea', '').trim(); // Split the remaining command into action and arguments const [action, ...args] = command.split(' '); console.log('Parsed action:', action); // Log the action console.log('Parsed args:', args); // Log the arguments const description = args.join(' '); console.log('Description:', description); // Log the description // Extract assigned user (if any) const assignedUserMatch = description.match(/@\w+/); const assignedUser = assignedUserMatch ? assignedUserMatch[0] : null; // Extract due date (if any) const dueDateMatch = description.match(/\d{4}-\d{2}-\d{2}/); const dueDate = dueDateMatch ? dueDateMatch[0] : null; // Remove the assigned user and due date from the description const cleanDescription = description .replace(/@\w+/g, '') // Remove assigned user .replace(/\d{4}-\d{2}-\d{2}/g, '') // Remove due date .trim(); // Remove extra spaces if (action === 'nueva') { // If no user is assigned, default to the sender const finalAssignedUser = assignedUser || `@${sender.split('@')[0]}`; createTask(sender, cleanDescription, finalAssignedUser, dueDate); } else if (action === 'asignar') { const taskId = args[0]; if (assignedUser) { assignTask(sender, taskId, assignedUser); } else { sendMessage(sender, 'Debes mencionar a un usuario para asignar la tarea. Ejemplo: /tarea asignar 1 @usuario'); } } else if (action === 'completar') { const taskId = args[0]; completeTask(sender, taskId); } else { sendMessage(sender, 'Acción no reconocida. Usa /tarea nueva, /tarea asignar, o /tarea completar.'); } } } } // Create a new task function createTask(sender: string, description: string, assignedUser?: string, dueDate?: string) { const task = { id: tasks.length + 1, description, assignedTo: assignedUser || null, dueDate: dueDate || null, completed: false, }; tasks.push(task); sendMessage(sender, `Tarea creada: ${task.id} - ${task.description}${assignedUser ? ` asignada a ${assignedUser}` : ''}${dueDate ? ` para el ${dueDate}` : ''}`); } // Assign a task function assignTask(sender: string, taskId: string, assignedUser: string) { const task = tasks.find((t) => t.id === parseInt(taskId)); if (task) { task.assignedTo = assignedUser; sendMessage(sender, `Tarea ${taskId} asignada a ${assignedUser}`); } else { sendMessage(sender, `Tarea ${taskId} no encontrada`); } } // Mark a task as completed function completeTask(sender: string, taskId: string) { const task = tasks.find((t) => t.id === parseInt(taskId)); if (task) { task.completed = true; sendMessage(sender, `Tarea ${taskId} marcada como completada`); } else { sendMessage(sender, `Tarea ${taskId} no encontrada`); } } // Send a message async function sendMessage(phone: string, message: string) { try { await axios.post(`${API_URL}/message/sendText/${INSTANCE_NAME}`, { number: phone, text: message, }, { headers: { 'Content-Type': 'application/json', apikey: API_KEY, }, }); } catch (error) { console.error('Error sending message:', error); } } // In-memory task storage const tasks: any[] = []; // Start the webhook server const PORT = 3000; app.listen(PORT, () => { console.log(`Webhook server running on port ${PORT}`); }); // Configure the webhook in Evolution API async function configureWebhook() { try { const response = await axios.post( `${API_URL}/webhook/set/${INSTANCE_NAME}`, { webhook: { url: WEBHOOK_URL, // Use the environment variable enabled: true, webhook_by_events: true, webhook_base64: true, events: [ 'MESSAGES_UPSERT', // New or updated messages 'GROUPS_UPSERT', // New or updated groups 'GROUP_PARTICIPANTS_UPDATE', // Group participant changes 'GROUP_UPDATE', // Group metadata changes (optional) ], }, }, { headers: { 'Content-Type': 'application/json', apikey: API_KEY, }, } ); console.log('Webhook configured successfully:', response.data); } catch (error) { console.error('Error configuring webhook:', error.response ? error.response.data : error.message); } } // Fetch groups and configure the webhook when the bot starts async function startBot() { await fetchGroups(); await configureWebhook(); } startBot();