n8n-nodes-zalo-custom 1.0.9 → 1.0.10

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 CHANGED
@@ -159,7 +159,7 @@ Khi bạn quản lý nhiều tài khoản Zalo, mỗi tài khoản sẽ có mộ
159
159
  <details>
160
160
  <summary><b>nhận sự kiện từ Zalo để phản hồi</b></summary>
161
161
 
162
- - **Lắng nghe sự kiện**: tin nhắn người dùng/nhóm, thả tim, thu hồi tin nhắn, lời mời kết bạn.
162
+ - **Lắng nghe sự kiện**: tin nhắn người dùng/nhóm, thả tim, thu hồi tin nhắn, lời mời kết bạn, đã xem tin nhắn, đang soạn tin.
163
163
  - **Cấu hình**:
164
164
  - Chấp nhận hoặc loại trừ các ID nhóm khi nhận sự kiện tin nhắn.
165
165
  - Chỉ nhận theo từ khoá hoặc loại trừ khi nhận sự kiện tin nhắn.
@@ -200,6 +200,7 @@ Khi bạn quản lý nhiều tài khoản Zalo, mỗi tài khoản sẽ có mộ
200
200
  - **Tương tác với người dùng:** Thu hồi tin nhắn, chặn/bỏ chặn, đổi tên gợi nhớ, đánh dấu đã đọc/chưa đọc.
201
201
  - **Lấy thông tin:** Lấy danh sách bạn bè, gợi ý kết bạn, thông tin chi tiết người dùng (qua User ID/SĐT), lấy mã QR.
202
202
  - **Cập nhật hồ sơ:** Thay đổi thông tin cá nhân của bạn (tên, ngày sinh, giới tính).
203
+ - **Lấy tin nhắn cũ:** Lấy danh sách các tin nhắn cũ trong khả năng.
203
204
  </details>
204
205
 
205
206
  ---
@@ -41,18 +41,18 @@ class ZaloCommunication {
41
41
 
42
42
  try {
43
43
  api = await (0, zalo_helper_1.getZaloApiClient)(this);
44
+ if (!api) {
45
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to initialize Zalo API. Check credentials or User ID.');
46
+ }
44
47
  }
45
48
  catch (error) {
46
49
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Zalo login error: ${error.message}`);
47
50
  }
48
- if (!api) {
49
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No API instance found. Please make sure to provide valid credentials.');
50
- }
51
51
 
52
52
  for (let i = 0; i < items.length; i++) {
53
53
  try {
54
54
  switch (resource) {
55
- // --- Logic cho Sticker ---
55
+ // --- Sticker ---
56
56
  case 'sticker':
57
57
  switch (operation) {
58
58
  case 'getStickers': {
@@ -122,7 +122,7 @@ class ZaloCommunication {
122
122
  }
123
123
  break;
124
124
 
125
- // --- Logic cho Poll ---
125
+ // --- Poll ---
126
126
  case 'poll':
127
127
  switch (operation) {
128
128
  case 'createPoll': {
@@ -223,7 +223,7 @@ class ZaloCommunication {
223
223
  }
224
224
  break;
225
225
 
226
- // --- Logic cho Tag ---
226
+ // --- Tag ---
227
227
  case 'tag':
228
228
  switch (operation) {
229
229
  case 'list': {
@@ -53,16 +53,21 @@ class ZaloGroup {
53
53
  } catch (e) { return false; }
54
54
  });
55
55
 
56
- api = await (0, zalo_helper_1.getZaloApiClient)(this, { needsImageMetadataGetter: needsImageMetadataGetter });
57
- if (!api) {
58
- throw new n8n_workflow_1.NodeApiError(this.getNode(), {}, 'Failed to initialize Zalo API. Check credentials or User ID.');
56
+ try {
57
+ api = await (0, zalo_helper_1.getZaloApiClient)(this, {needsImageMetadataGetter: needsImageMetadataGetter});
58
+ if (!api) {
59
+ throw new n8n_workflow_1.NodeApiError(this.getNode(), {}, 'Failed to initialize Zalo API. Check credentials or User ID.');
60
+ }
61
+ }
62
+ catch (error) {
63
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Zalo login error: ${error.message}`);
59
64
  }
60
65
 
61
66
  for (let i = 0; i < items.length; i++) {
62
67
  try {
63
68
  const resource = this.getNodeParameter('resource', i);
64
69
  const operation = this.getNodeParameter('operation', i);
65
- this.logger.info(`[GROUP] ${operation} - Image ${needsImageMetadataGetter}`)
70
+ // this.logger.info(`[GROUP] ${operation} - Image ${needsImageMetadataGetter}`)
66
71
 
67
72
  if (resource === 'zaloGroup') {
68
73
  switch (operation) {
@@ -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,
@@ -561,10 +561,8 @@ class ZaloSendMessage {
561
561
  const returnData = [];
562
562
  const items = this.getInputData();
563
563
  try {
564
- // Use the new helper function to get the API client
565
564
  api = await (0, zalo_helper_1.getZaloApiClient)(this, { needsImageMetadataGetter: true });
566
565
  if (!api) {
567
- // The helper function will throw an error, but as a fallback:
568
566
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to initialize Zalo API. Check credentials or User ID.');
569
567
  }
570
568
  }
@@ -577,7 +575,6 @@ class ZaloSendMessage {
577
575
  },
578
576
  });
579
577
  return [returnData]
580
- // throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Zalo login error: ${error.message}`);
581
578
  }
582
579
  for (let i = 0; i < items.length; i++) {
583
580
  try {
@@ -595,7 +592,6 @@ class ZaloSendMessage {
595
592
  const attachmentUrls = this.getNodeParameter('attachmentUrls', i, '');
596
593
  const reactionParam = this.getNodeParameter('reaction', i, {});
597
594
 
598
- // return json
599
595
  const returnJson = {
600
596
  threadId,
601
597
  threadType: type
@@ -677,15 +673,12 @@ class ZaloSendMessage {
677
673
  messageContent.attachments = [];
678
674
  for (const url of urls) {
679
675
  try {
680
- // Validate URL format
681
676
  new URL(url);
682
- // Download file and get its local path
683
677
  const fileData = await (0, helper_1.saveFile)(url);
684
678
  messageContent.attachments.push(fileData);
685
679
  this.logger.info(`Successfully prepared attachment from URL: ${url}`);
686
680
  }
687
681
  catch (e) {
688
- // Log a warning and skip if the URL is invalid or the file can't be downloaded
689
682
  this.logger.warn(`Skipping invalid or inaccessible attachment URL: ${url}. Error: ${e.message}`);
690
683
  }
691
684
  }
@@ -200,8 +200,7 @@ class ZaloTrigger {
200
200
  continueOnFail: continueOnFail,
201
201
  };
202
202
 
203
- let telegramToken;
204
- let telegramChatId;
203
+ let telegramToken, telegramChatId, telegramOptions;
205
204
  if (continueOnFail) {
206
205
  const tokenOverride = this.getNodeParameter('telegramTokenOverride', 0, '');
207
206
  const chatIdOverride = this.getNodeParameter('telegramChatIdOverride', 0, '');
@@ -211,12 +210,25 @@ class ZaloTrigger {
211
210
  telegramChatId = chatIdOverride || credsChatId;
212
211
  if (!telegramToken || !telegramChatId) {
213
212
  context.errorNotification = 'Vui lòng cung cấp BotToken và Chat_id để nhận thông báo khi phát hiện lỗi';
213
+ } else {
214
+ telegramOptions = {
215
+ token: telegramToken,
216
+ chatId: telegramChatId,
217
+ logger: this.logger,
218
+ };
214
219
  }
215
220
  }
221
+ const sendTelegramNotification = (message) => {
222
+ if (telegramOptions) {
223
+ (0, zalo_helper_1.sendToTelegram)({
224
+ ...telegramOptions,
225
+ text: message,
226
+ }).catch(e => this.logger.error(`Failed to send Telegram notification: ${e.message}`));
227
+ }
228
+ };
216
229
 
217
230
  let api = apiInstances.get(instanceKey);
218
231
  let closeFunction = async () => { };
219
-
220
232
  try {
221
233
  if (apiInstances.has(instanceKey)) {
222
234
  this.logger.info(`Instance ${instanceKey} already exists, reusing...`);
@@ -226,7 +238,6 @@ class ZaloTrigger {
226
238
  if (!api) {
227
239
  throw new Error('No API instance found. Please make sure to provide valid credentials.');
228
240
  }
229
-
230
241
  try {
231
242
  const fetchedInfo = await api.fetchAccountInfo();
232
243
  if (fetchedInfo && fetchedInfo.profile) {
@@ -261,7 +272,6 @@ class ZaloTrigger {
261
272
  catch (err) {
262
273
  this.logger.error(`[Zalo ${mode}] Failed to fetch account info: ${err.message}`);
263
274
  }
264
-
265
275
  apiInstances.set(instanceKey, api);
266
276
  }
267
277
 
@@ -270,7 +280,6 @@ class ZaloTrigger {
270
280
  if (!data?.isSelf) {
271
281
  context.logger.info(`[${apiInstances.size}] [Zalo ${context.mode} received] ${context.credentialPhone}: ${eventType}`);
272
282
  }
273
-
274
283
  const dataWithContext = {
275
284
  ...data,
276
285
  _timestamp: new Date().toISOString(),
@@ -283,7 +292,6 @@ class ZaloTrigger {
283
292
  _eventType: eventType,
284
293
  _source: 'zalo_trigger',
285
294
  };
286
-
287
295
  const webhookData = this.getWorkflowStaticData('node');
288
296
  if (!webhookData[context.instanceKey]) {
289
297
  webhookData[context.instanceKey] = {};
@@ -299,7 +307,6 @@ class ZaloTrigger {
299
307
 
300
308
  emitEvent(dataWithContext);
301
309
  };
302
-
303
310
  if (context.eventTypes.includes('message_user') || context.eventTypes.includes('message_group')) {
304
311
  api.listener.on('message', async (message) => {
305
312
  const eventType = message.type === threadTypeUser ? 'message_user' : 'message_group';
@@ -349,35 +356,30 @@ class ZaloTrigger {
349
356
  await handleEvent(message, eventType);
350
357
  });
351
358
  }
352
-
353
359
  if (context.eventTypes.includes('reaction')) {
354
360
  api.listener.on('reaction', async (reaction) => {
355
361
  if (context.ignoreSelfEvents && reaction.isSelf) return;
356
362
  await handleEvent(reaction, 'reaction');
357
363
  });
358
364
  }
359
-
360
365
  if (context.eventTypes.includes('undo')) {
361
366
  api.listener.on('undo', async (undo) => {
362
367
  if (context.ignoreSelfEvents && undo.isSelf) return;
363
368
  await handleEvent(undo, 'undo');
364
369
  });
365
370
  }
366
-
367
371
  if (context.eventTypes.includes('group_event')) {
368
372
  api.listener.on('group_event', async (groupEvent) => {
369
373
  if (context.ignoreSelfEvents && groupEvent.isSelf) return;
370
374
  await handleEvent(groupEvent, 'group_event');
371
375
  });
372
376
  }
373
-
374
377
  if (context.eventTypes.includes('friend_request')) {
375
378
  api.listener.on('friend_event', async (friendEvent) => {
376
379
  if (context.ignoreSelfEvents && friendEvent.isSelf) return;
377
380
  await handleEvent(friendEvent, 'friend_request');
378
381
  });
379
382
  }
380
-
381
383
  if (context.eventTypes.includes('seen_message')) {
382
384
  api.listener.on('seen_messages', async (seenMessages) => {
383
385
  for (const seen of seenMessages) {
@@ -387,7 +389,6 @@ class ZaloTrigger {
387
389
  }
388
390
  });
389
391
  }
390
-
391
392
  if (context.eventTypes.includes('typing')) {
392
393
  api.listener.on('typing', async (typing) => {
393
394
  if (seen.type === threadTypeUser && !seen.isSelf) {
@@ -396,7 +397,22 @@ class ZaloTrigger {
396
397
  });
397
398
  }
398
399
 
399
- // Start listening
400
+ api.listener.on('disconnected', (code, reason) => {
401
+ context.logger.warn(`[Zalo ${context.mode}] disconnected: ${currentPhone} - ${code}: "${reason || 'No reason'}". Attempting to reconnect...`);
402
+ if (reason !== 'NORMAL_CLOSURE') {
403
+ sendTelegramNotification(`[Zalo ${context.mode}] disconnected: ${currentPhone}: err: "${reason || 'No reason'}". Attempting to reconnect...`);
404
+ }
405
+ });
406
+ api.listener.on('closed', (code, reason) => {
407
+ context.logger.warn(`[Zalo ${context.mode}] closed: ${currentPhone} - ${code}: "${reason || 'No reason'}" (stop trigger)`);
408
+ if (reason !== 'NORMAL_CLOSURE') {
409
+ sendTelegramNotification(`[Zalo ${context.mode}] closed: ${currentPhone}: err: "${reason || 'No reason'}" (stop trigger)`);
410
+ }
411
+ });
412
+ api.listener.on('error', (error) => {
413
+ const errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
414
+ context.logger.warn(`[Zalo ${context.mode}] WebSocket Error: ${currentPhone} - ${errorMessage}`);
415
+ });
400
416
  api.listener.start({ retryOnClose: true });
401
417
  this.logger.info(`[${apiInstances.size}] [Zalo ${mode}] Listening: ${currentPhone} - ${currentName} (${context.eventTypes})`);
402
418
 
@@ -497,14 +513,8 @@ class ZaloTrigger {
497
513
  catch (error) {
498
514
  const continueOnFail = this.getNodeParameter('continueOnFail', 0, false);
499
515
  if (continueOnFail && mode !== 'manual') {
500
- if (telegramToken && telegramChatId) {
501
- const errorMessage = `⚠️ **Lỗi Kích Hoạt Zalo Trigger**\n\n- Tài khoản: ${currentName}\n- SĐT: ${currentPhone}\n- Lý do: ${error.message} (bỏ qua node này tiếp tục active workflow)`;
502
- (0, zalo_helper_1.sendToTelegram)({
503
- token: telegramToken,
504
- chatId: telegramChatId,
505
- text: errorMessage,
506
- }).catch(e => this.logger.error(`Failed to send Telegram notification: ${e.message}`));
507
- }
516
+ const errorMessage = `⚠️ **Lỗi Kích Hoạt Zalo Trigger**\n\n- Tài khoản: ${currentName}\n- SĐT: ${currentPhone}\n- Lý do: ${error.message} (bỏ qua node này tiếp tục active workflow)`;
517
+ sendTelegramNotification(errorMessage);
508
518
  return {
509
519
  closeFunction: async () => { },
510
520
  manualTriggerFunction: async () => true,
@@ -47,9 +47,14 @@ class ZaloUser {
47
47
  const items = this.getInputData();
48
48
  const returnData = [];
49
49
 
50
- api = await (0, zalo_helper_1.getZaloApiClient)(this, {});
51
- if (!api) {
52
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to initialize Zalo API. Check credentials or User ID.');
50
+ try {
51
+ api = await (0, zalo_helper_1.getZaloApiClient)(this, {});
52
+ if (!api) {
53
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to initialize Zalo API. Check credentials or User ID.');
54
+ }
55
+ }
56
+ catch (error) {
57
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Zalo login error: ${error.message}`);
53
58
  }
54
59
 
55
60
  for (let i = 0; i < items.length; i++) {
@@ -119,7 +124,7 @@ class ZaloUser {
119
124
  returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
120
125
  break;
121
126
  }
122
- case 'getFriendOnlines': {
127
+ case 'getFriendOnlines': { // err
123
128
  const response = await api.getFriendOnlines();
124
129
  returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
125
130
  break;
@@ -8,6 +8,8 @@ const fs_1 = require("fs");
8
8
  const axios_1 = require("axios");
9
9
  const FormData = require("form-data");
10
10
  const sql_js_1 = require("sql.js");
11
+ const https_proxy_agent_1 = require("https-proxy-agent");
12
+ const node_fetch_1 = __importDefault(require("node-fetch"));
11
13
  const crypto_helper_1 = require("./crypto.helper");
12
14
  const path_1 = require("path");
13
15
 
@@ -86,8 +88,8 @@ exports.imageMetadataGetter = imageMetadataGetter;
86
88
  async function getZaloApiClient(node, options = {}) {
87
89
  const useSession = node.getNodeParameter('useSession', 0, false);
88
90
  const userId = node.getNodeParameter('connectToId', 0, '');
89
- const { needsImageMetadataGetter = false, selfListen = false } = options;
90
- let cookie, imei, userAgent, sessionInfo = null, actualZaloId = '';
91
+ const { needsImageMetadataGetter = false, selfListen = false, logging = false } = options;
92
+ let cookie, imei, userAgent, sessionInfo, proxy = null, actualZaloId = '';
91
93
  if (useSession && userId) {
92
94
  try {
93
95
  await initDb();
@@ -125,6 +127,7 @@ async function getZaloApiClient(node, options = {}) {
125
127
  cookie = sessionData.cookie;
126
128
  imei = sessionData.imei;
127
129
  userAgent = sessionData.userAgent;
130
+ proxy = sessionData.proxy;
128
131
  }
129
132
  catch (e) {
130
133
  throw new n8n_workflow_1.NodeOperationError(node.getNode(), `[SS] Failed to decrypt session for Zalo ID: "${actualZaloId}". The file might be corrupt or the key has changed. Error: ${e.message}`);
@@ -144,14 +147,33 @@ async function getZaloApiClient(node, options = {}) {
144
147
  cookie = JSON.parse(zaloCred.cookie);
145
148
  imei = zaloCred.imei;
146
149
  userAgent = zaloCred.userAgent;
150
+ proxy = zaloCred.proxy;
151
+ }
152
+
153
+ const zaloOptions = {
154
+ logging,
155
+ selfListen,
156
+ settings: {
157
+ features: {
158
+ socket: {
159
+ close_and_retry_codes: [1006, 1000, 3000, 3003],
160
+ retries: {
161
+ "1006": {
162
+ max: 10,
163
+ times: [5000, 10000, 30000],
164
+ },
165
+ },
166
+ },
167
+ },
168
+ },
169
+ };
170
+ if (proxy) {
171
+ zaloOptions.agent = new https_proxy_agent_1.HttpsProxyAgent(proxy);
172
+ zaloOptions.polyfill = node_fetch_1.default;
147
173
  }
148
- const zaloOptions = {};
149
174
  if (needsImageMetadataGetter) {
150
175
  zaloOptions.imageMetadataGetter = imageMetadataGetter;
151
176
  }
152
- if (selfListen) {
153
- zaloOptions.selfListen = selfListen;
154
- }
155
177
  const zalo = new zca_js_1.Zalo(zaloOptions);
156
178
  return zalo.login({ cookie, imei, userAgent });
157
179
  }
@@ -242,7 +264,7 @@ exports.deleteSessionByUserId = deleteSessionByUserId;
242
264
 
243
265
  async function sendToTelegram({ token, chatId, text, binaryData, fileName, caption, logger, }) {
244
266
  if (!token || !chatId) {
245
- logger === null || logger === void 0 ? void 0 : logger.warn('Telegram token và chatId là bắt buộc nhưng không được cung cấp.');
267
+ logger === null || logger === void 0 ? void 0 : logger.warn('Telegram token và chatId là bắt buộc');
246
268
  return;
247
269
  }
248
270
  const baseUrl = `https://api.telegram.org/bot${token}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-zalo-custom",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "n8n nodes for Zalo automation. Send messages, manage groups, friends, and listen to events without third-party services.",
5
5
  "keywords": [
6
6
  "n8n",
@@ -51,6 +51,8 @@
51
51
  "dependencies": {
52
52
  "axios": "^1.8.4",
53
53
  "express": "^5.1.0",
54
+ "https-proxy-agent": "^7.0.5",
55
+ "node-fetch": "^3.3.2",
54
56
  "zca-js": "^2.0.4",
55
57
  "image-size": "^1.1.1",
56
58
  "sql.js": "^1.10.3"