n8n-nodes-zalo-custom 1.0.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -49
- package/nodes/ZaloCommunication/ZaloCommunication.node.js +6 -6
- package/nodes/ZaloGroup/ZaloGroup.node.js +136 -140
- package/nodes/ZaloGroup/ZaloGroupDescription.js +79 -70
- package/nodes/ZaloLoginByQr/ZaloLoginByQr.node.js +37 -52
- package/nodes/ZaloSendMessage/ZaloSendMessage.node.js +3 -539
- package/nodes/ZaloSendMessage/ZaloSendMessageDescription.js +389 -0
- package/nodes/ZaloTrigger/ZaloTrigger.node.js +33 -23
- package/nodes/ZaloUploadAttachment/ZaloUploadAttachment.node.js +24 -26
- package/nodes/ZaloUploadAttachment/ZaloUploadAttachmentDescription.js +1 -1
- package/nodes/ZaloUser/ZaloUser.node.js +103 -4
- package/nodes/ZaloUser/ZaloUserDescription.js +297 -47
- package/nodes/utils/helper.js +148 -0
- package/nodes/utils/zalo.helper.js +41 -13
- package/package.json +3 -1
|
@@ -37,6 +37,12 @@ exports.zaloGroupOperations = [
|
|
|
37
37
|
description: 'Tạo một nhóm mới',
|
|
38
38
|
action: 'Tạo Nhóm',
|
|
39
39
|
},
|
|
40
|
+
{
|
|
41
|
+
name: 'Cập Nhật Cài Đặt Nhóm',
|
|
42
|
+
value: 'updateGroupSettings',
|
|
43
|
+
description: 'Cập nhật các cài đặt quyền hạn trong nhóm',
|
|
44
|
+
action: 'Cập Nhật Cài Đặt Nhóm',
|
|
45
|
+
},
|
|
40
46
|
{
|
|
41
47
|
name: 'Chuyển Quyền Chủ Nhóm',
|
|
42
48
|
value: 'changeGroupOwner',
|
|
@@ -109,12 +115,6 @@ exports.zaloGroupOperations = [
|
|
|
109
115
|
description: 'Giải tán một nhóm. Chỉ chủ nhóm mới có thể thực hiện.',
|
|
110
116
|
action: 'Giải Tán Nhóm',
|
|
111
117
|
},
|
|
112
|
-
{
|
|
113
|
-
name: 'Xóa Tin Nhắn Của Thành Viên',
|
|
114
|
-
value: 'deleteMessage',
|
|
115
|
-
description: 'Xóa tin nhắn của thành viên trong nhóm (yêu cầu quyền admin)',
|
|
116
|
-
action: 'Xóa Tin Nhắn Của Thành Viên',
|
|
117
|
-
},
|
|
118
118
|
],
|
|
119
119
|
default: 'createGroup',
|
|
120
120
|
},
|
|
@@ -156,11 +156,11 @@ exports.zaloGroupFields = [
|
|
|
156
156
|
'changeGroupOwner',
|
|
157
157
|
'leaveGroup',
|
|
158
158
|
'disperseGroup',
|
|
159
|
-
'
|
|
159
|
+
'updateGroupSettings',
|
|
160
160
|
],
|
|
161
161
|
},
|
|
162
162
|
},
|
|
163
|
-
description: 'ID nhóm',
|
|
163
|
+
description: 'ID nhóm / threadID',
|
|
164
164
|
},
|
|
165
165
|
{
|
|
166
166
|
displayName: 'User ID Người Dùng',
|
|
@@ -171,7 +171,7 @@ exports.zaloGroupFields = [
|
|
|
171
171
|
displayOptions: {
|
|
172
172
|
show: {
|
|
173
173
|
resource: ['zaloGroup'],
|
|
174
|
-
operation: ['addGroupDeputy', 'changeGroupOwner'
|
|
174
|
+
operation: ['addGroupDeputy', 'changeGroupOwner'],
|
|
175
175
|
},
|
|
176
176
|
},
|
|
177
177
|
description: 'ID của người dùng',
|
|
@@ -309,78 +309,87 @@ exports.zaloGroupFields = [
|
|
|
309
309
|
displayOptions: {
|
|
310
310
|
show: {
|
|
311
311
|
resource: ['zaloGroup'],
|
|
312
|
-
operation: ['joinGroupLink'],
|
|
312
|
+
operation: ['joinGroupLink', 'getGroupLinkInfo'],
|
|
313
313
|
},
|
|
314
314
|
},
|
|
315
|
-
description: 'Link
|
|
315
|
+
description: 'Link tham gia nhóm (ví dụ: https://zalo.me/g/xxxxxx)',
|
|
316
316
|
},
|
|
317
317
|
{
|
|
318
|
-
displayName: '
|
|
319
|
-
name: '
|
|
320
|
-
type: '
|
|
321
|
-
|
|
322
|
-
default:
|
|
318
|
+
displayName: 'Cài Đặt',
|
|
319
|
+
name: 'settings',
|
|
320
|
+
type: 'collection',
|
|
321
|
+
placeholder: 'Thêm Cài Đặt',
|
|
322
|
+
default: {},
|
|
323
323
|
displayOptions: {
|
|
324
324
|
show: {
|
|
325
325
|
resource: ['zaloGroup'],
|
|
326
|
-
operation: ['
|
|
326
|
+
operation: ['updateGroupSettings'],
|
|
327
327
|
},
|
|
328
328
|
},
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
default: '',
|
|
337
|
-
displayOptions: {
|
|
338
|
-
show: {
|
|
339
|
-
resource: ['zaloGroup'],
|
|
340
|
-
operation: ['deleteMessage'],
|
|
329
|
+
options: [
|
|
330
|
+
{
|
|
331
|
+
displayName: 'Chặn Đổi Tên/Avatar',
|
|
332
|
+
name: 'blockName',
|
|
333
|
+
type: 'boolean',
|
|
334
|
+
default: false,
|
|
335
|
+
description: 'Không cho phép thành viên thay đổi tên và ảnh đại diện nhóm',
|
|
341
336
|
},
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
type: 'boolean',
|
|
349
|
-
default: true,
|
|
350
|
-
displayOptions: {
|
|
351
|
-
show: {
|
|
352
|
-
resource: ['zaloGroup'],
|
|
353
|
-
operation: ['deleteMessage'],
|
|
337
|
+
{
|
|
338
|
+
displayName: 'Đánh Dấu Tin Của Trưởng/Phó Nhóm',
|
|
339
|
+
name: 'signAdminMsg',
|
|
340
|
+
type: 'boolean',
|
|
341
|
+
default: false,
|
|
342
|
+
description: 'Đánh dấu tin nhắn từ trưởng/phó nhóm',
|
|
354
343
|
},
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
// type: 'number',
|
|
362
|
-
// default: 50,
|
|
363
|
-
// required: true,
|
|
364
|
-
// displayOptions: {
|
|
365
|
-
// show: {
|
|
366
|
-
// resource: ['zaloGroup'],
|
|
367
|
-
// operation: [],
|
|
368
|
-
// },
|
|
369
|
-
// },
|
|
370
|
-
// description: 'Số lượng tối đa cần lấy',
|
|
371
|
-
// },
|
|
372
|
-
{
|
|
373
|
-
displayName: 'Link Mời Nhóm',
|
|
374
|
-
name: 'groupLink',
|
|
375
|
-
type: 'string',
|
|
376
|
-
required: true,
|
|
377
|
-
default: '',
|
|
378
|
-
displayOptions: {
|
|
379
|
-
show: {
|
|
380
|
-
resource: ['zaloGroup'],
|
|
381
|
-
operation: ['getGroupLinkInfo'],
|
|
344
|
+
{
|
|
345
|
+
displayName: 'Chặn Ghim Tin Nhắn',
|
|
346
|
+
name: 'setTopicOnly',
|
|
347
|
+
type: 'boolean',
|
|
348
|
+
default: false,
|
|
349
|
+
description: 'Không cho phép thành viên ghim tin nhắn, ghi chú, bình chọn',
|
|
382
350
|
},
|
|
383
|
-
|
|
384
|
-
|
|
351
|
+
{
|
|
352
|
+
displayName: 'Xem Tin Nhắn Cũ',
|
|
353
|
+
name: 'enableMsgHistory',
|
|
354
|
+
type: 'boolean',
|
|
355
|
+
default: false,
|
|
356
|
+
description: 'Cho phép thành viên mới đọc tin nhắn gần nhất',
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
displayName: 'Duyệt Thành Viên',
|
|
360
|
+
name: 'joinAppr',
|
|
361
|
+
type: 'boolean',
|
|
362
|
+
default: false,
|
|
363
|
+
description: 'Bật chế độ phê duyệt thành viên mới',
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
displayName: 'Chặn Tạo Ghi Chú',
|
|
367
|
+
name: 'lockCreatePost',
|
|
368
|
+
type: 'boolean',
|
|
369
|
+
default: false,
|
|
370
|
+
description: 'Không cho phép thành viên tạo mới ghi chú, nhắc hẹn',
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
displayName: 'Chặn Tạo Bình Chọn',
|
|
374
|
+
name: 'lockCreatePoll',
|
|
375
|
+
type: 'boolean',
|
|
376
|
+
default: false,
|
|
377
|
+
description: 'Không cho phép thành viên tạo mới bình chọn',
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
displayName: 'Chặn Gửi Tin Nhắn',
|
|
381
|
+
name: 'lockSendMsg',
|
|
382
|
+
type: 'boolean',
|
|
383
|
+
default: false,
|
|
384
|
+
description: 'Không cho phép thành viên gửi tin nhắn',
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
displayName: 'Chặn Xem Thành Viên',
|
|
388
|
+
name: 'lockViewMember',
|
|
389
|
+
type: 'boolean',
|
|
390
|
+
default: false,
|
|
391
|
+
description: 'Không cho phép thành viên xem đầy đủ danh sách thành viên',
|
|
392
|
+
},
|
|
393
|
+
],
|
|
385
394
|
},
|
|
386
395
|
];
|
|
@@ -43,6 +43,8 @@ const path = __importStar(require("path"));
|
|
|
43
43
|
const zalo_helper_1 = require("../utils/zalo.helper");
|
|
44
44
|
const crypto_helper_1 = require("../utils/crypto.helper");
|
|
45
45
|
const axios_1 = __importDefault(require("axios"));
|
|
46
|
+
const https_proxy_agent_1 = require("https-proxy-agent");
|
|
47
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
46
48
|
|
|
47
49
|
class ZaloLoginByQr {
|
|
48
50
|
constructor() {
|
|
@@ -73,7 +75,7 @@ class ZaloLoginByQr {
|
|
|
73
75
|
type: 'string',
|
|
74
76
|
default: '',
|
|
75
77
|
placeholder: 'https://user:pass@host:port',
|
|
76
|
-
description: 'HTTP proxy to use for Zalo API requests',
|
|
78
|
+
description: 'HTTP proxy to use for Zalo API requests (proxy can only be saved once for a custom credential)',
|
|
77
79
|
},
|
|
78
80
|
// {
|
|
79
81
|
// displayName: 'Delete Zalo Credential Duplicate UserId',
|
|
@@ -116,12 +118,28 @@ class ZaloLoginByQr {
|
|
|
116
118
|
}
|
|
117
119
|
async execute() {
|
|
118
120
|
const returnData = [];
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
let proxy = this.getNodeParameter('proxy', 0, '');
|
|
122
|
+
if (proxy) {
|
|
123
|
+
if (!proxy.toLowerCase().startsWith('http')) {
|
|
124
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Proxy không hợp lệ: "${proxy}" (http(s)://user:pass@host:port)`);
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const response = await axios_1.default.get('https://api.ipify.org', {
|
|
128
|
+
httpsAgent: new https_proxy_agent_1.HttpsProxyAgent(proxy),
|
|
129
|
+
timeout: 5000,
|
|
130
|
+
});
|
|
131
|
+
this.logger.info(`Use Proxy IP Public: ${response.data}`);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
this.logger.error(`Kiểm tra proxy thất bại: ${error.message}`);
|
|
134
|
+
let errorMessage = error.message;
|
|
135
|
+
if (error.response) {
|
|
136
|
+
errorMessage += ` - ${JSON.stringify(error.response.data)}`;
|
|
137
|
+
}
|
|
138
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Proxy không hợp lệ hoặc không thể kết nối. Lỗi: ${errorMessage} (http(s)://user:pass@host:port)`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const timeout = 45; // telegram
|
|
125
143
|
const fileName = 'zalo-qr-code.png';
|
|
126
144
|
const deleteOldZaloApi = this.getNodeParameter('deleteOldZaloApi', 0, false); // bỏ vì xoá xong gây ra lỗi k lưu được
|
|
127
145
|
const sendToTelegram = this.getNodeParameter('sendToTelegram', 0, false);
|
|
@@ -133,41 +151,18 @@ class ZaloLoginByQr {
|
|
|
133
151
|
chatId: telegramChatId,
|
|
134
152
|
logger: this.logger,
|
|
135
153
|
};
|
|
136
|
-
let
|
|
137
|
-
try {
|
|
138
|
-
n8nCredential = await this.getCredentials('n8nZaloApi');
|
|
139
|
-
}
|
|
140
|
-
catch (error) {
|
|
141
|
-
}
|
|
142
|
-
const selectedCredential = n8nCredential;
|
|
143
|
-
if (selectedCredential) {
|
|
144
|
-
this.logger.info('Using n8n account credential');
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
this.logger.info('No credentials provided, will generate QR code for login');
|
|
148
|
-
}
|
|
154
|
+
let selectedCredential = await this.getCredentials('n8nZaloApi');
|
|
149
155
|
try {
|
|
150
156
|
const zaloOptions = {
|
|
151
157
|
selfListen: true,
|
|
152
|
-
logging:
|
|
158
|
+
logging: false,
|
|
153
159
|
};
|
|
154
160
|
if (proxy) {
|
|
155
|
-
zaloOptions.
|
|
156
|
-
|
|
157
|
-
let zalo;
|
|
158
|
-
if (selectedCredential) {
|
|
159
|
-
this.logger.info('Using existing Zalo credentials');
|
|
160
|
-
zalo = new zca_js_1.Zalo(zaloOptions);
|
|
161
|
-
this.logger.info('Using n8n credential to get Zalo credentials');
|
|
162
|
-
const n8nApiKey = selectedCredential.apiKey;
|
|
163
|
-
const n8nUrl = selectedCredential.url || 'http://localhost:5678';
|
|
164
|
-
this.logger.info(`Using n8n API at ${n8nUrl} with API key ${n8nApiKey ? 'provided' : 'not provided'}`);
|
|
165
|
-
this.logger.info('n8n credential support is not fully implemented yet. Will use QR code login.');
|
|
166
|
-
zalo = new zca_js_1.Zalo(zaloOptions);
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
zalo = new zca_js_1.Zalo(zaloOptions);
|
|
161
|
+
zaloOptions.agent = new https_proxy_agent_1.HttpsProxyAgent(proxy);
|
|
162
|
+
zaloOptions.polyfill = node_fetch_1.default;
|
|
170
163
|
}
|
|
164
|
+
let zalo = new zca_js_1.Zalo(zaloOptions);
|
|
165
|
+
|
|
171
166
|
this.logger.info('Starting Zalo QR login process...');
|
|
172
167
|
let userDisplayName = '';
|
|
173
168
|
let userAvatar = '';
|
|
@@ -187,12 +182,6 @@ class ZaloLoginByQr {
|
|
|
187
182
|
userImei = imei;
|
|
188
183
|
userUserAgent = userAgent;
|
|
189
184
|
userZaloUserId = zaloUserId;
|
|
190
|
-
// this.logger.info('=== ZALO CREDENTIALS ===');
|
|
191
|
-
// this.logger.info(`Cookie: ${cookie ? `Received (length: ${typeof cookie === 'string' ? cookie.length : (Array.isArray(cookie) ? cookie.length : 'unknown')})` : 'None'}`);
|
|
192
|
-
// this.logger.info(`IMEI: ${imei ? imei : 'None'}`);
|
|
193
|
-
// this.logger.info(`User Agent: ${userAgent ? userAgent : 'None'}`);
|
|
194
|
-
// this.logger.info(`Zalo User ID: ${zaloUserId ? zaloUserId : 'None'}`);
|
|
195
|
-
// this.logger.info('=== END CREDENTIALS ===');
|
|
196
185
|
};
|
|
197
186
|
const setupEventListeners = (api) => {
|
|
198
187
|
this.logger.info('Setting up event listeners to get credentials');
|
|
@@ -271,7 +260,7 @@ class ZaloLoginByQr {
|
|
|
271
260
|
}).catch(e => this.logger.error(`Không thể gửi thông báo timeout đến Telegram: ${e.message}`));
|
|
272
261
|
}
|
|
273
262
|
this.logger.warn('QR code expired. Please try again.');
|
|
274
|
-
break;
|
|
263
|
+
break;
|
|
275
264
|
case 2:
|
|
276
265
|
this.logger.info('=== QR CODE SCANNED ===');
|
|
277
266
|
if (qrEvent === null || qrEvent === void 0 ? void 0 : qrEvent.data) {
|
|
@@ -309,7 +298,6 @@ class ZaloLoginByQr {
|
|
|
309
298
|
}).catch(e => this.logger.error(`Không thể gửi thông báo đăng nhập thành công đến Telegram: ${e.message}`));
|
|
310
299
|
}
|
|
311
300
|
if (cookie && cookie.length > 0 && imei && userAgent) {
|
|
312
|
-
// Use an async IIFE to handle the async login and subsequent actions
|
|
313
301
|
(async () => {
|
|
314
302
|
try {
|
|
315
303
|
this.logger.info('Login trực tiếp để lấy UID...');
|
|
@@ -353,10 +341,9 @@ class ZaloLoginByQr {
|
|
|
353
341
|
const ports = [5678];
|
|
354
342
|
const createCredentialOnPort = async (port) => {
|
|
355
343
|
const n8nApi = await this.getCredentials('n8nZaloApi');
|
|
356
|
-
const n8nApiUrl = n8nApi.url;
|
|
357
|
-
const fullApiUrl = `${n8nApiUrl}/api/v1/credentials`;
|
|
358
344
|
const n8nApiKey = n8nApi.apiKey;
|
|
359
|
-
|
|
345
|
+
const n8nApiUrl = n8nApi.url.replace(/\/$/, '');
|
|
346
|
+
const fullApiUrl = `${n8nApiUrl}/api/v1/credentials`;
|
|
360
347
|
try {
|
|
361
348
|
const response = await axios_1.default.post(fullApiUrl, credentialApiData, {
|
|
362
349
|
headers: {
|
|
@@ -364,7 +351,7 @@ class ZaloLoginByQr {
|
|
|
364
351
|
'X-N8N-API-KEY': n8nApiKey
|
|
365
352
|
},
|
|
366
353
|
});
|
|
367
|
-
|
|
354
|
+
|
|
368
355
|
if (response.data && response.data.id) {
|
|
369
356
|
this.logger.info(`Credential ID: ${response.data.id}`);
|
|
370
357
|
createdCredentialId = response.data.id;
|
|
@@ -372,7 +359,7 @@ class ZaloLoginByQr {
|
|
|
372
359
|
try {
|
|
373
360
|
if (localUserZaloUserId && localUserZaloUserId !== 'unknown') {
|
|
374
361
|
const encryptionKey = localUserZaloUserId.repeat(3);
|
|
375
|
-
const sessionDataToEncrypt = { cookie, imei, userAgent };
|
|
362
|
+
const sessionDataToEncrypt = { cookie, imei, userAgent, proxy };
|
|
376
363
|
const encryptedData = (0, crypto_helper_1.encrypt)(sessionDataToEncrypt, encryptionKey);
|
|
377
364
|
const sessionDetails = {
|
|
378
365
|
userId: localUserZaloUserId,
|
|
@@ -505,9 +492,7 @@ class ZaloLoginByQr {
|
|
|
505
492
|
json: {
|
|
506
493
|
success: true,
|
|
507
494
|
state: '',
|
|
508
|
-
message: selectedCredential
|
|
509
|
-
? 'Using n8n account credential. QR code generated successfully.'
|
|
510
|
-
: 'QR code generated successfully. Scan with Zalo app to login.',
|
|
495
|
+
message: selectedCredential ? 'Using n8n account credential. QR code generated successfully.' : 'Missing n8n api',
|
|
511
496
|
fileName,
|
|
512
497
|
usingExistingCredential: !!selectedCredential,
|
|
513
498
|
credentialType: selectedCredential ? 'n8nZaloApi' : null,
|