UNPKG

n8n-nodes-wuzapi

Version:

n8n community nodes for Wuzapi - WhatsApp Multi-Device REST API

1,142 lines (1,141 loc) 50.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WuzapiMessage = void 0; const GenericFunctions_1 = require("../GenericFunctions"); class WuzapiMessage { constructor() { this.description = { displayName: 'Wuzapi Message', name: 'wuzapiMessage', icon: 'file:wuzapi.svg', group: ['transform'], version: 1, subtitle: '={{$parameter["messageType"]}}', description: 'Send messages through Wuzapi WhatsApp API', defaults: { name: 'Wuzapi Message', }, inputs: ["main" /* NodeConnectionType.Main */], outputs: ["main" /* NodeConnectionType.Main */], credentials: [ { name: 'wuzapiApi', required: true, }, ], properties: [ { displayName: 'Message Type', name: 'messageType', type: 'options', noDataExpression: true, options: [ { name: 'Text', value: 'text', description: 'Send a text message', action: 'Send text message', }, { name: 'Image', value: 'image', description: 'Send an image', action: 'Send image message', }, { name: 'Audio', value: 'audio', description: 'Send an audio message', action: 'Send audio message', }, { name: 'Video', value: 'video', description: 'Send a video', action: 'Send video message', }, { name: 'Document', value: 'document', description: 'Send a document', action: 'Send document', }, { name: 'Sticker', value: 'sticker', description: 'Send a sticker', action: 'Send sticker', }, { name: 'Location', value: 'location', description: 'Send a location', action: 'Send location', }, { name: 'Contact', value: 'contact', description: 'Send a contact card', action: 'Send contact', }, { name: 'Template', value: 'template', description: 'Send a template message with buttons', action: 'Send template message', }, { name: 'Buttons', value: 'buttons', description: 'Send a message with buttons', action: 'Send buttons message', }, { name: 'List', value: 'list', description: 'Send a list message', action: 'Send list message', }, { name: 'Poll', value: 'poll', description: 'Send a poll (groups only)', action: 'Send poll', }, ], default: 'text', }, // Common fields for all message types { displayName: 'Phone Number or Group ID', name: 'phone', type: 'string', default: '', placeholder: 'e.g. 5491155553934 or 120363312246943103@g.us', description: 'Phone number (without + or spaces) or Group ID', required: true, }, // Text message fields { displayName: 'Message', name: 'body', type: 'string', default: '', placeholder: 'Type your message here', description: 'The text message to send', required: true, displayOptions: { show: { messageType: ['text', 'buttons'], }, }, }, // Image fields { displayName: 'Image Source', name: 'imageSource', type: 'options', default: 'binary', options: [ { name: 'Binary Data', value: 'binary', description: 'Use binary data from previous node', }, { name: 'Base64', value: 'base64', description: 'Provide base64 encoded image', }, { name: 'URL', value: 'url', description: 'Provide URL to image file', }, ], displayOptions: { show: { messageType: ['image'], }, }, }, { displayName: 'Binary Property', name: 'binaryProperty', type: 'string', default: 'data', description: 'Name of the binary property containing the image', displayOptions: { show: { messageType: ['image'], imageSource: ['binary'], }, }, }, { displayName: 'Base64 Image', name: 'imageBase64', type: 'string', default: '', placeholder: 'data:image/jpeg;base64,iVBORw0...', description: 'Base64 encoded image with data URL prefix', displayOptions: { show: { messageType: ['image'], imageSource: ['base64'], }, }, }, { displayName: 'Image URL', name: 'imageUrl', type: 'string', default: '', placeholder: 'https://example.com/image.jpg', description: 'URL to the image file', displayOptions: { show: { messageType: ['image'], imageSource: ['url'], }, }, }, { displayName: 'Caption', name: 'caption', type: 'string', default: '', description: 'Caption for the image', displayOptions: { show: { messageType: ['image', 'video'], }, }, }, // Audio fields { displayName: 'Audio Source', name: 'audioSource', type: 'options', default: 'binary', options: [ { name: 'Binary Data', value: 'binary', description: 'Use binary data from previous node', }, { name: 'Base64', value: 'base64', description: 'Provide base64 encoded audio', }, { name: 'URL', value: 'url', description: 'Provide URL to audio file', }, ], displayOptions: { show: { messageType: ['audio'], }, }, }, { displayName: 'Binary Property', name: 'audioBinaryProperty', type: 'string', default: 'data', description: 'Name of the binary property containing the audio', displayOptions: { show: { messageType: ['audio'], audioSource: ['binary'], }, }, }, { displayName: 'Base64 Audio', name: 'audioBase64', type: 'string', default: '', placeholder: 'data:audio/ogg;base64,iVBORw0...', description: 'Base64 encoded audio in opus format', displayOptions: { show: { messageType: ['audio'], audioSource: ['base64'], }, }, }, { displayName: 'Audio URL', name: 'audioUrl', type: 'string', default: '', placeholder: 'https://example.com/audio.ogg', description: 'URL to the audio file', displayOptions: { show: { messageType: ['audio'], audioSource: ['url'], }, }, }, // Video fields { displayName: 'Video Source', name: 'videoSource', type: 'options', default: 'binary', options: [ { name: 'Binary Data', value: 'binary', description: 'Use binary data from previous node', }, { name: 'Base64', value: 'base64', description: 'Provide base64 encoded video', }, { name: 'URL', value: 'url', description: 'Provide URL to video file', }, ], displayOptions: { show: { messageType: ['video'], }, }, }, { displayName: 'Binary Property', name: 'videoBinaryProperty', type: 'string', default: 'data', description: 'Name of the binary property containing the video', displayOptions: { show: { messageType: ['video'], videoSource: ['binary'], }, }, }, { displayName: 'Base64 Video', name: 'videoBase64', type: 'string', default: '', placeholder: 'data:video/mp4;base64,iVBORw0...', description: 'Base64 encoded video (H.264 codec, AAC audio)', displayOptions: { show: { messageType: ['video'], videoSource: ['base64'], }, }, }, { displayName: 'Video URL', name: 'videoUrl', type: 'string', default: '', placeholder: 'https://example.com/video.mp4', description: 'URL to the video file', displayOptions: { show: { messageType: ['video'], videoSource: ['url'], }, }, }, // Document fields { displayName: 'Document Source', name: 'documentSource', type: 'options', default: 'binary', options: [ { name: 'Binary Data', value: 'binary', description: 'Use binary data from previous node', }, { name: 'Base64', value: 'base64', description: 'Provide base64 encoded document', }, { name: 'URL', value: 'url', description: 'Provide URL to document file', }, ], displayOptions: { show: { messageType: ['document'], }, }, }, { displayName: 'Binary Property', name: 'documentBinaryProperty', type: 'string', default: 'data', description: 'Name of the binary property containing the document', displayOptions: { show: { messageType: ['document'], documentSource: ['binary'], }, }, }, { displayName: 'Base64 Document', name: 'documentBase64', type: 'string', default: '', placeholder: 'data:application/pdf;base64,iVBORw0...', description: 'Base64 encoded document', displayOptions: { show: { messageType: ['document'], documentSource: ['base64'], }, }, }, { displayName: 'Document URL', name: 'documentUrl', type: 'string', default: '', placeholder: 'https://example.com/document.pdf', description: 'URL to the document file', displayOptions: { show: { messageType: ['document'], documentSource: ['url'], }, }, }, { displayName: 'File Name', name: 'fileName', type: 'string', default: '', placeholder: 'document.pdf', description: 'Name of the document file', required: true, displayOptions: { show: { messageType: ['document'], }, }, }, // Sticker fields { displayName: 'Sticker Source', name: 'stickerSource', type: 'options', default: 'binary', options: [ { name: 'Binary Data', value: 'binary', description: 'Use binary data from previous node', }, { name: 'Base64', value: 'base64', description: 'Provide base64 encoded sticker', }, { name: 'URL', value: 'url', description: 'Provide URL to sticker file', }, ], displayOptions: { show: { messageType: ['sticker'], }, }, }, { displayName: 'Binary Property', name: 'stickerBinaryProperty', type: 'string', default: 'data', description: 'Name of the binary property containing the sticker', displayOptions: { show: { messageType: ['sticker'], stickerSource: ['binary'], }, }, }, { displayName: 'Base64 Sticker', name: 'stickerBase64', type: 'string', default: '', placeholder: 'data:image/webp;base64,iVBORw0...', description: 'Base64 encoded sticker in WebP format', displayOptions: { show: { messageType: ['sticker'], stickerSource: ['base64'], }, }, }, { displayName: 'Sticker URL', name: 'stickerUrl', type: 'string', default: '', placeholder: 'https://example.com/sticker.webp', description: 'URL to the sticker file', displayOptions: { show: { messageType: ['sticker'], stickerSource: ['url'], }, }, }, // Location fields { displayName: 'Latitude', name: 'latitude', type: 'number', default: 0, placeholder: '48.858370', description: 'Latitude coordinate', required: true, displayOptions: { show: { messageType: ['location'], }, }, }, { displayName: 'Longitude', name: 'longitude', type: 'number', default: 0, placeholder: '2.294481', description: 'Longitude coordinate', required: true, displayOptions: { show: { messageType: ['location'], }, }, }, { displayName: 'Location Name', name: 'locationName', type: 'string', default: '', placeholder: 'Eiffel Tower', description: 'Name of the location', displayOptions: { show: { messageType: ['location'], }, }, }, // Contact fields { displayName: 'Contact Name', name: 'contactName', type: 'string', default: '', placeholder: 'John Doe', description: 'Full name of the contact', required: true, displayOptions: { show: { messageType: ['contact'], }, }, }, { displayName: 'VCard', name: 'vcard', type: 'string', default: '', placeholder: 'BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nTEL:+1234567890\nEND:VCARD', description: 'VCard data for the contact', required: true, displayOptions: { show: { messageType: ['contact'], }, }, }, // Template fields { displayName: 'Content', name: 'content', type: 'string', default: '', placeholder: 'Message content', description: 'Main content of the template message', required: true, displayOptions: { show: { messageType: ['template'], }, }, }, { displayName: 'Footer', name: 'footer', type: 'string', default: '', placeholder: 'Footer text', description: 'Footer text for the template', displayOptions: { show: { messageType: ['template'], }, }, }, { displayName: 'Buttons', name: 'templateButtons', type: 'fixedCollection', typeOptions: { multipleValues: true, }, default: {}, description: 'Buttons to add to the template', displayOptions: { show: { messageType: ['template'], }, }, options: [ { name: 'button', displayName: 'Button', values: [ { displayName: 'Display Text', name: 'displayText', type: 'string', default: '', description: 'Text to display on the button', required: true, }, { displayName: 'Type', name: 'type', type: 'options', default: 'quickreply', options: [ { name: 'Quick Reply', value: 'quickreply', }, { name: 'URL', value: 'url', }, { name: 'Call', value: 'call', }, ], description: 'Type of button', }, { displayName: 'URL', name: 'url', type: 'string', default: '', placeholder: 'https://example.com', description: 'URL to open when clicked', displayOptions: { show: { type: ['url'], }, }, required: true, }, { displayName: 'Phone Number', name: 'phoneNumber', type: 'string', default: '', placeholder: '1234567890', description: 'Phone number to call', displayOptions: { show: { type: ['call'], }, }, required: true, }, ], }, ], }, // List message fields { displayName: 'Button Text', name: 'buttonText', type: 'string', default: '', placeholder: 'Click here', description: 'Text for the list button', required: true, displayOptions: { show: { messageType: ['list'], }, }, }, { displayName: 'Description', name: 'description', type: 'string', default: '', placeholder: 'Choose an option', description: 'Description of the list', required: true, displayOptions: { show: { messageType: ['list'], }, }, }, { displayName: 'Top Text', name: 'topText', type: 'string', default: '', placeholder: 'Header text', description: 'Text to show above the list', required: true, displayOptions: { show: { messageType: ['list'], }, }, }, { displayName: 'Footer Text', name: 'footerText', type: 'string', default: '', placeholder: 'Footer text', description: 'Text to show below the list', displayOptions: { show: { messageType: ['list'], }, }, }, { displayName: 'List Items', name: 'listItems', type: 'fixedCollection', typeOptions: { multipleValues: true, }, default: {}, description: 'Items in the list', required: true, displayOptions: { show: { messageType: ['list'], }, }, options: [ { name: 'item', displayName: 'Item', values: [ { displayName: 'Title', name: 'title', type: 'string', default: '', description: 'Title of the list item', required: true, }, { displayName: 'Description', name: 'desc', type: 'string', default: '', description: 'Description of the list item', }, { displayName: 'Row ID', name: 'rowId', type: 'string', default: '', description: 'Unique identifier for the row', required: true, }, ], }, ], }, // Poll fields { displayName: 'Poll Question', name: 'pollHeader', type: 'string', default: '', placeholder: "What's your favorite color?", required: true, displayOptions: { show: { messageType: ['poll'], }, }, }, { displayName: 'Poll Options', name: 'pollOptions', type: 'string', default: '', placeholder: 'Red,Blue,Green,Yellow', description: 'Comma-separated list of poll options', required: true, displayOptions: { show: { messageType: ['poll'], }, }, }, // Additional options { displayName: 'Additional Options', name: 'additionalOptions', type: 'collection', placeholder: 'Add Option', default: {}, options: [ { displayName: 'Message ID', name: 'id', type: 'string', default: '', description: 'Custom message ID (auto-generated if not provided)', }, { displayName: 'Reply To Message', name: 'contextInfo', type: 'fixedCollection', default: {}, description: 'Reply to a specific message', options: [ { name: 'context', displayName: 'Context', values: [ { displayName: 'Message ID', name: 'stanzaId', type: 'string', default: '', description: 'ID of the message to reply to', required: true, }, { displayName: 'Participant', name: 'participant', type: 'string', default: '', placeholder: 'e.g. 5491155553935@s.whatsapp.net', description: 'JID of the message sender', required: true, }, ], }, ], }, { displayName: 'Mentions', name: 'mentions', type: 'fixedCollection', default: {}, description: 'Mention specific users or all group members', options: [ { name: 'mentionConfig', displayName: 'Mention Configuration', values: [ { displayName: 'Mention Type', name: 'type', type: 'options', default: 'none', options: [ { name: 'None', value: 'none', description: 'No mentions', }, { name: 'Specific Users', value: 'specific', description: 'Mention specific users', }, { name: 'All Group Members', value: 'all', description: 'Mention all group members (groups only)', }, ], }, { displayName: 'User JIDs', name: 'jids', type: 'string', default: '', placeholder: 'e.g. 5491155553934@s.whatsapp.net,5491155553935@s.whatsapp.net', description: 'Comma-separated list of user JIDs to mention', displayOptions: { show: { type: ['specific'], }, }, }, ], }, ], }, ], }, ], }; } async execute() { const items = this.getInputData(); const returnData = []; for (let i = 0; i < items.length; i++) { try { const messageType = this.getNodeParameter('messageType', i); const phone = this.getNodeParameter('phone', i); const additionalOptions = this.getNodeParameter('additionalOptions', i); let endpoint = ''; let body = { Phone: phone, }; // Add common fields if (additionalOptions.id) { body.Id = additionalOptions.id; } if (additionalOptions.contextInfo && additionalOptions.contextInfo.context) { const context = additionalOptions.contextInfo.context; if (context.stanzaId && context.participant) { body.ContextInfo = { StanzaId: context.stanzaId, Participant: context.participant, }; } } // Handle mentions if (additionalOptions.mentions && additionalOptions.mentions.mentionConfig) { const mentionConfig = additionalOptions.mentions.mentionConfig; if (Array.isArray(mentionConfig) && mentionConfig.length > 0) { const config = mentionConfig[0]; if (config.type === 'specific' && config.jids) { const jids = config.jids.split(',').map((jid) => jid.trim()); body.mention_info = { mentions: jids, }; } else if (config.type === 'all') { body.mention_info = { mention_all: true, }; } } } // Handle different message types if (messageType === 'text') { endpoint = '/chat/send/text'; body.Body = this.getNodeParameter('body', i); } else if (messageType === 'image') { endpoint = '/chat/send/image'; const imageSource = this.getNodeParameter('imageSource', i); if (imageSource === 'binary') { const binaryProperty = this.getNodeParameter('binaryProperty', i); const binaryData = items[i].binary?.[binaryProperty]; if (!binaryData) { throw new Error(`Binary property "${binaryProperty}" not found`); } const buffer = Buffer.from(binaryData.data, 'base64'); body.Image = (0, GenericFunctions_1.prepareMediaData)(buffer, binaryData.mimeType || 'image/jpeg'); } else if (imageSource === 'base64') { body.Image = this.getNodeParameter('imageBase64', i); } else if (imageSource === 'url') { body.Image = this.getNodeParameter('imageUrl', i); } const caption = this.getNodeParameter('caption', i); if (caption) { body.Caption = caption; } } else if (messageType === 'audio') { endpoint = '/chat/send/audio'; const audioSource = this.getNodeParameter('audioSource', i); if (audioSource === 'binary') { const binaryProperty = this.getNodeParameter('audioBinaryProperty', i); const binaryData = items[i].binary?.[binaryProperty]; if (!binaryData) { throw new Error(`Binary property "${binaryProperty}" not found`); } const buffer = Buffer.from(binaryData.data, 'base64'); body.Audio = (0, GenericFunctions_1.prepareMediaData)(buffer, binaryData.mimeType || 'audio/ogg'); } else if (audioSource === 'base64') { body.Audio = this.getNodeParameter('audioBase64', i); } else if (audioSource === 'url') { body.Audio = this.getNodeParameter('audioUrl', i); } } else if (messageType === 'video') { endpoint = '/chat/send/video'; const videoSource = this.getNodeParameter('videoSource', i); if (videoSource === 'binary') { const binaryProperty = this.getNodeParameter('videoBinaryProperty', i); const binaryData = items[i].binary?.[binaryProperty]; if (!binaryData) { throw new Error(`Binary property "${binaryProperty}" not found`); } const buffer = Buffer.from(binaryData.data, 'base64'); body.Video = (0, GenericFunctions_1.prepareMediaData)(buffer, binaryData.mimeType || 'video/mp4'); } else if (videoSource === 'base64') { body.Video = this.getNodeParameter('videoBase64', i); } else if (videoSource === 'url') { body.Video = this.getNodeParameter('videoUrl', i); } const caption = this.getNodeParameter('caption', i); if (caption) { body.Caption = caption; } } else if (messageType === 'document') { endpoint = '/chat/send/document'; const documentSource = this.getNodeParameter('documentSource', i); if (documentSource === 'binary') { const binaryProperty = this.getNodeParameter('documentBinaryProperty', i); const binaryData = items[i].binary?.[binaryProperty]; if (!binaryData) { throw new Error(`Binary property "${binaryProperty}" not found`); } const buffer = Buffer.from(binaryData.data, 'base64'); body.Document = (0, GenericFunctions_1.prepareMediaData)(buffer, binaryData.mimeType || 'application/octet-stream'); } else if (documentSource === 'base64') { body.Document = this.getNodeParameter('documentBase64', i); } else if (documentSource === 'url') { body.Document = this.getNodeParameter('documentUrl', i); } body.FileName = this.getNodeParameter('fileName', i); } else if (messageType === 'sticker') { endpoint = '/chat/send/sticker'; const stickerSource = this.getNodeParameter('stickerSource', i); if (stickerSource === 'binary') { const binaryProperty = this.getNodeParameter('stickerBinaryProperty', i); const binaryData = items[i].binary?.[binaryProperty]; if (!binaryData) { throw new Error(`Binary property "${binaryProperty}" not found`); } const buffer = Buffer.from(binaryData.data, 'base64'); body.Sticker = (0, GenericFunctions_1.prepareMediaData)(buffer, binaryData.mimeType || 'image/webp'); } else if (stickerSource === 'base64') { body.Sticker = this.getNodeParameter('stickerBase64', i); } else if (stickerSource === 'url') { body.Sticker = this.getNodeParameter('stickerUrl', i); } } else if (messageType === 'location') { endpoint = '/chat/send/location'; body.Latitude = this.getNodeParameter('latitude', i); body.Longitude = this.getNodeParameter('longitude', i); const locationName = this.getNodeParameter('locationName', i); if (locationName) { body.Name = locationName; } } else if (messageType === 'contact') { endpoint = '/chat/send/contact'; body.Name = this.getNodeParameter('contactName', i); body.Vcard = this.getNodeParameter('vcard', i); } else if (messageType === 'template') { endpoint = '/chat/send/template'; body.Content = this.getNodeParameter('content', i); const footer = this.getNodeParameter('footer', i); if (footer) { body.Footer = footer; } const buttons = this.getNodeParameter('templateButtons', i).button; if (buttons && buttons.length > 0) { body.Buttons = buttons.map((btn) => { const button = { DisplayText: btn.displayText, Type: btn.type, }; if (btn.type === 'url' && btn.url) { button.Url = btn.url; } if (btn.type === 'call' && btn.phoneNumber) { button.PhoneNumber = btn.phoneNumber; } return button; }); } } else if (messageType === 'buttons') { endpoint = '/chat/send/buttons'; body.Body = this.getNodeParameter('body', i); } else if (messageType === 'list') { endpoint = '/chat/send/list'; body.ButtonText = this.getNodeParameter('buttonText', i); body.Desc = this.getNodeParameter('description', i); body.TopText = this.getNodeParameter('topText', i); const footerText = this.getNodeParameter('footerText', i); if (footerText) { body.FooterText = footerText; } const listItems = this.getNodeParameter('listItems', i).item; if (listItems && listItems.length > 0) { body.List = listItems.map((item) => ({ title: item.title, desc: item.desc || '', RowId: item.rowId, })); } } else if (messageType === 'poll') { endpoint = '/chat/send/poll'; body.Group = phone; // For polls, phone should be a group ID body.Header = this.getNodeParameter('pollHeader', i); const pollOptions = this.getNodeParameter('pollOptions', i); body.Options = pollOptions.split(',').map(opt => opt.trim()); } const responseData = await GenericFunctions_1.wuzapiApiRequest.call(this, 'POST', endpoint, body);