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.
@@ -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
- 'deleteMessage',
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', 'deleteMessage'],
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 để tham gia nhóm (ví dụ: https://zalo.me/g/xxxxxx)',
315
+ description: 'Link tham gia nhóm (ví dụ: https://zalo.me/g/xxxxxx)',
316
316
  },
317
317
  {
318
- displayName: 'Message ID',
319
- name: 'msgId',
320
- type: 'string',
321
- required: true,
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: ['deleteMessage'],
326
+ operation: ['updateGroupSettings'],
327
327
  },
328
328
  },
329
- description: 'msgId của tin nhắn cần xóa',
330
- },
331
- {
332
- displayName: 'Client Message ID',
333
- name: 'cliMsgId',
334
- type: 'string',
335
- required: true,
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
- description: 'cliMsgId của tin nhắn cần xóa',
344
- },
345
- {
346
- displayName: 'Only Me',
347
- name: 'onlyMe',
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
- description: 'Bật để xóa tin nhắn ở phía bạn. Tắt để xóa với mọi người (quyền admin)',
357
- },
358
- // {
359
- // displayName: 'Giới Hạn',
360
- // name: 'limit',
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
- description: 'Link để lấy thông tin nhóm (ví dụ: https://zalo.me/g/xxxxxx)',
351
+ {
352
+ displayName: 'Xem Tin Nhắn ',
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
- let proxy = this.getNodeParameter('proxy', 0, '');
120
- if (proxy && !proxy.toLowerCase().startsWith('http')) {
121
- this.logger.warn(`Proxy không hợp lệ: "${proxy}"`);
122
- proxy = '';
123
- }
124
- const timeout = 30;
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 n8nCredential;
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: true,
158
+ logging: false,
153
159
  };
154
160
  if (proxy) {
155
- zaloOptions.proxy = proxy;
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; // This event often precedes a timeout error, so a log is sufficient.
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
- // this.logger.info(`Trying to create credential via n8n API at ${fullApiUrl}`);
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
- // this.logger.info('Credential created successfully via n8n API');
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,