@softtynet/zca-js 2.3.4 → 2.3.5
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 +47 -11
- package/dist/apis/call/addUserGroupCall.d.ts +11 -0
- package/dist/apis/call/addUserGroupCall.js +48 -0
- package/dist/apis/call/answerAckVoiceCall.d.ts +5 -0
- package/dist/apis/call/answerAckVoiceCall.js +38 -0
- package/dist/apis/call/answerGroupCall.d.ts +10 -0
- package/dist/apis/call/answerGroupCall.js +45 -0
- package/dist/apis/call/answerVoiceCall.d.ts +11 -0
- package/dist/apis/call/answerVoiceCall.js +50 -0
- package/dist/apis/call/broadcastGroupCallState.d.ts +10 -0
- package/dist/apis/call/broadcastGroupCallState.js +45 -0
- package/dist/apis/call/cancelGroupCall.d.ts +10 -0
- package/dist/apis/call/cancelGroupCall.js +45 -0
- package/dist/apis/call/cancelVoiceCall.d.ts +1 -1
- package/dist/apis/call/cancelVoiceCall.js +5 -1
- package/dist/apis/call/createController.d.ts +7 -0
- package/dist/apis/call/createController.js +9 -0
- package/dist/apis/call/createRtcSession.d.ts +8 -0
- package/dist/apis/call/createRtcSession.js +10 -0
- package/dist/apis/call/endGroupCall.d.ts +10 -0
- package/dist/apis/call/endGroupCall.js +45 -0
- package/dist/apis/call/finishGroupCall.d.ts +12 -0
- package/dist/apis/call/finishGroupCall.js +47 -0
- package/dist/apis/call/getGroupCallState.d.ts +3 -0
- package/dist/apis/call/getGroupCallState.js +38 -0
- package/dist/apis/call/holdVoiceCall.d.ts +5 -0
- package/dist/apis/call/holdVoiceCall.js +34 -0
- package/dist/apis/call/joinGroupCall.d.ts +11 -0
- package/dist/apis/call/joinGroupCall.js +46 -0
- package/dist/apis/call/logEndVoiceCall.d.ts +8 -0
- package/dist/apis/call/logEndVoiceCall.js +46 -0
- package/dist/apis/call/notifyGroupCall.d.ts +9 -0
- package/dist/apis/call/notifyGroupCall.js +44 -0
- package/dist/apis/call/pingGroupCall.d.ts +10 -0
- package/dist/apis/call/pingGroupCall.js +45 -0
- package/dist/apis/call/requestAudioToVideoCall.d.ts +5 -0
- package/dist/apis/call/requestAudioToVideoCall.js +37 -0
- package/dist/apis/call/requestGroupCall.d.ts +9 -0
- package/dist/apis/call/requestGroupCall.js +46 -0
- package/dist/apis/call/requestShareScreenCall.d.ts +5 -0
- package/dist/apis/call/requestShareScreenCall.js +37 -0
- package/dist/apis/call/requestVoiceCall.d.ts +10 -0
- package/dist/apis/call/requestVoiceCall.js +47 -0
- package/dist/apis/call/ringRingGroupCall.d.ts +10 -0
- package/dist/apis/call/ringRingGroupCall.js +45 -0
- package/dist/apis/call/ringRingVoiceCall.d.ts +5 -0
- package/dist/apis/call/ringRingVoiceCall.js +38 -0
- package/dist/apis/call/sendRequestGroupCall.d.ts +11 -0
- package/dist/apis/call/sendRequestGroupCall.js +48 -0
- package/dist/apis/call/sendRequestVoiceCall.d.ts +11 -0
- package/dist/apis/call/sendRequestVoiceCall.js +51 -0
- package/dist/apis/call/sendVoiceCall.d.ts +8 -1
- package/dist/apis/call/sendVoiceCall.js +10 -8
- package/dist/apis/call/shareScreenGroupCall.d.ts +10 -0
- package/dist/apis/call/shareScreenGroupCall.js +45 -0
- package/dist/apis/call/unholdVoiceCall.d.ts +5 -0
- package/dist/apis/call/unholdVoiceCall.js +34 -0
- package/dist/apis/call/updateCallMediaState.d.ts +7 -0
- package/dist/apis/call/updateCallMediaState.js +61 -0
- package/dist/apis.d.ts +81 -0
- package/dist/apis.js +135 -0
- package/dist/call-engine/ZRTPPacketCodec.d.ts +262 -0
- package/dist/call-engine/ZRTPPacketCodec.js +506 -0
- package/dist/call-engine/ZaloCallController.d.ts +134 -0
- package/dist/call-engine/ZaloCallController.js +638 -0
- package/dist/call-engine/ZaloCallTransport.d.ts +83 -0
- package/dist/call-engine/ZaloCallTransport.js +233 -0
- package/dist/call-engine/ZaloRtcSession.d.ts +609 -0
- package/dist/call-engine/ZaloRtcSession.js +2931 -0
- package/dist/call-engine/ZaloSrtpSession.d.ts +80 -0
- package/dist/call-engine/ZaloSrtpSession.js +278 -0
- package/dist/call-engine/index.d.ts +18 -0
- package/dist/call-engine/index.js +24 -0
- package/dist/cjs/apis/call/addUserGroupCall.cjs +53 -0
- package/dist/cjs/apis/call/answerAckVoiceCall.cjs +43 -0
- package/dist/cjs/apis/call/answerGroupCall.cjs +50 -0
- package/dist/cjs/apis/call/answerVoiceCall.cjs +55 -0
- package/dist/cjs/apis/call/broadcastGroupCallState.cjs +50 -0
- package/dist/cjs/apis/call/cancelGroupCall.cjs +50 -0
- package/dist/cjs/apis/call/cancelVoiceCall.cjs +5 -1
- package/dist/cjs/apis/call/createController.cjs +14 -0
- package/dist/cjs/apis/call/createRtcSession.cjs +15 -0
- package/dist/cjs/apis/call/endGroupCall.cjs +50 -0
- package/dist/cjs/apis/call/finishGroupCall.cjs +52 -0
- package/dist/cjs/apis/call/getGroupCallState.cjs +43 -0
- package/dist/cjs/apis/call/holdVoiceCall.cjs +39 -0
- package/dist/cjs/apis/call/joinGroupCall.cjs +51 -0
- package/dist/cjs/apis/call/logEndVoiceCall.cjs +51 -0
- package/dist/cjs/apis/call/notifyGroupCall.cjs +49 -0
- package/dist/cjs/apis/call/pingGroupCall.cjs +50 -0
- package/dist/cjs/apis/call/requestAudioToVideoCall.cjs +42 -0
- package/dist/cjs/apis/call/requestGroupCall.cjs +51 -0
- package/dist/cjs/apis/call/requestShareScreenCall.cjs +42 -0
- package/dist/cjs/apis/call/requestVoiceCall.cjs +52 -0
- package/dist/cjs/apis/call/ringRingGroupCall.cjs +50 -0
- package/dist/cjs/apis/call/ringRingVoiceCall.cjs +43 -0
- package/dist/cjs/apis/call/sendRequestGroupCall.cjs +53 -0
- package/dist/cjs/apis/call/sendRequestVoiceCall.cjs +56 -0
- package/dist/cjs/apis/call/sendVoiceCall.cjs +10 -8
- package/dist/cjs/apis/call/shareScreenGroupCall.cjs +50 -0
- package/dist/cjs/apis/call/unholdVoiceCall.cjs +39 -0
- package/dist/cjs/apis/call/updateCallMediaState.cjs +66 -0
- package/dist/cjs/apis.cjs +135 -0
- package/dist/cjs/call-engine/ZRTPPacketCodec.cjs +525 -0
- package/dist/cjs/call-engine/ZaloCallController.cjs +643 -0
- package/dist/cjs/call-engine/ZaloCallTransport.cjs +239 -0
- package/dist/cjs/call-engine/ZaloRtcSession.cjs +2937 -0
- package/dist/cjs/call-engine/ZaloSrtpSession.cjs +287 -0
- package/dist/cjs/constants/protocol.cjs +1 -1
- package/dist/cjs/index.cjs +61 -11
- package/dist/cjs/models/CallEvent.cjs +95 -1
- package/dist/cjs/realtime/handlers/control.handler.cjs +17 -3
- package/dist/cjs/realtime/listener.cjs +19 -0
- package/dist/constants/protocol.d.ts +1 -1
- package/dist/constants/protocol.js +1 -1
- package/dist/index.d.ts +32 -4
- package/dist/index.js +2 -0
- package/dist/models/CallEvent.d.ts +15 -0
- package/dist/models/CallEvent.js +95 -1
- package/dist/realtime/handlers/control.handler.js +17 -3
- package/dist/realtime/listener.d.ts +5 -0
- package/dist/realtime/listener.js +19 -0
- package/package.json +2 -1
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var crypto = require('node:crypto');
|
|
4
|
+
var EventEmitter = require('node:events');
|
|
5
|
+
var fs = require('node:fs');
|
|
6
|
+
var net = require('node:net');
|
|
7
|
+
var node_buffer = require('node:buffer');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Khoá AES-128-CBC tĩnh chuẩn trong module `4KIH` của Zalo PC và binary `ZaloCall` (`yjAF9oqMWl6XfXYJn9mA7w==`).
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_ZALOCALL_IPC_KEY_BASE64 = "yjAF9oqMWl6XfXYJn9mA7w==";
|
|
13
|
+
const MAX_IPC_CHUNK_LENGTH = 4000;
|
|
14
|
+
/**
|
|
15
|
+
* Tầng giao thức IPC双kênh (`receiveSocketPath` & `sendSocketPath`) mã hoá AES-128-CBC (Hex + `$` framing + 4000-char chunking)
|
|
16
|
+
* khớp 100% với module `3Zc2` / `4KIH` của Zalo PC và `zrtc_call::ZIpcClientMac` (`0x10012eb50`).
|
|
17
|
+
*/
|
|
18
|
+
class ZaloCallTransport extends EventEmitter {
|
|
19
|
+
socketId;
|
|
20
|
+
sendSocketPath;
|
|
21
|
+
receiveSocketPath;
|
|
22
|
+
keyBase64;
|
|
23
|
+
keyBuffer;
|
|
24
|
+
ivBuffer;
|
|
25
|
+
ackTimeoutMs;
|
|
26
|
+
sendServer = null;
|
|
27
|
+
receiveServer = null;
|
|
28
|
+
sendClient = null;
|
|
29
|
+
receiveClient = null;
|
|
30
|
+
receiveBuffer = "";
|
|
31
|
+
outgoingQueue = [];
|
|
32
|
+
waitingForAck = false;
|
|
33
|
+
ackTimer = null;
|
|
34
|
+
emittedConnected = false;
|
|
35
|
+
constructor(options = {}) {
|
|
36
|
+
super();
|
|
37
|
+
this.socketId = options.socketId ?? `${process.pid}_${Math.floor(Math.random() * 10000)}`;
|
|
38
|
+
this.keyBase64 = options.keyBase64 ?? DEFAULT_ZALOCALL_IPC_KEY_BASE64;
|
|
39
|
+
this.ackTimeoutMs = options.ackTimeoutMs ?? 250;
|
|
40
|
+
const decodedKey = node_buffer.Buffer.from(this.keyBase64, "base64");
|
|
41
|
+
this.keyBuffer = node_buffer.Buffer.alloc(16, 0);
|
|
42
|
+
decodedKey.copy(this.keyBuffer, 0, 0, Math.min(16, decodedKey.length));
|
|
43
|
+
this.ivBuffer = node_buffer.Buffer.alloc(16, 0);
|
|
44
|
+
if (process.platform === "win32") {
|
|
45
|
+
this.receiveSocketPath = `\\\\.\\pipe\\receive_socket_${this.socketId}`;
|
|
46
|
+
this.sendSocketPath = `\\\\.\\pipe\\send_socket_${this.socketId}`;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.receiveSocketPath = `/tmp/zcall_r_${this.socketId}`;
|
|
50
|
+
this.sendSocketPath = `/tmp/zcall_s_${this.socketId}`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Mã hoá đối tượng JSON thành mảng các mảnh Hex (tối đa 4000 ký tự/mảnh, mảnh cuối kết thúc bằng `$`).
|
|
55
|
+
*/
|
|
56
|
+
encryptFrames(payload) {
|
|
57
|
+
const json = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
58
|
+
const cipher = crypto.createCipheriv("aes-128-cbc", this.keyBuffer, this.ivBuffer);
|
|
59
|
+
const encryptedHex = cipher.update(json, "utf-8", "hex") + cipher.final("hex");
|
|
60
|
+
const chunks = [];
|
|
61
|
+
let offset = 0;
|
|
62
|
+
while (offset < encryptedHex.length) {
|
|
63
|
+
chunks.push(encryptedHex.slice(offset, offset + MAX_IPC_CHUNK_LENGTH));
|
|
64
|
+
offset += MAX_IPC_CHUNK_LENGTH;
|
|
65
|
+
}
|
|
66
|
+
if (chunks.length === 0) {
|
|
67
|
+
chunks.push("$");
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
chunks[chunks.length - 1] += "$";
|
|
71
|
+
}
|
|
72
|
+
return chunks;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Giải mã một khung chuỗi Hex (không bao gồm ký tự `$`) thành đối tượng JSON.
|
|
76
|
+
*/
|
|
77
|
+
decryptFrame(encryptedHex) {
|
|
78
|
+
const clean = encryptedHex.endsWith("$") ? encryptedHex.slice(0, -1) : encryptedHex;
|
|
79
|
+
const decipher = crypto.createDecipheriv("aes-128-cbc", this.keyBuffer, this.ivBuffer);
|
|
80
|
+
const plain = decipher.update(clean, "hex", "utf-8") + decipher.final("utf-8");
|
|
81
|
+
return JSON.parse(plain);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Khởi tạo 2 máy chủ IPC (`receiveSocketPath` và `sendSocketPath`) sẵn sàng đón tiến trình `ZaloCall`.
|
|
85
|
+
*/
|
|
86
|
+
async start() {
|
|
87
|
+
this.unlinkUnixSockets();
|
|
88
|
+
this.emittedConnected = false;
|
|
89
|
+
this.sendServer = net.createServer((socket) => {
|
|
90
|
+
this.sendClient = socket;
|
|
91
|
+
socket.on("data", (chunk) => {
|
|
92
|
+
const text = chunk.toString("utf-8");
|
|
93
|
+
if (text.includes("ack")) {
|
|
94
|
+
this.emit("raw_ack");
|
|
95
|
+
this.onAckReceived();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
socket.on("error", (err) => this.emit("error", err));
|
|
99
|
+
this.checkBothConnected();
|
|
100
|
+
this.flushOutgoingQueue();
|
|
101
|
+
});
|
|
102
|
+
this.receiveServer = net.createServer((socket) => {
|
|
103
|
+
this.receiveClient = socket;
|
|
104
|
+
socket.on("data", (chunk) => {
|
|
105
|
+
this.handleReceiveChunk(chunk.toString("utf-8"));
|
|
106
|
+
});
|
|
107
|
+
socket.on("error", (err) => this.emit("error", err));
|
|
108
|
+
this.checkBothConnected();
|
|
109
|
+
});
|
|
110
|
+
await Promise.all([
|
|
111
|
+
new Promise((resolve, reject) => {
|
|
112
|
+
this.receiveServer.once("error", reject);
|
|
113
|
+
this.receiveServer.listen(this.receiveSocketPath, () => resolve());
|
|
114
|
+
}),
|
|
115
|
+
new Promise((resolve, reject) => {
|
|
116
|
+
this.sendServer.once("error", reject);
|
|
117
|
+
this.sendServer.listen(this.sendSocketPath, () => resolve());
|
|
118
|
+
}),
|
|
119
|
+
]);
|
|
120
|
+
this.emit("ready");
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Gửi một thông điệp IPC đã mã hoá AES-128-CBC xuống tiến trình `ZaloCall` (qua hàng đợi `"ack"`).
|
|
124
|
+
*/
|
|
125
|
+
send(message) {
|
|
126
|
+
const frames = this.encryptFrames(message);
|
|
127
|
+
this.outgoingQueue.push(...frames);
|
|
128
|
+
this.flushOutgoingQueue();
|
|
129
|
+
}
|
|
130
|
+
isConnected() {
|
|
131
|
+
return Boolean(this.sendClient &&
|
|
132
|
+
!this.sendClient.destroyed &&
|
|
133
|
+
this.receiveClient &&
|
|
134
|
+
!this.receiveClient.destroyed);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Đóng toàn bộ kết nối và dọn dẹp file socket trên đĩa.
|
|
138
|
+
*/
|
|
139
|
+
async close() {
|
|
140
|
+
if (this.ackTimer) {
|
|
141
|
+
clearTimeout(this.ackTimer);
|
|
142
|
+
this.ackTimer = null;
|
|
143
|
+
}
|
|
144
|
+
this.outgoingQueue = [];
|
|
145
|
+
this.waitingForAck = false;
|
|
146
|
+
this.emittedConnected = false;
|
|
147
|
+
if (this.sendClient) {
|
|
148
|
+
this.sendClient.destroy();
|
|
149
|
+
this.sendClient = null;
|
|
150
|
+
}
|
|
151
|
+
if (this.receiveClient) {
|
|
152
|
+
this.receiveClient.destroy();
|
|
153
|
+
this.receiveClient = null;
|
|
154
|
+
}
|
|
155
|
+
await Promise.all([
|
|
156
|
+
new Promise((resolve) => {
|
|
157
|
+
if (this.sendServer)
|
|
158
|
+
this.sendServer.close(() => resolve());
|
|
159
|
+
else
|
|
160
|
+
resolve();
|
|
161
|
+
}),
|
|
162
|
+
new Promise((resolve) => {
|
|
163
|
+
if (this.receiveServer)
|
|
164
|
+
this.receiveServer.close(() => resolve());
|
|
165
|
+
else
|
|
166
|
+
resolve();
|
|
167
|
+
}),
|
|
168
|
+
]);
|
|
169
|
+
this.sendServer = null;
|
|
170
|
+
this.receiveServer = null;
|
|
171
|
+
this.unlinkUnixSockets();
|
|
172
|
+
this.emit("close");
|
|
173
|
+
}
|
|
174
|
+
checkBothConnected() {
|
|
175
|
+
if (this.isConnected() && !this.emittedConnected) {
|
|
176
|
+
this.emittedConnected = true;
|
|
177
|
+
this.emit("client_connected");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
onAckReceived() {
|
|
181
|
+
if (this.ackTimer) {
|
|
182
|
+
clearTimeout(this.ackTimer);
|
|
183
|
+
this.ackTimer = null;
|
|
184
|
+
}
|
|
185
|
+
this.waitingForAck = false;
|
|
186
|
+
this.flushOutgoingQueue();
|
|
187
|
+
}
|
|
188
|
+
flushOutgoingQueue() {
|
|
189
|
+
if (this.waitingForAck || !this.sendClient || this.sendClient.destroyed) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const nextFrame = this.outgoingQueue.shift();
|
|
193
|
+
if (!nextFrame)
|
|
194
|
+
return;
|
|
195
|
+
this.waitingForAck = true;
|
|
196
|
+
this.sendClient.write(nextFrame, "utf-8");
|
|
197
|
+
this.ackTimer = setTimeout(() => {
|
|
198
|
+
this.waitingForAck = false;
|
|
199
|
+
this.flushOutgoingQueue();
|
|
200
|
+
}, this.ackTimeoutMs);
|
|
201
|
+
if (typeof this.ackTimer.unref === "function") {
|
|
202
|
+
this.ackTimer.unref();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
handleReceiveChunk(chunk) {
|
|
206
|
+
this.receiveBuffer += chunk;
|
|
207
|
+
let dollarIdx = this.receiveBuffer.indexOf("$");
|
|
208
|
+
while (dollarIdx !== -1) {
|
|
209
|
+
const frame = this.receiveBuffer.slice(0, dollarIdx);
|
|
210
|
+
this.receiveBuffer = this.receiveBuffer.slice(dollarIdx + 1);
|
|
211
|
+
if (frame.length > 0) {
|
|
212
|
+
try {
|
|
213
|
+
const parsed = this.decryptFrame(frame);
|
|
214
|
+
this.emit("message", parsed);
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
this.emit("error", err);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
dollarIdx = this.receiveBuffer.indexOf("$");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
unlinkUnixSockets() {
|
|
224
|
+
if (process.platform === "win32")
|
|
225
|
+
return;
|
|
226
|
+
for (const p of [this.sendSocketPath, this.receiveSocketPath]) {
|
|
227
|
+
try {
|
|
228
|
+
if (fs.existsSync(p))
|
|
229
|
+
fs.unlinkSync(p);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// Ignore unlink errors
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
exports.DEFAULT_ZALOCALL_IPC_KEY_BASE64 = DEFAULT_ZALOCALL_IPC_KEY_BASE64;
|
|
239
|
+
exports.ZaloCallTransport = ZaloCallTransport;
|