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,309 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ZaloGroup = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const ZaloGroupDescription_1 = require("./ZaloGroupDescription"); // eslint-disable-line n8n-nodes-base/node-class-description-icon-not-svg
6
+ const ZaloNodeProperties_1 = require("../shared/ZaloNodeProperties");
7
+ const helper_1 = require("../utils/helper");
8
+ const zalo_helper_1 = require("../utils/zalo.helper");
9
+ let api;
10
+ class ZaloGroup {
11
+ constructor() {
12
+ this.description = {
13
+ displayName: 'Zalo Group',
14
+ name: 'zaloGroup',
15
+ icon: 'file:../shared/zalo.svg',
16
+ group: ['Zalo'],
17
+ version: 1,
18
+ subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
19
+ description: 'Quản lý nhóm Zalo',
20
+ defaults: {
21
+ name: 'Zalo Group',
22
+ },
23
+ inputs: ['main'],
24
+ outputs: ['main'],
25
+ credentials: [...ZaloNodeProperties_1.zaloApiCredential],
26
+ properties: [
27
+ ...ZaloNodeProperties_1.zaloSessionProperties,
28
+ {
29
+ displayName: 'Resource',
30
+ name: 'resource',
31
+ type: 'options',
32
+ noDataExpression: true,
33
+ options: [
34
+ {
35
+ name: 'Group',
36
+ value: 'zaloGroup',
37
+ },
38
+ ],
39
+ default: 'zaloGroup',
40
+ },
41
+ ...ZaloGroupDescription_1.zaloGroupOperations,
42
+ ...ZaloGroupDescription_1.zaloGroupFields,
43
+ ],
44
+ };
45
+ }
46
+ async execute() {
47
+ const items = this.getInputData();
48
+ const returnData = [];
49
+
50
+ const needsImageMetadataGetter = items.some((_, i) => {
51
+ try {
52
+ return this.getNodeParameter('operation', i) === 'changeGroupAvatar';
53
+ } catch (e) { return false; }
54
+ });
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.');
59
+ }
60
+
61
+ for (let i = 0; i < items.length; i++) {
62
+ try {
63
+ const resource = this.getNodeParameter('resource', i);
64
+ const operation = this.getNodeParameter('operation', i);
65
+ this.logger.info(`[GROUP] ${operation} - Image ${needsImageMetadataGetter}`)
66
+
67
+ if (resource === 'zaloGroup') {
68
+ switch (operation) {
69
+ case 'createGroup': {
70
+ const groupName = this.getNodeParameter('groupName', i);
71
+ const userIds = this.getNodeParameter('userIds', i);
72
+ const userList = userIds.split(',');
73
+ const createGroupResponse = await api.createGroup({ name: groupName, members: userList });
74
+ let finalResponse = { ...createGroupResponse };
75
+ if (createGroupResponse && createGroupResponse.groupId) {
76
+ try {
77
+ const groupLinkResponse = await api.getGroupLinkDetail(createGroupResponse.groupId);
78
+ finalResponse = { ...finalResponse, ...groupLinkResponse };
79
+ }
80
+ catch (linkError) {
81
+ this.logger.warn(`Could not fetch group link for new group ${createGroupResponse.groupId}: ${linkError.message}`);
82
+ }
83
+ }
84
+ returnData.push({ json: { success: true, response: finalResponse }, pairedItem: { item: i } });
85
+ break;
86
+ }
87
+ case 'getGroupInfo': {
88
+ const groupId = this.getNodeParameter('groupId', i);
89
+ const response = await api.getGroupInfo(groupId);
90
+ const groupInfo = response.gridInfoMap[groupId];
91
+ returnData.push({ json: { success: true, response: groupInfo }, pairedItem: { item: i } });
92
+ break;
93
+ }
94
+ case 'addGroupDeputy': {
95
+ const groupId = this.getNodeParameter('groupId', i);
96
+ const userId = this.getNodeParameter('userId', i);
97
+ const response = await api.addGroupDeputy(groupId, userId);
98
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
99
+ break;
100
+ }
101
+ case 'addUserToGroup': {
102
+ const groupId = this.getNodeParameter('groupId', i);
103
+ const userIdsParam = this.getNodeParameter('userIds', i, '');
104
+ const phoneNumber = this.getNodeParameter('phoneNumber', i, '');
105
+ if (!userIdsParam && !phoneNumber) {
106
+ returnData.push({
107
+ json: {
108
+ success: false,
109
+ error: 'You must provide either User IDs or a Phone Number.'
110
+ },
111
+ pairedItem: {
112
+ item: i
113
+ }
114
+ });
115
+ }
116
+ else {
117
+ const userList = new Set();
118
+ if (userIdsParam) {
119
+ userIdsParam.split(',').map(id => id.trim()).filter(id => id).forEach(id => userList.add(id));
120
+ }
121
+ if (phoneNumber) {
122
+ try {
123
+ const userFound = await api.findUser(phoneNumber);
124
+ if (userFound && userFound.uid) {
125
+ userList.add(userFound.uid);
126
+ }
127
+ else {
128
+ this.logger.warn(`User not found for phone number: ${phoneNumber}. Skipping.`);
129
+ }
130
+ }
131
+ catch (error) {
132
+ this.logger.warn(`Failed to find user by phone number ${phoneNumber}: ${error.message}`);
133
+ }
134
+ }
135
+ const finalUserList = Array.from(userList);
136
+ if (finalUserList.length > 0) {
137
+ const response = await api.addUserToGroup(finalUserList, groupId);
138
+ const errorMembers = response.errorMembers || [];
139
+ const allErrorMembers = errorMembers.length === finalUserList.length;
140
+ returnData.push({
141
+ json: { success: true, all_error: allErrorMembers, response: response },
142
+ pairedItem: { item: i }
143
+ });
144
+ }
145
+ else {
146
+ returnData.push({
147
+ json: { success: false, error: 'No valid users to add to the group.' },
148
+ pairedItem: { item: i }
149
+ });
150
+ }
151
+ }
152
+ break;
153
+ }
154
+ case 'changeGroupAvatar': {
155
+ const groupId = this.getNodeParameter('groupId', i);
156
+ const imageUrl = this.getNodeParameter('imageUrl', i);
157
+ let finalAvatarSource;
158
+ let tempFilePath;
159
+ try {
160
+ if (!imageUrl) {
161
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Image URL is required for changing group avatar.', { itemIndex: i });
162
+ }
163
+ try {
164
+ new URL(imageUrl);
165
+ this.logger.info(`Downloading image from URL for avatar: ${imageUrl}`);
166
+ tempFilePath = await (0, helper_1.saveFile)(imageUrl);
167
+ finalAvatarSource = tempFilePath;
168
+ }
169
+ catch (urlError) {
170
+ this.logger.info(`Treating image URL as local file path for avatar: ${imageUrl}`);
171
+ finalAvatarSource = imageUrl;
172
+ }
173
+ const response = await api.changeGroupAvatar(finalAvatarSource, groupId);
174
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
175
+ }
176
+ finally {
177
+ if (tempFilePath) {
178
+ await (0, helper_1.removeFile)(tempFilePath);
179
+ this.logger.info(`Successfully removed temporary avatar file: ${tempFilePath}`);
180
+ }
181
+ }
182
+ break;
183
+ }
184
+ case 'changeGroupName': {
185
+ const groupId = this.getNodeParameter('groupId', i);
186
+ const newName = this.getNodeParameter('newName', i);
187
+ const response = await api.changeGroupName(newName, groupId);
188
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
189
+ break;
190
+ }
191
+ case 'getGroupMembers': {
192
+ const groupId = this.getNodeParameter('groupId', i);
193
+ const response = await api.getGroupInfo(groupId);
194
+ const groupDetails = response?.gridInfoMap?.[groupId];
195
+ const memberIds = groupDetails?.memVerList || [];
196
+ if (memberIds.length > 0) {
197
+ const membersInfo = await api.getGroupMembersInfo(memberIds);
198
+ const profiles = membersInfo.profiles;
199
+ returnData.push({
200
+ json: {
201
+ success: true,
202
+ memberIds,
203
+ profiles,
204
+ totalMembers: memberIds.length,
205
+ groupInfo: groupDetails
206
+ },
207
+ pairedItem: {
208
+ item: i,
209
+ },
210
+ });
211
+ }
212
+ else {
213
+ returnData.push({
214
+ json: {
215
+ success: true,
216
+ memberIds: [],
217
+ profiles: {},
218
+ totalMembers: 0,
219
+ groupInfo: groupDetails || null
220
+ },
221
+ pairedItem: {
222
+ item: i,
223
+ },
224
+ });
225
+ }
226
+ break;
227
+ }
228
+ case 'getAllGroups': {
229
+ const response = await api.getAllGroups();
230
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
231
+ break;
232
+ }
233
+ case 'removeUserFromGroup': {
234
+ const groupId = this.getNodeParameter('groupId', i);
235
+ const userIds = this.getNodeParameter('userIds', i);
236
+ const userList = userIds.split(',');
237
+ const response = await api.removeUserFromGroup(userList, groupId);
238
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
239
+ break;
240
+ }
241
+ case 'createNote': {
242
+ const groupId = this.getNodeParameter('groupId', i);
243
+ const content = this.getNodeParameter('content', i);
244
+ const pinAct = this.getNodeParameter('pinAct', i);
245
+ const options = {
246
+ title: content,
247
+ pinAct: pinAct,
248
+ };
249
+ const response = await api.createNote(options, groupId);
250
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
251
+ break;
252
+ }
253
+ case 'getGroupLinkDetail': {
254
+ const groupId = this.getNodeParameter('groupId', i);
255
+ const response = await api.getGroupLinkDetail(groupId);
256
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
257
+ break;
258
+ }
259
+ case 'changeGroupOwner': {
260
+ const groupId = this.getNodeParameter('groupId', i);
261
+ const memberId = this.getNodeParameter('userId', i);
262
+ const response = await api.changeGroupOwner(memberId, groupId);
263
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
264
+ break;
265
+ }
266
+ case 'joinGroupLink': {
267
+ const groupLink = this.getNodeParameter('groupLink', i);
268
+ const response = await api.joinGroupLink(groupLink);
269
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
270
+ break;
271
+ }
272
+ case 'leaveGroup': {
273
+ const groupId = this.getNodeParameter('groupId', i);
274
+ const silent = this.getNodeParameter('silent', i, false);
275
+ const response = await api.leaveGroup(groupId, silent);
276
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
277
+ break;
278
+ }
279
+ case 'disperseGroup': {
280
+ const groupId = this.getNodeParameter('groupId', i);
281
+ const response = await api.disperseGroup(groupId);
282
+ returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
283
+ break;
284
+ }
285
+ }
286
+ }
287
+ }
288
+ catch (error) {
289
+ if (this.continueOnFail()) {
290
+ returnData.push({
291
+ json: {
292
+ success: false,
293
+ error: error.message,
294
+ },
295
+ pairedItem: {
296
+ item: i,
297
+ },
298
+ });
299
+ continue;
300
+ }
301
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, {
302
+ itemIndex: i,
303
+ });
304
+ }
305
+ }
306
+ return [returnData];
307
+ }
308
+ }
309
+ exports.ZaloGroup = ZaloGroup;
@@ -0,0 +1,318 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.zaloGroupFields = exports.zaloGroupOperations = void 0;
4
+ exports.zaloGroupOperations = [
5
+ {
6
+ displayName: 'Operation',
7
+ name: 'operation',
8
+ type: 'options',
9
+ noDataExpression: true,
10
+ displayOptions: {
11
+ show: {
12
+ resource: ['zaloGroup'],
13
+ },
14
+ },
15
+ options: [
16
+ {
17
+ name: 'Lấy Tất Cả Nhóm',
18
+ value: 'getAllGroups',
19
+ description: 'Lấy danh sách tất cả các nhóm',
20
+ action: 'Lấy Tất Cả Nhóm',
21
+ },
22
+ {
23
+ name: 'Lấy Thông Tin Nhóm',
24
+ value: 'getGroupInfo',
25
+ description: 'Lấy thông tin của một nhóm',
26
+ action: 'Lấy Thông Tin Nhóm',
27
+ },
28
+ {
29
+ name: 'Lấy Danh Sách Thành Viên',
30
+ value: 'getGroupMembers',
31
+ description: 'Lấy danh sách thành viên của nhóm',
32
+ action: 'Lấy Danh Sách Thành Viên',
33
+ },
34
+ {
35
+ name: 'Tạo Nhóm',
36
+ value: 'createGroup',
37
+ description: 'Tạo một nhóm mới',
38
+ action: 'Tạo Nhóm',
39
+ },
40
+ {
41
+ name: 'Chuyển Quyền Chủ Nhóm',
42
+ value: 'changeGroupOwner',
43
+ description: 'Đổi chủ sở hữu của một nhóm',
44
+ action: 'Chuyển Quyền Chủ Nhóm',
45
+ },
46
+ {
47
+ name: 'Thêm Phó Nhóm',
48
+ value: 'addGroupDeputy',
49
+ description: 'Thêm phó nhóm cho một nhóm',
50
+ action: 'Thêm Phó Nhóm',
51
+ },
52
+ {
53
+ name: 'Thêm Thành Viên Vào Nhóm',
54
+ value: 'addUserToGroup',
55
+ description: 'Thêm thành viên vào nhóm',
56
+ action: 'Thêm Thành Viên Vào Nhóm',
57
+ },
58
+ {
59
+ name: 'Đổi Avatar Nhóm',
60
+ value: 'changeGroupAvatar',
61
+ description: 'Đổi avatar của nhóm',
62
+ action: 'Đổi Avatar Nhóm',
63
+ },
64
+ {
65
+ name: 'Đổi Tên Nhóm',
66
+ value: 'changeGroupName',
67
+ description: 'Đổi tên của nhóm',
68
+ action: 'Đổi Tên Nhóm',
69
+ },
70
+ {
71
+ name: 'Tạo Ghi Chú Nhóm',
72
+ value: 'createNote',
73
+ description: 'Tạo ghi chú trong nhóm',
74
+ action: 'Tạo Ghi Chú Nhóm',
75
+ },
76
+ {
77
+ name: 'Lấy Link Nhóm',
78
+ value: 'getGroupLinkDetail',
79
+ description: 'Lấy Link Tham Gia Nhóm',
80
+ action: 'Lấy Link Mời Nhóm',
81
+ },
82
+ {
83
+ name: 'Xóa Thành Viên Khỏi Nhóm',
84
+ value: 'removeUserFromGroup',
85
+ description: 'Xóa thành ra viên khỏi nhóm',
86
+ action: 'Xóa Thành Viên Khỏi Nhóm',
87
+ },
88
+ {
89
+ name: 'Tham Gia Bằng Link Nhóm',
90
+ value: 'joinGroupLink',
91
+ description: 'Tham gia một nhóm bằng link mời',
92
+ action: 'Tham Gia Bằng Link Nhóm',
93
+ },
94
+ {
95
+ name: 'Rời Khỏi Nhóm',
96
+ value: 'leaveGroup',
97
+ description: 'Rời khỏi một nhóm',
98
+ action: 'Rời Khỏi Nhóm',
99
+ },
100
+ {
101
+ name: 'Giải Tán Nhóm',
102
+ value: 'disperseGroup',
103
+ description: 'Giải tán một nhóm. Chỉ chủ nhóm mới có thể thực hiện.',
104
+ action: 'Giải Tán Nhóm',
105
+ },
106
+ ],
107
+ default: 'createGroup',
108
+ },
109
+ ];
110
+ exports.zaloGroupFields = [
111
+ {
112
+ displayName: 'Tên Nhóm',
113
+ name: 'groupName',
114
+ type: 'string',
115
+ required: true,
116
+ default: '',
117
+ displayOptions: {
118
+ show: {
119
+ resource: ['zaloGroup'],
120
+ operation: ['createGroup'],
121
+ },
122
+ },
123
+ description: 'Tên của nhóm mới',
124
+ },
125
+ {
126
+ displayName: 'Group ID',
127
+ name: 'groupId',
128
+ type: 'string',
129
+ required: true,
130
+ default: '',
131
+ displayOptions: {
132
+ show: {
133
+ resource: ['zaloGroup'],
134
+ operation: [
135
+ 'getGroupInfo',
136
+ 'addGroupDeputy',
137
+ 'addUserToGroup',
138
+ 'changeGroupAvatar',
139
+ 'changeGroupName',
140
+ 'getGroupMembers',
141
+ 'removeUserFromGroup',
142
+ 'createNote',
143
+ 'getGroupLinkDetail',
144
+ 'changeGroupOwner',
145
+ 'leaveGroup',
146
+ 'disperseGroup',
147
+ ],
148
+ },
149
+ },
150
+ description: 'ID nhóm',
151
+ },
152
+ {
153
+ displayName: 'User ID Người Dùng',
154
+ name: 'userId',
155
+ type: 'string',
156
+ required: true,
157
+ default: '',
158
+ displayOptions: {
159
+ show: {
160
+ resource: ['zaloGroup'],
161
+ operation: ['addGroupDeputy', 'changeGroupOwner'],
162
+ },
163
+ },
164
+ description: 'ID của người dùng',
165
+ },
166
+ {
167
+ displayName: 'Danh Sách User ID',
168
+ name: 'userIds',
169
+ type: 'string',
170
+ required: true,
171
+ default: '',
172
+ displayOptions: {
173
+ show: {
174
+ resource: ['zaloGroup'],
175
+ operation: ['removeUserFromGroup', 'createGroup'],
176
+ },
177
+ },
178
+ description: 'Danh sách userId (phân cách bằng dấu phẩy: id1,id2,id3)',
179
+ },
180
+ {
181
+ displayName: 'Danh Sách User ID',
182
+ name: 'userIds',
183
+ type: 'string',
184
+ required: false,
185
+ default: '',
186
+ displayOptions: {
187
+ show: {
188
+ resource: ['zaloGroup'],
189
+ operation: ['addUserToGroup'],
190
+ },
191
+ },
192
+ description: 'Danh sách userId (phân cách bằng dấu phẩy: id1,id2,id3)',
193
+ },
194
+ {
195
+ displayName: 'Số Điện Thoại Người Dùng',
196
+ name: 'phoneNumber',
197
+ type: 'string',
198
+ required: false,
199
+ default: '',
200
+ displayOptions: {
201
+ show: {
202
+ resource: ['zaloGroup'],
203
+ operation: ['addUserToGroup'],
204
+ },
205
+ },
206
+ description: 'Có thể để trống',
207
+ },
208
+ {
209
+ displayName: 'URL Ảnh',
210
+ name: 'imageUrl',
211
+ type: 'string',
212
+ required: true,
213
+ default: '',
214
+ displayOptions: {
215
+ show: {
216
+ resource: ['zaloGroup'],
217
+ operation: ['changeGroupAvatar'],
218
+ },
219
+ },
220
+ description: 'URL của ảnh đại diện mới',
221
+ },
222
+ {
223
+ displayName: 'Tên Mới',
224
+ name: 'newName',
225
+ type: 'string',
226
+ required: true,
227
+ default: '',
228
+ displayOptions: {
229
+ show: {
230
+ resource: ['zaloGroup'],
231
+ operation: ['changeGroupName'],
232
+ },
233
+ },
234
+ description: 'Tên mới của nhóm',
235
+ },
236
+ {
237
+ displayName: 'Nội Dung Ghi Chú',
238
+ name: 'content',
239
+ type: 'string',
240
+ required: true,
241
+ default: '',
242
+ displayOptions: {
243
+ show: {
244
+ resource: ['zaloGroup'],
245
+ operation: ['createNote'],
246
+ },
247
+ },
248
+ description: 'Nội dung của ghi chú',
249
+ },
250
+ {
251
+ displayName: 'Ghim Ghi Chú',
252
+ name: 'pinAct',
253
+ type: 'boolean',
254
+ required: true,
255
+ default: false,
256
+ displayOptions: {
257
+ show: {
258
+ resource: ['zaloGroup'],
259
+ operation: ['createNote'],
260
+ },
261
+ },
262
+ description: 'Ghim ghi chú lên đầu nhóm',
263
+ },
264
+ {
265
+ displayName: 'Truyền UserId hoặc Phone hoặc cả 2',
266
+ name: 'noticeAddUserToGroup',
267
+ type: 'notice',
268
+ default: '',
269
+ displayOptions: {
270
+ show: {
271
+ resource: ['zaloGroup'],
272
+ operation: ['addUserToGroup'],
273
+ },
274
+ },
275
+ },
276
+ {
277
+ displayName: 'Rời Nhóm Trong Im Lặng',
278
+ name: 'silent',
279
+ type: 'boolean',
280
+ required: false,
281
+ default: false,
282
+ displayOptions: {
283
+ show: {
284
+ resource: ['zaloGroup'],
285
+ operation: ['leaveGroup'],
286
+ },
287
+ },
288
+ description: 'Nếu được bật, sẽ không có thông báo trong nhóm về việc bạn rời đi',
289
+ },
290
+ {
291
+ displayName: 'Link Mời Nhóm',
292
+ name: 'groupLink',
293
+ type: 'string',
294
+ required: true,
295
+ default: '',
296
+ displayOptions: {
297
+ show: {
298
+ resource: ['zaloGroup'],
299
+ operation: ['joinGroupLink'],
300
+ },
301
+ },
302
+ description: 'Link để tham gia nhóm (ví dụ: https://zalo.me/g/xxxxxx)',
303
+ },
304
+ // {
305
+ // displayName: 'Giới Hạn',
306
+ // name: 'limit',
307
+ // type: 'number',
308
+ // default: 50,
309
+ // required: true,
310
+ // displayOptions: {
311
+ // show: {
312
+ // resource: ['zaloGroup'],
313
+ // operation: [],
314
+ // },
315
+ // },
316
+ // description: 'Số lượng tối đa cần lấy',
317
+ // },
318
+ ];