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.
@@ -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(/&lt;/g, '<')
49
+ .replace(/&gt;/g, '>')
50
+ .replace(/&amp;/g, '&')
51
+ .replace(/&nbsp;/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
- const dimensions = (0, image_size_1.default)(filePath);
69
- return {
70
- height: dimensions.height,
71
- width: dimensions.width,
72
- size: stats.size,
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 nhưng không được cung cấp.');
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.9",
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"