UNPKG

n8n-nodes-japi

Version:

Node comunitário para integrar a J-API (WhatsApp) ao n8n

647 lines (646 loc) 30.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.JApi = void 0; var n8n_workflow_1 = require("n8n-workflow"); var JApi = /** @class */ (function () { function JApi() { this.description = { displayName: 'J-API', name: 'japi', group: ['output'], version: 1, icon: 'file:japi.png', description: 'Interage com a API J-API (WhatsApp)', defaults: { name: 'J-API' }, inputs: ['main'], outputs: ['main'], credentials: [{ name: 'japiApi', required: true }], /*────────── UI fields ──────────*/ properties: [ /* 1. seletor da ação */ { displayName: 'Tipo de envio', name: 'action', type: 'options', options: [ { name: 'Texto', value: 'text' }, { name: 'Mídia', value: 'media' }, { name: 'Contato', value: 'contact' }, { name: 'Localização', value: 'location' }, { name: 'Status', value: 'status' }, { name: 'Menu', value: 'menu' }, { name: 'Carousel', value: 'carousel' }, ], default: 'text', }, /* 2. campos compartilhados */ { displayName: 'Telefone (E.164)', name: 'phone', type: 'string', displayOptions: { show: { action: [ 'text', 'media', 'contact', 'location', 'menu', 'carousel', ], }, }, default: '', required: true, }, /* 3. campos específicos */ /* Texto */ { displayName: 'Mensagem de texto', name: 'text', type: 'string', displayOptions: { show: { action: ['text', 'status'] } }, default: '', required: true, }, /* Mídia */ /*{ displayName: 'URL da mídia', name: 'mediaUrl', type: 'string', displayOptions: { show: { action: ['media'] } }, default: '', required: true, },*/ { displayName: 'Legenda (opcional)', name: 'caption', type: 'string', displayOptions: { show: { action: ['media'] } }, default: '', }, { displayName: 'Tipo de mídia', name: 'mediaType', type: 'options', displayOptions: { show: { action: ['media'] } }, options: [ { name: 'Imagem', value: 'image' }, { name: 'Vídeo', value: 'video' }, { name: 'Documento', value: 'document' }, { name: 'Áudio', value: 'audio' }, { name: 'MyAudio', value: 'myaudio' }, { name: 'PTT', value: 'ptt' }, { name: 'Sticker', value: 'sticker' }, ], default: 'image', required: true, }, { displayName: 'Arquivo (URL ou base64)', name: 'file', type: 'string', displayOptions: { show: { action: ['media'] } }, default: '', required: true, }, { displayName: 'Nome do doc (apenas documents)', name: 'docName', type: 'string', displayOptions: { show: { action: ['media'] } }, default: '', }, { displayName: 'MIME type (opcional)', name: 'mimetype', type: 'string', displayOptions: { show: { action: ['media'] } }, default: '', }, /* Campos compartilhados */ { displayName: 'Reply ID', name: 'replyid', type: 'string', displayOptions: { show: { action: ['media', 'contact'] } }, default: '', description: 'ID da mensagem para responder', }, { displayName: 'Menções', name: 'mentions', type: 'string', displayOptions: { show: { action: ['media', 'contact'] } }, default: '', description: 'Números para mencionar (separados por vírgula)', placeholder: '5511999999999,5511888888888', }, { displayName: 'Marcar chat como lido', name: 'readchat', type: 'boolean', displayOptions: { show: { action: ['media', 'contact'] } }, default: false, description: 'Marca conversa como lida após envio', }, { displayName: 'Atraso (ms)', name: 'delay', type: 'number', displayOptions: { show: { action: ['media', 'contact'] } }, default: 0, description: 'Atraso em milissegundos antes do envio, durante o atraso aparecerá "Digitando..."', }, /* Contato (vCard) */ { displayName: 'Nome completo', name: 'fullName', type: 'string', displayOptions: { show: { action: ['contact'] } }, default: '', required: true, description: 'Nome completo do contato', }, { displayName: 'Números de telefone', name: 'phoneNumber', type: 'string', displayOptions: { show: { action: ['contact'] } }, default: '', required: true, description: 'Números de telefone (separados por vírgula)', placeholder: '5511999999999,5511888888888', }, { displayName: 'Organização', name: 'organization', type: 'string', displayOptions: { show: { action: ['contact'] } }, default: '', description: 'Nome da organização/empresa', }, { displayName: 'Email', name: 'email', type: 'string', displayOptions: { show: { action: ['contact'] } }, default: '', description: 'Endereço de email', placeholder: 'joao@empresa.com', }, { displayName: 'URL', name: 'url', type: 'string', displayOptions: { show: { action: ['contact'] } }, default: '', description: 'URL pessoal ou da empresa', placeholder: 'https://empresa.com/joao', }, /* Localização */ { displayName: 'Nome do local', name: 'name', type: 'string', displayOptions: { show: { action: ['location'] } }, default: '', description: 'Nome do local', }, { displayName: 'Endereço', name: 'address', type: 'string', displayOptions: { show: { action: ['location'] } }, default: '', description: 'Endereço completo do local', }, { displayName: 'Latitude', name: 'latitude', type: 'number', displayOptions: { show: { action: ['location'] } }, default: 0, required: true, description: 'Latitude (-90 a 90)', }, { displayName: 'Longitude', name: 'longitude', type: 'number', displayOptions: { show: { action: ['location'] } }, default: 0, required: true, description: 'Longitude (-180 a 180)', }, { displayName: 'Reply ID', name: 'replyid', type: 'string', displayOptions: { show: { action: ['location'] } }, default: '', description: 'ID da mensagem para responder', }, { displayName: 'Menções', name: 'mentions', type: 'string', displayOptions: { show: { action: ['location'] } }, default: '', description: 'Números para mencionar (separados por vírgula)', placeholder: '5511999999999,5511888888888', }, { displayName: 'Marcar chat como lido', name: 'readchat', type: 'boolean', displayOptions: { show: { action: ['location'] } }, default: false, description: 'Marca conversa como lida após envio', }, { displayName: 'Atraso (ms)', name: 'delay', type: 'number', displayOptions: { show: { action: ['location'] } }, default: 0, description: 'Atraso em milissegundos antes do envio, durante o atraso aparecerá "Digitando..."', }, /* Menu */ { displayName: 'Tipo do menu', name: 'menuType', type: 'options', displayOptions: { show: { action: ['menu'] } }, options: [ { name: 'Botões', value: 'button' }, { name: 'Lista', value: 'list' }, { name: 'Enquete', value: 'poll' }, { name: 'Carrossel', value: 'carousel' }, ], default: 'button', required: true, description: 'Tipo do menu interativo', }, { displayName: 'Texto principal', name: 'menuText', type: 'string', displayOptions: { show: { action: ['menu'] } }, default: '', required: true, description: 'Texto principal da mensagem (aceita placeholders)', }, { displayName: 'Texto do rodapé', name: 'footerText', type: 'string', displayOptions: { show: { action: ['menu'] } }, default: '', description: 'Texto do rodapé (opcional para botões e listas)', }, { displayName: 'Texto do botão principal', name: 'listButton', type: 'string', displayOptions: { show: { action: ['menu'] } }, default: '', description: 'Texto do botão principal (para listas)', }, { displayName: 'Número de opções selecionáveis', name: 'sen8nasd', type: 'number', displayOptions: { show: { action: ['menu'] } }, default: 1, description: 'Número máximo de opções selecionáveis (para enquetes)', }, { displayName: 'Opções', name: 'choices', type: 'json', displayOptions: { show: { action: ['menu'] } }, default: '["Opção 1|id1","Opção 2|id2"]', required: true, description: 'Lista de opções. Use [Título] para seções em listas', }, { displayName: 'Reply ID', name: 'replyid', type: 'string', displayOptions: { show: { action: ['menu'] } }, default: '', description: 'ID da mensagem para responder', }, { displayName: 'Menções', name: 'mentions', type: 'string', displayOptions: { show: { action: ['menu'] } }, default: '', description: 'Números para mencionar (separados por vírgula)', }, { displayName: 'Marcar chat como lido', name: 'readchat', type: 'boolean', displayOptions: { show: { action: ['menu'] } }, default: false, description: 'Marca conversa como lida após envio', }, { displayName: 'Atraso (ms)', name: 'delay', type: 'number', displayOptions: { show: { action: ['menu'] } }, default: 0, description: 'Atraso em milissegundos antes do envio', }, /* Carousel */ { displayName: 'Texto principal', name: 'carouselText', type: 'string', displayOptions: { show: { action: ['carousel'] } }, default: '', required: true, description: 'Texto principal da mensagem', }, { displayName: 'Carrossel (JSON)', name: 'carousel', type: 'json', displayOptions: { show: { action: ['carousel'] } }, default: '[{"text":"Texto do cartão","image":"https://exemplo.com/imagem.jpg","buttons":[{"id":"resposta1","text":"Texto do botão","type":"REPLY"}]}]', required: true, description: 'Array de cartões do carrossel com text, image e buttons', }, { displayName: 'Atraso (ms)', name: 'delay', type: 'number', displayOptions: { show: { action: ['carousel'] } }, default: 0, description: 'Atraso em milissegundos antes do envio', }, { displayName: 'Marcar chat como lido', name: 'readchat', type: 'boolean', displayOptions: { show: { action: ['carousel'] } }, default: false, description: 'Marca conversa como lida após envio', }, /* Status */ { displayName: 'Tipo do status', name: 'type', type: 'options', displayOptions: { show: { action: ['status'] } }, options: [ { name: 'Texto', value: 'text' }, { name: 'Imagem', value: 'image' }, { name: 'Vídeo', value: 'video' }, { name: 'Áudio', value: 'audio' }, { name: 'MyAudio', value: 'myaudio' }, { name: 'PTT', value: 'ptt' }, ], default: 'text', required: true, description: 'Tipo do status', }, { displayName: 'Texto', name: 'text', type: 'string', displayOptions: { show: { action: ['status'] } }, default: '', description: 'Texto principal ou legenda', }, { displayName: 'Cor de fundo', name: 'background_color', type: 'number', displayOptions: { show: { action: ['status'] } }, default: 1, description: 'Código da cor de fundo (1 – 19)', }, { displayName: 'Fonte', name: 'font', type: 'number', displayOptions: { show: { action: ['status'] } }, default: 0, description: 'Estilo da fonte (somente quando type = text) • (0 – 8)', }, { displayName: 'Arquivo', name: 'file', type: 'string', displayOptions: { show: { action: ['status'] } }, default: '', description: 'URL ou Base64 do arquivo de mídia', }, { displayName: 'Miniatura', name: 'thumbnail', type: 'string', displayOptions: { show: { action: ['status'] } }, default: '', description: 'URL ou Base64 da miniatura (opcional para vídeos)', }, { displayName: 'MIME type', name: 'mimetype', type: 'string', displayOptions: { show: { action: ['status'] } }, default: '', description: 'MIME type do arquivo (opcional)', }, ], }; /* end description */ } /*────────── EXECUTE ──────────*/ JApi.prototype.execute = function () { return __awaiter(this, void 0, void 0, function () { var creds, items, out, i, action, endpoint, body, options, res, err_1; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.getCredentials('japiApi')]; case 1: creds = _a.sent(); items = this.getInputData(); out = []; i = 0; _a.label = 2; case 2: if (!(i < items.length)) return [3 /*break*/, 7]; action = this.getNodeParameter('action', i); endpoint = "/send/".concat(action); body = {}; /* switch monta o corpo conforme o tipo */ switch (action) { case 'text': body = { number: this.getNodeParameter('phone', i), text: this.getNodeParameter('text', i), }; break; case 'media': body = { number: this.getNodeParameter('phone', i), type: this.getNodeParameter('mediaType', i), file: this.getNodeParameter('file', i), text: this.getNodeParameter('caption', i) || undefined, docName: this.getNodeParameter('docName', i) || undefined, mimetype: this.getNodeParameter('mimetype', i) || undefined, replyid: this.getNodeParameter('replyid', i) || undefined, mentions: this.getNodeParameter('mentions', i) || undefined, readchat: this.getNodeParameter('readchat', i), delay: this.getNodeParameter('delay', i), }; break; case 'contact': body = { number: this.getNodeParameter('phone', i), fullName: this.getNodeParameter('fullName', i), phoneNumber: this.getNodeParameter('phoneNumber', i), organization: this.getNodeParameter('organization', i), email: this.getNodeParameter('email', i), url: this.getNodeParameter('url', i), replyid: this.getNodeParameter('replyid', i), mentions: this.getNodeParameter('mentions', i), readchat: this.getNodeParameter('readchat', i), delay: this.getNodeParameter('delay', i), }; break; case 'location': body = { number: this.getNodeParameter('phone', i), name: this.getNodeParameter('name', i), address: this.getNodeParameter('address', i), latitude: this.getNodeParameter('latitude', i), longitude: this.getNodeParameter('longitude', i), replyid: this.getNodeParameter('replyid', i), mentions: this.getNodeParameter('mentions', i), readchat: this.getNodeParameter('readchat', i), delay: this.getNodeParameter('delay', i), }; break; case 'status': body = { type: this.getNodeParameter('type', i), text: this.getNodeParameter('text', i), background_color: this.getNodeParameter('background_color', i), font: this.getNodeParameter('font', i), file: this.getNodeParameter('file', i), thumbnail: this.getNodeParameter('thumbnail', i), mimetype: this.getNodeParameter('mimetype', i), }; break; case 'menu': body = { number: this.getNodeParameter('phone', i), type: this.getNodeParameter('menuType', i), text: this.getNodeParameter('menuText', i), footerText: this.getNodeParameter('footerText', i), listButton: this.getNodeParameter('listButton', i), selectableCount: this.getNodeParameter('selectableCount', i), choices: this.getNodeParameter('choices', i), replyid: this.getNodeParameter('replyid', i), mentions: this.getNodeParameter('mentions', i), readchat: this.getNodeParameter('readchat', i), delay: this.getNodeParameter('delay', i), }; break; case 'carousel': body = { number: this.getNodeParameter('phone', i), text: this.getNodeParameter('carouselText', i), carousel: this.getNodeParameter('carousel', i), delay: this.getNodeParameter('delay', i), readchat: this.getNodeParameter('readchat', i), }; break; } options = { method: 'POST', uri: "".concat(creds.baseUrl).concat(endpoint), headers: { token: creds.apiKey }, body: body, json: true, }; _a.label = 3; case 3: _a.trys.push([3, 5, , 6]); return [4 /*yield*/, this.helpers.request(options)]; case 4: res = _a.sent(); out.push({ json: res }); return [3 /*break*/, 6]; case 5: err_1 = _a.sent(); throw new n8n_workflow_1.NodeApiError(this.getNode(), err_1); case 6: i++; return [3 /*break*/, 2]; case 7: return [2 /*return*/, this.prepareOutputData(out)]; } }); }); }; return JApi; }()); exports.JApi = JApi;