@jambonz/node-red-contrib-jambonz 2.2.72 → 2.3.0

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.
Files changed (93) hide show
  1. package/examples/Authenticating sip devices.json +1 -0
  2. package/examples/Conference with LCC.json +1 -0
  3. package/{src/examples → examples}/Connecting to a dialogflow bot.json +0 -0
  4. package/examples/Create Call and Send DTMF.json +1 -0
  5. package/examples/Get Log Data.json +1 -0
  6. package/examples/Leave Queue.json +1 -0
  7. package/examples/SIP Responses.json +1 -0
  8. package/{src/examples → examples}/SIP trunking outbound call.json +0 -0
  9. package/examples/Simple IVR.json +1 -0
  10. package/examples/Simple Queue.json +1 -0
  11. package/examples/Using Redirect.json +1 -0
  12. package/examples/Voicemail using Listen into S3 bucket.json +130 -0
  13. package/examples/config.json +1 -0
  14. package/examples/gather.json +1 -0
  15. package/examples/lex.json +1 -0
  16. package/examples/messages.json +1 -0
  17. package/examples/rasa.json +1 -0
  18. package/examples/tag a call.json +1 -0
  19. package/package.json +52 -14
  20. package/resources/editor.js +414 -0
  21. package/src/data/recognizer.json +685 -1
  22. package/src/data/tts.json +2970 -1
  23. package/src/nodes/auth.html +108 -0
  24. package/src/nodes/auth.js +43 -0
  25. package/src/nodes/conference.html +121 -0
  26. package/src/nodes/conference.js +31 -0
  27. package/src/nodes/config.html +484 -0
  28. package/src/nodes/config.js +74 -0
  29. package/src/nodes/create_call.html +287 -0
  30. package/src/nodes/create_call.js +104 -0
  31. package/src/nodes/create_sms.html +106 -0
  32. package/src/nodes/create_sms.js +65 -0
  33. package/src/nodes/dequeue.html +102 -0
  34. package/src/nodes/dequeue.js +25 -0
  35. package/src/nodes/dial.html +619 -0
  36. package/src/nodes/dial.js +138 -0
  37. package/src/nodes/dialogflow.html +260 -0
  38. package/src/nodes/dialogflow.js +53 -0
  39. package/src/nodes/dtmf.html +74 -0
  40. package/src/nodes/dtmf.js +22 -0
  41. package/src/nodes/enqueue.html +90 -0
  42. package/src/nodes/enqueue.js +25 -0
  43. package/src/nodes/gather.html +322 -0
  44. package/src/nodes/gather.js +83 -0
  45. package/src/nodes/get_alerts.html +94 -0
  46. package/src/nodes/get_alerts.js +47 -0
  47. package/src/nodes/get_calls.html +59 -0
  48. package/src/nodes/get_calls.js +38 -0
  49. package/src/nodes/get_recent_calls.html +95 -0
  50. package/src/nodes/get_recent_calls.js +47 -0
  51. package/src/nodes/hangup.html +46 -0
  52. package/src/nodes/hangup.js +20 -0
  53. package/src/nodes/jambonz.html +96 -42
  54. package/src/nodes/lcc.html +256 -0
  55. package/src/nodes/lcc.js +102 -0
  56. package/src/nodes/leave.html +40 -0
  57. package/src/nodes/leave.js +21 -0
  58. package/src/nodes/lex.html +264 -0
  59. package/src/nodes/lex.js +75 -0
  60. package/src/nodes/libs.js +99 -0
  61. package/src/nodes/listen.html +341 -0
  62. package/src/nodes/listen.js +82 -0
  63. package/src/nodes/message.html +95 -0
  64. package/src/nodes/message.js +32 -0
  65. package/src/nodes/pause.html +62 -0
  66. package/src/nodes/pause.js +25 -0
  67. package/src/nodes/play.html +76 -0
  68. package/src/nodes/play.js +23 -0
  69. package/src/nodes/rasa.html +106 -0
  70. package/src/nodes/rasa.js +22 -0
  71. package/src/nodes/redirect.html +64 -0
  72. package/src/nodes/redirect.js +24 -0
  73. package/src/nodes/s3-upload.html +29 -14
  74. package/src/nodes/s3-upload.js +24 -9
  75. package/src/nodes/say.html +115 -0
  76. package/src/nodes/say.js +38 -0
  77. package/src/nodes/sip-decline.html +76 -0
  78. package/src/nodes/sip-decline.js +25 -0
  79. package/src/nodes/sip-refer.html +116 -0
  80. package/src/nodes/sip-refer.js +33 -0
  81. package/src/nodes/sip-request.html +96 -0
  82. package/src/nodes/sip-request.js +36 -0
  83. package/src/nodes/tag.html +60 -0
  84. package/src/nodes/tag.js +22 -0
  85. package/src/nodes/template.html +63 -0
  86. package/src/nodes/template.js +9 -0
  87. package/src/nodes/userauth.html +85 -0
  88. package/src/nodes/userauth.js +70 -0
  89. package/src/nodes/webhooks.js +0 -16
  90. package/src/utils/http-helpers.js +68 -0
  91. package/src/examples/Authenticating sip devices.json +0 -10
  92. package/src/nodes/http-helpers.js +0 -26
  93. package/src/nodes/jambonz.js +0 -1032
@@ -0,0 +1,256 @@
1
+ <!-- Javascript -->
2
+ <script type="text/javascript">
3
+ RED.nodes.registerType('lcc',{
4
+ category: 'jambonz',
5
+ color: '#aebfb9',
6
+ defaults: {
7
+ name: {value: ''},
8
+ server: {value: '', required: true, type: 'jambonz_auth'},
9
+ callSid: {required: true},
10
+ callSidType: {value: 'msg'},
11
+ action: {value: 'hangup', required: true},
12
+ text: {value: '', validate: function(v) {
13
+ const action = $('#node-input-action').val();
14
+ return action !== 'whisper' || v.length > 0;
15
+ }},
16
+ vendor: {value: 'default'},
17
+ lang: {value: 'default'},
18
+ voice: {value: 'default'},
19
+ callHook: {value: '', validate: function(v) {
20
+ const action = $('#node-input-action').val();
21
+ return action !== 'redirect' || v.length > 0;
22
+ }},
23
+ callHookType: {value: 'str'},
24
+ waitHook: {value: '', validate: function(v) {
25
+ const action = $('#node-input-action').val();
26
+ return action !== 'hold_conf' || v.length > 0;
27
+ }},
28
+ waitHookType: {value: 'str'},
29
+ },
30
+ inputs:1,
31
+ outputs:1,
32
+ icon: "font-awesome/fa-cubes",
33
+ label: function() {
34
+ return this.name || (this.action ? `LCC - ${this.action}` : 'LCC');
35
+ },
36
+ oneditprepare: function() {
37
+ var node = this;
38
+ var actionElem = $('#node-input-action');
39
+ var vendorElem = $('#node-input-vendor');
40
+ var langElem = $('#node-input-lang');
41
+ var voiceElem = $('#node-input-voice');
42
+ var sayDiv = $('#say-options');
43
+ var redirectDiv = $('#redirect-options');
44
+ var waitHookDiv = $('#wait-options');
45
+ sayDiv.hide();
46
+ waitHookDiv.hide();
47
+
48
+ $('#node-input-callSid').typedInput({
49
+ default: $('#node-input-callSidType').val(),
50
+ types: ['str','msg', 'flow', 'global', 'jsonata', 'env'],
51
+ typeField: $('#node-input-callSidType')
52
+ });
53
+ $('#node-input-callHook').typedInput({
54
+ default: $('#node-input-callHookType').val(),
55
+ types: ['str','msg', 'flow', 'global', 'jsonata', 'env'],
56
+ typeField: $('#node-input-callHookType')
57
+ });
58
+ $('#node-input-waitHook').typedInput({
59
+ default: $('#node-input-waitHookType').val(),
60
+ types: ['str','msg', 'flow', 'global', 'jsonata', 'env'],
61
+ typeField: $('#node-input-waitHookType')
62
+ });
63
+
64
+ var onActionChanged = function () {
65
+ node.action = actionElem.find(':selected').val();
66
+ if ('whisper' === node.action) sayDiv.show();
67
+ else sayDiv.hide();
68
+ if ('redirect' === node.action) redirectDiv.show();
69
+ else redirectDiv.hide();
70
+ if ('hold_conf' === node.action) waitHookDiv.show();
71
+ else waitHookDiv.hide();
72
+ }
73
+
74
+ var onVendorChanged = function() {
75
+ node.vendor = vendorElem.find(':selected').val();
76
+ console.log(`say vendor changed to ${node.vendor}`);
77
+ setLanguage(node.vendor);
78
+ }
79
+
80
+ var onLangChanged = function() {
81
+ node.lang = langElem.find(':selected').val();
82
+ console.log(`language changed to ${node.lang}`);
83
+ setVoice(node.vendor, node.lang)
84
+ }
85
+
86
+ var onVoiceChanged = function() {
87
+ node.voice = voiceElem.find(':selected').val();
88
+ console.log(`voice changed to ${node.voice}`);
89
+ }
90
+
91
+ var setLanguage = function(v) {
92
+ langElem.find('option').remove();
93
+ voiceElem.find('option').remove();
94
+
95
+ switch (v) {
96
+ case 'default':
97
+ langElem.append('<option value="default" selected>--application default--</option>');
98
+ voiceElem.append('<option value="default" selected>--application default--</option>');
99
+ node.lang = 'default';
100
+ node.voice = 'default';
101
+ break;
102
+
103
+ case 'google':
104
+ langElem.append(googleLanguageOptions);
105
+ break;
106
+
107
+ case 'aws':
108
+ langElem.append(awsLanguageOptions);
109
+ break;
110
+ }
111
+ langElem.val(node.lang);
112
+ }
113
+
114
+ var setVoice = function(vendor, lang) {
115
+ console.log(`say set voice for language ${lang} vendor ${vendor}`);
116
+ voiceElem.find('option').remove();
117
+
118
+ switch (vendor) {
119
+ case 'default':
120
+ voiceElem.append('<option value="default" selected>--application default--</option>');
121
+ break;
122
+
123
+ case 'google':
124
+ case 'aws':
125
+ var obj = 'google' === vendor ? mapGoogle[lang] : mapAws[lang];
126
+ if (obj) {
127
+ var options = '';
128
+ for (var i = 0; i < obj.voices.length; i++) {
129
+ if (i) options += `<option value="${obj.voices[i].value}">${obj.voices[i].name}</option>`;
130
+ else options += `<option value="${obj.voices[i].value}">${obj.voices[i].name}</option>`;
131
+ }
132
+ voiceElem.append(options);
133
+ }
134
+ break;
135
+ }
136
+ voiceElem.val(node.voice);
137
+ }
138
+
139
+ actionElem.change(onActionChanged);
140
+ vendorElem.change(onVendorChanged);
141
+ langElem.change(onLangChanged);
142
+ voiceElem.change(onVoiceChanged);
143
+ }
144
+ });
145
+
146
+ </script>
147
+
148
+
149
+ <!-- HTML -->
150
+ <script type="text/html" data-template-name="lcc">
151
+ <div class="form-row">
152
+ <label for="node-input-name"><i class="icon-tag"></i> Name</label>
153
+ <input type="text" id="node-input-name" placeholder="Name">
154
+ </div>
155
+ <div class="form-row">
156
+ <label for="node-input-server">Server</label>
157
+ <input type="text" id="node-input-server">
158
+ </div>
159
+ <div class="form-row">
160
+ <label for="node-input-callSid">CallSid</label>
161
+ <input type="text" id="node-input-callSid">
162
+ <input type="hidden" id="node-input-callSidType">
163
+ </div>
164
+ <div class="form-row">
165
+ <label for="node-input-action">Action</label>
166
+ <select id="node-input-action">
167
+ <option value="hangup">hangup call</option>
168
+ <option value="mute">mute caller</option>
169
+ <option value="unmute">unmute caller</option>
170
+ <option value="mute_conf">mute conference participants</option>
171
+ <option value="unmute_conf">unmute conference participants</option>
172
+ <option value="hold_conf">hold conference participant</option>
173
+ <option value="unhold_conf">unhold conference participant</option>
174
+ <option value="pause">pause listen audio feed</option>
175
+ <option value="resume">resume listen audio feed</option>
176
+ <option value="redirect">redirect call</option>
177
+ <option value="whisper">whisper</option>
178
+ </select>
179
+ </div>
180
+ <div id="say-options">
181
+ <div class="form-row">
182
+ <label for="node-input-text">Text</label>
183
+ <textarea id="node-input-text" rows="4" placeholder="Text or SSML to whisper" style="width:70%"></textarea>
184
+ </div>
185
+ <fieldset>
186
+ <legend>Speech synthesis options</legend>
187
+ <div class="form-row">
188
+ <label for="node-input-vendor">Vendor</label>
189
+ <select id="node-input-vendor">
190
+ <option value="default" selected>--application default--</option>
191
+ <option value="google">google</option>
192
+ <option value="aws">aws/polly</option>
193
+ </select>
194
+ </div>
195
+ <div class="form-row">
196
+ <label for="node-input-lang">Language</label>
197
+ <select id="node-input-lang">
198
+ <option value="default" selected>--application default--</option>
199
+ </select>
200
+ </div>
201
+ <div class="form-row">
202
+ <label for="node-input-voice">Voice</label>
203
+ <select id="node-input-voice">
204
+ <option value="default" selected>--application default--</option>
205
+ </select>
206
+ </div>
207
+ </fieldset>
208
+ </div>
209
+ <div id="redirect-options">
210
+ <div class="form-row">
211
+ <label for="node-input-callHook">Call webhook</label>
212
+ <input type="text" id="node-input-callHook" placeholder="/path">
213
+ <input type="hidden" id="node-input-callHookType">
214
+ </div>
215
+ </div>
216
+ <div id="wait-options">
217
+ <div class="form-row">
218
+ <label for="node-input-waitHook">Wait webhook</label>
219
+ <input type="text" id="node-input-waitHook" placeholder="/path">
220
+ <input type="hidden" id="node-input-waitHookType">
221
+ </div>
222
+ </div>
223
+ </script>
224
+
225
+ <!-- Help Text -->
226
+
227
+ <script type="text/html" data-help-name="lcc">
228
+ <p>Perform Live Call Control</p>
229
+ <h3>Properties</h3>
230
+ <p><code>Server</code> -
231
+ jambonz server to connect to</p>
232
+ <p><code>CallSid</code> -
233
+ Call identifier</p>
234
+ <p><code>Action</code> -
235
+ Action to perform on the call</p>
236
+ <p><code>Text</code> -
237
+ Text to speak, only if action is 'whisper'</p>
238
+ <p><code>Vendor</code> -
239
+ TTS vendor to use for whisper</p>
240
+ <p><code>Language</code> -
241
+ TTS language to use for whisper</p>
242
+ <p><code>Voice</code> -
243
+ TTS voice to use for whisper</p>
244
+
245
+
246
+ <h3>Details</h3>
247
+ The lcc command is used to perform live call control on a call in progress.
248
+ The call can be terminated, the caller can be muted or unmuted, or the live
249
+ audio feed that is being sent via a <a href="https://docs.jambonz.org/jambonz/#listen">listen</a> verb can be paused or resumed.
250
+ <br/><br/>
251
+ A common use case is to use dtmf captures in a <a href="https://docs.jambonz.org/jambonz/#dial">dial</a> verb to call a webhook
252
+ that then uses lcc to modify the state of the call.
253
+ <h3>References</h3>
254
+ <ul>
255
+ <li><a href="https://docs.jambonz.org/rest/#updating-a-call">Jambonz reference</a></li>
256
+ </script>
@@ -0,0 +1,102 @@
1
+ var {createHash} = require('crypto');
2
+ const bent = require('bent');
3
+ var mustache = require('mustache');
4
+ mustache.escape = function(text) {return text;};
5
+ var {v_resolve, doLCC} = require('./libs')
6
+
7
+ module.exports = function(RED) {
8
+ /** LCC */
9
+ function lcc(config) {
10
+ RED.nodes.createNode(this, config);
11
+ var node = this;
12
+ const server = RED.nodes.getNode(config.server);
13
+
14
+ node.on('input', async(msg, send, done) => {
15
+ send = send || function() { node.send.apply(node, arguments);};
16
+
17
+ const {accountSid, apiToken} = server.credentials;
18
+ const url = server.url;
19
+ const callSid = v_resolve(config.callSid, config.callSidType, this.context(), msg);
20
+ if (!url || !accountSid || !apiToken || !callSid) {
21
+ node.log(`invalid / missing credentials or callSid, skipping LCC node: ${JSON.stringify(server.credentials)}`);
22
+ send(msg);
23
+ if (done) done();
24
+ return;
25
+ }
26
+
27
+ const opts = {};
28
+ switch (config.action) {
29
+ case 'hangup':
30
+ opts.call_status = 'completed';
31
+ break;
32
+ case 'mute':
33
+ opts.mute_status = 'mute';
34
+ break;
35
+ case 'unmute':
36
+ opts.mute_status = 'unmute';
37
+ break;
38
+ case 'mute_conf':
39
+ opts.conf_mute_status = 'mute';
40
+ break;
41
+ case 'unmute_conf':
42
+ opts.conf_mute_status = 'unmute';
43
+ break;
44
+ case 'pause':
45
+ opts.listen_status = 'pause';
46
+ break;
47
+ case 'resume':
48
+ opts.listen_status = 'resume';
49
+ break;
50
+ case 'redirect':
51
+ node.log(`LCC redirect callHook ${config.callHook} callHookType: ${config.callHookType}`);
52
+ opts.call_hook = {url: v_resolve(config.callHook, config.callHookType, this.context(), msg)};
53
+ break;
54
+ case 'hold_conf':
55
+ opts.conf_hold_status = 'hold';
56
+ opts.wait_hook = {url: v_resolve(config.waitHook, config.waitHookType, this.context(), msg)};
57
+ break;
58
+ case 'unhold_conf':
59
+ opts.conf_hold_status = 'unhold';
60
+ case 'whisper':
61
+ Object.assign(opts, {
62
+ whisper: {
63
+ verb: 'say',
64
+ text: config.text
65
+ }
66
+ });
67
+ if (['aws', 'google'].includes(config.vendor)) {
68
+ Object.assign(opts.whisper, {
69
+ synthesizer: {
70
+ vendor: config.vendor,
71
+ language: config.lang,
72
+ voice: config.voice
73
+ }
74
+ });
75
+ }
76
+ break;
77
+ default:
78
+ node.log(`invalid action: ${config.action}`);
79
+ send(msg);
80
+ if (done) done();
81
+ return;
82
+ }
83
+
84
+ try {
85
+ msg.payload = await doLCC(node, url, accountSid, apiToken, callSid, opts);
86
+ msg.statusCode = 202;
87
+ } catch (err) {
88
+ if (err.statusCode) msg.statusCode = err.statusCode;
89
+ else {
90
+ node.error(`Error sending LCC ${JSON.stringify(err)}`);
91
+ if (done) done(err);
92
+ else node.error(err, msg);
93
+ send(msg);
94
+ return;
95
+ }
96
+ }
97
+ send(msg);
98
+ if (done) done();
99
+ });
100
+ }
101
+ RED.nodes.registerType('lcc', lcc);
102
+ }
@@ -0,0 +1,40 @@
1
+ <!-- Javascript -->
2
+ <script type="text/javascript">
3
+ RED.nodes.registerType('leave',{
4
+ category: 'jambonz',
5
+ color: '#bbabaa',
6
+ defaults: {
7
+ name: {value: ''},
8
+ },
9
+ inputs:1,
10
+ outputs:1,
11
+ icon: "font-awesome/fa-cubes",
12
+ label: function() { return this.name || 'leave';}
13
+ });
14
+ </script>
15
+
16
+
17
+ <!-- HTML -->
18
+ <script type="text/html" data-template-name="leave">
19
+ <div class="form-row">
20
+ <label for="node-input-name"><i class="icon-tag"></i> Name</label>
21
+ <input type="text" id="node-input-name" placeholder="Name">
22
+ </div>
23
+ </script>
24
+
25
+ <!-- Help Text -->
26
+ <script type="text/html" data-help-name="leave">
27
+ <p>transfer call out of queue</p>
28
+
29
+ <h3>Outputs</h3>
30
+ <dl class="message-properties">
31
+ <dt>jambonz<span class="property-type">object</span></dt>
32
+ <dd> <code>msg.jambonz</code> will contain any previous actions provided to the input with the new <code>leave</code> action appended </dd>
33
+ </dl>
34
+
35
+ <h3>Details</h3>
36
+ The leave verb transfers a call out of a queue. The call then returns to the flow of execution following the enqueue verb that parked the call, or the document returned by that verbs actionHook property, if provided.<h3>References</h3>
37
+ <ul>
38
+ <li><a href="https://docs.jambonz.org/jambonz/#leave">Jambonz leave reference</a></li>
39
+ </ul>
40
+ </script>
@@ -0,0 +1,21 @@
1
+
2
+ var {createHash} = require('crypto');
3
+ const bent = require('bent');
4
+ var mustache = require('mustache');
5
+ mustache.escape = function(text) {return text;};
6
+ var {appendVerb} = require('./libs')
7
+
8
+
9
+ module.exports = function(RED) {
10
+ function leave(config) {
11
+ RED.nodes.createNode(this, config);
12
+ var node = this;
13
+ node.on('input', function(msg, send, done) {
14
+ appendVerb(msg, {
15
+ verb: 'leave'
16
+ });
17
+ node.send(msg);
18
+ });
19
+ }
20
+ RED.nodes.registerType('leave', leave);
21
+ }