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/nodes/utils/helper.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.saveFile = saveFile;
|
|
7
7
|
exports.removeFile = removeFile;
|
|
8
|
+
exports.parseHtmlToStyles = parseHtmlToStyles;
|
|
8
9
|
const axios_1 = __importDefault(require("axios"));
|
|
9
10
|
const fs_1 = __importDefault(require("fs"));
|
|
10
11
|
const os_1 = __importDefault(require("os"));
|
|
@@ -39,3 +40,150 @@ function removeFile(filePath) {
|
|
|
39
40
|
console.error('Lỗi khi xoá file:', error);
|
|
40
41
|
}
|
|
41
42
|
}
|
|
43
|
+
function parseHtmlToStyles(html) {
|
|
44
|
+
var _a, _b;
|
|
45
|
+
let workingHtml = html
|
|
46
|
+
.replace(/"/g, '"')
|
|
47
|
+
.replace(/'/g, "'")
|
|
48
|
+
.replace(/</g, '<')
|
|
49
|
+
.replace(/>/g, '>')
|
|
50
|
+
.replace(/&/g, '&')
|
|
51
|
+
.replace(/ /g, ' ');
|
|
52
|
+
workingHtml = workingHtml.replace(/<a[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>/gi, '$2');
|
|
53
|
+
workingHtml = workingHtml.replace(/<br\s*\/?>/gi, '___BR___');
|
|
54
|
+
workingHtml = workingHtml.replace(/>\s+</g, '><');
|
|
55
|
+
workingHtml = workingHtml.replace(/___BR___/g, '<br>');
|
|
56
|
+
const styles = [];
|
|
57
|
+
const tagMappings = {
|
|
58
|
+
'b': 'b',
|
|
59
|
+
'strong': 'b',
|
|
60
|
+
'i': 'i',
|
|
61
|
+
'em': 'i',
|
|
62
|
+
'u': 'u',
|
|
63
|
+
's': 's',
|
|
64
|
+
'strike': 's',
|
|
65
|
+
'del': 's',
|
|
66
|
+
'small': 'f_13',
|
|
67
|
+
'big': 'f_18',
|
|
68
|
+
'red': 'c_db342e',
|
|
69
|
+
'orange': 'c_f27806',
|
|
70
|
+
'yellow': 'c_f7b503',
|
|
71
|
+
'green': 'c_15a85f',
|
|
72
|
+
};
|
|
73
|
+
const openTags = [];
|
|
74
|
+
let cleanText = '';
|
|
75
|
+
let htmlIndex = 0;
|
|
76
|
+
let textIndex = 0;
|
|
77
|
+
while (htmlIndex < workingHtml.length) {
|
|
78
|
+
const char = workingHtml[htmlIndex];
|
|
79
|
+
if (char === '<') {
|
|
80
|
+
const tagEndIndex = workingHtml.indexOf('>', htmlIndex);
|
|
81
|
+
if (tagEndIndex === -1) {
|
|
82
|
+
cleanText += char;
|
|
83
|
+
textIndex++;
|
|
84
|
+
htmlIndex++;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const tagContent = workingHtml.substring(htmlIndex + 1, tagEndIndex);
|
|
88
|
+
const isClosingTag = tagContent.startsWith('/');
|
|
89
|
+
const tagName = (isClosingTag ? tagContent.substring(1) : tagContent.split(/\s/)[0]).toLowerCase();
|
|
90
|
+
if (tagName === 'br') {
|
|
91
|
+
cleanText += '\n';
|
|
92
|
+
textIndex++;
|
|
93
|
+
}
|
|
94
|
+
else if (tagName === 'p') {
|
|
95
|
+
if (isClosingTag) {
|
|
96
|
+
cleanText += '\n\n';
|
|
97
|
+
textIndex += 2;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else if (tagName === 'div') {
|
|
101
|
+
if (isClosingTag) {
|
|
102
|
+
cleanText += '\n';
|
|
103
|
+
textIndex++;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else if (tagName.match(/^h[1-6]$/)) {
|
|
107
|
+
if (isClosingTag) {
|
|
108
|
+
cleanText += '\n';
|
|
109
|
+
textIndex++;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else if (tagName === 'li') {
|
|
113
|
+
if (!isClosingTag) {
|
|
114
|
+
cleanText += '• ';
|
|
115
|
+
textIndex += 2;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
cleanText += '\n';
|
|
119
|
+
textIndex++;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else if (tagName === 'ul' || tagName === 'ol') {
|
|
123
|
+
if (!isClosingTag) {
|
|
124
|
+
cleanText += '\n';
|
|
125
|
+
textIndex++;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
cleanText += '\n';
|
|
129
|
+
textIndex++;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const styleType = tagMappings[tagName];
|
|
134
|
+
if (styleType) {
|
|
135
|
+
if (isClosingTag) {
|
|
136
|
+
for (let i = openTags.length - 1; i >= 0; i--) {
|
|
137
|
+
if (openTags[i].tagName === tagName) {
|
|
138
|
+
const openTag = openTags[i];
|
|
139
|
+
if (textIndex > openTag.startPos) {
|
|
140
|
+
styles.push({
|
|
141
|
+
st: openTag.styleType,
|
|
142
|
+
start: openTag.startPos,
|
|
143
|
+
end: textIndex
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
openTags.splice(i, 1);
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
openTags.push({
|
|
153
|
+
tagName: tagName,
|
|
154
|
+
styleType: styleType,
|
|
155
|
+
startPos: textIndex
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
htmlIndex = tagEndIndex + 1;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
cleanText += char;
|
|
164
|
+
textIndex++;
|
|
165
|
+
htmlIndex++;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
for (const openTag of openTags) {
|
|
169
|
+
if (textIndex > openTag.startPos) {
|
|
170
|
+
styles.push({
|
|
171
|
+
st: openTag.styleType,
|
|
172
|
+
start: openTag.startPos,
|
|
173
|
+
end: textIndex
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
cleanText = cleanText
|
|
178
|
+
.replace(/\n\s*\n\s*\n/g, '\n\n')
|
|
179
|
+
.replace(/[ \t]+/g, ' ');
|
|
180
|
+
const trimStart = ((_b = (_a = cleanText.match(/^\s*/)) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.length) || 0;
|
|
181
|
+
cleanText = cleanText.replace(/^\s+|\s+$/g, '');
|
|
182
|
+
if (trimStart > 0) {
|
|
183
|
+
for (const style of styles) {
|
|
184
|
+
style.start = Math.max(0, style.start - trimStart);
|
|
185
|
+
style.end = Math.max(style.start, style.end - trimStart);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return { cleanText, styles };
|
|
189
|
+
}
|
|
@@ -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
|
|
|
@@ -65,12 +67,17 @@ async function persistDb() {
|
|
|
65
67
|
async function imageMetadataGetter(filePath) {
|
|
66
68
|
try {
|
|
67
69
|
const stats = await fs_1.promises.stat(filePath);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
try {
|
|
71
|
+
const dimensions = (0, image_size_1.default)(filePath);
|
|
72
|
+
return {
|
|
73
|
+
height: dimensions.height,
|
|
74
|
+
width: dimensions.width,
|
|
75
|
+
size: stats.size,
|
|
76
|
+
};
|
|
77
|
+
} catch (err) {
|
|
78
|
+
// Nếu không phải ảnh (ví dụ mp4), trả về size thực tế để API xử lý tiếp
|
|
79
|
+
return { height: 0, width: 0, size: stats.size };
|
|
80
|
+
}
|
|
74
81
|
}
|
|
75
82
|
catch (e) {
|
|
76
83
|
if (e.code === 'ENOENT' || e.code === 'EACCES') {
|
|
@@ -79,6 +86,7 @@ async function imageMetadataGetter(filePath) {
|
|
|
79
86
|
else {
|
|
80
87
|
console.log(`Could not get metadata for file at path: ${filePath}. It might not be a valid image file. Error: ${e.message}`);
|
|
81
88
|
}
|
|
89
|
+
return { height: 0, width: 0, size: 0 };
|
|
82
90
|
}
|
|
83
91
|
}
|
|
84
92
|
exports.imageMetadataGetter = imageMetadataGetter;
|
|
@@ -86,8 +94,8 @@ exports.imageMetadataGetter = imageMetadataGetter;
|
|
|
86
94
|
async function getZaloApiClient(node, options = {}) {
|
|
87
95
|
const useSession = node.getNodeParameter('useSession', 0, false);
|
|
88
96
|
const userId = node.getNodeParameter('connectToId', 0, '');
|
|
89
|
-
const { needsImageMetadataGetter = false, selfListen = false } = options;
|
|
90
|
-
let cookie, imei, userAgent, sessionInfo = null, actualZaloId = '';
|
|
97
|
+
const { needsImageMetadataGetter = false, selfListen = false, logging = false } = options;
|
|
98
|
+
let cookie, imei, userAgent, sessionInfo, proxy = null, actualZaloId = '';
|
|
91
99
|
if (useSession && userId) {
|
|
92
100
|
try {
|
|
93
101
|
await initDb();
|
|
@@ -125,6 +133,7 @@ async function getZaloApiClient(node, options = {}) {
|
|
|
125
133
|
cookie = sessionData.cookie;
|
|
126
134
|
imei = sessionData.imei;
|
|
127
135
|
userAgent = sessionData.userAgent;
|
|
136
|
+
proxy = sessionData.proxy;
|
|
128
137
|
}
|
|
129
138
|
catch (e) {
|
|
130
139
|
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 +153,33 @@ async function getZaloApiClient(node, options = {}) {
|
|
|
144
153
|
cookie = JSON.parse(zaloCred.cookie);
|
|
145
154
|
imei = zaloCred.imei;
|
|
146
155
|
userAgent = zaloCred.userAgent;
|
|
156
|
+
proxy = zaloCred.proxy;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const zaloOptions = {
|
|
160
|
+
logging,
|
|
161
|
+
selfListen,
|
|
162
|
+
settings: {
|
|
163
|
+
features: {
|
|
164
|
+
socket: {
|
|
165
|
+
close_and_retry_codes: [1006, 1000, 3000, 3003],
|
|
166
|
+
retries: {
|
|
167
|
+
"1006": {
|
|
168
|
+
max: 10,
|
|
169
|
+
times: [5000, 10000, 30000],
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
if (proxy) {
|
|
177
|
+
zaloOptions.agent = new https_proxy_agent_1.HttpsProxyAgent(proxy);
|
|
178
|
+
zaloOptions.polyfill = node_fetch_1.default;
|
|
147
179
|
}
|
|
148
|
-
const zaloOptions = {};
|
|
149
180
|
if (needsImageMetadataGetter) {
|
|
150
181
|
zaloOptions.imageMetadataGetter = imageMetadataGetter;
|
|
151
182
|
}
|
|
152
|
-
if (selfListen) {
|
|
153
|
-
zaloOptions.selfListen = selfListen;
|
|
154
|
-
}
|
|
155
183
|
const zalo = new zca_js_1.Zalo(zaloOptions);
|
|
156
184
|
return zalo.login({ cookie, imei, userAgent });
|
|
157
185
|
}
|
|
@@ -242,7 +270,7 @@ exports.deleteSessionByUserId = deleteSessionByUserId;
|
|
|
242
270
|
|
|
243
271
|
async function sendToTelegram({ token, chatId, text, binaryData, fileName, caption, logger, }) {
|
|
244
272
|
if (!token || !chatId) {
|
|
245
|
-
logger === null || logger === void 0 ? void 0 : logger.warn('Telegram token và chatId là bắt buộc
|
|
273
|
+
logger === null || logger === void 0 ? void 0 : logger.warn('Telegram token và chatId là bắt buộc');
|
|
246
274
|
return;
|
|
247
275
|
}
|
|
248
276
|
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
|
|
3
|
+
"version": "1.1.0",
|
|
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"
|