@jambonz/node-red-contrib-jambonz 2.4.19 → 2.4.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jambonz/node-red-contrib-jambonz",
3
- "version": "2.4.19",
3
+ "version": "2.4.21",
4
4
  "description": "Node-RED nodes for jambonz platform",
5
5
  "keywords": [
6
6
  "node-red"
@@ -17,6 +17,8 @@
17
17
  fromType: {value: ''},
18
18
  to: {value: '', required: true},
19
19
  toType: {value: ''},
20
+ trunk: {value: ''},
21
+ trunkType: {value: 'str'},
20
22
  dest: {value: 'phone', required: true},
21
23
  timeout: {validate: RED.validators.regex(/^\d*$/) },
22
24
  tag: {required: true},
@@ -66,9 +68,18 @@
66
68
  var destElem = $('#node-input-dest');
67
69
  var serverElem = $('#node-input-server');
68
70
  var applicationElem = $('#node-input-application');
71
+ var trunkDiv = $('#trunk');
69
72
  prepareTtsControls(node);
70
73
  prepareSttControls(node);
71
74
 
75
+ var onDestChanged = function () {
76
+ var selectedDest = destElem.find(':selected').val();
77
+ if ('phone' === selectedDest) trunkDiv.show();
78
+ else trunkDiv.hide();
79
+ }
80
+
81
+ destElem.change(onDestChanged);
82
+
72
83
  $('#node-input-from').typedInput({
73
84
  default: $('#node-input-fromType').val(),
74
85
  types: ['str','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
@@ -98,7 +109,11 @@
98
109
  types: ['json', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
99
110
  typeField: $('#node-input-tagType')
100
111
  });
101
-
112
+ $('#node-input-trunk').typedInput({
113
+ default: $('#node-input-trunkType').val(),
114
+ types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
115
+ typeField: $('#node-input-trunkType')
116
+ });
102
117
  var populateApplications = function() {
103
118
  var serverId = $('#node-input-server option:selected').val();
104
119
  $.ajax({
@@ -224,6 +239,11 @@
224
239
  <input type="text" id="node-input-to" placeholder="called party info">
225
240
  <input type="hidden" id="node-input-toType">
226
241
  </div>
242
+ <div class="form-row" id ="trunk">
243
+ <label for="node-input-trunk">Trunk</label>
244
+ <input type="text" id="node-input-trunk" placeholder="Specify the name of the trunk used for this outbound call">
245
+ <input type="hidden" id="node-input-trunkType">
246
+ </div>
227
247
  <div class="form-row">
228
248
  <label for="node-input-dest">Call type</label>
229
249
  <select id="node-input-dest">
@@ -71,6 +71,10 @@ module.exports = function(RED) {
71
71
 
72
72
  switch (config.dest) {
73
73
  case 'phone':
74
+ if (config.trunk) {
75
+ var trunk = new_resolve(RED, config.trunk, config.trunkType, node, msg);
76
+ opts.to.trunk = trunk;
77
+ }
74
78
  opts.to.number = to;
75
79
  break;
76
80
  case 'user':
@@ -16,6 +16,7 @@
16
16
  actionhook: {value: ''},
17
17
  actionhookType: {value: 'str'},
18
18
  answeronbridge: {value: false},
19
+ anchormedia: {value: false},
19
20
  callerid: {value: ''},
20
21
  calleridType: {value: ''},
21
22
  callername: {value: ''},
@@ -30,6 +31,8 @@
30
31
  referhookType: {value: 'str'},
31
32
  dtmfhook: {value: ''},
32
33
  dtmfhookType: {value: 'str'},
34
+ onholdhook: {value: ''},
35
+ onholdhookType: {value: 'str'},
33
36
  timelimit: {validate:RED.validators.regex(/^\d*$/) },
34
37
  timeout: {validate:RED.validators.regex(/^\d*$/) },
35
38
  listenurl: {value: ''},
@@ -106,6 +109,10 @@
106
109
  types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
107
110
  typeField: $('#node-input-dtmfhookType')
108
111
  });
112
+ $('#node-input-onholdhook').typedInput({
113
+ types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
114
+ typeField: $('#node-input-onholdhookType')
115
+ });
109
116
  $('#node-input-transcribeurl').typedInput({
110
117
  types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
111
118
  typeField: $('#node-input-transcribeurlType')
@@ -408,6 +415,10 @@
408
415
  <label for="node-input-answeronbridge">Answer on bridge</label>
409
416
  <input type="checkbox" id="node-input-answeronbridge">
410
417
  </div>
418
+ <div class="form-row">
419
+ <label for="node-input-anchormedia">Anchor media</label>
420
+ <input type="checkbox" id="node-input-anchormedia">
421
+ </div>
411
422
  <div class="form-row">
412
423
  <label for="node-input-callerid">Caller ID</label>
413
424
  <input type="text" id="node-input-callerid" placeholder="caller id to place on outbound call">
@@ -443,6 +454,11 @@
443
454
  <input type="text" id="node-input-dtmfhook" placeholder="webhook to call when dtmf is captured">
444
455
  <input type="hidden" id="node-input-dtmfhookType">
445
456
  </div>
457
+ <div class="form-row">
458
+ <label for="node-input-onholdhook">Onhold hook</label>
459
+ <input type="text" id="node-input-onholdhook" placeholder="webhook to call when call is on hold">
460
+ <input type="hidden" id="node-input-onholdhookType">
461
+ </div>
446
462
  <div class="form-row">
447
463
  <label for="node-input-timelimit">Time limit</label>
448
464
  <input type="text" id="node-input-timelimit" placeholder="max duration of call in secs">
package/src/nodes/dial.js CHANGED
@@ -54,6 +54,14 @@ module.exports = function(RED) {
54
54
  dtmfHook: new_resolve(RED, config.dtmfhook, config.dtmfhookType, node, msg),
55
55
  };
56
56
 
57
+ if (config.hasOwnProperty('anchormedia')) {
58
+ data.anchorMedia = config.anchormedia;
59
+ }
60
+
61
+ if (config.onholdhook) {
62
+ data.onHoldHook = new_resolve(RED, config.onholdhook, config.onholdhookType, node, msg);
63
+ }
64
+
57
65
  // headers
58
66
  var headers = {};
59
67
  config.headers.forEach(function(h) {
@@ -55,6 +55,15 @@
55
55
  siprecHeaders: {value: []},
56
56
  recordingID: {value: ''},
57
57
  recordingIDType: {value: 'str'},
58
+ dtmfDigit: {value: '', validate: function(v) {
59
+ const action = $('#node-input-action').val();
60
+ return action !== 'send_dtmf' || v.length > 0;
61
+ }},
62
+ dtmfDigitType: {value: 'str'},
63
+ dtmfDuration: {value: ''},
64
+ dtmfDurationType: {value: 'str'},
65
+ tag: {value: ''},
66
+ tagType: {value: 'str'},
58
67
  },
59
68
  inputs:1,
60
69
  outputs:1,
@@ -74,6 +83,8 @@
74
83
  var waitHookDiv = $('#wait-options');
75
84
  var recordDiv = $('#record-options');
76
85
  var sipRequestDiv = $('#sip-request-options');
86
+ var dtmfDiv = $('#dtmf-options');
87
+ var tagDiv = $('#tag-options');
77
88
 
78
89
  $('#node-input-callSid').typedInput({
79
90
  default: $('#node-input-callSidType').val(),
@@ -120,6 +131,21 @@
120
131
  types: ['str','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
121
132
  typeField: $('#node-input-recordingIDType')
122
133
  });
134
+ $('#node-input-dtmfDigit').typedInput({
135
+ default: $('#node-input-dtmfDigitType').val(),
136
+ types: ['str','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
137
+ typeField: $('#node-input-dtmfDigitType')
138
+ });
139
+ $('#node-input-dtmfDuration').typedInput({
140
+ default: $('#node-input-dtmfDurationType').val(),
141
+ types: ['str','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
142
+ typeField: $('#node-input-dtmfDurationType')
143
+ });
144
+ $('#node-input-tag').typedInput({
145
+ default: $('#node-input-tagType').val(),
146
+ types: ['json', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
147
+ typeField: $('#node-input-tagType')
148
+ });
123
149
 
124
150
  var onActionChanged = function () {
125
151
  var selectedAction = actionElem.find(':selected').val();
@@ -133,6 +159,10 @@
133
159
  else recordDiv.hide();
134
160
  if ('sip_request' === selectedAction) sipRequestDiv.show();
135
161
  else sipRequestDiv.hide();
162
+ if ('send_dtmf' === selectedAction) dtmfDiv.show();
163
+ else dtmfDiv.hide();
164
+ if ('tag' === selectedAction) tagDiv.show();
165
+ else tagDiv.hide();
136
166
  }
137
167
 
138
168
  var onVendorChanged = function() {
@@ -299,6 +329,8 @@
299
329
  <option value="stop_call_recording">stop call recording</option>
300
330
  <option value="pause_call_recording">pause call recording</option>
301
331
  <option value="resume_call_recording">resume call recording</option>
332
+ <option value="send_dtmf">send RFC 2833 DTMF</option>
333
+ <option value="tag">tag call</option>
302
334
  </select>
303
335
  </div>
304
336
  <div id="say-options">
@@ -394,6 +426,25 @@
394
426
  <input type="hidden" id="node-input-sipRequestHeadersType">
395
427
  </div>
396
428
  </div>
429
+ <div id="dtmf-options">
430
+ <div class="form-row">
431
+ <label for="node-input-dtmfDigit">DTMF digit</label>
432
+ <input type="text" id="node-input-dtmfDigit">
433
+ <input type="hidden" id="node-input-dtmfDigitType">
434
+ </div>
435
+ <div class="form-row">
436
+ <label for="node-input-dtmfDuration">DTMF duration</label>
437
+ <input type="text" id="node-input-dtmfDuration" placeholder="250">
438
+ <input type="hidden" id="node-input-dtmfDurationType">
439
+ </div>
440
+ </div>
441
+ <div id="tag-options">
442
+ <div class="form-row">
443
+ <label for="node-input-tag">Data</label>
444
+ <input type="text" id="node-input-tag" placeholder="data object">
445
+ <input type="hidden" id="node-input-tagType">
446
+ </div>
447
+ </div>
397
448
  </script>
398
449
 
399
450
  <!-- Help Text -->
package/src/nodes/lcc.js CHANGED
@@ -113,6 +113,15 @@ function lcc(config) {
113
113
  case 'resume_call_recording':
114
114
  opts.record = { action: 'resumeCallRecording' };
115
115
  break;
116
+ case 'send_dtmf':
117
+ opts.dtmf = {
118
+ digit: new_resolve(RED, config.dtmfDigit, config.dtmfDigitType, node, msg),
119
+ duration: new_resolve(RED, config.dtmfDuration, config.dtmfDurationType, node, msg) || '250'
120
+ };
121
+ break;
122
+ case 'tag':
123
+ opts.tag = new_resolve(RED, config.tag, config.tagType, node, msg);
124
+ break;
116
125
  default:
117
126
  node.log(`invalid action: ${config.action}`);
118
127
  send(msg);
package/src/nodes/libs.js CHANGED
@@ -127,7 +127,11 @@ exports.appendVerb = (msg, obj) => {
127
127
  error.statusText = response.statusText;
128
128
  throw error;
129
129
  }
130
- return response.json();
130
+ const contentType = response.headers.get('content-type');
131
+ if (contentType && contentType.indexOf('application/json') !== -1) {
132
+ return response.json();
133
+ }
134
+ return response.text();
131
135
  }
132
136
 
133
137
  exports.doCreateCall = async (node, baseUrl, accountSid, apiToken, opts) => {
@@ -14,7 +14,9 @@
14
14
  status: {},
15
15
  statusType: {value: 'num'},
16
16
  reason: {value: ''},
17
- reasonType: {value: 'str'}
17
+ reasonType: {value: 'str'},
18
+ headers: {value: ''},
19
+ headersType: {value: 'json'},
18
20
  },
19
21
  inputs:1,
20
22
  outputs:1,
@@ -31,6 +33,11 @@
31
33
  types: ['str','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
32
34
  typeField: $('#node-input-reasonType')
33
35
  });
36
+ $('#node-input-headers').typedInput({
37
+ default: $('#node-input-headersType').val(),
38
+ types: ['json','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
39
+ typeField: $('#node-input-headersType')
40
+ });
34
41
  }
35
42
  });
36
43
  </script>
@@ -51,6 +58,11 @@
51
58
  <input type="text" id="node-input-reason" placeholder="sip reason">
52
59
  <input type="hidden" id="node-input-reasonType">
53
60
  </div>
61
+ <div class="form-row">
62
+ <label for="node-input-headers">Headers</label>
63
+ <input type="text" id="node-input-headers" placeholder="SIP headers">
64
+ <input type="hidden" id="node-input-headersType">
65
+ </div>
54
66
  </script>
55
67
 
56
68
  <!-- Help Text -->
@@ -62,6 +74,8 @@
62
74
  <dd>A valid SIP status code in the range 4XX - 6XX</dd>
63
75
  <dt>Reason<span class="property-type">string</span></dt>
64
76
  <dd>A brief description</dd>
77
+ <dt>Headers<span class="property-type">object</span></dt>
78
+ <dd>Additional SIP headers to include in the response</dd>
65
79
  </dl>
66
80
 
67
81
  <h3>Outputs</h3>
@@ -8,11 +8,16 @@ module.exports = function(RED) {
8
8
  node.on('input', function(msg) {
9
9
  var status = new_resolve(RED, config.status, config.statusType, node, msg);
10
10
  var reason = new_resolve(RED, config.reason, config.reasonType, node, msg);
11
- appendVerb(msg, {
11
+ var obj = {
12
12
  verb: 'sip:decline',
13
13
  status: parseInt(status),
14
14
  reason
15
- });
15
+ }
16
+ config.headers ? obj.headers = new_resolve(RED, config.headers, config.headersType, node, msg) : null
17
+ if (typeof obj.headers == 'string'){
18
+ obj.headers = JSON.parse(obj.headers)
19
+ }
20
+ appendVerb(msg, obj);
16
21
  node.send(msg);
17
22
  });
18
23
  }