@babblevoice/babble-drachtio-callmanager 2.0.2 → 2.0.3

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/.eslintrc.js ADDED
@@ -0,0 +1,67 @@
1
+ module.exports = {
2
+ "env": {
3
+ "browser": false,
4
+ "node": true,
5
+ "mocha": true,
6
+ "commonjs": true,
7
+ "es2021": true
8
+ },
9
+ "extends": [ "plugin:promise/recommended", "eslint:recommended" ],
10
+ "overrides": [
11
+ ],
12
+ "parserOptions": {
13
+ "ecmaVersion": "latest"
14
+ },
15
+ "ignorePatterns": [ "out" ],
16
+ "rules": {
17
+ "require-atomic-updates": [
18
+ "error"
19
+ ],
20
+ "no-invalid-this": [
21
+ "error"
22
+ ],
23
+ "no-useless-call": [
24
+ "error"
25
+ ],
26
+ "no-useless-return": [
27
+ "error"
28
+ ],
29
+ "no-var": [
30
+ "error"
31
+ ],
32
+ "prefer-const": [
33
+ "error"
34
+ ],
35
+ "complexity": [
36
+ "error",
37
+ 10
38
+ ],
39
+ "max-depth": [
40
+ "error",
41
+ 5
42
+ ],
43
+ "no-eval": [
44
+ "error"
45
+ ],
46
+ "indent": [
47
+ "error",
48
+ 2
49
+ ],
50
+ "linebreak-style": [
51
+ "error",
52
+ "unix"
53
+ ],
54
+ "quotes": [
55
+ "error",
56
+ "double"
57
+ ],
58
+ "semi": [
59
+ "error",
60
+ "never"
61
+ ],
62
+ "yoda": [
63
+ "error",
64
+ "always"
65
+ ]
66
+ }
67
+ }
package/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
 
2
- const assert = require( "assert" )
3
2
  const callmanager = require( "./lib/callmanager.js" )
4
3
  const store = require( "./lib/store.js" )
5
4
 
@@ -9,7 +8,7 @@ const default_options = {
9
8
  "preferedcodecs": "g722 ilbc pcmu pcma",
10
9
  //"transcode": true, - this never made it into the software - TODO
11
10
  "uactimeout": 30000, /* timeout when calling a client */
12
- "seexpire": 120000, /* session expires timeout */
11
+ "seexpire": 120000, /* session expires timeout mS */
13
12
  "rfc2833": true, /* Enable RFC 2833 - DTMF */
14
13
  "late": false, /* Late negotiation */
15
14
  "registrar": false, /* our registrar object or falsey */
@@ -21,7 +20,7 @@ const default_options = {
21
20
  @returns { callmanager }
22
21
  */
23
22
  module.exports.callmanager = ( options ) => {
24
- let ouroptions = { ...default_options, ...options }
23
+ const ouroptions = { ...default_options, ...options }
25
24
  return callmanager.callmanager( ouroptions )
26
25
  }
27
26
 
@@ -43,4 +42,4 @@ module.exports.store = store
43
42
  /**
44
43
  * Call
45
44
  */
46
- module.exports.call = callmanager.call
45
+ module.exports.call = callmanager.call
package/lib/call.js CHANGED
@@ -66,17 +66,13 @@ const inboundsiperros = {
66
66
  404: hangupcodes.UNALLOCATED_NUMBER
67
67
  }
68
68
 
69
- var callmanager = {
69
+ let callmanager = {
70
70
  "options": {}
71
71
  }
72
72
 
73
73
  /** @class */
74
74
  class call {
75
75
 
76
- #state = {
77
- "waitingforevent": false
78
- }
79
-
80
76
  /**
81
77
  Construct our call object with all defaults, including a default UUID.
82
78
  @constructs call
@@ -1459,7 +1455,7 @@ class call {
1459
1455
  /**
1460
1456
  Private helper function to add events to our RTP channel.
1461
1457
  @param {object} e - the rtp event
1462
- @internal
1458
+ @private
1463
1459
  */
1464
1460
  _handlechannelevents( e ) {
1465
1461
 
@@ -1489,16 +1485,6 @@ class call {
1489
1485
  this._tevent( e.event )
1490
1486
  }
1491
1487
 
1492
- if( !this._promises.resolve.channelevent ) {
1493
- this.#state.waitingforevent = e
1494
- return
1495
- }
1496
-
1497
- this.#matchandthrow( e )
1498
-
1499
- }
1500
-
1501
- #matchandthrow( e ) {
1502
1488
  if( this._eventconstraints ) {
1503
1489
  let constraintkeys = Object.keys( this._eventconstraints )
1504
1490
  for( const k of constraintkeys ) {
@@ -1506,11 +1492,11 @@ class call {
1506
1492
  if( "object" === typeof this._eventconstraints[ k ] ) {
1507
1493
  /* regex */
1508
1494
  if( !e[ k ].match( this._eventconstraints[ k ] ) ) {
1509
- return false
1495
+ return
1510
1496
  }
1511
1497
  } else if( this._eventconstraints[ k ] != e[ k ] ) {
1512
1498
  /* not a match */
1513
- return false
1499
+ return
1514
1500
  }
1515
1501
  }
1516
1502
  }
@@ -1527,7 +1513,7 @@ class call {
1527
1513
  this._promises.resolve.channelevent = false
1528
1514
  this._promises.promise.channelevent = false
1529
1515
  if( r ) r( e )
1530
- return true
1516
+
1531
1517
  }
1532
1518
 
1533
1519
  /**
@@ -1540,7 +1526,6 @@ class call {
1540
1526
  A telephone event will resolve this promise as we typically need speech to be interupted
1541
1527
  by the user. Note, peeking a telephone-event (i.e. DTMF) will not clear it like waitfortelevents will.
1542
1528
  @param { regex } constraints - event to filter for from our RTP server - excluding DTMF events - these will always return
1543
- @returns { Promise< object > }
1544
1529
  */
1545
1530
  waitforanyevent( constraints, timeout = 500 ) {
1546
1531
 
@@ -1560,11 +1545,6 @@ class call {
1560
1545
  if( r ) r( "timeout" )
1561
1546
  }, timeout * 1000 )
1562
1547
 
1563
- if( this.#state.waitingforevent ) {
1564
- this.#matchandthrow( this.#state.waitingforevent )
1565
- }
1566
- this.#state.waitingforevent = false
1567
-
1568
1548
  return this._promises.promise.channelevent
1569
1549
  }
1570
1550
 
@@ -37,9 +37,9 @@ class callmanager {
37
37
  @private
38
38
  */
39
39
  async _oninvite( req, res, next ) {
40
- if( req.msg.method !== "INVITE" ) return next()
40
+ if( "INVITE" !== req.msg.method ) return next()
41
41
 
42
- let calldesc = {
42
+ const calldesc = {
43
43
  "callid": req.getParsedHeader( "call-id" ),
44
44
  "tags": {
45
45
  "remote": req.getParsedHeader( "from" ).params.tag,
@@ -97,7 +97,7 @@ Is configured to use drachtio and registers emitter for presence.
97
97
  */
98
98
  let cm = false
99
99
  module.exports.callmanager = ( options ) => {
100
- if( cm !== false ) return cm
100
+ if( false !== cm ) return cm
101
101
 
102
102
  assert( undefined !== options.srf )
103
103
  cm = new callmanager( options )
package/lib/sdp.js CHANGED
@@ -6,7 +6,7 @@ const crypto = require( "crypto" )
6
6
  /*
7
7
  An SDP Generator.
8
8
  */
9
- var sessionidcounter = Math.floor( Math.random() * 100000 )
9
+ let sessionidcounter = Math.floor( Math.random() * 100000 )
10
10
 
11
11
  const codecconv = {
12
12
  "0": "pcmu",
@@ -34,27 +34,27 @@ const codecdefs = {
34
34
  "rtp": {
35
35
  "pcmu": {
36
36
  payload: 0,
37
- codec: 'PCMU',
37
+ codec: "PCMU",
38
38
  rate: 8000
39
39
  },
40
40
  "pcma": {
41
41
  payload: 8,
42
- codec: 'PCMA',
42
+ codec: "PCMA",
43
43
  rate: 8000
44
44
  },
45
45
  "g722": {
46
46
  payload: 9,
47
- codec: 'G722',
47
+ codec: "G722",
48
48
  rate: 8000
49
49
  },
50
50
  "ilbc": {
51
51
  payload: 97,
52
- codec: 'ilbc',
52
+ codec: "ilbc",
53
53
  rate: 8000
54
54
  },
55
55
  "2833": {
56
56
  payload: 101,
57
- codec: 'telephone-event/8000'
57
+ codec: "telephone-event/8000"
58
58
  }
59
59
  },
60
60
  "fmtp": {
@@ -80,6 +80,44 @@ const defaultaudiomedia = {
80
80
  "direction": "sendrecv"
81
81
  }
82
82
 
83
+ /**
84
+ * Takes a mixed input and outputs an array in the form [ "pcmu", "pcma" ]
85
+ * @param { string | array<string|number> } codecarray
86
+ * @return { array< string >}
87
+ */
88
+ function alltocodecname( codecarray ) {
89
+
90
+ /* check and convert to array */
91
+ if ( "string" === typeof codecarray ) {
92
+ codecarray = codecarray.split( /[ ,]+/ )
93
+ }
94
+
95
+ /* convert to payloads */
96
+ const retval = []
97
+ for( const oin of codecarray ) {
98
+ if( undefined != codecrconv[ oin ] ) {
99
+ retval.push( oin )
100
+ } else if( codecconv[ oin ] ) {
101
+ retval.push( codecconv[ oin ] )
102
+ }
103
+ }
104
+
105
+ return retval
106
+ }
107
+
108
+ /**
109
+ *
110
+ * @param { object } audio
111
+ * @param { number } pt
112
+ * @returns { string }
113
+ */
114
+ function getconfigforpt( audio, pt ) {
115
+ for( const fmtp of audio.fmtp ) {
116
+ if( pt == fmtp.payload ) return fmtp.config
117
+ }
118
+ return ""
119
+ }
120
+
83
121
  class sdp {
84
122
 
85
123
  constructor( sdp ) {
@@ -90,14 +128,14 @@ class sdp {
90
128
  this.sdp = {
91
129
  version: 0,
92
130
  origin: {
93
- username: '-',
131
+ username: "-",
94
132
  sessionId: sessionidcounter,
95
133
  sessionVersion: 0,
96
- netType: 'IN',
134
+ netType: "IN",
97
135
  ipVer: 4,
98
136
  address: "127.0.0.1"
99
137
  },
100
- name: 'project',
138
+ name: "project",
101
139
  timing: {
102
140
  start: 0,
103
141
  stop: 0
@@ -130,7 +168,7 @@ class sdp {
130
168
  this.sdp.media.forEach( ( media, i, a ) => {
131
169
 
132
170
  if ( "audio" === media.type ) {
133
- if ( typeof media.payloads === "string" ) {
171
+ if ( "string" === typeof media.payloads ) {
134
172
  media.payloads = media.payloads.split( /[ ,]+/ )
135
173
  }
136
174
 
@@ -149,7 +187,7 @@ class sdp {
149
187
  Ideally we replicate the object required for target in our RTP service.
150
188
  */
151
189
  getaudio() {
152
- let m = this.sdp.media.find( mo => "audio" === mo.type )
190
+ const m = this.sdp.media.find( mo => "audio" === mo.type )
153
191
 
154
192
  if ( m ) {
155
193
 
@@ -270,11 +308,11 @@ class sdp {
270
308
  }
271
309
 
272
310
  codecarr.forEach( codec => {
273
- let codecn = codecrconv[ codec ]
274
- let def = codecdefs.rtp[ codec ]
311
+ const codecn = codecrconv[ codec ]
312
+ const def = codecdefs.rtp[ codec ]
275
313
  if ( undefined !== def ) {
276
314
  /* suported audio */
277
- let m = this.getmedia( codecdefs.type[ codec ] )
315
+ const m = this.getmedia( codecdefs.type[ codec ] )
278
316
 
279
317
  /* Don't allow duplicates */
280
318
  if( m.payloads.includes( codecn ) ) return
@@ -303,7 +341,7 @@ class sdp {
303
341
  }
304
342
 
305
343
  for( const m of this.sdp.media ) {
306
- let mmsid = crypto.randomBytes( 16 ).toString( "hex" )
344
+ const mmsid = crypto.randomBytes( 16 ).toString( "hex" )
307
345
  m.ssrcs = [
308
346
  { "id": ssrc, "attribute": "cname", "value": crypto.randomBytes( 16 ).toString( "hex" ) },
309
347
  { "id": ssrc, "attribute": "msid", "value": this.sdp.msidSemantic.token + " " + mmsid },
@@ -351,7 +389,7 @@ class sdp {
351
389
  "port": port,
352
390
  "type": "host",
353
391
  "generation": 0
354
- }
392
+ }
355
393
  ]
356
394
 
357
395
  m.iceUfrag = crypto.randomBytes( 8 ).toString( "hex" )
@@ -361,7 +399,7 @@ class sdp {
361
399
  return this
362
400
  }
363
401
 
364
- rtcpmux( v = true ) {
402
+ rtcpmux() {
365
403
  for( const m of this.sdp.media ) {
366
404
  m.rtcpMux = "rtcp-mux"
367
405
  }
@@ -395,43 +433,37 @@ class sdp {
395
433
  If first ony, it only returns the first match
396
434
  */
397
435
  intersection( other, firstonly = false ) {
398
- if ( typeof other === "string" ) {
399
- other = other.split( /[ ,]+/ )
436
+
437
+ /* ensure other side is on the format [ "pcma", "pcmu" ] */
438
+ other = alltocodecname( other )
439
+
440
+ let audio = []
441
+ for( const m of this.sdp.media ) {
442
+ if( "audio" == m.type ) audio = m
400
443
  }
401
444
 
402
- /* convert to payloads */
403
- other.forEach( ( codec, i, a ) => {
404
- if ( isNaN( codec ) ) {
405
- a[ i ] = codecrconv[ codec ]
445
+ /* work out an array of codecs on this side in the format of [ "pcma", "pcmu" ] */
446
+ const ourcodecs = []
447
+ for( const pt of audio.payloads ) {
448
+ if( undefined === codecconv[ pt ] ) continue
449
+ if( 97 == pt ) {
450
+ if( -1 == getconfigforpt( audio, 97 ).indexOf( "mode=30" ) )
451
+ ourcodecs.push( codecconv[ pt ] )
452
+ } else {
453
+ ourcodecs.push( codecconv[ pt ] )
406
454
  }
407
- } )
408
-
409
- /* Does it exist in payloads and fmtp where required */
410
- let retval = []
411
- this.sdp.media.forEach( m => {
412
- retval = retval.concat( other.filter( pl => {
413
- if ( m.payloads.includes( pl ) ) {
414
- let codecname = codecconv[ pl ]
415
- if ( undefined === codecdefs.fmtp[ codecname ] ) return true
455
+ }
416
456
 
417
- let fmtp = codecdefs.fmtp[ codecname ] /* i.e. { payload: 97, config: "mode=20" } */
418
- if ( undefined !== m.fmtp.find( f => f.payload == fmtp.payload && f.config == fmtp.config ) ) return true
419
- }
420
- return false
421
- } ) )
422
- } )
457
+ /* intersection */
458
+ let retval = other.filter( value => ourcodecs.includes( value ) )
423
459
 
424
- if ( firstonly && retval.length > 0 ) {
460
+ /* If fisrt only - i.e. select codec */
461
+ if ( firstonly && 0 < retval.length ) {
425
462
  retval = [ retval[ 0 ] ]
426
463
  this.select( retval[ 0 ] )
427
464
  }
428
465
 
429
- /* We want named codecs */
430
- retval.forEach( ( codec, i, a ) => {
431
- if ( undefined != codecconv[ codec ] ) a[ i ] = codecconv[ codec ]
432
- } )
433
-
434
- let full = retval.join( " " )
466
+ const full = retval.join( " " )
435
467
  if( !full ) return false
436
468
 
437
469
  return full
@@ -440,7 +472,7 @@ class sdp {
440
472
  toString() {
441
473
 
442
474
  /* We need to convert payloads back to string to stop a , being added */
443
- let co = Object.assign( this.sdp )
475
+ const co = Object.assign( this.sdp )
444
476
 
445
477
  co.media.forEach( ( media, i, a ) => {
446
478
  if( Array.isArray( media.payloads ) ) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babblevoice/babble-drachtio-callmanager",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "Call processing to create a PBX",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -21,14 +21,18 @@
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
23
  "@babblevoice/babble-drachtio-auth": "^1.0.2",
24
- "chai": "^4.3.4",
25
24
  "drachtio-srf": "^4.4.47",
26
- "mocha": "^9.2.2",
27
25
  "uuid": "^8.3.2"
28
26
  },
29
27
  "optionalDependencies": {
30
28
  "@babblevoice/projectrtp": "^2.3.7"
31
29
  },
30
+ "devDependencies": {
31
+ "chai": "^4.3.4",
32
+ "eslint": "^8.42.0",
33
+ "eslint-plugin-promise": "^6.1.1",
34
+ "mocha": "^9.2.2"
35
+ },
32
36
  "bugs": {
33
37
  "url": "https://github.com/tinpotnick/babble-drachtio-callmanager/issues"
34
38
  },