@softtynet/zca-js 2.3.5 → 2.3.7

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.
Files changed (61) hide show
  1. package/README.md +25 -2
  2. package/dist/apis/business/blockOA.d.ts +2 -0
  3. package/dist/apis/business/blockOA.js +28 -0
  4. package/dist/apis/business/convertOAIds.d.ts +4 -0
  5. package/dist/apis/business/convertOAIds.js +26 -0
  6. package/dist/apis/business/getOAProfile.d.ts +13 -0
  7. package/dist/apis/business/getOAProfile.js +64 -0
  8. package/dist/apis/business/unblockOA.d.ts +2 -0
  9. package/dist/apis/business/unblockOA.js +28 -0
  10. package/dist/apis/chat/preloadConversations.d.ts +3 -2
  11. package/dist/apis/chat/preloadConversations.js +14 -0
  12. package/dist/apis/sync/downloadTransferSync.js +19 -1
  13. package/dist/apis/sync/getSyncData.js +1 -1
  14. package/dist/apis.d.ts +12 -0
  15. package/dist/apis.js +20 -0
  16. package/dist/call-engine/ZaloCallTransport.js +28 -2
  17. package/dist/call-engine/ZaloRtcSession.js +9 -1
  18. package/dist/cjs/apis/business/blockOA.cjs +33 -0
  19. package/dist/cjs/apis/business/convertOAIds.cjs +31 -0
  20. package/dist/cjs/apis/business/getOAProfile.cjs +69 -0
  21. package/dist/cjs/apis/business/unblockOA.cjs +33 -0
  22. package/dist/cjs/apis/chat/preloadConversations.cjs +14 -0
  23. package/dist/cjs/apis/sync/downloadTransferSync.cjs +19 -1
  24. package/dist/cjs/apis/sync/getSyncData.cjs +20 -1
  25. package/dist/cjs/apis.cjs +20 -0
  26. package/dist/cjs/call-engine/ZaloCallTransport.cjs +28 -2
  27. package/dist/cjs/call-engine/ZaloRtcSession.cjs +9 -1
  28. package/dist/cjs/constants/defaults.cjs +2 -2
  29. package/dist/cjs/constants/protocol.cjs +1 -1
  30. package/dist/cjs/crypto/aes.cjs +3 -1
  31. package/dist/cjs/crypto/zdb-cipher.cjs +10 -3
  32. package/dist/cjs/index.cjs +10 -0
  33. package/dist/cjs/models/Message.cjs +16 -1
  34. package/dist/cjs/models/OAMessage.cjs +375 -0
  35. package/dist/cjs/realtime/handlers/control.handler.cjs +9 -1
  36. package/dist/cjs/realtime/handlers/message.handler.cjs +103 -37
  37. package/dist/cjs/realtime/listener.cjs +39 -18
  38. package/dist/cjs/utils.cjs +21 -2
  39. package/dist/cjs/zalo.cjs +21 -2
  40. package/dist/constants/defaults.d.ts +2 -2
  41. package/dist/constants/defaults.js +2 -2
  42. package/dist/constants/protocol.d.ts +1 -1
  43. package/dist/constants/protocol.js +1 -1
  44. package/dist/crypto/aes.js +3 -1
  45. package/dist/crypto/zdb-cipher.js +10 -3
  46. package/dist/index.d.ts +4 -0
  47. package/dist/models/Message.d.ts +12 -1
  48. package/dist/models/Message.js +16 -1
  49. package/dist/models/OAMessage.d.ts +221 -0
  50. package/dist/models/OAMessage.js +363 -0
  51. package/dist/models/index.d.ts +1 -0
  52. package/dist/models/index.js +1 -0
  53. package/dist/realtime/handlers/control.handler.js +9 -1
  54. package/dist/realtime/handlers/message.handler.js +103 -37
  55. package/dist/realtime/listener.d.ts +9 -3
  56. package/dist/realtime/listener.js +39 -18
  57. package/dist/realtime/types.d.ts +15 -1
  58. package/dist/utils.js +1 -1
  59. package/dist/zalo.d.ts +1 -1
  60. package/dist/zalo.js +1 -1
  61. package/package.json +2 -1
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ var ZaloApiError = require('../../Errors/ZaloApiError.cjs');
4
+ var utils = require('../../utils.cjs');
5
+
6
+ const convertOAIdsFactory = utils.apiFactory()((api, _ctx, utils) => {
7
+ const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/oa/convertid`);
8
+ /**
9
+ * Chuyển đổi danh sách OA ID sang Zalo UID nội bộ (hoặc ngược lại).
10
+ * Tương ứng với hàm `convertOAIds` (`commandId = 11863`) trên Zalo Desktop.
11
+ *
12
+ * @param oaIds Một OA ID hoặc mảng các OA ID cần chuyển đổi
13
+ *
14
+ * @throws {ZaloApiError}
15
+ */
16
+ return async function convertOAIds(oaIds) {
17
+ const normalizedIds = (Array.isArray(oaIds) ? oaIds : [oaIds]).map(String);
18
+ const params = {
19
+ oa_ids: normalizedIds,
20
+ };
21
+ const encryptedParams = utils.encodeAES(JSON.stringify(params));
22
+ if (!encryptedParams)
23
+ throw new ZaloApiError.ZaloApiError("Failed to encrypt params");
24
+ const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), {
25
+ method: "GET",
26
+ });
27
+ return utils.resolve(response);
28
+ };
29
+ });
30
+
31
+ exports.convertOAIdsFactory = convertOAIdsFactory;
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ var ZaloApiError = require('../../Errors/ZaloApiError.cjs');
4
+ var OAMessage = require('../../models/OAMessage.cjs');
5
+ var utils = require('../../utils.cjs');
6
+
7
+ const getOAProfileFactory = utils.apiFactory()((api, ctx, utils) => {
8
+ const profileServiceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/getprofiles/v2`);
9
+ const bizServiceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/get-bizacc`);
10
+ /**
11
+ * Lấy hồ sơ đầy đủ của một Zalo Official Account (OA) theo `oaId`
12
+ * (bao gồm `oaInfo`, trạng thái tích vàng `isVerified`, `isZaloBot`, `isGroupedMsg`, trạng thái chặn/DNR `dnrStatus`, hotline, website, giờ mở cửa, địa chỉ, danh mục...).
13
+ *
14
+ * @param oaId UID của Zalo Official Account
15
+ * @param options Tùy chọn truy vấn
16
+ * @throws {ZaloApiError}
17
+ */
18
+ return async function getOAProfile(oaId, options = {}) {
19
+ const cleanId = String(oaId ?? "").trim().split("_")[0];
20
+ if (!cleanId) {
21
+ throw new ZaloApiError.ZaloApiError("Missing oaId");
22
+ }
23
+ const profileParams = {
24
+ phonebook_version: 0,
25
+ friend_pversion_map: [`${cleanId}_0`],
26
+ avatar_size: options.avatarSize ?? 240,
27
+ language: ctx.language ?? "vi",
28
+ show_online_status: 1,
29
+ isReqOAInfo: 1,
30
+ imei: ctx.imei,
31
+ };
32
+ const encryptedProfileParams = utils.encodeAES(JSON.stringify(profileParams));
33
+ if (!encryptedProfileParams) {
34
+ throw new ZaloApiError.ZaloApiError("Failed to encrypt OA profile params");
35
+ }
36
+ const fetchProfilePromise = utils
37
+ .request(profileServiceURL, {
38
+ method: "POST",
39
+ body: new URLSearchParams({
40
+ params: encryptedProfileParams,
41
+ }),
42
+ })
43
+ .then((res) => utils.resolve(res));
44
+ const fetchBizPromise = options.includeBizAccount !== false
45
+ ? (async () => {
46
+ const encryptedBizParams = utils.encodeAES(JSON.stringify({ fid: cleanId }));
47
+ if (!encryptedBizParams)
48
+ return null;
49
+ const res = await utils.request(bizServiceURL, {
50
+ method: "POST",
51
+ body: new URLSearchParams({
52
+ params: encryptedBizParams,
53
+ }),
54
+ });
55
+ return utils.resolve(res);
56
+ })().catch(() => null)
57
+ : Promise.resolve(null);
58
+ const [profileRes, bizRes] = await Promise.all([fetchProfilePromise, fetchBizPromise]);
59
+ const profileData = (profileRes ?? {});
60
+ const changedProfiles = (profileData.changed_profiles ?? profileData.profiles ?? {});
61
+ const rawProfile = changedProfiles[cleanId] ??
62
+ changedProfiles[`${cleanId}_0`] ??
63
+ Object.values(changedProfiles)[0] ??
64
+ null;
65
+ return OAMessage.normalizeOAProfile(cleanId, rawProfile, bizRes && typeof bizRes === "object" ? bizRes : null);
66
+ };
67
+ });
68
+
69
+ exports.getOAProfileFactory = getOAProfileFactory;
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ var ZaloApiError = require('../../Errors/ZaloApiError.cjs');
4
+ var utils = require('../../utils.cjs');
5
+
6
+ const unblockOAFactory = utils.apiFactory()((api, _ctx, utils) => {
7
+ const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/oa/unblock`);
8
+ /**
9
+ * Bỏ chặn một Zalo Official Account (OA) để tiếp tục nhận tin nhắn / ZNS.
10
+ * Tương ứng với hàm `setBlockOA` (`commandId = 12682`) trên Zalo Desktop.
11
+ *
12
+ * @param oaId UID hoặc OA ID của Official Account cần bỏ chặn
13
+ *
14
+ * @throws {ZaloApiError}
15
+ */
16
+ return async function unblockOA(oaId) {
17
+ const params = {
18
+ oaid: String(oaId),
19
+ };
20
+ const encryptedParams = utils.encodeAES(JSON.stringify(params));
21
+ if (!encryptedParams)
22
+ throw new ZaloApiError.ZaloApiError("Failed to encrypt params");
23
+ const response = await utils.request(serviceURL, {
24
+ method: "POST",
25
+ body: new URLSearchParams({
26
+ params: encryptedParams,
27
+ }),
28
+ });
29
+ return utils.resolve(response);
30
+ };
31
+ });
32
+
33
+ exports.unblockOAFactory = unblockOAFactory;
@@ -43,6 +43,7 @@ const preloadConversationsFactory = utils.apiFactory()((api, ctx, utils) => {
43
43
  data = JSON.parse(data);
44
44
  }
45
45
  const messages = [];
46
+ const pageMessages = [];
46
47
  const seenMsgIds = new Set();
47
48
  if (Array.isArray(data.msgs)) {
48
49
  for (const msg of data.msgs) {
@@ -54,6 +55,18 @@ const preloadConversationsFactory = utils.apiFactory()((api, ctx, utils) => {
54
55
  messages.push(new Message.UserMessage(ctx.uid, msg));
55
56
  }
56
57
  }
58
+ if (Array.isArray(data.pageMsgs)) {
59
+ for (const msg of data.pageMsgs) {
60
+ const id = String(msg.msgId ?? "");
61
+ if (id && seenMsgIds.has(id))
62
+ continue;
63
+ if (id)
64
+ seenMsgIds.add(id);
65
+ const userMsg = new Message.UserMessage(ctx.uid, msg, true);
66
+ pageMessages.push(userMsg);
67
+ messages.push(userMsg);
68
+ }
69
+ }
57
70
  if (Array.isArray(data.groupMsgs)) {
58
71
  for (const msg of data.groupMsgs) {
59
72
  const id = String(msg.msgId ?? "");
@@ -87,6 +100,7 @@ const preloadConversationsFactory = utils.apiFactory()((api, ctx, utils) => {
87
100
  return {
88
101
  ...data,
89
102
  messages,
103
+ pageMessages,
90
104
  unreadCleared,
91
105
  };
92
106
  });
@@ -15,13 +15,31 @@ const downloadTransferSyncFactory = utils.apiFactory()((api, ctx, utils) => {
15
15
  return async function downloadTransferSync(options) {
16
16
  let downloadUrl = "";
17
17
  if (typeof options === "string") {
18
+ let parsedUrl;
19
+ try {
20
+ parsedUrl = new URL(options);
21
+ }
22
+ catch {
23
+ throw new ZaloApiError.ZaloApiError("Invalid downloadTransferSync URL");
24
+ }
25
+ if (parsedUrl.protocol !== "https:" && parsedUrl.protocol !== "http:") {
26
+ throw new ZaloApiError.ZaloApiError(`Unsupported protocol in downloadTransferSync URL: ${parsedUrl.protocol}`);
27
+ }
28
+ if (parsedUrl.hostname === "169.254.169.254" || parsedUrl.hostname === "metadata.google.internal") {
29
+ throw new ZaloApiError.ZaloApiError("Blocked internal metadata hostname in downloadTransferSync URL");
30
+ }
18
31
  downloadUrl = options;
19
32
  }
20
33
  else {
21
34
  if (!options.key || !options.exp || !options.sig) {
22
35
  throw new ZaloApiError.ZaloApiError("Missing key, exp or sig in options");
23
36
  }
24
- downloadUrl = `${domains.ZALO_DOMAINS.TRANSFER_SYNC}${endpoints.ZALO_ENDPOINTS.TRANSFER_SYNC.DOWNLOAD_V2}?key=${options.key}&exp=${options.exp}&sig=${options.sig}`;
37
+ const qs = new URLSearchParams({
38
+ key: String(options.key),
39
+ exp: String(options.exp),
40
+ sig: String(options.sig),
41
+ });
42
+ downloadUrl = `${domains.ZALO_DOMAINS.TRANSFER_SYNC}${endpoints.ZALO_ENDPOINTS.TRANSFER_SYNC.DOWNLOAD_V2}?${qs.toString()}`;
25
43
  }
26
44
  const response = await utils.request(downloadUrl, {
27
45
  method: "GET",
@@ -7,10 +7,29 @@ var ZaloApiError = require('../../Errors/ZaloApiError.cjs');
7
7
  var utils = require('../../utils.cjs');
8
8
  var domains = require('../../constants/domains.cjs');
9
9
 
10
+ function _interopNamespaceDefault(e) {
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var toughCookie__namespace = /*#__PURE__*/_interopNamespaceDefault(toughCookie);
28
+
10
29
  const getSyncDataFactory = utils.apiFactory()((api, ctx, utils) => {
11
30
  async function downloadFileStream(url) {
12
31
  if (!ctx.cookie)
13
- ctx.cookie = new toughCookie.CookieJar();
32
+ ctx.cookie = new toughCookie__namespace.CookieJar();
14
33
  const cookie = ctx.cookie;
15
34
  const manualHeaders = {
16
35
  "Cache-Control": "no-store, must-revalidate",
package/dist/cjs/apis.cjs CHANGED
@@ -15,6 +15,7 @@ var analyseGroup = require('./apis/group/analyseGroup.cjs');
15
15
  var answerAckVoiceCall = require('./apis/call/answerAckVoiceCall.cjs');
16
16
  var answerGroupCall = require('./apis/call/answerGroupCall.cjs');
17
17
  var answerVoiceCall = require('./apis/call/answerVoiceCall.cjs');
18
+ var blockOA = require('./apis/business/blockOA.cjs');
18
19
  var blockUser = require('./apis/friend/blockUser.cjs');
19
20
  var blockViewFeed = require('./apis/friend/blockViewFeed.cjs');
20
21
  var broadcastGroupCallState = require('./apis/call/broadcastGroupCallState.cjs');
@@ -28,6 +29,7 @@ var changeGroupName = require('./apis/group/changeGroupName.cjs');
28
29
  var changeGroupOwner = require('./apis/group/changeGroupOwner.cjs');
29
30
  var checkQuotaCommunityMember = require('./apis/group/checkQuotaCommunityMember.cjs');
30
31
  var clearUnreadFriendRequests = require('./apis/friend/clearUnreadFriendRequests.cjs');
32
+ var convertOAIds = require('./apis/business/convertOAIds.cjs');
31
33
  var createAutoReply = require('./apis/business/createAutoReply.cjs');
32
34
  var createBankAccount = require('./apis/business/createBankAccount.cjs');
33
35
  var createCatalog = require('./apis/business/createCatalog.cjs');
@@ -129,6 +131,7 @@ var getMultiGroupCloudMessages = require('./apis/group/getMultiGroupCloudMessage
129
131
  var getMultiUsersByPhones = require('./apis/friend/getMultiUsersByPhones.cjs');
130
132
  var getMute = require('./apis/chat/getMute.cjs');
131
133
  var getNoisedIds = require('./apis/auth/getNoisedIds.cjs');
134
+ var getOAProfile = require('./apis/business/getOAProfile.cjs');
132
135
  var getOwnId = require('./apis/auth/getOwnId.cjs');
133
136
  var getPendingGroupMembers = require('./apis/group/getPendingGroupMembers.cjs');
134
137
  var getPersonalizedStickerList = require('./apis/sticker/getPersonalizedStickerList.cjs');
@@ -224,6 +227,7 @@ var setMute = require('./apis/chat/setMute.cjs');
224
227
  var setPinnedConversations = require('./apis/chat/setPinnedConversations.cjs');
225
228
  var sharePoll = require('./apis/board/sharePoll.cjs');
226
229
  var shareScreenGroupCall = require('./apis/call/shareScreenGroupCall.cjs');
230
+ var unblockOA = require('./apis/business/unblockOA.cjs');
227
231
  var unblockUser = require('./apis/friend/unblockUser.cjs');
228
232
  var undo = require('./apis/chat/undo.cjs');
229
233
  var undoFriendRequest = require('./apis/friend/undoFriendRequest.cjs');
@@ -322,6 +326,8 @@ class API {
322
326
  if (!this._domains.business) {
323
327
  const createNamespace = (api) => ({
324
328
  get addQuickMessage() { return api.addQuickMessage; },
329
+ get blockOA() { return api.blockOA; },
330
+ get convertOAIds() { return api.convertOAIds; },
325
331
  get createAutoReply() { return api.createAutoReply; },
326
332
  get createBankAccount() { return api.createBankAccount; },
327
333
  get createCatalog() { return api.createCatalog; },
@@ -336,10 +342,12 @@ class API {
336
342
  get getLabels() { return api.getLabels; },
337
343
  get getListBank() { return api.getListBank; },
338
344
  get getListBankAccount() { return api.getListBankAccount; },
345
+ get getOAProfile() { return api.getOAProfile; },
339
346
  get getProductCatalogList() { return api.getProductCatalogList; },
340
347
  get getQuickMessageList() { return api.getQuickMessageList; },
341
348
  get registerCatalog() { return api.registerCatalog; },
342
349
  get removeQuickMessage() { return api.removeQuickMessage; },
350
+ get unblockOA() { return api.unblockOA; },
343
351
  get updateAutoReply() { return api.updateAutoReply; },
344
352
  get updateBankAccount() { return api.updateBankAccount; },
345
353
  get updateCatalog() { return api.updateCatalog; },
@@ -662,6 +670,9 @@ class API {
662
670
  get answerVoiceCall() {
663
671
  return (this._cache.answerVoiceCall ??= answerVoiceCall.answerVoiceCallFactory(this.ctx, this));
664
672
  }
673
+ get blockOA() {
674
+ return (this._cache.blockOA ??= blockOA.blockOAFactory(this.ctx, this));
675
+ }
665
676
  get blockUser() {
666
677
  return (this._cache.blockUser ??= blockUser.blockUserFactory(this.ctx, this));
667
678
  }
@@ -701,6 +712,9 @@ class API {
701
712
  get clearUnreadFriendRequests() {
702
713
  return (this._cache.clearUnreadFriendRequests ??= clearUnreadFriendRequests.clearUnreadFriendRequestsFactory(this.ctx, this));
703
714
  }
715
+ get convertOAIds() {
716
+ return (this._cache.convertOAIds ??= convertOAIds.convertOAIdsFactory(this.ctx, this));
717
+ }
704
718
  get createAutoReply() {
705
719
  return (this._cache.createAutoReply ??= createAutoReply.createAutoReplyFactory(this.ctx, this));
706
720
  }
@@ -1004,6 +1018,9 @@ class API {
1004
1018
  get getNoisedIds() {
1005
1019
  return (this._cache.getNoisedIds ??= getNoisedIds.getNoisedIdsFactory(this.ctx, this));
1006
1020
  }
1021
+ get getOAProfile() {
1022
+ return (this._cache.getOAProfile ??= getOAProfile.getOAProfileFactory(this.ctx, this));
1023
+ }
1007
1024
  get getOwnId() {
1008
1025
  return (this._cache.getOwnId ??= getOwnId.getOwnIdFactory(this.ctx, this));
1009
1026
  }
@@ -1289,6 +1306,9 @@ class API {
1289
1306
  get shareScreenGroupCall() {
1290
1307
  return (this._cache.shareScreenGroupCall ??= shareScreenGroupCall.shareScreenGroupCallFactory(this.ctx, this));
1291
1308
  }
1309
+ get unblockOA() {
1310
+ return (this._cache.unblockOA ??= unblockOA.unblockOAFactory(this.ctx, this));
1311
+ }
1292
1312
  get unblockUser() {
1293
1313
  return (this._cache.unblockUser ??= unblockUser.unblockUserFactory(this.ctx, this));
1294
1314
  }
@@ -11,6 +11,7 @@ var node_buffer = require('node:buffer');
11
11
  */
12
12
  const DEFAULT_ZALOCALL_IPC_KEY_BASE64 = "yjAF9oqMWl6XfXYJn9mA7w==";
13
13
  const MAX_IPC_CHUNK_LENGTH = 4000;
14
+ const MAX_IPC_RECEIVE_BUFFER_LENGTH = 4 * 1024 * 1024;
14
15
  /**
15
16
  * Tầng giao thức IPC双kênh (`receiveSocketPath` & `sendSocketPath`) mã hoá AES-128-CBC (Hex + `$` framing + 4000-char chunking)
16
17
  * khớp 100% với module `3Zc2` / `4KIH` của Zalo PC và `zrtc_call::ZIpcClientMac` (`0x10012eb50`).
@@ -110,11 +111,31 @@ class ZaloCallTransport extends EventEmitter {
110
111
  await Promise.all([
111
112
  new Promise((resolve, reject) => {
112
113
  this.receiveServer.once("error", reject);
113
- this.receiveServer.listen(this.receiveSocketPath, () => resolve());
114
+ this.receiveServer.listen(this.receiveSocketPath, () => {
115
+ if (process.platform !== "win32") {
116
+ try {
117
+ fs.chmodSync(this.receiveSocketPath, 0o600);
118
+ }
119
+ catch {
120
+ // Ignore chmod error if unsupported
121
+ }
122
+ }
123
+ resolve();
124
+ });
114
125
  }),
115
126
  new Promise((resolve, reject) => {
116
127
  this.sendServer.once("error", reject);
117
- this.sendServer.listen(this.sendSocketPath, () => resolve());
128
+ this.sendServer.listen(this.sendSocketPath, () => {
129
+ if (process.platform !== "win32") {
130
+ try {
131
+ fs.chmodSync(this.sendSocketPath, 0o600);
132
+ }
133
+ catch {
134
+ // Ignore chmod error if unsupported
135
+ }
136
+ }
137
+ resolve();
138
+ });
118
139
  }),
119
140
  ]);
120
141
  this.emit("ready");
@@ -203,6 +224,11 @@ class ZaloCallTransport extends EventEmitter {
203
224
  }
204
225
  }
205
226
  handleReceiveChunk(chunk) {
227
+ if (this.receiveBuffer.length + chunk.length > MAX_IPC_RECEIVE_BUFFER_LENGTH) {
228
+ this.receiveBuffer = "";
229
+ this.emit("error", new Error("IPC receive buffer exceeded maximum allowed size (4MB)"));
230
+ return;
231
+ }
206
232
  this.receiveBuffer += chunk;
207
233
  let dollarIdx = this.receiveBuffer.indexOf("$");
208
234
  while (dollarIdx !== -1) {
@@ -2792,7 +2792,12 @@ class ZaloRtcSession extends EventEmitter {
2792
2792
  }
2793
2793
  }
2794
2794
  else if (this.fuaFragments.length > 0) {
2795
- this.fuaFragments.push(body);
2795
+ if (this.fuaFragments.length >= 512) {
2796
+ this.fuaFragments = [];
2797
+ }
2798
+ else {
2799
+ this.fuaFragments.push(body);
2800
+ }
2796
2801
  }
2797
2802
  if (isEnd && this.fuaFragments.length > 0) {
2798
2803
  const fullNal = node_buffer.Buffer.concat(this.fuaFragments);
@@ -2826,6 +2831,9 @@ class ZaloRtcSession extends EventEmitter {
2826
2831
  else if (nalType === 5) {
2827
2832
  this.currentAccessUnitHasIdr = true;
2828
2833
  }
2834
+ if (this.currentAccessUnitNals.length >= 256) {
2835
+ this.currentAccessUnitNals = [];
2836
+ }
2829
2837
  this.currentAccessUnitNals.push(node_buffer.Buffer.from(nal));
2830
2838
  }
2831
2839
  flushIncomingAccessUnit(timestamp, ssrc) {
@@ -37,8 +37,8 @@ const ZALO_DEFAULTS = {
37
37
  /** Số lần lỗi 1006 liên tiếp trước khi xoay sang WebSocket endpoint tiếp theo */
38
38
  CONSECUTIVE_1006_ROTATE_THRESHOLD: 2,
39
39
  /** Con trỏ mặc định khi bắt đầu kéo hàng đợi tin nhắn cũ (cmd 510 / 511) */
40
- OLD_MSG_INITIAL_CURSOR: "10000000000000000",
41
- INITIAL_OLD_MESSAGES_CURSOR: "10000000000000000",
40
+ OLD_MSG_INITIAL_CURSOR: "1",
41
+ INITIAL_OLD_MESSAGES_CURSOR: "1",
42
42
  },
43
43
  UPLOAD: {
44
44
  /** Thời gian sống tối đa của uploadCallback trong bộ nhớ trước khi tự hủy (5 phút) */
@@ -12,7 +12,7 @@ const ZALO_PROTOCOL = {
12
12
  API_VERSION: 689,
13
13
  DEFAULT_API_VERSION: 689,
14
14
  /** Phiên bản SDK hiện tại */
15
- SDK_VERSION: "2.3.5",
15
+ SDK_VERSION: "2.3.7",
16
16
  /** Mã zpw_type đặc thù dành riêng cho phân hệ Gọi Thoại & Video (binary C++ ZaloCall) */
17
17
  VOICECALL_API_TYPE: 24,
18
18
  /** Phiên bản mã hóa ZCID (ParamDecryptor) */
@@ -170,7 +170,9 @@ function encryptPin(pin) {
170
170
  }
171
171
  function validatePin(encryptedPin, pin) {
172
172
  const hash = crypto.createHash("md5").update(pin).digest("hex");
173
- return hash === encryptedPin;
173
+ const a = Buffer.from(hash, "utf8");
174
+ const b = Buffer.from(String(encryptedPin), "utf8");
175
+ return a.length === b.length && crypto.timingSafeEqual(a, b);
174
176
  }
175
177
 
176
178
  exports.ParamsEncryptor = ParamsEncryptor;
@@ -149,6 +149,9 @@ function parseZdbHeader(zdbPlaintext) {
149
149
  throw new ZaloApiError.ZaloApiError("Buffer is not a valid decrypted ZDB4.0 container");
150
150
  }
151
151
  const count = zdbPlaintext.readUInt32BE(COUNT_OFFSET);
152
+ if (count > 100000) {
153
+ throw new ZaloApiError.ZaloApiError(`ZDB4.0 header entry count exceeds safety limit (${count})`);
154
+ }
152
155
  let offset = COUNT_OFFSET + 4;
153
156
  const entries = [];
154
157
  for (let i = 0; i < count; i++) {
@@ -156,12 +159,16 @@ function parseZdbHeader(zdbPlaintext) {
156
159
  throw new ZaloApiError.ZaloApiError("Truncated ZDB4.0 header while reading entry name length");
157
160
  }
158
161
  const nameLen = zdbPlaintext.readUInt32BE(offset);
159
- if (offset + 4 + nameLen + 4 > zdbPlaintext.length) {
160
- throw new ZaloApiError.ZaloApiError("Truncated ZDB4.0 header while reading entry metadata");
162
+ if (nameLen > 4096 || offset + 4 + nameLen + 4 > zdbPlaintext.length) {
163
+ throw new ZaloApiError.ZaloApiError("Truncated or invalid ZDB4.0 header while reading entry metadata");
161
164
  }
162
- const name = zdbPlaintext
165
+ const rawName = zdbPlaintext
163
166
  .toString("utf8", offset + 4, offset + 4 + nameLen)
164
167
  .replace(/\0+$/, "");
168
+ const name = rawName.split(/[\\/]/).pop() || rawName;
169
+ if (name === ".." || name === "." || name.includes("..")) {
170
+ throw new ZaloApiError.ZaloApiError(`Unsafe entry name in ZDB4.0 header: ${rawName}`);
171
+ }
165
172
  const size = zdbPlaintext.readUInt32BE(offset + 4 + nameLen);
166
173
  entries.push({ name, size });
167
174
  offset += 4 + nameLen + 4;
@@ -22,6 +22,7 @@ var ZBusiness = require('./models/ZBusiness.cjs');
22
22
  var ClearUnread = require('./models/ClearUnread.cjs');
23
23
  var SyncEvent = require('./models/SyncEvent.cjs');
24
24
  var CallEvent = require('./models/CallEvent.cjs');
25
+ var OAMessage = require('./models/OAMessage.cjs');
25
26
  var protocol = require('./constants/protocol.cjs');
26
27
  var domains = require('./constants/domains.cjs');
27
28
  var defaults = require('./constants/defaults.cjs');
@@ -145,6 +146,15 @@ Object.defineProperty(exports, "CallEventType", {
145
146
  get: function () { return CallEvent.CallEventType; }
146
147
  });
147
148
  exports.initializeCallEvent = CallEvent.initializeCallEvent;
149
+ exports.formatOAHighlightText = OAMessage.formatOAHighlightText;
150
+ exports.initializeOAEvent = OAMessage.initializeOAEvent;
151
+ exports.isOAProfile = OAMessage.isOAProfile;
152
+ exports.isOAVerified = OAMessage.isOAVerified;
153
+ exports.isZaloBot = OAMessage.isZaloBot;
154
+ exports.normalizeOAProfile = OAMessage.normalizeOAProfile;
155
+ exports.parseOADnrStatus = OAMessage.parseOADnrStatus;
156
+ exports.parseOAFooter = OAMessage.parseOAFooter;
157
+ exports.parseOAMessage = OAMessage.parseOAMessage;
148
158
  exports.ZALO_PROTOCOL = protocol.ZALO_PROTOCOL;
149
159
  exports.ZALO_DOMAINS = domains.ZALO_DOMAINS;
150
160
  exports.ZALO_DEFAULTS = defaults.ZALO_DEFAULTS;
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var Enum = require('./Enum.cjs');
4
+ var OAMessage = require('./OAMessage.cjs');
4
5
 
5
6
  class UserMessage {
6
7
  type = Enum.ThreadType.User;
@@ -10,7 +11,11 @@ class UserMessage {
10
11
  * true if the message is sent by the logged in account
11
12
  */
12
13
  isSelf;
13
- constructor(uid, data) {
14
+ /**
15
+ * Dữ liệu đã giải mã nếu đây là tin nhắn từ Zalo Official Account (OA) (`pageMsgs`, `footerv2`, `chat.list.action`, `msginfo.actionlist`, hoặc `chat.ecard`).
16
+ */
17
+ oaData;
18
+ constructor(uid, data, isPageMsg = false) {
14
19
  this.data = data;
15
20
  this.threadId = data.uidFrom == "0" ? data.idTo : data.uidFrom;
16
21
  this.isSelf = data.uidFrom == "0";
@@ -21,6 +26,16 @@ class UserMessage {
21
26
  if (data.quote) {
22
27
  data.quote.ownerId = String(data.quote.ownerId);
23
28
  }
29
+ const parsedOA = OAMessage.parseOAMessage(data, { isPageMsg });
30
+ if (parsedOA) {
31
+ this.oaData = parsedOA;
32
+ }
33
+ }
34
+ /**
35
+ * Kiểm tra xem tin nhắn có phải là tin nhắn Zalo Official Account (OA) hay không.
36
+ */
37
+ isOAMessage() {
38
+ return this.oaData !== undefined;
24
39
  }
25
40
  }
26
41
  class GroupMessage {