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.
305 lines • 14.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ZaloMessageTrigger = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const branding_1 = require("../shared/branding");
const constants_1 = require("./constants");
const EventFilter_1 = require("./EventFilter");
const EventListenerRegistry_1 = require("./EventListenerRegistry");
const ZaloConnectionManager_1 = require("./ZaloConnectionManager");
const CatchUpManager_1 = require("./CatchUpManager");
let api;
let reconnectTimer;
function getNodeConfig(functions) {
const selfListen = functions.getNodeParameter('selfListen', 0);
const eventTypes = functions.getNodeParameter('eventTypes', 0);
const messageContentTypes = functions.getNodeParameter('messageContentTypes', 0);
const threadTypes = functions.getNodeParameter('threadTypes', 0);
const specificIds = functions.getNodeParameter('specificIds', 0);
const catchUpOnReconnect = functions.getNodeParameter('catchUpOnReconnect', true);
const idList = specificIds
.split(',')
.map(id => id.trim())
.filter(id => id);
return {
selfListen,
eventTypes,
messageContentTypes,
threadTypes,
specificIds,
idList,
catchUpOnReconnect,
};
}
class ZaloMessageTrigger {
constructor() {
this.description = {
displayName: 'Zalo Event Trigger by diginno.net Trigger',
name: 'zaloMessageTrigger',
icon: 'file:../shared/image.svg',
group: ['trigger'],
version: 2,
description: 'Lắng nghe các sự kiện từ Zalo (tin nhắn, reaction, typing, group events, friend events, etc) by diginno.net',
defaults: {
name: 'Zalo Event Trigger by diginno.net',
},
inputs: [],
outputs: ['main'],
webhooks: [
{
name: 'default',
httpMethod: 'POST',
responseMode: 'onReceived',
path: 'webhook',
},
],
credentials: [
{
name: 'zaloApi',
required: true,
displayName: 'Zalo Credential to connect with',
},
],
properties: [
branding_1.BRANDING_CONFIG,
{
displayName: 'Loại Sự Kiện',
name: 'eventTypes',
type: 'multiOptions',
options: [
{
name: 'Tin Nhắn Mới',
value: constants_1.EVENT_TYPES.MESSAGE,
description: 'Lắng nghe tin nhắn mới từ user và group',
},
{
name: 'Reaction (Thả Cảm Xúc)',
value: constants_1.EVENT_TYPES.REACTION,
description: 'Khi có người thả reaction vào tin nhắn',
},
{
name: 'ĐAng Soạn Tin',
value: constants_1.EVENT_TYPES.TYPING,
description: 'Khi có người đang gõ tin nhắn',
},
{
name: 'ĐÃ Xem Tin Nhắn',
value: constants_1.EVENT_TYPES.SEEN_MESSAGES,
description: 'Khi tin nhắn được xem',
},
{
name: 'ĐÃ Nhận Tin Nhắn',
value: constants_1.EVENT_TYPES.DELIVERED_MESSAGES,
description: 'Khi tin nhắn được gửi thành công',
},
{
name: 'Thu Hồi Tin Nhắn',
value: constants_1.EVENT_TYPES.UNDO,
description: 'Khi có tin nhắn bị thu hồi',
},
{
name: 'Sự Kiện Nhóm',
value: constants_1.EVENT_TYPES.GROUP_EVENT,
description: 'Join/Leave/Kick/Admin changes trong nhóm',
},
{
name: 'Sự Kiện Bạn Bè',
value: constants_1.EVENT_TYPES.FRIEND_EVENT,
description: 'Friend request, accept, reject, etc',
},
{
name: 'Trạng Thái Kết Nối',
value: constants_1.EVENT_TYPES.CONNECTION,
description: 'Connected, disconnected, error events',
},
],
default: [constants_1.EVENT_TYPES.MESSAGE],
required: true,
description: 'Chọn các loại sự kiện cần lắng nghe',
hint: 'Có thể chọn nhiều loại sự kiện cùng lúc. Mỗi event sẽ trigger workflow riêng biệt.',
},
{
displayName: 'Lọc Theo Loại Nội Dung',
name: 'messageContentTypes',
type: 'multiOptions',
displayOptions: {
show: {
eventTypes: [constants_1.EVENT_TYPES.MESSAGE],
},
},
options: [
{
name: 'File',
value: 'file',
description: 'Tin nhắn file đính kèm',
},
{
name: 'GIF',
value: 'gif',
description: 'Tin nhắn ảnh động GIF',
},
{
name: 'Image',
value: 'image',
description: 'Tin nhắn hình ảnh',
},
{
name: 'Link',
value: 'link',
description: 'Tin nhắn có link hoặc thẻ xem trước',
},
{
name: 'Location',
value: 'location',
description: 'Tin nhắn chia sẻ vị trí',
},
{
name: 'Sticker',
value: 'sticker',
description: 'Tin nhắn sticker',
},
{
name: 'Text',
value: 'text',
description: 'Tin nhắn văn bản',
},
{
name: 'Video',
value: 'video',
description: 'Tin nhắn video',
},
{
name: 'Voice',
value: 'voice',
description: 'Tin nhắn voice/audio',
},
],
default: [],
description: 'Chỉ trigger khi nhận loại nội dung này (bỏ trống = tất cả)',
hint: 'Bộ lọc này chỉ áp dụng cho tin nhắn. Bỏ trống để nhận tất cả loại tin nhắn.',
},
{
displayName: 'Lọc Theo Nguồn',
name: 'threadTypes',
type: 'multiOptions',
displayOptions: {
show: {
eventTypes: [constants_1.EVENT_TYPES.MESSAGE, constants_1.EVENT_TYPES.REACTION, constants_1.EVENT_TYPES.TYPING],
},
},
options: [
{
name: 'Tin Nhắn Cá Nhân',
value: 'user',
description: 'Chỉ từ chat 1-1',
},
{
name: 'Tin Nhắn Nhóm',
value: 'group',
description: 'Chỉ từ nhóm chat',
},
],
default: ['user', 'group'],
description: 'Lọc theo nguồn tin nhắn',
hint: 'Chọn cả User và Group để nhận tất cả. Bỏ chọn một trong hai để lọc nguồn cụ thể.',
},
{
displayName: 'Lọc Theo ID Cụ Thể',
name: 'specificIds',
type: 'string',
displayOptions: {
show: {
eventTypes: [constants_1.EVENT_TYPES.MESSAGE, constants_1.EVENT_TYPES.REACTION, constants_1.EVENT_TYPES.TYPING],
},
},
default: '',
placeholder: '1234567890,0987654321',
description: 'Chỉ trigger từ các User ID hoặc Group ID này (phân cách bằng dấu phẩy, bỏ trống = tất cả)',
hint: 'Nhập danh sách User ID hoặc Group ID cách nhau bằng dấu phẩy. Bỏ trống để nhận từ tất cả.',
},
{
displayName: 'Self Listen',
name: 'selfListen',
type: 'boolean',
default: false,
required: true,
description: 'Cho phép lắng nghe tin nhắn của chính mình tự gửi',
hint: 'Bật tùy chọn này nếu muốn workflow chạy khi bạn tự gửi tin nhắn.',
},
{
displayName: 'Kéo Bù Sau Khi Mất Kết Nối',
name: 'catchUpOnReconnect',
type: 'boolean',
displayOptions: {
show: {
eventTypes: [constants_1.EVENT_TYPES.MESSAGE],
},
},
default: true,
description: 'Whether to tự lấy lại tin nhắn đến trong lúc mất kết nối. Kết nối Zalo rớt rất thường xuyên; không bật thì mỗi lần rớt là thủng một mảng tin nhắn mà không ai biết.',
hint: 'Tin kéo bù có cờ "isCatchUp": true và có thể trùng với tin đã nhận — hãy upsert theo msgId ở bước lưu dữ liệu.',
},
],
};
this.webhookMethods = {
default: {
async checkExists() {
const webhookData = this.getWorkflowStaticData('node');
return !!webhookData.isConnected;
},
async create() {
const credentials = await this.getCredentials('zaloApi');
if (!credentials) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No credentials found');
}
try {
const parsedCredentials = ZaloConnectionManager_1.ZaloConnectionManager.parseCredentials(credentials);
const config = getNodeConfig(this);
const filter = new EventFilter_1.EventFilter(config.threadTypes, config.idList, config.messageContentTypes);
api = await ZaloConnectionManager_1.ZaloConnectionManager.login(this, parsedCredentials.cookie, parsedCredentials.imei, parsedCredentials.userAgent, config.selfListen);
const webhookData = this.getWorkflowStaticData('node');
webhookData.eventQueue = [];
webhookData.connectionStatus = 'connecting';
EventListenerRegistry_1.EventListenerRegistry.registerAll(this, api, webhookData, config.eventTypes, filter);
webhookData.connectCount = 0;
if (config.catchUpOnReconnect && config.eventTypes.includes(constants_1.EVENT_TYPES.MESSAGE)) {
(0, CatchUpManager_1.registerCatchUp)(this, api, webhookData, filter, config.threadTypes);
}
api.listener.start({ retryOnClose: true });
webhookData.isConnected = true;
webhookData.eventTypes = config.eventTypes;
return true;
}
catch (error) {
ZaloConnectionManager_1.ZaloConnectionManager.handleError(this, error, api);
}
},
async delete() {
const webhookData = this.getWorkflowStaticData('node');
if (api) {
api.listener.stop();
api = undefined;
}
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = undefined;
}
delete webhookData.isConnected;
delete webhookData.eventTypes;
delete webhookData.eventQueue;
delete webhookData.connectCount;
delete webhookData.lastCatchUpTime;
return true;
},
},
};
}
async webhook() {
const bodyData = this.getBodyData();
return {
workflowData: [this.helpers.returnJsonArray([bodyData])],
};
}
}
exports.ZaloMessageTrigger = ZaloMessageTrigger;
//# sourceMappingURL=ZaloMessageTrigger.node.js.map