n8n-nodes-wuzapi
Version:
n8n community nodes for Wuzapi - WhatsApp Multi-Device REST API
1,004 lines • 44.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WuzapiAI = void 0;
const GenericFunctions_1 = require("../GenericFunctions");
class WuzapiAI {
constructor() {
this.description = {
displayName: 'Wuzapi AI',
name: 'wuzapiAi',
icon: 'file:wuzapi.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"]}} message',
description: 'Send WhatsApp messages using Wuzapi - optimized for AI workflows',
defaults: {
name: 'Wuzapi AI',
},
usableAsTool: true,
inputs: ["main" /* NodeConnectionType.Main */],
outputs: ["main" /* NodeConnectionType.Main */],
credentials: [
{
name: 'wuzapiApi',
required: true,
},
],
properties: [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Send Text',
value: 'sendText',
description: 'Send a text message',
action: 'Send a text message',
},
{
name: 'Send Image',
value: 'sendImage',
description: 'Send an image message',
action: 'Send an image message',
},
{
name: 'Send Document',
value: 'sendDocument',
description: 'Send a document message',
action: 'Send a document message',
},
{
name: 'Send Audio',
value: 'sendAudio',
description: 'Send an audio message',
action: 'Send an audio message',
},
{
name: 'Send Video',
value: 'sendVideo',
description: 'Send a video message',
action: 'Send a video message',
},
{
name: 'Send Location',
value: 'sendLocation',
description: 'Send a location message',
action: 'Send a location message',
},
{
name: 'Send Contact',
value: 'sendContact',
description: 'Send a contact message',
action: 'Send a contact message',
},
{
name: 'Send Sticker',
value: 'sendSticker',
description: 'Send a sticker',
action: 'Send a sticker message',
},
{
name: 'Send Buttons',
value: 'sendButtons',
description: 'Send a message with buttons',
action: 'Send buttons message',
},
{
name: 'Send List',
value: 'sendList',
description: 'Send a list message',
action: 'Send list message',
},
{
name: 'Send Poll',
value: 'sendPoll',
description: 'Send a poll',
action: 'Send poll message',
},
],
default: 'sendText',
},
// Phone Number
{
displayName: 'Phone Number',
name: 'phoneNumber',
type: 'string',
default: '',
placeholder: '5491155553934',
description: 'Phone number in international format (without +)',
required: true,
},
// Text Message
{
displayName: 'Message',
name: 'message',
type: 'string',
displayOptions: {
show: {
operation: ['sendText', 'sendButtons'],
},
},
default: '',
description: 'The text message to send',
required: true,
},
// Image fields - complete implementation
{
displayName: 'Image Source',
name: 'imageSource',
type: 'options',
default: 'url',
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: {
operation: ['sendImage'],
},
},
},
{
displayName: 'Binary Property',
name: 'imageBinaryProperty',
type: 'string',
default: 'data',
description: 'Name of the binary property containing the image',
displayOptions: {
show: {
operation: ['sendImage'],
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: {
operation: ['sendImage'],
imageSource: ['base64'],
},
},
required: true,
},
{
displayName: 'Image URL',
name: 'imageUrl',
type: 'string',
default: '',
placeholder: 'https://example.com/image.jpg',
description: 'URL to the image file',
displayOptions: {
show: {
operation: ['sendImage'],
imageSource: ['url'],
},
},
required: true,
},
// Video fields - complete implementation
{
displayName: 'Video Source',
name: 'videoSource',
type: 'options',
default: 'url',
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: {
operation: ['sendVideo'],
},
},
},
{
displayName: 'Binary Property',
name: 'videoBinaryProperty',
type: 'string',
default: 'data',
description: 'Name of the binary property containing the video',
displayOptions: {
show: {
operation: ['sendVideo'],
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: {
operation: ['sendVideo'],
videoSource: ['base64'],
},
},
required: true,
},
{
displayName: 'Video URL',
name: 'videoUrl',
type: 'string',
default: '',
placeholder: 'https://example.com/video.mp4',
description: 'URL to the video file',
displayOptions: {
show: {
operation: ['sendVideo'],
videoSource: ['url'],
},
},
required: true,
},
// Document fields - complete implementation
{
displayName: 'Document Source',
name: 'documentSource',
type: 'options',
default: 'url',
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: {
operation: ['sendDocument'],
},
},
},
{
displayName: 'Binary Property',
name: 'documentBinaryProperty',
type: 'string',
default: 'data',
description: 'Name of the binary property containing the document',
displayOptions: {
show: {
operation: ['sendDocument'],
documentSource: ['binary'],
},
},
},
{
displayName: 'Base64 Document',
name: 'documentBase64',
type: 'string',
default: '',
placeholder: 'data:application/pdf;base64,iVBORw0...',
description: 'Base64 encoded document',
displayOptions: {
show: {
operation: ['sendDocument'],
documentSource: ['base64'],
},
},
required: true,
},
{
displayName: 'Document URL',
name: 'documentUrl',
type: 'string',
default: '',
placeholder: 'https://example.com/document.pdf',
description: 'URL to the document file',
displayOptions: {
show: {
operation: ['sendDocument'],
documentSource: ['url'],
},
},
required: true,
},
{
displayName: 'File Name',
name: 'fileName',
type: 'string',
displayOptions: {
show: {
operation: ['sendDocument'],
},
},
default: '',
placeholder: 'document.pdf',
description: 'Name of the document file',
required: true,
},
// Audio fields - complete implementation
{
displayName: 'Audio Source',
name: 'audioSource',
type: 'options',
default: 'url',
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: {
operation: ['sendAudio'],
},
},
},
{
displayName: 'Binary Property',
name: 'audioBinaryProperty',
type: 'string',
default: 'data',
description: 'Name of the binary property containing the audio',
displayOptions: {
show: {
operation: ['sendAudio'],
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: {
operation: ['sendAudio'],
audioSource: ['base64'],
},
},
required: true,
},
{
displayName: 'Audio URL',
name: 'audioUrl',
type: 'string',
default: '',
placeholder: 'https://example.com/audio.ogg',
description: 'URL to the audio file',
displayOptions: {
show: {
operation: ['sendAudio'],
audioSource: ['url'],
},
},
required: true,
},
{
displayName: 'Voice Note (PTT)',
name: 'ptt',
type: 'boolean',
default: true,
description: 'Whether to send as voice note (PTT) or audio file',
displayOptions: {
show: {
operation: ['sendAudio'],
},
},
},
// Sticker fields - complete implementation
{
displayName: 'Sticker Source',
name: 'stickerSource',
type: 'options',
default: 'url',
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: {
operation: ['sendSticker'],
},
},
},
{
displayName: 'Binary Property',
name: 'stickerBinaryProperty',
type: 'string',
default: 'data',
description: 'Name of the binary property containing the sticker',
displayOptions: {
show: {
operation: ['sendSticker'],
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: {
operation: ['sendSticker'],
stickerSource: ['base64'],
},
},
required: true,
},
{
displayName: 'Sticker URL',
name: 'stickerUrl',
type: 'string',
default: '',
placeholder: 'https://example.com/sticker.webp',
description: 'URL to the sticker file',
displayOptions: {
show: {
operation: ['sendSticker'],
stickerSource: ['url'],
},
},
required: true,
},
// Caption for media
{
displayName: 'Caption',
name: 'caption',
type: 'string',
displayOptions: {
show: {
operation: ['sendImage', 'sendDocument', 'sendVideo'],
},
},
default: '',
description: 'Caption for the media file',
},
// Location fields
{
displayName: 'Latitude',
name: 'latitude',
type: 'number',
displayOptions: {
show: {
operation: ['sendLocation'],
},
},
default: 0,
description: 'Latitude coordinate',
required: true,
},
{
displayName: 'Longitude',
name: 'longitude',
type: 'number',
displayOptions: {
show: {
operation: ['sendLocation'],
},
},
default: 0,
description: 'Longitude coordinate',
required: true,
},
{
displayName: 'Location Name',
name: 'locationName',
type: 'string',
displayOptions: {
show: {
operation: ['sendLocation'],
},
},
default: '',
description: 'Name of the location',
},
// Contact fields
{
displayName: 'Contact Name',
name: 'contactName',
type: 'string',
displayOptions: {
show: {
operation: ['sendContact'],
},
},
default: '',
description: 'Name of the contact',
required: true,
},
{
displayName: 'VCard',
name: 'vcard',
type: 'string',
displayOptions: {
show: {
operation: ['sendContact'],
},
},
default: '',
placeholder: 'BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nTEL:+1234567890\nEND:VCARD',
description: 'VCard data for the contact',
required: true,
},
// List fields
{
displayName: 'Button Text',
name: 'buttonText',
type: 'string',
default: '',
placeholder: 'Click here',
description: 'Text for the list button',
required: true,
displayOptions: {
show: {
operation: ['sendList'],
},
},
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
placeholder: 'Choose an option',
description: 'Description of the list',
required: true,
displayOptions: {
show: {
operation: ['sendList'],
},
},
},
{
displayName: 'Top Text',
name: 'topText',
type: 'string',
default: '',
placeholder: 'Header text',
description: 'Text to show above the list',
required: true,
displayOptions: {
show: {
operation: ['sendList'],
},
},
},
{
displayName: 'Footer Text',
name: 'footerText',
type: 'string',
default: '',
placeholder: 'Footer text',
description: 'Text to show below the list',
displayOptions: {
show: {
operation: ['sendList'],
},
},
},
{
displayName: 'List Items',
name: 'listItems',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
default: {},
description: 'Items in the list',
required: true,
displayOptions: {
show: {
operation: ['sendList'],
},
},
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: {
operation: ['sendPoll'],
},
},
},
{
displayName: 'Poll Options',
name: 'pollOptions',
type: 'string',
default: '',
placeholder: 'Red,Blue,Green,Yellow',
description: 'Comma-separated list of poll options',
required: true,
displayOptions: {
show: {
operation: ['sendPoll'],
},
},
},
// Additional Options for all message types
{
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: '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 operation = this.getNodeParameter('operation', i);
const phoneNumber = this.getNodeParameter('phoneNumber', i);
const additionalOptions = this.getNodeParameter('additionalOptions', i);
// Format phone number for WhatsApp
const phone = phoneNumber.includes('@') ? phoneNumber : `${phoneNumber}.whatsapp.net`;
let endpoint = '';
let body = {
Phone: phone,
};
// Add common fields
if (additionalOptions.id) {
body.Id = additionalOptions.id;
}
// 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,
};
}
}
}
switch (operation) {
case 'sendText':
endpoint = '/chat/send/text';
body.Body = this.getNodeParameter('message', i);
break;
case 'sendImage':
endpoint = '/chat/send/image';
const imageSource = this.getNodeParameter('imageSource', i);
if (imageSource === 'binary') {
const binaryProperty = this.getNodeParameter('imageBinaryProperty', 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 imageCaption = this.getNodeParameter('caption', i, '');
if (imageCaption)
body.Caption = imageCaption;
break;
case 'sendDocument':
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);
const docCaption = this.getNodeParameter('caption', i, '');
if (docCaption)
body.Caption = docCaption;
break;
case 'sendAudio':
endpoint = '/chat/send/audio';
const audioSource = this.getNodeParameter('audioSource', i);
const ptt = this.getNodeParameter('ptt', 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);
}
body.PTT = ptt;
break;
case 'sendVideo':
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 videoCaption = this.getNodeParameter('caption', i, '');
if (videoCaption)
body.Caption = videoCaption;
break;
case 'sendLocation':
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;
break;
case 'sendContact':
endpoint = '/chat/send/contact';
body.Name = this.getNodeParameter('contactName', i);
body.Vcard = this.getNodeParameter('vcard', i);
break;
case 'sendSticker':
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);
}
break;
case 'sendButtons':
endpoint = '/chat/send/buttons';
body.Body = this.getNodeParameter('message', i);
break;
case 'sendList':
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,
}));
}
break;
case 'sendPoll':
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());
break;
default:
throw new Error(`Unknown operation: ${operation}`);
}
const response = await GenericFunctions_1.wuzapiApiRequest.call(this, 'POST', endpoint, body);
const executionData = this.helpers.constructExecutionMetaData([{ json: response }], { itemData: { item: i } });
returnData.push(...executionData);
}
catch (error) {
if (this.continueOnFail()) {
const executionData = this.helpers.constructExecutionMetaData([{ json: { error: error.message } }], { itemData: { item: i } });
returnData.push(...executionData);
continue;
}
throw error;
}
}
return [returnData];
}
}
exports.WuzapiAI = WuzapiAI;
//# sourceMappingURL=WuzapiAI.node.js.map