UNPKG

n8n-nodes-zola-fake

Version:

Các node hỗ trợ Zalo cho n8n Thong

868 lines (795 loc) 39.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ZaloSendMessage = void 0; const n8n_workflow_1 = require("n8n-workflow"); const zalo_sdk_1 = require("../zalo-sdk"); const ZaloSendMessageDescription_1 = require("./ZaloSendMessageDescription"); const helper_1 = require("../utils/helper"); const helper_2 = require("../utils/helper"); let api; class ZaloSendMessage { constructor() { this.description = { displayName: 'Zalo Send Message', name: 'zaloSendMessage', icon: 'file:../shared/zalo.svg', group: ['Zalo'], version: 4, 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, displayOptions: { show: { resource: ['zaloSendMessage', 'zaloTTS'], }, }, }, { name: 'zaloTTSApi', required: true, displayOptions: { show: { resource: ['zaloTTS'], }, }, }, ], properties: [ { displayName: 'Resource', name: 'resource', type: 'options', noDataExpression: true, options: [ { name: 'Zalo Send Message', value: 'zaloSendMessage', }, { name: 'Zalo Text To Speech', value: 'zaloTTS', }, ], default: 'zaloSendMessage', }, ...ZaloSendMessageDescription_1.zaloSendMessageOperations, ...ZaloSendMessageDescription_1.zaloSendMessageFields, { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: ['zaloTTS'], }, }, options: [ { name: 'Convert Text to Speech', value: 'convertTTS', action: 'Convert text to speech and return the URL', }, { name: 'Send TTS Message', value: 'sendTTSMessage', action: 'Convert text to speech and send it as a message', }, ], default: 'convertTTS', }, { displayName: 'Thread ID', name: 'threadId', type: 'string', default: '', required: true, description: 'ID of the user or group to send the message to', displayOptions: { show: { resource: ['zaloTTS'], operation: ['sendTTSMessage'], }, }, }, { displayName: 'Type', name: 'type', type: 'options', options: [ { name: 'User', value: 0 }, { name: 'Group', value: 1 }, ], default: 0, description: 'The type of the thread', displayOptions: { show: { resource: ['zaloTTS'], operation: ['sendTTSMessage'], }, }, }, { displayName: 'Text', name: 'text', type: 'string', default: '', required: true, typeOptions: { rows: 4, }, description: 'The text to convert to speech', displayOptions: { show: { resource: ['zaloTTS'], }, }, }, { displayName: 'Voice (Giọng đọc)', name: 'voice', type: 'options', options: [ { name: 'Phụ nữ miền Nam 1', value: '1' }, { name: 'Phụ nữ miền Bắc 1', value: '2' }, { name: 'Đàn ông miền Nam', value: '3' }, { name: 'Đàn ông miền Bắc', value: '4' }, { name: 'Phụ nữ miền Bắc 2', value: '5' }, { name: 'Phụ nữ miền Nam 2', value: '6' }, ], default: '1', description: 'The voice to use for the speech synthesis', displayOptions: { show: { resource: ['zaloTTS'], }, }, }, { displayName: 'EnCode Output', name: 'encode_type', type: 'options', options: [ { name: 'WAV', value: '0' }, { name: 'MP3', value: '1' }, { name: 'AAC', value: '2' }, ], default: '0', description: 'Thể loại mã hóa', displayOptions: { show: { resource: ['zaloTTS'], }, }, }, { displayName: 'Speed', name: 'speed', type: 'number', typeOptions: { minValue: 0.8, maxValue: 1.2, }, default: 1.0, description: 'Playback speed, from 0.8 to 1.2', displayOptions: { show: { resource: ['zaloTTS'], }, }, }, ], }; } 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; const resource = this.getNodeParameter('resource', 0); const operation = this.getNodeParameter('operation', 0); try { const zalo = new zalo_sdk_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 { if(resource === 'zaloSendMessage') { if(operation === 'sendDeliveredEvent') { // Lấy dữ liệu từ input const threadType = this.getNodeParameter('threadType', i); const messagesRaw = this.getNodeParameter('messages', i); const isSeen = this.getNodeParameter('isSeen', i, false); let messages; try { messages = typeof messagesRaw === 'string' ? JSON.parse(messagesRaw) : messagesRaw; } catch (e) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Messages phải là JSON array hợp lệ'); } if (!Array.isArray(messages) || messages.length === 0) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Messages phải là mảng và không được rỗng'); } // Gọi hàm gửi delivered event if (!api || typeof api.sendDeliveredEvent !== 'function') { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'API chưa hỗ trợ gửi delivered event'); } const response = await api.sendDeliveredEvent(threadType, messages, isSeen); returnData.push({ json: { success: true, response, threadType, messages, isSeen }, }); } if(operation === 'sendMessage') { // Lấy các trường cơ bản như cũ const threadId = this.getNodeParameter('threadId', i); const typeNumber = this.getNodeParameter('type', i); const type = typeNumber === 0 ? zalo_sdk_1.ThreadType.User : zalo_sdk_1.ThreadType.Group; const message = this.getNodeParameter('message', i); const urgency = this.getNodeParameter('urgency', i, 0); // Lấy các trường nâng cao từ collection const advancedOptions = this.getNodeParameter('advancedOptions', i, {}); const quote = advancedOptions.quote || {}; let quoteData = {}; if (quote.mode === 'auto' || !quote.mode) { // Tự động lấy từ node trước (giá trị mẫu, bạn có thể điều chỉnh lấy từ items[i] nếu cần) const messageData = (items[i].json && items[i].json.message && items[i].json.message.data) ? items[i].json.message.data : {}; quoteData = { msgId: messageData.msgId, uidFrom: messageData.uidFrom, cliMsgId: messageData.cliMsgId, content: messageData.content, ttl: '0', }; } else { quoteData = { msgId: quote.msgId, uidFrom: quote.uidFrom, cliMsgId: quote.cliMsgId, content: quote.contentquote, ttl: '0', }; } const mentions = advancedOptions.mentions || {}; const attachments = advancedOptions.attachments || {}; const messageContent = { msg: message, }; if (urgency !== 0) { messageContent.urgency = urgency; } if (quote && Object.keys(quote).length > 0) { messageContent.quote = quoteData; } 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) { let fileData; if (attachment.type === 'url') { fileData = await (0, helper_1.saveImage)(attachment.imageUrl); } messageContent.attachments.push(fileData); } } 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 }; this.logger.info("Send! typing event", { threadId, type }); const result = await api.sendTypingEvent(recipentObj.id, { type: 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); if (messageContent.attachments && messageContent.attachments.length > 0) { for (const attachment of messageContent.attachments) { this.logger.info(`Remove attachment: ${attachment}`); (0, helper_2.removeImage)(attachment); } } this.logger.info('Message sent successfully', { threadId, type }); returnData.push({ json: { success: true, response, threadId, threadType: type, messageContent, }, }); } if (operation === 'getStickers') { // Lấy danh sách sticker, có thể truyền queryString nếu có if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } let result; const queryString = this.getNodeParameter('queryString', i, ''); if (queryString && typeof queryString === 'string' && queryString.trim() !== '') { result = await api.getStickers(queryString); } else { result = await api.getStickers(); } returnData.push({ json: result }); } if (operation === 'getStickersDetail') { // Lấy chi tiết sticker const stickerId = this.getNodeParameter('stickerId', i); if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const result = await api.getStickersDetail(stickerId); returnData.push({ json: result }); } if (operation === 'sendSticker') { // Gửi sticker vào thread const threadId = this.getNodeParameter('threadId', i); const stickerId = this.getNodeParameter('stickerId', i); const typeNumber = this.getNodeParameter('type', i); const type = typeNumber === 0 ? zalo_sdk_1.ThreadType.User : zalo_sdk_1.ThreadType.Group; if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } // Lấy chi tiết sticker const stickerDetailResp = await api.getStickersDetail(stickerId); const stickerObj = Array.isArray(stickerDetailResp) ? stickerDetailResp[0] : stickerDetailResp; if (!stickerObj || !stickerObj.id || !stickerObj.cateId || !stickerObj.type) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Sticker detail is invalid or missing required fields'); } // Gửi sticker const response = await api.sendSticker(stickerObj, threadId, type); returnData.push({ json: { success: true, response, threadId, stickerId, stickerObj, }, }); } if (operation === 'sendVoice') { const threadId = this.getNodeParameter('threadId', i); const typeNumber = this.getNodeParameter('type', i); const type = typeNumber === 0 ? zalo_sdk_1.ThreadType.User : zalo_sdk_1.ThreadType.Group; const voiceUrl = this.getNodeParameter('voiceUrl', i); if (!voiceUrl) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Voice URL is required.', { itemIndex: i }); } if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const response = await api.sendVoice({ voiceUrl: voiceUrl }, threadId, type); returnData.push({ json: { success: true, response, threadId, threadType: type, }, }); } if (operation === 'sendCard') { const threadId = this.getNodeParameter('threadId', i); const typeNumber = this.getNodeParameter('type', i); const type = typeNumber === 0 ? zalo_sdk_1.ThreadType.User : zalo_sdk_1.ThreadType.Group; const userId = this.getNodeParameter('userId', i); const phoneNumber = this.getNodeParameter('phoneNumber', i, ''); if (!userId) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'User ID (for card) is required.', { itemIndex: i }); } if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const cardOptions = { userId: userId, }; if (phoneNumber) { cardOptions.phoneNumber = phoneNumber; } const response = await api.sendCard(cardOptions, threadId, type); returnData.push({ json: { success: true, response, threadId, threadType: type, }, }); } if (operation === 'sendVideo') { const threadId = this.getNodeParameter('threadId', i); const typeNumber = this.getNodeParameter('type', i); const type = typeNumber === 0 ? zalo_sdk_1.ThreadType.User : zalo_sdk_1.ThreadType.Group; const videoUrl = this.getNodeParameter('videoUrl', i); const thumbnailUrl = this.getNodeParameter('thumbnailUrl', i); const caption = this.getNodeParameter('caption', i, ''); const videoOptions = this.getNodeParameter('videoOptions', i, {}); if (!videoUrl || !thumbnailUrl) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Video URL and Thumbnail URL are required.', { itemIndex: i }); } if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const sendVideoOptions = { videoUrl: videoUrl, thumbnailUrl: thumbnailUrl, msg: caption, duration: videoOptions.duration, width: videoOptions.width, height: videoOptions.height, }; const response = await api.sendVideo(sendVideoOptions, threadId, type); returnData.push({ json: { success: true, response, threadId, threadType: type, }, }); } // Chức năng pinConversation đã được di chuyển sang ZaloUser để tránh trùng lắp // với chức năng setPinnedConversations if (operation === 'sendMessageStatus') { const threadId = this.getNodeParameter('threadId', i); const typeNumber = this.getNodeParameter('type', i); const type = typeNumber === 0 ? zalo_sdk_1.ThreadType.User : zalo_sdk_1.ThreadType.Group; if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } try { if (type === zalo_sdk_1.ThreadType.User) { const payLoad = { type: zalo_sdk_1.ThreadType.User, destType: zalo_sdk_1.DestType.User }; const result = await api.sendTypingEvent(threadId, payLoad); if (!!result) { this.logger.info("Send! typing event User"); } } else if (type === zalo_sdk_1.ThreadType.Group) { const result = await api.sendTypingEvent(threadId, { type: zalo_sdk_1.ThreadType.Group }); if (!!result) { this.logger.info("Send! typing event Group"); } } returnData.push({ json: { success: true, message: "OK", }, }); } catch (e) { this.logger.error("Cannot send typing event: " + e); } } // Xử lý gửi link if (operation === 'sendLink') { const threadId = this.getNodeParameter('threadId', i); const typeNumber = this.getNodeParameter('type', i); const type = typeNumber === 0 ? zalo_sdk_1.ThreadType.User : zalo_sdk_1.ThreadType.Group; const url = this.getNodeParameter('url', i); const title = this.getNodeParameter('title', i, ''); const desc = this.getNodeParameter('desc', i, ''); const thumb = this.getNodeParameter('thumb', i, ''); if (!url) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'URL is required.', { itemIndex: i }); } if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const linkOptions = { url: url, title: title, desc: desc, thumb: thumb }; const response = await api.sendLink(linkOptions, threadId, type); returnData.push({ json: { success: true, response, threadId, threadType: type, }, }); } // Xử lý chuyển tiếp tin nhắn if (operation === 'forwardMessage') { const sourceThreadId = this.getNodeParameter('sourceThreadId', i); const sourceTypeNumber = this.getNodeParameter('sourceType', i); const sourceType = sourceTypeNumber === 0 ? zalo_sdk_1.ThreadType.User : zalo_sdk_1.ThreadType.Group; const msgId = this.getNodeParameter('msgId', i); const targetThreadId = this.getNodeParameter('targetThreadId', i); const targetTypeNumber = this.getNodeParameter('targetType', i); const targetType = targetTypeNumber === 0 ? zalo_sdk_1.ThreadType.User : zalo_sdk_1.ThreadType.Group; if (!msgId) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Message ID is required.', { itemIndex: i }); } if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const forwardOptions = { sourceThreadId, sourceType, msgId, targetThreadId, targetType }; const response = await api.forwardMessage(forwardOptions); returnData.push({ json: { success: true, response, }, }); } // Xử lý thêm tin nhắn nhanh if (operation === 'addQuickMessage') { const keyword = this.getNodeParameter('keyword', i); const title = this.getNodeParameter('title', i); if (!keyword || !title) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Keyword and title are required.', { itemIndex: i }); } if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const addPayload = { keyword, title }; const response = await api.addQuickMessage(addPayload); returnData.push({ json: { success: true, response, }, }); } // Xử lý lấy danh sách tin nhắn nhanh if (operation === 'getQuickMessage') { if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const response = await api.getQuickMessage(); returnData.push({ json: { success: true, response, }, }); } // Xử lý cập nhật tin nhắn nhanh if (operation === 'updateQuickMessage') { const keyword = this.getNodeParameter('keyword', i); const title = this.getNodeParameter('title', i); if (!keyword || !title) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Keyword and title are required.', { itemIndex: i }); } if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const updatePayload = { keyword, title }; const response = await api.updateQuickMessage(updatePayload); returnData.push({ json: { success: true, response, }, }); } // Xử lý xóa tin nhắn nhanh if (operation === 'removeQuickMessage') { const keyword = this.getNodeParameter('keyword', i); if (!keyword) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Keyword is required.', { itemIndex: i }); } if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const response = await api.removeQuickMessage(keyword); returnData.push({ json: { success: true, response, }, }); } // Xử lý đánh dấu chưa đọc if (operation === 'addUnreadMark') { const threadId = this.getNodeParameter('threadId', i); const typeNumber = this.getNodeParameter('type', i); const type = typeNumber === 0 ? zalo_sdk_1.ThreadType.User : zalo_sdk_1.ThreadType.Group; if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const response = await api.addUnreadMark(threadId, type); returnData.push({ json: { success: true, response, }, }); } // Xử lý bỏ đánh dấu chưa đọc if (operation === 'removeUnreadMark') { const threadId = this.getNodeParameter('threadId', i); const typeNumber = this.getNodeParameter('type', i); const type = typeNumber === 0 ? zalo_sdk_1.ThreadType.User : zalo_sdk_1.ThreadType.Group; if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const response = await api.removeUnreadMark(threadId, type); returnData.push({ json: { success: true, response, }, }); } // Xử lý lấy danh sách đánh dấu chưa đọc if (operation === 'getUnreadMark') { if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const response = await api.getUnreadMark(); returnData.push({ json: { success: true, response, }, }); } } if (resource === 'zaloTTS') { const ttsCreds = await this.getCredentials('zaloTTSApi'); if (!ttsCreds || !ttsCreds.xiApiKey) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo TTS API credentials are not set or invalid. Please create a ZaloTTSApi credential with your API key.'); } const apiKey = ttsCreds.xiApiKey; const text = this.getNodeParameter('text', i); const speaker_id = this.getNodeParameter('voice', i); const encode_type = this.getNodeParameter('encode_type', i); const speed = this.getNodeParameter('speed', i); const ttsOptions = { url: 'https://api.zalo.ai/v1/tts/synthesize', method: 'POST', headers: { 'apikey': apiKey, 'Content-Type': 'application/x-www-form-urlencoded', }, body: `input=${encodeURIComponent(text)}&speaker_id=${speaker_id}&speed=${speed}&encode_type=${encode_type}`, json: true, }; const ttsResponse = await this.helpers.httpRequest(ttsOptions); if (ttsResponse.error || (ttsResponse.error_code && ttsResponse.error_code !== 0)) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Zalo TTS API error: ${ttsResponse.error_message || JSON.stringify(ttsResponse)}`); } if (operation === 'convertTTS') { returnData.push({ json: ttsResponse, }); } else if (operation === 'sendTTSMessage') { const audioUrl = ttsResponse.data.url; if (!audioUrl) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to get audio URL from Zalo TTS API response.'); } const threadId = this.getNodeParameter('threadId', i); const typeNumber = this.getNodeParameter('type', i); const type = typeNumber === 0 ? zalo_sdk_1.ThreadType.User : zalo_sdk_1.ThreadType.Group; if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API client not initialized. Check your Zalo API credentials.'); } const response = await api.sendVoice({ voiceUrl: audioUrl }, threadId, type); returnData.push({ json: { success: true, message: 'TTS voice message sent successfully.', response, threadId, threadType: type, ttsUrl: audioUrl, }, }); } } if (operation === 'addReaction') { const threadId = this.getNodeParameter('threadId', i); const typeNumber = this.getNodeParameter('type', i); const type = typeNumber === 0 ? zalo_sdk_1.ThreadType.User : zalo_sdk_1.ThreadType.Group; const msgId = this.getNodeParameter('msgId', i); const cliMsgId = this.getNodeParameter('cliMsgId', i); const reaction = this.getNodeParameter('reaction', i); if (!api) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized'); } const reactionEnum = zalo_sdk_1.Reactions[reaction]; if (reactionEnum === undefined) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid reaction: ${reaction}`); } const dest = { threadId: threadId, type: type, data: { msgId: msgId, cliMsgId: cliMsgId, }, }; const response = await api.addReaction(reactionEnum, dest); returnData.push({ json: { success: true, response, }, }); } } 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