@softtynet/zca-js 2.3.6 → 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 (51) 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/getSyncData.js +1 -1
  13. package/dist/apis.d.ts +12 -0
  14. package/dist/apis.js +20 -0
  15. package/dist/cjs/apis/business/blockOA.cjs +33 -0
  16. package/dist/cjs/apis/business/convertOAIds.cjs +31 -0
  17. package/dist/cjs/apis/business/getOAProfile.cjs +69 -0
  18. package/dist/cjs/apis/business/unblockOA.cjs +33 -0
  19. package/dist/cjs/apis/chat/preloadConversations.cjs +14 -0
  20. package/dist/cjs/apis/sync/getSyncData.cjs +20 -1
  21. package/dist/cjs/apis.cjs +20 -0
  22. package/dist/cjs/constants/defaults.cjs +2 -2
  23. package/dist/cjs/constants/protocol.cjs +1 -1
  24. package/dist/cjs/index.cjs +10 -0
  25. package/dist/cjs/models/Message.cjs +16 -1
  26. package/dist/cjs/models/OAMessage.cjs +375 -0
  27. package/dist/cjs/realtime/handlers/control.handler.cjs +9 -1
  28. package/dist/cjs/realtime/handlers/message.handler.cjs +103 -37
  29. package/dist/cjs/realtime/listener.cjs +39 -18
  30. package/dist/cjs/utils.cjs +21 -2
  31. package/dist/cjs/zalo.cjs +21 -2
  32. package/dist/constants/defaults.d.ts +2 -2
  33. package/dist/constants/defaults.js +2 -2
  34. package/dist/constants/protocol.d.ts +1 -1
  35. package/dist/constants/protocol.js +1 -1
  36. package/dist/index.d.ts +4 -0
  37. package/dist/models/Message.d.ts +12 -1
  38. package/dist/models/Message.js +16 -1
  39. package/dist/models/OAMessage.d.ts +221 -0
  40. package/dist/models/OAMessage.js +363 -0
  41. package/dist/models/index.d.ts +1 -0
  42. package/dist/models/index.js +1 -0
  43. package/dist/realtime/handlers/control.handler.js +9 -1
  44. package/dist/realtime/handlers/message.handler.js +103 -37
  45. package/dist/realtime/listener.d.ts +9 -3
  46. package/dist/realtime/listener.js +39 -18
  47. package/dist/realtime/types.d.ts +15 -1
  48. package/dist/utils.js +1 -1
  49. package/dist/zalo.d.ts +1 -1
  50. package/dist/zalo.js +1 -1
  51. package/package.json +2 -1
@@ -17,64 +17,130 @@ require('../../models/CallEvent.cjs');
17
17
  var utils = require('../../utils.cjs');
18
18
  var wsCipher = require('../../crypto/ws-cipher.cjs');
19
19
 
20
- async function handleRealtimeMessagePacket(listener, ctx, selfListen, onMessageCallback, version, cmd, subCmd, parsed, cipherKey) {
21
- if (version == 1 && [501, 551].includes(cmd) && subCmd == 0) {
22
- const parsedData = (await wsCipher.decodeEventData(parsed, cipherKey)).data;
23
- const { msgs } = parsedData;
24
- for (const msg of msgs) {
25
- if (typeof msg.content == "object" && utils.hasOwn(msg.content, "deleteMsg")) {
26
- const undoObject = new Undo.Undo(ctx.uid, msg, false);
27
- if (undoObject.isSelf && !selfListen)
28
- continue;
29
- listener.emit("undo", undoObject);
30
- }
31
- else {
32
- const messageObject = new Message.UserMessage(ctx.uid, msg);
33
- if (messageObject.isSelf && !selfListen)
34
- continue;
35
- onMessageCallback(messageObject);
36
- listener.emit("message", messageObject);
20
+ function processIncomingUserMessages(listener, ctx, selfListen, onMessageCallback, messages, isPageMsg = false) {
21
+ for (const msg of messages) {
22
+ if (!msg)
23
+ continue;
24
+ if (typeof msg.content == "object" && msg.content !== null && utils.hasOwn(msg.content, "deleteMsg")) {
25
+ const undoObject = new Undo.Undo(ctx.uid, msg, false);
26
+ if (undoObject.isSelf && !selfListen)
27
+ continue;
28
+ listener.emit("undo", undoObject);
29
+ }
30
+ else {
31
+ const messageObject = new Message.UserMessage(ctx.uid, msg, isPageMsg);
32
+ if (messageObject.isSelf && !selfListen)
33
+ continue;
34
+ onMessageCallback(messageObject);
35
+ listener.emit("message", messageObject);
36
+ if (messageObject.isOAMessage()) {
37
+ listener.emit("oa_message", messageObject);
37
38
  }
38
39
  }
40
+ }
41
+ }
42
+ function processIncomingGroupMessages(listener, ctx, selfListen, onMessageCallback, groupMessages) {
43
+ for (const msg of groupMessages) {
44
+ if (!msg)
45
+ continue;
46
+ if (typeof msg.content == "object" && msg.content !== null && utils.hasOwn(msg.content, "deleteMsg")) {
47
+ const undoObject = new Undo.Undo(ctx.uid, msg, true);
48
+ if (undoObject.isSelf && !selfListen)
49
+ continue;
50
+ listener.emit("undo", undoObject);
51
+ }
52
+ else {
53
+ const messageObject = new Message.GroupMessage(ctx.uid, msg);
54
+ if (messageObject.isSelf && !selfListen)
55
+ continue;
56
+ onMessageCallback(messageObject);
57
+ listener.emit("message", messageObject);
58
+ }
59
+ }
60
+ }
61
+ async function handleRealtimeMessagePacket(listener, ctx, selfListen, onMessageCallback, version, cmd, subCmd, parsed, cipherKey) {
62
+ if (version == 1 && [501, 551].includes(cmd) && subCmd == 0) {
63
+ const decoded = await wsCipher.decodeEventData(parsed, cipherKey);
64
+ const parsedData = decoded?.data ?? decoded;
65
+ const msgs = (Array.isArray(parsedData?.msgs) ? parsedData.msgs : []);
66
+ const pageMsgs = (Array.isArray(parsedData?.pageMsgs) ? parsedData.pageMsgs : []);
67
+ if (msgs.length > 0)
68
+ processIncomingUserMessages(listener, ctx, selfListen, onMessageCallback, msgs, false);
69
+ if (pageMsgs.length > 0)
70
+ processIncomingUserMessages(listener, ctx, selfListen, onMessageCallback, pageMsgs, true);
39
71
  return true;
40
72
  }
41
73
  if (version == 1 && cmd == 521 && subCmd == 0) {
42
- const parsedData = (await wsCipher.decodeEventData(parsed, cipherKey)).data;
43
- const { groupMsgs } = parsedData;
44
- for (const msg of groupMsgs) {
45
- if (typeof msg.content == "object" && utils.hasOwn(msg.content, "deleteMsg")) {
46
- const undoObject = new Undo.Undo(ctx.uid, msg, true);
47
- if (undoObject.isSelf && !selfListen)
48
- continue;
49
- listener.emit("undo", undoObject);
50
- }
51
- else {
52
- const messageObject = new Message.GroupMessage(ctx.uid, msg);
53
- if (messageObject.isSelf && !selfListen)
54
- continue;
55
- onMessageCallback(messageObject);
56
- listener.emit("message", messageObject);
57
- }
74
+ const decoded = await wsCipher.decodeEventData(parsed, cipherKey);
75
+ const parsedData = decoded?.data ?? decoded;
76
+ const groupMsgs = (Array.isArray(parsedData?.groupMsgs) ? parsedData.groupMsgs : []);
77
+ processIncomingGroupMessages(listener, ctx, selfListen, onMessageCallback, groupMsgs);
78
+ return true;
79
+ }
80
+ // HAS_MSG (cmd = 533): Server báo có tin nhắn đang chờ (thường gặp với OA / Missed Queue) -> kéo ngay qua cmd = 570 (GET_MSG)
81
+ if (cmd == 533 && subCmd == 0) {
82
+ const decoded = await wsCipher.decodeEventData(parsed, cipherKey);
83
+ const parsedData = (decoded?.data ?? decoded);
84
+ if (parsedData?.queueId && Array.isArray(parsedData.ids) && parsedData.ids.length > 0) {
85
+ listener.requestMessagesByIds(parsedData.queueId, parsedData.ids);
86
+ }
87
+ return true;
88
+ }
89
+ // PUSH_MISS_MSG (cmd = 534) & GET_MSG (cmd = 570): Gói tin trả về các tin nhắn OA (`pageMsgs`), User (`msgs`) và Group (`groupMsgs`)
90
+ if ((cmd == 534 || cmd == 570) && subCmd == 0) {
91
+ const decoded = await wsCipher.decodeEventData(parsed, cipherKey);
92
+ const parsedData = decoded?.data ?? decoded;
93
+ const msgs = (Array.isArray(parsedData?.msgs) ? parsedData.msgs : []);
94
+ const pageMsgs = (Array.isArray(parsedData?.pageMsgs) ? parsedData.pageMsgs : []);
95
+ const groupMsgs = (Array.isArray(parsedData?.groupMsgs) ? parsedData.groupMsgs : []);
96
+ if (msgs.length > 0) {
97
+ processIncomingUserMessages(listener, ctx, selfListen, onMessageCallback, msgs, false);
98
+ }
99
+ if (pageMsgs.length > 0) {
100
+ processIncomingUserMessages(listener, ctx, selfListen, onMessageCallback, pageMsgs, true);
101
+ }
102
+ if (groupMsgs.length > 0) {
103
+ processIncomingGroupMessages(listener, ctx, selfListen, onMessageCallback, groupMsgs);
58
104
  }
59
105
  return true;
60
106
  }
61
107
  if (cmd == 510 && (subCmd == 0 || subCmd == 1)) {
62
108
  const decoded = await wsCipher.decodeEventData(parsed, cipherKey);
63
109
  const parsedData = decoded?.data ?? decoded;
64
- const msgs = (parsedData?.msgs ?? []);
110
+ const msgs = (Array.isArray(parsedData?.msgs) ? parsedData.msgs : []);
111
+ const pageMsgs = (Array.isArray(parsedData?.pageMsgs) ? parsedData.pageMsgs : []);
65
112
  const responseMsgs = [];
66
113
  for (const msg of msgs) {
67
- if (typeof msg.content == "object" && utils.hasOwn(msg.content, "deleteMsg")) {
114
+ if (typeof msg.content == "object" && msg.content !== null && utils.hasOwn(msg.content, "deleteMsg")) {
115
+ const undoObject = new Undo.Undo(ctx.uid, msg, false);
116
+ if (undoObject.isSelf && !selfListen)
117
+ continue;
118
+ listener.emit("undo", undoObject);
119
+ }
120
+ else {
121
+ const userMsg = new Message.UserMessage(ctx.uid, msg, false);
122
+ responseMsgs.push(userMsg);
123
+ if (userMsg.isOAMessage() && (!userMsg.isSelf || selfListen)) {
124
+ listener.emit("oa_message", userMsg);
125
+ }
126
+ }
127
+ }
128
+ for (const msg of pageMsgs) {
129
+ if (typeof msg.content == "object" && msg.content !== null && utils.hasOwn(msg.content, "deleteMsg")) {
68
130
  const undoObject = new Undo.Undo(ctx.uid, msg, false);
69
131
  if (undoObject.isSelf && !selfListen)
70
132
  continue;
71
133
  listener.emit("undo", undoObject);
72
134
  }
73
135
  else {
74
- responseMsgs.push(new Message.UserMessage(ctx.uid, msg));
136
+ const oaUserMsg = new Message.UserMessage(ctx.uid, msg, true);
137
+ responseMsgs.push(oaUserMsg);
138
+ if (oaUserMsg.isOAMessage() && (!oaUserMsg.isSelf || selfListen)) {
139
+ listener.emit("oa_message", oaUserMsg);
140
+ }
75
141
  }
76
142
  }
77
- if (responseMsgs.length > 0 || msgs.length === 0) {
143
+ if (responseMsgs.length > 0 || (msgs.length === 0 && pageMsgs.length === 0)) {
78
144
  listener.emit("old_messages", responseMsgs, Enum.ThreadType.User, parsedData?.more === 1);
79
145
  }
80
146
  return true;
@@ -330,18 +330,19 @@ class Listener extends EventEmitter {
330
330
  }
331
331
  sendWs(payload, requireId = true) {
332
332
  if (this.ws && (this.ws.readyState === undefined || this.ws.readyState === WebSocket.OPEN)) {
333
- if (requireId)
334
- payload.data["req_id"] = `req_${this.id++}`;
333
+ if (requireId) {
334
+ const reqId = `req_${this.id++}`;
335
+ payload.data["req_id"] = reqId;
336
+ payload.data["reqId"] = reqId;
337
+ }
335
338
  const encodedData = new TextEncoder().encode(JSON.stringify(payload.data));
336
339
  const dataLength = encodedData.length;
337
- const data = new DataView(Buffer.alloc(4 + dataLength).buffer);
338
- data.setUint8(0, payload.version);
339
- data.setInt32(1, payload.cmd, true);
340
- data.setInt8(3, payload.subCmd);
341
- encodedData.forEach((e, i) => {
342
- data.setUint8(4 + i, e);
343
- });
344
- this.ws.send(data);
340
+ const buf = Buffer.alloc(4 + dataLength);
341
+ buf.writeUInt8(payload.version, 0);
342
+ buf.writeUInt16LE(payload.cmd, 1);
343
+ buf.writeInt8(payload.subCmd, 3);
344
+ Buffer.from(encodedData).copy(buf, 4);
345
+ this.ws.send(buf);
345
346
  }
346
347
  }
347
348
  /**
@@ -376,16 +377,36 @@ class Listener extends EventEmitter {
376
377
  this.sendWs(payload);
377
378
  }
378
379
  /**
379
- * Kích hoạt và kéo hàng đợi sự kiện điều khiển / VoIP (`cmd 603` CTRL_PULL_CTRL và `cmd 604` CTRL_PULL_VOIP).
380
- * Giúp nhận ngay các cuộc gọi đến đang đổ chuông ngay cả khi vừa mở kết nối WebSocket.
380
+ * Kéo danh sách tin nhắn cụ thể theo `queueId` và mảng `ids` (`cmd 570` GET_MSG)
381
+ * khi nhận tín hiệu `cmd 533` (`HAS_MSG`) từ Zalo WebSocket (thường dùng cho tin nhắn OA / ZNS / Missed Queue).
382
+ * Tự động chia batch tối đa 20 ID mỗi gói theo đúng cấu hình `max_msg_get = 20` trên Zalo Desktop.
383
+ */
384
+ requestMessagesByIds(queueId, ids, lastId = "0") {
385
+ if (!queueId || !Array.isArray(ids) || ids.length === 0)
386
+ return;
387
+ const maxBatchSize = 20;
388
+ for (let i = 0; i < ids.length; i += maxBatchSize) {
389
+ const chunk = ids.slice(i, i + maxBatchSize);
390
+ const reqId = `req_${this.id++}`;
391
+ this.sendWs({
392
+ version: 1,
393
+ cmd: 570,
394
+ subCmd: 0,
395
+ data: {
396
+ queueId,
397
+ ids: chunk,
398
+ lastId,
399
+ reqId,
400
+ req_id: reqId,
401
+ },
402
+ }, false);
403
+ }
404
+ }
405
+ /**
406
+ * Kích hoạt và kéo hàng đợi sự kiện điều khiển / VoIP (`cmd 603` CTRL_PULL_CTRL).
407
+ * Giúp nhận ngay các cuộc gọi đến đang đổ chuông và sự kiện điều khiển ngay cả khi vừa mở kết nối WebSocket.
381
408
  */
382
409
  requestOfflineVoipControls() {
383
- this.sendWs({
384
- version: 1,
385
- cmd: 604,
386
- subCmd: 0,
387
- data: { first: true, lastId: 1, preIds: [] },
388
- });
389
410
  this.sendWs({
390
411
  version: 1,
391
412
  cmd: 603,
@@ -17,6 +17,25 @@ var defaults = require('./constants/defaults.cjs');
17
17
  var aes = require('./crypto/aes.cjs');
18
18
  var wsCipher = require('./crypto/ws-cipher.cjs');
19
19
 
20
+ function _interopNamespaceDefault(e) {
21
+ var n = Object.create(null);
22
+ if (e) {
23
+ Object.keys(e).forEach(function (k) {
24
+ if (k !== 'default') {
25
+ var d = Object.getOwnPropertyDescriptor(e, k);
26
+ Object.defineProperty(n, k, d.get ? d : {
27
+ enumerable: true,
28
+ get: function () { return e[k]; }
29
+ });
30
+ }
31
+ });
32
+ }
33
+ n.default = e;
34
+ return Object.freeze(n);
35
+ }
36
+
37
+ var toughCookie__namespace = /*#__PURE__*/_interopNamespaceDefault(toughCookie);
38
+
20
39
  const isBun = typeof Bun !== "undefined";
21
40
  function hasOwn(obj, key) {
22
41
  return Object.prototype.hasOwnProperty.call(obj, key);
@@ -143,7 +162,7 @@ async function getDefaultHeaders(ctx, origin = domains.ZALO_DOMAINS.CHAT_WEB) {
143
162
  }
144
163
  async function request(ctx, url, options, raw = false) {
145
164
  if (!ctx.cookie)
146
- ctx.cookie = new toughCookie.CookieJar();
165
+ ctx.cookie = new toughCookie__namespace.CookieJar();
147
166
  const origin = new URL(url).origin;
148
167
  const defaultHeaders = await getDefaultHeaders(ctx, origin);
149
168
  if (!raw) {
@@ -208,7 +227,7 @@ async function request(ctx, url, options, raw = false) {
208
227
  cookieStrings = setCookieRaw.split(/,\s*(?=[a-zA-Z0-9_-]+\s*=)/);
209
228
  }
210
229
  for (const cookie of cookieStrings) {
211
- const parsed = toughCookie.Cookie.parse(cookie);
230
+ const parsed = toughCookie__namespace.Cookie.parse(cookie);
212
231
  try {
213
232
  if (parsed)
214
233
  await ctx.cookie.setCookie(parsed, parsed.domain != "zalo.me" ? `https://${parsed.domain}` : origin);
package/dist/cjs/zalo.cjs CHANGED
@@ -11,6 +11,25 @@ var apis = require('./apis.cjs');
11
11
  var domains = require('./constants/domains.cjs');
12
12
  var defaults = require('./constants/defaults.cjs');
13
13
 
14
+ function _interopNamespaceDefault(e) {
15
+ var n = Object.create(null);
16
+ if (e) {
17
+ Object.keys(e).forEach(function (k) {
18
+ if (k !== 'default') {
19
+ var d = Object.getOwnPropertyDescriptor(e, k);
20
+ Object.defineProperty(n, k, d.get ? d : {
21
+ enumerable: true,
22
+ get: function () { return e[k]; }
23
+ });
24
+ }
25
+ });
26
+ }
27
+ n.default = e;
28
+ return Object.freeze(n);
29
+ }
30
+
31
+ var toughCookie__namespace = /*#__PURE__*/_interopNamespaceDefault(toughCookie);
32
+
14
33
  class Zalo {
15
34
  options;
16
35
  enableEncryptParam = true;
@@ -23,10 +42,10 @@ class Zalo {
23
42
  if (typeof e.domain == "string" && e.domain.startsWith("."))
24
43
  cookieArr[i].domain = e.domain.slice(1);
25
44
  });
26
- const jar = new toughCookie.CookieJar();
45
+ const jar = new toughCookie__namespace.CookieJar();
27
46
  for (const each of cookieArr) {
28
47
  try {
29
- const cookieObj = toughCookie.Cookie.fromJSON({
48
+ const cookieObj = toughCookie__namespace.Cookie.fromJSON({
30
49
  ...each,
31
50
  key: each.key || each.name,
32
51
  });
@@ -35,8 +35,8 @@ export declare const ZALO_DEFAULTS: {
35
35
  /** Số lần lỗi 1006 liên tiếp trước khi xoay sang WebSocket endpoint tiếp theo */
36
36
  readonly CONSECUTIVE_1006_ROTATE_THRESHOLD: 2;
37
37
  /** Con trỏ mặc định khi bắt đầu kéo hàng đợi tin nhắn cũ (cmd 510 / 511) */
38
- readonly OLD_MSG_INITIAL_CURSOR: "10000000000000000";
39
- readonly INITIAL_OLD_MESSAGES_CURSOR: "10000000000000000";
38
+ readonly OLD_MSG_INITIAL_CURSOR: "1";
39
+ readonly INITIAL_OLD_MESSAGES_CURSOR: "1";
40
40
  };
41
41
  readonly UPLOAD: {
42
42
  /** Thời gian sống tối đa của uploadCallback trong bộ nhớ trước khi tự hủy (5 phút) */
@@ -35,8 +35,8 @@ export const ZALO_DEFAULTS = {
35
35
  /** Số lần lỗi 1006 liên tiếp trước khi xoay sang WebSocket endpoint tiếp theo */
36
36
  CONSECUTIVE_1006_ROTATE_THRESHOLD: 2,
37
37
  /** Con trỏ mặc định khi bắt đầu kéo hàng đợi tin nhắn cũ (cmd 510 / 511) */
38
- OLD_MSG_INITIAL_CURSOR: "10000000000000000",
39
- INITIAL_OLD_MESSAGES_CURSOR: "10000000000000000",
38
+ OLD_MSG_INITIAL_CURSOR: "1",
39
+ INITIAL_OLD_MESSAGES_CURSOR: "1",
40
40
  },
41
41
  UPLOAD: {
42
42
  /** Thời gian sống tối đa của uploadCallback trong bộ nhớ trước khi tự hủy (5 phút) */
@@ -10,7 +10,7 @@ export declare const ZALO_PROTOCOL: {
10
10
  readonly API_VERSION: 689;
11
11
  readonly DEFAULT_API_VERSION: 689;
12
12
  /** Phiên bản SDK hiện tại */
13
- readonly SDK_VERSION: "2.3.6";
13
+ readonly SDK_VERSION: "2.3.7";
14
14
  /** Mã zpw_type đặc thù dành riêng cho phân hệ Gọi Thoại & Video (binary C++ ZaloCall) */
15
15
  readonly VOICECALL_API_TYPE: 24;
16
16
  /** Phiên bản mã hóa ZCID (ParamDecryptor) */
@@ -10,7 +10,7 @@ export const ZALO_PROTOCOL = {
10
10
  API_VERSION: 689,
11
11
  DEFAULT_API_VERSION: 689,
12
12
  /** Phiên bản SDK hiện tại */
13
- SDK_VERSION: "2.3.6",
13
+ SDK_VERSION: "2.3.7",
14
14
  /** Mã zpw_type đặc thù dành riêng cho phân hệ Gọi Thoại & Video (binary C++ ZaloCall) */
15
15
  VOICECALL_API_TYPE: 24,
16
16
  /** Phiên bản mã hóa ZCID (ParamDecryptor) */
package/dist/index.d.ts CHANGED
@@ -256,6 +256,10 @@ export type { SendRequestVoiceCallOptions, SendRequestVoiceCallResponse } from "
256
256
  export type { ShareScreenGroupCallOptions, ShareScreenGroupCallResponse } from "./apis/call/shareScreenGroupCall.js";
257
257
  export type { UnholdVoiceCallOptions, UnholdVoiceCallResponse } from "./apis/call/unholdVoiceCall.js";
258
258
  export type { UpdateCallMediaStateOptions, UpdateCallMediaStateResponse } from "./apis/call/updateCallMediaState.js";
259
+ export type { BlockOAResponse } from "./apis/business/blockOA.js";
260
+ export type { UnblockOAResponse } from "./apis/business/unblockOA.js";
261
+ export type { ConvertOAIdsResponse } from "./apis/business/convertOAIds.js";
262
+ export type { GetOAProfileOptions, GetOAProfileResponse } from "./apis/business/getOAProfile.js";
259
263
  export { CloseReason } from "./apis/listen.js";
260
264
  export { LoginQRCallbackEventType } from "./apis/loginQR.js";
261
265
  export { FriendRecommendationsType } from "./apis/friend/getFriendRecommendations.js";
@@ -1,4 +1,5 @@
1
1
  import { ThreadType } from "./Enum.js";
2
+ import { type ParsedOAMessage } from "./OAMessage.js";
2
3
  export type TAttachmentContent = {
3
4
  title: string;
4
5
  description: string;
@@ -76,7 +77,17 @@ export declare class UserMessage {
76
77
  * true if the message is sent by the logged in account
77
78
  */
78
79
  isSelf: boolean;
79
- constructor(uid: string, data: TMessage);
80
+ /**
81
+ * 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`).
82
+ */
83
+ oaData?: ParsedOAMessage;
84
+ constructor(uid: string, data: TMessage, isPageMsg?: boolean);
85
+ /**
86
+ * Kiểm tra xem tin nhắn có phải là tin nhắn Zalo Official Account (OA) hay không.
87
+ */
88
+ isOAMessage(): this is UserMessage & {
89
+ oaData: ParsedOAMessage;
90
+ };
80
91
  }
81
92
  export declare class GroupMessage {
82
93
  type: ThreadType.Group;
@@ -1,4 +1,5 @@
1
1
  import { ThreadType } from "./Enum.js";
2
+ import { parseOAMessage } from "./OAMessage.js";
2
3
  export class UserMessage {
3
4
  type = ThreadType.User;
4
5
  data;
@@ -7,7 +8,11 @@ export class UserMessage {
7
8
  * true if the message is sent by the logged in account
8
9
  */
9
10
  isSelf;
10
- constructor(uid, data) {
11
+ /**
12
+ * 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`).
13
+ */
14
+ oaData;
15
+ constructor(uid, data, isPageMsg = false) {
11
16
  this.data = data;
12
17
  this.threadId = data.uidFrom == "0" ? data.idTo : data.uidFrom;
13
18
  this.isSelf = data.uidFrom == "0";
@@ -18,6 +23,16 @@ export class UserMessage {
18
23
  if (data.quote) {
19
24
  data.quote.ownerId = String(data.quote.ownerId);
20
25
  }
26
+ const parsedOA = parseOAMessage(data, { isPageMsg });
27
+ if (parsedOA) {
28
+ this.oaData = parsedOA;
29
+ }
30
+ }
31
+ /**
32
+ * Kiểm tra xem tin nhắn có phải là tin nhắn Zalo Official Account (OA) hay không.
33
+ */
34
+ isOAMessage() {
35
+ return this.oaData !== undefined;
21
36
  }
22
37
  }
23
38
  export class GroupMessage {
@@ -0,0 +1,221 @@
1
+ export type OAHighlightItem = string | number | {
2
+ type?: number;
3
+ uid?: string;
4
+ dpn?: string;
5
+ text?: string;
6
+ value?: string;
7
+ ts?: number;
8
+ [key: string]: unknown;
9
+ };
10
+ export type OACustomMessage = {
11
+ msg?: {
12
+ vi?: string;
13
+ en?: string;
14
+ [lang: string]: string | undefined;
15
+ };
16
+ previewTxt?: string;
17
+ highLightsV2?: OAHighlightItem[];
18
+ [key: string]: unknown;
19
+ };
20
+ export type OAListActionItem = {
21
+ type?: string;
22
+ title?: string;
23
+ description?: string;
24
+ thumb?: string;
25
+ href?: string;
26
+ childnumber?: number;
27
+ action?: string;
28
+ params?: string | Record<string, unknown>;
29
+ childAction?: unknown[];
30
+ [key: string]: unknown;
31
+ };
32
+ export type OAFooterItem = {
33
+ title?: string;
34
+ label?: string;
35
+ text?: string;
36
+ type?: number | string;
37
+ action?: string;
38
+ href?: string;
39
+ url?: string;
40
+ data?: unknown;
41
+ [key: string]: unknown;
42
+ };
43
+ export type OAFooterData = {
44
+ type?: number;
45
+ canForward: boolean;
46
+ footerList: OAFooterItem[];
47
+ raw?: unknown;
48
+ };
49
+ export type OAMessageKind = "oa_chat" | "oa_list" | "info_chat" | "ecard";
50
+ export type ParsedOAMessage = {
51
+ /**
52
+ * Phân loại tin nhắn Zalo Official Account (OA):
53
+ * - `"oa_chat"`: Tin nhắn hội thoại trực tiếp 2 chiều từ/đến Zalo OA (`pageMsgs`, có `footerv2`, hoặc `topOut === "1"`).
54
+ * - `"oa_list"`: `msgType === "chat.list.action"` (Danh sách bài viết / thẻ hành động đa phương tiện từ OA).
55
+ * - `"info_chat"`: `msgType === "webchat"` với `content.action === "msginfo.actionlist"` hoặc `"msginfo.action"` (Thông báo sự kiện/trạng thái OA).
56
+ * - `"ecard"`: `msgType === "chat.ecard"` (Thiệp chúc mừng / thông báo sinh nhật / sự kiện OA).
57
+ */
58
+ kind: OAMessageKind;
59
+ /**
60
+ * Nội dung văn bản hiển thị đã được giải mã và điền đầy đủ các biến `%1$s` / `$1$s`.
61
+ */
62
+ text: string;
63
+ /**
64
+ * Văn bản thông báo tiếng Việt (nếu có trong `customMsg.msg.vi`).
65
+ */
66
+ textVi?: string;
67
+ /**
68
+ * Văn bản thông báo tiếng Anh (nếu có trong `customMsg.msg.en`).
69
+ */
70
+ textEn?: string;
71
+ /**
72
+ * Dữ liệu chi tiết đính kèm trong `content` / `params` của tin nhắn OA.
73
+ */
74
+ bundleData: Record<string, unknown>;
75
+ /**
76
+ * Danh sách các thẻ bài viết / nút hành động (dành cho `chat.list.action`).
77
+ */
78
+ items?: OAListActionItem[];
79
+ /**
80
+ * Cấu hình chân trang tương tác (`footerv2`) đính kèm tin nhắn OA (danh sách nút bấm `footerList`, cờ `canForward`).
81
+ */
82
+ footer?: OAFooterData;
83
+ /**
84
+ * Cờ đánh dấu tin nhắn nổi bật từ OA (`topOut === "1"`).
85
+ */
86
+ topOut?: boolean;
87
+ /**
88
+ * Cờ cho phép trả lời trực tiếp tin nhắn OA.
89
+ */
90
+ canReply?: boolean;
91
+ /**
92
+ * Dữ liệu `customMsg` / `params` nguyên bản sau khi parse JSON.
93
+ */
94
+ metadata?: {
95
+ customMsg?: OACustomMessage;
96
+ [key: string]: unknown;
97
+ };
98
+ };
99
+ export type OAFieldEntry = {
100
+ label?: string;
101
+ value?: string;
102
+ icon?: string;
103
+ [key: string]: unknown;
104
+ };
105
+ export type OARawInfo = {
106
+ description?: string | null;
107
+ appUrl?: string | null;
108
+ hotline?: OAFieldEntry | null;
109
+ category?: OAFieldEntry | null;
110
+ website?: OAFieldEntry | null;
111
+ address?: OAFieldEntry | null;
112
+ openTime?: OAFieldEntry | null;
113
+ group_msg?: number;
114
+ [key: string]: unknown;
115
+ };
116
+ export type OADnrStatus = {
117
+ isBlocked: boolean;
118
+ enableDnr: boolean;
119
+ dnrType: number | null;
120
+ dnrText: string | null;
121
+ raw?: unknown;
122
+ };
123
+ export type OAProfileInfo = {
124
+ oaId: string;
125
+ displayName: string;
126
+ zaloName: string;
127
+ username: string;
128
+ avatar: string;
129
+ cover: string;
130
+ globalId: string;
131
+ type: number;
132
+ isOA: boolean;
133
+ isVerified: boolean;
134
+ isZaloBot: boolean;
135
+ isBlocked: boolean;
136
+ isGroupedMsg: boolean;
137
+ description: string;
138
+ category: string;
139
+ address: string;
140
+ website: string;
141
+ hotline: string;
142
+ openTime: string;
143
+ email: string;
144
+ appUrl: string | null;
145
+ dnrStatus: OADnrStatus;
146
+ oaInfo: OARawInfo | null;
147
+ rawProfile: Record<string, unknown>;
148
+ bizAccount?: Record<string, unknown> | null;
149
+ };
150
+ export type TOAEventData = {
151
+ uids: string[];
152
+ [key: string]: unknown;
153
+ };
154
+ export type OAEvent = {
155
+ act: "block" | "unblock" | string;
156
+ act_type: "oa";
157
+ data: TOAEventData;
158
+ isBlocked: boolean;
159
+ oaIds: string[];
160
+ };
161
+ /**
162
+ * Điền các biến `%1$s`, `%2$s`... và `$1$s`, `$2$s`... trong mẫu chuỗi thông báo Zalo OA
163
+ * theo chuẩn hàm `_renderItemReplace` trên Zalo PC.
164
+ */
165
+ export declare function formatOAHighlightText(templateText?: string, highLightsV2?: OAHighlightItem[]): string;
166
+ /**
167
+ * Bóc tách cấu hình chân trang tương tác `footerv2` / `footer` của tin nhắn Zalo OA
168
+ * (`footerList`, `canForward`, `type`).
169
+ */
170
+ export declare function parseOAFooter(rawFooter: unknown): OAFooterData | undefined;
171
+ /**
172
+ * Kiểm tra xem một hồ sơ người dùng có phải là Zalo Official Account (OA) hay không.
173
+ * Theo chuẩn `isOA(profile)` trên Zalo PC: `profile.type > 0` hoặc có `profile.oaInfo`.
174
+ */
175
+ export declare function isOAProfile(profile: {
176
+ type?: number;
177
+ oaInfo?: unknown;
178
+ } | null | undefined): boolean;
179
+ /**
180
+ * Kiểm tra xem Zalo OA có tích vàng xác thực (Verified OA) hay không.
181
+ * Theo chuẩn `isOAVerifiedByInfo(profile)` trên Zalo PC (`@10388120`):
182
+ * Chuyển `profile.type` sang nhị phân (`Number(type).toString(2)`), kiểm tra độ dài >= 3 và ký tự index `2` là `"1"`.
183
+ * Ví dụ: `type = 7` (`"111"`) -> Verified OA; `type = 5` (`"101"`) -> Verified OA.
184
+ */
185
+ export declare function isOAVerified(profileOrType: {
186
+ type?: number;
187
+ } | number | null | undefined): boolean;
188
+ /**
189
+ * Kiểm tra xem tài khoản có phải là Zalo Bot hay không.
190
+ * Theo chuẩn `isZaloBotByInfo(profile)` trên Zalo PC (`@10388180`):
191
+ * Chuyển `profile.type` sang nhị phân (`Number(type).toString(2)`), kiểm tra độ dài >= 4 và ký tự đầu tiên là `"1"`.
192
+ */
193
+ export declare function isZaloBot(profileOrType: {
194
+ type?: number;
195
+ } | number | null | undefined): boolean;
196
+ /**
197
+ * Giải mã trạng thái chặn & Do-Not-Disturb (`oa_status`) của Zalo OA
198
+ * theo chuẩn `_getOAStatus` trên Zalo PC (`@1719856`).
199
+ */
200
+ export declare function parseOADnrStatus(oaStatus: unknown, fallbackIsBlocked?: number | boolean): OADnrStatus;
201
+ /**
202
+ * Chuẩn hóa dữ liệu hồ sơ Zalo Official Account (`getUserInfo` + `getBizAccount`) thành cấu trúc `OAProfileInfo` đồng nhất.
203
+ */
204
+ export declare function normalizeOAProfile(oaId: string, rawProfile?: Record<string, unknown> | null, bizAccount?: Record<string, unknown> | null): OAProfileInfo;
205
+ /**
206
+ * Bóc tách và giải mã các loại tin nhắn từ Zalo Official Account (OA):
207
+ * - Danh sách bài viết / thẻ hành động OA (`chat.list.action`, bao gồm `l.a.header.full`, `l.a.normal`)
208
+ * - Thông báo sự kiện OA (`webchat` với `action === "msginfo.actionlist"` hoặc `"msginfo.action"`)
209
+ * - Thiệp chúc mừng / sự kiện OA (`chat.ecard`)
210
+ * - Tin nhắn hội thoại trực tiếp 2 chiều với OA (`pageMsgs`, có `footerv2`, hoặc `topOut === "1"`)
211
+ *
212
+ * Trả về `undefined` nếu tin nhắn là tin nhắn cá nhân thông thường không thuộc Zalo OA.
213
+ */
214
+ export declare function parseOAMessage(msg: {
215
+ msgType: string;
216
+ content: unknown;
217
+ [key: string]: unknown;
218
+ }, options?: {
219
+ isPageMsg?: boolean;
220
+ }): ParsedOAMessage | undefined;
221
+ export declare function initializeOAEvent(data: TOAEventData, act: string): OAEvent;