n8n-nodes-zalo-custom 1.0.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.
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ZaloUploadAttachment = void 0;
7
+ const n8n_workflow_1 = require("n8n-workflow");
8
+ const zca_js_1 = require("zca-js");
9
+ const helper_1 = require("../utils/helper");
10
+ const fs_1 = __importDefault(require("fs"));
11
+ let api;
12
+ class ZaloUploadAttachment {
13
+ constructor() {
14
+ this.description = {
15
+ displayName: 'Zalo Upload Attachment',
16
+ name: 'zaloUploadAttachment',
17
+ icon: 'file:../shared/zalo.svg',
18
+ group: ['Zalo'],
19
+ version: 1,
20
+ description: 'Upload file đính kèm lên Zalo để lấy thông tin file (photoId, fileId, fileUrl...)',
21
+ defaults: {
22
+ name: 'Zalo Upload Attachment',
23
+ },
24
+ inputs: ['main'],
25
+ outputs: ['main'],
26
+ credentials: [
27
+ {
28
+ name: 'zaloApi',
29
+ required: true,
30
+ },
31
+ ],
32
+ properties: [
33
+ {
34
+ displayName: 'Thread ID',
35
+ name: 'threadId',
36
+ type: 'string',
37
+ default: '',
38
+ required: true,
39
+ description: 'ID của thread (user ID hoặc group ID) để upload file',
40
+ },
41
+ {
42
+ displayName: 'Thread Type',
43
+ name: 'threadType',
44
+ type: 'options',
45
+ options: [
46
+ {
47
+ name: 'User',
48
+ value: 'user',
49
+ },
50
+ {
51
+ name: 'Group',
52
+ value: 'group',
53
+ },
54
+ ],
55
+ default: 'user',
56
+ description: 'Loại thread (user hoặc group)',
57
+ },
58
+ {
59
+ displayName: 'Upload Sources',
60
+ name: 'sources',
61
+ type: 'fixedCollection',
62
+ typeOptions: {
63
+ multipleValues: true,
64
+ },
65
+ placeholder: 'Add Source',
66
+ default: {},
67
+ options: [
68
+ {
69
+ name: 'source',
70
+ displayName: 'Source',
71
+ values: [
72
+ {
73
+ displayName: 'Source Type',
74
+ name: 'type',
75
+ type: 'options',
76
+ options: [
77
+ {
78
+ name: 'File Path',
79
+ value: 'filePath',
80
+ },
81
+ {
82
+ name: 'File URL',
83
+ value: 'fileUrl',
84
+ },
85
+ {
86
+ name: 'Binary Data',
87
+ value: 'binaryData',
88
+ },
89
+ ],
90
+ default: 'filePath',
91
+ description: 'Loại nguồn file để upload',
92
+ },
93
+ {
94
+ displayName: 'File Path',
95
+ name: 'filePath',
96
+ type: 'string',
97
+ default: '',
98
+ displayOptions: {
99
+ show: {
100
+ 'type': ['filePath'],
101
+ },
102
+ },
103
+ description: 'Đường dẫn đến file cần upload',
104
+ },
105
+ {
106
+ displayName: 'File URL',
107
+ name: 'fileUrl',
108
+ type: 'string',
109
+ default: '',
110
+ displayOptions: {
111
+ show: {
112
+ 'type': ['fileUrl'],
113
+ },
114
+ },
115
+ description: 'URL của file cần upload',
116
+ },
117
+ {
118
+ displayName: 'Binary Property',
119
+ name: 'binaryProperty',
120
+ type: 'string',
121
+ default: 'data',
122
+ displayOptions: {
123
+ show: {
124
+ 'type': ['binaryData'],
125
+ },
126
+ },
127
+ description: 'Tên của binary property chứa file data',
128
+ },
129
+ {
130
+ displayName: 'File Name',
131
+ name: 'fileName',
132
+ type: 'string',
133
+ default: '',
134
+ displayOptions: {
135
+ show: {
136
+ 'type': ['binaryData'],
137
+ },
138
+ },
139
+ description: 'Tên file (bắt buộc khi sử dụng binary data)',
140
+ },
141
+ ],
142
+ },
143
+ ],
144
+ description: 'Danh sách các file cần upload',
145
+ },
146
+ ],
147
+ };
148
+ }
149
+ async execute() {
150
+ const returnData = [];
151
+ 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
+ try {
157
+ const zalo = new zca_js_1.Zalo({
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
+ });
173
+ if (!api) {
174
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to initialize Zalo API. Check your credentials.');
175
+ }
176
+ }
177
+ catch (error) {
178
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Zalo login error: ${error.message}`);
179
+ }
180
+ for (let i = 0; i < items.length; i++) {
181
+ try {
182
+ const threadId = this.getNodeParameter('threadId', i);
183
+ const threadTypeStr = this.getNodeParameter('threadType', i);
184
+ const threadType = threadTypeStr === 'user' ? zca_js_1.ThreadType.User : zca_js_1.ThreadType.Group;
185
+ const sourcesData = this.getNodeParameter('sources', i, {});
186
+ if (!sourcesData.source || sourcesData.source.length === 0) {
187
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Cần ít nhất một file để upload');
188
+ }
189
+ const sources = [];
190
+ const tempFiles = [];
191
+ for (const sourceConfig of sourcesData.source) {
192
+ let source;
193
+ if (sourceConfig.type === 'filePath') {
194
+ const filePath = sourceConfig.filePath;
195
+ if (!fs_1.default.existsSync(filePath)) {
196
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `File không tồn tại: ${filePath}`);
197
+ }
198
+ source = filePath;
199
+ }
200
+ else if (sourceConfig.type === 'fileUrl') {
201
+ const fileUrl = sourceConfig.fileUrl;
202
+ const tempFilePath = await (0, helper_1.saveFile)(fileUrl);
203
+ if (!tempFilePath) {
204
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Không thể tải file từ URL: ${fileUrl}`);
205
+ }
206
+ tempFiles.push(tempFilePath);
207
+ source = tempFilePath;
208
+ }
209
+ else if (sourceConfig.type === 'binaryData') {
210
+ const binaryProperty = sourceConfig.binaryProperty;
211
+ const fileName = sourceConfig.fileName;
212
+ if (!fileName) {
213
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'File name là bắt buộc khi sử dụng binary data');
214
+ }
215
+ this.helpers.assertBinaryData(i, binaryProperty);
216
+ const buffer = await this.helpers.getBinaryDataBuffer(i, binaryProperty);
217
+ source = {
218
+ data: buffer,
219
+ filename: fileName,
220
+ metadata: {
221
+ fileName: fileName,
222
+ totalSize: buffer.length,
223
+ }
224
+ };
225
+ }
226
+ sources.push(source);
227
+ }
228
+ 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
+ const uploadResults = await api.uploadAttachment(sources, threadId, threadType);
233
+ for (const tempFile of tempFiles) {
234
+ (0, helper_1.removeFile)(tempFile);
235
+ }
236
+ this.logger.info(`Successfully uploaded ${uploadResults.length} file(s)`);
237
+ returnData.push({
238
+ json: {
239
+ success: true,
240
+ threadId,
241
+ threadType: threadTypeStr,
242
+ uploadResults,
243
+ totalFiles: uploadResults.length,
244
+ },
245
+ });
246
+ }
247
+ catch (error) {
248
+ this.logger.error('Error uploading attachments:', error);
249
+ if (this.continueOnFail()) {
250
+ returnData.push({
251
+ json: {
252
+ success: false,
253
+ error: error.message,
254
+ },
255
+ });
256
+ }
257
+ else {
258
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
259
+ }
260
+ }
261
+ }
262
+ return [returnData];
263
+ }
264
+ }
265
+ exports.ZaloUploadAttachment = ZaloUploadAttachment;
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ZaloUploadAttachmentDescription = void 0;
4
+ exports.ZaloUploadAttachmentDescription = {
5
+ displayName: 'Zalo Upload Attachment',
6
+ name: 'zaloUploadAttachment',
7
+ icon: 'file:../shared/zalo.svg',
8
+ group: ['Zalo'],
9
+ version: 1,
10
+ description: 'Upload attachment (ảnh, video, file) lên Zalo sử dụng kết nối đăng nhập bằng cookie',
11
+ defaults: {
12
+ name: 'Zalo Upload Attachment',
13
+ },
14
+ inputs: ['main'],
15
+ outputs: ['main'],
16
+ credentials: [
17
+ {
18
+ name: 'zaloApi',
19
+ required: true,
20
+ },
21
+ ],
22
+ properties: [
23
+ {
24
+ displayName: 'Thread ID',
25
+ name: 'threadId',
26
+ type: 'string',
27
+ default: '',
28
+ required: true,
29
+ description: 'ID của thread để upload attachment (User ID hoặc Group ID)',
30
+ },
31
+ {
32
+ displayName: 'Type',
33
+ name: 'type',
34
+ type: 'options',
35
+ options: [
36
+ {
37
+ name: 'User',
38
+ value: 0,
39
+ },
40
+ {
41
+ name: 'Group',
42
+ value: 1,
43
+ },
44
+ ],
45
+ default: 0,
46
+ description: 'Loại của thread (user hoặc group)',
47
+ },
48
+ {
49
+ displayName: 'Attachment Source',
50
+ name: 'attachmentSource',
51
+ type: 'options',
52
+ options: [
53
+ {
54
+ name: 'File Path',
55
+ value: 'filePath',
56
+ description: 'Đường dẫn file trên server n8n',
57
+ },
58
+ {
59
+ name: 'URL',
60
+ value: 'url',
61
+ description: 'URL công khai của file',
62
+ },
63
+ {
64
+ name: 'Binary Data',
65
+ value: 'binary',
66
+ description: 'Binary data từ n8n workflow',
67
+ },
68
+ ],
69
+ default: 'filePath',
70
+ description: 'Nguồn của attachment',
71
+ },
72
+ {
73
+ displayName: 'File Path',
74
+ name: 'filePath',
75
+ type: 'string',
76
+ default: '',
77
+ displayOptions: {
78
+ show: {
79
+ attachmentSource: ['filePath'],
80
+ },
81
+ },
82
+ description: 'Đường dẫn tuyệt đối đến file trên server n8n',
83
+ placeholder: '/path/to/your/file.jpg',
84
+ },
85
+ {
86
+ displayName: 'File URL',
87
+ name: 'fileUrl',
88
+ type: 'string',
89
+ default: '',
90
+ displayOptions: {
91
+ show: {
92
+ attachmentSource: ['url'],
93
+ },
94
+ },
95
+ description: 'URL công khai của file cần upload',
96
+ placeholder: 'https://example.com/image.jpg',
97
+ },
98
+ {
99
+ displayName: 'Binary Property',
100
+ name: 'binaryProperty',
101
+ type: 'string',
102
+ default: 'data',
103
+ displayOptions: {
104
+ show: {
105
+ attachmentSource: ['binary'],
106
+ },
107
+ },
108
+ description: 'Tên của binary property chứa file data',
109
+ },
110
+ {
111
+ displayName: 'Filename',
112
+ name: 'filename',
113
+ type: 'string',
114
+ default: '',
115
+ displayOptions: {
116
+ show: {
117
+ attachmentSource: ['binary'],
118
+ },
119
+ },
120
+ description: 'Tên file với extension (ví dụ: image.jpg, document.pdf)',
121
+ placeholder: 'image.jpg',
122
+ },
123
+ {
124
+ displayName: 'Multiple Files',
125
+ name: 'multipleFiles',
126
+ type: 'boolean',
127
+ default: false,
128
+ description: 'Upload nhiều file cùng lúc',
129
+ },
130
+ {
131
+ displayName: 'File Paths (Multiple)',
132
+ name: 'filePaths',
133
+ type: 'string',
134
+ default: '',
135
+ displayOptions: {
136
+ show: {
137
+ attachmentSource: ['filePath'],
138
+ multipleFiles: [true],
139
+ },
140
+ },
141
+ description: 'Danh sách đường dẫn file cách nhau bởi dấu phẩy hoặc xuống dòng',
142
+ placeholder: '/path/to/file1.jpg\n/path/to/file2.pdf\n/path/to/file3.mp4',
143
+ },
144
+ {
145
+ displayName: 'File URLs (Multiple)',
146
+ name: 'fileUrls',
147
+ type: 'string',
148
+ default: '',
149
+ displayOptions: {
150
+ show: {
151
+ attachmentSource: ['url'],
152
+ multipleFiles: [true],
153
+ },
154
+ },
155
+ description: 'Danh sách URL file cách nhau bởi dấu phẩy hoặc xuống dòng',
156
+ placeholder: 'https://example.com/file1.jpg\nhttps://example.com/file2.pdf',
157
+ },
158
+ {
159
+ displayName: 'Binary Properties (Multiple)',
160
+ name: 'binaryProperties',
161
+ type: 'string',
162
+ default: '',
163
+ displayOptions: {
164
+ show: {
165
+ attachmentSource: ['binary'],
166
+ multipleFiles: [true],
167
+ },
168
+ },
169
+ description: 'Tên các binary properties cách nhau bởi dấu phẩy',
170
+ placeholder: 'data1,data2,data3',
171
+ },
172
+ {
173
+ displayName: 'Filenames (Multiple)',
174
+ name: 'filenames',
175
+ type: 'string',
176
+ default: '',
177
+ displayOptions: {
178
+ show: {
179
+ attachmentSource: ['binary'],
180
+ multipleFiles: [true],
181
+ },
182
+ },
183
+ description: 'Tên các file với extension cách nhau bởi dấu phẩy',
184
+ placeholder: 'file1.jpg,file2.pdf,file3.mp4',
185
+ },
186
+ ],
187
+ };
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ZaloUser = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const ZaloNodeProperties_1 = require("../shared/ZaloNodeProperties");
6
+ const ZaloUserDescription_1 = require("./ZaloUserDescription");
7
+ const zalo_helper_1 = require("../utils/zalo.helper");
8
+ let api;
9
+ class ZaloUser {
10
+ constructor() {
11
+ this.description = {
12
+ displayName: 'Zalo User',
13
+ name: 'zaloUser',
14
+ icon: 'file:../shared/zalo.svg',
15
+ group: ['Zalo'],
16
+ version: 1,
17
+ subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
18
+ description: 'Quản lý người dùng Zalo',
19
+ defaults: {
20
+ name: 'Zalo User',
21
+ },
22
+ inputs: ['main'],
23
+ outputs: ['main'],
24
+ credentials: [...ZaloNodeProperties_1.zaloApiCredential],
25
+ properties: [
26
+ ...ZaloNodeProperties_1.zaloSessionProperties,
27
+ {
28
+ displayName: 'Resource',
29
+ name: 'resource',
30
+ type: 'options',
31
+ noDataExpression: true,
32
+ options: [
33
+ {
34
+ name: 'Zalo User',
35
+ value: 'zaloUser',
36
+ },
37
+ ],
38
+ default: 'zaloUser',
39
+ },
40
+ ...ZaloUserDescription_1.zaloUserOperations,
41
+ ...ZaloUserDescription_1.zaloUserFields,
42
+ ],
43
+ };
44
+ }
45
+ async execute() {
46
+ const items = this.getInputData();
47
+ const returnData = [];
48
+
49
+ api = await (0, zalo_helper_1.getZaloApiClient)(this, {});
50
+ if (!api) {
51
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to initialize Zalo API. Check credentials or User ID.');
52
+ }
53
+
54
+ for (let i = 0; i < items.length; i++) {
55
+ try {
56
+ const resource = this.getNodeParameter('resource', i);
57
+ const operation = this.getNodeParameter('operation', i);
58
+ if (resource === 'zaloUser') {
59
+ switch (operation) {
60
+ case 'acceptFriendRequest': {
61
+ const userId = this.getNodeParameter('userId', i);
62
+ const response = await api.acceptFriendRequest(userId);
63
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
64
+ break;
65
+ }
66
+ case 'rejectFriendRequest': {
67
+ const userId = this.getNodeParameter('userId', i);
68
+ const response = await api.rejectFriendRequest(userId);
69
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
70
+ break;
71
+ }
72
+ case 'removeFriend': {
73
+ const userId = this.getNodeParameter('userId', i);
74
+ const response = await api.removeFriend(userId);
75
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
76
+ break;
77
+ }
78
+ case 'getFriendRecommendations': {
79
+ const response = await api.getFriendRecommendations();
80
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
81
+ break;
82
+ }
83
+ case 'sendFriendRequest': {
84
+ const userId = this.getNodeParameter('userId', i);
85
+ const message = this.getNodeParameter('message', i);
86
+ const response = await api.sendFriendRequest(message, userId);
87
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
88
+ break;
89
+ }
90
+ case 'blockUser': {
91
+ const userId = this.getNodeParameter('userId', i);
92
+ const response = await api.blockUser(userId);
93
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
94
+ break;
95
+ }
96
+ case 'unblockUser': {
97
+ const userId = this.getNodeParameter('userId', i);
98
+ const response = await api.unblockUser(userId);
99
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
100
+ break;
101
+ }
102
+ case 'updateProfile': {
103
+ const name = this.getNodeParameter('name', i);
104
+ const dob = this.getNodeParameter('dob', i);
105
+ const gender = this.getNodeParameter('gender', i);
106
+ const response = await api.updateProfile(name, dob, gender);
107
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
108
+ break;
109
+ }
110
+ case 'getUserInfo': {
111
+ const userId = this.getNodeParameter('userId', i);
112
+ const response = await api.getUserInfo(userId);
113
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
114
+ break;
115
+ }
116
+ case 'getAllFriends': {
117
+ const response = await api.getAllFriends();
118
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
119
+ break;
120
+ }
121
+ case 'findUser': {
122
+ const phoneNumber = this.getNodeParameter('phoneNumber', i);
123
+ const response = await api.findUser(phoneNumber);
124
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
125
+ break;
126
+ }
127
+ case 'changeAliasName': {
128
+ const userId = this.getNodeParameter('userId', i);
129
+ const aliasName = this.getNodeParameter('aliasName', i);
130
+ const response = await api.changeFriendAlias(aliasName, userId);
131
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
132
+ break;
133
+ }
134
+ case 'undoMessage': {
135
+ const threadId = this.getNodeParameter('threadId', i);
136
+ const type = this.getNodeParameter('threadType', i);
137
+ const msgId = this.getNodeParameter('msgId', i);
138
+ const cliMsgId = this.getNodeParameter('cliMsgId', i);
139
+ const UndoOptions = {
140
+ msgId: msgId,
141
+ cliMsgId: cliMsgId,
142
+ };
143
+ const response = await api.undo(UndoOptions, threadId, type);
144
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
145
+ break;
146
+ }
147
+ case 'removeUnreadMark': {
148
+ const threadId = this.getNodeParameter('threadId', i);
149
+ const type = this.getNodeParameter('threadType', i);
150
+ const response = await api.removeUnreadMark(threadId, type);
151
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
152
+ break;
153
+ }
154
+ case 'addUnreadMark': {
155
+ const threadId = this.getNodeParameter('threadId', i);
156
+ const type = this.getNodeParameter('threadType', i);
157
+ const response = await api.addUnreadMark(threadId, type);
158
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
159
+ break;
160
+ }
161
+ case 'undoFriendRequest': {
162
+ const userId = this.getNodeParameter('userId', i);
163
+ const response = await api.undoFriendRequest(userId);
164
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
165
+ break;
166
+ }
167
+ case 'getSentFriendRequest': {
168
+ const response = await api.getSentFriendRequest();
169
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
170
+ break;
171
+ }
172
+ case 'getQR': {
173
+ const userId = this.getNodeParameter('userId', i);
174
+ const response = await api.getQR(userId);
175
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
176
+ break;
177
+ }
178
+ case 'fetchAccountInfo': {
179
+ const response = await api.fetchAccountInfo();
180
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
181
+ break;
182
+ }
183
+ case 'getContext': {
184
+ const response = api.getContext();
185
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
186
+ break;
187
+ }
188
+ }
189
+ }
190
+ }
191
+ catch (error) {
192
+ if (this.continueOnFail()) {
193
+ returnData.push({
194
+ json: {
195
+ success: false,
196
+ error: error.message,
197
+ },
198
+ pairedItem: {
199
+ item: i,
200
+ },
201
+ });
202
+ continue;
203
+ }
204
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, {
205
+ itemIndex: i,
206
+ });
207
+ }
208
+ }
209
+ return [returnData];
210
+ }
211
+ }
212
+ exports.ZaloUser = ZaloUser;