@koi-design/callkit 1.0.22

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/dist/index.mjs ADDED
@@ -0,0 +1,1662 @@
1
+ // package/index.ts
2
+ import md5 from "blueimp-md5";
3
+
4
+ // package/axios.ts
5
+ import axios from "axios";
6
+ var instance = axios.create({
7
+ headers: {
8
+ "Content-Type": "application/x-www-form-urlencoded"
9
+ }
10
+ });
11
+ instance.interceptors.request.use((config) => config);
12
+ instance.interceptors.response.use(
13
+ (response) => response.data,
14
+ (error) => Promise.reject(error)
15
+ );
16
+ var request = (config) => instance.request(config);
17
+ var axios_default = request;
18
+
19
+ // package/api.ts
20
+ var Api = class {
21
+ callKit;
22
+ constructor(callKit) {
23
+ this.callKit = callKit;
24
+ }
25
+ async login(params) {
26
+ return this.post({
27
+ url: "/auth/agentUser/login",
28
+ method: "post",
29
+ data: params
30
+ });
31
+ }
32
+ async loginOut(params) {
33
+ return this.post({
34
+ url: "/auth/agentUser/loginOut",
35
+ method: "post",
36
+ data: params
37
+ });
38
+ }
39
+ /**
40
+ *
41
+ * @param params agentId userStatus
42
+ * @description userStatus: Logout(1, "unregistered"), Free(2, "free"), Break(3, "nap"), Busy(4, "busy")
43
+ * @returns
44
+ */
45
+ async setUserStatus(params) {
46
+ return this.post({
47
+ url: "/agent/user/changeStatus",
48
+ method: "post",
49
+ data: params
50
+ });
51
+ }
52
+ async post(config) {
53
+ this.callKit.logger.debug(`Request ${config.url}:`, config.data);
54
+ const { userInfo, host } = this.callKit.config.getConfig();
55
+ const { sessionId } = userInfo;
56
+ config.url = `${host}${config.url}`;
57
+ config.headers = {
58
+ ...config.headers,
59
+ "Content-Type": "application/x-www-form-urlencoded"
60
+ };
61
+ if (config.headers["Content-Type"] === "application/x-www-form-urlencoded") {
62
+ config.data = new URLSearchParams(config.data).toString();
63
+ }
64
+ if (sessionId) {
65
+ config.headers.sessionId = sessionId;
66
+ }
67
+ const res = await axios_default(config).catch(() => {
68
+ this.callKit.config.reset();
69
+ });
70
+ if (!res) {
71
+ this.callKit.config.reset();
72
+ throw new Error("Network error");
73
+ }
74
+ const { code, data, message } = res;
75
+ if (code === "000000") {
76
+ return data;
77
+ }
78
+ if (code === "100013") {
79
+ this.callKit.config.reset();
80
+ }
81
+ throw new Error(message ?? "Request failed");
82
+ }
83
+ };
84
+
85
+ // package/const.ts
86
+ var UserStatus = {
87
+ /**
88
+ * Offline
89
+ */
90
+ offline: 1,
91
+ /**
92
+ * Online & Idle
93
+ */
94
+ online: 2,
95
+ /**
96
+ * Nap
97
+ */
98
+ catNap: 3,
99
+ /**
100
+ * Busy
101
+ */
102
+ busy: 4,
103
+ /**
104
+ * Training
105
+ */
106
+ training: 5,
107
+ /**
108
+ * Processing
109
+ */
110
+ processing: 6
111
+ };
112
+ var CallStatus = {
113
+ /**
114
+ * Initial state/Hang up
115
+ */
116
+ init: 0,
117
+ /**
118
+ * Registered
119
+ */
120
+ registered: 1,
121
+ /**
122
+ * Connecting
123
+ */
124
+ connecting: 2,
125
+ /**
126
+ * Call on hold
127
+ */
128
+ holding: 3,
129
+ /**
130
+ * Ringing
131
+ */
132
+ ringing: 4,
133
+ /**
134
+ * In call
135
+ */
136
+ calling: 5
137
+ };
138
+ var KitEvent = {
139
+ /**
140
+ * User status change
141
+ */
142
+ KIT_USER_STATUS_CHANGE: "userStatusChange",
143
+ /**
144
+ * User login status change
145
+ */
146
+ KIT_LOGIN_CHANGE: "loginChange",
147
+ /**
148
+ * Registration status change
149
+ */
150
+ KIT_REGISTER_CHANGE: "registerChange",
151
+ /**
152
+ * Call status change
153
+ */
154
+ KIT_CALL_STATUS_CHANGE: "callStatusChange",
155
+ /**
156
+ * Hold status change
157
+ */
158
+ KIT_SET_HOLD: "holdChange",
159
+ /**
160
+ * Mute status change
161
+ */
162
+ KIT_SET_MUTE: "muteChange",
163
+ /**
164
+ * Error
165
+ */
166
+ KIT_ERROR: "error",
167
+ /**
168
+ * Server initiated call
169
+ */
170
+ KIT_INVITE: "invite",
171
+ /**
172
+ * Connecting
173
+ */
174
+ CALL_CONNECTING: "connecting",
175
+ /**
176
+ * Customer ringing
177
+ */
178
+ CALL_RINGING: "ringing",
179
+ /**
180
+ * Agent pick up
181
+ */
182
+ AGENT_PICK_UP: "agentPickUp",
183
+ /**
184
+ * Customer answer
185
+ */
186
+ CALL_PICK_UP: "pickUp",
187
+ /**
188
+ * Customer no response
189
+ */
190
+ CALL_NO_ANSWER: "noAnswer",
191
+ /**
192
+ * Customer hang up
193
+ */
194
+ CALL_HANG_UP: "hangUp",
195
+ /**
196
+ * Call end
197
+ */
198
+ CALL_END: "callEnd",
199
+ /**
200
+ * Call detail record push
201
+ */
202
+ CALL_CDR: "callCdr",
203
+ /**
204
+ * Forward all socket events
205
+ */
206
+ SERVER_SOCKET_EVENT: "socketEvent",
207
+ /**
208
+ * User status change
209
+ */
210
+ USER_STATUS_CHANGE: "userStatusChange"
211
+ };
212
+ var ErrorCode = {
213
+ /**
214
+ * Unknown error
215
+ */
216
+ UNKNOWN_ERROR: -1,
217
+ /**
218
+ * API user login failed
219
+ */
220
+ API_USER_LOGIN_ERROR: 1000001,
221
+ /**
222
+ * API user status update failed
223
+ */
224
+ API_USER_STATUS_UPDATE_ERROR: 1000002,
225
+ /**
226
+ * API user logout failed
227
+ */
228
+ API_USER_LOGOUT_ERROR: 1000003,
229
+ /**
230
+ * connectStatus call status error
231
+ */
232
+ CONNECT_CALL_STATUS_ERROR: 2000001,
233
+ /**
234
+ * User not logged in
235
+ */
236
+ USER_NOT_LOGIN: 2000002,
237
+ /**
238
+ * Browser version too low, does not support webrtc getUserMedia
239
+ */
240
+ WEBRTC_USER_MEDIA_ERROR: 2000003,
241
+ /**
242
+ * Call hold failed
243
+ */
244
+ WEBRTC_HOLE_STATUS_ERROR: 2000004,
245
+ /**
246
+ * Audio player does not exist
247
+ */
248
+ WEBRTC_AUDIO_PLAYER_ERROR: 2000005,
249
+ /**
250
+ * Audio playback failed
251
+ */
252
+ WEBRTC_AUDIO_PLAY_ERROR: 2000006,
253
+ /**
254
+ * User agent startup failed
255
+ */
256
+ WEBRTC_USER_AGENT_ERROR: 2000007,
257
+ /**
258
+ * Call request failed
259
+ */
260
+ WEBRTC_CALL_INVITE_ERROR: 2000008,
261
+ /**
262
+ * Register request failed
263
+ */
264
+ WEBRTC_REGISTER_ERROR: 2000009,
265
+ /**
266
+ * Mute failed
267
+ */
268
+ WEBRTC_MUTE_STATUS_ERROR: 2000010,
269
+ /**
270
+ * Unregister failed
271
+ */
272
+ WEBRTC_CANCEL_REGISTER_ERROR: 2000011,
273
+ /**
274
+ * Mute failed
275
+ */
276
+ WEBRTC_MUTE_ERROR: 2000012,
277
+ /**
278
+ * Socket connection error
279
+ */
280
+ SOCKET_CONNECT_ERROR: 3000001,
281
+ /**
282
+ * Ping timeout
283
+ */
284
+ SOCKET_PING_TIMEOUT: 3000002,
285
+ /**
286
+ * Server error
287
+ */
288
+ SOKET_SERVER_ERROR: 3000003,
289
+ /**
290
+ * API call failed
291
+ */
292
+ SOCKET_CALL_ERROR: 3000004,
293
+ /**
294
+ * Reconnection limit exceeded
295
+ */
296
+ SOCKET_RECONNECT_FAILED: 3000005
297
+ };
298
+ var LoggerLevelMap = {
299
+ debug: 9,
300
+ log: 4,
301
+ warn: 3,
302
+ error: 2,
303
+ silent: 1
304
+ };
305
+ var SocketSendEvent = {
306
+ /**
307
+ * ping
308
+ */
309
+ PING: "PING",
310
+ /**
311
+ * Start
312
+ */
313
+ START: "START",
314
+ /**
315
+ * Hang up
316
+ */
317
+ AGENT_HANGUP: "AGENT_HANG_UP",
318
+ /**
319
+ * Cancel
320
+ */
321
+ CALL_CANCEL: "AGENT_CANCEL",
322
+ /**
323
+ * Hold
324
+ */
325
+ HOLD: "AGENT_HOLD",
326
+ /**
327
+ * Unhold
328
+ */
329
+ UNHOLD: "AGENT_UN_HOLD",
330
+ /**
331
+ * Call
332
+ */
333
+ CALL: "CALL",
334
+ /**
335
+ * End
336
+ */
337
+ END: "STOP"
338
+ };
339
+ var SocketReceiveEvent = {
340
+ /**
341
+ * Response
342
+ */
343
+ PONG: "PONG",
344
+ /**
345
+ * Start confirmation
346
+ */
347
+ START_CONFIRM: "START_CONFIRM",
348
+ /**
349
+ * Call success
350
+ */
351
+ CALL_SUCCESS: "CALL_SUCCESS",
352
+ /**
353
+ * Call failed
354
+ */
355
+ CALL_FAILED: "CALL_FAILED",
356
+ /**
357
+ * Customer ringing
358
+ */
359
+ CUSTOMER_RINGING: "CUSTOMER_RINGING",
360
+ /**
361
+ * Agent pick up
362
+ */
363
+ AGENT_PICK_UP: "AGENT_PICK_UP",
364
+ /**
365
+ * Customer pick up
366
+ */
367
+ CUSTOMER_PICK_UP: "CUSTOMER_PICK_UP",
368
+ /**
369
+ * Customer no answer
370
+ */
371
+ CUSTOMER_NO_ANSWER: "CUSTOMER_NO_ANSWER",
372
+ /**
373
+ * Customer hang up
374
+ */
375
+ CUSTOMER_HANG_UP: "CUSTOMER_HANG_UP",
376
+ /**
377
+ * Agent no answer
378
+ */
379
+ AGENT_NO_ANSWER: "AGENT_NO_ANSWER",
380
+ /**
381
+ * Call detail record push
382
+ */
383
+ CALL_CDR: "CALL_CDR",
384
+ /**
385
+ * Stop confirmation
386
+ */
387
+ STOP_CONFIRM: "STOP_CONFIRM",
388
+ /**
389
+ * Close
390
+ */
391
+ CLOSE: "CLOSE",
392
+ /**
393
+ * Error
394
+ */
395
+ ERROR: "ERROR"
396
+ };
397
+ var constrainsDefault = {
398
+ audio: {
399
+ autoGainControl: true,
400
+ // Noise suppression
401
+ noiseSuppression: true,
402
+ // Set echo cancellation
403
+ echoCancellation: true
404
+ },
405
+ video: false
406
+ };
407
+ var isAutoUpdateUserStatusDefault = true;
408
+
409
+ // package/call.ts
410
+ var Call = class {
411
+ callKit;
412
+ constructor(callKit) {
413
+ this.callKit = callKit;
414
+ }
415
+ async callStart() {
416
+ if (!this.callKit.config.check())
417
+ return;
418
+ this.callKit.logger.debug("callStart");
419
+ if (!this.callKit.socket.satrtConfirm) {
420
+ this.callKit.logger.warn("server not confirm start");
421
+ return;
422
+ }
423
+ this.callKit.connect.call(async (user) => {
424
+ const queryTrain = {
425
+ agentId: user.agentId,
426
+ phoneNum: user.extno
427
+ };
428
+ this.callKit.socket.send(SocketSendEvent.CALL, queryTrain);
429
+ });
430
+ }
431
+ /**
432
+ * Hang up
433
+ * @param isUnprompted Whether to actively hang up
434
+ * @param isError Whether an error occurred
435
+ * @returns
436
+ */
437
+ async callEnd(isUnprompted = false, isError = false) {
438
+ if (this.callKit.connect.connectStatus === CallStatus.init || this.callKit.connect.connectStatus === CallStatus.registered)
439
+ return;
440
+ if (!this.callKit.config.check())
441
+ return;
442
+ this.callKit.connect.hangup(isUnprompted, isError);
443
+ }
444
+ async callHold() {
445
+ if (!this.callKit.config.check())
446
+ return;
447
+ if (!this.callKit.connect.isCalling()) {
448
+ this.callKit.logger.warn("Current state cannot be held");
449
+ return;
450
+ }
451
+ this.callKit.connect.hold();
452
+ this.callKit.socket.send(SocketSendEvent.HOLD);
453
+ this.callKit.connect.setConnectStatus(CallStatus.holding);
454
+ }
455
+ async callUnhold() {
456
+ if (!this.callKit.config.check())
457
+ return;
458
+ if (!this.callKit.connect.isHolding()) {
459
+ this.callKit.logger.warn("Current state cannot unhold");
460
+ return;
461
+ }
462
+ this.callKit.connect.unhold();
463
+ this.callKit.socket.send(SocketSendEvent.UNHOLD);
464
+ this.callKit.connect.setConnectStatus(CallStatus.calling);
465
+ }
466
+ };
467
+
468
+ // package/config.ts
469
+ var Config = class {
470
+ callKit;
471
+ constructor(callKit) {
472
+ this.callKit = callKit;
473
+ }
474
+ config = {
475
+ version: "1.0.2",
476
+ host: "",
477
+ log: "debug",
478
+ audioRef: void 0,
479
+ constrains: constrainsDefault,
480
+ socket: "",
481
+ userInfo: {
482
+ wsUrl: "",
483
+ sessionId: "",
484
+ // User
485
+ username: "",
486
+ // Password
487
+ password: "",
488
+ // Extension number
489
+ extno: "",
490
+ userPart: "",
491
+ agentId: "",
492
+ fsUserId: "",
493
+ fsPassword: "",
494
+ fsIp: "",
495
+ fsPort: "",
496
+ iceInfo: [],
497
+ iceGatheringTimeout: 0
498
+ },
499
+ // EXECUTE setUserStatus
500
+ isAutoUpdateUserStatus: true
501
+ };
502
+ getConfig = () => this.config;
503
+ setConfig = async (key, value) => {
504
+ this.config[key] = value;
505
+ };
506
+ setUserInfo = async (key, value) => {
507
+ this.config.userInfo[key] = value;
508
+ this.callKit.logger.debug(`setUserInfo: key: ${key}, value: ${value}`);
509
+ };
510
+ reset = () => {
511
+ if (this.isLogin()) {
512
+ this.config.userInfo = {
513
+ wsUrl: "",
514
+ sessionId: "",
515
+ username: "",
516
+ password: "",
517
+ userPart: "",
518
+ extno: "",
519
+ agentId: "",
520
+ fsUserId: "",
521
+ fsPassword: "",
522
+ fsIp: "",
523
+ fsPort: "",
524
+ iceInfo: [],
525
+ iceGatheringTimeout: this.config.userInfo.iceGatheringTimeout
526
+ };
527
+ this.callKit.trigger(KitEvent.KIT_LOGIN_CHANGE, false);
528
+ }
529
+ };
530
+ validate = () => {
531
+ const {
532
+ userPart,
533
+ fsIp,
534
+ fsPassword,
535
+ fsPort
536
+ } = this.config.userInfo;
537
+ if (!userPart || !fsIp || !fsPort || !fsPassword) {
538
+ return false;
539
+ }
540
+ return true;
541
+ };
542
+ isLogin = () => this.validate();
543
+ check() {
544
+ if (!this.isLogin()) {
545
+ this.callKit.logger.error("User not logged in", {
546
+ errCode: ErrorCode.USER_NOT_LOGIN
547
+ });
548
+ return false;
549
+ }
550
+ return true;
551
+ }
552
+ };
553
+
554
+ // package/logger.ts
555
+ function getLevel(level) {
556
+ return LoggerLevelMap[level];
557
+ }
558
+ var Logger = class {
559
+ prefix = "CallKit";
560
+ level = "debug";
561
+ callKit;
562
+ constructor(callKit, level) {
563
+ this.callKit = callKit;
564
+ this.level = level || "debug";
565
+ }
566
+ setLevel(level) {
567
+ this.level = level;
568
+ }
569
+ debug(msg, extra) {
570
+ if (getLevel(this.level) >= getLevel("debug")) {
571
+ this.output("blue")(msg, extra);
572
+ }
573
+ }
574
+ log(msg, extra) {
575
+ if (getLevel(this.level) >= getLevel("log")) {
576
+ this.output("green")(msg, extra);
577
+ }
578
+ }
579
+ warn(msg, extra) {
580
+ if (getLevel(this.level) >= getLevel("warn")) {
581
+ this.output("orange")(msg, extra);
582
+ }
583
+ }
584
+ error(msg, extra) {
585
+ const message = msg?.message ?? msg;
586
+ if (getLevel(this.level) >= getLevel("error")) {
587
+ this.output("red")(message, extra);
588
+ }
589
+ const { errCode, ...rest } = extra;
590
+ const errorCode = errCode ?? ErrorCode.UNKNOWN_ERROR;
591
+ this.callKit.trigger(KitEvent.KIT_ERROR, {
592
+ code: errorCode,
593
+ msg: message,
594
+ data: rest
595
+ });
596
+ this.callKit.reset();
597
+ const error = new Error(message);
598
+ error.name = "CallKitError";
599
+ error.code = errorCode;
600
+ error.data = rest;
601
+ throw error;
602
+ }
603
+ output(color) {
604
+ return (msg, extra = {}) => {
605
+ const now = /* @__PURE__ */ new Date();
606
+ if (Object.keys(extra).length > 0) {
607
+ console.log(
608
+ `%c[${this.prefix}] %c ${msg} %o %c ${now.toLocaleString()}`,
609
+ `color: ${color};`,
610
+ "color:block;",
611
+ extra,
612
+ "color:#999;"
613
+ );
614
+ } else {
615
+ console.log(
616
+ `%c[${this.prefix}] %c ${msg} %c ${now.toLocaleString()}`,
617
+ `color: ${color};`,
618
+ "color:block;",
619
+ "color:#999;"
620
+ );
621
+ }
622
+ };
623
+ }
624
+ };
625
+
626
+ // package/connect.ts
627
+ import {
628
+ UserAgent,
629
+ Web,
630
+ Registerer,
631
+ SessionState
632
+ } from "sip.js";
633
+ function convertObjectStringToJSON(input) {
634
+ const corrected = input.replace(/(\w+):\s*'(.*?)'/g, '"$1": "$2"').replace(/'/g, '"');
635
+ return corrected;
636
+ }
637
+ var closeStream = (stream) => {
638
+ if (stream)
639
+ stream.getTracks().forEach((track) => track.stop());
640
+ };
641
+ var initUserMedia = () => {
642
+ if (typeof navigator.mediaDevices === "undefined") {
643
+ navigator.mediaDevices = {};
644
+ }
645
+ if (typeof navigator.mediaDevices.getUserMedia === "undefined") {
646
+ navigator.mediaDevices.getUserMedia = (constraints) => {
647
+ const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
648
+ if (!getUserMedia) {
649
+ return Promise.reject(
650
+ new Error("Browser version is too low, please upgrade and try again.")
651
+ );
652
+ }
653
+ return new Promise((resolve, reject) => {
654
+ getUserMedia.call(navigator, constraints, resolve, reject);
655
+ });
656
+ };
657
+ }
658
+ };
659
+ var Connect = class {
660
+ callKit;
661
+ /**
662
+ * connectStatus: init registered connecting calling
663
+ */
664
+ connectStatus = CallStatus.init;
665
+ currentSession;
666
+ mediaStream;
667
+ userAgent;
668
+ registerer;
669
+ sipConnected = false;
670
+ /**
671
+ * Whether it's an outgoing call
672
+ */
673
+ isOutgoing = false;
674
+ /**
675
+ * Whether it's an active hangup
676
+ */
677
+ isUnprompted = false;
678
+ /**
679
+ * Whether registered
680
+ * @param
681
+ * @deprecated Deprecated, please use isRegistered method
682
+ */
683
+ get isRegister() {
684
+ return this.isRegistered();
685
+ }
686
+ /**
687
+ * Call hold
688
+ * @param isHold
689
+ */
690
+ isHold = false;
691
+ /**
692
+ * Whether muted
693
+ */
694
+ isMute = false;
695
+ constructor(callKit) {
696
+ this.callKit = callKit;
697
+ }
698
+ reset() {
699
+ if (this.connectStatus !== CallStatus.init) {
700
+ this.setConnectStatus(CallStatus.init);
701
+ }
702
+ if (this.isRegistered()) {
703
+ this.unregister();
704
+ }
705
+ this.currentSession = void 0;
706
+ this.mediaStream = void 0;
707
+ this.userAgent = void 0;
708
+ this.registerer = void 0;
709
+ this.isOutgoing = false;
710
+ this.isUnprompted = false;
711
+ this.sipConnected = false;
712
+ if (this.isHold) {
713
+ this.setHoldStatus(false);
714
+ }
715
+ }
716
+ getAduioReference() {
717
+ const { audioRef } = this.callKit.config.getConfig();
718
+ if (typeof audioRef === "function") {
719
+ return audioRef();
720
+ }
721
+ return audioRef;
722
+ }
723
+ async permission() {
724
+ this.callKit.logger.debug("permission");
725
+ initUserMedia();
726
+ const _stream = await navigator.mediaDevices.getUserMedia({ audio: true });
727
+ closeStream(_stream);
728
+ }
729
+ isRegistered() {
730
+ return [
731
+ CallStatus.registered,
732
+ CallStatus.connecting,
733
+ CallStatus.ringing,
734
+ CallStatus.calling,
735
+ CallStatus.holding
736
+ ].includes(this.connectStatus);
737
+ }
738
+ /**
739
+ * Making connection
740
+ * @returns
741
+ */
742
+ isConnecting() {
743
+ return this.connectStatus === CallStatus.connecting;
744
+ }
745
+ /**
746
+ * In call
747
+ * @returns
748
+ */
749
+ isCalling() {
750
+ return this.connectStatus === CallStatus.calling;
751
+ }
752
+ /**
753
+ * Ringing
754
+ */
755
+ isRinging() {
756
+ return this.connectStatus === CallStatus.ringing;
757
+ }
758
+ /**
759
+ *
760
+ */
761
+ isHolding() {
762
+ return this.connectStatus === CallStatus.holding;
763
+ }
764
+ /**
765
+ * Call ended, call not started
766
+ * @returns
767
+ */
768
+ isInit() {
769
+ return this.connectStatus === CallStatus.init;
770
+ }
771
+ async register() {
772
+ if (this.connectStatus !== CallStatus.init) {
773
+ if (this.connectStatus === CallStatus.registered) {
774
+ this.callKit.logger.warn("connectStatus is registered");
775
+ return;
776
+ }
777
+ this.callKit.logger.error("connectStatus is not init", {
778
+ errCode: ErrorCode.CONNECT_CALL_STATUS_ERROR
779
+ });
780
+ this.callKit.callCenter.callEnd();
781
+ return;
782
+ }
783
+ this.callKit.logger.debug("connect register");
784
+ await this.permission().catch((err) => {
785
+ this.callKit.logger.error(err, {
786
+ errCode: ErrorCode.WEBRTC_USER_MEDIA_ERROR
787
+ });
788
+ });
789
+ const { userInfo, constrains } = this.callKit.config.getConfig();
790
+ const localStreamFactory = async () => {
791
+ this.mediaStream = await navigator.mediaDevices.getUserMedia(constrains);
792
+ return this.mediaStream;
793
+ };
794
+ const {
795
+ userPart,
796
+ fsIp,
797
+ fsPort,
798
+ iceInfo,
799
+ wsUrl
800
+ } = userInfo;
801
+ const connectConfig = {
802
+ uri: UserAgent.makeURI(`sip:${userPart}@${fsIp}:${fsPort}`),
803
+ displayName: userPart,
804
+ transportOptions: {
805
+ wsServers: [wsUrl],
806
+ traceSip: true
807
+ },
808
+ logLevel: "error",
809
+ allowLegacyNotifications: true,
810
+ contactName: userPart,
811
+ sessionDescriptionHandlerFactory: Web.defaultSessionDescriptionHandlerFactory(localStreamFactory),
812
+ sessionDescriptionHandlerFactoryOptions: {
813
+ constraints: constrains,
814
+ iceGatheringTimeout: userInfo.iceGatheringTimeout,
815
+ peerConnectionConfiguration: {
816
+ iceServers: JSON.parse(convertObjectStringToJSON(iceInfo))
817
+ }
818
+ }
819
+ };
820
+ this.callKit.logger.debug("connect connectConfig", connectConfig);
821
+ this.userAgent = new UserAgent(connectConfig);
822
+ const remoteStream = new MediaStream();
823
+ const setupRemoteMedia = (session) => {
824
+ const audioRef = this.getAduioReference();
825
+ this.callKit.logger.debug("connect setupRemoteMedia", audioRef);
826
+ session.sessionDescriptionHandler.peerConnection.getReceivers().forEach((receiver) => {
827
+ if (receiver.track) {
828
+ remoteStream.addTrack(receiver.track);
829
+ }
830
+ });
831
+ if (audioRef) {
832
+ audioRef.srcObject = remoteStream;
833
+ audioRef.play().catch((error) => {
834
+ this.callKit.logger.error(error.message, {
835
+ errCode: ErrorCode.WEBRTC_AUDIO_PLAY_ERROR
836
+ });
837
+ });
838
+ } else {
839
+ this.callKit.logger.error("video is not exist", {
840
+ errCode: ErrorCode.WEBRTC_AUDIO_PLAYER_ERROR
841
+ });
842
+ }
843
+ };
844
+ const registererOptions = {};
845
+ this.registerer = new Registerer(this.userAgent, registererOptions);
846
+ this.userAgent.delegate = {
847
+ onInvite: (invite) => {
848
+ this.callKit.logger.debug("connect onInvite");
849
+ this.currentSession = invite;
850
+ this.currentSession.stateChange.addListener((state) => {
851
+ switch (state) {
852
+ case SessionState.Establishing:
853
+ this.callKit.logger.debug("connect Establishing");
854
+ break;
855
+ case SessionState.Established:
856
+ this.callKit.logger.debug("connect Established");
857
+ if (this.connectStatus === CallStatus.registered) {
858
+ this.callKit.logger.warn("call is already hangup");
859
+ return;
860
+ }
861
+ this.sipConnected = true;
862
+ setupRemoteMedia(this.currentSession);
863
+ break;
864
+ case SessionState.Terminating:
865
+ this.callKit.logger.debug("connect Terminating");
866
+ break;
867
+ case SessionState.Terminated:
868
+ this.callKit.logger.debug("connect Terminated");
869
+ if (!this.isUnprompted) {
870
+ this.callKit.callCenter.callEnd();
871
+ }
872
+ this.isUnprompted = false;
873
+ break;
874
+ default:
875
+ break;
876
+ }
877
+ });
878
+ const options = {
879
+ sessionDescriptionHandlerOptions: {
880
+ constraints: constrains,
881
+ alwaysAcquireMediaFirst: true
882
+ }
883
+ };
884
+ if (this.isOutgoing) {
885
+ this.currentSession.accept(options);
886
+ } else {
887
+ this.callKit.trigger(KitEvent.KIT_INVITE, {
888
+ accept: () => {
889
+ this.setConnectStatus(CallStatus.connecting);
890
+ this.callKit.trigger(KitEvent.CALL_CONNECTING, /* @__PURE__ */ new Date());
891
+ this.currentSession.accept(options);
892
+ },
893
+ reject: () => {
894
+ this.currentSession.reject();
895
+ this.callKit.callCenter.callEnd(true, false);
896
+ },
897
+ getInviteData: () => {
898
+ const { request: request2 } = this.currentSession;
899
+ const headerNames = Object.keys(request2.headers);
900
+ const xHeaders = {};
901
+ headerNames.filter((name) => name.toLocaleLowerCase().startsWith("x-antaios")).forEach((name) => {
902
+ xHeaders[name.toLocaleLowerCase()] = request2.getHeader(name);
903
+ });
904
+ this.callKit.logger.debug("get invite data", xHeaders);
905
+ return xHeaders;
906
+ }
907
+ });
908
+ }
909
+ },
910
+ onConnect: async () => {
911
+ this.callKit.logger.debug("connect onConnect");
912
+ await this.registerer.register().then(() => {
913
+ this.callKit.socket.send(SocketSendEvent.START);
914
+ }).catch((err) => {
915
+ this.callKit.logger.error(err?.message, {
916
+ errCode: ErrorCode.WEBRTC_REGISTER_ERROR
917
+ });
918
+ });
919
+ },
920
+ onDisconnect: () => {
921
+ this.callKit.logger.debug("connect onDisconnect");
922
+ this.callKit.callCenter.callEnd();
923
+ },
924
+ onRegister: () => {
925
+ this.callKit.logger.debug("connect onRegister");
926
+ }
927
+ };
928
+ await this.userAgent.start().catch((err) => {
929
+ this.callKit.callCenter.callEnd(false, true);
930
+ this.callKit.logger.error(err, {
931
+ errCode: ErrorCode.WEBRTC_USER_AGENT_ERROR
932
+ });
933
+ });
934
+ }
935
+ async unregister() {
936
+ this.callKit.logger.debug("connect unregister");
937
+ if (!this.isRegistered() || !this.registerer) {
938
+ this.callKit.logger.warn("No registerer to unregister.");
939
+ return;
940
+ }
941
+ await this.registerer.unregister({ all: true }).then(() => {
942
+ this.setConnectStatus(CallStatus.init);
943
+ }).catch((err) => {
944
+ this.callKit.logger.error(err, {
945
+ errCode: ErrorCode.WEBRTC_CANCEL_REGISTER_ERROR
946
+ });
947
+ });
948
+ }
949
+ async call(callback) {
950
+ this.callKit.logger.debug("connect call");
951
+ this.isOutgoing = true;
952
+ if (!this.isRegistered()) {
953
+ await this.register();
954
+ }
955
+ this.setConnectStatus(CallStatus.connecting);
956
+ this.callKit.trigger(KitEvent.CALL_CONNECTING, /* @__PURE__ */ new Date());
957
+ const { userInfo } = this.callKit.config.getConfig();
958
+ callback(userInfo);
959
+ }
960
+ /**
961
+ * Update registration status
962
+ * @param register
963
+ */
964
+ setRegister(register) {
965
+ this.callKit.logger.debug(`connect setRegister:${register}`);
966
+ this.callKit.trigger(KitEvent.KIT_REGISTER_CHANGE, register);
967
+ }
968
+ /**
969
+ * Set call status
970
+ * @param status
971
+ */
972
+ setConnectStatus(status) {
973
+ this.callKit.logger.debug(`connect setConnectStatus: ${status}`);
974
+ if (this.isRegistered() && status === CallStatus.init) {
975
+ this.setRegister(false);
976
+ }
977
+ if (!this.isRegistered() && status !== CallStatus.init) {
978
+ this.setRegister(true);
979
+ }
980
+ this.connectStatus = status;
981
+ this.callKit.trigger(KitEvent.KIT_CALL_STATUS_CHANGE, status);
982
+ }
983
+ /**
984
+ * Hang up
985
+ * @param isUnprompted Whether actively hanging up
986
+ * @param isError Whether an error occurred
987
+ * @returns
988
+ */
989
+ async hangup(isUnprompted = false, isError = false) {
990
+ this.callKit.logger.debug(`connect hangup isError: ${isError}`);
991
+ if (this.connectStatus === CallStatus.init || this.connectStatus === CallStatus.registered)
992
+ return;
993
+ this.isOutgoing = false;
994
+ this.isUnprompted = isUnprompted;
995
+ try {
996
+ if (isUnprompted) {
997
+ this.callKit.socket.send(SocketSendEvent.AGENT_HANGUP);
998
+ const shouldSendBye = this.sipConnected || this.isRinging() || this.isCalling() || this.isHolding();
999
+ if (shouldSendBye) {
1000
+ await this.currentSession?.bye();
1001
+ }
1002
+ }
1003
+ this.sipConnected = false;
1004
+ closeStream(this.mediaStream);
1005
+ const audioRef = this.getAduioReference();
1006
+ if (audioRef) {
1007
+ audioRef.pause();
1008
+ audioRef.srcObject = null;
1009
+ }
1010
+ if (isError) {
1011
+ this.setConnectStatus(CallStatus.init);
1012
+ } else {
1013
+ this.setConnectStatus(CallStatus.registered);
1014
+ }
1015
+ this.callKit.trigger(KitEvent.CALL_END, /* @__PURE__ */ new Date());
1016
+ } catch (err) {
1017
+ this.callKit.trigger(KitEvent.CALL_END, /* @__PURE__ */ new Date());
1018
+ }
1019
+ }
1020
+ /**
1021
+ * The remote media stream. Undefined if call not answered.
1022
+ * @param session - Session to get the media stream from.
1023
+ */
1024
+ getRemoteMediaStream(session) {
1025
+ this.callKit.logger.debug("connect getRemoteMediaStream");
1026
+ const sdh = session.sessionDescriptionHandler;
1027
+ if (!sdh) {
1028
+ return void 0;
1029
+ }
1030
+ return sdh.remoteMediaStream;
1031
+ }
1032
+ setupRemoteMedia(session) {
1033
+ this.callKit.logger.debug("connect setupRemoteMedia");
1034
+ const remoteStream = this.getRemoteMediaStream(session);
1035
+ const audioRef = this.getAduioReference();
1036
+ if (audioRef) {
1037
+ audioRef.autoplay = true;
1038
+ audioRef.srcObject = remoteStream;
1039
+ audioRef.play().catch((error) => {
1040
+ this.callKit.logger.error(error.message, {
1041
+ errCode: ErrorCode.WEBRTC_AUDIO_PLAY_ERROR
1042
+ });
1043
+ });
1044
+ remoteStream.onaddtrack = () => {
1045
+ this.callKit.logger.debug("Remote media onaddtrack");
1046
+ audioRef.load();
1047
+ audioRef.play().catch((error) => {
1048
+ this.callKit.logger.error(error.message, {
1049
+ errCode: ErrorCode.WEBRTC_AUDIO_PLAY_ERROR
1050
+ });
1051
+ });
1052
+ };
1053
+ } else {
1054
+ this.callKit.logger.error("video is not exist", {
1055
+ errCode: ErrorCode.WEBRTC_AUDIO_PLAYER_ERROR
1056
+ });
1057
+ }
1058
+ }
1059
+ /**
1060
+ * Update hold
1061
+ * @param hold
1062
+ */
1063
+ setHoldStatus(hold) {
1064
+ this.callKit.logger.debug("connect setHold", hold);
1065
+ this.isHold = hold;
1066
+ this.callKit.trigger(KitEvent.KIT_SET_HOLD, hold);
1067
+ }
1068
+ async setHold(hold) {
1069
+ this.setHoldStatus(hold);
1070
+ }
1071
+ async hold() {
1072
+ this.callKit.logger.debug("connect hold");
1073
+ if (this.connectStatus !== CallStatus.calling || !this.currentSession) {
1074
+ this.callKit.logger.error("Current status is not in call", {
1075
+ errCode: ErrorCode.WEBRTC_HOLE_STATUS_ERROR
1076
+ });
1077
+ return;
1078
+ }
1079
+ await this.setHold(true);
1080
+ }
1081
+ async unhold() {
1082
+ this.callKit.logger.debug("connect unhold");
1083
+ await this.setHold(false);
1084
+ }
1085
+ async setMute(mute) {
1086
+ this.callKit.logger.debug("connect setMute", mute);
1087
+ if (!this.currentSession) {
1088
+ this.callKit.logger.error("No active session", {
1089
+ errCode: ErrorCode.WEBRTC_MUTE_STATUS_ERROR
1090
+ });
1091
+ return;
1092
+ }
1093
+ try {
1094
+ const sdh = this.currentSession.sessionDescriptionHandler;
1095
+ if (!sdh || !("peerConnection" in sdh)) {
1096
+ throw new Error("Invalid session description handler");
1097
+ }
1098
+ const pc = sdh.peerConnection;
1099
+ const audioSender = pc.getSenders().find((sender) => sender.track?.kind === "audio");
1100
+ if (audioSender && audioSender.track) {
1101
+ audioSender.track.enabled = !mute;
1102
+ this.isMute = mute;
1103
+ this.callKit.trigger(KitEvent.KIT_SET_MUTE, mute);
1104
+ } else {
1105
+ throw new Error("No audio track found");
1106
+ }
1107
+ } catch (error) {
1108
+ this.callKit.logger.error("Failed to set mute state", {
1109
+ errCode: ErrorCode.WEBRTC_MUTE_ERROR,
1110
+ error
1111
+ });
1112
+ }
1113
+ }
1114
+ async mute() {
1115
+ this.callKit.logger.debug("connect mute");
1116
+ if (this.connectStatus !== CallStatus.calling || !this.currentSession) {
1117
+ this.callKit.logger.error("Current status is not in call", {
1118
+ errCode: ErrorCode.WEBRTC_MUTE_STATUS_ERROR
1119
+ });
1120
+ return;
1121
+ }
1122
+ await this.setMute(true);
1123
+ }
1124
+ async unmute() {
1125
+ this.callKit.logger.debug("connect unmute");
1126
+ if (this.connectStatus !== CallStatus.calling || !this.currentSession) {
1127
+ this.callKit.logger.error("Current status is not in call", {
1128
+ errCode: ErrorCode.WEBRTC_MUTE_STATUS_ERROR
1129
+ });
1130
+ return;
1131
+ }
1132
+ await this.setMute(false);
1133
+ }
1134
+ };
1135
+
1136
+ // package/socket.ts
1137
+ var RECONNECT_CONFIG = {
1138
+ enabled: true,
1139
+ maxAttempts: 1,
1140
+ delay: 1e3,
1141
+ backoffMultiplier: 1.5,
1142
+ pingInterval: 3e4,
1143
+ pingTimeout: 5e3
1144
+ };
1145
+ var Socket = class {
1146
+ callKit;
1147
+ ws;
1148
+ socketConfig;
1149
+ lastPingTime = void 0;
1150
+ isConnected = false;
1151
+ pingTimer;
1152
+ // Whether received server hangup confirmation
1153
+ recivedClose = false;
1154
+ // Whether received start confirmation
1155
+ satrtConfirm = false;
1156
+ reconnectTimer;
1157
+ isReconnecting = false;
1158
+ reconnectAttempts = 0;
1159
+ constructor(callKit) {
1160
+ this.callKit = callKit;
1161
+ const { reconnect } = this.callKit.config.getConfig();
1162
+ this.socketConfig = {
1163
+ ...RECONNECT_CONFIG,
1164
+ ...reconnect
1165
+ };
1166
+ }
1167
+ init() {
1168
+ const { socket } = this.callKit.config.getConfig();
1169
+ this.callKit.logger.debug(`socket init: ${socket}`);
1170
+ this.connect(socket);
1171
+ }
1172
+ connect(socketUrl) {
1173
+ this.ws = new WebSocket(socketUrl);
1174
+ this.ws.onopen = (ev) => this.onOpen(ev);
1175
+ this.ws.onclose = (ev) => this.onClose(ev);
1176
+ this.ws.onerror = (ev) => this.onError(ev);
1177
+ this.ws.onmessage = (ev) => this.onMessage(ev);
1178
+ }
1179
+ onOpen(ev) {
1180
+ this.callKit.logger.debug("socket onOpen", ev);
1181
+ this.isConnected = true;
1182
+ this.lastPingTime = Date.now();
1183
+ this.checkPing();
1184
+ if (this.reconnectTimer) {
1185
+ clearTimeout(this.reconnectTimer);
1186
+ }
1187
+ this.reconnectAttempts = 0;
1188
+ }
1189
+ onClose(ev) {
1190
+ this.callKit.logger.debug("socket onClose", ev);
1191
+ this.isConnected = false;
1192
+ this.satrtConfirm = false;
1193
+ if (this.pingTimer) {
1194
+ clearInterval(this.pingTimer);
1195
+ this.pingTimer = void 0;
1196
+ }
1197
+ this.callKit.connect.hangup();
1198
+ this.reset();
1199
+ }
1200
+ onError(ev) {
1201
+ this.callKit.logger.error("socket onError", {
1202
+ errCode: ErrorCode.SOCKET_CONNECT_ERROR,
1203
+ data: ev
1204
+ });
1205
+ this.isConnected = false;
1206
+ if (!this.isReconnecting && this.socketConfig.maxAttempts > 0) {
1207
+ this.attemptReconnect();
1208
+ } else if (this.reconnectAttempts >= this.socketConfig.maxAttempts) {
1209
+ this.reset();
1210
+ this.callKit.logger.error(
1211
+ "Reconnection failed, maximum retry attempts reached",
1212
+ {
1213
+ errCode: ErrorCode.SOCKET_RECONNECT_FAILED
1214
+ }
1215
+ );
1216
+ }
1217
+ }
1218
+ onMessage(ev) {
1219
+ const data = JSON.parse(ev.data);
1220
+ this.callKit.logger.debug("socket onMessage", data);
1221
+ if (data.event === SocketReceiveEvent.PONG) {
1222
+ this.lastPingTime = Date.now();
1223
+ return;
1224
+ }
1225
+ if (data.event === SocketReceiveEvent.START_CONFIRM) {
1226
+ this.callKit.logger.debug("register success");
1227
+ this.satrtConfirm = true;
1228
+ this.callKit.connect.setConnectStatus(CallStatus.registered);
1229
+ }
1230
+ if (data.event === SocketReceiveEvent.CALL_SUCCESS) {
1231
+ this.recivedClose = false;
1232
+ this.callKit.logger.debug("callStart");
1233
+ this.callKit.user.setUserStatus(UserStatus.busy);
1234
+ }
1235
+ if (data.event === SocketReceiveEvent.CALL_FAILED) {
1236
+ this.callKit.logger.debug(data.msg, {
1237
+ errCode: ErrorCode.SOCKET_CALL_ERROR
1238
+ });
1239
+ this.callKit.user.setUserStatus(UserStatus.online);
1240
+ }
1241
+ if (data.event === SocketReceiveEvent.CUSTOMER_RINGING) {
1242
+ this.callKit.logger.debug(data.msg);
1243
+ if (this.callKit.connect.isConnecting()) {
1244
+ this.callKit.trigger(KitEvent.CALL_RINGING, /* @__PURE__ */ new Date());
1245
+ this.callKit.connect.setConnectStatus(CallStatus.ringing);
1246
+ }
1247
+ }
1248
+ if (data.event === SocketReceiveEvent.CUSTOMER_PICK_UP) {
1249
+ this.callKit.logger.debug(data.msg);
1250
+ if (this.callKit.connect.isRinging()) {
1251
+ this.callKit.connect.setConnectStatus(CallStatus.calling);
1252
+ this.callKit.trigger(KitEvent.CALL_PICK_UP, /* @__PURE__ */ new Date());
1253
+ }
1254
+ }
1255
+ if (data.event === SocketReceiveEvent.AGENT_PICK_UP) {
1256
+ this.callKit.logger.debug(data.msg);
1257
+ this.callKit.trigger(KitEvent.AGENT_PICK_UP, /* @__PURE__ */ new Date());
1258
+ }
1259
+ if (data.event === SocketReceiveEvent.CUSTOMER_HANG_UP) {
1260
+ this.callKit.logger.debug(data.msg);
1261
+ this.callKit.trigger(KitEvent.CALL_HANG_UP, /* @__PURE__ */ new Date());
1262
+ this.callKit.user.setUserStatus(UserStatus.catNap);
1263
+ }
1264
+ if (data.event === SocketReceiveEvent.CUSTOMER_NO_ANSWER) {
1265
+ this.callKit.logger.debug(data.msg);
1266
+ this.callKit.trigger(KitEvent.CALL_NO_ANSWER);
1267
+ this.callKit.user.setUserStatus(UserStatus.catNap);
1268
+ }
1269
+ if (data.event === SocketReceiveEvent.CALL_CDR) {
1270
+ this.callKit.logger.debug(data.msg);
1271
+ this.callKit.trigger(KitEvent.CALL_CDR, data.data);
1272
+ }
1273
+ if (data.event === SocketReceiveEvent.STOP_CONFIRM) {
1274
+ this.callKit.logger.debug(data.msg);
1275
+ this.recivedClose = true;
1276
+ }
1277
+ if (data.event === SocketReceiveEvent.CLOSE) {
1278
+ const { userInfo } = this.callKit.config.getConfig();
1279
+ this.callKit.logger.debug(data.msg);
1280
+ this.send(SocketSendEvent.END, {
1281
+ agentId: userInfo.agentId
1282
+ });
1283
+ }
1284
+ if (data.event === SocketReceiveEvent.ERROR) {
1285
+ this.callKit.logger.error(data.msg, {
1286
+ errCode: ErrorCode.SOKET_SERVER_ERROR,
1287
+ data
1288
+ });
1289
+ this.callKit.user.setUserStatus(UserStatus.catNap);
1290
+ }
1291
+ if (data.event === SocketReceiveEvent.AGENT_NO_ANSWER) {
1292
+ this.callKit.user.setUserStatus(UserStatus.catNap);
1293
+ }
1294
+ this.callKit.trigger(KitEvent.SERVER_SOCKET_EVENT, data);
1295
+ }
1296
+ send(event, message) {
1297
+ if (!this.isConnected) {
1298
+ this.callKit.logger.error("socket not connected", {
1299
+ errCode: ErrorCode.SOCKET_CONNECT_ERROR
1300
+ });
1301
+ this.callKit.config.reset();
1302
+ this.callKit.reset();
1303
+ return;
1304
+ }
1305
+ const { userInfo } = this.callKit.config.getConfig();
1306
+ const { sessionId, extno, agentId } = userInfo;
1307
+ if (!sessionId) {
1308
+ this.callKit.logger.error("sessionId is empty", {
1309
+ errCode: ErrorCode.SOCKET_CONNECT_ERROR
1310
+ });
1311
+ return;
1312
+ }
1313
+ const msg = {
1314
+ event,
1315
+ sessionId,
1316
+ ...message
1317
+ };
1318
+ if (SocketSendEvent.CALL === event) {
1319
+ msg.phoneNum = extno;
1320
+ msg.agentId = agentId;
1321
+ }
1322
+ this.callKit.logger.debug("socket send", msg);
1323
+ switch (event) {
1324
+ case SocketSendEvent.PING:
1325
+ this.lastPingTime = Date.now();
1326
+ this.ws?.send(JSON.stringify({ event, ...msg }));
1327
+ break;
1328
+ default:
1329
+ this.ws?.send(JSON.stringify({ event, ...msg }));
1330
+ break;
1331
+ }
1332
+ }
1333
+ /**
1334
+ * Attempt to close socket, need to wait for server confirmation
1335
+ * @returns
1336
+ */
1337
+ async requestClose() {
1338
+ return new Promise((resolve) => {
1339
+ let remainingTime = 5;
1340
+ const interval = setInterval(() => {
1341
+ remainingTime -= 1;
1342
+ if (this.recivedClose || remainingTime <= 0) {
1343
+ clearInterval(interval);
1344
+ resolve(this.recivedClose);
1345
+ }
1346
+ }, 1e3);
1347
+ });
1348
+ }
1349
+ ping() {
1350
+ if (!this.isConnected)
1351
+ return;
1352
+ this.send(SocketSendEvent.PING);
1353
+ const now = Date.now();
1354
+ this.callKit.logger.debug(`socket ping: ${now - this.lastPingTime}ms`);
1355
+ const { pingInterval, pingTimeout } = this.socketConfig;
1356
+ if (now - this.lastPingTime > pingInterval + pingTimeout) {
1357
+ this.callKit.logger.error("socket ping timeout", {
1358
+ errCode: ErrorCode.SOCKET_PING_TIMEOUT
1359
+ });
1360
+ if (this.ws && this.isConnected) {
1361
+ this.ws.close(4001, "ping timeout");
1362
+ } else {
1363
+ this.reset();
1364
+ }
1365
+ }
1366
+ }
1367
+ checkPing() {
1368
+ if (this.pingTimer) {
1369
+ clearInterval(this.pingTimer);
1370
+ }
1371
+ this.ping();
1372
+ this.pingTimer = setInterval(() => {
1373
+ this.ping();
1374
+ }, this.socketConfig.pingInterval);
1375
+ }
1376
+ /**
1377
+ *
1378
+ * @param isWaitConfirm Whether need to wait for close confirmation
1379
+ */
1380
+ async reset(isWaitConfirm = true) {
1381
+ if (this.pingTimer) {
1382
+ clearInterval(this.pingTimer);
1383
+ }
1384
+ if (this.reconnectTimer) {
1385
+ clearTimeout(this.reconnectTimer);
1386
+ }
1387
+ this.lastPingTime = void 0;
1388
+ this.pingTimer = void 0;
1389
+ this.satrtConfirm = false;
1390
+ this.isReconnecting = false;
1391
+ this.reconnectAttempts = 0;
1392
+ if (this.ws && this.isConnected) {
1393
+ if (isWaitConfirm) {
1394
+ await this.requestClose();
1395
+ }
1396
+ this.recivedClose = false;
1397
+ this.callKit.logger.debug("socket destroy");
1398
+ this.ws.close();
1399
+ this.isConnected = false;
1400
+ }
1401
+ }
1402
+ attemptReconnect() {
1403
+ if (this.isReconnecting) {
1404
+ return;
1405
+ }
1406
+ this.isReconnecting = true;
1407
+ const delay = this.socketConfig.delay * this.socketConfig.backoffMultiplier ** this.reconnectAttempts;
1408
+ this.callKit.logger.debug(
1409
+ `Preparing reconnection attempt ${this.reconnectAttempts + 1}, delay: ${delay}ms`
1410
+ );
1411
+ this.reconnectTimer = setTimeout(() => {
1412
+ const { socket } = this.callKit.config.getConfig();
1413
+ this.connect(socket);
1414
+ this.reconnectAttempts += 1;
1415
+ this.isReconnecting = false;
1416
+ }, delay);
1417
+ }
1418
+ };
1419
+
1420
+ // package/user.ts
1421
+ var User = class {
1422
+ callKit;
1423
+ userStatus;
1424
+ // logout(1, "unregistered"), idle(2, "free"), break(3, "nap"), busy(4, "busy")
1425
+ constructor(callKit) {
1426
+ this.callKit = callKit;
1427
+ }
1428
+ /**
1429
+ *
1430
+ * @param status logout(1, "unregistered"), idle(2, "free"), break(3, "nap"), busy(4, "busy")
1431
+ */
1432
+ async setUserStatus(status) {
1433
+ const { isAutoUpdateUserStatus } = this.callKit.config.getConfig();
1434
+ if (status === this.userStatus || !isAutoUpdateUserStatus)
1435
+ return;
1436
+ return this.updateUserStatus(status);
1437
+ }
1438
+ /**
1439
+ * Update user status
1440
+ * @param status
1441
+ */
1442
+ async updateUserStatus(status) {
1443
+ const { agentId } = this.callKit.config.getConfig().userInfo;
1444
+ if (!this.callKit.config.isLogin()) {
1445
+ this.userStatus = UserStatus.offline;
1446
+ this.callKit.trigger(KitEvent.KIT_USER_STATUS_CHANGE, UserStatus.offline);
1447
+ return;
1448
+ }
1449
+ await this.callKit.api.setUserStatus({
1450
+ agentId,
1451
+ userStatus: status
1452
+ }).then(() => {
1453
+ this.userStatus = status;
1454
+ this.callKit.trigger(KitEvent.KIT_USER_STATUS_CHANGE, status);
1455
+ }).catch((err) => {
1456
+ this.callKit.logger.error(err, {
1457
+ errCode: ErrorCode.API_USER_STATUS_UPDATE_ERROR
1458
+ });
1459
+ });
1460
+ }
1461
+ };
1462
+
1463
+ // package/index.ts
1464
+ var CallKit = class {
1465
+ api;
1466
+ config;
1467
+ logger;
1468
+ callCenter;
1469
+ connect;
1470
+ socket;
1471
+ user;
1472
+ listener = [];
1473
+ constructor(options) {
1474
+ this.config = new Config(this);
1475
+ this.config.setConfig("log", options.log);
1476
+ this.config.setConfig("audioRef", options.audioRef);
1477
+ this.config.setConfig("host", options.host);
1478
+ this.config.setConfig(
1479
+ "constrains",
1480
+ options.constrains || constrainsDefault
1481
+ );
1482
+ this.config.setConfig("socket", options.socket);
1483
+ this.config.setConfig(
1484
+ "isAutoUpdateUserStatus",
1485
+ options.isAutoUpdateUserStatus === void 0 ? isAutoUpdateUserStatusDefault : options.isAutoUpdateUserStatus
1486
+ );
1487
+ this.logger = new Logger(this, options.log);
1488
+ this.logger.debug("callKit init", options);
1489
+ this.api = new Api(this);
1490
+ this.connect = new Connect(this);
1491
+ this.callCenter = new Call(this);
1492
+ this.socket = new Socket(this);
1493
+ this.user = new User(this);
1494
+ }
1495
+ async login(username, password, extra = {}) {
1496
+ if (this.config.isLogin()) {
1497
+ this.logger.warn("already login");
1498
+ return;
1499
+ }
1500
+ this.logger.debug("login info:", {
1501
+ username,
1502
+ password
1503
+ });
1504
+ const user = await this.api.login({
1505
+ userName: username,
1506
+ password: md5(username + md5(password))
1507
+ }).catch((err) => {
1508
+ this.logger.error(err, { errCode: ErrorCode.API_USER_LOGIN_ERROR });
1509
+ });
1510
+ if (user) {
1511
+ this.config.setConfig("userInfo", {
1512
+ wsUrl: `wss://${user.wsUrl}`,
1513
+ sessionId: user.sessionId,
1514
+ username,
1515
+ password,
1516
+ agentId: user.agentId,
1517
+ fsUserId: user.fsUserId,
1518
+ userPart: user.userPart,
1519
+ fsPassword: user.fsPassword,
1520
+ fsIp: user.fsIp,
1521
+ fsPort: user.fsPort,
1522
+ iceInfo: user.iceInfo,
1523
+ iceGatheringTimeout: user.iceGatheringTimeout,
1524
+ ...extra
1525
+ });
1526
+ this.socket.init();
1527
+ this.trigger(KitEvent.KIT_LOGIN_CHANGE, true);
1528
+ this.user.setUserStatus(UserStatus.offline);
1529
+ }
1530
+ }
1531
+ async logout() {
1532
+ if (!this.config.check())
1533
+ return;
1534
+ this.logger.debug("logout");
1535
+ await this.user.setUserStatus(UserStatus.offline);
1536
+ if (this.config.isLogin()) {
1537
+ const { sessionId } = this.config.getConfig().userInfo;
1538
+ this.api.loginOut({ sessionId }).catch((err) => {
1539
+ this.logger.error(err, { errCode: ErrorCode.API_USER_LOGOUT_ERROR });
1540
+ });
1541
+ }
1542
+ await this.hangup();
1543
+ this.socket.reset(false);
1544
+ this.connect.reset();
1545
+ this.config.reset();
1546
+ this.trigger(KitEvent.KIT_LOGIN_CHANGE, false);
1547
+ }
1548
+ async call(extno) {
1549
+ if (!this.config.check())
1550
+ return;
1551
+ if (!this.connect.isRegistered()) {
1552
+ this.logger.warn("Currently not registered");
1553
+ return;
1554
+ }
1555
+ if (extno) {
1556
+ this.config.setUserInfo("extno", extno);
1557
+ }
1558
+ this.logger.debug("call");
1559
+ this.callCenter.callStart();
1560
+ }
1561
+ async register() {
1562
+ if (!this.config.check())
1563
+ return;
1564
+ this.logger.debug("register");
1565
+ this.connect.register();
1566
+ await this.user.setUserStatus(UserStatus.online);
1567
+ }
1568
+ async unregister() {
1569
+ if (!this.config.check())
1570
+ return;
1571
+ this.logger.debug("unregister");
1572
+ this.connect.unregister();
1573
+ await this.user.setUserStatus(UserStatus.offline);
1574
+ }
1575
+ async hangup() {
1576
+ if (!this.config.check())
1577
+ return;
1578
+ this.logger.debug("hangup", this.connect.connectStatus);
1579
+ if (!this.connect.isConnecting() && !this.connect.isRinging() && !this.connect.isCalling() && !this.connect.isHolding()) {
1580
+ this.logger.warn("Currently not in a call");
1581
+ return;
1582
+ }
1583
+ await this.callCenter.callEnd(true);
1584
+ this.user.setUserStatus(UserStatus.catNap);
1585
+ }
1586
+ hold() {
1587
+ if (!this.config.check())
1588
+ return;
1589
+ this.logger.debug("hold");
1590
+ this.callCenter.callHold();
1591
+ }
1592
+ unhold() {
1593
+ if (!this.config.check())
1594
+ return;
1595
+ this.logger.debug("unhold");
1596
+ this.callCenter.callUnhold();
1597
+ }
1598
+ async setFree() {
1599
+ if (!this.config.check())
1600
+ return;
1601
+ this.logger.debug("setFree");
1602
+ await this.user.setUserStatus(UserStatus.online);
1603
+ }
1604
+ async setSleep() {
1605
+ if (!this.config.check())
1606
+ return;
1607
+ this.logger.debug("setSleep");
1608
+ await this.user.setUserStatus(UserStatus.catNap);
1609
+ }
1610
+ async setBusy() {
1611
+ if (!this.config.check())
1612
+ return;
1613
+ this.logger.debug("setBusy");
1614
+ await this.user.setUserStatus(UserStatus.busy);
1615
+ }
1616
+ async reset() {
1617
+ this.logger.debug("reset");
1618
+ this.connect.reset();
1619
+ if (this.config.isLogin()) {
1620
+ await this.user.setUserStatus(UserStatus.online);
1621
+ } else {
1622
+ await this.user.setUserStatus(UserStatus.offline);
1623
+ }
1624
+ }
1625
+ on(event, callback) {
1626
+ this.logger.debug(`on ${event}`);
1627
+ this.listener.push({
1628
+ event,
1629
+ callback
1630
+ });
1631
+ }
1632
+ off(event, callback) {
1633
+ this.logger.debug(`off ${event}`);
1634
+ if (callback) {
1635
+ this.listener = this.listener.filter(
1636
+ (item) => !(item.event === event && item.callback === callback)
1637
+ );
1638
+ } else {
1639
+ this.listener = this.listener.filter((item) => item.event !== event);
1640
+ }
1641
+ }
1642
+ removeAllListeners() {
1643
+ this.listener = this.listener.splice(0, this.listener.length);
1644
+ this.logger.debug("removeAllListeners");
1645
+ }
1646
+ trigger(event, data) {
1647
+ this.logger.debug(`trigger ${event}`, { data });
1648
+ this.listener.forEach((item) => {
1649
+ if (item.event === event) {
1650
+ try {
1651
+ item.callback(data);
1652
+ } catch (err) {
1653
+ this.logger.error("Event callback error:", err);
1654
+ }
1655
+ }
1656
+ });
1657
+ }
1658
+ };
1659
+ export {
1660
+ CallKit
1661
+ };
1662
+ //# sourceMappingURL=index.mjs.map