@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,638 @@
1
+ import EventEmitter from "node:events";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { CallType } from "../apis/call/sendVoiceCall.js";
6
+ import { CallEventType } from "../models/CallEvent.js";
7
+ import { ZaloCallTransport, } from "./ZaloCallTransport.js";
8
+ import { ZaloRtcSession, parseIncomingCallEvent, } from "./ZaloRtcSession.js";
9
+ /**
10
+ * Bộ điều phối cuộc gọi thuần TypeScript / Node.js tích hợp sẵn `ZaloRtcSession`
11
+ * và kênh IPC cục bộ (`ZaloCallTransport`) cho giao diện tuỳ chỉnh (HTML/JS, Electron, WebView, Mobile Bridge)
12
+ * mà hoàn toàn không phụ thuộc ứng dụng Zalo PC.
13
+ */
14
+ export class ZaloCallController extends EventEmitter {
15
+ api;
16
+ transport;
17
+ activeSession = null;
18
+ isEngineReady = false;
19
+ options;
20
+ boundCallEventListener;
21
+ ringNotifiedCalls = new Set();
22
+ constructor(api, options = {}) {
23
+ super();
24
+ this.api = api;
25
+ this.options = {
26
+ spawnProcess: false,
27
+ autoForwardCallEvents: true,
28
+ ...options,
29
+ };
30
+ this.transport = new ZaloCallTransport(options);
31
+ this.transport.on("client_connected", () => {
32
+ this.isEngineReady = true;
33
+ this.sendBootstrapConfig();
34
+ this.emit("ready");
35
+ });
36
+ this.transport.on("message", (msg) => {
37
+ void this.handleIpcMessage(msg);
38
+ });
39
+ this.transport.on("error", (err) => {
40
+ this.emit("error", err);
41
+ });
42
+ }
43
+ isReady() {
44
+ return this.isEngineReady;
45
+ }
46
+ getActiveSession() {
47
+ return this.activeSession;
48
+ }
49
+ /**
50
+ * Khởi tạo bộ điều khiển cuộc gọi thuần TypeScript & kênh IPC cục bộ.
51
+ */
52
+ async start() {
53
+ await this.transport.start();
54
+ if (this.options.autoForwardCallEvents) {
55
+ this.attachCallEventListener();
56
+ }
57
+ this.isEngineReady = true;
58
+ this.emit("ready");
59
+ }
60
+ /**
61
+ * 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`).
62
+ */
63
+ async makeCall(options) {
64
+ const isVideo = Boolean(options.isVideoCall);
65
+ if (this.activeSession) {
66
+ await this.activeSession.endCall("new_call").catch(() => { });
67
+ }
68
+ const session = new ZaloRtcSession(this.api, this.options);
69
+ this.activeSession = session;
70
+ session.on("ended", (reason) => {
71
+ this.emit("call_ended", { reason });
72
+ });
73
+ session.on("error", (err) => {
74
+ this.emit("error", err);
75
+ });
76
+ const params = await session.startOutgoingCall(String(options.calleeId), {
77
+ callType: isVideo ? CallType.Video : CallType.Audio,
78
+ offCameraAtStart: isVideo && options.enableCamera === false,
79
+ });
80
+ this.emit("connection_params", params);
81
+ return params;
82
+ }
83
+ /**
84
+ * Giải mã và báo máy đang đổ chuông (`ringRingVoiceCall` / `ringRingGroupCall`) cho cuộc gọi đến.
85
+ */
86
+ async ringIncomingCall(callEvent) {
87
+ const info = parseIncomingCallEvent(callEvent, this.api.getContext().uid);
88
+ if (this.activeSession &&
89
+ this.activeSession.getState() !== "idle" &&
90
+ this.activeSession.getState() !== "ended" &&
91
+ this.activeSession.getMediaState().callId !== info.callId) {
92
+ await this.activeSession.endCall("new_incoming_call").catch(() => { });
93
+ this.activeSession = null;
94
+ }
95
+ if (!this.activeSession || this.activeSession.getState() === "ended") {
96
+ const session = new ZaloRtcSession(this.api, this.options);
97
+ this.activeSession = session;
98
+ session.on("ended", (reason) => {
99
+ this.emit("call_ended", { reason });
100
+ });
101
+ session.on("error", (err) => {
102
+ this.emit("error", err);
103
+ });
104
+ }
105
+ return this.activeSession.ringIncomingCall(callEvent);
106
+ }
107
+ /**
108
+ * Chấp nhận cuộc gọi đến (1-1 hoặc Nhóm) bằng `ZaloRtcSession` thuần TypeScript.
109
+ */
110
+ async acceptIncomingCall(callEvent, options = {}) {
111
+ const info = parseIncomingCallEvent(callEvent, this.api.getContext().uid);
112
+ if (this.activeSession &&
113
+ this.activeSession.getState() !== "idle" &&
114
+ this.activeSession.getState() !== "ended" &&
115
+ this.activeSession.getMediaState().callId !== info.callId) {
116
+ await this.activeSession.endCall("accept_new_incoming_call").catch(() => { });
117
+ this.activeSession = null;
118
+ }
119
+ if (!this.activeSession || this.activeSession.getState() === "ended") {
120
+ const session = new ZaloRtcSession(this.api, this.options);
121
+ this.activeSession = session;
122
+ session.on("ended", (reason) => {
123
+ this.emit("call_ended", { reason });
124
+ });
125
+ session.on("error", (err) => {
126
+ this.emit("error", err);
127
+ });
128
+ }
129
+ const params = await this.activeSession.acceptIncomingCall(callEvent, options);
130
+ this.emit("connection_params", params);
131
+ return params;
132
+ }
133
+ /**
134
+ * 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)
135
+ * cho phiên gọi đang hoạt động.
136
+ */
137
+ async reconnectUdp(options = {}) {
138
+ if (!this.activeSession)
139
+ return null;
140
+ const params = await this.activeSession.reconnectUdp(options);
141
+ this.emit("connection_params", params);
142
+ return params;
143
+ }
144
+ /**
145
+ * Từ chối cuộc gọi đến (1-1 hoặc Nhóm) bằng `ZaloRtcSession` thuần TypeScript.
146
+ */
147
+ async rejectIncomingCall(callEvent, options = {}) {
148
+ const session = this.activeSession ?? new ZaloRtcSession(this.api, this.options);
149
+ await session.rejectIncomingCall(callEvent, options);
150
+ }
151
+ /**
152
+ * 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.
153
+ */
154
+ handoffUdpToExternal(routingTokenOverride) {
155
+ if (!this.activeSession)
156
+ return null;
157
+ return this.activeSession.handoffUdpToExternal(routingTokenOverride);
158
+ }
159
+ /**
160
+ * Khởi tạo cuộc gọi Nhóm (Group Audio / Video Call) qua kênh IPC và `ZaloRtcSession`.
161
+ */
162
+ makeGroupCall(options) {
163
+ const isVideo = Boolean(options.isVideoCall);
164
+ this.transport.send({
165
+ type: "request",
166
+ command: "makeCall",
167
+ data: {
168
+ partner: options.members.map((m) => ({
169
+ id: String(m.id),
170
+ name: m.name,
171
+ avatar: m.avatar ?? "",
172
+ })),
173
+ type: isVideo ? CallType.GroupVideo : CallType.GroupAudio,
174
+ groupInfo: {
175
+ id: String(options.groupId),
176
+ name: options.groupName ?? String(options.groupId),
177
+ avatar: options.groupAvatar ?? "",
178
+ },
179
+ },
180
+ });
181
+ }
182
+ /**
183
+ * 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" }`.
184
+ */
185
+ forwardCallEvent(event) {
186
+ const normalizedAct = this.normalizeControlAct(event.act);
187
+ const parsedInfo = parseIncomingCallEvent(event, this.api.getContext().uid);
188
+ const callId = parsedInfo.callId ||
189
+ Number(event.data.callId ?? event.data.call_id ?? event.data.id ?? 0);
190
+ if (event.type === CallEventType.REQUEST ||
191
+ event.type === CallEventType.GROUP_REQUEST ||
192
+ normalizedAct === "request" ||
193
+ normalizedAct === "group_request") {
194
+ this.emit("incoming_call", parsedInfo);
195
+ }
196
+ if (normalizedAct === "ring_ring" && callId > 0) {
197
+ this.ringNotifiedCalls.add(callId);
198
+ }
199
+ // Trong ZCallInfo::receiveAnswer (0x1000cbf28), ZaloCall yêu cầu trạng thái đã qua bước ring_ring (state 8)
200
+ if (normalizedAct === "answer" && callId > 0 && !this.ringNotifiedCalls.has(callId)) {
201
+ this.ringNotifiedCalls.add(callId);
202
+ this.transport.send({
203
+ type: "control",
204
+ data: {
205
+ act: "ring_ring",
206
+ act_type: "voip",
207
+ inCallStatus: "zalo",
208
+ data: {
209
+ callId,
210
+ codec: typeof event.data.codec === "string" ? event.data.codec : "[]",
211
+ params: typeof event.data.params === "string"
212
+ ? event.data.params
213
+ : JSON.stringify(event.data.params ?? {}),
214
+ },
215
+ },
216
+ });
217
+ }
218
+ const controlData = {
219
+ ...event.data,
220
+ };
221
+ if (callId > 0) {
222
+ controlData.callId = callId;
223
+ controlData.id = callId;
224
+ }
225
+ if (normalizedAct === "answer" &&
226
+ controlData.params &&
227
+ typeof controlData.params !== "string") {
228
+ controlData.params = JSON.stringify(controlData.params);
229
+ }
230
+ this.transport.send({
231
+ type: "control",
232
+ data: {
233
+ act: normalizedAct,
234
+ act_type: event.act_type || "voip",
235
+ inCallStatus: "zalo",
236
+ data: controlData,
237
+ },
238
+ });
239
+ }
240
+ /**
241
+ * Đóng phiên gọi hiện tại và giải phóng toàn bộ kênh IPC.
242
+ */
243
+ async stop() {
244
+ this.detachCallEventListener();
245
+ this.isEngineReady = false;
246
+ if (this.activeSession) {
247
+ await this.activeSession.endCall("controller_stop").catch(() => { });
248
+ this.activeSession = null;
249
+ }
250
+ await this.transport.close();
251
+ }
252
+ sendBootstrapConfig() {
253
+ const ctx = this.api.getContext();
254
+ const appPath = path.join(os.tmpdir(), `zalo-call-${this.transport.socketId}`);
255
+ try {
256
+ fs.mkdirSync(appPath, { recursive: true });
257
+ }
258
+ catch {
259
+ // Ignore
260
+ }
261
+ this.transport.send({
262
+ type: "update",
263
+ command: "init",
264
+ data: {
265
+ local: {
266
+ id: String(ctx.uid ?? "0"),
267
+ name: this.options.displayName ?? "Zalo User",
268
+ avatar: this.options.avatar ?? "",
269
+ },
270
+ osInfo: process.platform === "darwin" ? "macOS" : "Windows",
271
+ mainWindowId: 1,
272
+ logPath: appPath,
273
+ zrtcLogPath: appPath,
274
+ dumpPath: appPath,
275
+ clientVersion: ctx.API_VERSION || 689,
276
+ language: this.options.lang ?? ctx.language ?? "vi",
277
+ timeoutMakeCall2ZRtp: 30000,
278
+ enablePreviewDeviceOutgoing: false,
279
+ enablePreviewDeviceIncall: true,
280
+ enableUnstableConnecting: true,
281
+ autoAudioInput: true,
282
+ autoAudioOutput: true,
283
+ defaultAudInputDevice: "",
284
+ defaultAudOutputDevice: "",
285
+ defaultVidDevice: "",
286
+ },
287
+ });
288
+ }
289
+ async handleIpcMessage(msg) {
290
+ this.emit("ipc_message", msg);
291
+ if (msg.type === "ready") {
292
+ if (!this.isEngineReady) {
293
+ this.isEngineReady = true;
294
+ this.sendBootstrapConfig();
295
+ this.emit("ready");
296
+ }
297
+ return;
298
+ }
299
+ if ((msg.type === "request" && msg.command === "serverTime") ||
300
+ msg.type === "serverTime") {
301
+ this.transport.send({
302
+ type: "response",
303
+ command: "serverTime",
304
+ data: { timestamp: Date.now() },
305
+ });
306
+ return;
307
+ }
308
+ if ((msg.type === "request" && msg.command === "getAliasName") ||
309
+ msg.type === "getAliasName") {
310
+ const reqData = (msg.data ?? {});
311
+ this.transport.send({
312
+ type: "response",
313
+ command: "getAliasName",
314
+ data: {
315
+ uid: reqData.uid,
316
+ aliasName: reqData.name ?? "",
317
+ },
318
+ });
319
+ return;
320
+ }
321
+ if (msg.type === "sendSignal") {
322
+ const inner = (msg.data ?? {});
323
+ const signalType = Number(msg.command ?? inner.type ?? 0);
324
+ const seqId = Number(inner.seqId ?? 0);
325
+ const rawParams = inner.params !== undefined ? inner.params : inner;
326
+ if (signalType > 0) {
327
+ await this.handleSignalFromEngine(signalType, seqId, rawParams);
328
+ }
329
+ return;
330
+ }
331
+ if (msg.type === "endCall" || msg.command === "endCall") {
332
+ this.emit("call_ended", msg.data);
333
+ }
334
+ else if (msg.type === "stats" || msg.command === "stats") {
335
+ this.emit("call_stats", msg.data);
336
+ }
337
+ }
338
+ /**
339
+ * Cầu nối điều phối toàn bộ 28 mã lệnh `sendSignal` (`401..418` và `501..514`)
340
+ * từ `ZaloCall` sang các hàm trong `api.call.*` và trả kết quả về qua `recvSignal`.
341
+ */
342
+ async handleSignalFromEngine(type, seqId, rawParams) {
343
+ const paramsObj = typeof rawParams === "string"
344
+ ? JSON.parse(rawParams)
345
+ : (rawParams ?? {});
346
+ this.emit("signal_sent", type, seqId, paramsObj);
347
+ const partnerId = String(paramsObj.calleeId ?? paramsObj.callerId ?? paramsObj.partnerId ?? paramsObj.uid ?? "");
348
+ const callId = Number(paramsObj.callId ?? 0);
349
+ const session = String(paramsObj.session ?? paramsObj.sessionId ?? paramsObj.sessId ?? "");
350
+ const rtpAddress = String(paramsObj.rtpAddress ?? "");
351
+ const rtcpAddress = String(paramsObj.rtcpAddress ?? rtpAddress);
352
+ const codec = typeof paramsObj.codec === "string" ? paramsObj.codec : undefined;
353
+ const extendData = paramsObj.extendData;
354
+ try {
355
+ let result;
356
+ switch (type) {
357
+ case 401: {
358
+ const srv = (await this.api.call.requestVoiceCall(partnerId, {
359
+ callId: callId || undefined,
360
+ callType: Number(paramsObj.typeRequest) || CallType.Audio,
361
+ codec,
362
+ }));
363
+ const rtpAddr = srv.rtpServer ?? srv.rtpIP ?? srv.servers?.[0]?.rtpaddr ?? "";
364
+ const rtcpAddr = srv.rtcpServer ?? srv.rtcpIP ?? srv.servers?.[0]?.rtcpaddr ?? rtpAddr;
365
+ result = {
366
+ ...srv,
367
+ id: srv.callId || callId,
368
+ callId: srv.callId || callId,
369
+ fromId: srv.fromId ?? String(this.api.getContext().uid ?? ""),
370
+ toId: srv.toId ?? partnerId,
371
+ ts: srv.ts ?? Date.now(),
372
+ protocol: srv.protocol ?? 3,
373
+ status: 0,
374
+ sessId: srv.sessId ?? `${partnerId}_${srv.callId || callId}_401`,
375
+ msg: "",
376
+ showDebugInfo: 0,
377
+ showShareScreenBtn: 1,
378
+ zrtc_config: srv.zrtc_config ?? {},
379
+ servers: srv.servers ?? [
380
+ { rtpaddr: rtpAddr, rtcpaddr: rtcpAddr, spTcp: 1, bonus: 0 },
381
+ ],
382
+ };
383
+ break;
384
+ }
385
+ case 402:
386
+ result = await this.api.call.answerVoiceCall(partnerId, callId, {
387
+ sessionId: session,
388
+ rtpAddress,
389
+ rtcpAddress,
390
+ codec,
391
+ extendData,
392
+ status: Number(paramsObj.status ?? 0),
393
+ });
394
+ break;
395
+ case 403:
396
+ result = await this.api.call.holdVoiceCall(partnerId, {
397
+ isResponse: false,
398
+ });
399
+ break;
400
+ case 404:
401
+ result = await this.api.call.holdVoiceCall(partnerId, {
402
+ isResponse: true,
403
+ });
404
+ break;
405
+ case 405:
406
+ result = await this.api.call.cancelVoiceCall(partnerId, callId, {
407
+ callType: Number(paramsObj.callType) || CallType.Audio,
408
+ });
409
+ break;
410
+ case 406:
411
+ result = await this.api.call.endVoiceCall(partnerId, callId, {
412
+ status: Number(paramsObj.status ?? 3),
413
+ });
414
+ break;
415
+ case 407:
416
+ result = await this.api.call.ringRingVoiceCall(partnerId, callId, {
417
+ status: Number(paramsObj.status ?? 0),
418
+ });
419
+ break;
420
+ case 408:
421
+ result = await this.api.call.logEndVoiceCall(partnerId, callId, {
422
+ duration: Number(paramsObj.duration ?? 0),
423
+ status: Number(paramsObj.status ?? 0),
424
+ });
425
+ break;
426
+ case 409:
427
+ result = await this.api.call.answerAckVoiceCall(partnerId, callId);
428
+ break;
429
+ case 411:
430
+ result = await this.api.call.unholdVoiceCall(partnerId, {
431
+ isResponse: false,
432
+ });
433
+ break;
434
+ case 412:
435
+ result = await this.api.call.unholdVoiceCall(partnerId, {
436
+ isResponse: true,
437
+ });
438
+ break;
439
+ case 413:
440
+ case 415:
441
+ result = await this.api.call.updateCallMediaState(partnerId, callId, {
442
+ isOnCam: paramsObj.isOnCam === -1 ||
443
+ paramsObj.isOnCam === 0 ||
444
+ paramsObj.isOnCam === 1 ||
445
+ typeof paramsObj.isOnCam === "boolean"
446
+ ? paramsObj.isOnCam
447
+ : undefined,
448
+ isOnMic: paramsObj.isOnMic === -1 ||
449
+ paramsObj.isOnMic === 0 ||
450
+ paramsObj.isOnMic === 1 ||
451
+ typeof paramsObj.isOnMic === "boolean"
452
+ ? paramsObj.isOnMic
453
+ : undefined,
454
+ });
455
+ break;
456
+ case 416: {
457
+ const reqRes = await this.api.call.sendRequestVoiceCall(partnerId, {
458
+ callId,
459
+ session,
460
+ rtpAddress,
461
+ rtcpAddress,
462
+ codec,
463
+ extendData,
464
+ subCommand: Number(paramsObj.subCommand ?? 3),
465
+ });
466
+ result = {
467
+ ...(typeof reqRes === "object" && reqRes !== null ? reqRes : {}),
468
+ status: 0,
469
+ id: callId,
470
+ };
471
+ break;
472
+ }
473
+ case 417:
474
+ result = await this.api.call.requestAudioToVideoCall(partnerId, callId, {
475
+ status: Number(paramsObj.status ?? 0),
476
+ });
477
+ break;
478
+ case 418:
479
+ result = await this.api.call.requestShareScreenCall(partnerId, callId, {
480
+ status: Number(paramsObj.status ?? 1),
481
+ });
482
+ break;
483
+ case 501:
484
+ result = await this.api.call.requestGroupCall(String(paramsObj.groupId ?? ""), {
485
+ callId: callId || undefined,
486
+ typeRequest: Number(paramsObj.typeRequest) || CallType.GroupAudio,
487
+ data: paramsObj.data,
488
+ partners: paramsObj.partners ?? [],
489
+ });
490
+ break;
491
+ case 502:
492
+ result = await this.api.call.addUserGroupCall(callId, {
493
+ hostCall: String(paramsObj.hostCall ?? ""),
494
+ session,
495
+ partners: paramsObj.partners ?? [],
496
+ });
497
+ break;
498
+ case 503:
499
+ result = await this.api.call.answerGroupCall(callId, {
500
+ hostCall: String(paramsObj.hostCall ?? ""),
501
+ data: paramsObj.data,
502
+ });
503
+ break;
504
+ case 504:
505
+ result = await this.api.call.cancelGroupCall(callId, {
506
+ hostCall: String(paramsObj.hostCall ?? ""),
507
+ data: paramsObj.data,
508
+ });
509
+ break;
510
+ case 505:
511
+ result = await this.api.call.endGroupCall(callId, {
512
+ hostCall: String(paramsObj.hostCall ?? ""),
513
+ data: paramsObj.data,
514
+ });
515
+ break;
516
+ case 506:
517
+ result = await this.api.call.ringRingGroupCall(callId, {
518
+ hostCall: String(paramsObj.hostCall ?? ""),
519
+ data: paramsObj.data,
520
+ });
521
+ break;
522
+ case 507:
523
+ result = await this.api.call.finishGroupCall(callId, {
524
+ hostCall: String(paramsObj.hostCall ?? ""),
525
+ duration: Number(paramsObj.duration ?? 0),
526
+ });
527
+ break;
528
+ case 508:
529
+ result = await this.api.call.getGroupCallState(callId, String(paramsObj.hostCall ?? ""));
530
+ break;
531
+ case 509:
532
+ result = await this.api.call.pingGroupCall(callId, {
533
+ hostCall: String(paramsObj.hostCall ?? ""),
534
+ });
535
+ break;
536
+ case 510:
537
+ result = await this.api.call.broadcastGroupCallState(callId, {
538
+ hostCall: String(paramsObj.hostCall ?? ""),
539
+ });
540
+ break;
541
+ case 511:
542
+ result = await this.api.call.joinGroupCall(callId, {
543
+ hostCall: String(paramsObj.hostCall ?? ""),
544
+ });
545
+ break;
546
+ case 512:
547
+ result = await this.api.call.shareScreenGroupCall(callId, {
548
+ hostCall: String(paramsObj.hostCall ?? ""),
549
+ });
550
+ break;
551
+ case 513:
552
+ result = await this.api.call.sendRequestGroupCall(String(paramsObj.groupId ?? ""), {
553
+ callId,
554
+ session,
555
+ partners: paramsObj.partners ?? [],
556
+ });
557
+ break;
558
+ case 514:
559
+ result = await this.api.call.notifyGroupCall(callId, {
560
+ hostCall: String(paramsObj.hostCall ?? ""),
561
+ });
562
+ break;
563
+ default:
564
+ result = {};
565
+ break;
566
+ }
567
+ this.emit("signal_response", type, seqId, result);
568
+ this.transport.send({
569
+ type: "recvSignal",
570
+ command: type,
571
+ data: {
572
+ ...(typeof result === "object" && result !== null
573
+ ? result
574
+ : {}),
575
+ type,
576
+ seqId,
577
+ errorCode: 0,
578
+ errorMessage: "Successful.",
579
+ },
580
+ });
581
+ }
582
+ catch (err) {
583
+ this.emit("error", err);
584
+ this.transport.send({
585
+ type: "recvSignal",
586
+ command: type,
587
+ data: {
588
+ type,
589
+ seqId,
590
+ errorCode: -1,
591
+ errorMessage: err instanceof Error ? err.message : String(err),
592
+ },
593
+ });
594
+ }
595
+ }
596
+ normalizeControlAct(act) {
597
+ switch (act) {
598
+ case "ringring":
599
+ return "ring_ring";
600
+ case "end":
601
+ case "endcall":
602
+ return "end_call";
603
+ case "answerack":
604
+ return "answer_ack";
605
+ case "group_ringring":
606
+ return "group_ring_ring";
607
+ case "group_end":
608
+ case "group_endcall":
609
+ return "group_end_call";
610
+ default:
611
+ return act;
612
+ }
613
+ }
614
+ attachCallEventListener() {
615
+ if (this.boundCallEventListener)
616
+ return;
617
+ this.boundCallEventListener = (event) => {
618
+ this.forwardCallEvent(event);
619
+ };
620
+ try {
621
+ this.api.listener.on("call_event", this.boundCallEventListener);
622
+ }
623
+ catch {
624
+ // Ignore if listener is not active
625
+ }
626
+ }
627
+ detachCallEventListener() {
628
+ if (!this.boundCallEventListener)
629
+ return;
630
+ try {
631
+ this.api.listener.removeListener("call_event", this.boundCallEventListener);
632
+ }
633
+ catch {
634
+ // Ignore
635
+ }
636
+ this.boundCallEventListener = undefined;
637
+ }
638
+ }