@telegenta/webclient 3.0.0 → 3.0.1
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/demos/event_client.html +225 -210
- package/demos/phone_client.html +539 -519
- package/dist/telegenta.js +326 -290
- package/dist/telegenta.min.js +1 -1
- package/docs/EventClient.html +1 -1
- package/docs/EventServiceNotConnected.html +3 -0
- package/docs/InvalidMediaDevice.html +1 -1
- package/docs/InvalidOptions.html +1 -1
- package/docs/InvalidParameter.html +1 -1
- package/docs/LogHandler.html +2 -2
- package/docs/MediaNotPrepared.html +1 -1
- package/docs/NoActiveSession.html +1 -1
- package/docs/NotAllowed.html +1 -1
- package/docs/NotReady.html +1 -1
- package/docs/Phone.html +2 -2
- package/docs/Request.html +1 -1
- package/docs/Requestor.html +1 -1
- package/docs/Session.html +1 -1
- package/docs/UnableToConnect.html +1 -1
- package/docs/data/search.json +1 -1
- package/docs/event-client.js.html +44 -38
- package/docs/exceptions.js.html +12 -2
- package/docs/global.html +3 -3
- package/docs/index.html +1 -1
- package/docs/logger.js.html +2 -2
- package/docs/phone-client.js.html +284 -264
- package/docs/requestor.js.html +3 -3
- package/package.json +45 -45
package/dist/telegenta.js
CHANGED
|
@@ -197,6 +197,16 @@ class NotAllowed extends Error {
|
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Exception raised if event service is not connected
|
|
202
|
+
*/
|
|
203
|
+
class EventServiceNotConnected extends Error {
|
|
204
|
+
constructor() {
|
|
205
|
+
super();
|
|
206
|
+
this.name = 'EventServiceNotConnected';
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
200
210
|
/*
|
|
201
211
|
* TOOLBOX
|
|
202
212
|
*/
|
|
@@ -444,7 +454,7 @@ class Requestor extends EventEmitter {
|
|
|
444
454
|
resolve: resolve,
|
|
445
455
|
reject: reject,
|
|
446
456
|
timeoutObj: setTimeout(function () {
|
|
447
|
-
self.log.
|
|
457
|
+
self.log.info(`Timeout on request ${r.id}, rejecting!`);
|
|
448
458
|
self.removePendingRequest(r.id);
|
|
449
459
|
reject("Request timed out");
|
|
450
460
|
}, REQUEST_TIMEOUT),
|
|
@@ -512,7 +522,7 @@ class Requestor extends EventEmitter {
|
|
|
512
522
|
// Keep alive in seconds
|
|
513
523
|
const PING_INTERVAL = 50;
|
|
514
524
|
|
|
515
|
-
const ES_DEFAULT_SERVER_URL =
|
|
525
|
+
const ES_DEFAULT_SERVER_URL = 'wss://api.telegenta.com/es/';
|
|
516
526
|
|
|
517
527
|
// Starting retry interval
|
|
518
528
|
const RETRY_INTERVAL_START = 1 + Math.random();
|
|
@@ -550,7 +560,7 @@ class EventClient {
|
|
|
550
560
|
this.log = logger ? logger : new LogHandler(LOGLVL.ERROR);
|
|
551
561
|
this._requestor = new Requestor(this.log);
|
|
552
562
|
|
|
553
|
-
this._requestor.on(
|
|
563
|
+
this._requestor.on('event', (request) => {
|
|
554
564
|
this._handleIncomingEvent(request);
|
|
555
565
|
});
|
|
556
566
|
}
|
|
@@ -573,11 +583,11 @@ class EventClient {
|
|
|
573
583
|
|
|
574
584
|
/**
|
|
575
585
|
* Disconnect from event service and clear all active subscriptions
|
|
576
|
-
* @throws
|
|
586
|
+
* @throws EventServiceNotConnected if not connected
|
|
577
587
|
*/
|
|
578
588
|
disconnect() {
|
|
579
589
|
if (this._socket.readyState !== 1) {
|
|
580
|
-
throw new
|
|
590
|
+
throw new EventServiceNotConnected();
|
|
581
591
|
}
|
|
582
592
|
|
|
583
593
|
this._activeSubscriptions = [];
|
|
@@ -602,26 +612,29 @@ class EventClient {
|
|
|
602
612
|
* @param eventSpecification object Event parameters (please check docs for further description)
|
|
603
613
|
* @param callback object Callback function (will be called when event is received)
|
|
604
614
|
* @throws InvalidParameter
|
|
615
|
+
* @throws EventServiceNotConnected
|
|
605
616
|
* @returns object Promise on subscription
|
|
606
617
|
*/
|
|
607
618
|
subscribe(eventSpecification, callback) {
|
|
608
619
|
if (!eventSpecification || !callback) {
|
|
609
|
-
throw new InvalidParameter(
|
|
620
|
+
throw new InvalidParameter('You must specify both event spec and callback function');
|
|
610
621
|
}
|
|
611
622
|
|
|
612
|
-
if (!eventSpecification.hasOwnProperty(
|
|
613
|
-
throw new InvalidParameter(
|
|
623
|
+
if (!eventSpecification.hasOwnProperty('event_name')) {
|
|
624
|
+
throw new InvalidParameter('Your event specification must contain event name');
|
|
614
625
|
}
|
|
615
626
|
|
|
616
627
|
if (!this.isConnected()) {
|
|
617
|
-
throw new
|
|
628
|
+
throw new EventServiceNotConnected();
|
|
618
629
|
}
|
|
619
630
|
|
|
620
631
|
const self = this;
|
|
621
632
|
return new Promise(function (resolve, reject) {
|
|
622
|
-
self._requestor.sendRequest(self._socket,
|
|
633
|
+
self._requestor.sendRequest(self._socket, 'subscribe', eventSpecification).then(
|
|
623
634
|
function (subscriptionId) {
|
|
624
|
-
self.log.debug(
|
|
635
|
+
self.log.debug(
|
|
636
|
+
`Subscription to '${eventSpecification.event_name}' was successful, ID = ${subscriptionId}`,
|
|
637
|
+
);
|
|
625
638
|
self._activeSubscriptions.push(new EventSubscription(subscriptionId, eventSpecification, callback));
|
|
626
639
|
resolve(subscriptionId);
|
|
627
640
|
},
|
|
@@ -637,22 +650,23 @@ class EventClient {
|
|
|
637
650
|
* Unsubscribe event messages
|
|
638
651
|
* @param subscriptionId Object Event specification to unsubscribe (must match specification used on subscribe)
|
|
639
652
|
* @throws InvalidParameter
|
|
653
|
+
* @throws EventServiceNotConnected
|
|
640
654
|
* @returns object Promise on unsubscribe
|
|
641
655
|
*/
|
|
642
656
|
unsubscribe(subscriptionId) {
|
|
643
657
|
if (!subscriptionId) {
|
|
644
|
-
throw new InvalidParameter(
|
|
658
|
+
throw new InvalidParameter('You must specify subscription id');
|
|
645
659
|
}
|
|
646
660
|
|
|
647
661
|
if (!this.isConnected()) {
|
|
648
|
-
throw new
|
|
662
|
+
throw new EventServiceNotConnected();
|
|
649
663
|
}
|
|
650
664
|
|
|
651
|
-
this.log.info(
|
|
665
|
+
this.log.info('Unsubscribe id: ' + subscriptionId);
|
|
652
666
|
|
|
653
667
|
const self = this;
|
|
654
668
|
return new Promise(function (resolve, reject) {
|
|
655
|
-
self._requestor.sendRequest(self._socket,
|
|
669
|
+
self._requestor.sendRequest(self._socket, 'unsubscribe', { id: subscriptionId }).then(
|
|
656
670
|
(data) => {
|
|
657
671
|
self.log.info(`Unsubscribe to '${subscriptionId}' was successful`);
|
|
658
672
|
self._removeSubscription(subscriptionId);
|
|
@@ -670,12 +684,12 @@ class EventClient {
|
|
|
670
684
|
* Fire event message
|
|
671
685
|
* @param type Event type to fire
|
|
672
686
|
* @param payload Event payload
|
|
673
|
-
* @throws
|
|
687
|
+
* @throws EventServiceNotConnected
|
|
674
688
|
* @returns object Promise on event
|
|
675
689
|
*/
|
|
676
690
|
fireEvent(type, payload) {
|
|
677
691
|
if (!this.isConnected()) {
|
|
678
|
-
throw new
|
|
692
|
+
throw new EventServiceNotConnected();
|
|
679
693
|
}
|
|
680
694
|
|
|
681
695
|
return new Promise((resolve, reject) => {
|
|
@@ -708,20 +722,20 @@ class EventClient {
|
|
|
708
722
|
|
|
709
723
|
// Connect to event service
|
|
710
724
|
_connectServer() {
|
|
711
|
-
this.log.info(
|
|
725
|
+
this.log.info('Connecting to event server: ' + this._serverAddress);
|
|
712
726
|
|
|
713
727
|
const self = this;
|
|
714
728
|
return new Promise(function (resolve, reject) {
|
|
715
729
|
if (self._socket) {
|
|
716
730
|
switch (self._socket.readyState) {
|
|
717
731
|
case 0:
|
|
718
|
-
reject(
|
|
732
|
+
reject('Unable to connect, already connecting');
|
|
719
733
|
return;
|
|
720
734
|
case 1:
|
|
721
|
-
reject(
|
|
735
|
+
reject('Unable to connect, already connected');
|
|
722
736
|
return;
|
|
723
737
|
case 2:
|
|
724
|
-
reject(
|
|
738
|
+
reject('Unable to connect, socket is still closing');
|
|
725
739
|
return;
|
|
726
740
|
}
|
|
727
741
|
}
|
|
@@ -730,8 +744,8 @@ class EventClient {
|
|
|
730
744
|
let was_connected = false;
|
|
731
745
|
|
|
732
746
|
// CONNECTION OPEN
|
|
733
|
-
self._socket.addEventListener(
|
|
734
|
-
self.log.info(
|
|
747
|
+
self._socket.addEventListener('open', function (/*event*/) {
|
|
748
|
+
self.log.info('Successfully connected to event service');
|
|
735
749
|
|
|
736
750
|
was_connected = true;
|
|
737
751
|
// After successful connect we enable auto connect and reset reconnect timer
|
|
@@ -739,7 +753,7 @@ class EventClient {
|
|
|
739
753
|
self._reconnectRetrySeconds = RETRY_INTERVAL_START;
|
|
740
754
|
|
|
741
755
|
// Hook up event for incoming messages to requestor
|
|
742
|
-
self._socket.addEventListener(
|
|
756
|
+
self._socket.addEventListener('message', function (event) {
|
|
743
757
|
self._requestor.receiveMessage(self._socket, event.data);
|
|
744
758
|
});
|
|
745
759
|
|
|
@@ -751,7 +765,7 @@ class EventClient {
|
|
|
751
765
|
// self.log.debug('Pong');
|
|
752
766
|
},
|
|
753
767
|
function (reason) {
|
|
754
|
-
self.log.warning(
|
|
768
|
+
self.log.warning('Ping failed: ' + reason);
|
|
755
769
|
self._reconnectServer();
|
|
756
770
|
},
|
|
757
771
|
);
|
|
@@ -772,19 +786,19 @@ class EventClient {
|
|
|
772
786
|
});
|
|
773
787
|
|
|
774
788
|
// CONNECTION CLOSE
|
|
775
|
-
self._socket.addEventListener(
|
|
776
|
-
self.log.info(
|
|
789
|
+
self._socket.addEventListener('close', function (/*event*/) {
|
|
790
|
+
self.log.info('Disconnected from event service');
|
|
777
791
|
clearTimeout(self._pingTimerObj);
|
|
778
792
|
// We try to reconnect if possible
|
|
779
793
|
self._reconnectServer();
|
|
780
794
|
});
|
|
781
795
|
|
|
782
796
|
// CONNECTION ERROR
|
|
783
|
-
self._socket.addEventListener(
|
|
784
|
-
self.log.error(
|
|
797
|
+
self._socket.addEventListener('error', function (error) {
|
|
798
|
+
self.log.error('Websocket connection error');
|
|
785
799
|
// Only reject promise if no connection was made (in that case promise is already resolved)
|
|
786
800
|
if (!was_connected) {
|
|
787
|
-
reject(
|
|
801
|
+
reject('Unable to connect: ' + error);
|
|
788
802
|
}
|
|
789
803
|
});
|
|
790
804
|
});
|
|
@@ -798,10 +812,10 @@ class EventClient {
|
|
|
798
812
|
setTimeout(function () {
|
|
799
813
|
self._connectServer().then(
|
|
800
814
|
function () {
|
|
801
|
-
self.log.info(
|
|
815
|
+
self.log.info('Reconnect attempt successful');
|
|
802
816
|
},
|
|
803
817
|
function () {
|
|
804
|
-
self.log.warning(
|
|
818
|
+
self.log.warning('Reconnect attempt failed');
|
|
805
819
|
},
|
|
806
820
|
);
|
|
807
821
|
if (self._reconnectRetrySeconds <= RETRY_MAX_BACKOFF) {
|
|
@@ -809,7 +823,7 @@ class EventClient {
|
|
|
809
823
|
}
|
|
810
824
|
}, self._reconnectRetrySeconds * 1000);
|
|
811
825
|
} else {
|
|
812
|
-
this.log.info(
|
|
826
|
+
this.log.info('Auto reconnect disabled (disconnecting?)');
|
|
813
827
|
}
|
|
814
828
|
}
|
|
815
829
|
|
|
@@ -818,7 +832,9 @@ class EventClient {
|
|
|
818
832
|
const subId = request.data.subscription_id;
|
|
819
833
|
for (let sub of this._activeSubscriptions) {
|
|
820
834
|
if (sub.id === subId) {
|
|
821
|
-
this.log.debug(
|
|
835
|
+
this.log.debug(
|
|
836
|
+
`Received event for subcription id ${sub.id} payload: ` + JSON.stringify(request.data.payload),
|
|
837
|
+
);
|
|
822
838
|
try {
|
|
823
839
|
sub.callback(request.data.payload);
|
|
824
840
|
} catch (e) {
|
|
@@ -1487,8 +1503,8 @@ function camelcaseKeys(input, options) {
|
|
|
1487
1503
|
// 'use strict';
|
|
1488
1504
|
|
|
1489
1505
|
const options = {
|
|
1490
|
-
wssServer:
|
|
1491
|
-
eventServer:
|
|
1506
|
+
wssServer: 'wss://api.telegenta.com/sip/',
|
|
1507
|
+
eventServer: 'wss://api.telegenta.com/es/',
|
|
1492
1508
|
provisionToken: null,
|
|
1493
1509
|
authorizationUsername: null,
|
|
1494
1510
|
password: null,
|
|
@@ -1521,9 +1537,9 @@ const DTMF_FREQUENCY_TABLE = {
|
|
|
1521
1537
|
8: [1336, 852],
|
|
1522
1538
|
9: [1477, 852],
|
|
1523
1539
|
c: [1633, 852],
|
|
1524
|
-
|
|
1540
|
+
'*': [1209, 941],
|
|
1525
1541
|
0: [1336, 941],
|
|
1526
|
-
|
|
1542
|
+
'#': [1477, 941],
|
|
1527
1543
|
d: [1633, 941],
|
|
1528
1544
|
};
|
|
1529
1545
|
|
|
@@ -1594,11 +1610,11 @@ class Phone extends EventEmitter {
|
|
|
1594
1610
|
this._activeSessions = {};
|
|
1595
1611
|
|
|
1596
1612
|
// Create remote audio element
|
|
1597
|
-
this._remoteAudioElement = document.createElement(
|
|
1613
|
+
this._remoteAudioElement = document.createElement('AUDIO');
|
|
1598
1614
|
this._remoteAudioElement.autoplay = true;
|
|
1599
1615
|
|
|
1600
1616
|
// Tone generator
|
|
1601
|
-
this._toneAudioElement = document.createElement(
|
|
1617
|
+
this._toneAudioElement = document.createElement('AUDIO');
|
|
1602
1618
|
this._toneAudioElement.autoplay = true;
|
|
1603
1619
|
this._toneTimer = null;
|
|
1604
1620
|
this._repeatToneActive = false;
|
|
@@ -1613,9 +1629,9 @@ class Phone extends EventEmitter {
|
|
|
1613
1629
|
|
|
1614
1630
|
// Handle jssip debug logs
|
|
1615
1631
|
if (this.log.level === LOGLVL$1.DEBUG) {
|
|
1616
|
-
JsSIP.debug.disable(
|
|
1632
|
+
JsSIP.debug.disable('JsSIP:*');
|
|
1617
1633
|
} else {
|
|
1618
|
-
JsSIP.debug.disable(
|
|
1634
|
+
JsSIP.debug.disable('JsSIP:*');
|
|
1619
1635
|
}
|
|
1620
1636
|
}
|
|
1621
1637
|
|
|
@@ -1627,7 +1643,7 @@ class Phone extends EventEmitter {
|
|
|
1627
1643
|
*
|
|
1628
1644
|
*/
|
|
1629
1645
|
prepareMedia() {
|
|
1630
|
-
this.log.info(
|
|
1646
|
+
this.log.info('Get media permission');
|
|
1631
1647
|
const self = this;
|
|
1632
1648
|
const constraints = { audio: true, video: false };
|
|
1633
1649
|
return new Promise(function (resolve, reject) {
|
|
@@ -1635,13 +1651,13 @@ class Phone extends EventEmitter {
|
|
|
1635
1651
|
() => {
|
|
1636
1652
|
self._mediaPermission = true;
|
|
1637
1653
|
self._mediaPermissionError = false;
|
|
1638
|
-
self.log.info(
|
|
1654
|
+
self.log.info('Media permission granted');
|
|
1639
1655
|
resolve();
|
|
1640
1656
|
},
|
|
1641
1657
|
(err) => {
|
|
1642
1658
|
self._mediaPermission = false;
|
|
1643
1659
|
self._mediaPermissionError = true;
|
|
1644
|
-
self.log.error(
|
|
1660
|
+
self.log.error('Denied media permission: ' + err);
|
|
1645
1661
|
reject(err);
|
|
1646
1662
|
},
|
|
1647
1663
|
);
|
|
@@ -1676,7 +1692,7 @@ class Phone extends EventEmitter {
|
|
|
1676
1692
|
this._checkMediaPermission();
|
|
1677
1693
|
// Check phone is not running already, if so stop it first
|
|
1678
1694
|
if (this._isStarted) {
|
|
1679
|
-
this.log.warning(
|
|
1695
|
+
this.log.warning('Phone already started, stopping first');
|
|
1680
1696
|
this.stop();
|
|
1681
1697
|
}
|
|
1682
1698
|
|
|
@@ -1691,29 +1707,29 @@ class Phone extends EventEmitter {
|
|
|
1691
1707
|
try {
|
|
1692
1708
|
pt = JSON.parse(atob(this._options.provisionToken));
|
|
1693
1709
|
} catch (err) {
|
|
1694
|
-
throw new InvalidOptions(
|
|
1710
|
+
throw new InvalidOptions('provisionToken', 'Invalid token');
|
|
1695
1711
|
}
|
|
1696
1712
|
|
|
1697
|
-
this.log.info(
|
|
1713
|
+
this.log.info('Using provisioning token authorization');
|
|
1698
1714
|
this._options.wssServer = pt.wss_server;
|
|
1699
1715
|
this._options.eventServer = pt.event_server;
|
|
1700
1716
|
this._options.authorizationUsername = pt.wss_username;
|
|
1701
1717
|
this._options.password = pt.wss_password;
|
|
1702
1718
|
this._options.callToken = pt.wss_token;
|
|
1703
1719
|
} else {
|
|
1704
|
-
this.log.info(
|
|
1720
|
+
this.log.info('Using manual specified credentials');
|
|
1705
1721
|
|
|
1706
1722
|
// Check auth info is provided
|
|
1707
1723
|
if (!this._options.authorizationUsername) {
|
|
1708
|
-
throw new InvalidOptions(
|
|
1724
|
+
throw new InvalidOptions('authorizationUsername', 'Missing username');
|
|
1709
1725
|
}
|
|
1710
1726
|
|
|
1711
1727
|
if (!this._options.password) {
|
|
1712
|
-
throw new InvalidOptions(
|
|
1728
|
+
throw new InvalidOptions('password', 'Missing password');
|
|
1713
1729
|
}
|
|
1714
1730
|
|
|
1715
1731
|
if (!this._options.callToken) {
|
|
1716
|
-
throw new InvalidOptions(
|
|
1732
|
+
throw new InvalidOptions('callToken', 'Missing call token');
|
|
1717
1733
|
}
|
|
1718
1734
|
}
|
|
1719
1735
|
|
|
@@ -1736,18 +1752,18 @@ class Phone extends EventEmitter {
|
|
|
1736
1752
|
if (this._options.useCallDataInfoEvent) {
|
|
1737
1753
|
// Subscribe to calldata_available events
|
|
1738
1754
|
let eventSpec = {
|
|
1739
|
-
event_name:
|
|
1755
|
+
event_name: 'calldata_available',
|
|
1740
1756
|
call_token: this._options.callToken,
|
|
1741
1757
|
};
|
|
1742
1758
|
|
|
1743
1759
|
this._eventClient
|
|
1744
1760
|
.subscribe(eventSpec, (data) => {
|
|
1745
1761
|
try {
|
|
1746
|
-
this.log.debug(
|
|
1762
|
+
this.log.debug('Received remote call data info');
|
|
1747
1763
|
this.log.debug(data);
|
|
1748
1764
|
this._fireCallDataInfoEvent(data);
|
|
1749
1765
|
} catch (e) {
|
|
1750
|
-
this.log.error(
|
|
1766
|
+
this.log.error('Event callback failed: ' + e);
|
|
1751
1767
|
}
|
|
1752
1768
|
})
|
|
1753
1769
|
.then(
|
|
@@ -1770,10 +1786,10 @@ class Phone extends EventEmitter {
|
|
|
1770
1786
|
try {
|
|
1771
1787
|
wsSocket = new JsSIP.WebSocketInterface(this._options.wssServer);
|
|
1772
1788
|
} catch (err) {
|
|
1773
|
-
throw new UnableToConnect(
|
|
1789
|
+
throw new UnableToConnect('Unable to connect phone websocket: ' + err);
|
|
1774
1790
|
}
|
|
1775
1791
|
|
|
1776
|
-
this.log.info(
|
|
1792
|
+
this.log.info('Starting phone');
|
|
1777
1793
|
this.log.debug(this._options);
|
|
1778
1794
|
|
|
1779
1795
|
this._UA = new JsSIP.UA({
|
|
@@ -1782,15 +1798,15 @@ class Phone extends EventEmitter {
|
|
|
1782
1798
|
password: this._options.password,
|
|
1783
1799
|
register: this._options.register,
|
|
1784
1800
|
display_name: this._options.authorizationUsername,
|
|
1785
|
-
uri: this._options.authorizationUsername +
|
|
1801
|
+
uri: this._options.authorizationUsername + '@telegenta.com',
|
|
1786
1802
|
session_timers: false,
|
|
1787
1803
|
});
|
|
1788
1804
|
|
|
1789
1805
|
// Setup remote media handler
|
|
1790
1806
|
const self = this;
|
|
1791
|
-
this._UA.on(
|
|
1792
|
-
self.log.debug(
|
|
1793
|
-
if (data.originator ===
|
|
1807
|
+
this._UA.on('newRTCSession', function (data) {
|
|
1808
|
+
self.log.debug('New RTCSession');
|
|
1809
|
+
if (data.originator === 'remote') {
|
|
1794
1810
|
// Create new session
|
|
1795
1811
|
const session = new Session(self);
|
|
1796
1812
|
|
|
@@ -1803,18 +1819,20 @@ class Phone extends EventEmitter {
|
|
|
1803
1819
|
// Check if we have other sessions and reject call if autoBusyIncoming is true
|
|
1804
1820
|
if (self._options.autoBusyIncoming) {
|
|
1805
1821
|
if (Object.keys(self._activeSessions).length > 0) {
|
|
1806
|
-
self.log.info(
|
|
1807
|
-
|
|
1822
|
+
self.log.info(
|
|
1823
|
+
`Autorejecting incoming call ${session.uuid} because there is already active calls`,
|
|
1824
|
+
);
|
|
1825
|
+
session.reject(486, 'Busy');
|
|
1808
1826
|
return;
|
|
1809
1827
|
} else {
|
|
1810
|
-
self.log.debug(
|
|
1828
|
+
self.log.debug('There are no other sessions, adding incoming call');
|
|
1811
1829
|
}
|
|
1812
1830
|
}
|
|
1813
1831
|
|
|
1814
1832
|
self._addSession(session);
|
|
1815
1833
|
|
|
1816
1834
|
// Set new session state
|
|
1817
|
-
session._setSessionState(
|
|
1835
|
+
session._setSessionState('INCOMING');
|
|
1818
1836
|
|
|
1819
1837
|
// Now fire incomingCall event
|
|
1820
1838
|
self._fireIncomingCallEvent(session);
|
|
@@ -1822,33 +1840,33 @@ class Phone extends EventEmitter {
|
|
|
1822
1840
|
});
|
|
1823
1841
|
|
|
1824
1842
|
// Setup connection state event subscriptions
|
|
1825
|
-
this._UA.on(
|
|
1826
|
-
self._setConnectionState(
|
|
1843
|
+
this._UA.on('connecting', function (/* data */) {
|
|
1844
|
+
self._setConnectionState('CONNECTING');
|
|
1827
1845
|
});
|
|
1828
1846
|
|
|
1829
|
-
this._UA.on(
|
|
1830
|
-
self._setConnectionState(
|
|
1847
|
+
this._UA.on('connected', function (/* data */) {
|
|
1848
|
+
self._setConnectionState('CONNECTED');
|
|
1831
1849
|
});
|
|
1832
1850
|
|
|
1833
|
-
this._UA.on(
|
|
1834
|
-
self._setConnectionState(
|
|
1851
|
+
this._UA.on('disconnected', function (/* data */) {
|
|
1852
|
+
self._setConnectionState('DISCONNECTED');
|
|
1835
1853
|
});
|
|
1836
1854
|
|
|
1837
|
-
this._UA.on(
|
|
1838
|
-
self._setConnectionState(
|
|
1855
|
+
this._UA.on('registered', function (/* data */) {
|
|
1856
|
+
self._setConnectionState('REGISTERED');
|
|
1839
1857
|
});
|
|
1840
1858
|
|
|
1841
|
-
this._UA.on(
|
|
1842
|
-
self._setConnectionState(
|
|
1859
|
+
this._UA.on('unregistered', function (/* data */) {
|
|
1860
|
+
self._setConnectionState('UNREGISTERED');
|
|
1843
1861
|
});
|
|
1844
1862
|
|
|
1845
|
-
this._UA.on(
|
|
1846
|
-
self._setConnectionState(
|
|
1863
|
+
this._UA.on('registrationFailed', function (/* data */) {
|
|
1864
|
+
self._setConnectionState('REGISTRATION_FAILED');
|
|
1847
1865
|
});
|
|
1848
1866
|
|
|
1849
1867
|
this._UA.start();
|
|
1850
1868
|
this._isStarted = true;
|
|
1851
|
-
this.log.info(
|
|
1869
|
+
this.log.info('Successfully started phone');
|
|
1852
1870
|
}
|
|
1853
1871
|
|
|
1854
1872
|
/**
|
|
@@ -1858,7 +1876,7 @@ class Phone extends EventEmitter {
|
|
|
1858
1876
|
stop() {
|
|
1859
1877
|
// Check phone is started first
|
|
1860
1878
|
if (!this._isStarted) {
|
|
1861
|
-
throw new NotReady(
|
|
1879
|
+
throw new NotReady('Phone is not started');
|
|
1862
1880
|
}
|
|
1863
1881
|
|
|
1864
1882
|
// Make sure events are not firing anymore, we can't rely on GC.
|
|
@@ -1870,18 +1888,18 @@ class Phone extends EventEmitter {
|
|
|
1870
1888
|
|
|
1871
1889
|
// Disconnect event client if needed
|
|
1872
1890
|
if (!this._eventClient) {
|
|
1873
|
-
this.log.debug(
|
|
1891
|
+
this.log.debug('Event client not running, not stopping');
|
|
1874
1892
|
return;
|
|
1875
1893
|
}
|
|
1876
1894
|
try {
|
|
1877
1895
|
this._eventClient.disconnect();
|
|
1878
1896
|
delete this._eventClient;
|
|
1879
1897
|
} catch (e) {
|
|
1880
|
-
this.log.warning(
|
|
1898
|
+
this.log.warning('Unable to disconnect event client');
|
|
1881
1899
|
}
|
|
1882
1900
|
|
|
1883
1901
|
this._isStarted = false;
|
|
1884
|
-
this.log.info(
|
|
1902
|
+
this.log.info('Phone client successfully stopped');
|
|
1885
1903
|
}
|
|
1886
1904
|
|
|
1887
1905
|
/**
|
|
@@ -1903,12 +1921,12 @@ class Phone extends EventEmitter {
|
|
|
1903
1921
|
call(number, params = {}) {
|
|
1904
1922
|
// Check phone is started first
|
|
1905
1923
|
if (!this._isStarted) {
|
|
1906
|
-
throw new NotReady(
|
|
1924
|
+
throw new NotReady('Phone is not started');
|
|
1907
1925
|
}
|
|
1908
1926
|
|
|
1909
1927
|
// Check we dont have any other connecting calls
|
|
1910
1928
|
if (this._checkForConnectingCalls()) {
|
|
1911
|
-
throw NotAllowed(
|
|
1929
|
+
throw new NotAllowed('Unable to make new calls while others are connecting');
|
|
1912
1930
|
}
|
|
1913
1931
|
|
|
1914
1932
|
// Hold other calls if needed
|
|
@@ -1928,9 +1946,9 @@ class Phone extends EventEmitter {
|
|
|
1928
1946
|
);
|
|
1929
1947
|
|
|
1930
1948
|
// Trim phone number to remove spaces and '()'
|
|
1931
|
-
number = number.trim().replace(/ |\(|\)/g,
|
|
1949
|
+
number = number.trim().replace(/ |\(|\)/g, '');
|
|
1932
1950
|
|
|
1933
|
-
this.log.info(
|
|
1951
|
+
this.log.info('Calling number: ' + number);
|
|
1934
1952
|
|
|
1935
1953
|
let session = new Session(this);
|
|
1936
1954
|
session.newOutboundSession(number, callParams);
|
|
@@ -1946,12 +1964,12 @@ class Phone extends EventEmitter {
|
|
|
1946
1964
|
* @throws NotAllowed - If there are calls currently connecting
|
|
1947
1965
|
*/
|
|
1948
1966
|
listen(listenKey) {
|
|
1949
|
-
const s = listenKey.split(
|
|
1967
|
+
const s = listenKey.split('@');
|
|
1950
1968
|
let callUUID = s[0];
|
|
1951
|
-
let mediaSwitch =
|
|
1969
|
+
let mediaSwitch = 'sip:' + s[1];
|
|
1952
1970
|
|
|
1953
|
-
this.log.debug(
|
|
1954
|
-
return this.call(
|
|
1971
|
+
this.log.debug('Initiating listen to call with uuid ' + callUUID + ' on media switch ' + mediaSwitch);
|
|
1972
|
+
return this.call('listen-' + callUUID, {
|
|
1955
1973
|
mediaSwitchURI: mediaSwitch,
|
|
1956
1974
|
_isListenCall: true,
|
|
1957
1975
|
});
|
|
@@ -2077,7 +2095,7 @@ class Phone extends EventEmitter {
|
|
|
2077
2095
|
|
|
2078
2096
|
// Begin repeating tones
|
|
2079
2097
|
_startRepeatTone(sequences) {
|
|
2080
|
-
this.log.info(
|
|
2098
|
+
this.log.info('Repeating tone sequence started');
|
|
2081
2099
|
this.log.debug(sequences);
|
|
2082
2100
|
this._repeatToneSequence = sequences;
|
|
2083
2101
|
this._repeatToneSequencePos = 0;
|
|
@@ -2107,7 +2125,7 @@ class Phone extends EventEmitter {
|
|
|
2107
2125
|
// End repeating tones
|
|
2108
2126
|
_endRepeatTone() {
|
|
2109
2127
|
if (this._repeatToneActive) {
|
|
2110
|
-
this.log.info(
|
|
2128
|
+
this.log.info('Repeating tone stopped');
|
|
2111
2129
|
this._repeatToneActive = false;
|
|
2112
2130
|
this.stopTone();
|
|
2113
2131
|
}
|
|
@@ -2118,7 +2136,7 @@ class Phone extends EventEmitter {
|
|
|
2118
2136
|
// Create audio context
|
|
2119
2137
|
const AudioContext = window.AudioContext || window.webkitAudioContext || false;
|
|
2120
2138
|
if (!AudioContext) {
|
|
2121
|
-
throw new NotAllowed(
|
|
2139
|
+
throw new NotAllowed('Audio API not supported by this browser');
|
|
2122
2140
|
}
|
|
2123
2141
|
|
|
2124
2142
|
const ctx = new AudioContext();
|
|
@@ -2146,17 +2164,17 @@ class Phone extends EventEmitter {
|
|
|
2146
2164
|
* @throws NotAllowed If there are connection calls
|
|
2147
2165
|
*/
|
|
2148
2166
|
holdAllCalls() {
|
|
2149
|
-
this.log.info(
|
|
2167
|
+
this.log.info('Holding all active sessions');
|
|
2150
2168
|
// Check all session can be set on hold
|
|
2151
2169
|
if (this._checkForConnectingCalls()) {
|
|
2152
|
-
throw NotAllowed(
|
|
2170
|
+
throw new NotAllowed('A session is currently connecting');
|
|
2153
2171
|
}
|
|
2154
2172
|
|
|
2155
2173
|
// Iterate through all active calls and hold them
|
|
2156
2174
|
for (let key in this._activeSessions) {
|
|
2157
2175
|
if (this._activeSessions.hasOwnProperty(key)) {
|
|
2158
2176
|
let session = this._activeSessions[key];
|
|
2159
|
-
if (session.state ===
|
|
2177
|
+
if (session.state === 'ACTIVE') {
|
|
2160
2178
|
session.hold();
|
|
2161
2179
|
}
|
|
2162
2180
|
}
|
|
@@ -2185,9 +2203,9 @@ class Phone extends EventEmitter {
|
|
|
2185
2203
|
|
|
2186
2204
|
// Fire event
|
|
2187
2205
|
try {
|
|
2188
|
-
this.emit(
|
|
2206
|
+
this.emit('sessionCreated', { session: session });
|
|
2189
2207
|
} catch (e) {
|
|
2190
|
-
this.log.error(
|
|
2208
|
+
this.log.error('Exception in sessionCreated event: ' + e);
|
|
2191
2209
|
}
|
|
2192
2210
|
}
|
|
2193
2211
|
|
|
@@ -2203,9 +2221,9 @@ class Phone extends EventEmitter {
|
|
|
2203
2221
|
|
|
2204
2222
|
// Fire event
|
|
2205
2223
|
try {
|
|
2206
|
-
this.emit(
|
|
2224
|
+
this.emit('sessionRemoved', { session: session });
|
|
2207
2225
|
} catch (e) {
|
|
2208
|
-
this.log.error(
|
|
2226
|
+
this.log.error('Exception in sessionRemoved event: ' + e);
|
|
2209
2227
|
}
|
|
2210
2228
|
|
|
2211
2229
|
delete this._activeSessions[session.__containerId];
|
|
@@ -2230,7 +2248,12 @@ class Phone extends EventEmitter {
|
|
|
2230
2248
|
for (let key in this._activeSessions) {
|
|
2231
2249
|
if (this._activeSessions.hasOwnProperty(key)) {
|
|
2232
2250
|
let session = this._activeSessions[key];
|
|
2233
|
-
if (
|
|
2251
|
+
if (
|
|
2252
|
+
session.state === 'INITIALIZING' ||
|
|
2253
|
+
session.state === 'CALLING' ||
|
|
2254
|
+
session.state === 'PROGRESS' ||
|
|
2255
|
+
session.state === 'INCOMING'
|
|
2256
|
+
) {
|
|
2234
2257
|
return true;
|
|
2235
2258
|
}
|
|
2236
2259
|
}
|
|
@@ -2255,12 +2278,12 @@ class Phone extends EventEmitter {
|
|
|
2255
2278
|
* @param {CONNECTION_STATE} state - The current connection state
|
|
2256
2279
|
*/
|
|
2257
2280
|
|
|
2258
|
-
this.log.info(
|
|
2281
|
+
this.log.info('Connection state: ' + state);
|
|
2259
2282
|
this._connectionState = state;
|
|
2260
2283
|
try {
|
|
2261
|
-
this.emit(
|
|
2284
|
+
this.emit('connectionStateChange', { state: state });
|
|
2262
2285
|
} catch (e) {
|
|
2263
|
-
this.log.error(
|
|
2286
|
+
this.log.error('Exception in connectionStateChange event: ' + e);
|
|
2264
2287
|
}
|
|
2265
2288
|
}
|
|
2266
2289
|
|
|
@@ -2271,9 +2294,9 @@ class Phone extends EventEmitter {
|
|
|
2271
2294
|
*/
|
|
2272
2295
|
_fireIncomingCallEvent(session) {
|
|
2273
2296
|
try {
|
|
2274
|
-
this.emit(
|
|
2297
|
+
this.emit('incomingCall', session);
|
|
2275
2298
|
} catch (e) {
|
|
2276
|
-
this.log.error(
|
|
2299
|
+
this.log.error('Exception in incomingCall event: ' + e);
|
|
2277
2300
|
}
|
|
2278
2301
|
}
|
|
2279
2302
|
|
|
@@ -2326,12 +2349,12 @@ class Phone extends EventEmitter {
|
|
|
2326
2349
|
|
|
2327
2350
|
// Add some extra convenience fields
|
|
2328
2351
|
e.durationStr = createDurationString(e.duration);
|
|
2329
|
-
e.answerDurationStr = e.answerDuration ? createDurationString(e.answerDuration) :
|
|
2330
|
-
e.activeDurationStr = e.activeDuration ? createDurationString(e.activeDuration) :
|
|
2352
|
+
e.answerDurationStr = e.answerDuration ? createDurationString(e.answerDuration) : '';
|
|
2353
|
+
e.activeDurationStr = e.activeDuration ? createDurationString(e.activeDuration) : '';
|
|
2331
2354
|
try {
|
|
2332
|
-
this.emit(
|
|
2355
|
+
this.emit('callDataInfo', e);
|
|
2333
2356
|
} catch (e) {
|
|
2334
|
-
this.log.error(
|
|
2357
|
+
this.log.error('Exception in callDataInfo event: ' + e);
|
|
2335
2358
|
}
|
|
2336
2359
|
}
|
|
2337
2360
|
|
|
@@ -2412,33 +2435,37 @@ class Session extends EventEmitter {
|
|
|
2412
2435
|
let options = {
|
|
2413
2436
|
mediaConstraints: { audio: true, video: false },
|
|
2414
2437
|
pcConfig: {
|
|
2415
|
-
iceServers: [{ urls: [
|
|
2438
|
+
iceServers: [{ urls: ['stun:stun.l.google.com:19302'] }],
|
|
2416
2439
|
},
|
|
2417
|
-
extraHeaders: [
|
|
2440
|
+
extraHeaders: [
|
|
2441
|
+
'X-Call-token: ' + this._phone.options.callToken,
|
|
2442
|
+
'X-P-UUID: ' + this._callUUID,
|
|
2443
|
+
'X-Max-cost: ' + callParams.maximumCallCost,
|
|
2444
|
+
],
|
|
2418
2445
|
};
|
|
2419
2446
|
|
|
2420
2447
|
// Append explicit caller id if needed
|
|
2421
2448
|
if (callParams.explicitCallerId) {
|
|
2422
|
-
options.extraHeaders.push(
|
|
2449
|
+
options.extraHeaders.push('X-CID-Number-Id: ' + callParams.explicitCallerId);
|
|
2423
2450
|
}
|
|
2424
2451
|
|
|
2425
2452
|
// Append explicit short cid if needed
|
|
2426
2453
|
if (callParams.explicitShortCallerId) {
|
|
2427
|
-
options.extraHeaders.push(
|
|
2454
|
+
options.extraHeaders.push('X-Short-CID-Id: ' + callParams.explicitShortCallerId);
|
|
2428
2455
|
}
|
|
2429
2456
|
|
|
2430
2457
|
// Append meta info
|
|
2431
2458
|
if (callParams.metaInfo) {
|
|
2432
|
-
options.extraHeaders.push(
|
|
2459
|
+
options.extraHeaders.push('X-Meta-Info: ' + encodeURI(callParams.metaInfo.toString().substr(0, 255)));
|
|
2433
2460
|
}
|
|
2434
2461
|
|
|
2435
2462
|
// Enable recording?
|
|
2436
2463
|
if (callParams.record) {
|
|
2437
2464
|
if (isNaN(callParams.record)) {
|
|
2438
|
-
throw new InvalidParameter(
|
|
2465
|
+
throw new InvalidParameter('Record parameter must be a number');
|
|
2439
2466
|
}
|
|
2440
2467
|
this.log.debug(`Recording enabled and kept for ${callParams.record} days`);
|
|
2441
|
-
options.extraHeaders.push(
|
|
2468
|
+
options.extraHeaders.push('X-Record: ' + callParams.record);
|
|
2442
2469
|
this._isRecording = true;
|
|
2443
2470
|
} else {
|
|
2444
2471
|
this._isRecording = false;
|
|
@@ -2449,8 +2476,8 @@ class Session extends EventEmitter {
|
|
|
2449
2476
|
|
|
2450
2477
|
// Manually specified media switch
|
|
2451
2478
|
if (callParams.mediaSwitchURI) {
|
|
2452
|
-
this.log.debug(
|
|
2453
|
-
options.extraHeaders.push(
|
|
2479
|
+
this.log.debug('Requesting specific media switch ' + callParams.mediaSwitchURI);
|
|
2480
|
+
options.extraHeaders.push('X-RMS: ' + callParams.mediaSwitchURI);
|
|
2454
2481
|
} else {
|
|
2455
2482
|
// Request specific media switch to group calls on same server
|
|
2456
2483
|
const otherSessions = this.getOtherSessions();
|
|
@@ -2458,24 +2485,24 @@ class Session extends EventEmitter {
|
|
|
2458
2485
|
if (otherSessions.length > 0) {
|
|
2459
2486
|
if (otherSessions[0]._mediaSwitch) {
|
|
2460
2487
|
// There are existing active calls, append first media switch address found and request call grouping to this
|
|
2461
|
-
this.log.debug(
|
|
2462
|
-
options.extraHeaders.push(
|
|
2488
|
+
this.log.debug('Requesting call grouping on media switch ' + otherSessions[0]._mediaSwitch);
|
|
2489
|
+
options.extraHeaders.push('X-RMS: sip:' + otherSessions[0]._mediaSwitch);
|
|
2463
2490
|
} else {
|
|
2464
|
-
this.log.warning(
|
|
2491
|
+
this.log.warning('Other sessions exist but media switch address is invalid');
|
|
2465
2492
|
}
|
|
2466
2493
|
} else {
|
|
2467
|
-
this.log.debug(
|
|
2494
|
+
this.log.debug('No other active calls, no media switch grouping requested');
|
|
2468
2495
|
}
|
|
2469
2496
|
}
|
|
2470
2497
|
|
|
2471
2498
|
// Prepare invite uri
|
|
2472
|
-
const uri =
|
|
2499
|
+
const uri = 'sip:' + number + '@telegenta.com';
|
|
2473
2500
|
|
|
2474
2501
|
// Not start call and create sip session
|
|
2475
2502
|
try {
|
|
2476
2503
|
this._initWebRTCSession(this._phone._UA.call(uri, options));
|
|
2477
2504
|
} catch (error) {
|
|
2478
|
-
this.log.error(
|
|
2505
|
+
this.log.error('Error starting call:', error);
|
|
2479
2506
|
return null;
|
|
2480
2507
|
}
|
|
2481
2508
|
|
|
@@ -2486,16 +2513,16 @@ class Session extends EventEmitter {
|
|
|
2486
2513
|
|
|
2487
2514
|
// Create new inbound session
|
|
2488
2515
|
newInboundSession(newSessionRequest) {
|
|
2489
|
-
this._callUUID = newSessionRequest.request.getHeader(
|
|
2490
|
-
this._destinationNumber = newSessionRequest.request.getHeader(
|
|
2491
|
-
this._localNumberName = unescape(newSessionRequest.request.getHeader(
|
|
2516
|
+
this._callUUID = newSessionRequest.request.getHeader('X-P-UUID', 0);
|
|
2517
|
+
this._destinationNumber = newSessionRequest.request.getHeader('X-DID', 0);
|
|
2518
|
+
this._localNumberName = unescape(newSessionRequest.request.getHeader('X-DID-NAME', 0));
|
|
2492
2519
|
this._originName = newSessionRequest.request.from.display_name;
|
|
2493
2520
|
this._originatingNumber = newSessionRequest.request.from.uri.user;
|
|
2494
2521
|
this.log.info(`Incoming call from ${this._originatingNumber} [${this._callUUID}]`);
|
|
2495
2522
|
this.log.debug(newSessionRequest);
|
|
2496
2523
|
|
|
2497
2524
|
// Get media switch info
|
|
2498
|
-
this._mediaSwitch = newSessionRequest.request.getHeader(
|
|
2525
|
+
this._mediaSwitch = newSessionRequest.request.getHeader('X-MS');
|
|
2499
2526
|
this.log.debug(`Media switch handling call: ${this._mediaSwitch}`);
|
|
2500
2527
|
|
|
2501
2528
|
this._initWebRTCSession(newSessionRequest.session);
|
|
@@ -2529,10 +2556,10 @@ class Session extends EventEmitter {
|
|
|
2529
2556
|
const self = this;
|
|
2530
2557
|
|
|
2531
2558
|
// ICE CANDIDATES
|
|
2532
|
-
this._webRTCSession.on(
|
|
2559
|
+
this._webRTCSession.on('icecandidate', function (event) {
|
|
2533
2560
|
const c = event.candidate;
|
|
2534
2561
|
self.log.debug(`Got ICE candidate: ${c.candidate}`);
|
|
2535
|
-
if (c.type ===
|
|
2562
|
+
if (c.type === 'srflx' && c.relatedAddress !== null && c.relatedPort !== null) {
|
|
2536
2563
|
self.log.info(`Accepting ICE candidate: ${c.candidate}`);
|
|
2537
2564
|
|
|
2538
2565
|
// Clear timer first
|
|
@@ -2555,24 +2582,24 @@ class Session extends EventEmitter {
|
|
|
2555
2582
|
}
|
|
2556
2583
|
});
|
|
2557
2584
|
|
|
2558
|
-
this._webRTCSession.on(
|
|
2585
|
+
this._webRTCSession.on('connecting', function (/* data */) {
|
|
2559
2586
|
self._startDurationUpdateTimer();
|
|
2560
2587
|
self._startRTCPStatsUpdateTimer();
|
|
2561
|
-
self._setSessionState(
|
|
2588
|
+
self._setSessionState('INITIALIZING');
|
|
2562
2589
|
});
|
|
2563
2590
|
|
|
2564
|
-
this._webRTCSession.on(
|
|
2591
|
+
this._webRTCSession.on('sending', function (/* data */) {
|
|
2565
2592
|
// Play call initialized tone if needed
|
|
2566
2593
|
if (self._phone.options.playCallInitializedTone) {
|
|
2567
2594
|
self._phone._startRepeatTone(ST_CALLINIT);
|
|
2568
2595
|
}
|
|
2569
2596
|
|
|
2570
|
-
self._setSessionState(
|
|
2597
|
+
self._setSessionState('CALLING');
|
|
2571
2598
|
});
|
|
2572
2599
|
|
|
2573
|
-
this._webRTCSession.on(
|
|
2574
|
-
if (data.originator ===
|
|
2575
|
-
self._setSessionState(
|
|
2600
|
+
this._webRTCSession.on('progress', function (data) {
|
|
2601
|
+
if (data.originator === 'remote') {
|
|
2602
|
+
self._setSessionState('PROGRESS', { code: data.response.status_code });
|
|
2576
2603
|
self._phone._endRepeatTone();
|
|
2577
2604
|
|
|
2578
2605
|
// Ringback handling on 180 RINGING
|
|
@@ -2587,14 +2614,14 @@ class Session extends EventEmitter {
|
|
|
2587
2614
|
try {
|
|
2588
2615
|
self.hangup();
|
|
2589
2616
|
} catch (e) {
|
|
2590
|
-
self.log.info(
|
|
2617
|
+
self.log.info('No active session, ignoring answer call timeout');
|
|
2591
2618
|
}
|
|
2592
2619
|
}, self._answerCallTimeout * 1000);
|
|
2593
2620
|
}
|
|
2594
2621
|
}
|
|
2595
2622
|
});
|
|
2596
2623
|
|
|
2597
|
-
this._webRTCSession.on(
|
|
2624
|
+
this._webRTCSession.on('accepted', function (data) {
|
|
2598
2625
|
// Always end ringsback
|
|
2599
2626
|
self._phone._endRepeatTone();
|
|
2600
2627
|
|
|
@@ -2607,27 +2634,27 @@ class Session extends EventEmitter {
|
|
|
2607
2634
|
}
|
|
2608
2635
|
|
|
2609
2636
|
// Get initial recording state
|
|
2610
|
-
if (data.originator ===
|
|
2611
|
-
const recState = data.response.getHeader(
|
|
2612
|
-
if (recState ===
|
|
2613
|
-
self.log.debug(
|
|
2637
|
+
if (data.originator === 'remote') {
|
|
2638
|
+
const recState = data.response.getHeader('X-Recording');
|
|
2639
|
+
if (recState === 'true') {
|
|
2640
|
+
self.log.debug('Call is being prerecorded');
|
|
2614
2641
|
// self._isRecording = true;
|
|
2615
2642
|
// self.emit('recordingStateChange', {state: true, error: null});
|
|
2616
2643
|
} else {
|
|
2617
|
-
self.log.debug(
|
|
2644
|
+
self.log.debug('No call recording started yet');
|
|
2618
2645
|
}
|
|
2619
2646
|
|
|
2620
2647
|
// Get media switch info
|
|
2621
|
-
self._mediaSwitch = data.response.getHeader(
|
|
2648
|
+
self._mediaSwitch = data.response.getHeader('X-MS');
|
|
2622
2649
|
self.log.debug(`Media switch handling call: ${self._mediaSwitch}`);
|
|
2623
2650
|
}
|
|
2624
2651
|
});
|
|
2625
2652
|
|
|
2626
|
-
this._webRTCSession.on(
|
|
2627
|
-
self._setSessionState(
|
|
2653
|
+
this._webRTCSession.on('confirmed', function (/* data */) {
|
|
2654
|
+
self._setSessionState('ACTIVE');
|
|
2628
2655
|
});
|
|
2629
2656
|
|
|
2630
|
-
this._webRTCSession.on(
|
|
2657
|
+
this._webRTCSession.on('ended', function (/* data */) {
|
|
2631
2658
|
// End any ringsbacks if needed
|
|
2632
2659
|
self._phone._endRepeatTone();
|
|
2633
2660
|
|
|
@@ -2638,10 +2665,10 @@ class Session extends EventEmitter {
|
|
|
2638
2665
|
clearTimeout(self._answerCallTimer);
|
|
2639
2666
|
}
|
|
2640
2667
|
|
|
2641
|
-
self._setSessionState(
|
|
2668
|
+
self._setSessionState('TERMINATED');
|
|
2642
2669
|
});
|
|
2643
2670
|
|
|
2644
|
-
this._webRTCSession.on(
|
|
2671
|
+
this._webRTCSession.on('failed', function (data) {
|
|
2645
2672
|
// End any ringsbacks if needed
|
|
2646
2673
|
self._phone._endRepeatTone();
|
|
2647
2674
|
|
|
@@ -2659,54 +2686,54 @@ class Session extends EventEmitter {
|
|
|
2659
2686
|
if (data.message) {
|
|
2660
2687
|
m = `${data.message.status_code} ${data.message.reason_phrase}`;
|
|
2661
2688
|
} else {
|
|
2662
|
-
m =
|
|
2689
|
+
m = '';
|
|
2663
2690
|
}
|
|
2664
2691
|
|
|
2665
|
-
self._setSessionState(
|
|
2692
|
+
self._setSessionState('FAILED', { cause: data.cause, message: m });
|
|
2666
2693
|
});
|
|
2667
2694
|
|
|
2668
|
-
this._webRTCSession.on(
|
|
2669
|
-
self.log.debug(
|
|
2695
|
+
this._webRTCSession.on('dtmf', function (data) {
|
|
2696
|
+
self.log.debug('Session DTMF', data);
|
|
2670
2697
|
});
|
|
2671
2698
|
|
|
2672
|
-
this._webRTCSession.on(
|
|
2673
|
-
self.log.debug(
|
|
2699
|
+
this._webRTCSession.on('newInfo', function (data) {
|
|
2700
|
+
self.log.debug('New session info', data);
|
|
2674
2701
|
});
|
|
2675
2702
|
|
|
2676
|
-
this._webRTCSession.on(
|
|
2703
|
+
this._webRTCSession.on('hold', function (/* data */) {
|
|
2677
2704
|
self._isOnHold = true;
|
|
2678
2705
|
self._fireHoldStateEvent();
|
|
2679
|
-
self._setSessionState(
|
|
2706
|
+
self._setSessionState('ON_HOLD');
|
|
2680
2707
|
});
|
|
2681
2708
|
|
|
2682
|
-
this._webRTCSession.on(
|
|
2709
|
+
this._webRTCSession.on('unhold', function (/* data */) {
|
|
2683
2710
|
self._isOnHold = false;
|
|
2684
2711
|
self._fireHoldStateEvent();
|
|
2685
|
-
self._setSessionState(
|
|
2712
|
+
self._setSessionState('ACTIVE');
|
|
2686
2713
|
});
|
|
2687
2714
|
|
|
2688
|
-
this._webRTCSession.on(
|
|
2689
|
-
self.log.debug(
|
|
2715
|
+
this._webRTCSession.on('muted', function (data) {
|
|
2716
|
+
self.log.debug('Microphone muted', data);
|
|
2690
2717
|
self._isMuted = true;
|
|
2691
2718
|
self._fireMuteStateEvent();
|
|
2692
2719
|
});
|
|
2693
2720
|
|
|
2694
|
-
this._webRTCSession.on(
|
|
2695
|
-
self.log.debug(
|
|
2721
|
+
this._webRTCSession.on('unmuted', function (data) {
|
|
2722
|
+
self.log.debug('Microphone unmuted', data);
|
|
2696
2723
|
self._isMuted = false;
|
|
2697
2724
|
self._fireMuteStateEvent();
|
|
2698
2725
|
});
|
|
2699
2726
|
|
|
2700
|
-
this._webRTCSession.on(
|
|
2701
|
-
self.log.debug(
|
|
2727
|
+
this._webRTCSession.on('reinvite', function (data) {
|
|
2728
|
+
self.log.debug('Session reinvite', data);
|
|
2702
2729
|
});
|
|
2703
2730
|
|
|
2704
|
-
this._webRTCSession.on(
|
|
2705
|
-
self.log.debug(
|
|
2731
|
+
this._webRTCSession.on('update', function (data) {
|
|
2732
|
+
self.log.debug('Session update', data);
|
|
2706
2733
|
});
|
|
2707
2734
|
|
|
2708
|
-
this._webRTCSession.on(
|
|
2709
|
-
self.log.error(
|
|
2735
|
+
this._webRTCSession.on('getusermediafailed', function (data) {
|
|
2736
|
+
self.log.error('Getusermedia failed', data);
|
|
2710
2737
|
});
|
|
2711
2738
|
|
|
2712
2739
|
// If connection connect audio stream
|
|
@@ -2715,7 +2742,7 @@ class Session extends EventEmitter {
|
|
|
2715
2742
|
}
|
|
2716
2743
|
|
|
2717
2744
|
// DEBUG STUFF
|
|
2718
|
-
this.log.debug(
|
|
2745
|
+
this.log.debug('WebRTCSession object:');
|
|
2719
2746
|
this.log.debug(this);
|
|
2720
2747
|
}
|
|
2721
2748
|
|
|
@@ -2726,13 +2753,13 @@ class Session extends EventEmitter {
|
|
|
2726
2753
|
*/
|
|
2727
2754
|
hangup(disposition = null) {
|
|
2728
2755
|
if (!disposition) {
|
|
2729
|
-
this.log.info(
|
|
2756
|
+
this.log.info('Hanging up call');
|
|
2730
2757
|
this._webRTCSession.terminate();
|
|
2731
2758
|
} else {
|
|
2732
2759
|
this.log.info(`Hanging up call with disposition ${disposition}`);
|
|
2733
2760
|
this._manualDisposition = disposition;
|
|
2734
2761
|
this._webRTCSession.terminate({
|
|
2735
|
-
extraHeaders: [
|
|
2762
|
+
extraHeaders: ['X-HDISP: ' + disposition],
|
|
2736
2763
|
});
|
|
2737
2764
|
}
|
|
2738
2765
|
}
|
|
@@ -2745,14 +2772,14 @@ class Session extends EventEmitter {
|
|
|
2745
2772
|
let options = {
|
|
2746
2773
|
mediaConstraints: { audio: true, video: false },
|
|
2747
2774
|
pcConfig: {
|
|
2748
|
-
iceServers: [{ urls: [
|
|
2775
|
+
iceServers: [{ urls: ['stun:stun.l.google.com:19302'] }],
|
|
2749
2776
|
},
|
|
2750
|
-
extraHeaders: [
|
|
2777
|
+
extraHeaders: ['X-Call-token: ' + this._phone.options.callToken],
|
|
2751
2778
|
};
|
|
2752
2779
|
|
|
2753
2780
|
// Append meta info if needed
|
|
2754
2781
|
if (metaInfo) {
|
|
2755
|
-
options.extraHeaders.push(
|
|
2782
|
+
options.extraHeaders.push('X-Meta-Info: ' + encodeURI(metaInfo.toString().substr(0, 255)));
|
|
2756
2783
|
}
|
|
2757
2784
|
|
|
2758
2785
|
this._webRTCSession.answer(options);
|
|
@@ -2766,8 +2793,8 @@ class Session extends EventEmitter {
|
|
|
2766
2793
|
* @param statusCode - SIP Status code
|
|
2767
2794
|
* @param reasonPhrase - Reason phrase
|
|
2768
2795
|
*/
|
|
2769
|
-
reject(statusCode = 603, reasonPhrase =
|
|
2770
|
-
this.log.info(
|
|
2796
|
+
reject(statusCode = 603, reasonPhrase = 'Rejected call') {
|
|
2797
|
+
this.log.info('Rejecting call');
|
|
2771
2798
|
this._webRTCSession.terminate({
|
|
2772
2799
|
status_code: statusCode,
|
|
2773
2800
|
reason_phrase: reasonPhrase,
|
|
@@ -2808,7 +2835,7 @@ class Session extends EventEmitter {
|
|
|
2808
2835
|
|
|
2809
2836
|
// Set destination number
|
|
2810
2837
|
set destinationNumber(number) {
|
|
2811
|
-
this.log.debug(
|
|
2838
|
+
this.log.debug('Setting destination number to: ' + number);
|
|
2812
2839
|
this._destinationNumber = number;
|
|
2813
2840
|
}
|
|
2814
2841
|
|
|
@@ -2826,17 +2853,17 @@ class Session extends EventEmitter {
|
|
|
2826
2853
|
*/
|
|
2827
2854
|
get direction() {
|
|
2828
2855
|
if (this._isListenCall) {
|
|
2829
|
-
this.log.debug(
|
|
2830
|
-
return
|
|
2831
|
-
} else if (this._webRTCSession.direction ===
|
|
2832
|
-
this.log.debug(
|
|
2833
|
-
return
|
|
2834
|
-
} else if (this._webRTCSession.direction ===
|
|
2835
|
-
this.log.debug(
|
|
2836
|
-
return
|
|
2856
|
+
this.log.debug('Call direction is LISTEN');
|
|
2857
|
+
return 'LISTEN';
|
|
2858
|
+
} else if (this._webRTCSession.direction === 'incoming') {
|
|
2859
|
+
this.log.debug('Call direction is INBOUND');
|
|
2860
|
+
return 'INBOUND';
|
|
2861
|
+
} else if (this._webRTCSession.direction === 'outgoing') {
|
|
2862
|
+
this.log.debug('Call direction is OUTBOUND');
|
|
2863
|
+
return 'OUTBOUND';
|
|
2837
2864
|
} else {
|
|
2838
|
-
this.log.error(
|
|
2839
|
-
return
|
|
2865
|
+
this.log.error('Unknown call direction: ' + this._webRTCSession.direction);
|
|
2866
|
+
return 'UNKNOWN';
|
|
2840
2867
|
}
|
|
2841
2868
|
}
|
|
2842
2869
|
|
|
@@ -2846,33 +2873,33 @@ class Session extends EventEmitter {
|
|
|
2846
2873
|
*/
|
|
2847
2874
|
startRecording() {
|
|
2848
2875
|
const opt = {
|
|
2849
|
-
extraHeaders: [
|
|
2876
|
+
extraHeaders: ['Record: on'],
|
|
2850
2877
|
};
|
|
2851
2878
|
|
|
2852
|
-
this.log.debug(
|
|
2879
|
+
this.log.debug('Request start recording');
|
|
2853
2880
|
|
|
2854
2881
|
const self = this;
|
|
2855
|
-
this._webRTCSession.once(
|
|
2856
|
-
if (data.originator ===
|
|
2857
|
-
data.info.on(
|
|
2858
|
-
self.log.info(
|
|
2882
|
+
this._webRTCSession.once('newInfo', (data) => {
|
|
2883
|
+
if (data.originator === 'local') {
|
|
2884
|
+
data.info.on('succeeded', function (/* data */) {
|
|
2885
|
+
self.log.info('Recording started successfully');
|
|
2859
2886
|
self._isRecording = true;
|
|
2860
2887
|
try {
|
|
2861
|
-
self.emit(
|
|
2888
|
+
self.emit('recordingStateChange', { state: true, error: null });
|
|
2862
2889
|
} catch (e) {
|
|
2863
|
-
self.log.error(
|
|
2890
|
+
self.log.error('Exception in recordingStateChange event: ' + e);
|
|
2864
2891
|
}
|
|
2865
2892
|
});
|
|
2866
2893
|
|
|
2867
|
-
data.info.on(
|
|
2894
|
+
data.info.on('failed', function (data) {
|
|
2868
2895
|
self._recordingError = data.response.reason_phrase;
|
|
2869
|
-
self.log.error(
|
|
2896
|
+
self.log.error('Recording start failed: ' + self._recordingError);
|
|
2870
2897
|
self._fireRecordingStateEvent();
|
|
2871
2898
|
});
|
|
2872
2899
|
}
|
|
2873
2900
|
});
|
|
2874
2901
|
|
|
2875
|
-
this._webRTCSession.sendInfo(
|
|
2902
|
+
this._webRTCSession.sendInfo('application/info', null, opt);
|
|
2876
2903
|
}
|
|
2877
2904
|
|
|
2878
2905
|
/**
|
|
@@ -2880,35 +2907,35 @@ class Session extends EventEmitter {
|
|
|
2880
2907
|
*/
|
|
2881
2908
|
stopRecording() {
|
|
2882
2909
|
const opt = {
|
|
2883
|
-
extraHeaders: [
|
|
2910
|
+
extraHeaders: ['Record: off'],
|
|
2884
2911
|
};
|
|
2885
2912
|
|
|
2886
|
-
this.log.debug(
|
|
2913
|
+
this.log.debug('Request stop recording');
|
|
2887
2914
|
|
|
2888
2915
|
const self = this;
|
|
2889
2916
|
|
|
2890
|
-
this._webRTCSession.once(
|
|
2891
|
-
data.info.on(
|
|
2892
|
-
self.log.info(
|
|
2917
|
+
this._webRTCSession.once('newInfo', (data) => {
|
|
2918
|
+
data.info.on('succeeded', function (/* data */) {
|
|
2919
|
+
self.log.info('Recording stopped successfully');
|
|
2893
2920
|
self._isRecording = false;
|
|
2894
2921
|
try {
|
|
2895
|
-
self.emit(
|
|
2922
|
+
self.emit('recordingStateChange', { state: false, error: null });
|
|
2896
2923
|
} catch (e) {
|
|
2897
|
-
self.log.error(
|
|
2924
|
+
self.log.error('Exception in recordingStateChange event: ' + e);
|
|
2898
2925
|
}
|
|
2899
2926
|
});
|
|
2900
2927
|
|
|
2901
|
-
data.info.on(
|
|
2902
|
-
self.log.error(
|
|
2928
|
+
data.info.on('failed', function (data) {
|
|
2929
|
+
self.log.error('Recording stopped failed: ' + data.response.reason_phrase);
|
|
2903
2930
|
try {
|
|
2904
|
-
self.emit(
|
|
2931
|
+
self.emit('recordingStateChange', { state: self._isRecording, error: data.response.reason_phrase });
|
|
2905
2932
|
} catch (e) {
|
|
2906
|
-
self.log.error(
|
|
2933
|
+
self.log.error('Exception in recordingStateChange event: ' + e);
|
|
2907
2934
|
}
|
|
2908
2935
|
});
|
|
2909
2936
|
});
|
|
2910
2937
|
|
|
2911
|
-
this._webRTCSession.sendInfo(
|
|
2938
|
+
this._webRTCSession.sendInfo('application/info', null, opt);
|
|
2912
2939
|
}
|
|
2913
2940
|
|
|
2914
2941
|
/**
|
|
@@ -2937,7 +2964,7 @@ class Session extends EventEmitter {
|
|
|
2937
2964
|
* Mute microphone
|
|
2938
2965
|
*/
|
|
2939
2966
|
muteMic() {
|
|
2940
|
-
this.log.info(
|
|
2967
|
+
this.log.info('Mute microphone');
|
|
2941
2968
|
this._webRTCSession.mute();
|
|
2942
2969
|
}
|
|
2943
2970
|
|
|
@@ -2945,7 +2972,7 @@ class Session extends EventEmitter {
|
|
|
2945
2972
|
* Unmute microphone
|
|
2946
2973
|
*/
|
|
2947
2974
|
unmuteMic() {
|
|
2948
|
-
this.log.info(
|
|
2975
|
+
this.log.info('Unmute microphone');
|
|
2949
2976
|
this._webRTCSession.unmute();
|
|
2950
2977
|
}
|
|
2951
2978
|
|
|
@@ -2973,7 +3000,7 @@ class Session extends EventEmitter {
|
|
|
2973
3000
|
* Hold call
|
|
2974
3001
|
*/
|
|
2975
3002
|
hold() {
|
|
2976
|
-
this.log.info(
|
|
3003
|
+
this.log.info('Hold call');
|
|
2977
3004
|
this._webRTCSession.hold();
|
|
2978
3005
|
}
|
|
2979
3006
|
|
|
@@ -2981,7 +3008,7 @@ class Session extends EventEmitter {
|
|
|
2981
3008
|
* Unhold call
|
|
2982
3009
|
*/
|
|
2983
3010
|
unhold() {
|
|
2984
|
-
this.log.info(
|
|
3011
|
+
this.log.info('Uncall call');
|
|
2985
3012
|
this._phone.holdAllCalls();
|
|
2986
3013
|
this._webRTCSession.unhold();
|
|
2987
3014
|
}
|
|
@@ -3014,10 +3041,10 @@ class Session extends EventEmitter {
|
|
|
3014
3041
|
sendDTMF(code, playLocal = true) {
|
|
3015
3042
|
// Check for correct DTMF and get frequencies
|
|
3016
3043
|
if (!(code in DTMF_FREQUENCY_TABLE)) {
|
|
3017
|
-
throw new InvalidParameter(
|
|
3044
|
+
throw new InvalidParameter('Invalid DTMF code');
|
|
3018
3045
|
}
|
|
3019
3046
|
|
|
3020
|
-
this.log.info(
|
|
3047
|
+
this.log.info('Sending DTMF: ' + code);
|
|
3021
3048
|
this._webRTCSession.sendDTMF(code);
|
|
3022
3049
|
|
|
3023
3050
|
if (playLocal) {
|
|
@@ -3025,15 +3052,15 @@ class Session extends EventEmitter {
|
|
|
3025
3052
|
|
|
3026
3053
|
// First mute microphone before playing DTMF
|
|
3027
3054
|
if (this._isMuted) {
|
|
3028
|
-
this.log.debug(
|
|
3055
|
+
this.log.debug('Mic already muted, just sending DTMF');
|
|
3029
3056
|
this._phone.playTone(500, f[0], f[1]);
|
|
3030
3057
|
} else {
|
|
3031
|
-
this.log.debug(
|
|
3058
|
+
this.log.debug('Mute mic while sending DTMF');
|
|
3032
3059
|
this._webRTCSession.mute();
|
|
3033
3060
|
|
|
3034
3061
|
const self = this;
|
|
3035
3062
|
this._phone.playTone(500, f[0], f[1], function () {
|
|
3036
|
-
self.log.debug(
|
|
3063
|
+
self.log.debug('Unmute mic after DTMF');
|
|
3037
3064
|
self._webRTCSession.unmute();
|
|
3038
3065
|
});
|
|
3039
3066
|
}
|
|
@@ -3049,20 +3076,22 @@ class Session extends EventEmitter {
|
|
|
3049
3076
|
this.log.info(`Transferring ${this.uuid} => ${dstSession.uuid}`);
|
|
3050
3077
|
|
|
3051
3078
|
// Create ReferSubscriber event class
|
|
3052
|
-
const referSubscriber = this._webRTCSession.refer(this._webRTCSession._request.ruri, {
|
|
3079
|
+
const referSubscriber = this._webRTCSession.refer(this._webRTCSession._request.ruri, {
|
|
3080
|
+
replaces: dstSession._webRTCSession,
|
|
3081
|
+
});
|
|
3053
3082
|
|
|
3054
3083
|
const self = this;
|
|
3055
3084
|
|
|
3056
3085
|
// Setup some logging
|
|
3057
|
-
referSubscriber.on(
|
|
3058
|
-
self.log.info(
|
|
3086
|
+
referSubscriber.on('requestSucceeded', function () {
|
|
3087
|
+
self.log.info('Transfer completed successfully');
|
|
3059
3088
|
});
|
|
3060
3089
|
|
|
3061
|
-
referSubscriber.on(
|
|
3090
|
+
referSubscriber.on('requestFailed', function (cause) {
|
|
3062
3091
|
self.log.error(`Transfer rejected: ${cause}`);
|
|
3063
3092
|
});
|
|
3064
3093
|
|
|
3065
|
-
referSubscriber.on(
|
|
3094
|
+
referSubscriber.on('failed', function (cause) {
|
|
3066
3095
|
self.log.error(`Transfer failed: ${cause}`);
|
|
3067
3096
|
});
|
|
3068
3097
|
}
|
|
@@ -3080,14 +3109,14 @@ class Session extends EventEmitter {
|
|
|
3080
3109
|
playback(media_file_id, hangup_after = false) {
|
|
3081
3110
|
// Check event service is enabled first
|
|
3082
3111
|
if (!this._phone._options.connectEventService) {
|
|
3083
|
-
throw new NotAllowed(
|
|
3112
|
+
throw new NotAllowed('Event service not enabled');
|
|
3084
3113
|
}
|
|
3085
3114
|
|
|
3086
3115
|
this.log.info(`Playback media file ${media_file_id} hangup after ${hangup_after}`);
|
|
3087
3116
|
|
|
3088
|
-
const p = this._phone._eventClient.fireEvent(
|
|
3117
|
+
const p = this._phone._eventClient.fireEvent('call_command', {
|
|
3089
3118
|
call_uuid: this.uuid,
|
|
3090
|
-
call_command:
|
|
3119
|
+
call_command: 'playback',
|
|
3091
3120
|
media_file_id: media_file_id,
|
|
3092
3121
|
hangup_after: hangup_after,
|
|
3093
3122
|
});
|
|
@@ -3103,7 +3132,7 @@ class Session extends EventEmitter {
|
|
|
3103
3132
|
// When playback starts set state to playback
|
|
3104
3133
|
p.then(() => {
|
|
3105
3134
|
// Set session state
|
|
3106
|
-
this._setSessionState(
|
|
3135
|
+
this._setSessionState('PLAYBACK');
|
|
3107
3136
|
});
|
|
3108
3137
|
}
|
|
3109
3138
|
|
|
@@ -3119,12 +3148,12 @@ class Session extends EventEmitter {
|
|
|
3119
3148
|
* @throws InvalidOptions - If invalid mode given
|
|
3120
3149
|
*/
|
|
3121
3150
|
setWhisperMode(mode = 0) {
|
|
3122
|
-
if (!this.direction ===
|
|
3123
|
-
throw new NotAllowed(
|
|
3151
|
+
if (!this.direction === 'LISTEN') {
|
|
3152
|
+
throw new NotAllowed('Call is not correct type (listen)');
|
|
3124
3153
|
}
|
|
3125
3154
|
|
|
3126
3155
|
if (mode < 0 || mode > 2) {
|
|
3127
|
-
throw new InvalidOptions(
|
|
3156
|
+
throw new InvalidOptions('Invalid mode');
|
|
3128
3157
|
}
|
|
3129
3158
|
|
|
3130
3159
|
let t_dtmf_mode;
|
|
@@ -3182,13 +3211,13 @@ class Session extends EventEmitter {
|
|
|
3182
3211
|
duration: Date.now() - self._callBeginTime,
|
|
3183
3212
|
durationStr: createDurationString(Date.now() - self._callBeginTime),
|
|
3184
3213
|
activeDuration: activeDuration,
|
|
3185
|
-
activeDurationStr: activeDuration ? createDurationString(activeDuration) :
|
|
3214
|
+
activeDurationStr: activeDuration ? createDurationString(activeDuration) : '',
|
|
3186
3215
|
};
|
|
3187
3216
|
|
|
3188
3217
|
try {
|
|
3189
|
-
self.emit(
|
|
3218
|
+
self.emit('callDurationUpdate', e);
|
|
3190
3219
|
} catch (e) {
|
|
3191
|
-
self.log.error(
|
|
3220
|
+
self.log.error('Exception in call duration update event: ' + e);
|
|
3192
3221
|
}
|
|
3193
3222
|
}, 1000);
|
|
3194
3223
|
}
|
|
@@ -3252,40 +3281,46 @@ class Session extends EventEmitter {
|
|
|
3252
3281
|
for (let report of data.values()) {
|
|
3253
3282
|
// this.log.debug(report);
|
|
3254
3283
|
switch (report.type) {
|
|
3255
|
-
case
|
|
3284
|
+
case 'outbound-rtp':
|
|
3256
3285
|
// this.log.debug('Got: outbound-rtp');
|
|
3257
3286
|
// this.log.debug(report);
|
|
3258
3287
|
// Packets
|
|
3259
3288
|
totalReport.outboundPacketsSent = report.packetsSent;
|
|
3260
|
-
deltaReport.outboundPacketsSent =
|
|
3289
|
+
deltaReport.outboundPacketsSent =
|
|
3290
|
+
report.packetsSent - this._currentReport.outboundPacketsSent;
|
|
3261
3291
|
// Bytes
|
|
3262
3292
|
totalReport.outboundBytesSent = report.bytesSent;
|
|
3263
|
-
deltaReport.outboundBytesSent =
|
|
3293
|
+
deltaReport.outboundBytesSent =
|
|
3294
|
+
report.bytesSent - this._currentReport.outboundBytesSent;
|
|
3264
3295
|
break;
|
|
3265
3296
|
|
|
3266
|
-
case
|
|
3297
|
+
case 'inbound-rtp':
|
|
3267
3298
|
// this.log.debug('Got: inbound-rtp');
|
|
3268
3299
|
// this.log.debug(report);
|
|
3269
3300
|
// Packets
|
|
3270
3301
|
totalReport.inboundPacketsReceived = report.packetsReceived;
|
|
3271
|
-
deltaReport.inboundPacketsReceived =
|
|
3302
|
+
deltaReport.inboundPacketsReceived =
|
|
3303
|
+
report.packetsReceived - this._currentReport.inboundPacketsReceived;
|
|
3272
3304
|
// Bytes
|
|
3273
3305
|
totalReport.inboundBytesReceived = report.bytesReceived;
|
|
3274
|
-
deltaReport.inboundBytesReceived =
|
|
3306
|
+
deltaReport.inboundBytesReceived =
|
|
3307
|
+
report.bytesReceived - this._currentReport.inboundBytesReceived;
|
|
3275
3308
|
// Loss
|
|
3276
3309
|
totalReport.inboundPacketsLost = report.packetsLost;
|
|
3277
|
-
deltaReport.inboundPacketsLost =
|
|
3310
|
+
deltaReport.inboundPacketsLost =
|
|
3311
|
+
report.packetsLost - this._currentReport.inboundPacketsLost;
|
|
3278
3312
|
// Jitter
|
|
3279
3313
|
totalReport.inboundJitter = report.jitter * 1000;
|
|
3280
3314
|
deltaReport.inboundJitter = report.jitter * 1000;
|
|
3281
3315
|
break;
|
|
3282
3316
|
|
|
3283
|
-
case
|
|
3317
|
+
case 'remote-inbound-rtp':
|
|
3284
3318
|
// this.log.debug('Got: remote-inbound-rtp');
|
|
3285
3319
|
// this.log.debug(report);
|
|
3286
3320
|
// Loss
|
|
3287
3321
|
totalReport.outboundPacketsLost = report.packetsLost;
|
|
3288
|
-
deltaReport.outboundPacketsLost =
|
|
3322
|
+
deltaReport.outboundPacketsLost =
|
|
3323
|
+
report.packetsLost - this._currentReport.outboundPacketsLost;
|
|
3289
3324
|
// Jitter
|
|
3290
3325
|
totalReport.outboundJitter = report.jitter * 1000;
|
|
3291
3326
|
deltaReport.outboundJitter = report.jitter * 1000;
|
|
@@ -3307,9 +3342,9 @@ class Session extends EventEmitter {
|
|
|
3307
3342
|
};
|
|
3308
3343
|
|
|
3309
3344
|
try {
|
|
3310
|
-
this.emit(
|
|
3345
|
+
this.emit('callQualityReportUpdate', finalReport);
|
|
3311
3346
|
} catch (e) {
|
|
3312
|
-
this.log.error(
|
|
3347
|
+
this.log.error('Exception in call quality report update event: ' + e);
|
|
3313
3348
|
}
|
|
3314
3349
|
}
|
|
3315
3350
|
});
|
|
@@ -3333,27 +3368,28 @@ class Session extends EventEmitter {
|
|
|
3333
3368
|
// this.log.debug(`R factor loss effect ${lossEffect} / latency effect ${latencyEffect}`);
|
|
3334
3369
|
|
|
3335
3370
|
report.rFactor = Math.max(0, 100 - lossEffect - latencyEffect);
|
|
3336
|
-
report.mos =
|
|
3371
|
+
report.mos =
|
|
3372
|
+
1 + 0.035 * report.rFactor + 0.000007 * report.rFactor * (report.rFactor - 60) * (100 - report.rFactor);
|
|
3337
3373
|
|
|
3338
3374
|
// Quality string
|
|
3339
3375
|
switch (true) {
|
|
3340
3376
|
case report.rFactor < 50:
|
|
3341
|
-
report.qualityString =
|
|
3377
|
+
report.qualityString = 'Bad';
|
|
3342
3378
|
break;
|
|
3343
3379
|
case report.rFactor < 70:
|
|
3344
|
-
report.qualityString =
|
|
3380
|
+
report.qualityString = 'Poor';
|
|
3345
3381
|
break;
|
|
3346
3382
|
case report.rFactor < 80:
|
|
3347
|
-
report.qualityString =
|
|
3383
|
+
report.qualityString = 'Fair';
|
|
3348
3384
|
break;
|
|
3349
3385
|
case report.rFactor < 90:
|
|
3350
|
-
report.qualityString =
|
|
3386
|
+
report.qualityString = 'Good';
|
|
3351
3387
|
break;
|
|
3352
3388
|
case report.rFactor <= 100:
|
|
3353
|
-
report.qualityString =
|
|
3389
|
+
report.qualityString = 'Excellent';
|
|
3354
3390
|
break;
|
|
3355
3391
|
default:
|
|
3356
|
-
report.qualityString =
|
|
3392
|
+
report.qualityString = 'Error';
|
|
3357
3393
|
}
|
|
3358
3394
|
|
|
3359
3395
|
return report;
|
|
@@ -3387,7 +3423,7 @@ class Session extends EventEmitter {
|
|
|
3387
3423
|
*
|
|
3388
3424
|
*/
|
|
3389
3425
|
|
|
3390
|
-
this.log.debug(
|
|
3426
|
+
this.log.debug('Session state: ' + state);
|
|
3391
3427
|
if (info) {
|
|
3392
3428
|
this.log.debug(info);
|
|
3393
3429
|
}
|
|
@@ -3395,7 +3431,7 @@ class Session extends EventEmitter {
|
|
|
3395
3431
|
this._sessionState = state;
|
|
3396
3432
|
try {
|
|
3397
3433
|
this.emit(
|
|
3398
|
-
|
|
3434
|
+
'sessionStateChange',
|
|
3399
3435
|
Object.assign(
|
|
3400
3436
|
{
|
|
3401
3437
|
state: state,
|
|
@@ -3405,51 +3441,51 @@ class Session extends EventEmitter {
|
|
|
3405
3441
|
),
|
|
3406
3442
|
);
|
|
3407
3443
|
} catch (e) {
|
|
3408
|
-
this.log.error(
|
|
3444
|
+
this.log.error('Exception in sessionStateChange event:' + e);
|
|
3409
3445
|
}
|
|
3410
3446
|
|
|
3411
3447
|
// Trigger callDataAvailable dummy event
|
|
3412
|
-
if (state ===
|
|
3413
|
-
this._callDisposition = this._manualDisposition ||
|
|
3448
|
+
if (state === 'TERMINATED') {
|
|
3449
|
+
this._callDisposition = this._manualDisposition || 'NORMAL';
|
|
3414
3450
|
this._callFinished();
|
|
3415
3451
|
}
|
|
3416
3452
|
|
|
3417
|
-
if (state ===
|
|
3418
|
-
if (info.cause ===
|
|
3419
|
-
this._callDisposition =
|
|
3453
|
+
if (state === 'FAILED') {
|
|
3454
|
+
if (info.cause === 'Unavailable') {
|
|
3455
|
+
this._callDisposition = 'TEMP_UNAVAIL';
|
|
3420
3456
|
if (this._phone.options.playSignalTones) {
|
|
3421
3457
|
this._phone._startRepeatTone(ST_TEMP_UNAVAIL);
|
|
3422
3458
|
}
|
|
3423
3459
|
}
|
|
3424
3460
|
|
|
3425
|
-
if (info.cause ===
|
|
3426
|
-
this._callDisposition =
|
|
3461
|
+
if (info.cause === 'Not Found') {
|
|
3462
|
+
this._callDisposition = 'INVALID_NUMBER';
|
|
3427
3463
|
if (this._phone.options.playSignalTones) {
|
|
3428
3464
|
this._phone._startRepeatTone(ST_INVALID_NUMBER);
|
|
3429
3465
|
}
|
|
3430
3466
|
}
|
|
3431
3467
|
|
|
3432
|
-
if (info.cause ===
|
|
3433
|
-
this._callDisposition = this._manualDisposition ||
|
|
3468
|
+
if (info.cause === 'Canceled') {
|
|
3469
|
+
this._callDisposition = this._manualDisposition || 'NO_ANSWER';
|
|
3434
3470
|
}
|
|
3435
3471
|
|
|
3436
|
-
if (info.cause ===
|
|
3437
|
-
if (this.direction ===
|
|
3438
|
-
this._callDisposition =
|
|
3472
|
+
if (info.cause === 'Rejected') {
|
|
3473
|
+
if (this.direction === 'INBOUND') {
|
|
3474
|
+
this._callDisposition = 'REJECTED';
|
|
3439
3475
|
} else {
|
|
3440
|
-
this._callDisposition =
|
|
3476
|
+
this._callDisposition = 'BARRED';
|
|
3441
3477
|
}
|
|
3442
3478
|
}
|
|
3443
3479
|
|
|
3444
|
-
if (info.cause ===
|
|
3445
|
-
this._callDisposition =
|
|
3480
|
+
if (info.cause === 'Busy') {
|
|
3481
|
+
this._callDisposition = 'BUSY';
|
|
3446
3482
|
if (this._phone.options.playSignalTones) {
|
|
3447
3483
|
this._phone._startRepeatTone(ST_BUSY);
|
|
3448
3484
|
}
|
|
3449
3485
|
}
|
|
3450
3486
|
|
|
3451
3487
|
// If session is incoming and failed, we don't fire calldataAvailable event
|
|
3452
|
-
if (this._webRTCSession.direction ===
|
|
3488
|
+
if (this._webRTCSession.direction === 'incoming') ;
|
|
3453
3489
|
|
|
3454
3490
|
this._callFinished();
|
|
3455
3491
|
}
|
|
@@ -3477,7 +3513,7 @@ class Session extends EventEmitter {
|
|
|
3477
3513
|
* @property {string} REJECTED Incoming call was rejected
|
|
3478
3514
|
*/
|
|
3479
3515
|
_callFinished() {
|
|
3480
|
-
this.log.info(
|
|
3516
|
+
this.log.info('Call finished');
|
|
3481
3517
|
|
|
3482
3518
|
// Fire callEnded event
|
|
3483
3519
|
this._fireCallEndedEvent();
|
|
@@ -3494,7 +3530,7 @@ class Session extends EventEmitter {
|
|
|
3494
3530
|
|
|
3495
3531
|
// Set answer call timeout
|
|
3496
3532
|
_setAnswerCallTimeout(timeout) {
|
|
3497
|
-
this.log.debug(
|
|
3533
|
+
this.log.debug('Setting answer call timeout to: ' + timeout);
|
|
3498
3534
|
this._answerCallTimeout = timeout;
|
|
3499
3535
|
}
|
|
3500
3536
|
|
|
@@ -3506,9 +3542,9 @@ class Session extends EventEmitter {
|
|
|
3506
3542
|
* @property {boolean} state - New recording state
|
|
3507
3543
|
*/
|
|
3508
3544
|
try {
|
|
3509
|
-
this.emit(
|
|
3545
|
+
this.emit('recordingStateChange', { state: this._isRecording, error: this._recordingError });
|
|
3510
3546
|
} catch (e) {
|
|
3511
|
-
this.log.error(
|
|
3547
|
+
this.log.error('Exception in recording state change event: ' + e);
|
|
3512
3548
|
}
|
|
3513
3549
|
}
|
|
3514
3550
|
|
|
@@ -3520,9 +3556,9 @@ class Session extends EventEmitter {
|
|
|
3520
3556
|
* @property {boolean} state - Mute state (true = muted / false = unmuted)
|
|
3521
3557
|
*/
|
|
3522
3558
|
try {
|
|
3523
|
-
this.emit(
|
|
3559
|
+
this.emit('muteStateChange', { state: this._isMuted });
|
|
3524
3560
|
} catch (e) {
|
|
3525
|
-
this.log.error(
|
|
3561
|
+
this.log.error('Exception in mute state change event: ' + e);
|
|
3526
3562
|
}
|
|
3527
3563
|
}
|
|
3528
3564
|
|
|
@@ -3534,9 +3570,9 @@ class Session extends EventEmitter {
|
|
|
3534
3570
|
* @property {boolean} state - New hold state
|
|
3535
3571
|
*/
|
|
3536
3572
|
try {
|
|
3537
|
-
this.emit(
|
|
3573
|
+
this.emit('holdStateChange', { state: this._isOnHold });
|
|
3538
3574
|
} catch (e) {
|
|
3539
|
-
this.log.error(
|
|
3575
|
+
this.log.error('Exception in hold state change event: ' + e);
|
|
3540
3576
|
}
|
|
3541
3577
|
}
|
|
3542
3578
|
|
|
@@ -3548,9 +3584,9 @@ class Session extends EventEmitter {
|
|
|
3548
3584
|
* @property {boolean} mode - New whisper mode (0 = No whisper / 1 = Local only / 2 = Three way)
|
|
3549
3585
|
*/
|
|
3550
3586
|
try {
|
|
3551
|
-
this.emit(
|
|
3587
|
+
this.emit('whisperModeChange', { mode: this._whisperMode });
|
|
3552
3588
|
} catch (e) {
|
|
3553
|
-
this.log.error(
|
|
3589
|
+
this.log.error('Exception in whisper mode change event: ' + e);
|
|
3554
3590
|
}
|
|
3555
3591
|
}
|
|
3556
3592
|
|
|
@@ -3599,14 +3635,14 @@ class Session extends EventEmitter {
|
|
|
3599
3635
|
|
|
3600
3636
|
// Add some extra convenience fields
|
|
3601
3637
|
e.durationStr = createDurationString(e.duration);
|
|
3602
|
-
e.answerDurationStr = e.answerDuration ? createDurationString(e.answerDuration) :
|
|
3603
|
-
e.activeDurationStr = e.activeDuration ? createDurationString(e.activeDuration) :
|
|
3638
|
+
e.answerDurationStr = e.answerDuration ? createDurationString(e.answerDuration) : '';
|
|
3639
|
+
e.activeDurationStr = e.activeDuration ? createDurationString(e.activeDuration) : '';
|
|
3604
3640
|
e.callQualityReport = this._currentReport;
|
|
3605
3641
|
|
|
3606
3642
|
try {
|
|
3607
|
-
this.emit(
|
|
3643
|
+
this.emit('callEnded', e);
|
|
3608
3644
|
} catch (e) {
|
|
3609
|
-
this.log.error(
|
|
3645
|
+
this.log.error('Exception in callEnded event: ' + e);
|
|
3610
3646
|
}
|
|
3611
3647
|
}
|
|
3612
3648
|
|
|
@@ -3617,11 +3653,11 @@ class Session extends EventEmitter {
|
|
|
3617
3653
|
|
|
3618
3654
|
const self = this;
|
|
3619
3655
|
session.connection.ontrack = function (event) {
|
|
3620
|
-
self.log.debug(
|
|
3656
|
+
self.log.debug('Start media output (track):');
|
|
3621
3657
|
self.log.debug(event);
|
|
3622
3658
|
self._phone._remoteAudioElement.srcObject = event.streams[0];
|
|
3623
3659
|
};
|
|
3624
3660
|
}
|
|
3625
3661
|
}
|
|
3626
3662
|
|
|
3627
|
-
export { EventClient, InvalidMediaDevice, InvalidOptions, InvalidParameter, LOGLVL$1 as LOGLVL, LogHandler, MediaNotPrepared, NoActiveSession, NotAllowed, NotReady, Phone, Request, Requestor, Session, UnableToConnect, browserIsChrome, browserIsEdge, browserIsFirefox, browserIsOpera, browserIsSafari, compareObjects, createDurationString, createUUID };
|
|
3663
|
+
export { EventClient, EventServiceNotConnected, InvalidMediaDevice, InvalidOptions, InvalidParameter, LOGLVL$1 as LOGLVL, LogHandler, MediaNotPrepared, NoActiveSession, NotAllowed, NotReady, Phone, Request, Requestor, Session, UnableToConnect, browserIsChrome, browserIsEdge, browserIsFirefox, browserIsOpera, browserIsSafari, compareObjects, createDurationString, createUUID };
|