@softtynet/zca-js 2.3.4 → 2.3.6

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 (129) hide show
  1. package/README.md +47 -11
  2. package/dist/apis/call/addUserGroupCall.d.ts +11 -0
  3. package/dist/apis/call/addUserGroupCall.js +48 -0
  4. package/dist/apis/call/answerAckVoiceCall.d.ts +5 -0
  5. package/dist/apis/call/answerAckVoiceCall.js +38 -0
  6. package/dist/apis/call/answerGroupCall.d.ts +10 -0
  7. package/dist/apis/call/answerGroupCall.js +45 -0
  8. package/dist/apis/call/answerVoiceCall.d.ts +11 -0
  9. package/dist/apis/call/answerVoiceCall.js +50 -0
  10. package/dist/apis/call/broadcastGroupCallState.d.ts +10 -0
  11. package/dist/apis/call/broadcastGroupCallState.js +45 -0
  12. package/dist/apis/call/cancelGroupCall.d.ts +10 -0
  13. package/dist/apis/call/cancelGroupCall.js +45 -0
  14. package/dist/apis/call/cancelVoiceCall.d.ts +1 -1
  15. package/dist/apis/call/cancelVoiceCall.js +5 -1
  16. package/dist/apis/call/createController.d.ts +7 -0
  17. package/dist/apis/call/createController.js +9 -0
  18. package/dist/apis/call/createRtcSession.d.ts +8 -0
  19. package/dist/apis/call/createRtcSession.js +10 -0
  20. package/dist/apis/call/endGroupCall.d.ts +10 -0
  21. package/dist/apis/call/endGroupCall.js +45 -0
  22. package/dist/apis/call/finishGroupCall.d.ts +12 -0
  23. package/dist/apis/call/finishGroupCall.js +47 -0
  24. package/dist/apis/call/getGroupCallState.d.ts +3 -0
  25. package/dist/apis/call/getGroupCallState.js +38 -0
  26. package/dist/apis/call/holdVoiceCall.d.ts +5 -0
  27. package/dist/apis/call/holdVoiceCall.js +34 -0
  28. package/dist/apis/call/joinGroupCall.d.ts +11 -0
  29. package/dist/apis/call/joinGroupCall.js +46 -0
  30. package/dist/apis/call/logEndVoiceCall.d.ts +8 -0
  31. package/dist/apis/call/logEndVoiceCall.js +46 -0
  32. package/dist/apis/call/notifyGroupCall.d.ts +9 -0
  33. package/dist/apis/call/notifyGroupCall.js +44 -0
  34. package/dist/apis/call/pingGroupCall.d.ts +10 -0
  35. package/dist/apis/call/pingGroupCall.js +45 -0
  36. package/dist/apis/call/requestAudioToVideoCall.d.ts +5 -0
  37. package/dist/apis/call/requestAudioToVideoCall.js +37 -0
  38. package/dist/apis/call/requestGroupCall.d.ts +9 -0
  39. package/dist/apis/call/requestGroupCall.js +46 -0
  40. package/dist/apis/call/requestShareScreenCall.d.ts +5 -0
  41. package/dist/apis/call/requestShareScreenCall.js +37 -0
  42. package/dist/apis/call/requestVoiceCall.d.ts +10 -0
  43. package/dist/apis/call/requestVoiceCall.js +47 -0
  44. package/dist/apis/call/ringRingGroupCall.d.ts +10 -0
  45. package/dist/apis/call/ringRingGroupCall.js +45 -0
  46. package/dist/apis/call/ringRingVoiceCall.d.ts +5 -0
  47. package/dist/apis/call/ringRingVoiceCall.js +38 -0
  48. package/dist/apis/call/sendRequestGroupCall.d.ts +11 -0
  49. package/dist/apis/call/sendRequestGroupCall.js +48 -0
  50. package/dist/apis/call/sendRequestVoiceCall.d.ts +11 -0
  51. package/dist/apis/call/sendRequestVoiceCall.js +51 -0
  52. package/dist/apis/call/sendVoiceCall.d.ts +8 -1
  53. package/dist/apis/call/sendVoiceCall.js +10 -8
  54. package/dist/apis/call/shareScreenGroupCall.d.ts +10 -0
  55. package/dist/apis/call/shareScreenGroupCall.js +45 -0
  56. package/dist/apis/call/unholdVoiceCall.d.ts +5 -0
  57. package/dist/apis/call/unholdVoiceCall.js +34 -0
  58. package/dist/apis/call/updateCallMediaState.d.ts +7 -0
  59. package/dist/apis/call/updateCallMediaState.js +61 -0
  60. package/dist/apis/sync/downloadTransferSync.js +19 -1
  61. package/dist/apis.d.ts +81 -0
  62. package/dist/apis.js +135 -0
  63. package/dist/call-engine/ZRTPPacketCodec.d.ts +262 -0
  64. package/dist/call-engine/ZRTPPacketCodec.js +506 -0
  65. package/dist/call-engine/ZaloCallController.d.ts +134 -0
  66. package/dist/call-engine/ZaloCallController.js +638 -0
  67. package/dist/call-engine/ZaloCallTransport.d.ts +83 -0
  68. package/dist/call-engine/ZaloCallTransport.js +259 -0
  69. package/dist/call-engine/ZaloRtcSession.d.ts +609 -0
  70. package/dist/call-engine/ZaloRtcSession.js +2939 -0
  71. package/dist/call-engine/ZaloSrtpSession.d.ts +80 -0
  72. package/dist/call-engine/ZaloSrtpSession.js +278 -0
  73. package/dist/call-engine/index.d.ts +18 -0
  74. package/dist/call-engine/index.js +24 -0
  75. package/dist/cjs/apis/call/addUserGroupCall.cjs +53 -0
  76. package/dist/cjs/apis/call/answerAckVoiceCall.cjs +43 -0
  77. package/dist/cjs/apis/call/answerGroupCall.cjs +50 -0
  78. package/dist/cjs/apis/call/answerVoiceCall.cjs +55 -0
  79. package/dist/cjs/apis/call/broadcastGroupCallState.cjs +50 -0
  80. package/dist/cjs/apis/call/cancelGroupCall.cjs +50 -0
  81. package/dist/cjs/apis/call/cancelVoiceCall.cjs +5 -1
  82. package/dist/cjs/apis/call/createController.cjs +14 -0
  83. package/dist/cjs/apis/call/createRtcSession.cjs +15 -0
  84. package/dist/cjs/apis/call/endGroupCall.cjs +50 -0
  85. package/dist/cjs/apis/call/finishGroupCall.cjs +52 -0
  86. package/dist/cjs/apis/call/getGroupCallState.cjs +43 -0
  87. package/dist/cjs/apis/call/holdVoiceCall.cjs +39 -0
  88. package/dist/cjs/apis/call/joinGroupCall.cjs +51 -0
  89. package/dist/cjs/apis/call/logEndVoiceCall.cjs +51 -0
  90. package/dist/cjs/apis/call/notifyGroupCall.cjs +49 -0
  91. package/dist/cjs/apis/call/pingGroupCall.cjs +50 -0
  92. package/dist/cjs/apis/call/requestAudioToVideoCall.cjs +42 -0
  93. package/dist/cjs/apis/call/requestGroupCall.cjs +51 -0
  94. package/dist/cjs/apis/call/requestShareScreenCall.cjs +42 -0
  95. package/dist/cjs/apis/call/requestVoiceCall.cjs +52 -0
  96. package/dist/cjs/apis/call/ringRingGroupCall.cjs +50 -0
  97. package/dist/cjs/apis/call/ringRingVoiceCall.cjs +43 -0
  98. package/dist/cjs/apis/call/sendRequestGroupCall.cjs +53 -0
  99. package/dist/cjs/apis/call/sendRequestVoiceCall.cjs +56 -0
  100. package/dist/cjs/apis/call/sendVoiceCall.cjs +10 -8
  101. package/dist/cjs/apis/call/shareScreenGroupCall.cjs +50 -0
  102. package/dist/cjs/apis/call/unholdVoiceCall.cjs +39 -0
  103. package/dist/cjs/apis/call/updateCallMediaState.cjs +66 -0
  104. package/dist/cjs/apis/sync/downloadTransferSync.cjs +19 -1
  105. package/dist/cjs/apis.cjs +135 -0
  106. package/dist/cjs/call-engine/ZRTPPacketCodec.cjs +525 -0
  107. package/dist/cjs/call-engine/ZaloCallController.cjs +643 -0
  108. package/dist/cjs/call-engine/ZaloCallTransport.cjs +265 -0
  109. package/dist/cjs/call-engine/ZaloRtcSession.cjs +2945 -0
  110. package/dist/cjs/call-engine/ZaloSrtpSession.cjs +287 -0
  111. package/dist/cjs/constants/protocol.cjs +1 -1
  112. package/dist/cjs/crypto/aes.cjs +3 -1
  113. package/dist/cjs/crypto/zdb-cipher.cjs +10 -3
  114. package/dist/cjs/index.cjs +61 -11
  115. package/dist/cjs/models/CallEvent.cjs +95 -1
  116. package/dist/cjs/realtime/handlers/control.handler.cjs +17 -3
  117. package/dist/cjs/realtime/listener.cjs +19 -0
  118. package/dist/constants/protocol.d.ts +1 -1
  119. package/dist/constants/protocol.js +1 -1
  120. package/dist/crypto/aes.js +3 -1
  121. package/dist/crypto/zdb-cipher.js +10 -3
  122. package/dist/index.d.ts +32 -4
  123. package/dist/index.js +2 -0
  124. package/dist/models/CallEvent.d.ts +15 -0
  125. package/dist/models/CallEvent.js +95 -1
  126. package/dist/realtime/handlers/control.handler.js +17 -3
  127. package/dist/realtime/listener.d.ts +5 -0
  128. package/dist/realtime/listener.js +19 -0
  129. package/package.json +2 -1
@@ -0,0 +1,506 @@
1
+ import { Buffer } from "node:buffer";
2
+ /**
3
+ * Các mã lệnh điều khiển UDP trong `zrtc::ZRTPPacket` (`_buildPacketInternal` @ `0x10015830c`)
4
+ */
5
+ export var ZRTPControlCommand;
6
+ (function (ZRTPControlCommand) {
7
+ ZRTPControlCommand[ZRTPControlCommand["RelayPing"] = 2] = "RelayPing";
8
+ ZRTPControlCommand[ZRTPControlCommand["EndCall"] = 3] = "EndCall";
9
+ ZRTPControlCommand[ZRTPControlCommand["Ping"] = 5] = "Ping";
10
+ ZRTPControlCommand[ZRTPControlCommand["InitCallCaller"] = 11] = "InitCallCaller";
11
+ ZRTPControlCommand[ZRTPControlCommand["InitCallCallee"] = 12] = "InitCallCallee";
12
+ ZRTPControlCommand[ZRTPControlCommand["RequestForward"] = 32] = "RequestForward";
13
+ ZRTPControlCommand[ZRTPControlCommand["InitZaviPing"] = 51] = "InitZaviPing";
14
+ })(ZRTPControlCommand || (ZRTPControlCommand = {}));
15
+ /**
16
+ * Sub-command cho gói tin điều khiển `RequestForward` (`cmd = 32` / `0x0020`, `byte[20]`)
17
+ * dùng để Ping/Pong kiểm tra kết nối 2 chiều qua máy chủ Relay và điều khiển Audio/Video.
18
+ */
19
+ export var ZRTPForwardSubCommand;
20
+ (function (ZRTPForwardSubCommand) {
21
+ ZRTPForwardSubCommand[ZRTPForwardSubCommand["OnMic"] = 5] = "OnMic";
22
+ ZRTPForwardSubCommand[ZRTPForwardSubCommand["OffMic"] = 6] = "OffMic";
23
+ ZRTPForwardSubCommand[ZRTPForwardSubCommand["OnCamera"] = 7] = "OnCamera";
24
+ ZRTPForwardSubCommand[ZRTPForwardSubCommand["OffCamera"] = 8] = "OffCamera";
25
+ ZRTPForwardSubCommand[ZRTPForwardSubCommand["OffCameraCallKit"] = 9] = "OffCameraCallKit";
26
+ ZRTPForwardSubCommand[ZRTPForwardSubCommand["VideoControl"] = 10] = "VideoControl";
27
+ ZRTPForwardSubCommand[ZRTPForwardSubCommand["Ping"] = 11] = "Ping";
28
+ ZRTPForwardSubCommand[ZRTPForwardSubCommand["Pong"] = 16] = "Pong";
29
+ ZRTPForwardSubCommand[ZRTPForwardSubCommand["PartnerSuspendVideo"] = 17] = "PartnerSuspendVideo";
30
+ })(ZRTPForwardSubCommand || (ZRTPForwardSubCommand = {}));
31
+ /**
32
+ * Loại gói tin Media qua máy chủ Relay:
33
+ * - Gửi đi (5-byte header `[type(1B)] + [routingToken(4B LE)] + [RTP/RTCP]`): `{3, 5, 7, 13, 15}`
34
+ * - Nhận về từ Relay (1-byte header `[type(1B)] + [RTP]`): `{4, 7, 14}`
35
+ * - Nhận về RTCP từ Relay (5-byte header `[type(1B)] + [routingToken(4B LE)] + [RTCP]`): `{5, 15}`
36
+ */
37
+ export var ZRTPLegacyMediaType;
38
+ (function (ZRTPLegacyMediaType) {
39
+ ZRTPLegacyMediaType[ZRTPLegacyMediaType["AudioRtp"] = 3] = "AudioRtp";
40
+ ZRTPLegacyMediaType[ZRTPLegacyMediaType["IncomingAudioRtp"] = 4] = "IncomingAudioRtp";
41
+ ZRTPLegacyMediaType[ZRTPLegacyMediaType["AudioRtcp"] = 5] = "AudioRtcp";
42
+ ZRTPLegacyMediaType[ZRTPLegacyMediaType["AudioFec"] = 7] = "AudioFec";
43
+ ZRTPLegacyMediaType[ZRTPLegacyMediaType["VideoRtp"] = 13] = "VideoRtp";
44
+ ZRTPLegacyMediaType[ZRTPLegacyMediaType["IncomingVideoRtp"] = 14] = "IncomingVideoRtp";
45
+ ZRTPLegacyMediaType[ZRTPLegacyMediaType["VideoRtcp"] = 15] = "VideoRtcp";
46
+ })(ZRTPLegacyMediaType || (ZRTPLegacyMediaType = {}));
47
+ /**
48
+ * Sub-command cho gói tin ZRTC Multiplexed Media (`byte[0] == 0x7f`, 9-byte header)
49
+ */
50
+ export var ZRTCMediaSubCommand;
51
+ (function (ZRTCMediaSubCommand) {
52
+ ZRTCMediaSubCommand[ZRTCMediaSubCommand["P2PPingRequest"] = 1] = "P2PPingRequest";
53
+ ZRTCMediaSubCommand[ZRTCMediaSubCommand["P2PPingResponse"] = 2] = "P2PPingResponse";
54
+ ZRTCMediaSubCommand[ZRTCMediaSubCommand["P2PInitCaller"] = 3] = "P2PInitCaller";
55
+ ZRTCMediaSubCommand[ZRTCMediaSubCommand["P2PInitCallee"] = 4] = "P2PInitCallee";
56
+ ZRTCMediaSubCommand[ZRTCMediaSubCommand["AudioRtp"] = 6] = "AudioRtp";
57
+ ZRTCMediaSubCommand[ZRTCMediaSubCommand["AudioRtcp"] = 7] = "AudioRtcp";
58
+ ZRTCMediaSubCommand[ZRTCMediaSubCommand["VideoRtp"] = 8] = "VideoRtp";
59
+ ZRTCMediaSubCommand[ZRTCMediaSubCommand["VideoRtcp"] = 9] = "VideoRtcp";
60
+ ZRTCMediaSubCommand[ZRTCMediaSubCommand["AudioFec"] = 10] = "AudioFec";
61
+ })(ZRTCMediaSubCommand || (ZRTCMediaSubCommand = {}));
62
+ export var ZRTCRole;
63
+ (function (ZRTCRole) {
64
+ ZRTCRole[ZRTCRole["Caller"] = 0] = "Caller";
65
+ ZRTCRole[ZRTCRole["Callee"] = 1] = "Callee";
66
+ })(ZRTCRole || (ZRTCRole = {}));
67
+ /**
68
+ * Chuỗi codec Opus chuẩn được `ZaloCall` tạo trong `sendSignal 416`
69
+ */
70
+ export const DEFAULT_ZALO_AUDIO_CODEC = JSON.stringify([
71
+ {
72
+ dynamicFptime: 0,
73
+ frmPtime: 20,
74
+ name: "opus/16000/1",
75
+ payload: 112,
76
+ },
77
+ ]);
78
+ /**
79
+ * Chuỗi codec Audio + Video H264 chuẩn cho cuộc gọi Video
80
+ */
81
+ export const DEFAULT_ZALO_VIDEO_CODEC = JSON.stringify([
82
+ {
83
+ dynamicFptime: 0,
84
+ frmPtime: 20,
85
+ name: "opus/16000/1",
86
+ payload: 112,
87
+ },
88
+ ]);
89
+ /**
90
+ * Tạo cấu trúc `extendData` chuẩn xác theo `ZaloCall` (`sendSignal 416` / `answer 402`).
91
+ */
92
+ export function buildZrtcExtendData(options) {
93
+ const rtp = options.rtpAddress;
94
+ const rtcp = options.rtcpAddress || options.rtpAddress;
95
+ const isVideo = Boolean(options.isVideo);
96
+ const extend = {
97
+ callType: isVideo ? 1 : 0,
98
+ fecTP: 0,
99
+ gccAudio: 0,
100
+ gccEarlyCall: 0,
101
+ gccMode: 0,
102
+ gccSVLR: 0,
103
+ maxFT: 120,
104
+ newZrtc: 1,
105
+ packetMode: options.packetMode ?? 2,
106
+ platform: options.platform ?? 2,
107
+ sP2P: 0,
108
+ serverAddr: [
109
+ {
110
+ rtcp,
111
+ rtcpIPv6: `::FFFF:${rtcp}`,
112
+ rtp,
113
+ rtpIPv6: `::FFFF:${rtp}`,
114
+ tpType: 0,
115
+ },
116
+ ],
117
+ spTcp: 0,
118
+ srtcp: 0,
119
+ srtpMode: options.srtpMode ?? 0,
120
+ supportCallBusy: 0,
121
+ supportHevcDecode: 0,
122
+ tpType: 0,
123
+ };
124
+ if (options.srtpKey) {
125
+ extend.srtpKey = options.srtpKey;
126
+ }
127
+ // Khớp với CallController::getExtendData (0x10016871c): luôn đính kèm danh sách codec video
128
+ // để phiên gọi có thể nâng cấp Thoại ➔ Video hoặc bật/tắt Camera giữa cuộc gọi.
129
+ extend.video = {
130
+ codec: [{ name: "h264", payload: 97 }],
131
+ };
132
+ return extend;
133
+ }
134
+ /**
135
+ * Rút gọn UID 64-bit dạng chuỗi của Zalo về `uint32` dùng trong header `ZRTPPacket`
136
+ */
137
+ export function uidToUint32(uid) {
138
+ if (uid === undefined || uid === null || uid === "")
139
+ return 0;
140
+ if (typeof uid === "number")
141
+ return uid >>> 0;
142
+ try {
143
+ return Number(BigInt(uid) & 0xffffffffn) >>> 0;
144
+ }
145
+ catch {
146
+ return (Number.parseInt(uid, 10) || 0) >>> 0;
147
+ }
148
+ }
149
+ function writeControlHeader(buf, packetType, subType, seqNum, headerCallId, fromUid, toUid, cmd, flags) {
150
+ buf.writeUInt8(packetType, 0);
151
+ buf.writeUInt8(subType & 0xff, 1);
152
+ buf.writeUInt32LE(seqNum >>> 0, 2);
153
+ buf.writeUInt32LE(headerCallId >>> 0, 6);
154
+ buf.writeUInt32LE(fromUid >>> 0, 10);
155
+ buf.writeUInt32LE(toUid >>> 0, 14);
156
+ buf.writeUInt16LE(cmd & 0xffff, 18);
157
+ buf.writeUInt8(flags & 0xff, 20);
158
+ }
159
+ /**
160
+ * Đóng gói gói tin UDP `InitCall` (`0x01 0x7e`, `cmd = 11` cho Caller hoặc `12` cho Callee)
161
+ * gửi tới máy chủ `rtpaddr` của Zalo (`zrtc::ZRTPPacket::_buildPacketInternal` @ `0x10015830c`).
162
+ */
163
+ export function buildInitCallRequest(options) {
164
+ const isCaller = options.isCaller ?? true;
165
+ const cmd = isCaller ? ZRTPControlCommand.InitCallCaller : ZRTPControlCommand.InitCallCallee;
166
+ const seqNum = options.seqNum ?? 0;
167
+ const headerCallId = options.headerCallId ?? options.callId ?? 0;
168
+ const fromUid = options.fromUid ?? 0;
169
+ const toUid = options.toUid ?? 0;
170
+ const flags = options.flags ?? 1;
171
+ const subFlags = options.subFlags ?? toUid;
172
+ const sessBytes = Buffer.from(options.sessId, "utf-8");
173
+ const cfgBytes = options.zrtcConfig ? Buffer.from(options.zrtcConfig, "utf-8") : undefined;
174
+ const totalLen = 21 + 4 + 4 + 2 + sessBytes.length + (cfgBytes ? 2 + cfgBytes.length : 0);
175
+ const buf = Buffer.alloc(totalLen);
176
+ writeControlHeader(buf, 1, 0x7e, seqNum, headerCallId, fromUid, toUid, cmd, flags);
177
+ buf.writeUInt32LE(options.callId >>> 0, 21);
178
+ buf.writeUInt32LE(subFlags >>> 0, 25);
179
+ buf.writeUInt16LE(sessBytes.length, 29);
180
+ sessBytes.copy(buf, 31);
181
+ if (cfgBytes) {
182
+ const offset = 31 + sessBytes.length;
183
+ buf.writeUInt16LE(cfgBytes.length, offset);
184
+ cfgBytes.copy(buf, offset + 2);
185
+ }
186
+ return buf;
187
+ }
188
+ /**
189
+ * Đóng gói gói tin UDP phản hồi `InitCall` (`0x02 0x7e`, `cmd = 11 | 12`)
190
+ * khớp chính xác với `zrtc::ZRTPPacket::_parsePacketInternal` (`0x100158db8`)
191
+ * và `zrtc::CallController` (`0x100177920`).
192
+ */
193
+ export function buildInitCallResponse(options) {
194
+ const isCaller = options.isCaller ?? true;
195
+ const cmd = isCaller ? ZRTPControlCommand.InitCallCaller : ZRTPControlCommand.InitCallCallee;
196
+ const seqNum = options.seqNum ?? 0;
197
+ const headerCallId = options.headerCallId ?? 0;
198
+ const fromUid = options.fromUid ?? 0;
199
+ const toUid = options.toUid ?? 0;
200
+ const flags = options.flags ?? 1;
201
+ const errorCode = options.errorCode ?? 0;
202
+ const routingToken = (options.routingToken >>> 0) || 1;
203
+ const sessBytes = Buffer.from(options.sessId, "utf-8");
204
+ const buf = Buffer.alloc(35 + sessBytes.length);
205
+ writeControlHeader(buf, 2, 0x7e, seqNum, headerCallId, fromUid, toUid, cmd, flags);
206
+ buf.writeUInt32LE(errorCode >>> 0, 21);
207
+ buf.writeUInt32LE(options.callId >>> 0, 25);
208
+ buf.writeUInt32LE(routingToken, 29);
209
+ buf.writeUInt16LE(sessBytes.length, 33);
210
+ sessBytes.copy(buf, 35);
211
+ return buf;
212
+ }
213
+ /**
214
+ * Đóng gói gói tin UDP `Ping` (`0x01 0x01`, `cmd = 5`) gửi định kỳ tới máy chủ relay của Zalo.
215
+ */
216
+ export function buildPingRequest(options) {
217
+ const elapsedBytes = Buffer.from(String(options.elapsedMs), "utf-8");
218
+ const minLen = options.minByteLength ?? 40;
219
+ const rawLen = 21 + 2 + elapsedBytes.length;
220
+ const buf = Buffer.alloc(Math.max(rawLen, minLen));
221
+ writeControlHeader(buf, 1, 0x01, options.seqNum, options.callId, options.fromUid ?? 0, options.toUid ?? 0, ZRTPControlCommand.Ping, 0x01);
222
+ buf.writeUInt16LE(elapsedBytes.length, 21);
223
+ elapsedBytes.copy(buf, 23);
224
+ return buf;
225
+ }
226
+ /**
227
+ * Đóng gói gói tin UDP phản hồi `Ping` (`0x02 0x01`, `cmd = 5`).
228
+ */
229
+ export function buildPingResponse(options) {
230
+ const elapsedBytes = Buffer.from(String(options.elapsedMs), "utf-8");
231
+ const buf = Buffer.alloc(21 + 4 + 2 + elapsedBytes.length);
232
+ writeControlHeader(buf, 2, 0x01, options.seqNum, options.callId, options.fromUid ?? 0, options.toUid ?? 0, ZRTPControlCommand.Ping, 0x01);
233
+ buf.writeUInt32LE((options.errorCode ?? 0) >>> 0, 21);
234
+ buf.writeUInt16LE(elapsedBytes.length, 25);
235
+ elapsedBytes.copy(buf, 27);
236
+ return buf;
237
+ }
238
+ /**
239
+ * Đóng gói gói tin UDP `RequestPing` (`0x01 0x01`, `cmd = 2`) gửi tới máy chủ Relay Zalo
240
+ * (`zrtc::ZRTPPacket::initZRTPPacketRequestPing` @ `0x100157b88`).
241
+ */
242
+ export function buildRelayPingRequest(options) {
243
+ const sessBytes = Buffer.from(options.sessId, "utf-8");
244
+ const buf = Buffer.alloc(21 + 2 + sessBytes.length);
245
+ writeControlHeader(buf, 1, 0x01, options.seqNum ?? 0, options.callId ?? 0, options.fromUid >>> 0, options.routingToken >>> 0, ZRTPControlCommand.RelayPing, 0x01);
246
+ buf.writeUInt16LE(sessBytes.length, 21);
247
+ sessBytes.copy(buf, 23);
248
+ return buf;
249
+ }
250
+ /**
251
+ * Đóng gói gói tin UDP `RequestForward` (`0x01 0x01`, `cmd = 32` / `0x0020`)
252
+ * dùng cho Ping (`subCmd = 11`) / Pong (`subCmd = 16`) giữa 2 đầu qua máy chủ Relay
253
+ * (`zrtc::ZRTPPacket::initZRTPPacketRequestForward` @ `0x100157c4c`).
254
+ */
255
+ export function buildRequestForwardPacket(options) {
256
+ const payloadBuf = options.payload === undefined
257
+ ? Buffer.alloc(0)
258
+ : Buffer.isBuffer(options.payload)
259
+ ? options.payload
260
+ : Buffer.from(options.payload, "utf-8");
261
+ const buf = Buffer.alloc(21 + 4 + payloadBuf.length);
262
+ writeControlHeader(buf, 1, 0x01, options.seqNum ?? 0, options.seqId >>> 0, options.fromUid >>> 0, options.routingToken >>> 0, ZRTPControlCommand.RequestForward, options.subCmd & 0xff);
263
+ buf.writeUInt32LE(payloadBuf.length >>> 0, 21);
264
+ if (payloadBuf.length > 0) {
265
+ payloadBuf.copy(buf, 25);
266
+ }
267
+ return buf;
268
+ }
269
+ /**
270
+ * Đóng gói gói tin UDP `Echo` (`cmd = 3`)
271
+ */
272
+ export function buildEchoRequest(options) {
273
+ const buf = Buffer.alloc(25);
274
+ writeControlHeader(buf, 1, 0x01, options.seqNum, options.callId, options.fromUid ?? 0, options.toUid ?? 0, ZRTPControlCommand.EndCall, 0x01);
275
+ buf.writeUInt32LE(options.token >>> 0, 21);
276
+ return buf;
277
+ }
278
+ /**
279
+ * Đóng gói gói tin UDP `RequestEndCall` (`0x01 0x7e`, `cmd = 3`, 25 bytes)
280
+ * khớp với `zrtc::ZRTPPacket::initZRTPPacketRequestEndCall` (`0x100157b44`)
281
+ * và `_buildPacketInternal` (`0x10015830c`).
282
+ */
283
+ export function buildEndCallRequest(options) {
284
+ const buf = Buffer.alloc(25);
285
+ const routingOrTo = (options.routingToken ?? options.toUid ?? 0) >>> 0;
286
+ writeControlHeader(buf, 1, 0x7e, options.seqNum ?? 0, 0, options.fromUid ?? 0, routingOrTo, ZRTPControlCommand.EndCall, options.flags ?? 0);
287
+ buf.writeUInt32LE(options.callId >>> 0, 21);
288
+ return buf;
289
+ }
290
+ /**
291
+ * Bọc gói tin chuẩn WebRTC RTP/RTCP/SRTP vào khung 5-byte Legacy Media của Zalo
292
+ * (`byte[0]` in `{3, 5, 7, 13, 15}` + `routingId` 4B LE).
293
+ */
294
+ export function wrapLegacyMediaPacket(mediaType, routingId, rtpOrRtcpPayload) {
295
+ const out = Buffer.allocUnsafe(5 + rtpOrRtcpPayload.length);
296
+ out.writeUInt8(mediaType, 0);
297
+ out.writeUInt32LE(routingId >>> 0, 1);
298
+ rtpOrRtcpPayload.copy(out, 5);
299
+ return out;
300
+ }
301
+ /**
302
+ * Bọc gói tin chuẩn WebRTC RTP/RTCP/SRTP vào khung 9-byte ZRTC Multiplexed Media (`0x7f`)
303
+ * (`[0x7f, 0x00, role, routingToken(4B LE), subCmd(2B LE)] + [RTP/RTCP]`).
304
+ */
305
+ export function wrapZrtcMediaPacket(subCmd, routingToken, rtpOrRtcpPayload, role = ZRTCRole.Caller) {
306
+ const out = Buffer.allocUnsafe(9 + rtpOrRtcpPayload.length);
307
+ out.writeUInt8(0x7f, 0);
308
+ out.writeUInt8(0x00, 1);
309
+ out.writeUInt8(role & 0xff, 2);
310
+ out.writeUInt32LE(routingToken >>> 0, 3);
311
+ out.writeUInt16LE(subCmd & 0xffff, 7);
312
+ rtpOrRtcpPayload.copy(out, 9);
313
+ return out;
314
+ }
315
+ /**
316
+ * Giải mã bất kỳ gói tin UDP `ZRTPPacket` nào nhận được từ máy chủ Relay Zalo hoặc Peer
317
+ * (`zrtc::ZRTPPacket::_parsePacketInternal` @ `0x1001589e8`).
318
+ */
319
+ export function parseZRTPPacket(buf) {
320
+ if (!buf || buf.length < 5) {
321
+ return { family: "raw", raw: buf ?? Buffer.alloc(0) };
322
+ }
323
+ const firstByte = buf.readUInt8(0);
324
+ // 1. Gói tin điều khiển / bắt tay (0x01 = Request, 0x02 = Response)
325
+ if ((firstByte === 1 || firstByte === 2) && buf.length >= 21) {
326
+ const packetType = firstByte;
327
+ const isResponse = packetType === 2;
328
+ const subType = buf.readUInt8(1);
329
+ const seqNum = buf.readUInt32LE(2);
330
+ const headerCallId = buf.readUInt32LE(6);
331
+ const fromUid = buf.readUInt32LE(10);
332
+ const toUid = buf.readUInt32LE(14);
333
+ const cmd = buf.readUInt16LE(18);
334
+ const flags = buf.readUInt8(20);
335
+ const rawBody = buf.subarray(21);
336
+ const packet = {
337
+ family: "control",
338
+ isResponse,
339
+ packetType,
340
+ subType,
341
+ seqNum,
342
+ headerCallId,
343
+ fromUid,
344
+ toUid,
345
+ cmd,
346
+ flags,
347
+ rawBody,
348
+ };
349
+ if (cmd === ZRTPControlCommand.InitCallCaller || cmd === ZRTPControlCommand.InitCallCallee) {
350
+ if (!isResponse && flags !== 6 && buf.length >= 31) {
351
+ packet.callId = buf.readUInt32LE(21);
352
+ packet.subFlags = buf.readUInt32LE(25);
353
+ const sessLen = buf.readUInt16LE(29);
354
+ const sessEnd = Math.min(31 + sessLen, buf.length);
355
+ packet.sessId = buf.subarray(31, sessEnd).toString("utf-8");
356
+ if (buf.length >= sessEnd + 2) {
357
+ const cfgLen = buf.readUInt16LE(sessEnd);
358
+ const cfgEnd = Math.min(sessEnd + 2 + cfgLen, buf.length);
359
+ if (cfgLen > 0) {
360
+ packet.zrtcConfig = buf.subarray(sessEnd + 2, cfgEnd).toString("utf-8");
361
+ }
362
+ }
363
+ }
364
+ else if (isResponse && buf.length >= 25) {
365
+ packet.errorCode = buf.readInt32LE(21);
366
+ if (buf.length >= 33) {
367
+ packet.callId = buf.readUInt32LE(25);
368
+ packet.routingToken = buf.readUInt32LE(29);
369
+ }
370
+ if (buf.length >= 35) {
371
+ const sessLen = buf.readUInt16LE(33);
372
+ const sessEnd = Math.min(35 + sessLen, buf.length);
373
+ packet.sessId = buf.subarray(35, sessEnd).toString("utf-8");
374
+ }
375
+ }
376
+ }
377
+ else if (cmd === ZRTPControlCommand.RelayPing) {
378
+ if (!isResponse && buf.length >= 23) {
379
+ const sessLen = buf.readUInt16LE(21);
380
+ const sessEnd = Math.min(23 + sessLen, buf.length);
381
+ packet.sessId = buf.subarray(23, sessEnd).toString("utf-8");
382
+ }
383
+ else if (isResponse && buf.length >= 25) {
384
+ packet.errorCode = buf.readInt32LE(21);
385
+ if (buf.length >= 27) {
386
+ const sessLen = buf.readUInt16LE(25);
387
+ const sessEnd = Math.min(27 + sessLen, buf.length);
388
+ packet.sessId = buf.subarray(27, sessEnd).toString("utf-8");
389
+ }
390
+ }
391
+ }
392
+ else if (cmd === ZRTPControlCommand.Ping) {
393
+ if (!isResponse && buf.length >= 23) {
394
+ const strLen = buf.readUInt16LE(21);
395
+ const strEnd = Math.min(23 + strLen, buf.length);
396
+ packet.pingString = buf.subarray(23, strEnd).toString("utf-8");
397
+ }
398
+ else if (isResponse && buf.length >= 25) {
399
+ packet.errorCode = buf.readUInt32LE(21);
400
+ if (buf.length >= 27) {
401
+ const strLen = buf.readUInt16LE(25);
402
+ const strEnd = Math.min(27 + strLen, buf.length);
403
+ packet.pingString = buf.subarray(27, strEnd).toString("utf-8");
404
+ }
405
+ }
406
+ }
407
+ else if (cmd === ZRTPControlCommand.RequestForward) {
408
+ if (!isResponse && buf.length >= 25) {
409
+ const payloadLen = buf.readUInt32LE(21);
410
+ const payloadEnd = Math.min(25 + payloadLen, buf.length);
411
+ packet.forwardPayload = buf.subarray(25, payloadEnd);
412
+ }
413
+ else if (!isResponse) {
414
+ packet.forwardPayload = buf.subarray(21);
415
+ }
416
+ else if (isResponse && buf.length >= 25) {
417
+ packet.errorCode = buf.readInt32LE(21);
418
+ }
419
+ }
420
+ else if (cmd === ZRTPControlCommand.EndCall) {
421
+ if (!isResponse && buf.length >= 25) {
422
+ const val = buf.readUInt32LE(21);
423
+ packet.callId = val;
424
+ packet.echoToken = val;
425
+ }
426
+ else if (isResponse && buf.length >= 25) {
427
+ packet.errorCode = buf.readInt32LE(21);
428
+ if (buf.length >= 29) {
429
+ packet.echoToken = buf.readUInt32LE(25);
430
+ }
431
+ }
432
+ }
433
+ return packet;
434
+ }
435
+ // 2a. Gói tin Relay Media nhận về từ máy chủ Zalo (1-byte header: type in {4, 14})
436
+ // (`zrtc::ZRTPPacket::_parsePacketInternal` @ `0x100158a1c`, mask `0x4090`)
437
+ if ((firstByte === ZRTPLegacyMediaType.IncomingAudioRtp ||
438
+ firstByte === ZRTPLegacyMediaType.IncomingVideoRtp) &&
439
+ buf.length >= 13) {
440
+ const mediaType = firstByte;
441
+ return {
442
+ family: "legacy_media",
443
+ mediaType,
444
+ isVideo: mediaType === ZRTPLegacyMediaType.IncomingVideoRtp,
445
+ isRtcp: false,
446
+ isFec: false,
447
+ routingId: 0,
448
+ rtpPayload: buf.subarray(1),
449
+ };
450
+ }
451
+ // 2b. Gói tin Relay Media 5-byte header (`type` in `{3, 5, 7, 13, 15}` + `routingToken` 4B LE)
452
+ if (firstByte === ZRTPLegacyMediaType.AudioRtp ||
453
+ firstByte === ZRTPLegacyMediaType.AudioRtcp ||
454
+ firstByte === ZRTPLegacyMediaType.AudioFec ||
455
+ firstByte === ZRTPLegacyMediaType.VideoRtp ||
456
+ firstByte === ZRTPLegacyMediaType.VideoRtcp) {
457
+ const mediaType = firstByte;
458
+ const routingId = buf.readUInt32LE(1);
459
+ return {
460
+ family: "legacy_media",
461
+ mediaType,
462
+ isVideo: mediaType === ZRTPLegacyMediaType.VideoRtp || mediaType === ZRTPLegacyMediaType.VideoRtcp,
463
+ isRtcp: mediaType === ZRTPLegacyMediaType.AudioRtcp || mediaType === ZRTPLegacyMediaType.VideoRtcp,
464
+ isFec: mediaType === ZRTPLegacyMediaType.AudioFec,
465
+ routingId,
466
+ rtpPayload: buf.subarray(5),
467
+ };
468
+ }
469
+ // 3. Gói tin ZRTC Multiplexed Media / P2P (0x7f, >= 9 bytes)
470
+ if (firstByte === 0x7f && buf.length >= 9) {
471
+ const version = buf.readUInt8(1);
472
+ const role = (buf.readUInt8(2) === 1 ? ZRTCRole.Callee : ZRTCRole.Caller);
473
+ const routingToken = buf.readUInt32LE(3);
474
+ const subCmd = buf.readUInt16LE(7);
475
+ if (subCmd === ZRTCMediaSubCommand.AudioRtp ||
476
+ subCmd === ZRTCMediaSubCommand.AudioRtcp ||
477
+ subCmd === ZRTCMediaSubCommand.VideoRtp ||
478
+ subCmd === ZRTCMediaSubCommand.VideoRtcp ||
479
+ subCmd === ZRTCMediaSubCommand.AudioFec) {
480
+ return {
481
+ family: "zrtc_media",
482
+ version,
483
+ role,
484
+ routingToken,
485
+ subCmd,
486
+ isVideo: subCmd === ZRTCMediaSubCommand.VideoRtp || subCmd === ZRTCMediaSubCommand.VideoRtcp,
487
+ isRtcp: subCmd === ZRTCMediaSubCommand.AudioRtcp || subCmd === ZRTCMediaSubCommand.VideoRtcp,
488
+ isFec: subCmd === ZRTCMediaSubCommand.AudioFec,
489
+ rtpPayload: buf.subarray(9),
490
+ };
491
+ }
492
+ if (buf.length >= 17) {
493
+ return {
494
+ family: "zrtc_p2p",
495
+ version,
496
+ role,
497
+ routingToken,
498
+ subCmd,
499
+ seqNum: buf.readUInt32LE(9),
500
+ callId: buf.readUInt32LE(13),
501
+ payload: buf.subarray(17),
502
+ };
503
+ }
504
+ }
505
+ return { family: "raw", raw: buf };
506
+ }
@@ -0,0 +1,134 @@
1
+ import EventEmitter from "node:events";
2
+ import type { API } from "../apis.js";
3
+ import { type CallEvent } from "../models/CallEvent.js";
4
+ import { ZaloCallTransport, type ZaloCallIpcMessage, type ZaloCallTransportOptions } from "./ZaloCallTransport.js";
5
+ import { ZaloRtcSession, type AcceptIncomingCallOptions, type ParsedIncomingCallInfo, type RejectIncomingCallOptions, type ZaloCallConnectionParams, type ZaloMobileDirectHandoffBundle, type ZaloRtcSessionOptions } from "./ZaloRtcSession.js";
6
+ export type ZaloCallControllerOptions = ZaloCallTransportOptions & ZaloRtcSessionOptions & {
7
+ /**
8
+ * Tự động khởi chạy IPC bridge nội bộ (mặc định `false` — ưu tiên dùng trực tiếp `ZaloRtcSession` thuần TypeScript).
9
+ */
10
+ spawnProcess?: boolean;
11
+ /**
12
+ * Tự động lắng nghe `call_event` từ `api.listener` và cập nhật phiên gọi (mặc định `true`).
13
+ */
14
+ autoForwardCallEvents?: boolean;
15
+ /**
16
+ * Tên hiển thị của tài khoản hiện tại.
17
+ */
18
+ displayName?: string;
19
+ /**
20
+ * Ảnh đại diện của tài khoản hiện tại.
21
+ */
22
+ avatar?: string;
23
+ /**
24
+ * Ngôn ngữ giao diện (`"vi"` hoặc `"en"`).
25
+ */
26
+ lang?: string;
27
+ };
28
+ export type MakeDesktopCallOptions = {
29
+ calleeId: string | number;
30
+ calleeName?: string;
31
+ calleeAvatar?: string;
32
+ isVideoCall?: boolean;
33
+ enableCamera?: boolean;
34
+ };
35
+ export type MakeDesktopGroupCallOptions = {
36
+ groupId: string | number;
37
+ groupName?: string;
38
+ groupAvatar?: string;
39
+ isVideoCall?: boolean;
40
+ members: Array<{
41
+ id: number | string;
42
+ name: string;
43
+ avatar?: string;
44
+ }>;
45
+ };
46
+ export type ZaloCallSignalPayload = {
47
+ type?: number;
48
+ command?: number;
49
+ seqId?: number;
50
+ params?: string | Record<string, unknown>;
51
+ data?: Record<string, unknown>;
52
+ };
53
+ export interface ZaloCallControllerEvents {
54
+ ready: [];
55
+ ipc_message: [message: ZaloCallIpcMessage];
56
+ signal_sent: [signalType: number, seqId: number, params: Record<string, unknown>];
57
+ signal_response: [signalType: number, seqId: number, response: unknown];
58
+ connection_params: [params: ZaloCallConnectionParams];
59
+ incoming_call: [info: ParsedIncomingCallInfo];
60
+ call_ended: [data: unknown];
61
+ call_stats: [data: unknown];
62
+ error: [error: unknown];
63
+ close: [code: number | null];
64
+ }
65
+ /**
66
+ * Bộ điều phối cuộc gọi thuần TypeScript / Node.js tích hợp sẵn `ZaloRtcSession`
67
+ * và kênh IPC cục bộ (`ZaloCallTransport`) cho giao diện tuỳ chỉnh (HTML/JS, Electron, WebView, Mobile Bridge)
68
+ * mà hoàn toàn không phụ thuộc ứng dụng Zalo PC.
69
+ */
70
+ export declare class ZaloCallController extends EventEmitter<ZaloCallControllerEvents> {
71
+ private readonly api;
72
+ readonly transport: ZaloCallTransport;
73
+ private activeSession;
74
+ private isEngineReady;
75
+ private readonly options;
76
+ private boundCallEventListener?;
77
+ private ringNotifiedCalls;
78
+ constructor(api: API, options?: ZaloCallControllerOptions);
79
+ isReady(): boolean;
80
+ getActiveSession(): ZaloRtcSession | null;
81
+ /**
82
+ * Khởi tạo bộ điều khiển cuộc gọi thuần TypeScript & kênh IPC cục bộ.
83
+ */
84
+ start(): Promise<void>;
85
+ /**
86
+ * Thực hiện cuộc gọi 1-1 (Thoại hoặc Video) bằng thuật toán thuần TypeScript (`ZaloRtcSession`).
87
+ */
88
+ makeCall(options: MakeDesktopCallOptions): Promise<ZaloCallConnectionParams>;
89
+ /**
90
+ * Giải mã và báo máy đang đổ chuông (`ringRingVoiceCall` / `ringRingGroupCall`) cho cuộc gọi đến.
91
+ */
92
+ ringIncomingCall(callEvent: CallEvent | Record<string, unknown>): Promise<ParsedIncomingCallInfo>;
93
+ /**
94
+ * Chấp nhận cuộc gọi đến (1-1 hoặc Nhóm) bằng `ZaloRtcSession` thuần TypeScript.
95
+ */
96
+ acceptIncomingCall(callEvent: CallEvent | Record<string, unknown>, options?: AcceptIncomingCallOptions): Promise<ZaloCallConnectionParams>;
97
+ /**
98
+ * Tái kích hoạt kết nối UDP (Re-bind UDP socket + bắt tay `InitCall` + khởi động lại vòng lặp Ping/Media/RTCP)
99
+ * cho phiên gọi đang hoạt động.
100
+ */
101
+ reconnectUdp(options?: {
102
+ skipUdpHandshake?: boolean;
103
+ }): Promise<ZaloCallConnectionParams | null>;
104
+ /**
105
+ * Từ chối cuộc gọi đến (1-1 hoặc Nhóm) bằng `ZaloRtcSession` thuần TypeScript.
106
+ */
107
+ rejectIncomingCall(callEvent: CallEvent | Record<string, unknown>, options?: RejectIncomingCallOptions): Promise<void>;
108
+ /**
109
+ * Ngắt kênh UDP cục bộ tại Worker để bàn giao luồng kết nối UDP trực tiếp cho Mobile App.
110
+ */
111
+ handoffUdpToExternal(routingTokenOverride?: number): ZaloMobileDirectHandoffBundle | null;
112
+ /**
113
+ * Khởi tạo cuộc gọi Nhóm (Group Audio / Video Call) qua kênh IPC và `ZaloRtcSession`.
114
+ */
115
+ makeGroupCall(options: MakeDesktopGroupCallOptions): void;
116
+ /**
117
+ * Chuyển tiếp một sự kiện `call_event` từ WebSocket Zalo xuống `ZaloCall` dưới dạng gói `{ type: "control" }`.
118
+ */
119
+ forwardCallEvent(event: CallEvent): void;
120
+ /**
121
+ * Đóng phiên gọi hiện tại và giải phóng toàn bộ kênh IPC.
122
+ */
123
+ stop(): Promise<void>;
124
+ private sendBootstrapConfig;
125
+ private handleIpcMessage;
126
+ /**
127
+ * Cầu nối điều phối toàn bộ 28 mã lệnh `sendSignal` (`401..418` và `501..514`)
128
+ * từ `ZaloCall` sang các hàm trong `api.call.*` và trả kết quả về qua `recvSignal`.
129
+ */
130
+ private handleSignalFromEngine;
131
+ private normalizeControlAct;
132
+ private attachCallEventListener;
133
+ private detachCallEventListener;
134
+ }