@jambonz/node-red-contrib-jambonz 2.4.27 → 2.4.29

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.
@@ -0,0 +1,124 @@
1
+ [
2
+ {
3
+ "id": "5d9a3f1fa6551a9f",
4
+ "type": "webhook in",
5
+ "z": "1b97c147228e3829",
6
+ "name": "",
7
+ "url": "/call",
8
+ "method": "post",
9
+ "x": 100,
10
+ "y": 220,
11
+ "wires": [
12
+ [
13
+ "e627c4f20ede7606"
14
+ ]
15
+ ]
16
+ },
17
+ {
18
+ "id": "1d33431d324d0453",
19
+ "type": "webhook out",
20
+ "z": "1b97c147228e3829",
21
+ "name": "",
22
+ "x": 1270,
23
+ "y": 180,
24
+ "wires": []
25
+ },
26
+ {
27
+ "id": "9bf6ecad5a81a2bf",
28
+ "type": "debug",
29
+ "z": "1b97c147228e3829",
30
+ "name": "debug 18",
31
+ "active": true,
32
+ "tosidebar": true,
33
+ "console": false,
34
+ "tostatus": false,
35
+ "complete": "jambonz",
36
+ "targetType": "msg",
37
+ "statusVal": "",
38
+ "statusType": "auto",
39
+ "x": 1260,
40
+ "y": 280,
41
+ "wires": []
42
+ },
43
+ {
44
+ "id": "73e79d1cc5ac62d0",
45
+ "type": "generic",
46
+ "z": "1b97c147228e3829",
47
+ "name": "generic - say",
48
+ "verb": "say",
49
+ "data": "{\"text\":\"goodbye\"}",
50
+ "dataType": "json",
51
+ "x": 1030,
52
+ "y": 220,
53
+ "wires": [
54
+ [
55
+ "9bf6ecad5a81a2bf",
56
+ "1d33431d324d0453"
57
+ ]
58
+ ]
59
+ },
60
+ {
61
+ "id": "e627c4f20ede7606",
62
+ "type": "generic",
63
+ "z": "1b97c147228e3829",
64
+ "name": "generic - answer",
65
+ "verb": "answer",
66
+ "data": "{}",
67
+ "dataType": "json",
68
+ "x": 290,
69
+ "y": 220,
70
+ "wires": [
71
+ [
72
+ "453b7fb4d62ef93e"
73
+ ]
74
+ ]
75
+ },
76
+ {
77
+ "id": "453b7fb4d62ef93e",
78
+ "type": "generic",
79
+ "z": "1b97c147228e3829",
80
+ "name": "generic - pause",
81
+ "verb": "pause",
82
+ "data": "{\"length\":1}",
83
+ "dataType": "json",
84
+ "x": 480,
85
+ "y": 220,
86
+ "wires": [
87
+ [
88
+ "da2ac5d1cd9ab0a5"
89
+ ]
90
+ ]
91
+ },
92
+ {
93
+ "id": "da2ac5d1cd9ab0a5",
94
+ "type": "generic",
95
+ "z": "1b97c147228e3829",
96
+ "name": "generic -say",
97
+ "verb": "say",
98
+ "data": "{\"text\":\"Please hold\",\"loop\":1,\"earlyMedia\":false}",
99
+ "dataType": "json",
100
+ "x": 670,
101
+ "y": 220,
102
+ "wires": [
103
+ [
104
+ "b7225263b1115bde"
105
+ ]
106
+ ]
107
+ },
108
+ {
109
+ "id": "b7225263b1115bde",
110
+ "type": "generic",
111
+ "z": "1b97c147228e3829",
112
+ "name": "generic - pause",
113
+ "verb": "pause",
114
+ "data": "{\"length\":5}",
115
+ "dataType": "json",
116
+ "x": 840,
117
+ "y": 220,
118
+ "wires": [
119
+ [
120
+ "73e79d1cc5ac62d0"
121
+ ]
122
+ ]
123
+ }
124
+ ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jambonz/node-red-contrib-jambonz",
3
- "version": "2.4.27",
3
+ "version": "2.4.29",
4
4
  "description": "Node-RED nodes for jambonz platform",
5
5
  "keywords": [
6
6
  "node-red"
@@ -50,14 +50,15 @@
50
50
  "get_calls": "src/nodes/get_calls.js",
51
51
  "get_recent_calls": "src/nodes/get_recent_calls.js",
52
52
  "answer": "src/nodes/answer.js",
53
- "dub": "src/nodes/dub.js"
53
+ "dub": "src/nodes/dub.js",
54
+ "generic": "src/nodes/generic-verb.js"
54
55
  }
55
56
  },
56
57
  "author": "Dave Horton",
57
58
  "license": "MIT",
58
59
  "dependencies": {
59
60
  "aws-sdk": "^2.1665.0",
60
- "body-parser": "^1.20.2",
61
+ "body-parser": "^1.20.3",
61
62
  "cookie-parser": "^1.4.6",
62
63
  "cors": "^2.8.5",
63
64
  "hash-sum": "^2.0.0",
@@ -68,7 +69,7 @@
68
69
  "on-headers": "^1.0.2",
69
70
  "raw-body": "^2.5.2",
70
71
  "s3-upload-stream": "^1.0.7",
71
- "undici": "^5.28.4",
72
+ "undici": "^5.28.5",
72
73
  "ws": "^8.18.0"
73
74
  }
74
75
  }
@@ -482,7 +482,7 @@
482
482
  <div class="form-row">
483
483
  <label for="node-input-dialmusic">Dial music</label>
484
484
  <input type="text" id="node-input-dialmusic" placeholder="url to .wav or .mp3 file to play during dial">
485
- <input type="hidden" id="node-input-dialMusicType">
485
+ <input type="hidden" id="node-input-dialmusicType">
486
486
  </div>
487
487
  <div class="form-row">
488
488
  <label for="node-input-dtmfcapture">Dtmf capture</label>
package/src/nodes/dial.js CHANGED
@@ -8,7 +8,7 @@ module.exports = function(RED) {
8
8
  node.on('input', async function(msg) {
9
9
  node.log(`dial config: ${JSON.stringify(config)}, msg.call: ${JSON.stringify(msg.call)}`);
10
10
  const target = await Promise.all(config.targets.map(async (t) => {
11
- const obj = {};
11
+ const obj = { type: t.type }; // Initialize with type property
12
12
  const dest = await new_resolve(RED, t.dest, t.varType, node, msg);
13
13
  const trunk = await new_resolve(RED, t.trunk, t.trunkType, node, msg);
14
14
  const tenant = t.tenant ? await new_resolve(RED, t.tenant, t.tenantType, node, msg) : '';
@@ -0,0 +1,60 @@
1
+ <!-- Javascript -->
2
+ <script type="text/javascript">
3
+
4
+
5
+ RED.nodes.registerType('generic',{
6
+ category: 'jambonz',
7
+ color: '#bbabaa',
8
+ defaults: {
9
+ name: {value: ''},
10
+ verb: {required: true},
11
+ data: {value: '{}'},
12
+ dataType: {value: 'json'},
13
+ },
14
+ inputs:1,
15
+ outputs:1,
16
+ icon: "font-awesome/fa-cubes",
17
+ label: function() { return this.name || 'generic';},
18
+ oneditprepare: function() {
19
+ $('#node-input-data').typedInput({
20
+ default: $('#node-input-dataType').val(),
21
+ types: ['json','msg', 'flow', 'global', 'jsonata', 'env'],
22
+ typeField: $('#node-input-dataType')
23
+ });
24
+ }
25
+ });
26
+ </script>
27
+
28
+ <!-- HTML -->
29
+ <script type="text/html" data-template-name="generic">
30
+ <div class="form-row">
31
+ <label for="node-input-name"><i class="icon-tag"></i> Name</label>
32
+ <input type="text" id="node-input-name" placeholder="Name">
33
+ </div>
34
+ <div class="form-row">
35
+ <label for="node-input-verb">Verb</label>
36
+ <input type="text" id="node-input-verb" placeholder="verb">
37
+ </div>
38
+ <div class="form-row">
39
+ <label for="node-input-data">Attributes</label>
40
+ <input type="text" id="node-input-data">
41
+ <input type="hidden" id="node-input-dataType">
42
+ </div>
43
+ </script>
44
+
45
+ <!-- Help Text -->
46
+ <script type="text/html" data-help-name="generic">
47
+ <p>A generic node to set any verb</p>
48
+ <h3>Outputs</h3>
49
+ <dl class="message-properties">
50
+ <dt>jambonz<span class="property-type">object</span></dt>
51
+ <dd> <code>msg.jambonz</code> will contain any previous actions provided to the input with the new <code>pause</code> action appended </dd>
52
+ </dl>
53
+
54
+ <h3>Details</h3>
55
+ Allows use of complex or new verbs in Jambonz that may not have nodes, set the verb name and then pass any additional attributes as a JSON object in the data field.
56
+ <h3>References</h3>
57
+ <ul>
58
+ <li><a href="https://www.jambonz.org/docs/webhooks/">Jambonz Verb reference</a></li>
59
+ </ul>
60
+ </script>
@@ -0,0 +1,18 @@
1
+ var {appendVerb, new_resolve} = require('./libs')
2
+
3
+ module.exports = function(RED) {
4
+ function generic(config) {
5
+ RED.nodes.createNode(this, config);
6
+ var node = this;
7
+ node.verb = config.verb
8
+ node.on('input', async function(msg) {
9
+ let data = await new_resolve(RED, config.data, config.dataType, node, msg);
10
+ appendVerb(msg, {
11
+ ...{verb: node.verb},
12
+ ...data
13
+ });
14
+ node.send(msg);
15
+ });
16
+ }
17
+ RED.nodes.registerType('generic', generic);
18
+ }
@@ -70,6 +70,16 @@
70
70
  return action !== 'tag' || v.length > 0;
71
71
  }},
72
72
  tagType: {value: 'json'},
73
+ dubTrack: {value: ''},
74
+ dubTrackType: {value: 'str'},
75
+ dubPlay: {},
76
+ dubPlayType: {value: 'str'},
77
+ dubSay: {},
78
+ dubSayType: {value: 'str'},
79
+ dubLoop: {value: false},
80
+ dubLoopType: {value: 'bool'},
81
+ dubGain: {value: 0},
82
+ dubGainType: {value: 'num'},
73
83
  },
74
84
  inputs:1,
75
85
  outputs:1,
@@ -91,6 +101,7 @@
91
101
  var sipRequestDiv = $('#sip-request-options');
92
102
  var dtmfDiv = $('#dtmf-options');
93
103
  var tagDiv = $('#tag-options');
104
+ var dubDiv = $('#dub-options');
94
105
 
95
106
  $('#node-input-callSid').typedInput({
96
107
  default: $('#node-input-callSidType').val(),
@@ -152,6 +163,31 @@
152
163
  types: ['msg', 'flow', 'global', 'json', 'jsonata', 'env', mustacheType],
153
164
  typeField: $('#node-input-tagType')
154
165
  });
166
+ $('#node-input-dubTrack').typedInput({
167
+ default: $('#node-input-dubTrackType').val(),
168
+ types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
169
+ typeField: $('#node-input-dubTrackType')
170
+ });
171
+ $('#node-input-dubPlay').typedInput({
172
+ default: $('#node-input-dubPlayType').val(),
173
+ types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
174
+ typeField: $('#node-input-dubPlayType')
175
+ });
176
+ $('#node-input-dubSay').typedInput({
177
+ default: $('#node-input-dubSayType').val(),
178
+ types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
179
+ typeField: $('#node-input-dubSayType')
180
+ });
181
+ $('#node-input-dubLoop').typedInput({
182
+ default: $('#node-input-dubLoopType').val(),
183
+ types: ['bool', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
184
+ typeField: $('#node-input-dubLoopType')
185
+ });
186
+ $('#node-input-dubGain').typedInput({
187
+ default: $('#node-input-dubFainType').val(),
188
+ types: ['num', 'flow', 'global', 'json', 'jsonata', 'env', mustacheType],
189
+ typeField: $('#node-input-dubGainType')
190
+ });
155
191
 
156
192
  var onActionChanged = function () {
157
193
  var selectedAction = actionElem.find(':selected').val();
@@ -169,6 +205,8 @@
169
205
  else dtmfDiv.hide();
170
206
  if ('tag' === selectedAction) tagDiv.show();
171
207
  else tagDiv.hide();
208
+ if ('dub' === selectedAction.substring(0,3)) dubDiv.show();
209
+ else dubDiv.hide();
172
210
  }
173
211
 
174
212
  var onVendorChanged = function() {
@@ -337,6 +375,11 @@
337
375
  <option value="resume_call_recording">resume call recording</option>
338
376
  <option value="send_dtmf">send RFC 2833 DTMF</option>
339
377
  <option value="tag">tag call</option>
378
+ <option value="dubAdd">Dub - addTrack</option>
379
+ <option value="dubRemove">Dub - removeTrack</option>
380
+ <option value="dubPlay">Dub - playOnTrack</option>
381
+ <option value="dubSay">Dub - sayOnTrack</option>
382
+ <option value="dubSilence">Dub - SilenceTrack</option>
340
383
  </select>
341
384
  </div>
342
385
  <div id="say-options">
@@ -451,6 +494,33 @@
451
494
  <input type="hidden" id="node-input-tagType">
452
495
  </div>
453
496
  </div>
497
+ <div id="dub-options">
498
+ <div class="form-row">
499
+ <label for="node-input-dubTrack">Track</label>
500
+ <input type="text" id="node-input-dubTrack">
501
+ <input type="hidden" id="node-input-dubTrackType">
502
+ </div>
503
+ <div class="form-row">
504
+ <label for="node-input-dubPlay">Play URL</label>
505
+ <input type="text" id="node-input-dubPlay" placeholder="https://example.com/file.mp3">
506
+ <input type="hidden" id="node-input-dubPlayType">
507
+ </div>
508
+ <div class="form-row">
509
+ <label for="node-input-dubSay">Say Text</label>
510
+ <input type="text" id="node-input-dubSay" placeholder="Text">
511
+ <input type="hidden" id="node-input-tagType">
512
+ </div>
513
+ <div class="form-row">
514
+ <label for="node-input-dubLoop">Loop</label>
515
+ <input type="text" id="node-input-dubLoop">
516
+ <input type="hidden" id="node-input-dubLoopType">
517
+ </div>
518
+ <div class="form-row">
519
+ <label for="node-input-dubGain">Gain</label>
520
+ <input type="text" id="node-input-dubGain">
521
+ <input type="hidden" id="node-input-dubGainType">
522
+ </div>
523
+ </div>
454
524
  </script>
455
525
 
456
526
  <!-- Help Text -->
package/src/nodes/lcc.js CHANGED
@@ -121,8 +121,39 @@ function lcc(config) {
121
121
  };
122
122
  break;
123
123
  case 'tag':
124
- opts.tag = await new_resolve(RED, config.tag, config.tagType, node, msg);
125
- break;
124
+ opts.tag = await new_resolve(RED, config.tag, config.tagType, node, msg);
125
+ break;
126
+ case 'dubAdd':
127
+ opts.dub = { action: 'addTrack' };
128
+ opts.dub.track = await new_resolve(RED, config.dubTrack, config.dubTrackType, node, msg);
129
+ opts.dub.say = await new_resolve(RED, config.dubSay, config.dubSayType, node, msg) || undefined
130
+ opts.dub.play = await new_resolve(RED, config.dubPlay, config.dubPlayType, node, msg) || undefined
131
+ opts.dub.loop = await new_resolve(RED, config.dubLoop, config.dubLoopType, node, msg)
132
+ opts.dub.gain = await new_resolve(RED, config.dubGain, config.dubGainType, node, msg);
133
+ break
134
+ case 'dubRemove':
135
+ opts.dub = { action: 'removeTrack' };
136
+ opts.dub.track = await new_resolve(RED, config.dubTrack, config.dubTrackType, node, msg);
137
+ break
138
+ case 'dubPlay':
139
+ console.log(config)
140
+ opts.dub = { action: 'playOnTrack' };
141
+ opts.dub.track = await new_resolve(RED, config.dubTrack, config.dubTrackType, node, msg);
142
+ opts.dub.play = await new_resolve(RED, config.dubPlay, config.dubPlayType, node, msg);
143
+ opts.dub.loop = await new_resolve(RED, config.dubLoop, config.dubLoopType, node, msg);
144
+ opts.dub.gain = await new_resolve(RED, config.dubGain, config.dubGainType, node, msg);
145
+ break
146
+ case 'dubSay':
147
+ opts.dub = { action : 'sayOnTrack' };
148
+ opts.dub.track = await new_resolve(RED, config.dubTrack, config.dubTrackType, node, msg);
149
+ opts.dub.say = await new_resolve(RED, config.dubSay, config.dubSayType, node, msg);
150
+ opts.dub.loop = await new_resolve(RED, config.dubLoop, config.dubLoopType, node, msg);
151
+ opts.dub.gain = await new_resolve(RED, config.dubGain, config.dubGainType, node, msg);
152
+ break
153
+ case 'dubSilence':
154
+ opts.dub = { action: 'silenceTrack' };
155
+ opts.dub.track = await new_resolve(RED, config.dubTrack, config.dubTrackType, node, msg);
156
+ break
126
157
  default:
127
158
  node.log(`invalid action: ${config.action}`);
128
159
  send(msg);
package/src/nodes/libs.js CHANGED
@@ -133,7 +133,7 @@ exports.appendVerb = (msg, obj) => {
133
133
  const url = `${baseUrl}/v1/Accounts/${accountSid}/Calls/${callSid}`;
134
134
  node.log(`invoking LCC with payload ${JSON.stringify(opts)} at ${url}`);
135
135
  const response = await fetch(url, {
136
- method: 'POST',
136
+ method: 'POST', // TODO Should move this to PUT in next major update but will need regression testing
137
137
  headers: {
138
138
  'Content-Type': 'application/json',
139
139
  'Authorization': `Bearer ${apiToken}`
package/src/nodes/tag.js CHANGED
@@ -6,8 +6,15 @@ module.exports = function(RED) {
6
6
  var node = this;
7
7
  node.on('input', async function(msg) {
8
8
  var data = await new_resolve(RED, config.data, config.dataType, node, msg);
9
- if (typeof(data) != 'object'){
10
- data = JSON.parse(data)
9
+ if (typeof(data) != 'object') {
10
+ try {
11
+ data = JSON.parse(data)
12
+ } catch (e) {
13
+ console.log('Failed to parse data as JSON: ', data)
14
+ return node.send(msg);
15
+ }
16
+ } else {
17
+ data = Object.assign({}, data);
11
18
  }
12
19
  appendVerb(msg, {
13
20
  verb: 'tag',