UNPKG

n8n-nodes-zalo-nnt

Version:

Unofficial Zalo integration for n8n - Send messages, manage groups, user operations with QR login. No API key required, works via browser simulation.

432 lines (431 loc) 20.8 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.ZaloSendMessage = void 0; var n8n_workflow_1 = require("n8n-workflow"); var zca_js_1 = require("zca-js"); var helper_1 = require("../utils/helper"); var api; var ZaloSendMessage = /** @class */ (function () { function ZaloSendMessage() { this.description = { displayName: 'Zalo Send Message', name: 'zaloSendMessage', icon: 'file:../shared/zalo.svg', group: ['Zalo'], version: 5, description: 'Gửi tin nhắn qua API Zalo sử dụng kết nối đăng nhập bằng cookie', defaults: { name: 'Zalo Send Message', }, // @ts-ignore inputs: ['main'], // @ts-ignore outputs: ['main'], credentials: [ { name: 'zaloApi', required: true, }, ], properties: [ { displayName: 'Thread ID', name: 'threadId', type: 'string', default: '', required: true, description: 'ID của thread để gửi tin nhắn', }, { displayName: 'Type', name: 'type', type: 'options', options: [ { name: 'User', value: 0, }, { name: 'Group', value: 1, }, ], default: 0, description: 'Loại của tin nhắn (user hoặc group)', }, { displayName: 'Message', name: 'message', type: 'string', default: '', required: true, description: 'Nội dung tin nhắn cần gửi', }, { displayName: 'Urgency', name: 'urgency', type: 'options', options: [ { name: 'Default', value: 0, }, { name: 'Important', value: 1, }, { name: 'Urgent', value: 2, }, ], default: 0, description: 'Mức độ khẩn cấp của tin nhắn', }, { displayName: 'Quote Message', name: 'quote', type: 'collection', placeholder: 'Add Quote', default: {}, options: [ { displayName: 'Message ID', name: 'msgId', type: 'string', default: '', description: 'ID của tin nhắn cần trích dẫn', }, { displayName: 'Sender ID', name: 'senderId', type: 'string', default: '', description: 'ID của người gửi tin nhắn trích dẫn', }, { displayName: 'Content', name: 'content', type: 'string', default: '', description: 'Nội dung tin nhắn trích dẫn', }, ], }, { displayName: 'Mentions', name: 'mentions', type: 'collection', placeholder: 'Add Mention', default: {}, options: [ { displayName: 'User ID', name: 'uid', type: 'string', default: '', description: 'ID của người dùng được mention', }, { displayName: 'Position', name: 'pos', type: 'number', default: 0, description: 'Vị trí mention trong tin nhắn', }, { displayName: 'Length', name: 'len', type: 'number', default: 0, description: 'Độ dài của mention', }, ], }, { displayName: 'Attachments', name: 'attachments', type: 'fixedCollection', typeOptions: { multipleValues: true, }, placeholder: 'Add Attachment', default: {}, options: [ { name: 'attachment', displayName: 'Attachment', values: [ { displayName: 'Type', name: 'type', type: 'options', options: [ { name: 'Image URL/File URL', value: 'url', }, { name: 'Array of URLs', value: 'urlArray', } ], default: 'url', description: 'Loại file đính kèm', }, { displayName: 'Image URL/File URL', name: 'imageUrl', type: 'string', default: '', displayOptions: { show: { 'type': ['url'], }, }, description: 'URL công khai của ảnh hoặc file', }, { displayName: 'Image URLs Array', name: 'imageUrls', type: 'string', default: '', displayOptions: { show: { 'type': ['urlArray'], }, }, description: 'Array of URLs (JSON format) hoặc comma-separated URLs. VD: ["url1","url2"] hoặc url1,url2', } ], }, ], description: 'Một hoặc nhiều ảnh đính kèm để gửi', }, ], }; } ZaloSendMessage.prototype.execute = function () { return __awaiter(this, void 0, void 0, function () { var returnData, items, zaloCred, cookieFromCred, imeiFromCred, userAgentFromCred, zalo, error_1, i, threadId, typeNumber, type, message, urgency, quote, mentions, attachments, messageContent, _i, _a, attachment, fileData, urls, _b, urls_1, url, fileData, recipentObj, result, e_1, response, _c, _d, attachment, error_2; return __generator(this, function (_e) { switch (_e.label) { case 0: returnData = []; items = this.getInputData(); return [4 /*yield*/, this.getCredentials('zaloApi')]; case 1: zaloCred = _e.sent(); cookieFromCred = JSON.parse(zaloCred.cookie); imeiFromCred = zaloCred.imei; userAgentFromCred = zaloCred.userAgent; _e.label = 2; case 2: _e.trys.push([2, 4, , 5]); zalo = new zca_js_1.Zalo(); return [4 /*yield*/, zalo.login({ cookie: cookieFromCred, imei: imeiFromCred, userAgent: userAgentFromCred })]; case 3: api = _e.sent(); if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to initialize Zalo API. Check your credentials.'); } return [3 /*break*/, 5]; case 4: error_1 = _e.sent(); throw new n8n_workflow_1.NodeOperationError(this.getNode(), "Zalo login error: ".concat(error_1.message)); case 5: i = 0; _e.label = 6; case 6: if (!(i < items.length)) return [3 /*break*/, 23]; _e.label = 7; case 7: _e.trys.push([7, 21, , 22]); threadId = this.getNodeParameter('threadId', i); typeNumber = this.getNodeParameter('type', i); type = typeNumber === 0 ? zca_js_1.ThreadType.User : zca_js_1.ThreadType.Group; message = this.getNodeParameter('message', i); urgency = this.getNodeParameter('urgency', i, 0); quote = this.getNodeParameter('quote', i, {}); mentions = this.getNodeParameter('mentions', i, {}); attachments = this.getNodeParameter('attachments', i, {}); messageContent = { msg: message, }; // Add urgency if specified if (urgency !== 0) { messageContent.urgency = urgency; } // Add quote if specified if (quote && Object.keys(quote).length > 0) { messageContent.quote = { msgId: quote.msgId, senderId: quote.senderId, content: quote.content, }; } // Add mentions if specified if (mentions && Object.keys(mentions).length > 0) { messageContent.mentions = [{ pos: mentions.pos || 0, uid: mentions.uid, len: mentions.len || 0, }]; } if (!(attachments && attachments.attachment && attachments.attachment.length > 0)) return [3 /*break*/, 15]; messageContent.attachments = []; _i = 0, _a = attachments.attachment; _e.label = 8; case 8: if (!(_i < _a.length)) return [3 /*break*/, 15]; attachment = _a[_i]; if (!(attachment.type === 'url')) return [3 /*break*/, 10]; return [4 /*yield*/, (0, helper_1.saveFile)(attachment.imageUrl)]; case 9: fileData = _e.sent(); messageContent.attachments.push(fileData); return [3 /*break*/, 14]; case 10: if (!(attachment.type === 'urlArray')) return [3 /*break*/, 14]; urls = []; // Parse URLs from string input if (typeof attachment.imageUrls === 'string') { try { // Try to parse as JSON array first urls = JSON.parse(attachment.imageUrls); } catch (_f) { // If not JSON, try comma-separated urls = attachment.imageUrls.split(',').map(function (url) { return url.trim(); }).filter(function (url) { return url; }); } } _b = 0, urls_1 = urls; _e.label = 11; case 11: if (!(_b < urls_1.length)) return [3 /*break*/, 14]; url = urls_1[_b]; if (!url) return [3 /*break*/, 13]; return [4 /*yield*/, (0, helper_1.saveFile)(url)]; case 12: fileData = _e.sent(); messageContent.attachments.push(fileData); _e.label = 13; case 13: _b++; return [3 /*break*/, 11]; case 14: _i++; return [3 /*break*/, 8]; case 15: // Log the parameters before sending this.logger.info("Sending message with parameters: ".concat(JSON.stringify(messageContent))); // Send the message if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } _e.label = 16; case 16: _e.trys.push([16, 18, , 19]); recipentObj = { id: threadId, type: type }; return [4 /*yield*/, api.sendTypingEvent(recipentObj.id, recipentObj.type)]; case 17: result = _e.sent(); if (!!result) { this.logger.info("Send! typing event"); } return [3 /*break*/, 19]; case 18: e_1 = _e.sent(); this.logger.error("Cannot send typing event"); return [3 /*break*/, 19]; case 19: return [4 /*yield*/, api.sendMessage(messageContent, threadId, type)]; case 20: response = _e.sent(); //Remove temp img if (messageContent.attachments && messageContent.attachments.length > 0) { for (_c = 0, _d = messageContent.attachments; _c < _d.length; _c++) { attachment = _d[_c]; this.logger.info("Remove attachment: ".concat(attachment)); (0, helper_1.removeFile)(attachment); } } this.logger.info('Message sent successfully', { threadId: threadId, type: type }); returnData.push({ json: { success: true, response: response, threadId: threadId, threadType: type, messageContent: messageContent, }, }); return [3 /*break*/, 22]; case 21: error_2 = _e.sent(); this.logger.error('Error sending Zalo message:', error_2); if (this.continueOnFail()) { returnData.push({ json: { success: false, error: error_2.message, }, }); } else { throw new n8n_workflow_1.NodeOperationError(this.getNode(), error_2, { itemIndex: i }); } return [3 /*break*/, 22]; case 22: i++; return [3 /*break*/, 6]; case 23: return [2 /*return*/, [returnData]]; } }); }); }; return ZaloSendMessage; }()); exports.ZaloSendMessage = ZaloSendMessage;