@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.
- package/README.md +25 -2
- package/dist/apis/business/blockOA.d.ts +2 -0
- package/dist/apis/business/blockOA.js +28 -0
- package/dist/apis/business/convertOAIds.d.ts +4 -0
- package/dist/apis/business/convertOAIds.js +26 -0
- package/dist/apis/business/getOAProfile.d.ts +13 -0
- package/dist/apis/business/getOAProfile.js +64 -0
- package/dist/apis/business/unblockOA.d.ts +2 -0
- package/dist/apis/business/unblockOA.js +28 -0
- package/dist/apis/chat/preloadConversations.d.ts +3 -2
- package/dist/apis/chat/preloadConversations.js +14 -0
- package/dist/apis/sync/downloadTransferSync.js +19 -1
- package/dist/apis/sync/getSyncData.js +1 -1
- package/dist/apis.d.ts +12 -0
- package/dist/apis.js +20 -0
- package/dist/call-engine/ZaloCallTransport.js +28 -2
- package/dist/call-engine/ZaloRtcSession.js +9 -1
- package/dist/cjs/apis/business/blockOA.cjs +33 -0
- package/dist/cjs/apis/business/convertOAIds.cjs +31 -0
- package/dist/cjs/apis/business/getOAProfile.cjs +69 -0
- package/dist/cjs/apis/business/unblockOA.cjs +33 -0
- package/dist/cjs/apis/chat/preloadConversations.cjs +14 -0
- package/dist/cjs/apis/sync/downloadTransferSync.cjs +19 -1
- package/dist/cjs/apis/sync/getSyncData.cjs +20 -1
- package/dist/cjs/apis.cjs +20 -0
- package/dist/cjs/call-engine/ZaloCallTransport.cjs +28 -2
- package/dist/cjs/call-engine/ZaloRtcSession.cjs +9 -1
- package/dist/cjs/constants/defaults.cjs +2 -2
- package/dist/cjs/constants/protocol.cjs +1 -1
- package/dist/cjs/crypto/aes.cjs +3 -1
- package/dist/cjs/crypto/zdb-cipher.cjs +10 -3
- package/dist/cjs/index.cjs +10 -0
- package/dist/cjs/models/Message.cjs +16 -1
- package/dist/cjs/models/OAMessage.cjs +375 -0
- package/dist/cjs/realtime/handlers/control.handler.cjs +9 -1
- package/dist/cjs/realtime/handlers/message.handler.cjs +103 -37
- package/dist/cjs/realtime/listener.cjs +39 -18
- package/dist/cjs/utils.cjs +21 -2
- package/dist/cjs/zalo.cjs +21 -2
- package/dist/constants/defaults.d.ts +2 -2
- package/dist/constants/defaults.js +2 -2
- package/dist/constants/protocol.d.ts +1 -1
- package/dist/constants/protocol.js +1 -1
- package/dist/crypto/aes.js +3 -1
- package/dist/crypto/zdb-cipher.js +10 -3
- package/dist/index.d.ts +4 -0
- package/dist/models/Message.d.ts +12 -1
- package/dist/models/Message.js +16 -1
- package/dist/models/OAMessage.d.ts +221 -0
- package/dist/models/OAMessage.js +363 -0
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +1 -0
- package/dist/realtime/handlers/control.handler.js +9 -1
- package/dist/realtime/handlers/message.handler.js +103 -37
- package/dist/realtime/listener.d.ts +9 -3
- package/dist/realtime/listener.js +39 -18
- package/dist/realtime/types.d.ts +15 -1
- package/dist/utils.js +1 -1
- package/dist/zalo.d.ts +1 -1
- package/dist/zalo.js +1 -1
- 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
|
-
|
|
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
|
|
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, () =>
|
|
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, () =>
|
|
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.
|
|
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: "
|
|
41
|
-
INITIAL_OLD_MESSAGES_CURSOR: "
|
|
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.
|
|
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) */
|
package/dist/cjs/crypto/aes.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -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
|
-
|
|
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 {
|