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
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ZaloSendMessageDescription = exports.reactionOptions = void 0;
|
|
4
|
+
exports.reactionOptions = [
|
|
5
|
+
{ name: 'Heart', value: 'heart' },
|
|
6
|
+
{ name: 'Like', value: 'like' },
|
|
7
|
+
{ name: 'Haha', value: 'haha' },
|
|
8
|
+
{ name: 'Wow', value: 'wow' },
|
|
9
|
+
{ name: 'Cry', value: 'cry' },
|
|
10
|
+
{ name: 'Angry', value: 'angry' },
|
|
11
|
+
{ name: 'Kiss', value: 'kiss' },
|
|
12
|
+
{ name: 'Tears of Joy', value: 'tears_of_joy' },
|
|
13
|
+
{ name: 'Dislike', value: 'dislike' },
|
|
14
|
+
{ name: 'OK', value: 'ok' },
|
|
15
|
+
{ name: 'Thanks', value: 'thanks' },
|
|
16
|
+
{ name: 'Big Smile', value: 'big_smile' },
|
|
17
|
+
{ name: 'Sad', value: 'sad' },
|
|
18
|
+
{ name: 'Very Sad', value: 'very_sad' },
|
|
19
|
+
{ name: 'Rose', value: 'rose' },
|
|
20
|
+
{ name: 'Broken Heart', value: 'broken_heart' },
|
|
21
|
+
{ name: 'Love', value: 'love' },
|
|
22
|
+
{ name: 'Confused', value: 'confused' },
|
|
23
|
+
{ name: 'Wink', value: 'wink' },
|
|
24
|
+
{ name: 'Birthday', value: 'birthday' },
|
|
25
|
+
{ name: 'Bomb', value: 'bomb' },
|
|
26
|
+
{ name: 'Peace', value: 'peace' },
|
|
27
|
+
{ name: 'Pray', value: 'pray' },
|
|
28
|
+
{ name: 'No', value: 'no' },
|
|
29
|
+
{ name: 'Love You', value: 'love_you' },
|
|
30
|
+
{ name: 'Cool', value: 'cool' },
|
|
31
|
+
{ name: 'Nerd', value: 'nerd' },
|
|
32
|
+
{ name: 'Sunglasses', value: 'sunglasses' },
|
|
33
|
+
{ name: 'Neutral', value: 'neutral' },
|
|
34
|
+
{ name: 'Bye', value: 'bye' },
|
|
35
|
+
{ name: 'Sleepy', value: 'sleepy' },
|
|
36
|
+
{ name: 'Wipe', value: 'wipe' },
|
|
37
|
+
{ name: 'Dig', value: 'dig' },
|
|
38
|
+
{ name: 'Handclap', value: 'handclap' },
|
|
39
|
+
{ name: 'Silent', value: 'silent' },
|
|
40
|
+
{ name: 'Surprise', value: 'surprise' },
|
|
41
|
+
{ name: 'Embarrassed', value: 'embarrassed' },
|
|
42
|
+
{ name: 'Afraid', value: 'afraid' },
|
|
43
|
+
{ name: 'Big Laugh', value: 'big_laugh' },
|
|
44
|
+
{ name: 'Beer', value: 'beer' },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
exports.ZaloSendMessageDescription = [
|
|
48
|
+
{
|
|
49
|
+
displayName: 'Thread ID',
|
|
50
|
+
name: 'threadId',
|
|
51
|
+
type: 'string',
|
|
52
|
+
default: '',
|
|
53
|
+
required: true,
|
|
54
|
+
description: 'ID của thread để gửi tin nhắn',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
displayName: 'Type',
|
|
58
|
+
name: 'type',
|
|
59
|
+
type: 'options',
|
|
60
|
+
options: [
|
|
61
|
+
{
|
|
62
|
+
name: 'User',
|
|
63
|
+
value: 0,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'Group',
|
|
67
|
+
value: 1,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
default: 0,
|
|
71
|
+
description: 'Tin nhắn gửi đến (người dùng hoặc nhóm)',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
displayName: 'Message',
|
|
75
|
+
name: 'message',
|
|
76
|
+
type: 'string',
|
|
77
|
+
default: '',
|
|
78
|
+
required: true,
|
|
79
|
+
description: 'Nội dung tin nhắn cần gửi',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
displayName: 'Message Format',
|
|
83
|
+
name: 'messageFormat',
|
|
84
|
+
type: 'options',
|
|
85
|
+
options: [
|
|
86
|
+
{
|
|
87
|
+
name: 'Plain Text',
|
|
88
|
+
value: 'plain',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'HTML',
|
|
92
|
+
value: 'html',
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
default: 'plain',
|
|
96
|
+
description: 'Định dạng mặc định hoặc HTML',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
displayName: 'Urgency',
|
|
100
|
+
name: 'urgency',
|
|
101
|
+
type: 'options',
|
|
102
|
+
options: [
|
|
103
|
+
{
|
|
104
|
+
name: 'Default',
|
|
105
|
+
value: 0,
|
|
106
|
+
description: 'Tin nhắn mặc định',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'Important',
|
|
110
|
+
value: 1,
|
|
111
|
+
description: 'Đánh dấu tin nhắn quan trọng',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: 'Urgent',
|
|
115
|
+
value: 2,
|
|
116
|
+
description: 'Người nhận được thông báo nhiều lần trong vài phút',
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
default: 0,
|
|
120
|
+
description: 'Mức độ khẩn cấp của tin nhắn',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
displayName: 'Image URLs',
|
|
124
|
+
name: 'attachmentUrls',
|
|
125
|
+
type: 'string',
|
|
126
|
+
default: '',
|
|
127
|
+
placeholder: 'https://../img1.jpg, https://../img2.png',
|
|
128
|
+
description: 'Nhập 1 hoặc nhiều url ảnh cách nhau với dấu (,)',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
displayName: 'Send "Typing..." Event',
|
|
132
|
+
name: 'sendTypingEvent',
|
|
133
|
+
type: 'boolean',
|
|
134
|
+
default: true,
|
|
135
|
+
description: 'Gửi sự kiện "Đang soạn tin..." (tự động chờ 1 chút trước khi gửi tin)',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
displayName: 'Reaction',
|
|
139
|
+
name: 'reaction',
|
|
140
|
+
type: 'fixedCollection',
|
|
141
|
+
typeOptions: {
|
|
142
|
+
multipleValues: false,
|
|
143
|
+
},
|
|
144
|
+
placeholder: 'Add Reaction',
|
|
145
|
+
default: {},
|
|
146
|
+
options: [
|
|
147
|
+
{
|
|
148
|
+
displayName: 'Reaction Settings',
|
|
149
|
+
name: 'reactionValue',
|
|
150
|
+
values: [
|
|
151
|
+
{
|
|
152
|
+
displayName: 'Reaction Icon',
|
|
153
|
+
name: 'reactionIcon',
|
|
154
|
+
type: 'options',
|
|
155
|
+
options: exports.reactionOptions,
|
|
156
|
+
default: 'heart',
|
|
157
|
+
description: 'Biểu tượng cảm xúc',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
displayName: 'Message ID',
|
|
161
|
+
name: 'reactionMsgId',
|
|
162
|
+
type: 'string',
|
|
163
|
+
default: '',
|
|
164
|
+
description: 'msgId của tin nhắn hiện có để reaction',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
displayName: 'Client Message ID',
|
|
168
|
+
name: 'reactionCliMsgId',
|
|
169
|
+
type: 'string',
|
|
170
|
+
default: '',
|
|
171
|
+
description: 'cliMsgId của tin nhắn hiện có để reaction',
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
displayName: 'Quote Message',
|
|
179
|
+
name: 'quote',
|
|
180
|
+
type: 'fixedCollection',
|
|
181
|
+
typeOptions: {
|
|
182
|
+
multipleValues: false,
|
|
183
|
+
},
|
|
184
|
+
placeholder: 'Add Quote',
|
|
185
|
+
default: {},
|
|
186
|
+
options: [
|
|
187
|
+
{
|
|
188
|
+
displayName: 'Quote Details',
|
|
189
|
+
name: 'quoteValue',
|
|
190
|
+
values: [
|
|
191
|
+
{
|
|
192
|
+
displayName: 'User ID',
|
|
193
|
+
name: 'uidFrom',
|
|
194
|
+
type: 'string',
|
|
195
|
+
default: '',
|
|
196
|
+
description: 'ID của người gửi tin nhắn (không phải threadId của group)',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
displayName: 'Message ID',
|
|
200
|
+
name: 'msgId',
|
|
201
|
+
type: 'string',
|
|
202
|
+
default: '',
|
|
203
|
+
description: 'msgId của tin nhắn cần trích dẫn',
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
displayName: 'Client Message ID',
|
|
207
|
+
name: 'cliMsgId',
|
|
208
|
+
type: 'string',
|
|
209
|
+
default: '',
|
|
210
|
+
description: 'cliMsgId của tin nhắn cần trích dẫn',
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
displayName: 'Content',
|
|
214
|
+
name: 'content',
|
|
215
|
+
type: 'string',
|
|
216
|
+
default: '',
|
|
217
|
+
description: 'Nội dung tin nhắn trích dẫn',
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
displayName: 'Timestamp',
|
|
221
|
+
name: 'ts',
|
|
222
|
+
type: 'number',
|
|
223
|
+
default: 0,
|
|
224
|
+
description: 'Timestamp (ms) của tin nhắn cần trích dẫn',
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
displayName: 'Message Type',
|
|
228
|
+
name: 'msgType',
|
|
229
|
+
type: 'number',
|
|
230
|
+
default: 1,
|
|
231
|
+
description: 'Loại tin nhắn được trích dẫn (ví dụ: 1 cho text)',
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
displayName: 'Mentions',
|
|
239
|
+
name: 'mentions',
|
|
240
|
+
type: 'fixedCollection',
|
|
241
|
+
typeOptions: {
|
|
242
|
+
multipleValues: false,
|
|
243
|
+
},
|
|
244
|
+
placeholder: 'Add Mention',
|
|
245
|
+
default: {},
|
|
246
|
+
options: [
|
|
247
|
+
{
|
|
248
|
+
displayName: 'Mention Details',
|
|
249
|
+
name: 'mentionValue',
|
|
250
|
+
values: [
|
|
251
|
+
{
|
|
252
|
+
displayName: 'User ID',
|
|
253
|
+
name: 'uid',
|
|
254
|
+
type: 'string',
|
|
255
|
+
default: '',
|
|
256
|
+
description: 'ID của người dùng được mention',
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
displayName: 'Position',
|
|
260
|
+
name: 'pos',
|
|
261
|
+
type: 'number',
|
|
262
|
+
default: 0,
|
|
263
|
+
description: 'Vị trí mention trong tin nhắn',
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
displayName: 'Length',
|
|
267
|
+
name: 'len',
|
|
268
|
+
type: 'number',
|
|
269
|
+
default: 0,
|
|
270
|
+
description: 'Độ dài của mention',
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
displayName: 'Text Styles',
|
|
278
|
+
name: 'styles',
|
|
279
|
+
type: 'fixedCollection',
|
|
280
|
+
typeOptions: {
|
|
281
|
+
multipleValues: true,
|
|
282
|
+
},
|
|
283
|
+
placeholder: 'Add Text Style',
|
|
284
|
+
default: {},
|
|
285
|
+
options: [
|
|
286
|
+
{
|
|
287
|
+
name: 'style',
|
|
288
|
+
displayName: 'Style',
|
|
289
|
+
values: [
|
|
290
|
+
{
|
|
291
|
+
displayName: 'Style Type',
|
|
292
|
+
name: 'st',
|
|
293
|
+
type: 'options',
|
|
294
|
+
options: [
|
|
295
|
+
{
|
|
296
|
+
name: 'Big Font',
|
|
297
|
+
value: 'f_18',
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: 'Bold',
|
|
301
|
+
value: 'b',
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
name: 'Green Color',
|
|
305
|
+
value: 'c_15a85f',
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
name: 'Indent',
|
|
309
|
+
value: 'ind_$',
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
name: 'Italic',
|
|
313
|
+
value: 'i',
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: 'Orange Color',
|
|
317
|
+
value: 'c_f27806',
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
name: 'Ordered List',
|
|
321
|
+
value: 'lst_2',
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: 'Red Color',
|
|
325
|
+
value: 'c_db342e',
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
name: 'Small Font',
|
|
329
|
+
value: 'f_13',
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: 'Strike Through',
|
|
333
|
+
value: 's',
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
name: 'Underline',
|
|
337
|
+
value: 'u',
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: 'Unordered List',
|
|
341
|
+
value: 'lst_1',
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: 'Yellow Color',
|
|
345
|
+
value: 'c_f7b503',
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
default: 'b',
|
|
349
|
+
description: 'Loại style áp dụng cho text',
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
displayName: 'Start Position',
|
|
353
|
+
name: 'start',
|
|
354
|
+
type: 'number',
|
|
355
|
+
default: 0,
|
|
356
|
+
description: 'Vị trí bắt đầu áp dụng style (tính từ 0)',
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
displayName: 'End Position',
|
|
360
|
+
name: 'end',
|
|
361
|
+
type: 'number',
|
|
362
|
+
default: 0,
|
|
363
|
+
description: 'Vị trí kết thúc áp dụng style',
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
displayName: 'Indent Size',
|
|
367
|
+
name: 'indentSize',
|
|
368
|
+
type: 'number',
|
|
369
|
+
default: 1,
|
|
370
|
+
displayOptions: {
|
|
371
|
+
show: {
|
|
372
|
+
'st': ['ind_$'],
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
description: 'Kích thước indent (chỉ dùng cho Indent style)',
|
|
376
|
+
},
|
|
377
|
+
],
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
description: 'Định dạng text với các style khác nhau',
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
displayName: 'TTL (Time To Live)',
|
|
384
|
+
name: 'ttl',
|
|
385
|
+
type: 'number',
|
|
386
|
+
default: 0,
|
|
387
|
+
description: 'Thời gian tồn tại của tin nhắn (giây). 0 = vĩnh viễn.',
|
|
388
|
+
},
|
|
389
|
+
];
|
|
@@ -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
|
-
|
|
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
|
-
|
|
501
|
-
|
|
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,
|
|
@@ -7,6 +7,7 @@ exports.ZaloUploadAttachment = void 0;
|
|
|
7
7
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
8
|
const zca_js_1 = require("zca-js");
|
|
9
9
|
const helper_1 = require("../utils/helper");
|
|
10
|
+
const zalo_helper_1 = require("../utils/zalo.helper");
|
|
10
11
|
const fs_1 = __importDefault(require("fs"));
|
|
11
12
|
let api;
|
|
12
13
|
class ZaloUploadAttachment {
|
|
@@ -149,34 +150,16 @@ class ZaloUploadAttachment {
|
|
|
149
150
|
async execute() {
|
|
150
151
|
const returnData = [];
|
|
151
152
|
const items = this.getInputData();
|
|
152
|
-
const zaloCred = await this.getCredentials('zaloApi');
|
|
153
|
-
const cookieFromCred = JSON.parse(zaloCred.cookie);
|
|
154
|
-
const imeiFromCred = zaloCred.imei;
|
|
155
|
-
const userAgentFromCred = zaloCred.userAgent;
|
|
156
153
|
try {
|
|
157
|
-
|
|
158
|
-
selfListen: false,
|
|
159
|
-
logging: true,
|
|
160
|
-
imageMetadataGetter: async (filePath) => {
|
|
161
|
-
return {
|
|
162
|
-
width: 0,
|
|
163
|
-
height: 0,
|
|
164
|
-
size: 0
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
api = await zalo.login({
|
|
169
|
-
cookie: cookieFromCred,
|
|
170
|
-
imei: imeiFromCred,
|
|
171
|
-
userAgent: userAgentFromCred
|
|
172
|
-
});
|
|
154
|
+
api = await (0, zalo_helper_1.getZaloApiClient)(this, { needsImageMetadataGetter: true, selfListen: true });
|
|
173
155
|
if (!api) {
|
|
174
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to initialize Zalo API. Check
|
|
156
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to initialize Zalo API. Check credentials or User ID.');
|
|
175
157
|
}
|
|
176
158
|
}
|
|
177
159
|
catch (error) {
|
|
178
160
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Zalo login error: ${error.message}`);
|
|
179
161
|
}
|
|
162
|
+
|
|
180
163
|
for (let i = 0; i < items.length; i++) {
|
|
181
164
|
try {
|
|
182
165
|
const threadId = this.getNodeParameter('threadId', i);
|
|
@@ -188,12 +171,29 @@ class ZaloUploadAttachment {
|
|
|
188
171
|
}
|
|
189
172
|
const sources = [];
|
|
190
173
|
const tempFiles = [];
|
|
174
|
+
|
|
175
|
+
const path_1 = __importDefault(require("path"));
|
|
176
|
+
const os_1 = __importDefault(require("os"));
|
|
177
|
+
const n8nUserFolder = process.env.N8N_USER_FOLDER || path_1.default.join(os_1.default.homedir(), '.n8n');
|
|
178
|
+
|
|
191
179
|
for (const sourceConfig of sourcesData.source) {
|
|
192
180
|
let source;
|
|
193
181
|
if (sourceConfig.type === 'filePath') {
|
|
194
|
-
|
|
182
|
+
let filePath = sourceConfig.filePath;
|
|
195
183
|
if (!fs_1.default.existsSync(filePath)) {
|
|
196
|
-
|
|
184
|
+
const relativePath = path_1.default.join(n8nUserFolder, filePath);
|
|
185
|
+
const tempPath = path_1.default.join(n8nUserFolder, 'temp_files', path_1.default.basename(filePath));
|
|
186
|
+
|
|
187
|
+
if (fs_1.default.existsSync(relativePath)) {
|
|
188
|
+
filePath = relativePath;
|
|
189
|
+
this.logger.info(`Resolved relative path: ${filePath}`);
|
|
190
|
+
} else if (fs_1.default.existsSync(tempPath)) {
|
|
191
|
+
filePath = tempPath;
|
|
192
|
+
this.logger.info(`Resolved temp path: ${filePath}`);
|
|
193
|
+
} else {
|
|
194
|
+
this.logger.error(`File path not found: ${sourceConfig.filePath}`);
|
|
195
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `File không tồn tại: ${sourceConfig.filePath}. (Đã thử tìm tại: ${relativePath})`);
|
|
196
|
+
}
|
|
197
197
|
}
|
|
198
198
|
source = filePath;
|
|
199
199
|
}
|
|
@@ -203,6 +203,7 @@ class ZaloUploadAttachment {
|
|
|
203
203
|
if (!tempFilePath) {
|
|
204
204
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Không thể tải file từ URL: ${fileUrl}`);
|
|
205
205
|
}
|
|
206
|
+
this.logger.info(`Downloaded URL to temp file: ${tempFilePath}`);
|
|
206
207
|
tempFiles.push(tempFilePath);
|
|
207
208
|
source = tempFilePath;
|
|
208
209
|
}
|
|
@@ -226,9 +227,6 @@ class ZaloUploadAttachment {
|
|
|
226
227
|
sources.push(source);
|
|
227
228
|
}
|
|
228
229
|
this.logger.info(`Uploading ${sources.length} file(s) to thread ${threadId} (${threadTypeStr})`);
|
|
229
|
-
if (!api) {
|
|
230
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Zalo API not initialized');
|
|
231
|
-
}
|
|
232
230
|
const uploadResults = await api.uploadAttachment(sources, threadId, threadType);
|
|
233
231
|
for (const tempFile of tempFiles) {
|
|
234
232
|
(0, helper_1.removeFile)(tempFile);
|