@softtynet/zca-js 2.1.21 → 2.2.2

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 (94) hide show
  1. package/README.md +47 -21
  2. package/dist/apis/cancelPullMobile.d.ts +11 -0
  3. package/dist/apis/cancelPullMobile.js +33 -0
  4. package/dist/apis/cancelVoiceCall.d.ts +7 -0
  5. package/dist/apis/cancelVoiceCall.js +43 -0
  6. package/dist/apis/decryptGids.d.ts +2 -1
  7. package/dist/apis/decryptGids.js +2 -0
  8. package/dist/apis/endVoiceCall.d.ts +5 -0
  9. package/dist/apis/endVoiceCall.js +41 -0
  10. package/dist/apis/getNoisedIds.d.ts +9 -0
  11. package/dist/apis/getNoisedIds.js +67 -0
  12. package/dist/apis/getSyncData.d.ts +13 -0
  13. package/dist/apis/getSyncData.js +103 -0
  14. package/dist/apis/listen.d.ts +11 -3
  15. package/dist/apis/listen.js +71 -14
  16. package/dist/apis/preloadConversations.d.ts +20 -2
  17. package/dist/apis/preloadConversations.js +56 -3
  18. package/dist/apis/pullMobile.d.ts +15 -0
  19. package/dist/apis/pullMobile.js +38 -0
  20. package/dist/apis/requestSyncFromPhone.d.ts +17 -1
  21. package/dist/apis/requestSyncFromPhone.js +34 -20
  22. package/dist/apis/sendVoiceCall.d.ts +29 -0
  23. package/dist/apis/sendVoiceCall.js +105 -0
  24. package/dist/apis/uploadAttachment.js +6 -1
  25. package/dist/apis.d.ts +14 -0
  26. package/dist/apis.js +14 -0
  27. package/dist/cjs/apis/addReaction.cjs +2 -0
  28. package/dist/cjs/apis/addUnreadMark.cjs +2 -0
  29. package/dist/cjs/apis/cancelPullMobile.cjs +38 -0
  30. package/dist/cjs/apis/cancelVoiceCall.cjs +48 -0
  31. package/dist/cjs/apis/createReminder.cjs +2 -0
  32. package/dist/cjs/apis/decryptGids.cjs +2 -0
  33. package/dist/cjs/apis/deleteChat.cjs +2 -0
  34. package/dist/cjs/apis/deleteMessage.cjs +2 -0
  35. package/dist/cjs/apis/editReminder.cjs +2 -0
  36. package/dist/cjs/apis/endVoiceCall.cjs +46 -0
  37. package/dist/cjs/apis/findUser.cjs +2 -0
  38. package/dist/cjs/apis/findUserByUsername.cjs +2 -0
  39. package/dist/cjs/apis/forwardMessage.cjs +2 -0
  40. package/dist/cjs/apis/getAllFriends.cjs +2 -0
  41. package/dist/cjs/apis/getAvatarUrlProfile.cjs +2 -0
  42. package/dist/cjs/apis/getGroupChatHistory.cjs +2 -0
  43. package/dist/cjs/apis/getListBoard.cjs +2 -0
  44. package/dist/cjs/apis/getListReminder.cjs +2 -0
  45. package/dist/cjs/apis/getMultiUsersByPhones.cjs +2 -0
  46. package/dist/cjs/apis/getNoisedIds.cjs +72 -0
  47. package/dist/cjs/apis/getSyncData.cjs +108 -0
  48. package/dist/cjs/apis/getUserInfo.cjs +2 -0
  49. package/dist/cjs/apis/listen.cjs +71 -14
  50. package/dist/cjs/apis/preloadConversations.cjs +69 -3
  51. package/dist/cjs/apis/pullMobile.cjs +43 -0
  52. package/dist/cjs/apis/removeReminder.cjs +2 -0
  53. package/dist/cjs/apis/removeUnreadMark.cjs +2 -0
  54. package/dist/cjs/apis/requestSyncFromPhone.cjs +33 -19
  55. package/dist/cjs/apis/sendBankCard.cjs +2 -0
  56. package/dist/cjs/apis/sendCard.cjs +2 -0
  57. package/dist/cjs/apis/sendDeliveredEvent.cjs +2 -0
  58. package/dist/cjs/apis/sendLink.cjs +2 -0
  59. package/dist/cjs/apis/sendMessage.cjs +2 -0
  60. package/dist/cjs/apis/sendReport.cjs +2 -0
  61. package/dist/cjs/apis/sendSeenEvent.cjs +2 -0
  62. package/dist/cjs/apis/sendSticker.cjs +2 -0
  63. package/dist/cjs/apis/sendTypingEvent.cjs +2 -0
  64. package/dist/cjs/apis/sendVideo.cjs +2 -0
  65. package/dist/cjs/apis/sendVoice.cjs +2 -0
  66. package/dist/cjs/apis/sendVoiceCall.cjs +110 -0
  67. package/dist/cjs/apis/setHiddenConversations.cjs +2 -0
  68. package/dist/cjs/apis/setMute.cjs +2 -0
  69. package/dist/cjs/apis/setPinnedConversations.cjs +2 -0
  70. package/dist/cjs/apis/undo.cjs +2 -0
  71. package/dist/cjs/apis/updateAutoDeleteChat.cjs +2 -0
  72. package/dist/cjs/apis/uploadAttachment.cjs +8 -1
  73. package/dist/cjs/apis.cjs +14 -0
  74. package/dist/cjs/index.cjs +24 -0
  75. package/dist/cjs/models/CallEvent.cjs +51 -0
  76. package/dist/cjs/models/ClearUnread.cjs +17 -0
  77. package/dist/cjs/models/SyncEvent.cjs +54 -0
  78. package/dist/cjs/utils.cjs +153 -1
  79. package/dist/index.d.ts +11 -1
  80. package/dist/index.js +2 -0
  81. package/dist/models/CallEvent.d.ts +30 -0
  82. package/dist/models/CallEvent.js +47 -0
  83. package/dist/models/ClearUnread.d.ts +22 -0
  84. package/dist/models/ClearUnread.js +12 -0
  85. package/dist/models/SyncData.d.ts +35 -0
  86. package/dist/models/SyncData.js +1 -0
  87. package/dist/models/SyncEvent.d.ts +83 -0
  88. package/dist/models/SyncEvent.js +50 -0
  89. package/dist/models/index.d.ts +4 -0
  90. package/dist/models/index.js +4 -0
  91. package/dist/utils.d.ts +34 -0
  92. package/dist/utils.js +148 -1
  93. package/docs/REA_REVERSE_ENGINEERING_PLAN.md +140 -0
  94. package/package.json +1 -1
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ var ZaloApiError = require('../Errors/ZaloApiError.cjs');
4
+ var utils = require('../utils.cjs');
5
+ var sendVoiceCall = require('./sendVoiceCall.cjs');
6
+
7
+ const cancelVoiceCallFactory = utils.apiFactory()((api, ctx, utils) => {
8
+ var _a;
9
+ const voiceCallService = (_a = api.zpwServiceMap.voice_call) === null || _a === void 0 ? void 0 : _a[0];
10
+ /**
11
+ * Huỷ cuộc gọi đang đổ chuông trước khi người nhận bắt máy (`/api/voicecall/cancel`, command `11305`).
12
+ *
13
+ * @param calleeId User ID của người nhận cuộc gọi
14
+ * @param callId ID của cuộc gọi (nhận từ `sendVoiceCall`)
15
+ * @param options Tuỳ chọn loại cuộc gọi (`callType`) và `status`
16
+ * @throws {ZaloApiError}
17
+ */
18
+ return async function cancelVoiceCall(calleeId, callId, options = {}) {
19
+ var _a, _b;
20
+ if (!calleeId)
21
+ throw new ZaloApiError.ZaloApiError("Missing calleeId");
22
+ if (!callId)
23
+ throw new ZaloApiError.ZaloApiError("Missing callId");
24
+ if (!voiceCallService)
25
+ throw new ZaloApiError.ZaloApiError("Missing voice_call service URL in zpwServiceMap");
26
+ const numericCalleeId = Number.isNaN(Number(calleeId)) ? calleeId : Number(calleeId);
27
+ const params = {
28
+ callerId: numericCalleeId,
29
+ callId,
30
+ callType: (_a = options.callType) !== null && _a !== void 0 ? _a : sendVoiceCall.CallType.Audio,
31
+ status: (_b = options.status) !== null && _b !== void 0 ? _b : 0,
32
+ imei: ctx.imei,
33
+ };
34
+ const encryptedParams = utils.encodeAES(JSON.stringify(params));
35
+ if (!encryptedParams)
36
+ throw new ZaloApiError.ZaloApiError("Failed to encrypt cancelVoiceCall params");
37
+ const url = utils.makeURL(`${voiceCallService}/api/voicecall/cancel`, {
38
+ zpw_type: 24,
39
+ params: encryptedParams,
40
+ });
41
+ const response = await utils.request(url, {
42
+ method: "GET",
43
+ });
44
+ return utils.resolve(response);
45
+ };
46
+ });
47
+
48
+ exports.cancelVoiceCallFactory = cancelVoiceCallFactory;
@@ -11,6 +11,8 @@ require('../models/GroupEvent.cjs');
11
11
  require('../models/Reaction.cjs');
12
12
  var Reminder = require('../models/Reminder.cjs');
13
13
  require('../models/ZBusiness.cjs');
14
+ require('../models/SyncEvent.cjs');
15
+ require('../models/CallEvent.cjs');
14
16
  var utils = require('../utils.cjs');
15
17
 
16
18
  const createReminderFactory = utils.apiFactory()((api, ctx, utils) => {
@@ -7,6 +7,7 @@ const decryptGidsFactory = utils.apiFactory()((api, ctx, utils) => {
7
7
  const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/gid/decrypt`);
8
8
  /**
9
9
  * Giải mã danh sách Global ID (g_...) ẩn danh thành User ID thật
10
+ * (tương ứng hàm `getUidFromGlobalNoiseId` / command `12054` trên Zalo PC)
10
11
  *
11
12
  * @param gids Danh sách Global ID (hoặc 1 GID đơn lẻ)
12
13
  * @throws {ZaloApiError}
@@ -17,6 +18,7 @@ const decryptGidsFactory = utils.apiFactory()((api, ctx, utils) => {
17
18
  if (!Array.isArray(gids))
18
19
  gids = [gids];
19
20
  const params = {
21
+ globalUids: JSON.stringify(gids),
20
22
  gids,
21
23
  imei: ctx.imei,
22
24
  };
@@ -11,6 +11,8 @@ require('../models/GroupEvent.cjs');
11
11
  require('../models/Reaction.cjs');
12
12
  require('../models/Reminder.cjs');
13
13
  require('../models/ZBusiness.cjs');
14
+ require('../models/SyncEvent.cjs');
15
+ require('../models/CallEvent.cjs');
14
16
  var utils = require('../utils.cjs');
15
17
 
16
18
  const deleteChatFactory = utils.apiFactory()((api, ctx, utils) => {
@@ -11,6 +11,8 @@ require('../models/GroupEvent.cjs');
11
11
  require('../models/Reaction.cjs');
12
12
  require('../models/Reminder.cjs');
13
13
  require('../models/ZBusiness.cjs');
14
+ require('../models/SyncEvent.cjs');
15
+ require('../models/CallEvent.cjs');
14
16
  var utils = require('../utils.cjs');
15
17
 
16
18
  const deleteMessageFactory = utils.apiFactory()((api, ctx, utils) => {
@@ -11,6 +11,8 @@ require('../models/GroupEvent.cjs');
11
11
  require('../models/Reaction.cjs');
12
12
  require('../models/Reminder.cjs');
13
13
  require('../models/ZBusiness.cjs');
14
+ require('../models/SyncEvent.cjs');
15
+ require('../models/CallEvent.cjs');
14
16
  var utils = require('../utils.cjs');
15
17
 
16
18
  const editReminderFactory = utils.apiFactory()((api, ctx, utils) => {
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+
3
+ var ZaloApiError = require('../Errors/ZaloApiError.cjs');
4
+ var utils = require('../utils.cjs');
5
+
6
+ const endVoiceCallFactory = utils.apiFactory()((api, ctx, utils) => {
7
+ var _a;
8
+ const voiceCallService = (_a = api.zpwServiceMap.voice_call) === null || _a === void 0 ? void 0 : _a[0];
9
+ /**
10
+ * Kết thúc cuộc gọi thoại / video (`/api/voicecall/endcall`, command `11306`).
11
+ *
12
+ * @param calleeId User ID của đối phương
13
+ * @param callId ID của cuộc gọi
14
+ * @param options Tuỳ chọn mã trạng thái kết thúc (`status`, mặc định `3`)
15
+ * @throws {ZaloApiError}
16
+ */
17
+ return async function endVoiceCall(calleeId, callId, options = {}) {
18
+ var _a;
19
+ if (!calleeId)
20
+ throw new ZaloApiError.ZaloApiError("Missing calleeId");
21
+ if (!callId)
22
+ throw new ZaloApiError.ZaloApiError("Missing callId");
23
+ if (!voiceCallService)
24
+ throw new ZaloApiError.ZaloApiError("Missing voice_call service URL in zpwServiceMap");
25
+ const numericCalleeId = Number.isNaN(Number(calleeId)) ? calleeId : Number(calleeId);
26
+ const params = {
27
+ uidTo: numericCalleeId,
28
+ callId,
29
+ status: (_a = options.status) !== null && _a !== void 0 ? _a : 3,
30
+ imei: ctx.imei,
31
+ };
32
+ const encryptedParams = utils.encodeAES(JSON.stringify(params));
33
+ if (!encryptedParams)
34
+ throw new ZaloApiError.ZaloApiError("Failed to encrypt endVoiceCall params");
35
+ const url = utils.makeURL(`${voiceCallService}/api/voicecall/endcall`, {
36
+ zpw_type: 24,
37
+ params: encryptedParams,
38
+ });
39
+ const response = await utils.request(url, {
40
+ method: "GET",
41
+ });
42
+ return utils.resolve(response);
43
+ };
44
+ });
45
+
46
+ exports.endVoiceCallFactory = endVoiceCallFactory;
@@ -12,6 +12,8 @@ require('../models/GroupEvent.cjs');
12
12
  require('../models/Reaction.cjs');
13
13
  require('../models/Reminder.cjs');
14
14
  require('../models/ZBusiness.cjs');
15
+ require('../models/SyncEvent.cjs');
16
+ require('../models/CallEvent.cjs');
15
17
 
16
18
  const findUserFactory = utils.apiFactory()((api, ctx, utils) => {
17
19
  const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/profile/get`);
@@ -12,6 +12,8 @@ require('../models/GroupEvent.cjs');
12
12
  require('../models/Reaction.cjs');
13
13
  require('../models/Reminder.cjs');
14
14
  require('../models/ZBusiness.cjs');
15
+ require('../models/SyncEvent.cjs');
16
+ require('../models/CallEvent.cjs');
15
17
 
16
18
  const findUserByUsernameFactory = utils.apiFactory()((api, _ctx, utils) => {
17
19
  const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/search/by-user-name`);
@@ -11,6 +11,8 @@ require('../models/GroupEvent.cjs');
11
11
  require('../models/Reaction.cjs');
12
12
  require('../models/Reminder.cjs');
13
13
  require('../models/ZBusiness.cjs');
14
+ require('../models/SyncEvent.cjs');
15
+ require('../models/CallEvent.cjs');
14
16
  var utils = require('../utils.cjs');
15
17
 
16
18
  const forwardMessageFactory = utils.apiFactory()((api, ctx, utils) => {
@@ -12,6 +12,8 @@ require('../models/GroupEvent.cjs');
12
12
  require('../models/Reaction.cjs');
13
13
  require('../models/Reminder.cjs');
14
14
  require('../models/ZBusiness.cjs');
15
+ require('../models/SyncEvent.cjs');
16
+ require('../models/CallEvent.cjs');
15
17
 
16
18
  const getAllFriendsFactory = utils.apiFactory()((api, ctx, utils) => {
17
19
  const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/getfriends`);
@@ -11,6 +11,8 @@ require('../models/GroupEvent.cjs');
11
11
  require('../models/Reaction.cjs');
12
12
  require('../models/Reminder.cjs');
13
13
  require('../models/ZBusiness.cjs');
14
+ require('../models/SyncEvent.cjs');
15
+ require('../models/CallEvent.cjs');
14
16
  var utils = require('../utils.cjs');
15
17
 
16
18
  const getAvatarUrlProfileFactory = utils.apiFactory()((api, _ctx, utils) => {
@@ -13,6 +13,8 @@ var Message = require('../models/Message.cjs');
13
13
  require('../models/Reaction.cjs');
14
14
  require('../models/Reminder.cjs');
15
15
  require('../models/ZBusiness.cjs');
16
+ require('../models/SyncEvent.cjs');
17
+ require('../models/CallEvent.cjs');
16
18
 
17
19
  const getGroupChatHistoryFactory = utils.apiFactory()((api, ctx, utils) => {
18
20
  const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_cloud_message[0]}/api/cm/getrecentv2`);
@@ -11,6 +11,8 @@ require('../models/GroupEvent.cjs');
11
11
  require('../models/Reaction.cjs');
12
12
  require('../models/Reminder.cjs');
13
13
  require('../models/ZBusiness.cjs');
14
+ require('../models/SyncEvent.cjs');
15
+ require('../models/CallEvent.cjs');
14
16
  var utils = require('../utils.cjs');
15
17
 
16
18
  const getListBoardFactory = utils.apiFactory()((api, ctx, utils) => {
@@ -11,6 +11,8 @@ require('../models/GroupEvent.cjs');
11
11
  require('../models/Reaction.cjs');
12
12
  require('../models/Reminder.cjs');
13
13
  require('../models/ZBusiness.cjs');
14
+ require('../models/SyncEvent.cjs');
15
+ require('../models/CallEvent.cjs');
14
16
  var utils = require('../utils.cjs');
15
17
 
16
18
  const getListReminderFactory = utils.apiFactory()((api, ctx, utils) => {
@@ -12,6 +12,8 @@ require('../models/GroupEvent.cjs');
12
12
  require('../models/Reaction.cjs');
13
13
  require('../models/Reminder.cjs');
14
14
  require('../models/ZBusiness.cjs');
15
+ require('../models/SyncEvent.cjs');
16
+ require('../models/CallEvent.cjs');
15
17
 
16
18
  const getMultiUsersByPhonesFactory = utils.apiFactory()((api, ctx, utils) => {
17
19
  const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/profile/multiget`);
@@ -0,0 +1,72 @@
1
+ 'use strict';
2
+
3
+ var ZaloApiError = require('../Errors/ZaloApiError.cjs');
4
+ var utils = require('../utils.cjs');
5
+
6
+ const ZWID_ZNOISE_URL = "https://zwid.api.zalo.me/api/znoise";
7
+ function toNumericIds(ids) {
8
+ return ids.map((id) => Number.parseInt(String(id), 10));
9
+ }
10
+ function normalizeNoised(list, expected, stripGroupPrefix) {
11
+ const out = [];
12
+ const arr = Array.isArray(list) ? list : [];
13
+ for (let i = 0; i < expected; i++) {
14
+ let v = arr[i] == null ? "" : String(arr[i]);
15
+ if (stripGroupPrefix && v.startsWith("g"))
16
+ v = v.slice(1);
17
+ out.push(v === "0" ? "" : v);
18
+ }
19
+ return out;
20
+ }
21
+ const getNoisedIdsFactory = utils.apiFactory()((api, ctx, utils) => {
22
+ const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/znoise`);
23
+ /**
24
+ * Chuyển đổi danh sách UID cá nhân (`fids`) và GID nhóm (`gids`) dạng số thuần (từ SQLite ZDB4.0 mobile)
25
+ * sang định dạng Noised ID 19 chữ số dùng trên Zalo Web/PC (`getNoisedUids`, command `12390`).
26
+ * Tự động fallback sang `https://zwid.api.zalo.me/api/znoise` nếu profile domain trả về lỗi.
27
+ *
28
+ * @param params Danh sách `fids` và `gids`
29
+ * @throws {ZaloApiError}
30
+ */
31
+ return async function getNoisedIds({ fids = [], gids = [] }) {
32
+ const payload = {
33
+ fids: toNumericIds(fids),
34
+ gids: toNumericIds(gids),
35
+ };
36
+ const encryptedParams = utils.encodeAES(JSON.stringify(payload));
37
+ if (!encryptedParams)
38
+ throw new ZaloApiError.ZaloApiError("Failed to encrypt params");
39
+ try {
40
+ const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), {
41
+ method: "GET",
42
+ });
43
+ const data = await utils.resolve(response);
44
+ const raw = (data !== null && data !== void 0 ? data : {});
45
+ return {
46
+ fids: normalizeNoised(raw.fids, fids.length, false),
47
+ gids: normalizeNoised(raw.gids, gids.length, true),
48
+ };
49
+ }
50
+ catch (primaryErr) {
51
+ try {
52
+ const body = new URLSearchParams({ params: encryptedParams }).toString();
53
+ const fallbackUrl = utils.makeURL(ZWID_ZNOISE_URL);
54
+ const response = await utils.request(fallbackUrl, {
55
+ method: "POST",
56
+ body,
57
+ });
58
+ const data = await utils.resolve(response);
59
+ const raw = (data !== null && data !== void 0 ? data : {});
60
+ return {
61
+ fids: normalizeNoised(raw.fids, fids.length, false),
62
+ gids: normalizeNoised(raw.gids, gids.length, true),
63
+ };
64
+ }
65
+ catch (_a) {
66
+ throw primaryErr;
67
+ }
68
+ }
69
+ };
70
+ });
71
+
72
+ exports.getNoisedIdsFactory = getNoisedIdsFactory;
@@ -0,0 +1,108 @@
1
+ 'use strict';
2
+
3
+ var fs = require('node:fs');
4
+ var path = require('node:path');
5
+ var toughCookie = require('tough-cookie');
6
+ var ZaloApiError = require('../Errors/ZaloApiError.cjs');
7
+ var utils = require('../utils.cjs');
8
+
9
+ const getSyncDataFactory = utils.apiFactory()((api, ctx, utils) => {
10
+ async function downloadFileStream(url) {
11
+ if (!ctx.cookie)
12
+ ctx.cookie = new toughCookie.CookieJar();
13
+ const cookie = ctx.cookie;
14
+ const manualHeaders = {
15
+ "Cache-Control": "no-store, must-revalidate",
16
+ Pragma: "no-cache",
17
+ "Accept-Encoding": "gzip, deflate, br",
18
+ "Accept-Language": "en-US",
19
+ "User-Agent": ctx.userAgent || "",
20
+ cookie: await cookie.getCookieString("https://chat.zalo.me/"),
21
+ Expires: "0",
22
+ "Sec-Fetch-Dest": "empty",
23
+ "Sec-Fetch-Mode": "no-cors",
24
+ "Sec-Fetch-Site": "none",
25
+ Connection: "keep-alive",
26
+ };
27
+ const response = await utils.request(url, {
28
+ method: "GET",
29
+ headers: manualHeaders,
30
+ }, true);
31
+ if (!response.ok) {
32
+ throw new ZaloApiError.ZaloApiError(`Failed to download sync file: HTTP ${response.status}`);
33
+ }
34
+ return response;
35
+ }
36
+ async function downloadFile(url) {
37
+ const response = await downloadFileStream(url);
38
+ const arrayBuffer = await response.arrayBuffer();
39
+ return Buffer.from(arrayBuffer);
40
+ }
41
+ /**
42
+ * Tải xuống file sao lưu tin nhắn từ sự kiện `syncmsg_info` (`.db.crypt`, định dạng `ZDB4.0`)
43
+ * và lưu ra ổ đĩa hoặc trả về `ReadableStream`.
44
+ *
45
+ * @param params Thông tin file sao lưu nhận được từ `SyncEventType.SYNCMSG_INFO`
46
+ * @returns `Promise<SyncDataResponse>`
47
+ */
48
+ return async function getSyncData({ url, encrypted_key, file_name, file_size, checksum_code, from_seq_id, is_full_transfer, returnStream = false, savePath = "./", }) {
49
+ try {
50
+ if (returnStream) {
51
+ const response = await downloadFileStream(url);
52
+ const syncDataStream = {
53
+ stream: response.body,
54
+ metadata: {
55
+ file_name,
56
+ file_size,
57
+ checksum_code,
58
+ from_seq_id,
59
+ is_full_transfer,
60
+ encrypted_key,
61
+ },
62
+ response: {
63
+ status: response.status,
64
+ statusText: response.statusText,
65
+ url: response.url,
66
+ contentType: response.headers.get("content-type"),
67
+ contentLength: response.headers.get("content-length"),
68
+ },
69
+ };
70
+ return {
71
+ success: true,
72
+ stream: syncDataStream,
73
+ };
74
+ }
75
+ const encryptedFileData = await downloadFile(url);
76
+ const fileName = file_name.endsWith(".db.crypt") ? file_name : `${file_name}.db.crypt`;
77
+ const fullPath = path.join(savePath, fileName);
78
+ const dir = path.dirname(fullPath);
79
+ if (!fs.existsSync(dir)) {
80
+ fs.mkdirSync(dir, { recursive: true });
81
+ }
82
+ fs.writeFileSync(fullPath, encryptedFileData);
83
+ const syncData = {
84
+ file_path: fullPath,
85
+ encrypted_key,
86
+ metadata: {
87
+ file_name: fileName,
88
+ file_size,
89
+ checksum_code,
90
+ from_seq_id,
91
+ is_full_transfer,
92
+ },
93
+ };
94
+ return {
95
+ success: true,
96
+ data: syncData,
97
+ };
98
+ }
99
+ catch (error) {
100
+ return {
101
+ success: false,
102
+ error: error instanceof Error ? error.message : "Unknown error occurred",
103
+ };
104
+ }
105
+ };
106
+ });
107
+
108
+ exports.getSyncDataFactory = getSyncDataFactory;
@@ -12,6 +12,8 @@ require('../models/GroupEvent.cjs');
12
12
  require('../models/Reaction.cjs');
13
13
  require('../models/Reminder.cjs');
14
14
  require('../models/ZBusiness.cjs');
15
+ require('../models/SyncEvent.cjs');
16
+ require('../models/CallEvent.cjs');
15
17
 
16
18
  const getUserInfoFactory = utils.apiFactory()((api, ctx, utils) => {
17
19
  const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/social/friend/getprofiles/v2`);
@@ -17,6 +17,9 @@ var SeenMessage = require('../models/SeenMessage.cjs');
17
17
  var Typing = require('../models/Typing.cjs');
18
18
  var Undo = require('../models/Undo.cjs');
19
19
  require('../models/ZBusiness.cjs');
20
+ var ClearUnread = require('../models/ClearUnread.cjs');
21
+ var SyncEvent = require('../models/SyncEvent.cjs');
22
+ var CallEvent = require('../models/CallEvent.cjs');
20
23
  var utils = require('../utils.cjs');
21
24
  var ZaloApiError = require('../Errors/ZaloApiError.cjs');
22
25
 
@@ -233,7 +236,7 @@ class Listener extends EventEmitter {
233
236
  this.emit("error", event);
234
237
  };
235
238
  ws.onmessage = async (event) => {
236
- var _a, _b, _c;
239
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
237
240
  this.resetWatchdog();
238
241
  this.emit("pong"); // Pulse event to notify watchdog that socket is alive
239
242
  const { data } = event;
@@ -355,6 +358,21 @@ class Listener extends EventEmitter {
355
358
  continue;
356
359
  this.emit("friend_event", friendEvent);
357
360
  }
361
+ else if (control.content.act_type == "syncmsgmb" ||
362
+ control.content.act_type == "transfer_msg") {
363
+ const syncEventData = typeof control.content.data == "string"
364
+ ? JSON.parse(control.content.data)
365
+ : control.content.data;
366
+ const syncEvent = SyncEvent.initializeSyncEvent(syncEventData, control.content.act, control.content.act_type);
367
+ this.emit("sync_event", syncEvent);
368
+ }
369
+ else if (control.content.act_type == "voip") {
370
+ const callEventData = typeof control.content.data == "string"
371
+ ? JSON.parse(control.content.data)
372
+ : control.content.data;
373
+ const callEvent = CallEvent.initializeCallEvent(callEventData, control.content.act, control.content.act_type);
374
+ this.emit("call_event", callEvent);
375
+ }
358
376
  }
359
377
  }
360
378
  if (cmd == 612) {
@@ -382,17 +400,54 @@ class Listener extends EventEmitter {
382
400
  const reactionObjects = reacts.map((react) => new Reaction.Reaction(this.ctx.uid, react, isGroup));
383
401
  this.emit("old_reactions", reactionObjects, isGroup);
384
402
  }
385
- if (cmd == 510 && subCmd == 1) {
386
- const parsedData = (await utils.decodeEventData(parsed, this.cipherKey)).data;
387
- const msgs = parsedData.msgs;
388
- const responseMsgs = msgs.map((msg) => new Message.UserMessage(this.ctx.uid, msg));
389
- this.emit("old_messages", responseMsgs, Enum.ThreadType.User);
403
+ if (cmd == 510 && (subCmd == 0 || subCmd == 1)) {
404
+ const decoded = await utils.decodeEventData(parsed, this.cipherKey);
405
+ const parsedData = (_d = decoded === null || decoded === void 0 ? void 0 : decoded.data) !== null && _d !== void 0 ? _d : decoded;
406
+ const msgs = ((_e = parsedData === null || parsedData === void 0 ? void 0 : parsedData.msgs) !== null && _e !== void 0 ? _e : []);
407
+ const responseMsgs = [];
408
+ for (const msg of msgs) {
409
+ if (typeof msg.content == "object" && utils.hasOwn(msg.content, "deleteMsg")) {
410
+ const undoObject = new Undo.Undo(this.ctx.uid, msg, false);
411
+ if (undoObject.isSelf && !this.selfListen)
412
+ continue;
413
+ this.emit("undo", undoObject);
414
+ }
415
+ else {
416
+ responseMsgs.push(new Message.UserMessage(this.ctx.uid, msg));
417
+ }
418
+ }
419
+ if (responseMsgs.length > 0 || msgs.length === 0) {
420
+ this.emit("old_messages", responseMsgs, Enum.ThreadType.User, (parsedData === null || parsedData === void 0 ? void 0 : parsedData.more) === 1);
421
+ }
390
422
  }
391
- if (cmd == 511 && subCmd == 1) {
392
- const parsedData = (await utils.decodeEventData(parsed, this.cipherKey)).data;
393
- const groupMsgs = parsedData.groupMsgs;
394
- const responseMsgs = groupMsgs.map((msg) => new Message.GroupMessage(this.ctx.uid, msg));
395
- this.emit("old_messages", responseMsgs, Enum.ThreadType.Group);
423
+ if (cmd == 511 && (subCmd == 0 || subCmd == 1)) {
424
+ const decoded = await utils.decodeEventData(parsed, this.cipherKey);
425
+ const parsedData = (_f = decoded === null || decoded === void 0 ? void 0 : decoded.data) !== null && _f !== void 0 ? _f : decoded;
426
+ const groupMsgs = ((_g = parsedData === null || parsedData === void 0 ? void 0 : parsedData.groupMsgs) !== null && _g !== void 0 ? _g : []);
427
+ const responseMsgs = [];
428
+ for (const msg of groupMsgs) {
429
+ if (typeof msg.content == "object" && utils.hasOwn(msg.content, "deleteMsg")) {
430
+ const undoObject = new Undo.Undo(this.ctx.uid, msg, true);
431
+ if (undoObject.isSelf && !this.selfListen)
432
+ continue;
433
+ this.emit("undo", undoObject);
434
+ }
435
+ else {
436
+ responseMsgs.push(new Message.GroupMessage(this.ctx.uid, msg));
437
+ }
438
+ }
439
+ if (responseMsgs.length > 0 || groupMsgs.length === 0) {
440
+ this.emit("old_messages", responseMsgs, Enum.ThreadType.Group, (parsedData === null || parsedData === void 0 ? void 0 : parsedData.more) === 1);
441
+ }
442
+ }
443
+ if ((cmd == 504 || cmd == 524) && subCmd == 0) {
444
+ const decoded = await utils.decodeEventData(parsed, this.cipherKey);
445
+ const parsedData = (_h = decoded === null || decoded === void 0 ? void 0 : decoded.data) !== null && _h !== void 0 ? _h : decoded;
446
+ const clearUnreads = ((_j = parsedData === null || parsedData === void 0 ? void 0 : parsedData.clearUnreads) !== null && _j !== void 0 ? _j : []);
447
+ if (Array.isArray(clearUnreads) && clearUnreads.length > 0) {
448
+ const items = clearUnreads.map((item) => new ClearUnread.ClearUnread(item));
449
+ this.emit("unread_cleared", items);
450
+ }
396
451
  }
397
452
  if (cmd == 602 && subCmd == 0) {
398
453
  const parsedData = (await utils.decodeEventData(parsed, this.cipherKey)).data;
@@ -488,16 +543,18 @@ class Listener extends EventEmitter {
488
543
  }
489
544
  }
490
545
  /**
491
- * Request old messages
546
+ * Request old messages from Zalo WebSocket queue (`cmd 510` for User, `cmd 511` for Group).
547
+ * When `lastMsgId` is `null` or omitted, defaults to `"10000000000000000"` to fetch the latest offline messages.
492
548
  *
493
- * @param lastMsgId
549
+ * @param threadType ThreadType.User or ThreadType.Group
550
+ * @param lastMsgId Optional cursor message ID (`actionId`) for pagination
494
551
  */
495
552
  requestOldMessages(threadType, lastMsgId = null) {
496
553
  const payload = {
497
554
  version: 1,
498
555
  cmd: threadType === Enum.ThreadType.User ? 510 : 511,
499
556
  subCmd: 1,
500
- data: { first: true, lastId: lastMsgId, preIds: [] },
557
+ data: { first: true, lastId: lastMsgId !== null && lastMsgId !== void 0 ? lastMsgId : "10000000000000000", preIds: [] },
501
558
  };
502
559
  this.sendWs(payload);
503
560
  }