@softtynet/zca-js 2.2.7 → 2.2.9

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 (147) hide show
  1. package/README.md +160 -179
  2. package/dist/apis/clearUnreadFriendRequests.d.ts +2 -0
  3. package/dist/apis/clearUnreadFriendRequests.js +29 -0
  4. package/dist/apis/encryptGids.d.ts +4 -0
  5. package/dist/apis/encryptGids.js +24 -0
  6. package/dist/apis/getAliasListV2.d.ts +10 -0
  7. package/dist/apis/getAliasListV2.js +28 -0
  8. package/dist/apis/getBlockFriends.d.ts +6 -0
  9. package/dist/apis/getBlockFriends.js +28 -0
  10. package/dist/apis/getCloudInfo.d.ts +3 -0
  11. package/dist/apis/getCloudInfo.js +34 -0
  12. package/dist/apis/getCloudSettings.d.ts +10 -0
  13. package/dist/apis/getCloudSettings.js +34 -0
  14. package/dist/apis/getCloudViewerKey.d.ts +5 -0
  15. package/dist/apis/getCloudViewerKey.js +25 -0
  16. package/dist/apis/getConversationMedia.d.ts +26 -0
  17. package/dist/apis/getConversationMedia.js +39 -0
  18. package/dist/apis/getEmojiStatusConfig.d.ts +17 -0
  19. package/dist/apis/getEmojiStatusConfig.js +23 -0
  20. package/dist/apis/getExtraFriendInfo.d.ts +3 -0
  21. package/dist/apis/getExtraFriendInfo.js +31 -0
  22. package/dist/apis/getExtraGroupInfo.d.ts +7 -0
  23. package/dist/apis/getExtraGroupInfo.js +32 -0
  24. package/dist/apis/getFamilyProfile.d.ts +3 -0
  25. package/dist/apis/getFamilyProfile.js +34 -0
  26. package/dist/apis/getFamilyUsage.d.ts +7 -0
  27. package/dist/apis/getFamilyUsage.js +34 -0
  28. package/dist/apis/getFamilyViewerKey.d.ts +5 -0
  29. package/dist/apis/getFamilyViewerKey.js +25 -0
  30. package/dist/apis/getFriendsEmojiStatus.d.ts +17 -0
  31. package/dist/apis/getFriendsEmojiStatus.js +30 -0
  32. package/dist/apis/getGroupCloudMessageJump.d.ts +3 -0
  33. package/dist/apis/getGroupCloudMessageJump.js +46 -0
  34. package/dist/apis/getLastReadContactTab.d.ts +4 -0
  35. package/dist/apis/getLastReadContactTab.js +15 -0
  36. package/dist/apis/getLastSeen.d.ts +11 -0
  37. package/dist/apis/getLastSeen.js +40 -0
  38. package/dist/apis/getLastSeenReaction.d.ts +9 -0
  39. package/dist/apis/getLastSeenReaction.js +28 -0
  40. package/dist/apis/getListPinTopics.d.ts +5 -0
  41. package/dist/apis/getListPinTopics.js +28 -0
  42. package/dist/apis/getMediaStorePreview.d.ts +3 -0
  43. package/dist/apis/getMediaStorePreview.js +31 -0
  44. package/dist/apis/getMiniProfile.d.ts +13 -0
  45. package/dist/apis/getMiniProfile.js +34 -0
  46. package/dist/apis/getPersonalizedStickerList.d.ts +5 -0
  47. package/dist/apis/getPersonalizedStickerList.js +24 -0
  48. package/dist/apis/getPhonebookV2.d.ts +6 -0
  49. package/dist/apis/getPhonebookV2.js +21 -0
  50. package/dist/apis/getProfileRank.d.ts +5 -0
  51. package/dist/apis/getProfileRank.js +25 -0
  52. package/dist/apis/getProfileVerificationStatus.d.ts +5 -0
  53. package/dist/apis/getProfileVerificationStatus.js +20 -0
  54. package/dist/apis/getQuotaCommunity.d.ts +8 -0
  55. package/dist/apis/getQuotaCommunity.js +27 -0
  56. package/dist/apis/getRecentSearchList.d.ts +6 -0
  57. package/dist/apis/getRecentSearchList.js +25 -0
  58. package/dist/apis/getStickerCategoriesV2.d.ts +17 -0
  59. package/dist/apis/getStickerCategoriesV2.js +21 -0
  60. package/dist/apis/getStickerCategoryData.d.ts +3 -0
  61. package/dist/apis/getStickerCategoryData.js +25 -0
  62. package/dist/apis/getStickerHello.d.ts +16 -0
  63. package/dist/apis/getStickerHello.js +34 -0
  64. package/dist/apis/getStickerSuggestKeywords.d.ts +12 -0
  65. package/dist/apis/getStickerSuggestKeywords.js +24 -0
  66. package/dist/apis/getStickerSuggestion.d.ts +12 -0
  67. package/dist/apis/getStickerSuggestion.js +30 -0
  68. package/dist/apis/getUsageMyCloud.d.ts +7 -0
  69. package/dist/apis/getUsageMyCloud.js +34 -0
  70. package/dist/apis/getUsageZCloud.d.ts +11 -0
  71. package/dist/apis/getUsageZCloud.js +34 -0
  72. package/dist/apis/keepAlive.d.ts +2 -1
  73. package/dist/apis/keepAlive.js +5 -3
  74. package/dist/apis/listen.d.ts +13 -0
  75. package/dist/apis/listen.js +10 -0
  76. package/dist/apis/readContactTab.d.ts +2 -0
  77. package/dist/apis/readContactTab.js +26 -0
  78. package/dist/apis/removeConversationMedia.d.ts +8 -0
  79. package/dist/apis/removeConversationMedia.js +30 -0
  80. package/dist/apis/undoMultiMessage.d.ts +9 -0
  81. package/dist/apis/undoMultiMessage.js +46 -0
  82. package/dist/apis/updateRecentSearch.d.ts +11 -0
  83. package/dist/apis/updateRecentSearch.js +41 -0
  84. package/dist/apis/verifyCloudMedia.d.ts +14 -0
  85. package/dist/apis/verifyCloudMedia.js +43 -0
  86. package/dist/apis/voiceToText.d.ts +4 -0
  87. package/dist/apis/voiceToText.js +30 -0
  88. package/dist/apis.d.ts +82 -0
  89. package/dist/apis.js +82 -0
  90. package/dist/cjs/apis/clearUnreadFriendRequests.cjs +34 -0
  91. package/dist/cjs/apis/encryptGids.cjs +29 -0
  92. package/dist/cjs/apis/getAliasListV2.cjs +33 -0
  93. package/dist/cjs/apis/getBlockFriends.cjs +33 -0
  94. package/dist/cjs/apis/getCloudInfo.cjs +39 -0
  95. package/dist/cjs/apis/getCloudSettings.cjs +39 -0
  96. package/dist/cjs/apis/getCloudViewerKey.cjs +30 -0
  97. package/dist/cjs/apis/getConversationMedia.cjs +44 -0
  98. package/dist/cjs/apis/getEmojiStatusConfig.cjs +28 -0
  99. package/dist/cjs/apis/getExtraFriendInfo.cjs +36 -0
  100. package/dist/cjs/apis/getExtraGroupInfo.cjs +37 -0
  101. package/dist/cjs/apis/getFamilyProfile.cjs +39 -0
  102. package/dist/cjs/apis/getFamilyUsage.cjs +39 -0
  103. package/dist/cjs/apis/getFamilyViewerKey.cjs +30 -0
  104. package/dist/cjs/apis/getFriendsEmojiStatus.cjs +35 -0
  105. package/dist/cjs/apis/getGroupCloudMessageJump.cjs +63 -0
  106. package/dist/cjs/apis/getLastReadContactTab.cjs +20 -0
  107. package/dist/cjs/apis/getLastSeen.cjs +45 -0
  108. package/dist/cjs/apis/getLastSeenReaction.cjs +33 -0
  109. package/dist/cjs/apis/getListPinTopics.cjs +33 -0
  110. package/dist/cjs/apis/getMediaStorePreview.cjs +36 -0
  111. package/dist/cjs/apis/getMiniProfile.cjs +39 -0
  112. package/dist/cjs/apis/getPersonalizedStickerList.cjs +29 -0
  113. package/dist/cjs/apis/getPhonebookV2.cjs +26 -0
  114. package/dist/cjs/apis/getProfileRank.cjs +30 -0
  115. package/dist/cjs/apis/getProfileVerificationStatus.cjs +25 -0
  116. package/dist/cjs/apis/getQuotaCommunity.cjs +32 -0
  117. package/dist/cjs/apis/getRecentSearchList.cjs +30 -0
  118. package/dist/cjs/apis/getStickerCategoriesV2.cjs +26 -0
  119. package/dist/cjs/apis/getStickerCategoryData.cjs +30 -0
  120. package/dist/cjs/apis/getStickerHello.cjs +39 -0
  121. package/dist/cjs/apis/getStickerSuggestKeywords.cjs +29 -0
  122. package/dist/cjs/apis/getStickerSuggestion.cjs +35 -0
  123. package/dist/cjs/apis/getUsageMyCloud.cjs +39 -0
  124. package/dist/cjs/apis/getUsageZCloud.cjs +39 -0
  125. package/dist/cjs/apis/keepAlive.cjs +5 -3
  126. package/dist/cjs/apis/listen.cjs +10 -0
  127. package/dist/cjs/apis/readContactTab.cjs +31 -0
  128. package/dist/cjs/apis/removeConversationMedia.cjs +35 -0
  129. package/dist/cjs/apis/undoMultiMessage.cjs +62 -0
  130. package/dist/cjs/apis/updateRecentSearch.cjs +46 -0
  131. package/dist/cjs/apis/verifyCloudMedia.cjs +48 -0
  132. package/dist/cjs/apis/voiceToText.cjs +35 -0
  133. package/dist/cjs/apis.cjs +82 -0
  134. package/dist/cjs/index.cjs +2 -0
  135. package/dist/cjs/update.cjs +8 -6
  136. package/dist/cjs/utils.cjs +25 -0
  137. package/dist/context.d.ts +4 -0
  138. package/dist/index.d.ts +42 -1
  139. package/dist/index.js +1 -1
  140. package/dist/update.js +8 -6
  141. package/dist/utils.d.ts +10 -0
  142. package/dist/utils.js +23 -0
  143. package/docs/INTEGRATION_GUIDE_SYNC_AND_CALL.md +110 -0
  144. package/package.json +12 -9
  145. package/CODE_OF_CONDUCT.md +0 -163
  146. package/CONTRIBUTING.md +0 -470
  147. package/SECURITY.md +0 -83
package/README.md CHANGED
@@ -1,54 +1,73 @@
1
- # ZCA-JS (Forked Version)
1
+ # @softtynet/zca-js — Softtynet Zalo API SDK for Node.js & Bun
2
2
 
3
- > [!NOTE]
4
- > **This is a forked version of the original [zca-js](https://github.com/RFS-ADRENO/zca-js) project by RFS-ADRENO.** This package (`@softtynet/zca-js`) is maintained separately for quick hotfixes.
5
- >
6
- > This is an unofficial Zalo API for personal account. It work by simulating the browser to interact with Zalo Web.
3
+ <p align="left">
4
+ <a href="https://www.npmjs.com/package/@softtynet/zca-js"><img src="https://img.shields.io/npm/v/@softtynet/zca-js.svg?label=%40softtynet%2Fzca-js&color=0068ff" alt="npm version" /></a>
5
+ <a href="https://github.com/softtyos/zca-js"><img src="https://img.shields.io/badge/Maintained%20by-Softtynet%20Co.%2C%20Ltd.-0a7cff" alt="Maintained by Softtynet" /></a>
6
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green.svg" alt="License: MIT" /></a>
7
+ </p>
8
+
9
+ **`@softtynet/zca-js`** là bộ thư viện Zalo API nâng cao dành cho Node.js / TypeScript / Bun, được phát triển, tối ưu hoá và duy trì bởi **Softtynet Co., Ltd.** nhằm đáp ứng các bài toán tích hợp chuyên sâu: **Đồng bộ dữ liệu đa thiết bị (Offline Catch-up & Mobile Backup Sync `ZDB4.0`)**, **Tín hiệu Gọi Thoại & Gọi Video (Voice / Video Call Signaling)**, và **Xử lý WebSocket thời gian thực độ tin cậy cao**.
7
10
 
8
11
  > [!WARNING]
9
- > Using this API could get your account locked or banned. We are not responsible for any issues that may happen. Use it at your own risk.
12
+ > Đây là thư viện tương tác không chính thức thông qua giao thức Zalo Web / Zalo PC dành cho tài khoản cá nhân. Vui lòng sử dụng có trách nhiệm và tuân thủ quy định an toàn tài khoản.
13
+
14
+ ---
15
+
16
+ ## Mục lục
17
+
18
+ - [Điểm nổi bật trên phiên bản Softtynet (`@softtynet/zca-js`)](#-điểm-nổi-bật-trên-phiên-bản-softtynet-softtynetzca-js)
19
+ - [Cài đặt](#-cài-đặt)
20
+ - [Tài liệu chuyên sâu](#-tài-liệu-chuyên-sâu)
21
+ - [Hướng dẫn sử dụng nhanh](#-hướng-dẫn-sử-dụng-nhanh)
22
+ - [1. Đăng nhập & Lưu phiên](#1-đăng-nhập--lưu-phiên)
23
+ - [2. Lắng nghe tin nhắn thời gian thực](#2-lắng-nghe-tin-nhắn-thời-gian-thực)
24
+ - [3. Đồng bộ bù tin nhắn khi Offline (Catch-up Sync)](#3-đồng-bộ-bù-tin-nhắn-khi-offline-catch-up-sync)
25
+ - [4. Đồng bộ lịch sử sao lưu từ Điện thoại (`ZDB4.0`)](#4-đồng-bộ-lịch-sử-sao-lưu-từ-điện-thoại-zdb40)
26
+ - [5. Khởi tạo Cuộc gọi Thoại & Gọi Video](#5-khởi-tạo-cuộc-gọi-thoại--gọi-video)
27
+ - [6. Gửi tin nhắn & Sticker](#6-gửi-tin-nhắn--sticker)
28
+ - [Kiểm định & Chạy thử nghiệm](#-kiểm-định--chạy-thử-nghiệm)
29
+ - [Nguồn gốc & Ghi nhận (Credits & Attribution)](#-nguồn-gốc--ghi-nhận-credits--attribution)
30
+ - [Giấy phép (License)](#-giấy-phép-license)
10
31
 
11
32
  ---
12
33
 
13
- ## Table of Contents
14
-
15
- - [Installation](#installation)
16
- - [Migrate to V2](#migrate-to-v2)
17
- - [Documentation](#documentation)
18
- - [Basic Usages](#basic-usages)
19
- - [Login](#login)
20
- - [Listen for new messages](#listen-for-new-messages)
21
- - [Synchronize Messages from Mobile](#synchronize-messages-from-mobile)
22
- - [Send a message](#send-a-message)
23
- - [Get/Send a sticker](#getsend-a-sticker)
24
- - [Example](#example)
25
- - [Projects & Useful Resources](#projects--useful-resources)
26
- - [Contributing](#contributing)
27
- - [License](#license)
28
- - [Support Us](#support-us)
29
-
30
- ## Installation
34
+ ## 🚀 Điểm nổi bật trên phiên bản Softtynet (`@softtynet/zca-js`)
35
+
36
+ So với phiên bản cộng đồng tiêu chuẩn, **`@softtynet/zca-js`** bổ sung các phân hệ cốt lõi đã được kiểm định trực tiếp trên máy chủ Zalo thật:
37
+
38
+ | Phân hệ | Tính năng trên `@softtynet/zca-js` |
39
+ | :--- | :--- |
40
+ | **Đồng bộ bù khi Offline (Catch-up Sync)** | Hỗ trợ [`api.preloadConversations()`](src/apis/preloadConversations.ts) (`cmd 10053`), kéo hàng đợi tin nhắn cá nhân & nhóm qua [`api.listener.requestOldMessages()`](src/apis/listen.ts) (`cmd 510` / `511`, cả `subCmd 0` & `1`), kèm cờ phân trang `hasMore`, tách biệt sự kiện thu hồi `undo` và đồng bộ trạng thái đã đọc `unread_cleared` (`cmd 504` / `524`). |
41
+ | **Đồng bộ sao lưu từ Điện thoại (`ZDB4.0`)** | Hỗ trợ trọn vẹn chu trình [`api.requestSyncFromPhone()`](src/apis/requestSyncFromPhone.ts) (`cmd 12000`), [`api.cancelPullMobile()`](src/apis/cancelPullMobile.ts) (`cmd 12700`), sự kiện `sync_event` (`user_confirm`, `syncmsg_info`, `transfer_after_login`), tải gói `.db.crypt` qua [`api.getSyncData()`](src/apis/getSyncData.ts) và giải mã RSA-2048 PKCS#1 v1.5 + AES-256-CBC đa khối 64KB + đọc mục lục SQLite (`parseZdbHeader`). |
42
+ | **Ánh xạ ID ẩn danh (Noised / Global ID)** | Hỗ trợ [`api.getNoisedIds()`](src/apis/getNoisedIds.ts) (`cmd 12390` kèm cơ chế tự động fallback sang `zwid.api.zalo.me`) và [`api.decryptGids()`](src/apis/decryptGids.ts) (`cmd 12054`) để ánh xạ tên file SQLite `3_<me>_<peer>.db` về User ID / Group ID thật. |
43
+ | **Gọi Thoại & Gọi Video (Signaling 2 bước)** | Hỗ trợ [`api.sendVoiceCall()`](src/apis/sendVoiceCall.ts) (`cmd 11300` + `11301`), [`api.cancelVoiceCall()`](src/apis/cancelVoiceCall.ts) (`cmd 11305`), [`api.endVoiceCall()`](src/apis/endVoiceCall.ts) (`cmd 11306`) và sự kiện `call_event` chuẩn giao thức C++ `ZaloCall` cho cả **Gọi Thoại (`CallType.Audio = 1`)** lẫn **Gọi Video (`CallType.Video = 3`, H.264)**. |
44
+ | **Hệ sinh thái ZCloud, ZFamily & AI (`26.7.10`)** | Hỗ trợ chuẩn mã hóa `98yS` (AES-256-CBC với 16-byte Random IV) cho ZCloud & ZFamily ([`getCloudViewerKey`](src/apis/getCloudViewerKey.ts), [`getUsageZCloud`](src/apis/getUsageZCloud.ts), [`getUsageMyCloud`](src/apis/getUsageMyCloud.ts), [`getCloudSettings`](src/apis/getCloudSettings.ts), [`getCloudInfo`](src/apis/getCloudInfo.ts), [`getFamilyUsage`](src/apis/getFamilyUsage.ts), [`verifyCloudMedia`](src/apis/verifyCloudMedia.ts), [`getFamilyViewerKey`](src/apis/getFamilyViewerKey.ts), [`getFamilyProfile`](src/apis/getFamilyProfile.ts)), AI chuyển giọng nói thành văn bản [`voiceToText`](src/apis/voiceToText.ts) (`cmd 13394` + `voice2text_event`), Emoji Status ([`getEmojiStatusConfig`](src/apis/getEmojiStatusConfig.ts), [`getFriendsEmojiStatus`](src/apis/getFriendsEmojiStatus.ts)), Kho Lưu Trữ Hội Thoại ([`getConversationMedia`](src/apis/getConversationMedia.ts), [`getMediaStorePreview`](src/apis/getMediaStorePreview.ts), [`removeConversationMedia`](src/apis/removeConversationMedia.ts)), Lịch sử tìm kiếm ([`getRecentSearchList`](src/apis/getRecentSearchList.ts), [`updateRecentSearch`](src/apis/updateRecentSearch.ts)) cùng 25+ API Nhóm, Hồ sơ, Danh bạ & Nhãn dán V2 đã kiểm định Live 100%. |
45
+ | **Độ ổn định & Bảo vệ bộ nhớ** | Tự động giải phóng bộ nhớ `uploadCallbacks` sau 5 phút trong [`uploadAttachment`](src/apis/uploadAttachment.ts), xử lý phản hồi rỗng hợp lệ (`error_code === 0, data === ""`) và bảo toàn độ chính xác chuỗi UID 19 chữ số. |
46
+
47
+ ---
48
+
49
+ ## 📦 Cài đặt
31
50
 
32
51
  ```bash
33
- bun add zca-js # or npm install zca-js
52
+ npm install @softtynet/zca-js
53
+ # hoặc dùng Bun
54
+ bun add @softtynet/zca-js
34
55
  ```
35
56
 
36
- ### Migrate to V2
57
+ ### Cấu hình `imageMetadataGetter` (Khi gửi ảnh từ đường dẫn file)
37
58
 
38
- Since official version 2.0.0, `zca-js` has removed sharp dependency for image metadata extraction. It now requires users to provide their own `imageMetadataGetter` function when initializing the `Zalo` class if they want to send images/gifs by file path.
39
-
40
- Example of custom `imageMetadataGetter` using `sharp`:
59
+ Từ kiến trúc V2, thư viện tách rời phụ thuộc xử lý ảnh để giữ dung lượng gọn nhẹ. Nếu ứng dụng của bạn gửi ảnh/GIF từ file nội bộ, hãy truyền hàm `imageMetadataGetter` (ví dụ dùng `sharp`):
41
60
 
42
61
  ```bash
43
- bun add sharp # or npm install sharp
62
+ npm install sharp
44
63
  ```
45
64
 
46
- ```javascript
47
- import { Zalo } from "zca-js";
65
+ ```ts
66
+ import { Zalo } from "@softtynet/zca-js";
48
67
  import sharp from "sharp";
49
68
  import fs from "node:fs";
50
69
 
51
- async function imageMetadataGetter(filePath) {
70
+ async function imageMetadataGetter(filePath: string) {
52
71
  const data = await fs.promises.readFile(filePath);
53
72
  const metadata = await sharp(data).metadata();
54
73
  return {
@@ -58,80 +77,75 @@ async function imageMetadataGetter(filePath) {
58
77
  };
59
78
  }
60
79
 
61
- const zalo = new Zalo({
62
- imageMetadataGetter,
63
- });
80
+ const zalo = new Zalo({ imageMetadataGetter });
64
81
  ```
65
82
 
66
83
  ---
67
84
 
68
- ## Documentation
85
+ ## 📚 Tài liệu chuyên sâu
69
86
 
70
- - [API Documentation](https://zca-js.tdung.com)
71
- - [Hướng dẫn Tích hợp: Đồng bộ Tin nhắn Đa Thiết bị (`ZDB4.0`) & Gọi Thoại / Video](docs/INTEGRATION_GUIDE_SYNC_AND_CALL.md)
72
- - [Kế hoạch & Hướng dẫn Dịch ngược Zalo bằng REA (`morluto/rea`)](docs/REA_REVERSE_ENGINEERING_PLAN.md)
87
+ - **[Hướng dẫn Tích hợp: Đồng bộ Tin nhắn Đa Thiết bị (`ZDB4.0`) & Gọi Thoại / Video](docs/INTEGRATION_GUIDE_SYNC_AND_CALL.md)** — Tài liệu đầy đủ kèm sơ đồ kiến trúc và code mẫu tích hợp thực tế của Softtynet.
88
+ - **[Kế hoạch & Hướng dẫn Dịch ngược Zalo bằng REA (`morluto/rea`)](docs/REA_REVERSE_ENGINEERING_PLAN.md)** — Tài liệu quy trình phân tích tĩnh Binary C++ (`ZaloCall`, `libzdatabase.dylib`) và ASAR.
89
+ - **[API Reference Cơ bản](https://zca-js.tdung.com)** — Tra cứu danh mục các API cơ bản.
73
90
 
74
91
  ---
75
92
 
76
- ## Basic Usages
93
+ ## ⚡ Hướng dẫn sử dụng nhanh
77
94
 
78
- ### Login
95
+ ### 1. Đăng nhập & Lưu phiên
79
96
 
80
- ```javascript
81
- import { Zalo } from "zca-js";
97
+ ```ts
98
+ import { Zalo } from "@softtynet/zca-js";
82
99
 
83
- const zalo = new Zalo();
100
+ const zalo = new Zalo({ selfListen: true });
84
101
  const api = await zalo.loginQR();
102
+ console.log("Đã đăng nhập với UID:", api.getOwnId());
85
103
  ```
86
104
 
87
- ### Listen for new messages
105
+ ### 2. Lắng nghe tin nhắn thời gian thực
88
106
 
89
- ```javascript
90
- import { Zalo, ThreadType } from "zca-js";
107
+ ```ts
108
+ import { Zalo, ThreadType } from "@softtynet/zca-js";
91
109
 
92
- const zalo = new Zalo();
110
+ const zalo = new Zalo({ selfListen: true });
93
111
  const api = await zalo.loginQR();
94
112
 
95
113
  api.listener.on("message", (message) => {
96
114
  const isPlainText = typeof message.data.content === "string";
115
+ if (!isPlainText) return;
97
116
 
98
- switch (message.type) {
99
- case ThreadType.User: {
100
- if (isPlainText) {
101
- // received plain text direct message
102
- }
103
- break;
104
- }
105
- case ThreadType.Group: {
106
- if (isPlainText) {
107
- // received plain text group message
108
- }
109
- break;
110
- }
117
+ if (message.type === ThreadType.User) {
118
+ console.log(`[Cá nhân] ${message.data.dName}: ${message.data.content}`);
119
+ } else if (message.type === ThreadType.Group) {
120
+ console.log(`[Nhóm ${message.threadId}] ${message.data.dName}: ${message.data.content}`);
111
121
  }
112
122
  });
113
123
 
114
- api.listener.start();
124
+ api.listener.start({ retryOnClose: true });
115
125
  ```
116
126
 
117
127
  > [!IMPORTANT]
118
- > Only one web listener can run per account at a time. If you open Zalo in the browser while the listener is active, the listener will be automatically stopped.
128
+ > Mỗi tài khoản Zalo chỉ duy trì một kết nối WebSocket Web tại một thời điểm. Nếu bạn mở thêm tab Zalo Web trên trình duyệt, kết nối WebSocket cũ sẽ nhận sự kiện `duplicate_connection`.
119
129
 
120
- ### Synchronize Messages & Offline Catch-up
130
+ ### 3. Đồng bộ bù tin nhắn khi Offline (Catch-up Sync)
121
131
 
122
- `zca-js` supports both **offline message catch-up** (when reconnecting or restarting your app) and **full mobile backup synchronization** (`ZDB4.0`) from the Zalo mobile app:
123
-
124
- #### 1. Catch Up Missed Messages After Reconnect / Restart
125
- ```javascript
126
- import { Zalo, ThreadType } from "zca-js";
132
+ ```ts
133
+ import { ThreadType } from "@softtynet/zca-js";
127
134
 
135
+ // 1. Lấy nhanh các tin nhắn mới nhất & danh sách đã đọc qua HTTP (cmd 10053)
128
136
  const preload = await api.preloadConversations();
129
- console.log(`Preloaded ${preload.messages.length} latest messages`);
137
+ console.log(`Preloaded: ${preload.messages.length} tin nhắn, ${preload.unreadCleared.length} trạng thái đã đọc`);
130
138
 
139
+ // 2. Nhận tin nhắn từ hàng đợi WebSocket (cmd 510 & 511)
131
140
  api.listener.on("old_messages", (messages, type, hasMore) => {
132
- console.log(`Received ${messages.length} queued offline messages (type=${type}, hasMore=${hasMore})`);
141
+ console.log(`Nhận ${messages.length} tin nhắn cũ (type=${type}, hasMore=${hasMore})`);
142
+ if (hasMore && messages.length > 0) {
143
+ const lastMsgId = messages[messages.length - 1].data.msgId;
144
+ api.listener.requestOldMessages(type, lastMsgId);
145
+ }
133
146
  });
134
147
 
148
+ // 3. Kích hoạt kéo hàng đợi ngay khi nhận khoá giải mã WebSocket
135
149
  api.listener.on("cipher_key", () => {
136
150
  api.listener.requestOldMessages(ThreadType.User);
137
151
  api.listener.requestOldMessages(ThreadType.Group);
@@ -140,155 +154,122 @@ api.listener.on("cipher_key", () => {
140
154
  api.listener.start({ retryOnClose: true });
141
155
  ```
142
156
 
143
- #### 2. Trigger Mobile Backup Sync (`pull_mobile_msg`)
144
- ```javascript
145
- import { SyncEventType, decryptWithPrivateKey, deriveZdbAesKey, decryptZdbBuffer } from "zca-js";
157
+ ### 4. Đồng bộ lịch sử sao lưu từ Điện thoại (`ZDB4.0`)
158
+
159
+ ```ts
160
+ import fs from "node:fs";
161
+ import {
162
+ SyncEventType,
163
+ decryptWithPrivateKey,
164
+ deriveZdbAesKey,
165
+ decryptZdbBuffer,
166
+ parseZdbHeader,
167
+ } from "@softtynet/zca-js";
146
168
 
147
169
  let privateKey = "";
148
170
 
149
171
  api.listener.on("sync_event", async (event) => {
150
172
  if (event.type === SyncEventType.USER_CONFIRM) {
151
- console.log("User confirmed sync on phone, waiting for backup upload...");
173
+ console.log("Người dùng đã bấm xác nhận trên điện thoại, đang chờ tải gói sao lưu...");
152
174
  } else if (event.type === SyncEventType.SYNCMSG_INFO) {
153
175
  const res = await api.getSyncData({ ...event.data, savePath: "./sync_data" });
154
176
  if (res.success && res.data && privateKey) {
155
177
  const rawRsa = decryptWithPrivateKey(privateKey, res.data.encrypted_key);
156
178
  const aesKey = deriveZdbAesKey(rawRsa);
157
- console.log("Downloaded backup:", res.data.file_path, "AES key length:", aesKey.length);
179
+ const zdbPlain = decryptZdbBuffer(fs.readFileSync(res.data.file_path), aesKey);
180
+ const toc = parseZdbHeader(zdbPlain);
181
+ console.log(`Giải mã thành công ZDB4.0 (${toc.entries.length} hội thoại SQLite):`, toc.entries.slice(0, 5));
158
182
  }
159
183
  }
160
184
  });
161
185
 
162
- api.listener.on("connected", async () => {
163
- const session = await api.requestSyncFromPhone();
186
+ api.listener.on("cipher_key", async () => {
187
+ const session = await api.requestSyncFromPhone({ pcName: "Web" });
164
188
  privateKey = session.privateKey;
165
- console.log("Please tap 'Sync now' on your phone!");
189
+ console.log("Vui lòng mở điện thoại và bấm 'Đồng bộ ngay' trên thông báo Zalo!");
166
190
  });
167
-
168
- api.listener.start();
169
191
  ```
170
192
 
171
- ### Voice / Video Call Signaling
193
+ ### 5. Khởi tạo Cuộc gọi Thoại & Gọi Video
172
194
 
173
- ```javascript
174
- import { CallType } from "zca-js";
195
+ ```ts
196
+ import { CallType } from "@softtynet/zca-js";
175
197
 
176
- // Ring a user's phone (Audio or Video call)
177
- const call = await api.sendVoiceCall("target_user_id", { callType: CallType.Audio });
178
- console.log("Ringing callId:", call.callId, "session:", call.sessId);
179
-
180
- // Cancel ringing or end an active call
181
- await api.cancelVoiceCall("target_user_id", call.callId);
182
- ```
198
+ const targetUid = "5249661060655759359"; // Chuỗi UID 19 chữ số
183
199
 
184
- ### Send a message
185
-
186
- ```javascript
187
- import { Zalo, ThreadType } from "zca-js";
188
-
189
- const zalo = new Zalo();
190
- const api = await zalo.loginQR();
200
+ // Gọi Video (CallType.Video = 3, tự động đính kèm H.264 payload 97)
201
+ const videoCall = await api.sendVoiceCall(targetUid, {
202
+ callType: CallType.Video,
203
+ });
204
+ console.log("Đang đổ chuông Video:", videoCall.callId, videoCall.rtpServer);
191
205
 
192
- // Echo bot
193
- api.listener.on("message", (message) => {
194
- const isPlainText = typeof message.data.content === "string";
195
- if (message.isSelf || !isPlainText) return;
196
-
197
- switch (message.type) {
198
- case ThreadType.User: {
199
- api.sendMessage(
200
- {
201
- msg: "echo: " + message.data.content,
202
- quote: message.data, // the message to reply to (optional)
203
- },
204
- message.threadId,
205
- message.type, // ThreadType.User
206
- );
207
- break;
208
- }
209
- case ThreadType.Group: {
210
- api.sendMessage(
211
- {
212
- msg: "echo: " + message.data.content,
213
- quote: message.data, // the message to reply to (optional)
214
- },
215
- message.threadId,
216
- message.type, // ThreadType.Group
217
- );
218
- break;
219
- }
220
- }
206
+ // Huỷ cuộc gọi khi đang đổ chuông
207
+ await api.cancelVoiceCall(targetUid, videoCall.callId, {
208
+ callType: CallType.Video,
221
209
  });
222
210
 
223
- api.listener.start();
211
+ // Hoặc gọi Thoại (CallType.Audio = 1)
212
+ const audioCall = await api.sendVoiceCall(targetUid, {
213
+ callType: CallType.Audio,
214
+ });
215
+ await api.cancelVoiceCall(targetUid, audioCall.callId);
224
216
  ```
225
217
 
226
- ### Get/Send a sticker
227
-
228
- ```javascript
229
- api.getStickers("hello").then(async (stickerIds) => {
230
- // Get the first sticker
218
+ ### 6. Gửi tin nhắn & Sticker
219
+
220
+ ```ts
221
+ // Trả lời tin nhắn văn bản
222
+ await api.sendMessage(
223
+ {
224
+ msg: "Xin chào từ hệ thống Softtynet!",
225
+ quote: message.data,
226
+ },
227
+ message.threadId,
228
+ message.type,
229
+ );
230
+
231
+ // Tìm và gửi Sticker
232
+ const stickerIds = await api.getStickers("xin chào");
233
+ if (stickerIds.length > 0) {
231
234
  const stickerObject = await api.getStickersDetail(stickerIds[0]);
232
- api.sendMessageSticker(
233
- stickerObject,
234
- message.threadId,
235
- message.type, // ThreadType.User or ThreadType.Group
236
- );
237
- });
235
+ await api.sendMessageSticker(stickerObject, message.threadId, message.type);
236
+ }
238
237
  ```
239
238
 
240
239
  ---
241
240
 
242
- ## Example
243
-
244
- See [examples](examples) folder for more details.
245
-
246
- ---
241
+ ## 🧪 Kiểm định & Chạy thử nghiệm
247
242
 
248
- ## Projects & Useful Resources
243
+ Dự án đi kèm bộ kiểm định 2 lớp trong thư mục [`test/`](test):
249
244
 
250
- <div align="center">
245
+ 1. **Kiểm định tự động (Offline / Unit Verification Suite)**:
246
+ ```bash
247
+ npm run test:verify
248
+ ```
249
+ Kiểm tra tự động toàn bộ 6 hạng mục: `preloadConversations`, `requestSyncFromPhone` / `pullMobile` / `cancelPullMobile`, `getNoisedIds` / `decryptGids`, giải mã `RSA-2048` + `ZDB4.0` đa khối 64KB, `sendVoiceCall` (Audio & Video) / `cancelVoiceCall` / `endVoiceCall`, và các sự kiện WebSocket (`510`, `511`, `504`, `601`).
251
250
 
252
- | Repository | Description |
253
- | :--- | :--- |
254
- | [**ZaloDataExtractor**](https://github.com/JustKemForFun/ZaloDataExtractor) | A browser `Extension` to extract IMEI, cookies, and user agent from Zalo Web. |
255
- | [**MultiZlogin**](https://github.com/ChickenAI/multizlogin) | A multi-account Zalo management system that lets you log in to and manage multiple accounts simultaneously, with proxy and webhook integration. |
256
- | [**n8n-nodes-zalo-tools**](https://github.com/ChickenAI/zalo-node) | N8N node for personal Zalo account. |
257
- | [**Zalo-F12**](https://github.com/ElectroHeavenVN/Zalo-F12) | A collection of JavaScript code snippets to paste into DevTools to change how Zalo Web/PC works. |
258
- | [**Zalo-F12-Tools**](https://github.com/JustKemForFun/Zalo-F12-Tools) | Toggle hidden modes for Zalo Web. |
251
+ 2. **Kiểm định trực tiếp trên tài khoản Zalo thật (Live Verification Suite)**:
252
+ ```bash
253
+ # Chạy toàn bộ 6 bước (Quét QR -> Preload -> ZNoise -> OldMessages -> Đồng bộ ZDB4.0 từ ĐT -> Voice/Video Call)
254
+ npm run test:live
259
255
 
260
- </div>
256
+ # Chỉ test nhanh Gọi Thoại + Gọi Video tới 1 UID bạn bè
257
+ npm run test:live -- --only-call --call <UID_BAN_BE>
258
+ ```
261
259
 
262
260
  ---
263
261
 
264
- ## Contributing
265
-
266
- We welcome contributions from the community! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details on how to:
267
-
268
- - 🐛 Report bugs and issues
269
- - ✨ Suggest new features
270
- - 🔧 Submit code contributions
271
- - 📚 Improve documentation
272
- - 🧪 Add or improve tests
273
- - 🔒 Report security vulnerabilities
262
+ ## 🤝 Nguồn gốc & Ghi nhận (Credits & Attribution)
274
263
 
275
- For more information, please read our [Code of Conduct](CODE_OF_CONDUCT.md) and [Security Policy](SECURITY.md) before participating.
276
-
277
- ---
264
+ Dự án **`@softtynet/zca-js`** được phát triển, nâng cấp và duy trì bởi **Softtynet Co., Ltd.** ([`softtyos/zca-js`](https://github.com/softtyos/zca-js)), kế thừa nền tảng mã nguồn mở từ dự án gốc:
278
265
 
279
- ## License
266
+ - **Dự án gốc**: [RFS-ADRENO/zca-js](https://github.com/RFS-ADRENO/zca-js)
267
+ - **Tác giả nền tảng gốc**: RFS-ADRENO, truong9c2208, JustKemForFun cùng các thành viên đóng góp từ cộng đồng mã nguồn mở.
280
268
 
281
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
269
+ Chúng tôi trân trọng cảm ơn các tác giả của dự án gốc đã xây dựng nền tảng ban đầu cho cộng đồng JavaScript/TypeScript.
282
270
 
283
271
  ---
284
272
 
285
- ## **Support Us**
273
+ ## 📄 Giấy phép (License)
286
274
 
287
- - ⭐ **Star our repositories** if you find them useful!
288
- - 🔄 **Share** with your network to help us grow
289
- - 💡 **Contribute** your ideas and code
290
- - ☕ **A coffee**:
291
- - [Buy Me a Coffee](https://ko-fi.com/grosse)
292
- - [Paypal](https://www.paypal.com/paypalme/dungto213)
293
- - [VietQR](https://github.com/user-attachments/assets/e1f319d6-9d11-4082-8248-55b55e645caa)
294
- - [Momo](https://me.momo.vn/gMIMulsaUqsbf6iAiXt3)
275
+ Phát hành theo giấy phép [MIT License](LICENSE).
@@ -0,0 +1,2 @@
1
+ export type ClearUnreadFriendRequestsResponse = Record<string, unknown> | string;
2
+ export declare const clearUnreadFriendRequestsFactory: (ctx: import("../context.js").ContextBase, api: import("../apis.js").API) => (uidsSeen?: string[]) => Promise<ClearUnreadFriendRequestsResponse>;
@@ -0,0 +1,29 @@
1
+ import { ZaloApiError } from "../Errors/ZaloApiError.js";
2
+ import { apiFactory } from "../utils.js";
3
+ export const clearUnreadFriendRequestsFactory = apiFactory()((api, _ctx, utils) => {
4
+ const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/recommendsv2/read-requests`);
5
+ /**
6
+ * Đánh dấu đã xem danh sách lời mời kết bạn chưa đọc (command 12232)
7
+ *
8
+ * @param uidsSeen Danh sách UID đã xem lời mời kết bạn (mặc định `[]`)
9
+ * @throws {ZaloApiError}
10
+ */
11
+ return async function clearUnreadFriendRequests(uidsSeen = []) {
12
+ if (!uidsSeen || uidsSeen.length === 0) {
13
+ return {};
14
+ }
15
+ const params = {
16
+ uids_seen: uidsSeen,
17
+ };
18
+ const encryptedParams = utils.encodeAES(JSON.stringify(params));
19
+ if (!encryptedParams)
20
+ throw new ZaloApiError("Failed to encrypt params");
21
+ const response = await utils.request(serviceURL, {
22
+ method: "POST",
23
+ body: new URLSearchParams({
24
+ params: encryptedParams,
25
+ }),
26
+ });
27
+ return utils.resolve(response);
28
+ };
29
+ });
@@ -0,0 +1,4 @@
1
+ export type EncryptGidsResponse = {
2
+ data: Record<string, string>;
3
+ };
4
+ export declare const encryptGidsFactory: (ctx: import("../context.js").ContextBase, api: import("../apis.js").API) => (uids: string[]) => Promise<EncryptGidsResponse>;
@@ -0,0 +1,24 @@
1
+ import { ZaloApiError } from "../Errors/ZaloApiError.js";
2
+ import { apiFactory } from "../utils.js";
3
+ export const encryptGidsFactory = apiFactory()((api, _ctx, utils) => {
4
+ const serviceURL = utils.makeURL(`${api.zpwServiceMap.profile[0]}/api/gid/encrypt`);
5
+ /**
6
+ * Mã hóa danh sách UID thật sang định danh ẩn danh Global Noise ID (`OTDAKO...`) (command 12053)
7
+ *
8
+ * @param uids Mảng chuỗi các UID cần mã hóa
9
+ * @throws {ZaloApiError}
10
+ */
11
+ return async function encryptGids(uids) {
12
+ if (!Array.isArray(uids) || uids.length === 0) {
13
+ return { data: {} };
14
+ }
15
+ const encryptedParams = utils.encodeAES(JSON.stringify({ noiseUids: uids }));
16
+ if (!encryptedParams) {
17
+ throw new ZaloApiError("Failed to encrypt params");
18
+ }
19
+ const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), {
20
+ method: "GET",
21
+ });
22
+ return utils.resolve(response);
23
+ };
24
+ });
@@ -0,0 +1,10 @@
1
+ export type AliasItemV2 = {
2
+ userId: string;
3
+ alias: string;
4
+ };
5
+ export type GetAliasListV2Response = {
6
+ items: AliasItemV2[];
7
+ updateTime?: string;
8
+ [key: string]: unknown;
9
+ };
10
+ export declare const getAliasListV2Factory: (ctx: import("../context.js").ContextBase, api: import("../apis.js").API) => (page?: number, count?: number) => Promise<GetAliasListV2Response>;
@@ -0,0 +1,28 @@
1
+ import { ZaloApiError } from "../Errors/ZaloApiError.js";
2
+ import { apiFactory } from "../utils.js";
3
+ export const getAliasListV2Factory = apiFactory()((api, ctx, utils) => {
4
+ const serviceURL = utils.makeURL(`${api.zpwServiceMap.alias[0]}/api/alias/list/v2`);
5
+ /**
6
+ * Lấy danh sách tên gợi nhớ (Alias) của bạn bè chuẩn V2 có hỗ trợ phân trang (command 12205)
7
+ *
8
+ * @param page Trang cần lấy (mặc định: 1)
9
+ * @param count Số lượng mục mỗi trang (mặc định: 100)
10
+ * @throws {ZaloApiError}
11
+ */
12
+ return async function getAliasListV2(page = 1, count = 100) {
13
+ const params = {
14
+ page,
15
+ count,
16
+ src: -1,
17
+ imei: ctx.imei,
18
+ };
19
+ const encryptedParams = utils.encodeAES(JSON.stringify(params));
20
+ if (!encryptedParams) {
21
+ throw new ZaloApiError("Failed to encrypt params");
22
+ }
23
+ const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), {
24
+ method: "GET",
25
+ });
26
+ return utils.resolve(response);
27
+ };
28
+ });
@@ -0,0 +1,6 @@
1
+ export type GetBlockFriendsResponse = {
2
+ profiles: Record<string, unknown>[];
3
+ version: number;
4
+ hasMore: number;
5
+ };
6
+ export declare const getBlockFriendsFactory: (ctx: import("../context.js").ContextBase, api: import("../apis.js").API) => (page?: number, count?: number, avatarSize?: number) => Promise<GetBlockFriendsResponse>;
@@ -0,0 +1,28 @@
1
+ import { ZaloApiError } from "../Errors/ZaloApiError.js";
2
+ import { apiFactory } from "../utils.js";
3
+ export const getBlockFriendsFactory = apiFactory()((api, _ctx, utils) => {
4
+ const serviceURL = utils.makeURL(`${api.zpwServiceMap.friend[0]}/api/friend/blocklistv2`);
5
+ /**
6
+ * Lấy danh sách người dùng đang bị chặn chuẩn V2 (command 11429)
7
+ *
8
+ * @param page Trang cần lấy (mặc định: 1)
9
+ * @param count Số lượng mỗi trang (mặc định: 50)
10
+ * @param avatarSize Kích thước ảnh đại diện (mặc định: 120)
11
+ * @throws {ZaloApiError}
12
+ */
13
+ return async function getBlockFriends(page = 1, count = 50, avatarSize = 120) {
14
+ const params = {
15
+ page,
16
+ count,
17
+ avatar: avatarSize,
18
+ };
19
+ const encryptedParams = utils.encodeAES(JSON.stringify(params));
20
+ if (!encryptedParams) {
21
+ throw new ZaloApiError("Failed to encrypt params");
22
+ }
23
+ const response = await utils.request(utils.makeURL(serviceURL, { params: encryptedParams }), {
24
+ method: "GET",
25
+ });
26
+ return utils.resolve(response);
27
+ };
28
+ });
@@ -0,0 +1,3 @@
1
+ import type { GetCloudViewerKeyResponse } from "./getCloudViewerKey.js";
2
+ export type GetCloudInfoResponse = Record<string, unknown>;
3
+ export declare const getCloudInfoFactory: (ctx: import("../context.js").ContextBase, api: import("../apis.js").API) => (viewerKeyInfo?: GetCloudViewerKeyResponse) => Promise<GetCloudInfoResponse>;
@@ -0,0 +1,34 @@
1
+ import { ZaloApiError } from "../Errors/ZaloApiError.js";
2
+ import { apiFactory, decryptZCloudAES } from "../utils.js";
3
+ export const getCloudInfoFactory = apiFactory()((api, _ctx, utils) => {
4
+ var _a;
5
+ const baseDomain = ((_a = api.zpwServiceMap.zcloud) === null || _a === void 0 ? void 0 : _a[0]) || "https://zcld.chat.zalo.me";
6
+ const serviceURL = utils.makeURL(`${baseDomain}/zalocloud/cloudinfopcweb/v1/info`);
7
+ /**
8
+ * Lấy thông tin gói thuê bao Zalo Cloud (command 12754)
9
+ *
10
+ * @param viewerKeyInfo (Tùy chọn) Cặp `{ viewer_key, enk }` từ `api.getCloudViewerKey()`.
11
+ * @throws {ZaloApiError}
12
+ */
13
+ return async function getCloudInfo(viewerKeyInfo) {
14
+ const keyInfo = viewerKeyInfo !== null && viewerKeyInfo !== void 0 ? viewerKeyInfo : (await api.getCloudViewerKey());
15
+ if (!(keyInfo === null || keyInfo === void 0 ? void 0 : keyInfo.viewer_key) || !(keyInfo === null || keyInfo === void 0 ? void 0 : keyInfo.enk)) {
16
+ throw new ZaloApiError("Invalid CloudViewerKey");
17
+ }
18
+ const response = await utils.request(serviceURL, {
19
+ method: "GET",
20
+ headers: {
21
+ "cloud-viewer-key": keyInfo.viewer_key,
22
+ },
23
+ });
24
+ const json = (await response.json());
25
+ if (json.error_code !== 0 && json.error_code !== -904) {
26
+ throw new ZaloApiError(json.error_message || "getCloudInfo failed", json.error_code);
27
+ }
28
+ if (!json.data) {
29
+ return {};
30
+ }
31
+ const decrypted = decryptZCloudAES(json.data, keyInfo.enk);
32
+ return JSON.parse(decrypted);
33
+ };
34
+ });