@softtynet/zca-js 2.1.16 → 2.1.18

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/dist/apis.js CHANGED
@@ -21,6 +21,7 @@ import { createNoteFactory } from "./apis/createNote.js";
21
21
  import { createPollFactory } from "./apis/createPoll.js";
22
22
  import { createProductCatalogFactory } from "./apis/createProductCatalog.js";
23
23
  import { createReminderFactory } from "./apis/createReminder.js";
24
+ import { decryptGidsFactory } from "./apis/decryptGids.js";
24
25
  import { deleteAutoReplyFactory } from "./apis/deleteAutoReply.js";
25
26
  import { deleteAvatarFactory } from "./apis/deleteAvatar.js";
26
27
  import { deleteCatalogFactory } from "./apis/deleteCatalog.js";
@@ -30,6 +31,7 @@ import { deleteMessageFactory } from "./apis/deleteMessage.js";
30
31
  import { deleteProductCatalogFactory } from "./apis/deleteProductCatalog.js";
31
32
  import { disableGroupLinkFactory } from "./apis/disableGroupLink.js";
32
33
  import { disperseGroupFactory } from "./apis/disperseGroup.js";
34
+ import { downloadTransferSyncFactory } from "./apis/downloadTransferSync.js";
33
35
  import { editNoteFactory } from "./apis/editNote.js";
34
36
  import { editReminderFactory } from "./apis/editReminder.js";
35
37
  import { enableGroupLinkFactory } from "./apis/enableGroupLink.js";
@@ -94,6 +96,7 @@ import { lastOnlineFactory } from "./apis/lastOnline.js";
94
96
  import { leaveGroupFactory } from "./apis/leaveGroup.js";
95
97
  import { lockPollFactory } from "./apis/lockPoll.js";
96
98
  import { parseLinkFactory } from "./apis/parseLink.js";
99
+ import { preloadConversationsFactory } from "./apis/preloadConversations.js";
97
100
  import { rejectFriendRequestFactory } from "./apis/rejectFriendRequest.js";
98
101
  import { removeFriendFactory } from "./apis/removeFriend.js";
99
102
  import { removeFriendAliasFactory } from "./apis/removeFriendAlias.js";
@@ -103,6 +106,7 @@ import { removeQuickMessageFactory } from "./apis/removeQuickMessage.js";
103
106
  import { removeReminderFactory } from "./apis/removeReminder.js";
104
107
  import { removeUnreadMarkFactory } from "./apis/removeUnreadMark.js";
105
108
  import { removeUserFromGroupFactory } from "./apis/removeUserFromGroup.js";
109
+ import { requestSyncFromPhoneFactory } from "./apis/requestSyncFromPhone.js";
106
110
  import { resetHiddenConversPinFactory } from "./apis/resetHiddenConversPin.js";
107
111
  import { reuseAvatarFactory } from "./apis/reuseAvatar.js";
108
112
  import { reviewPendingMemberRequestFactory } from "./apis/reviewPendingMemberRequest.js";
@@ -171,6 +175,7 @@ export class API {
171
175
  this.createPoll = createPollFactory(ctx, this);
172
176
  this.createProductCatalog = createProductCatalogFactory(ctx, this);
173
177
  this.createReminder = createReminderFactory(ctx, this);
178
+ this.decryptGids = decryptGidsFactory(ctx, this);
174
179
  this.deleteAutoReply = deleteAutoReplyFactory(ctx, this);
175
180
  this.deleteAvatar = deleteAvatarFactory(ctx, this);
176
181
  this.deleteCatalog = deleteCatalogFactory(ctx, this);
@@ -180,6 +185,7 @@ export class API {
180
185
  this.deleteProductCatalog = deleteProductCatalogFactory(ctx, this);
181
186
  this.disableGroupLink = disableGroupLinkFactory(ctx, this);
182
187
  this.disperseGroup = disperseGroupFactory(ctx, this);
188
+ this.downloadTransferSync = downloadTransferSyncFactory(ctx, this);
183
189
  this.editNote = editNoteFactory(ctx, this);
184
190
  this.editReminder = editReminderFactory(ctx, this);
185
191
  this.enableGroupLink = enableGroupLinkFactory(ctx, this);
@@ -244,6 +250,7 @@ export class API {
244
250
  this.leaveGroup = leaveGroupFactory(ctx, this);
245
251
  this.lockPoll = lockPollFactory(ctx, this);
246
252
  this.parseLink = parseLinkFactory(ctx, this);
253
+ this.preloadConversations = preloadConversationsFactory(ctx, this);
247
254
  this.rejectFriendRequest = rejectFriendRequestFactory(ctx, this);
248
255
  this.removeFriend = removeFriendFactory(ctx, this);
249
256
  this.removeFriendAlias = removeFriendAliasFactory(ctx, this);
@@ -253,6 +260,7 @@ export class API {
253
260
  this.removeReminder = removeReminderFactory(ctx, this);
254
261
  this.removeUnreadMark = removeUnreadMarkFactory(ctx, this);
255
262
  this.removeUserFromGroup = removeUserFromGroupFactory(ctx, this);
263
+ this.requestSyncFromPhone = requestSyncFromPhoneFactory(ctx, this);
256
264
  this.resetHiddenConversPin = resetHiddenConversPinFactory(ctx, this);
257
265
  this.reuseAvatar = reuseAvatarFactory(ctx, this);
258
266
  this.reviewPendingMemberRequest = reviewPendingMemberRequestFactory(ctx, this);
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ var ZaloApiError = require('../Errors/ZaloApiError.cjs');
4
+ var utils = require('../utils.cjs');
5
+
6
+ const decryptGidsFactory = utils.apiFactory()((api, ctx, utils) => {
7
+ const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/gid/decrypt`);
8
+ /**
9
+ * Giải mã danh sách Global ID (g_...) ẩn danh thành User ID thật
10
+ *
11
+ * @param gids Danh sách Global ID (hoặc 1 GID đơn lẻ)
12
+ * @throws {ZaloApiError}
13
+ */
14
+ return async function decryptGids(gids) {
15
+ if (!gids)
16
+ throw new ZaloApiError.ZaloApiError("Missing gids");
17
+ if (!Array.isArray(gids))
18
+ gids = [gids];
19
+ const params = {
20
+ gids,
21
+ imei: ctx.imei,
22
+ };
23
+ const encryptedParams = utils.encodeAES(JSON.stringify(params));
24
+ if (!encryptedParams)
25
+ throw new ZaloApiError.ZaloApiError("Failed to encrypt params");
26
+ const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), {
27
+ method: "GET",
28
+ });
29
+ return utils.resolve(response);
30
+ };
31
+ });
32
+
33
+ exports.decryptGidsFactory = decryptGidsFactory;
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ var ZaloApiError = require('../Errors/ZaloApiError.cjs');
4
+ var utils = require('../utils.cjs');
5
+
6
+ const downloadTransferSyncFactory = utils.apiFactory()((api, ctx, utils) => {
7
+ /**
8
+ * Tải khối dữ liệu snapshot đồng bộ tin nhắn từ máy chủ transfersync.zaloapp.com
9
+ *
10
+ * @param options URL đầy đủ hoặc object chứa { key, exp, sig }
11
+ * @throws {ZaloApiError}
12
+ */
13
+ return async function downloadTransferSync(options) {
14
+ let downloadUrl = "";
15
+ if (typeof options === "string") {
16
+ downloadUrl = options;
17
+ }
18
+ else {
19
+ if (!options.key || !options.exp || !options.sig) {
20
+ throw new ZaloApiError.ZaloApiError("Missing key, exp or sig in options");
21
+ }
22
+ downloadUrl = `https://transfersync.zaloapp.com/api/transfer-sync-v2/download?key=${options.key}&exp=${options.exp}&sig=${options.sig}`;
23
+ }
24
+ const response = await utils.request(downloadUrl, {
25
+ method: "GET",
26
+ });
27
+ if (!response.ok) {
28
+ throw new ZaloApiError.ZaloApiError(`Failed to download transfer snapshot: ${response.statusText} (${response.status})`);
29
+ }
30
+ const arrayBuffer = await response.arrayBuffer();
31
+ return Buffer.from(arrayBuffer);
32
+ };
33
+ });
34
+
35
+ exports.downloadTransferSyncFactory = downloadTransferSyncFactory;
@@ -14,36 +14,64 @@ require('../models/Reminder.cjs');
14
14
  require('../models/ZBusiness.cjs');
15
15
 
16
16
  const getGroupChatHistoryFactory = utils.apiFactory()((api, ctx, utils) => {
17
- const serviceURL = utils.makeURL(`${api.zpwServiceMap.group[0]}/api/group/history`);
17
+ const serviceURL = utils.makeURL(`${api.zpwServiceMap.group_cloud_message[0]}/api/cm/getrecentv2`);
18
18
  /**
19
- * Get group chat history
19
+ * Get group chat history from Zalo Cloud Message (newest first)
20
20
  *
21
21
  * @param groupId group id
22
- * @param count count of messages to return (default: 50)
22
+ * @param count count of messages to return (default: 50, max safe limit: 200)
23
23
  *
24
24
  * @throws {ZaloApiError}
25
25
  */
26
26
  return async function getGroupChatHistory(groupId, count = 50) {
27
- const params = {
28
- grid: groupId,
29
- count: count,
30
- };
31
- const encryptedParams = utils.encodeAES(JSON.stringify(params));
32
- if (!encryptedParams)
33
- throw new ZaloApiError.ZaloApiError("Failed to encrypt params");
34
- const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), {
35
- method: "GET",
36
- });
37
- return utils.resolve(response, (result) => {
38
- let data = result.data;
39
- if (typeof data === "string") {
40
- data = JSON.parse(data);
27
+ if (!groupId)
28
+ throw new ZaloApiError.ZaloApiError("Missing groupId");
29
+ if (groupId.startsWith("g"))
30
+ groupId = groupId.slice(1);
31
+ const safeCount = Math.min(Math.max(count, 1), 200);
32
+ const messages = [];
33
+ let cursor = 0;
34
+ let lastPage = null;
35
+ let pageCount = 0;
36
+ while (messages.length < safeCount && pageCount < 10) {
37
+ pageCount++;
38
+ const params = {
39
+ groupId,
40
+ globalMsgId: cursor,
41
+ count: Math.min(50, safeCount - messages.length),
42
+ msgIds: [],
43
+ imei: ctx.imei,
44
+ src: 3,
45
+ };
46
+ const encryptedParams = utils.encodeAES(JSON.stringify(params));
47
+ if (!encryptedParams)
48
+ throw new ZaloApiError.ZaloApiError("Failed to encrypt params");
49
+ const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams, nretry: 0 }), {
50
+ method: "GET",
51
+ });
52
+ lastPage = await utils.resolve(response, (result) => {
53
+ let data = result.data;
54
+ if (typeof data === "string") {
55
+ data = JSON.parse(data);
56
+ }
57
+ return data;
58
+ });
59
+ if (!lastPage || !Array.isArray(lastPage.groupMsgs))
60
+ break;
61
+ for (const rawMsg of lastPage.groupMsgs) {
62
+ if (messages.length >= safeCount)
63
+ break;
64
+ if (messages.some((m) => m.msgId === rawMsg.msgId))
65
+ continue;
66
+ messages.push(rawMsg);
41
67
  }
42
- for (let i = 0; i < data.groupMsgs.length; i++) {
43
- data.groupMsgs[i] = new Message.GroupMessage(ctx.uid, data.groupMsgs[i]);
44
- }
45
- return data;
46
- });
68
+ const nextCursor = Number(lastPage.lastMsgId);
69
+ if (!lastPage.hasMore || !nextCursor || nextCursor === cursor)
70
+ break;
71
+ cursor = nextCursor;
72
+ }
73
+ const groupMsgs = messages.map((data) => new Message.GroupMessage(ctx.uid, data));
74
+ return Object.assign({}, lastPage || {}, { groupMsgs });
47
75
  };
48
76
  });
49
77
 
@@ -31,6 +31,7 @@ class Listener extends EventEmitter {
31
31
  super();
32
32
  this.ctx = ctx;
33
33
  this.urls = urls;
34
+ this.consecutive1006Count = 0;
34
35
  this.id = 0;
35
36
  if (!ctx.cookie)
36
37
  throw new ZaloApiError.ZaloApiError("Cookie is not available");
@@ -82,26 +83,71 @@ class Listener extends EventEmitter {
82
83
  onMessage(cb) {
83
84
  this.onMessageCallback = cb;
84
85
  }
86
+ isRetryable(code) {
87
+ return (code === exports.CloseReason.AbnormalClosure ||
88
+ this.ctx.settings.features.socket.close_and_retry_codes.includes(code));
89
+ }
85
90
  canRetry(code) {
86
- if (!this.ctx.settings.features.socket.close_and_retry_codes.includes(code))
91
+ var _a;
92
+ if (!this.isRetryable(code))
87
93
  return false;
88
- if (this.retryCount[code.toString()].count >= this.retryCount[code.toString()].max)
94
+ const retry = (_a = this.retryCount[code.toString()]) !== null && _a !== void 0 ? _a : this.retryCount["internal"];
95
+ if (!retry || retry.count >= retry.max)
89
96
  return false;
90
- this.retryCount[code.toString()].count++;
91
- const { count, max, times } = this.retryCount[code.toString()];
92
- const retryTime = count - 1 < times.length ? times[count - 1] : times[times.length - 1];
97
+ retry.count++;
98
+ const { count, max, times } = retry;
99
+ let retryTime;
100
+ if (count - 1 < times.length) {
101
+ retryTime = times[count - 1];
102
+ }
103
+ else {
104
+ const lastTime = times[times.length - 1] || 1000;
105
+ const multiplier = Math.min(Math.pow(1.5, count - times.length), 10);
106
+ const jitter = Math.floor(Math.random() * 1000);
107
+ retryTime = Math.min(Math.floor(lastTime * multiplier) + jitter, 30000);
108
+ }
93
109
  utils.logger(this.ctx).verbose(`Retry for code ${code} in ${retryTime}ms (${count}/${max})`);
94
110
  return retryTime;
95
111
  }
112
+ resetRetryCount() {
113
+ for (const retry of Object.values(this.retryCount)) {
114
+ retry.count = 0;
115
+ }
116
+ }
117
+ resetWatchdog() {
118
+ if (this.watchdogTimeout)
119
+ clearTimeout(this.watchdogTimeout);
120
+ this.watchdogTimeout = setTimeout(() => {
121
+ utils.logger(this.ctx).verbose("Watchdog timeout: no packet/pong received from Zalo WebSocket in 50s. Forcing reconnect...");
122
+ if (this.ws) {
123
+ try {
124
+ if (typeof this.ws.terminate === "function") {
125
+ this.ws.terminate();
126
+ }
127
+ else if (typeof this.ws.close === "function") {
128
+ this.ws.close(exports.CloseReason.AbnormalClosure);
129
+ }
130
+ }
131
+ catch (_a) {
132
+ // Ignore if already terminated
133
+ }
134
+ }
135
+ }, 50000);
136
+ }
96
137
  shouldRotate(code) {
97
- if (!this.ctx.settings.features.socket.rotate_error_codes.includes(code))
98
- return false;
99
- if (this.rotateCount >= this.urls.length - 1)
138
+ if (code === exports.CloseReason.AbnormalClosure) {
139
+ this.consecutive1006Count++;
140
+ if (this.consecutive1006Count >= 2) {
141
+ this.consecutive1006Count = 0;
142
+ return true;
143
+ }
100
144
  return false;
101
- return true;
145
+ }
146
+ this.consecutive1006Count = 0;
147
+ return this.ctx.settings.features.socket.rotate_error_codes.includes(code);
102
148
  }
103
149
  rotateEndpoint() {
104
- this.rotateCount++;
150
+ this.rotateCount = (this.rotateCount + 1) % this.urls.length;
105
151
  this.wsURL = utils.makeURL(this.ctx, this.urls[this.rotateCount], {
106
152
  t: Date.now(),
107
153
  });
@@ -110,37 +156,46 @@ class Listener extends EventEmitter {
110
156
  start({ retryOnClose = false } = {}) {
111
157
  if (this.ws)
112
158
  throw new ZaloApiError.ZaloApiError("Already started");
159
+ this.wsURL = utils.makeURL(this.ctx, this.urls[this.rotateCount], {
160
+ t: Date.now(),
161
+ });
162
+ const cookie = this.ctx.cookie.getCookieStringSync("https://chat.zalo.me");
113
163
  const ws = new WebSocket(this.wsURL, {
114
164
  headers: {
115
- "accept-encoding": "gzip, deflate, br, zstd",
116
- "accept-language": "en-US,en;q=0.9",
165
+ "accept-language": utils.getBrowserLanguage(this.ctx.language),
117
166
  "cache-control": "no-cache",
118
167
  connection: "Upgrade",
119
168
  host: new URL(this.wsURL).host,
120
169
  origin: "https://chat.zalo.me",
121
- prgama: "no-cache",
170
+ pragma: "no-cache",
122
171
  "sec-websocket-extensions": "permessage-deflate; client_max_window_bits",
123
172
  "sec-websocket-version": "13",
124
173
  upgrade: "websocket",
125
174
  "user-agent": this.userAgent,
126
- cookie: this.cookie,
175
+ cookie,
127
176
  },
128
- agent: this.ctx.options.agent
177
+ agent: this.ctx.options.agent,
129
178
  });
130
179
  this.ws = ws;
131
180
  ws.onopen = () => {
181
+ this.consecutive1006Count = 0;
182
+ this.resetRetryCount();
183
+ this.resetWatchdog();
132
184
  this.onConnectedCallback();
133
185
  this.emit("connected");
134
186
  };
135
187
  ws.onclose = (event) => {
136
188
  this.reset();
137
189
  this.emit("disconnected", event.code, event.reason);
138
- const retry = retryOnClose && this.canRetry(event.code);
190
+ const retry = retryOnClose &&
191
+ this.isRetryable(event.code) &&
192
+ this.canRetry(event.code);
139
193
  if (retry && retryOnClose) {
140
194
  const shouldRotate = this.shouldRotate(event.code);
141
195
  if (shouldRotate) {
142
196
  this.rotateEndpoint();
143
197
  }
198
+ this.emit("reconnecting", event.code);
144
199
  this.retryTimeout = setTimeout(() => {
145
200
  this.start({ retryOnClose: true });
146
201
  }, retry);
@@ -155,6 +210,8 @@ class Listener extends EventEmitter {
155
210
  this.emit("error", event);
156
211
  };
157
212
  ws.onmessage = async (event) => {
213
+ var _a, _b, _c;
214
+ this.resetWatchdog();
158
215
  this.emit("pong"); // Pulse event to notify watchdog that socket is alive
159
216
  const { data } = event;
160
217
  if (!(data instanceof Buffer))
@@ -182,9 +239,10 @@ class Listener extends EventEmitter {
182
239
  this.sendWs(payload, false);
183
240
  this.emit("ping");
184
241
  };
242
+ const pingIntervalTime = Math.min(Math.max(((_c = (_b = (_a = this.ctx.settings) === null || _a === void 0 ? void 0 : _a.features) === null || _b === void 0 ? void 0 : _b.socket) === null || _c === void 0 ? void 0 : _c.ping_interval) || 25000, 10000), 25000);
185
243
  this.pingInterval = setInterval(() => {
186
244
  ping();
187
- }, this.ctx.settings.features.socket.ping_interval);
245
+ }, pingIntervalTime);
188
246
  }
189
247
  if (version == 1 && cmd == 501 && subCmd == 0) {
190
248
  const parsedData = (await utils.decodeEventData(parsed, this.cipherKey)).data;
@@ -381,6 +439,8 @@ class Listener extends EventEmitter {
381
439
  stop() {
382
440
  if (this.retryTimeout)
383
441
  clearTimeout(this.retryTimeout);
442
+ if (this.watchdogTimeout)
443
+ clearTimeout(this.watchdogTimeout);
384
444
  if (this.ws) {
385
445
  if (this.ws.readyState !== WebSocket.CLOSED) {
386
446
  this.ws.close(exports.CloseReason.ManualClosure);
@@ -437,6 +497,8 @@ class Listener extends EventEmitter {
437
497
  this.cipherKey = undefined;
438
498
  if (this.pingInterval)
439
499
  clearInterval(this.pingInterval);
500
+ if (this.watchdogTimeout)
501
+ clearTimeout(this.watchdogTimeout);
440
502
  }
441
503
  }
442
504
  function getHeader(buffer) {
@@ -6,7 +6,7 @@ var utils = require('../utils.cjs');
6
6
  async function login(ctx, encryptParams) {
7
7
  const encryptedParams = await getEncryptParam(ctx, encryptParams, "getlogininfo");
8
8
  try {
9
- const response = await utils.request(ctx, utils.makeURL(ctx, "https://wpa.chat.zalo.me/api/login/getLoginInfo", Object.assign(Object.assign({}, encryptedParams.params), { nretry: 0 })));
9
+ const response = await utils.request(ctx, utils.makeURL(ctx, "https://wpa.chat.zalo.me/api/login/getLoginInfo", Object.assign(Object.assign({}, encryptedParams.params), { nretry: 0 }), false));
10
10
  if (!response.ok)
11
11
  throw new ZaloApiError.ZaloApiError("Failed to fetch login info: " + response.statusText);
12
12
  const data = await response.json();
@@ -8,31 +8,16 @@ var ZaloApiLoginQRAborted = require('../Errors/ZaloApiLoginQRAborted.cjs');
8
8
  var ZaloApiLoginQRDeclined = require('../Errors/ZaloApiLoginQRDeclined.cjs');
9
9
 
10
10
  /**
11
- * Tạo bộ header Sec-CH-UA dùng chung cho toàn bộ các request trong luồng đăng nhập QR.
11
+ * Tạo bộ header Sec-CH-UA và ngôn ngữ dùng chung cho toàn bộ các request trong luồng đăng nhập QR.
12
12
  *
13
- * Trước đây 8 hàm con trong loginQR đều copy-paste cùng một bộ header với giá trị
14
- * `sec-ch-ua-platform: "Windows"` bị hardcode cứng. Điều này gây ra mâu thuẫn fingerprint
15
- * (Browser Mismatch) khi người dùng truyền vào User-Agent của Mac hoặc Linux,
16
- * khiến hệ thống chống bot của Zalo phát hiện và ban session ngay lập tức.
13
+ * Tất cả giá trị sec-ch-ua-* đều được suy luận ĐỘNG từ ctx.userAgent (chỉ khi là Chromium)
14
+ * và accept-language theo ctx.language, đảm bảo nhất quán 100% với môi trường trình duyệt thật.
17
15
  *
18
- * Giải pháp: Tất cả giá trị sec-ch-ua-* đều được suy luận ĐỘNG từ ctx.userAgent,
19
- * đảm bảo nhất quán 100% giữa User-Agent và các Client Hint headers.
20
- *
21
- * @param ctx - Context chứa ctx.userAgent đã được gán trước khi gọi hàm này
16
+ * @param ctx - Context chứa ctx.userAgent và ctx.language
22
17
  * @param extra - Các header bổ sung đặc thù cho từng request (sec-fetch-dest, Referer...)
23
18
  */
24
19
  function getLoginHeaders(ctx, extra) {
25
- // Suy luận platform từ UA: "Windows" | "macOS" | "Linux" (mặc định "Windows" nếu không nhận ra)
26
- const platform = utils.getPlatformFromUA(ctx.userAgent);
27
- // Trích xuất phiên bản Chrome major từ UA (ví dụ: "130"), fallback về "130" nếu không phải Chrome
28
- const chromeVer = utils.getChromeVersionFromUA(ctx.userAgent);
29
- return Object.assign({ "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5",
30
- // sec-ch-ua phải khớp với phiên bản Chrome trong User-Agent
31
- "sec-ch-ua": `"Chromium";v="${chromeVer}", "Google Chrome";v="${chromeVer}", "Not?A_Brand";v="99"`,
32
- // Luôn là desktop (?0) vì Zalo Web không hỗ trợ mobile
33
- "sec-ch-ua-mobile": "?0",
34
- // sec-ch-ua-platform PHẢI nhất quán với User-Agent — đây là nguyên nhân gốc rễ gây ban session
35
- "sec-ch-ua-platform": `"${platform}"` }, extra);
20
+ return Object.assign(Object.assign({ "accept-language": utils.getBrowserLanguage(ctx.language) }, utils.getSecChUaHeaders(ctx.userAgent)), extra);
36
21
  }
37
22
  exports.LoginQRCallbackEventType = void 0;
38
23
  (function (LoginQRCallbackEventType) {
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ var ZaloApiError = require('../Errors/ZaloApiError.cjs');
4
+ var utils = require('../utils.cjs');
5
+
6
+ const preloadConversationsFactory = utils.apiFactory()((api, ctx, utils) => {
7
+ const serviceURL = utils.makeURL(`${api.zpwServiceMap.conversation[0]}/api/preloadconvers/get-last-msgs`);
8
+ /**
9
+ * Preload last messages across all active conversations.
10
+ * Useful for initial state loading and catching up missed messages after reconnect.
11
+ *
12
+ * @throws {ZaloApiError}
13
+ */
14
+ return async function preloadConversations() {
15
+ const params = {
16
+ imei: ctx.imei,
17
+ };
18
+ const encryptedParams = utils.encodeAES(JSON.stringify(params));
19
+ if (!encryptedParams)
20
+ throw new ZaloApiError.ZaloApiError("Failed to encrypt params");
21
+ const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), {
22
+ method: "GET",
23
+ });
24
+ return utils.resolve(response);
25
+ };
26
+ });
27
+
28
+ exports.preloadConversationsFactory = preloadConversationsFactory;
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ var ZaloApiError = require('../Errors/ZaloApiError.cjs');
4
+ var utils = require('../utils.cjs');
5
+
6
+ const requestSyncFromPhoneFactory = utils.apiFactory()((api, ctx) => {
7
+ /**
8
+ * Gửi tín hiệu kích hoạt yêu cầu đồng bộ tin nhắn từ xa sang app điện thoại (Trường hợp 2)
9
+ *
10
+ * @throws {ZaloApiError}
11
+ */
12
+ return async function requestSyncFromPhone() {
13
+ if (!api.listener) {
14
+ throw new ZaloApiError.ZaloApiError("WebSocket listener is not initialized. Please call api.listener.start() first.");
15
+ }
16
+ // Gửi frame yêu cầu đồng bộ qua WebSocket
17
+ const syncPayload = {
18
+ version: 1,
19
+ cmd: 510,
20
+ subCmd: 0,
21
+ data: {
22
+ act: "sync_request",
23
+ imei: ctx.imei,
24
+ ts: Date.now(),
25
+ },
26
+ };
27
+ api.listener.sendWs(syncPayload, true);
28
+ return { success: true };
29
+ };
30
+ });
31
+
32
+ exports.requestSyncFromPhoneFactory = requestSyncFromPhoneFactory;
package/dist/cjs/apis.cjs CHANGED
@@ -23,6 +23,7 @@ var createNote = require('./apis/createNote.cjs');
23
23
  var createPoll = require('./apis/createPoll.cjs');
24
24
  var createProductCatalog = require('./apis/createProductCatalog.cjs');
25
25
  var createReminder = require('./apis/createReminder.cjs');
26
+ var decryptGids = require('./apis/decryptGids.cjs');
26
27
  var deleteAutoReply = require('./apis/deleteAutoReply.cjs');
27
28
  var deleteAvatar = require('./apis/deleteAvatar.cjs');
28
29
  var deleteCatalog = require('./apis/deleteCatalog.cjs');
@@ -32,6 +33,7 @@ var deleteMessage = require('./apis/deleteMessage.cjs');
32
33
  var deleteProductCatalog = require('./apis/deleteProductCatalog.cjs');
33
34
  var disableGroupLink = require('./apis/disableGroupLink.cjs');
34
35
  var disperseGroup = require('./apis/disperseGroup.cjs');
36
+ var downloadTransferSync = require('./apis/downloadTransferSync.cjs');
35
37
  var editNote = require('./apis/editNote.cjs');
36
38
  var editReminder = require('./apis/editReminder.cjs');
37
39
  var enableGroupLink = require('./apis/enableGroupLink.cjs');
@@ -96,6 +98,7 @@ var lastOnline = require('./apis/lastOnline.cjs');
96
98
  var leaveGroup = require('./apis/leaveGroup.cjs');
97
99
  var lockPoll = require('./apis/lockPoll.cjs');
98
100
  var parseLink = require('./apis/parseLink.cjs');
101
+ var preloadConversations = require('./apis/preloadConversations.cjs');
99
102
  var rejectFriendRequest = require('./apis/rejectFriendRequest.cjs');
100
103
  var removeFriend = require('./apis/removeFriend.cjs');
101
104
  var removeFriendAlias = require('./apis/removeFriendAlias.cjs');
@@ -105,6 +108,7 @@ var removeQuickMessage = require('./apis/removeQuickMessage.cjs');
105
108
  var removeReminder = require('./apis/removeReminder.cjs');
106
109
  var removeUnreadMark = require('./apis/removeUnreadMark.cjs');
107
110
  var removeUserFromGroup = require('./apis/removeUserFromGroup.cjs');
111
+ var requestSyncFromPhone = require('./apis/requestSyncFromPhone.cjs');
108
112
  var resetHiddenConversPin = require('./apis/resetHiddenConversPin.cjs');
109
113
  var reuseAvatar = require('./apis/reuseAvatar.cjs');
110
114
  var reviewPendingMemberRequest = require('./apis/reviewPendingMemberRequest.cjs');
@@ -174,6 +178,7 @@ class API {
174
178
  this.createPoll = createPoll.createPollFactory(ctx, this);
175
179
  this.createProductCatalog = createProductCatalog.createProductCatalogFactory(ctx, this);
176
180
  this.createReminder = createReminder.createReminderFactory(ctx, this);
181
+ this.decryptGids = decryptGids.decryptGidsFactory(ctx, this);
177
182
  this.deleteAutoReply = deleteAutoReply.deleteAutoReplyFactory(ctx, this);
178
183
  this.deleteAvatar = deleteAvatar.deleteAvatarFactory(ctx, this);
179
184
  this.deleteCatalog = deleteCatalog.deleteCatalogFactory(ctx, this);
@@ -183,6 +188,7 @@ class API {
183
188
  this.deleteProductCatalog = deleteProductCatalog.deleteProductCatalogFactory(ctx, this);
184
189
  this.disableGroupLink = disableGroupLink.disableGroupLinkFactory(ctx, this);
185
190
  this.disperseGroup = disperseGroup.disperseGroupFactory(ctx, this);
191
+ this.downloadTransferSync = downloadTransferSync.downloadTransferSyncFactory(ctx, this);
186
192
  this.editNote = editNote.editNoteFactory(ctx, this);
187
193
  this.editReminder = editReminder.editReminderFactory(ctx, this);
188
194
  this.enableGroupLink = enableGroupLink.enableGroupLinkFactory(ctx, this);
@@ -247,6 +253,7 @@ class API {
247
253
  this.leaveGroup = leaveGroup.leaveGroupFactory(ctx, this);
248
254
  this.lockPoll = lockPoll.lockPollFactory(ctx, this);
249
255
  this.parseLink = parseLink.parseLinkFactory(ctx, this);
256
+ this.preloadConversations = preloadConversations.preloadConversationsFactory(ctx, this);
250
257
  this.rejectFriendRequest = rejectFriendRequest.rejectFriendRequestFactory(ctx, this);
251
258
  this.removeFriend = removeFriend.removeFriendFactory(ctx, this);
252
259
  this.removeFriendAlias = removeFriendAlias.removeFriendAliasFactory(ctx, this);
@@ -256,6 +263,7 @@ class API {
256
263
  this.removeReminder = removeReminder.removeReminderFactory(ctx, this);
257
264
  this.removeUnreadMark = removeUnreadMark.removeUnreadMarkFactory(ctx, this);
258
265
  this.removeUserFromGroup = removeUserFromGroup.removeUserFromGroupFactory(ctx, this);
266
+ this.requestSyncFromPhone = requestSyncFromPhone.requestSyncFromPhoneFactory(ctx, this);
259
267
  this.resetHiddenConversPin = resetHiddenConversPin.resetHiddenConversPinFactory(ctx, this);
260
268
  this.reuseAvatar = reuseAvatar.reuseAvatarFactory(ctx, this);
261
269
  this.reviewPendingMemberRequest = reviewPendingMemberRequest.reviewPendingMemberRequestFactory(ctx, this);
@@ -12,7 +12,7 @@ class CallbacksMap extends Map {
12
12
  return super.set(key, value);
13
13
  }
14
14
  }
15
- const createContext = (apiType = 30, apiVersion = 671) => ({
15
+ const createContext = (apiType = 30, apiVersion = 689) => ({
16
16
  API_TYPE: apiType,
17
17
  API_VERSION: apiVersion,
18
18
  uploadCallbacks: new CallbacksMap(),