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