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.

426 lines 20 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ZaloSendMessage = void 0; const n8n_workflow_1 = require("n8n-workflow"); const zca_js_1 = require("zca-js"); const helper_1 = require("../utils/helper"); const path = __importStar(require("path")); let api; class ZaloSendMessage { constructor() { 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', }, inputs: ['main'], 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', }, ], }; } async execute() { const returnData = []; const items = this.getInputData(); const zaloCred = await this.getCredentials('zaloApi'); const cookieFromCred = JSON.parse(zaloCred.cookie); const imeiFromCred = zaloCred.imei; const userAgentFromCred = zaloCred.userAgent; try { const zalo = new zca_js_1.Zalo(); api = await zalo.login({ cookie: cookieFromCred, imei: imeiFromCred, userAgent: userAgentFromCred }); if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to initialize Zalo API. Check your credentials.'); } } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Zalo login error: ${error.message}`); } for (let i = 0; i < items.length; i++) { try { const threadId = this.getNodeParameter('threadId', i); const typeNumber = this.getNodeParameter('type', i); const type = typeNumber === 0 ? zca_js_1.ThreadType.User : zca_js_1.ThreadType.Group; const message = this.getNodeParameter('message', i); const urgency = this.getNodeParameter('urgency', i, 0); const quote = this.getNodeParameter('quote', i, {}); const mentions = this.getNodeParameter('mentions', i, {}); const attachments = this.getNodeParameter('attachments', i, {}); const messageContent = { msg: message, }; if (urgency !== 0) { messageContent.urgency = urgency; } if (quote && Object.keys(quote).length > 0) { messageContent.quote = { msgId: quote.msgId, senderId: quote.senderId, content: quote.content, }; } 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) { messageContent.attachments = []; for (const attachment of attachments.attachment) { if (attachment.type === 'url') { let urls = []; if (typeof attachment.imageUrl === 'string') { if (attachment.imageUrl.includes(',')) { urls = attachment.imageUrl.split(',').map((url) => url.trim()).filter((url) => url); } else { urls = [attachment.imageUrl.trim()]; } } for (const url of urls) { if (url) { this.logger.info(`Processing URL: ${url}`); console.log(`[ZaloSendMessage] Processing URL: ${url}`); const fileData = await (0, helper_1.saveFile)(url); if (fileData) { const ext = path.extname(fileData).toLowerCase(); const supportedImageExts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']; console.log(`[ZaloSendMessage] Downloaded file: ${fileData}, extension: ${ext}`); if (supportedImageExts.includes(ext)) { messageContent.attachments.push(fileData); this.logger.info(`Successfully downloaded image: ${fileData}`); console.log(`[ZaloSendMessage] Added supported image to attachments: ${fileData}`); } else { this.logger.warn(`File type ${ext} may not be supported by Zalo: ${fileData}`); console.log(`[ZaloSendMessage] Warning: Unsupported file type ${ext}, but will try to send: ${fileData}`); messageContent.attachments.push(fileData); } } else { this.logger.error(`Failed to download file from URL: ${url}`); console.error(`[ZaloSendMessage] Failed to download file from URL: ${url}`); } } } } else if (attachment.type === 'urlArray') { let urls = []; if (typeof attachment.imageUrls === 'string') { try { urls = JSON.parse(attachment.imageUrls); } catch { urls = attachment.imageUrls.split(',').map((url) => url.trim()).filter((url) => url); } } for (const url of urls) { if (url) { this.logger.info(`Processing URL: ${url}`); console.log(`[ZaloSendMessage] Processing URL: ${url}`); const fileData = await (0, helper_1.saveFile)(url); if (fileData) { const ext = path.extname(fileData).toLowerCase(); const supportedImageExts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']; console.log(`[ZaloSendMessage] Downloaded file: ${fileData}, extension: ${ext}`); if (supportedImageExts.includes(ext)) { messageContent.attachments.push(fileData); this.logger.info(`Successfully downloaded image: ${fileData}`); console.log(`[ZaloSendMessage] Added supported image to attachments: ${fileData}`); } else { this.logger.warn(`File type ${ext} may not be supported by Zalo: ${fileData}`); console.log(`[ZaloSendMessage] Warning: Unsupported file type ${ext}, but will try to send: ${fileData}`); messageContent.attachments.push(fileData); } } else { this.logger.error(`Failed to download file from URL: ${url}`); console.error(`[ZaloSendMessage] Failed to download file from URL: ${url}`); } } } } } } this.logger.info(`Sending message with parameters: ${JSON.stringify(messageContent)}`); if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } try { const recipentObj = { id: threadId, type: type }; const result = await api.sendTypingEvent(recipentObj.id, recipentObj.type); if (!!result) { this.logger.info("Send! typing event"); } } catch (e) { this.logger.error("Cannot send typing event"); } const response = await api.sendMessage(messageContent, threadId, type); this.logger.info(`Zalo API response: ${JSON.stringify(response)}`); if (messageContent.attachments && messageContent.attachments.length > 0) { for (const attachment of messageContent.attachments) { this.logger.info(`Remove attachment: ${attachment}`); (0, helper_1.removeFile)(attachment); } } this.logger.info('Message sent successfully', { threadId, type }); const responseData = { success: true, threadId, threadType: type, messageContent, response: response || {} }; returnData.push({ json: responseData, }); } catch (error) { this.logger.error('Error sending Zalo message:', error); if (this.continueOnFail()) { returnData.push({ json: { success: false, error: error.message, }, }); } else { throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i }); } } } return [returnData]; } } exports.ZaloSendMessage = ZaloSendMessage; //# sourceMappingURL=ZaloSendMessage.node.js.map