@telegenta/webclient 3.3.0 → 3.4.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/README.md CHANGED
@@ -50,7 +50,8 @@ remembered there, so you will be prompted for the microphone every time.
50
50
  #### Example: phone with a provisioning token
51
51
 
52
52
  `demos/phone_client.html` is a complete softphone - start/stop, outbound calls, transfer, listen-in,
53
- incoming calls and the event client.
53
+ incoming calls and the event client. A hosted copy is published with the docs:
54
+ https://www.telegenta.com/docs/webclient/latest/demos/phone_client.html
54
55
 
55
56
  First create a static API token in the admin interface
56
57
  (https://admin.telegenta.com/integration/access-tokens). Open the demo and paste the token in; it then
@@ -116,7 +116,10 @@
116
116
 
117
117
  <!-- Javascript stuff -->
118
118
  <script>
119
- const API_URL = 'https://api.telegenta.com/api';
119
+ // The demo is published with the docs on both www.telegenta.com and www.telegenta-test.com
120
+ const API_URL = location.hostname.endsWith('telegenta-test.com')
121
+ ? 'https://api.telegenta-test.com/api'
122
+ : 'https://api.telegenta.com/api';
120
123
  let phoneClient = null;
121
124
  let logger;
122
125
  let transferSession;
@@ -245,6 +248,9 @@
245
248
 
246
249
  // Enable call initialized tone
247
250
  playCallInitializedTone: true,
251
+
252
+ // Enable call ended tone
253
+ playCallEndedTone: true,
248
254
  };
249
255
 
250
256
  try {
package/dist/telegenta.js CHANGED
@@ -1567,6 +1567,7 @@ const options = {
1567
1567
  autoRemoveSessionTime: 3000,
1568
1568
  playRingOnIncoming: true,
1569
1569
  playCallInitializedTone: false,
1570
+ playCallEndedTone: false,
1570
1571
  };
1571
1572
 
1572
1573
  // DTMF Frequency table
@@ -1603,6 +1604,11 @@ const ST_CALLINIT = [
1603
1604
  { d: 200, f1: 500, f2: 0, g: 0 },
1604
1605
  { d: 200, f1: 700, f2: 0, g: 5000 },
1605
1606
  ];
1607
+ const ST_CALLENDED = [
1608
+ { d: 150, f1: 600, f2: 0, g: 50 },
1609
+ { d: 150, f1: 500, f2: 0, g: 50 },
1610
+ { d: 150, f1: 400, f2: 0, g: 0 },
1611
+ ];
1606
1612
  const ST_RINGBACK = [{ d: 1000, f1: 425, f2: 0, g: 4000 }];
1607
1613
  const ST_INCOMING = [
1608
1614
  { d: 150, f1: 400, f2: 0, g: 0 },
@@ -1783,6 +1789,7 @@ class Phone extends EventEmitter {
1783
1789
  * @param {number} [phoneOptions.autoRemoveSessionTime=3000] Automatically remove session after interval specified
1784
1790
  * @param {number} [phoneOptions.playRingOnIncoming=true] Play ringing tones on incoming calls
1785
1791
  * @param {number} [phoneOptions.playCallInitializedTone=false] Play tone when call has been initiated (before progress/ringback tones)
1792
+ * @param {boolean} [phoneOptions.playCallEndedTone=false] Play tone once when an answered call ends without the agent hanging up (remote hangup, server hangup, connection loss, timeouts)
1786
1793
  *
1787
1794
  * @throws MediaNotPrepared - If {@link prepareMedia} was not called and resolved prior to calling this method
1788
1795
  * @throws InvalidOptions - If one or more options are invalid
@@ -2218,14 +2225,18 @@ class Phone extends EventEmitter {
2218
2225
 
2219
2226
  // Set timer to stop tone again
2220
2227
  const self = this;
2221
- this._toneTimer = setTimeout(function () {
2222
- self.stopTone();
2228
+ const timer = setTimeout(function () {
2229
+ // A later playTone() has taken over the oscillators; don't silence it or cancel its timer
2230
+ if (self._toneTimer === timer) {
2231
+ self.stopTone();
2232
+ }
2223
2233
 
2224
2234
  // Handle callback now
2225
2235
  if (callback) {
2226
2236
  callback();
2227
2237
  }
2228
2238
  }, duration);
2239
+ this._toneTimer = timer;
2229
2240
  }
2230
2241
 
2231
2242
  /**
@@ -2268,6 +2279,18 @@ class Phone extends EventEmitter {
2268
2279
  }
2269
2280
  }
2270
2281
 
2282
+ // Play a tone sequence once. A repeat tone starting meanwhile (e.g. a new call's ringing) cancels the rest.
2283
+ _playToneSequence(sequence, pos = 0) {
2284
+ const toneDef = sequence[pos];
2285
+ if (!toneDef || this._repeatToneActive) {
2286
+ return;
2287
+ }
2288
+ const self = this;
2289
+ this.playTone(toneDef.d, toneDef.f1, toneDef.f2, function () {
2290
+ setTimeout(self._playToneSequence.bind(self, sequence, pos + 1), toneDef.g);
2291
+ });
2292
+ }
2293
+
2271
2294
  // End repeating tones
2272
2295
  _endRepeatTone() {
2273
2296
  if (this._repeatToneActive) {
@@ -2597,6 +2620,8 @@ class Session extends EventEmitter {
2597
2620
  this._isListenCall = false;
2598
2621
  this._whisperMode = null;
2599
2622
  this._manualDisposition = null;
2623
+ this._transferInitiated = false;
2624
+ this._voicemailDrop = false;
2600
2625
 
2601
2626
  // Quality report
2602
2627
  this._rtcpStatsTimer = null;
@@ -2863,10 +2888,18 @@ class Session extends EventEmitter {
2863
2888
  self._setSessionState('ACTIVE');
2864
2889
  });
2865
2890
 
2866
- this._webRTCSession.on('ended', function (/* data */) {
2891
+ this._webRTCSession.on('ended', function (data) {
2867
2892
  // End any ringsbacks if needed
2868
2893
  self._phone._endRepeatTone();
2869
2894
 
2895
+ // jssip fires 'ended' once a call is answered. Its own errors (connection loss, timeouts, ICE failure)
2896
+ // also end as 'local' but with their own cause, so only a local BYE is the agent hanging up.
2897
+ const agentHangup = data.originator === 'local' && data.cause === JsSIP.C.causes.BYE;
2898
+ const agentEnded = agentHangup || self._transferInitiated || self._voicemailDrop;
2899
+ if (self._phone.options.playCallEndedTone && !agentEnded) {
2900
+ self._phone._playToneSequence(ST_CALLENDED);
2901
+ }
2902
+
2870
2903
  self._callEndTime = Date.now();
2871
2904
 
2872
2905
  // Clear answer call timeout timer if needed
@@ -3308,6 +3341,10 @@ class Session extends EventEmitter {
3308
3341
  transfer(dstSession) {
3309
3342
  this.log.info(`Transferring ${this.uuid} => ${dstSession.uuid}`);
3310
3343
 
3344
+ // The server hangs up both legs once the transfer completes - that's the agent's doing, no call-ended tone
3345
+ this._transferInitiated = true;
3346
+ dstSession._transferInitiated = true;
3347
+
3311
3348
  // Create ReferSubscriber event class
3312
3349
  const referSubscriber = this._webRTCSession.refer(this._webRTCSession._request.ruri, {
3313
3350
  replaces: dstSession._webRTCSession,
@@ -3322,10 +3359,15 @@ class Session extends EventEmitter {
3322
3359
 
3323
3360
  referSubscriber.on('requestFailed', function (cause) {
3324
3361
  self.log.error(`Transfer rejected: ${cause}`);
3362
+ // Legs stay up, so a later hangup by the other party should get the cue
3363
+ self._transferInitiated = false;
3364
+ dstSession._transferInitiated = false;
3325
3365
  });
3326
3366
 
3327
3367
  referSubscriber.on('failed', function (cause) {
3328
3368
  self.log.error(`Transfer failed: ${cause}`);
3369
+ self._transferInitiated = false;
3370
+ dstSession._transferInitiated = false;
3329
3371
  });
3330
3372
  }
3331
3373
 
@@ -3356,6 +3398,9 @@ class Session extends EventEmitter {
3356
3398
 
3357
3399
  // When playback is acknowledged and 'hangup_after' is true, we set session state into PLAYBACK
3358
3400
  if (hangup_after) {
3401
+ // The server hangs up after the voicemail drop - that's the agent's doing, no call-ended tone
3402
+ this._voicemailDrop = true;
3403
+
3359
3404
  // Stop remote audio
3360
3405
  this._phone._remoteAudioElement.srcObject = null;
3361
3406
 
@@ -3363,10 +3408,16 @@ class Session extends EventEmitter {
3363
3408
  this._webRTCSession.mute();
3364
3409
 
3365
3410
  // When playback starts set state to playback
3366
- p.then(() => {
3367
- // Set session state
3368
- this._setSessionState('PLAYBACK');
3369
- });
3411
+ p.then(
3412
+ () => {
3413
+ // Set session state
3414
+ this._setSessionState('PLAYBACK');
3415
+ },
3416
+ () => {
3417
+ // Call stays up, so a later hangup by the other party should get the cue
3418
+ this._voicemailDrop = false;
3419
+ }
3420
+ );
3370
3421
  }
3371
3422
 
3372
3423
  return p;