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
JavaScript
;
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