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
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Hoạt động độc lập trong môi trường **n8n của bạn** — **khôn
|
|
|
6
6
|
|
|
7
7
|
Đây là một dự án **mã nguồn mở**, chúng tôi khuyến khích cộng đồng cùng đóng góp và phát triển thêm các tính năng mới.
|
|
8
8
|
|
|
9
|
-
## 📱 Liên hệ &
|
|
9
|
+
## 📱 Liên hệ & Báo lỗi
|
|
10
10
|
|
|
11
11
|
**Github:** [codedao12](https://github.com/codedao12)
|
|
12
12
|
**Email:** codedao12@gmail.com
|
|
@@ -17,53 +17,56 @@ Vui lòng liên hệ để yêu cầu tính năng mới hoặc báo lỗi.
|
|
|
17
17
|
|
|
18
18
|
## ✨ Các Tính Năng Chính
|
|
19
19
|
|
|
20
|
-
| 💯 | Tính Năng
|
|
21
|
-
|
|
22
|
-
| 🔑 | **_XÁC THỰC & KẾT NỐI_**
|
|
23
|
-
| ✓ | Đăng nhập bằng Mã QR
|
|
24
|
-
| ✓✓ | Expression Zalo Credential
|
|
25
|
-
| ✓ | Hỗ trợ đăng nhập nhiều tài khoản, proxy
|
|
26
|
-
| ✓✓ | Đăng nhập, thông báo qua telegram
|
|
27
|
-
| ⚡️ | **_TRIGGER_**
|
|
28
|
-
| ✓ | Sự kiện tin nhắn mới (lọc theo từ khóa, nhóm)
|
|
29
|
-
| ✓ | Sự kiện thu hồi tin nhắn, thả cảm xúc
|
|
30
|
-
| ✓ | Sự kiện trong nhóm (tham gia, rời, đổi quyền,...)
|
|
31
|
-
| ✓✓
|
|
32
|
-
| ✓✓
|
|
33
|
-
| ✓✓
|
|
34
|
-
| 💬 | **_NHẮN TIN_**
|
|
35
|
-
| ✓ | Gửi tin nhắn (Văn bản, Ảnh, Sticker)
|
|
36
|
-
| ✓ | Trả lời tin nhắn (Quote)
|
|
37
|
-
| ✓ | Gắn thẻ (Tag) thành viên trong nhóm
|
|
38
|
-
| ✓✓
|
|
39
|
-
| ✓ | Thu hồi tin nhắn đã gửi
|
|
40
|
-
| ✓✓
|
|
41
|
-
| ✓ | Mô phỏng trạng thái "Đang soạn tin..."
|
|
42
|
-
| 👤 | **_QUẢN LÝ TÀI KHOẢN & BẠN BÈ_**
|
|
43
|
-
| ✓ | Gửi / Hủy lời mời kết bạn
|
|
44
|
-
| ✓ | Chấp nhận / Từ chối lời mời kết bạn
|
|
45
|
-
| ✓ | Hủy kết bạn (xóa bạn)
|
|
46
|
-
| ✓ | Lấy danh sách bạn bè & lời mời đã gửi
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
| ✓ |
|
|
51
|
-
|
|
|
52
|
-
| ✓ |
|
|
53
|
-
| ✓ |
|
|
54
|
-
|
|
|
55
|
-
| ✓ |
|
|
56
|
-
|
|
|
57
|
-
| ✓ |
|
|
58
|
-
|
|
|
59
|
-
| ✓ |
|
|
60
|
-
| ✓ |
|
|
61
|
-
|
|
|
62
|
-
| ✓ |
|
|
63
|
-
|
|
|
64
|
-
| ✓ |
|
|
65
|
-
| ✓ |
|
|
66
|
-
|
|
|
20
|
+
| 💯 | Tính Năng |
|
|
21
|
+
|:----:|:----------------------------------------------------|
|
|
22
|
+
| 🔑 | **_XÁC THỰC & KẾT NỐI_** |
|
|
23
|
+
| ✓ | Đăng nhập bằng Mã QR |
|
|
24
|
+
| ✓✓ | Expression Zalo Credential |
|
|
25
|
+
| ✓ | Hỗ trợ đăng nhập nhiều tài khoản, proxy |
|
|
26
|
+
| ✓✓ | Đăng nhập, thông báo qua telegram |
|
|
27
|
+
| ⚡️ | **_TRIGGER_** |
|
|
28
|
+
| ✓ | Sự kiện tin nhắn mới (lọc theo từ khóa, nhóm) |
|
|
29
|
+
| ✓ | Sự kiện thu hồi tin nhắn, thả cảm xúc |
|
|
30
|
+
| ✓ | Sự kiện trong nhóm (tham gia, rời, đổi quyền,...) |
|
|
31
|
+
| ✓✓ | Sự kiện về bạn bè (kết bạn, hủy bạn) |
|
|
32
|
+
| ✓✓ | Sự kiện bạn bè đã xem tin nhắn |
|
|
33
|
+
| ✓✓ | Sự kiện bạn bè đang soạn tin |
|
|
34
|
+
| 💬 | **_NHẮN TIN_** |
|
|
35
|
+
| ✓ | Gửi tin nhắn (Văn bản, Ảnh, Sticker) |
|
|
36
|
+
| ✓ | Trả lời tin nhắn (Quote) |
|
|
37
|
+
| ✓ | Gắn thẻ (Tag) thành viên trong nhóm |
|
|
38
|
+
| ✓✓ | Thả/Gỡ cảm xúc (Reaction) vào tin nhắn |
|
|
39
|
+
| ✓ | Thu hồi tin nhắn đã gửi |
|
|
40
|
+
| ✓✓ | Lấy danh sách tin nhắn cũ |
|
|
41
|
+
| ✓ | Mô phỏng trạng thái "Đang soạn tin..." |
|
|
42
|
+
| 👤 | **_QUẢN LÝ TÀI KHOẢN & BẠN BÈ_** |
|
|
43
|
+
| ✓ | Gửi / Hủy lời mời kết bạn |
|
|
44
|
+
| ✓ | Chấp nhận / Từ chối lời mời kết bạn |
|
|
45
|
+
| ✓ | Hủy kết bạn (xóa bạn) |
|
|
46
|
+
| ✓ | Lấy danh sách bạn bè & lời mời đã gửi |
|
|
47
|
+
| ✓✓ | Xóa tin nhắn của User/Group |
|
|
48
|
+
| ✓✓ | Tắt mở thông báo User/Group |
|
|
49
|
+
| ✓✓ | Cập nhật quyền riêng tư |
|
|
50
|
+
| ✓ | Cập nhật thông tin cá nhân (name, gender, avatar..) |
|
|
51
|
+
| ✓ | Tìm người dùng bằng SĐT hoặc User ID |
|
|
52
|
+
| ✓ | Lấy thông tin chi tiết người dùng |
|
|
53
|
+
| ✓ | Chặn / Bỏ chặn người dùng |
|
|
54
|
+
| 👥 | **_QUẢN LÝ NHÓM_** |
|
|
55
|
+
| ✓ | Tạo nhóm |
|
|
56
|
+
| ✓✓ | Cập nhật cài đặt nhóm |
|
|
57
|
+
| ✓ | Thêm / Xóa thành viên khỏi nhóm |
|
|
58
|
+
| ✓ | Giải tán nhóm |
|
|
59
|
+
| ✓ | Thay đổi tên & ảnh đại diện nhóm |
|
|
60
|
+
| ✓ | Bổ nhiệm quyền Phó nhóm |
|
|
61
|
+
| ✓✓ | Chuyển quyền Trưởng nhóm |
|
|
62
|
+
| ✓ | Lấy danh sách tất cả các nhóm đã tham gia |
|
|
63
|
+
| ✓ | Lấy thông tin nhóm (từ ID hoặc link) |
|
|
64
|
+
| ✓ | Tham gia nhóm bằng link / Rời nhóm |
|
|
65
|
+
| ✓ | Tạo ghi chú (Note) trong nhóm |
|
|
66
|
+
| 🎨 | **_KHÁC_** |
|
|
67
|
+
| ✓ | Tạo bình chọn (Poll) trong nhóm |
|
|
68
|
+
| ✓ | Quản lý thẻ phân loại (Tag) |
|
|
69
|
+
| ✓ | Tìm kiếm sticker |
|
|
67
70
|
|
|
68
71
|
|
|
69
72
|
## 🚀 Hướng Dẫn Cài Đặt
|
|
@@ -159,7 +162,7 @@ Khi bạn quản lý nhiều tài khoản Zalo, mỗi tài khoản sẽ có mộ
|
|
|
159
162
|
<details>
|
|
160
163
|
<summary><b>nhận sự kiện từ Zalo để phản hồi</b></summary>
|
|
161
164
|
|
|
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.
|
|
165
|
+
- **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
166
|
- **Cấu hình**:
|
|
164
167
|
- Chấp nhận hoặc loại trừ các ID nhóm khi nhận sự kiện tin nhắn.
|
|
165
168
|
- Chỉ nhận theo từ khoá hoặc loại trừ khi nhận sự kiện tin nhắn.
|
|
@@ -200,6 +203,7 @@ Khi bạn quản lý nhiều tài khoản Zalo, mỗi tài khoản sẽ có mộ
|
|
|
200
203
|
- **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
204
|
- **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
205
|
- **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).
|
|
206
|
+
- **Lấy tin nhắn cũ:** Lấy danh sách các tin nhắn cũ trong khả năng.
|
|
203
207
|
</details>
|
|
204
208
|
|
|
205
209
|
---
|
|
@@ -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
|
-
// ---
|
|
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
|
-
// ---
|
|
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
|
-
// ---
|
|
226
|
+
// --- Tag ---
|
|
227
227
|
case 'tag':
|
|
228
228
|
switch (operation) {
|
|
229
229
|
case 'list': {
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ZaloGroup = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
-
const
|
|
5
|
+
const zca_js_1 = require("zca-js");
|
|
6
|
+
const ZaloGroupDescription_1 = require("./ZaloGroupDescription");
|
|
6
7
|
const ZaloNodeProperties_1 = require("../shared/ZaloNodeProperties");
|
|
7
8
|
const helper_1 = require("../utils/helper");
|
|
8
9
|
const zalo_helper_1 = require("../utils/zalo.helper");
|
|
@@ -53,16 +54,21 @@ class ZaloGroup {
|
|
|
53
54
|
} catch (e) { return false; }
|
|
54
55
|
});
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
try {
|
|
58
|
+
api = await (0, zalo_helper_1.getZaloApiClient)(this, {needsImageMetadataGetter: needsImageMetadataGetter});
|
|
59
|
+
if (!api) {
|
|
60
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), {}, 'Failed to initialize Zalo API. Check credentials or User ID.');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Zalo login error: ${error.message}`);
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
for (let i = 0; i < items.length; i++) {
|
|
62
68
|
try {
|
|
63
69
|
const resource = this.getNodeParameter('resource', i);
|
|
64
70
|
const operation = this.getNodeParameter('operation', i);
|
|
65
|
-
this.logger.info(`[GROUP] ${operation} - Image ${needsImageMetadataGetter}`)
|
|
71
|
+
// this.logger.info(`[GROUP] ${operation} - Image ${needsImageMetadataGetter}`)
|
|
66
72
|
|
|
67
73
|
if (resource === 'zaloGroup') {
|
|
68
74
|
switch (operation) {
|
|
@@ -98,89 +104,6 @@ class ZaloGroup {
|
|
|
98
104
|
returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
|
|
99
105
|
break;
|
|
100
106
|
}
|
|
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
107
|
case 'changeGroupName': {
|
|
185
108
|
const groupId = this.getNodeParameter('groupId', i);
|
|
186
109
|
const newName = this.getNodeParameter('newName', i);
|
|
@@ -188,43 +111,6 @@ class ZaloGroup {
|
|
|
188
111
|
returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
|
|
189
112
|
break;
|
|
190
113
|
}
|
|
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
114
|
case 'getAllGroups': {
|
|
229
115
|
const response = await api.getAllGroups();
|
|
230
116
|
returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
|
|
@@ -288,21 +174,131 @@ class ZaloGroup {
|
|
|
288
174
|
returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
|
|
289
175
|
break;
|
|
290
176
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
177
|
+
case 'updateGroupSettings' : {
|
|
178
|
+
const groupId = this.getNodeParameter('groupId', i);
|
|
179
|
+
const settings = this.getNodeParameter('settings', i, {});
|
|
180
|
+
const response = await api.updateGroupSettings(settings, groupId);
|
|
181
|
+
returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case 'getGroupMembers': {
|
|
185
|
+
const groupId = this.getNodeParameter('groupId', i);
|
|
186
|
+
const response = await api.getGroupInfo(groupId);
|
|
187
|
+
const groupDetails = response?.gridInfoMap?.[groupId];
|
|
188
|
+
const memberIds = groupDetails?.memVerList || [];
|
|
189
|
+
if (memberIds.length > 0) {
|
|
190
|
+
const membersInfo = await api.getGroupMembersInfo(memberIds);
|
|
191
|
+
const profiles = membersInfo.profiles;
|
|
192
|
+
returnData.push({
|
|
193
|
+
json: {
|
|
194
|
+
success: true,
|
|
195
|
+
memberIds,
|
|
196
|
+
profiles,
|
|
197
|
+
totalMembers: memberIds.length,
|
|
198
|
+
groupInfo: groupDetails
|
|
199
|
+
},
|
|
200
|
+
pairedItem: {
|
|
201
|
+
item: i,
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
returnData.push({
|
|
207
|
+
json: {
|
|
208
|
+
success: true,
|
|
209
|
+
memberIds: [],
|
|
210
|
+
profiles: {},
|
|
211
|
+
totalMembers: 0,
|
|
212
|
+
groupInfo: groupDetails || null
|
|
213
|
+
},
|
|
214
|
+
pairedItem: {
|
|
215
|
+
item: i,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
case 'addUserToGroup': {
|
|
222
|
+
const groupId = this.getNodeParameter('groupId', i);
|
|
223
|
+
const userIdsParam = this.getNodeParameter('userIds', i, '');
|
|
224
|
+
const phoneNumber = this.getNodeParameter('phoneNumber', i, '');
|
|
225
|
+
if (!userIdsParam && !phoneNumber) {
|
|
226
|
+
returnData.push({
|
|
227
|
+
json: {
|
|
228
|
+
success: false,
|
|
229
|
+
error: 'You must provide either User IDs or a Phone Number.'
|
|
230
|
+
},
|
|
231
|
+
pairedItem: {
|
|
232
|
+
item: i
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
const userList = new Set();
|
|
238
|
+
if (userIdsParam) {
|
|
239
|
+
userIdsParam.split(',').map(id => id.trim()).filter(id => id).forEach(id => userList.add(id));
|
|
240
|
+
}
|
|
241
|
+
if (phoneNumber) {
|
|
242
|
+
try {
|
|
243
|
+
const userFound = await api.findUser(phoneNumber);
|
|
244
|
+
if (userFound && userFound.uid) {
|
|
245
|
+
userList.add(userFound.uid);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
this.logger.warn(`User not found for phone number: ${phoneNumber}. Skipping.`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
this.logger.warn(`Failed to find user by phone number ${phoneNumber}: ${error.message}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const finalUserList = Array.from(userList);
|
|
256
|
+
if (finalUserList.length > 0) {
|
|
257
|
+
const response = await api.addUserToGroup(finalUserList, groupId);
|
|
258
|
+
const errorMembers = response.errorMembers || [];
|
|
259
|
+
const allErrorMembers = errorMembers.length === finalUserList.length;
|
|
260
|
+
returnData.push({
|
|
261
|
+
json: { success: true, all_error: allErrorMembers, response: response },
|
|
262
|
+
pairedItem: { item: i }
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
returnData.push({
|
|
267
|
+
json: { success: false, error: 'No valid users to add to the group.' },
|
|
268
|
+
pairedItem: { item: i }
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
case 'changeGroupAvatar': {
|
|
275
|
+
const groupId = this.getNodeParameter('groupId', i);
|
|
276
|
+
const imageUrl = this.getNodeParameter('imageUrl', i);
|
|
277
|
+
let finalAvatarSource;
|
|
278
|
+
let tempFilePath;
|
|
279
|
+
try {
|
|
280
|
+
if (!imageUrl) {
|
|
281
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Image URL is required for changing group avatar.', { itemIndex: i });
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
new URL(imageUrl);
|
|
285
|
+
tempFilePath = await (0, helper_1.saveFile)(imageUrl);
|
|
286
|
+
finalAvatarSource = tempFilePath;
|
|
287
|
+
}
|
|
288
|
+
catch (urlError) {
|
|
289
|
+
finalAvatarSource = imageUrl;
|
|
290
|
+
}
|
|
291
|
+
const response = await api.changeGroupAvatar(finalAvatarSource, groupId);
|
|
292
|
+
returnData.push({ json: { success: true, response }, pairedItem: { item: i } });
|
|
293
|
+
}
|
|
294
|
+
finally {
|
|
295
|
+
if (tempFilePath) {
|
|
296
|
+
await (0, helper_1.removeFile)(tempFilePath);
|
|
297
|
+
this.logger.info(`Successfully removed temporary avatar file: ${tempFilePath}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
306
302
|
}
|
|
307
303
|
}
|
|
308
304
|
}
|