UNPKG

evolution-api-mcp

Version:

MCP Server for Evolution API v2 - Integrate WhatsApp functionality with Claude Desktop and other MCP clients

650 lines (648 loc) 26 kB
"use strict"; /** * Group Controller MCP Tools Implementation * Implements all tools for group management through Evolution API */ Object.defineProperty(exports, "__esModule", { value: true }); exports.GroupTools = void 0; const zod_1 = require("zod"); const group_endpoints_1 = require("../../registry/endpoints/group-endpoints"); /** * Group Controller tool implementations */ class GroupTools { constructor(httpClient) { this.httpClient = httpClient; } /** * Create a new group */ createCreateGroupTool() { const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'create-group'); return { name: 'evolution_create_group', description: 'Create a new WhatsApp group with participants', controller: 'group', endpoint, schema: zod_1.z.object({ instance: zod_1.z.string() .min(1, 'Instance name is required') .describe('Name of the WhatsApp instance'), subject: zod_1.z.string() .min(1, 'Group name is required') .max(100, 'Group name cannot exceed 100 characters') .describe('Name of the group to create'), description: zod_1.z.string() .max(512, 'Group description cannot exceed 512 characters') .optional() .describe('Optional description for the group'), participants: zod_1.z.array(zod_1.z.string() .min(10, 'Valid phone number is required') .regex(/^\d+$/, 'Phone number must contain only digits')).min(1, 'At least one participant is required') .max(256, 'Cannot add more than 256 participants at once') .describe('Array of participant phone numbers (format: 5511999999999)') }), handler: this.createGroupHandler.bind(this), examples: { usage: 'Create a new WhatsApp group with multiple participants', parameters: { instance: 'my_whatsapp_bot', subject: 'My New Group', description: 'This is a test group created via Evolution API', participants: ['5511999999999', '5511888888888'] } } }; } async createGroupHandler(params) { try { const requestData = { subject: params.subject, participants: params.participants, ...(params.description && { description: params.description }) }; const response = await this.httpClient.post(`/group/create/${params.instance}`, requestData); if (!response.success) { return this.handleApiError(response.error, 'create group'); } return { success: true, data: { message: `Group "${params.subject}" created successfully with ${params.participants.length} participants`, instance: params.instance, groupName: params.subject, groupJid: response.data?.groupJid || response.data?.id, participantCount: params.participants.length, participants: params.participants, description: params.description, result: response.data } }; } catch (error) { return this.handleUnexpectedError(error, 'create group'); } } /** * Update group picture */ createUpdateGroupPictureTool() { const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'update-group-picture'); return { name: 'evolution_update_group_picture', description: 'Update the profile picture of a WhatsApp group', controller: 'group', endpoint, schema: zod_1.z.object({ instance: zod_1.z.string() .min(1, 'Instance name is required') .describe('Name of the WhatsApp instance'), groupJid: zod_1.z.string() .min(1, 'Group JID is required') .regex(/@g\.us$/, 'Group JID must end with @g.us') .describe('JID of the group (format: 120363123456789012@g.us)'), image: zod_1.z.string() .min(1, 'Image URL or base64 is required') .describe('Image URL (https://...) or base64 encoded image data') }), handler: this.updateGroupPictureHandler.bind(this), examples: { usage: 'Update the profile picture of a WhatsApp group', parameters: { instance: 'my_whatsapp_bot', groupJid: '120363123456789012@g.us', image: 'https://example.com/group-picture.jpg' } } }; } async updateGroupPictureHandler(params) { try { const requestData = { groupJid: params.groupJid, image: params.image }; const response = await this.httpClient.put(`/group/updateGroupPicture/${params.instance}`, requestData); if (!response.success) { return this.handleApiError(response.error, 'update group picture'); } return { success: true, data: { message: `Group picture updated successfully`, instance: params.instance, groupJid: params.groupJid, imageSource: this.detectImageType(params.image), result: response.data } }; } catch (error) { return this.handleUnexpectedError(error, 'update group picture'); } } /** * Update group subject (name) */ createUpdateGroupSubjectTool() { const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'update-group-subject'); return { name: 'evolution_update_group_subject', description: 'Update the name/subject of a WhatsApp group', controller: 'group', endpoint, schema: zod_1.z.object({ instance: zod_1.z.string() .min(1, 'Instance name is required') .describe('Name of the WhatsApp instance'), groupJid: zod_1.z.string() .min(1, 'Group JID is required') .regex(/@g\.us$/, 'Group JID must end with @g.us') .describe('JID of the group (format: 120363123456789012@g.us)'), subject: zod_1.z.string() .min(1, 'Group name is required') .max(100, 'Group name cannot exceed 100 characters') .describe('New name for the group') }), handler: this.updateGroupSubjectHandler.bind(this), examples: { usage: 'Change the name of a WhatsApp group', parameters: { instance: 'my_whatsapp_bot', groupJid: '120363123456789012@g.us', subject: 'Updated Group Name' } } }; } async updateGroupSubjectHandler(params) { try { const requestData = { groupJid: params.groupJid, subject: params.subject }; const response = await this.httpClient.put(`/group/updateGroupSubject/${params.instance}`, requestData); if (!response.success) { return this.handleApiError(response.error, 'update group subject'); } return { success: true, data: { message: `Group name updated successfully to "${params.subject}"`, instance: params.instance, groupJid: params.groupJid, newSubject: params.subject, result: response.data } }; } catch (error) { return this.handleUnexpectedError(error, 'update group subject'); } } /** * Update group description */ createUpdateGroupDescriptionTool() { const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'update-group-description'); return { name: 'evolution_update_group_description', description: 'Update the description of a WhatsApp group', controller: 'group', endpoint, schema: zod_1.z.object({ instance: zod_1.z.string() .min(1, 'Instance name is required') .describe('Name of the WhatsApp instance'), groupJid: zod_1.z.string() .min(1, 'Group JID is required') .regex(/@g\.us$/, 'Group JID must end with @g.us') .describe('JID of the group (format: 120363123456789012@g.us)'), description: zod_1.z.string() .max(512, 'Group description cannot exceed 512 characters') .describe('New description for the group (empty string to remove description)') }), handler: this.updateGroupDescriptionHandler.bind(this), examples: { usage: 'Update the description of a WhatsApp group', parameters: { instance: 'my_whatsapp_bot', groupJid: '120363123456789012@g.us', description: 'This is the updated group description' } } }; } async updateGroupDescriptionHandler(params) { try { const requestData = { groupJid: params.groupJid, description: params.description }; const response = await this.httpClient.put(`/group/updateGroupDescription/${params.instance}`, requestData); if (!response.success) { return this.handleApiError(response.error, 'update group description'); } const action = params.description ? 'updated' : 'removed'; return { success: true, data: { message: `Group description ${action} successfully`, instance: params.instance, groupJid: params.groupJid, newDescription: params.description, result: response.data } }; } catch (error) { return this.handleUnexpectedError(error, 'update group description'); } } /** * Fetch group invite code */ createFetchInviteCodeTool() { const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'fetch-invite-code'); return { name: 'evolution_fetch_group_invite_code', description: 'Get the invite code/link for a WhatsApp group', controller: 'group', endpoint, schema: zod_1.z.object({ instance: zod_1.z.string() .min(1, 'Instance name is required') .describe('Name of the WhatsApp instance'), groupJid: zod_1.z.string() .min(1, 'Group JID is required') .regex(/@g\.us$/, 'Group JID must end with @g.us') .describe('JID of the group (format: 120363123456789012@g.us)') }), handler: this.fetchInviteCodeHandler.bind(this), examples: { usage: 'Get the invite link for a WhatsApp group', parameters: { instance: 'my_whatsapp_bot', groupJid: '120363123456789012@g.us' } } }; } async fetchInviteCodeHandler(params) { try { const requestData = { groupJid: params.groupJid }; const response = await this.httpClient.post(`/group/fetchInviteCode/${params.instance}`, requestData); if (!response.success) { return this.handleApiError(response.error, 'fetch group invite code'); } const inviteCode = response.data?.inviteCode || response.data?.code; const inviteUrl = inviteCode ? `https://chat.whatsapp.com/${inviteCode}` : null; return { success: true, data: { message: `Group invite code retrieved successfully`, instance: params.instance, groupJid: params.groupJid, inviteCode: inviteCode, inviteUrl: inviteUrl, result: response.data } }; } catch (error) { return this.handleUnexpectedError(error, 'fetch group invite code'); } } /** * Revoke group invite code */ createRevokeInviteCodeTool() { const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'revoke-invite-code'); return { name: 'evolution_revoke_group_invite_code', description: 'Revoke the current invite code for a WhatsApp group (generates a new one)', controller: 'group', endpoint, schema: zod_1.z.object({ instance: zod_1.z.string() .min(1, 'Instance name is required') .describe('Name of the WhatsApp instance'), groupJid: zod_1.z.string() .min(1, 'Group JID is required') .regex(/@g\.us$/, 'Group JID must end with @g.us') .describe('JID of the group (format: 120363123456789012@g.us)') }), handler: this.revokeInviteCodeHandler.bind(this), examples: { usage: 'Revoke the current invite link and generate a new one for a WhatsApp group', parameters: { instance: 'my_whatsapp_bot', groupJid: '120363123456789012@g.us' } } }; } async revokeInviteCodeHandler(params) { try { const requestData = { groupJid: params.groupJid }; const response = await this.httpClient.put(`/group/revokeInviteCode/${params.instance}`, requestData); if (!response.success) { return this.handleApiError(response.error, 'revoke group invite code'); } const newInviteCode = response.data?.inviteCode || response.data?.code; const newInviteUrl = newInviteCode ? `https://chat.whatsapp.com/${newInviteCode}` : null; return { success: true, data: { message: `Group invite code revoked and new code generated successfully`, instance: params.instance, groupJid: params.groupJid, newInviteCode: newInviteCode, newInviteUrl: newInviteUrl, result: response.data } }; } catch (error) { return this.handleUnexpectedError(error, 'revoke group invite code'); } } /** * Update group participants (add, remove, promote, demote) */ createUpdateParticipantTool() { const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'update-participant'); return { name: 'evolution_update_group_participant', description: 'Manage group participants: add, remove, promote to admin, or demote from admin', controller: 'group', endpoint, schema: zod_1.z.object({ instance: zod_1.z.string() .min(1, 'Instance name is required') .describe('Name of the WhatsApp instance'), groupJid: zod_1.z.string() .min(1, 'Group JID is required') .regex(/@g\.us$/, 'Group JID must end with @g.us') .describe('JID of the group (format: 120363123456789012@g.us)'), action: zod_1.z.enum(['add', 'remove', 'promote', 'demote']) .describe('Action to perform: add (new members), remove (kick members), promote (make admin), demote (remove admin)'), participants: zod_1.z.array(zod_1.z.string() .min(10, 'Valid phone number is required') .regex(/^\d+$/, 'Phone number must contain only digits')).min(1, 'At least one participant is required') .max(50, 'Cannot process more than 50 participants at once') .describe('Array of participant phone numbers (format: 5511999999999)') }), handler: this.updateParticipantHandler.bind(this), examples: { usage: 'Add, remove, promote, or demote participants in a WhatsApp group', parameters: { instance: 'my_whatsapp_bot', groupJid: '120363123456789012@g.us', action: 'add', participants: ['5511999999999', '5511888888888'] } } }; } async updateParticipantHandler(params) { try { const requestData = { groupJid: params.groupJid, action: params.action, participants: params.participants }; const response = await this.httpClient.put(`/group/updateParticipant/${params.instance}`, requestData); if (!response.success) { return this.handleApiError(response.error, 'update group participants'); } const actionLabels = { add: 'added to', remove: 'removed from', promote: 'promoted to admin in', demote: 'demoted from admin in' }; return { success: true, data: { message: `${params.participants.length} participant(s) ${actionLabels[params.action]} the group successfully`, instance: params.instance, groupJid: params.groupJid, action: params.action, participantCount: params.participants.length, participants: params.participants, result: response.data } }; } catch (error) { return this.handleUnexpectedError(error, 'update group participants'); } } /** * Leave group */ createLeaveGroupTool() { const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'leave-group'); return { name: 'evolution_leave_group', description: 'Leave a WhatsApp group (the bot instance will exit the group)', controller: 'group', endpoint, schema: zod_1.z.object({ instance: zod_1.z.string() .min(1, 'Instance name is required') .describe('Name of the WhatsApp instance'), groupJid: zod_1.z.string() .min(1, 'Group JID is required') .regex(/@g\.us$/, 'Group JID must end with @g.us') .describe('JID of the group to leave (format: 120363123456789012@g.us)') }), handler: this.leaveGroupHandler.bind(this), examples: { usage: 'Leave a WhatsApp group', parameters: { instance: 'my_whatsapp_bot', groupJid: '120363123456789012@g.us' } } }; } async leaveGroupHandler(params) { try { const requestData = { groupJid: params.groupJid }; const response = await this.httpClient.delete(`/group/leaveGroup/${params.instance}`, requestData); if (!response.success) { return this.handleApiError(response.error, 'leave group'); } return { success: true, data: { message: `Successfully left the group`, instance: params.instance, groupJid: params.groupJid, result: response.data } }; } catch (error) { return this.handleUnexpectedError(error, 'leave group'); } } /** * Fetch group information */ createFetchGroupInfoTool() { const endpoint = group_endpoints_1.groupEndpoints.find(e => e.name === 'fetch-group-info'); return { name: 'evolution_fetch_group_info', description: 'Fetch information about all groups the instance is part of', controller: 'group', endpoint, schema: zod_1.z.object({ instance: zod_1.z.string() .min(1, 'Instance name is required') .describe('Name of the WhatsApp instance') }), handler: this.fetchGroupInfoHandler.bind(this), examples: { usage: 'Get information about all groups the bot is part of', parameters: { instance: 'my_whatsapp_bot' } } }; } async fetchGroupInfoHandler(params) { try { const response = await this.httpClient.get(`/group/fetchAllGroups/${params.instance}`); if (!response.success) { return this.handleApiError(response.error, 'fetch group information'); } const groups = response.data || []; const groupCount = Array.isArray(groups) ? groups.length : 0; return { success: true, data: { message: `Found ${groupCount} group(s)`, instance: params.instance, groupCount, groups: groups } }; } catch (error) { return this.handleUnexpectedError(error, 'fetch group information'); } } /** * Get all group tools */ getAllTools() { return [ this.createCreateGroupTool(), this.createUpdateGroupPictureTool(), this.createUpdateGroupSubjectTool(), this.createUpdateGroupDescriptionTool(), this.createFetchInviteCodeTool(), this.createRevokeInviteCodeTool(), this.createUpdateParticipantTool(), this.createLeaveGroupTool(), this.createFetchGroupInfoTool() ]; } /** * Detect image type from URL or base64 */ detectImageType(image) { if (image.startsWith('data:')) { return 'base64'; } else if (image.startsWith('http')) { return 'url'; } return 'unknown'; } /** * Handle API errors with user-friendly messages */ handleApiError(error, operation) { const statusCode = error.statusCode || error.status || 0; let message = `Failed to ${operation}`; switch (statusCode) { case 400: message = `Invalid request parameters for ${operation}`; break; case 401: message = `Authentication failed for ${operation}`; break; case 403: message = `Permission denied for ${operation}`; break; case 404: message = `Resource not found for ${operation}`; break; case 422: message = `Invalid data provided for ${operation}`; break; case 429: message = `Rate limit exceeded for ${operation}`; break; case 500: message = `Server error occurred during ${operation}`; break; } return { success: false, error: { type: this.mapStatusToErrorType(statusCode), message, code: statusCode.toString(), details: error } }; } /** * Handle unexpected errors */ handleUnexpectedError(error, operation) { const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred'; return { success: false, error: { type: 'UNKNOWN_ERROR', message: `Unexpected error during ${operation}: ${errorMessage}`, details: error } }; } /** * Map HTTP status codes to error types */ mapStatusToErrorType(statusCode) { switch (statusCode) { case 400: case 422: return 'VALIDATION_ERROR'; case 401: case 403: return 'AUTHENTICATION_ERROR'; case 404: return 'NOT_FOUND_ERROR'; case 429: return 'RATE_LIMIT_ERROR'; case 500: case 502: case 503: return 'API_ERROR'; default: return 'NETWORK_ERROR'; } } } exports.GroupTools = GroupTools;