@jambonz/node-red-contrib-jambonz 2.3.8 → 2.3.10

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.3.8",
3
+ "version": "2.3.10",
4
4
  "description": "Node-RED nodes for jambonz platform",
5
5
  "keywords": [
6
6
  "node-red"
@@ -7,6 +7,8 @@
7
7
  name: {value: ''},
8
8
  queue: {required: true, value: ''},
9
9
  queueType: {value: 'str'},
10
+ callSid: {value: ''},
11
+ callSidType: {value: 'str'},
10
12
  actionHook: {},
11
13
  actionHookType: {value: 'str'},
12
14
  confirmHook: {},
@@ -26,6 +28,10 @@
26
28
  types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env'],
27
29
  typeField: $('#node-input-queueType')
28
30
  });
31
+ $('#node-input-callSid').typedInput({
32
+ types: ['str', 'msg', 'flow', 'global'],
33
+ typeField: $('#node-input-callSidType')
34
+ });
29
35
  $('#node-input-actionHook').typedInput({
30
36
  types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env'],
31
37
  typeField: $('#node-input-actionHookType')
@@ -53,6 +59,11 @@
53
59
  <input type="text" id="node-input-queue" placeholder="name of queue to remove caller from">
54
60
  <input type="hidden" id="node-input-queueType">
55
61
  </div>
62
+ <div class="form-row">
63
+ <label for="node-input-callSid">Call Sid</label>
64
+ <input type="text" id="node-input-callSid" placeholder="sid of the call">
65
+ <input type="hidden" id="node-input-callSidType">
66
+ </div>
56
67
  <div class="form-row">
57
68
  <label for="node-input-beep">Play beep on connecting</label>
58
69
  <input type="checkbox" id="node-input-beep">
@@ -79,6 +90,7 @@
79
90
  <p>removes the a call from the front of a queue and bridges that call to the current caller.</p>
80
91
  <h3>Properties</h3>
81
92
  <p><code>Queue name</code> - Name of the queue.</p>
93
+ <p><code>Call Sid</code> - An optional parameter to specify which call to dequeue.</p>
82
94
  <p><code>Play beep on connecting</code> - Play a beep tone to this caller only just prior to connecting the queued call.</p>
83
95
  <p><code>Action hook</code> - A webhook invoke when call ends.</p>
84
96
  <p><code>Confirm hook</code> - A webhook for an application to run on the callee's end before the call is bridged.</p>
@@ -9,6 +9,7 @@ module.exports = function(RED) {
9
9
  appendVerb(msg, {
10
10
  verb: 'dequeue',
11
11
  name: v_resolve(config.queue, config.queueType, this.context(), msg),
12
+ callSid: v_resolve(config.callSid, config.callSidType, this.context(), msg),
12
13
  beep: config.beep,
13
14
  actionHook: v_resolve(config.actionHook, config.actionHookType, this.context(), msg),
14
15
  confirmHook: v_resolve(config.confirmHook, config.confirmHookType, this.context(), msg),
@@ -7,6 +7,8 @@
7
7
  name: {value: ''},
8
8
  queue: {required: true, value: ''},
9
9
  queueType: {value: 'str'},
10
+ priority: {value: 999},
11
+ priorityType: {value: 'num'},
10
12
  actionHook: {},
11
13
  actionHookType: {value: 'str'},
12
14
  waitHook: {},
@@ -23,6 +25,10 @@
23
25
  types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env'],
24
26
  typeField: $('#node-input-queueType')
25
27
  });
28
+ $('#node-input-priority').typedInput({
29
+ types: ['num', 'msg', 'flow', 'global'],
30
+ typeField: $('#node-input-priorityType')
31
+ });
26
32
  $('#node-input-actionHook').typedInput({
27
33
  types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env'],
28
34
  typeField: $('#node-input-actionHookType')
@@ -46,6 +52,11 @@
46
52
  <input type="text" id="node-input-queue" placeholder="name of queue">
47
53
  <input type="hidden" id="node-input-queueType">
48
54
  </div>
55
+ <div class="form-row">
56
+ <label for="node-input-priority">Priority</label>
57
+ <input type="text" id="node-input-priority" placeholder="caller priority">
58
+ <input type="hidden" id="node-input-priorityType">
59
+ </div>
49
60
  <div class="form-row">
50
61
  <label for="node-input-actionHook">Action hook</label>
51
62
  <input type="text" id="node-input-actionHook" placeholder="webhook url">
@@ -64,6 +75,8 @@
64
75
  <h3>Properties</h3>
65
76
  <p><code>Queue name</code> -
66
77
  The name of the queue</p>
78
+ <p><code>Priority</code> -
79
+ The priority for which the call will be added to the queue. Must be a non-negative value and defaults to 999 (low-priority) if not set.</p>
67
80
  <p><code>Action hook</code> -
68
81
  A webhook invoke when operation completes.</p>
69
82
  <p><code>Wait hook</code> -
@@ -6,9 +6,12 @@ module.exports = function(RED) {
6
6
  RED.nodes.createNode(this, config);
7
7
  var node = this;
8
8
  node.on('input', function(msg, send, done) {
9
+ const priorityValue = v_resolve(config.priority, config.priorityType, this.context(), msg);
10
+
9
11
  appendVerb(msg, {
10
12
  verb: 'enqueue',
11
13
  name: v_resolve(config.queue, config.queueType, this.context(), msg),
14
+ priority: (/^\d+$/.test(priorityValue)) ? parseInt(priorityValue) : null,
12
15
  actionHook: v_resolve(config.actionHook, config.actionHookType, this.context(), msg),
13
16
  waitHook: v_resolve(config.waitHook, config.waitHookType, this.context(), msg)
14
17
  });
@@ -26,10 +26,18 @@
26
26
  return action !== 'hold_conf' || v.length > 0;
27
27
  }},
28
28
  waitHookType: {value: 'str'},
29
+ siprecServerURL: {value: '', validate: function(v) {
30
+ const action = $('#node-input-action').val();
31
+ return action !== 'start_call_recording' || v.length > 0;
32
+ }},
33
+ siprecServerURLType: {value: 'str'},
34
+ recordingID: {value: ''},
35
+ recordingIDType: {value: 'str'},
29
36
  },
30
37
  inputs:1,
31
38
  outputs:1,
32
39
  icon: "font-awesome/fa-cubes",
40
+ paletteLabel: "Live Call Control",
33
41
  label: function() {
34
42
  return this.name || (this.action ? `LCC - ${this.action}` : 'LCC');
35
43
  },
@@ -42,8 +50,7 @@
42
50
  var sayDiv = $('#say-options');
43
51
  var redirectDiv = $('#redirect-options');
44
52
  var waitHookDiv = $('#wait-options');
45
- sayDiv.hide();
46
- waitHookDiv.hide();
53
+ var recordDiv = $('#record-options');
47
54
 
48
55
  $('#node-input-callSid').typedInput({
49
56
  default: $('#node-input-callSidType').val(),
@@ -60,15 +67,27 @@
60
67
  types: ['str','msg', 'flow', 'global', 'jsonata', 'env'],
61
68
  typeField: $('#node-input-waitHookType')
62
69
  });
70
+ $('#node-input-siprecServerURL').typedInput({
71
+ default: $('#node-input-siprecServerURLType').val(),
72
+ types: ['str','msg', 'flow', 'global', 'jsonata', 'env'],
73
+ typeField: $('#node-input-siprecServerURLType')
74
+ });
75
+ $('#node-input-recordingID').typedInput({
76
+ default: $('#node-input-recordingIDType').val(),
77
+ types: ['str','msg', 'flow', 'global', 'jsonata', 'env'],
78
+ typeField: $('#node-input-recordingIDType')
79
+ });
63
80
 
64
81
  var onActionChanged = function () {
65
- node.action = actionElem.find(':selected').val();
66
- if ('whisper' === node.action) sayDiv.show();
82
+ var selectedAction = actionElem.find(':selected').val();
83
+ if ('whisper' === selectedAction) sayDiv.show();
67
84
  else sayDiv.hide();
68
- if ('redirect' === node.action) redirectDiv.show();
85
+ if ('redirect' === selectedAction) redirectDiv.show();
69
86
  else redirectDiv.hide();
70
- if ('hold_conf' === node.action) waitHookDiv.show();
87
+ if ('hold_conf' === selectedAction) waitHookDiv.show();
71
88
  else waitHookDiv.hide();
89
+ if ('start_call_recording' === selectedAction) recordDiv.show();
90
+ else recordDiv.hide();
72
91
  }
73
92
 
74
93
  var onVendorChanged = function() {
@@ -146,7 +165,7 @@
146
165
 
147
166
  <!-- HTML -->
148
167
  <script type="text/html" data-template-name="lcc">
149
- <div class="form-row">
168
+ <div class="form-row">
150
169
  <label for="node-input-name"><i class="icon-tag"></i> Name</label>
151
170
  <input type="text" id="node-input-name" placeholder="Name">
152
171
  </div>
@@ -173,6 +192,10 @@
173
192
  <option value="resume">resume listen audio feed</option>
174
193
  <option value="redirect">redirect call</option>
175
194
  <option value="whisper">whisper</option>
195
+ <option value="start_call_recording">start call recording</option>
196
+ <option value="stop_call_recording">stop call recording</option>
197
+ <option value="pause_call_recording">pause call recording</option>
198
+ <option value="resume_call_recording">resume call recording</option>
176
199
  </select>
177
200
  </div>
178
201
  <div id="say-options">
@@ -216,6 +239,18 @@
216
239
  <label for="node-input-waitHook">Wait webhook</label>
217
240
  <input type="text" id="node-input-waitHook" placeholder="/path">
218
241
  <input type="hidden" id="node-input-waitHookType">
242
+ </div>
243
+ </div>
244
+ <div id="record-options">
245
+ <div class="form-row">
246
+ <label for="node-input-siprecServerURL">SIPREC URI</label>
247
+ <input type="text" id="node-input-siprecServerURL" placeholder="sip:srs@recording.example.com">
248
+ <input type="hidden" id="node-input-siprecServerURLType">
249
+ </div>
250
+ <div class="form-row">
251
+ <label for="node-input-recordingID">Recording ID</label>
252
+ <input type="text" id="node-input-recordingID" placeholder="545362a9-4cb3-4a55-85b8-b33ca83a6517">
253
+ <input type="hidden" id="node-input-recordingIDType">
219
254
  </div>
220
255
  </div>
221
256
  </script>
package/src/nodes/lcc.js CHANGED
@@ -1,3 +1,4 @@
1
+ var crypto = require('crypto');
1
2
  var {v_resolve, doLCC} = require('./libs')
2
3
 
3
4
  module.exports = function(RED) {
@@ -44,7 +45,6 @@ function lcc(config) {
44
45
  opts.listen_status = 'resume';
45
46
  break;
46
47
  case 'redirect':
47
- node.log(`LCC redirect callHook ${config.callHook} callHookType: ${config.callHookType}`);
48
48
  opts.call_hook = {url: v_resolve(config.callHook, config.callHookType, this.context(), msg)};
49
49
  break;
50
50
  case 'hold_conf':
@@ -70,6 +70,22 @@ function lcc(config) {
70
70
  });
71
71
  }
72
72
  break;
73
+ case 'start_call_recording':
74
+ opts.record = {
75
+ action: 'startCallRecording',
76
+ siprecServerURL: v_resolve(config.siprecServerURL, config.siprecServerURLType, this.context(), msg),
77
+ recordingID: v_resolve(config.recordingID, config.recordingIDType, this.context(), msg) || crypto.randomUUID()
78
+ };
79
+ break;
80
+ case 'stop_call_recording':
81
+ opts.record = { action: 'stopCallRecording' };
82
+ break;
83
+ case 'pause_call_recording':
84
+ opts.record = { action: 'pauseCallRecording' };
85
+ break;
86
+ case 'resume_call_recording':
87
+ opts.record = { action: 'resumeCallRecording' };
88
+ break;
73
89
  default:
74
90
  node.log(`invalid action: ${config.action}`);
75
91
  send(msg);
package/src/nodes/libs.js CHANGED
@@ -81,7 +81,7 @@ exports.appendVerb = (msg, obj) => {
81
81
  'Authorization': `Bearer ${apiToken}`
82
82
  });
83
83
  const url = `Accounts/${accountSid}/Calls/${callSid}`;
84
- node.log(`invoking LCC with callSid ${callSid} at ${baseUrl}/v1/${url}`);
84
+ node.log(`invoking LCC with payload ${JSON.stringify(opts)} at ${baseUrl}/v1/${url}`);
85
85
  return post(url, opts);
86
86
  }
87
87
 
@@ -53,17 +53,22 @@ module.exports = function(RED) {
53
53
  });
54
54
 
55
55
  RED.httpAdmin.get('/_jambonz/applications/:serverId', (req, res) => {
56
- var conn = RED.nodes.getNode(req.params.serverId);
56
+ const conn = RED.nodes.getNode(req.params.serverId);
57
57
  if (conn && conn.credentials) {
58
- const {apiToken} = conn.credentials;
59
- const url = conn.url
58
+ const { apiToken } = conn.credentials;
59
+ const { url } = conn;
60
60
  const getApps = bent(`${url}/v1/Applications`, 'GET', 'json', 200, {
61
61
  'Authorization': `Bearer ${apiToken}`
62
62
  });
63
63
  getApps()
64
- .then( (apps) =>{
65
- res.send(apps)
64
+ .then((apps) => {
65
+ res.send(apps);
66
66
  })
67
+ .catch((error) => {
68
+ res.status(500).send(error.message);
69
+ });
70
+ } else {
71
+ res.status(404);
67
72
  }
68
73
  });
69
74
  };