@jambonz/node-red-contrib-jambonz 2.5.3 → 2.6.1

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.
@@ -1,281 +0,0 @@
1
- <!-- Javascript -->
2
- <script src="resources/@jambonz/node-red-contrib-jambonz/editor.js"></script>
3
- <script type="text/javascript">
4
- var mustacheType = {
5
- value: 'mustache',
6
- label: 'mustache',
7
- hasvalue: true,
8
- icon: 'resources/@jambonz/node-red-contrib-jambonz/icons/mustache.svg'
9
- }
10
- RED.nodes.registerType('lex',{
11
- category: 'jambonz',
12
- color: '#bbabaa',
13
- defaults: {
14
- name: {value: ''},
15
- aws: {value: '', type: 'aws_auth'},
16
- bot: {required: true},
17
- botType: {value: 'str'},
18
- alias: {required: true},
19
- aliasType: {value: 'str'},
20
- region: {required: true, value: 'us-east-1'},
21
- locale: {value: 'en_US'},
22
- localeType: {value: 'str'},
23
- specifyIntent: {value: true},
24
- intent: {value: ''},
25
- intentType: {value: 'str'},
26
- slots: {value: ''},
27
- slotsType: {value: 'json'},
28
- metadata: {value: ''},
29
- metadataType: {value: 'json'},
30
- welcomeMessage: {value: 'str'},
31
- eventHook: {},
32
- eventHookType: {value: 'str'},
33
- actionHook: {},
34
- actionHookType: {value: 'str'},
35
- bargein: {value: true},
36
- inputTimeout: {},
37
- inputTimeoutType: {value: 'num'},
38
- passDtmf: {value: true},
39
- prompt: {value: 'lex', required: true},
40
- vendor: {value: 'default'},
41
- lang: {value: 'default'},
42
- voice: {value: 'default'},
43
- xlang: {},
44
- xvoice: {}
45
- },
46
- inputs:1,
47
- outputs:1,
48
- icon: "font-awesome/fa-cubes",
49
- label: function() { return this.name || 'lex';},
50
- oneditprepare: () => {
51
- var node = this;
52
- var promptElem = $('#node-input-prompt');
53
- var ttsDiv = $('#tts-options');
54
-
55
- prepareTtsControls(node);
56
-
57
- $('#node-input-eventHook').typedInput({
58
- types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
59
- typeField: $('#node-input-eventHookType')
60
- });
61
- $('#node-input-actionHook').typedInput({
62
- types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
63
- typeField: $('#node-input-actionHookType')
64
- });
65
-
66
- $('#node-input-bot').typedInput({
67
- types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
68
- typeField: $('#node-input-botType')
69
- });
70
- $('#node-input-alias').typedInput({
71
- types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
72
- typeField: $('#node-input-aliasType')
73
- });
74
- $('#node-input-locale').typedInput({
75
- types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
76
- typeField: $('#node-input-localeType')
77
- });
78
- $('#node-input-inputTimeout').typedInput({
79
- types: ['num', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
80
- typeField: $('#node-input-inputTimeoutType')
81
- });
82
- $('#node-input-intent').typedInput({
83
- types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
84
- typeField: $('#node-input-intentType')
85
- });
86
- $('#node-input-slots').typedInput({
87
- typeField: $('#node-input-slotsType'),
88
- types: ['json', 'msg', 'flow', 'global'],
89
- });
90
- $('#node-input-metadata').typedInput({
91
- typeField: $('#node-input-metadataType'),
92
- types: ['json', 'msg', 'flow', 'global'],
93
- });
94
-
95
- var onPromptChanged = function () {
96
- node.action = promptElem.find(':selected').val();
97
- if ('tts' === node.action) ttsDiv.show();
98
- else ttsDiv.hide();
99
- }
100
-
101
- promptElem.change(onPromptChanged);
102
- $('#node-input-specifyIntent').change(function() {
103
- const doIntent = $('#node-input-specifyIntent:checked').val();
104
- console.log('intent? ' + doIntent);
105
- if (doIntent) {
106
- $('#intent-options').show();
107
- $('#welcome-msg-options').hide();
108
- }
109
- else {
110
- $('#intent-options').hide();
111
- $('#welcome-msg-options').show();
112
- }
113
- });
114
- }
115
- });
116
- </script>
117
-
118
- <!-- HTML -->
119
- <script type="text/html" data-template-name="lex">
120
- <div class="form-row">
121
- <label for="node-input-name"><i class="icon-tag"></i> Name</label>
122
- <input type="text" id="node-input-name" placeholder="Name">
123
- </div>
124
- <div class="form-row">
125
- <label for="node-input-aws">AWS credentials</label>
126
- <input type="text" id="node-input-aws">
127
- </div>
128
- <div class="form-row">
129
- <label for="node-input-bot">Bot ID</label>
130
- <input type="text" id="node-input-bot" placeholder="bot name">
131
- <input type="hidden" id="node-input-botType">
132
- </div>
133
- <div class="form-row">
134
- <label for="node-input-alias">Bot Alias</label>
135
- <input type="text" id="node-input-alias" placeholder="bot alias">
136
- <input type="hidden" id="node-input-aliasType">
137
- </div>
138
- <div class="form-row">
139
- <label for="node-input-locale">Locale</label>
140
- <input type="text" id="node-input-locale" placeholder="en_US">
141
- <input type="hidden" id="node-input-localeType">
142
- </div>
143
- <div class="form-row">
144
- <label for="node-input-region">Region</label>
145
- <select id="node-input-region">
146
- <option value="ap-northeast-1">Tokyo (ap-northeast-1)</option>
147
- <option value="ap-southeast-1">Singapore (ap-southeast-1)</option>
148
- <option value="ap-southeast-2">Sydney (ap-southeast-2)</option>
149
- <option value="eu-central-1">Frankfurt (eu-central-1)</option>
150
- <option value="eu-west-1">Ireland (eu-west-1)</option>
151
- <option value="eu-west-2">London (eu-west-2)</option>
152
- <option value="us-east-1" selected>N. Virginia (us-east-1)</option>
153
- <option value="us-west-2">Oregon (us-west-2)</option>
154
- </select>
155
- </div>
156
- <div class="form-row">
157
- <label for="node-input-specifyIntent">Initial intent?</label>
158
- <input type="checkbox" id="node-input-specifyIntent">
159
- </div>
160
- <div id="intent-options">
161
- <div class="form-row">
162
- <label for="node-input-intent">Intent name</label>
163
- <input type="text" id="node-input-intent" placeholder="name of initial intent (if any)">
164
- <input type="hidden" id="node-input-intentType">
165
- </div>
166
- <div class="form-row">
167
- <label for="node-input-slots">Slots</label>
168
- <input type="text" id="node-input-slots" placeholder="initial slot values (if any)">
169
- <input type="hidden" id="node-input-slotsType">
170
- </div>
171
- </div>
172
- <div id="welcome-msg-options">
173
- <div class="form-row">
174
- <label for="node-input-welcomeMessage">Welcome message</label>
175
- <textarea id="node-input-welcomeMessage" rows="2" placeholder="Initial message to speak, if any" style="width:65%"></textarea>
176
- </div>
177
- </div>
178
- <div class="form-row">
179
- <label for="node-input-metadata">Metadata</label>
180
- <input type="text" id="node-input-metadata" placeholder="key-value pairs to send to lex (if any)">
181
- <input type="hidden" id="node-input-metadataType">
182
- </div>
183
- <div class="form-row">
184
- <label for="node-input-inputTimeout">Input timeout</label>
185
- <input type="text" id="node-input-inputTimeout" placeholder="speech timeout, in seconds">
186
- <input type="hidden" id="node-input-inputTimeoutType">
187
- </div>
188
- <div class="form-row">
189
- <label for="node-input-beep">Enable barge-in</label>
190
- <input type="checkbox" id="node-input-bargein">
191
- </div>
192
- <div class="form-row">
193
- <label for="node-input-passDtmf">Enable dtmf</label>
194
- <input type="checkbox" id="node-input-passDtmf">
195
- </div>
196
- <div class="form-row">
197
- <label for="node-input-eventHook">Event Hook</label>
198
- <input type="text" id="node-input-eventHook">
199
- <input type="hidden" id="node-input-eventHookType">
200
- </div>
201
- <div class="form-row">
202
- <label for="node-input-actionHook">Action Hook</label>
203
- <input type="text" id="node-input-actionHook">
204
- <input type="hidden" id="node-input-actionHookType">
205
- </div>
206
- <div class="form-row">
207
- <label for="node-input-prompt">Prompt audio</label>
208
- <select id="node-input-prompt">
209
- <option value="lex">Use Lex-provided audio</option>
210
- <option value="tts">Use text to speech</option>
211
- </select>
212
- </div>
213
- <div id="tts-options">
214
- <div class="form-row">
215
- <label for="node-input-vendor"> Vendor</label>
216
- <select id="node-input-vendor">
217
- <option value="default" selected>--application default--</option>
218
- <option value="google">google</option>
219
- <option value="aws">aws/polly</option>
220
- <option value="microsoft">microsoft</option>
221
- <option value="ibm">ibm</option>
222
- <option value="nuance">nuance</option>
223
-
224
- </select>
225
- </div>
226
- <div class="form-row" style="display: none;">
227
- <label for="node-input-lang"> Lang</label>
228
- <input type="text" id="node-input-lang">
229
- </div>
230
- <div class="form-row">
231
- <label for="node-input-xlang">Language</label>
232
- <select id="node-input-xlang">
233
- <option value="default" selected>--application default--</option>
234
- </select>
235
- </div>
236
- <div class="form-row" style="display: none;">
237
- <label for="node-input-voice"> Voice</label>
238
- <input type="text" id="node-input-voice">
239
- </div>
240
- <div class="form-row">
241
- <label for="node-input-xvoice">Voice</label>
242
- <select id="node-input-xvoice">
243
- <option value="default" selected>--application default--</option>
244
- </select>
245
- </div>
246
- </div>
247
- </script>
248
-
249
- <!-- Help Text -->
250
- <script type="text/html" data-help-name="lex">
251
- <p>Connects a call to a Amazon lex bot</p>
252
-
253
- <h3>Properties</h3>
254
- <p><code>Name</code> - Label for the node</p>
255
- <p><code>AWS Credentias</code> - A config node containing AWS credentials that will execute the lex bot.</p>
256
- <p><code>Bot ID</code> - ID of the Lex bot on AWS</p>
257
- <p><code>Bot Alias</code> - Lex bot alias ID </p>
258
- <p><code>Locale</code> - language code of speaker (currently supported languages: en_AU, en_GB, en_US, fr_CA, fr_FR, es_ES, es_US, it_IT)</p>
259
- <p><code>Region</code> - AWS region bot is running in</p>
260
- <p><code>Initial Intent?</code> - Should lex use an initial intent on connection</p>
261
- <p><code>Intent name</code> - initial intent to trigger (i.e. "welcome intent") </p>
262
- <p><code>Slots</code> - key-value pairs for slot names and initial values to be pre-filled </p>
263
- <p><code>Metadata</code> - key-value pairs for context data to pass to Lex bot </p>
264
- <p><code>Input timeout</code> - timeout in millseconds Lex will wait for a response before triggering fallback intent </p>
265
- <p><code>Enable barge-in </code> - ?? </p>
266
- <p><code>Enable DTMF </code> - If enabled then <code>dtmf</code> events will be sent to the event Hook </p>
267
- <p><code>Event Hook </code> - A webhook url to invoke when a Lex event occurs (e.g intent detected, transcription, etc) </p>
268
- <p><code>Action Hook </code> - ?? </p>
269
-
270
- <p><code>Prompt audio</code> - Controls whether jambonz wil playback the audio received from Lex or use its own text to speech to render the response </p>
271
- <p><code>Vendor</code> - If using TTS then which vendor to use </p>
272
- <p><code>Language </code> - If using TTS then which language to use </p>
273
- <p><code>Voice </code> - If using TTS then which voice to use </p>
274
- <p><code> </code> - </p>
275
- <p><code> </code> - </p>
276
-
277
- <h3>References</h3>
278
- <ul>
279
- <li><a href="https://www.jambonz.org/docs/webhooks/lex/">Jambonz lex reference</a></li>
280
- </ul>
281
- </script>
package/src/nodes/lex.js DELETED
@@ -1,69 +0,0 @@
1
- var {appendVerb, new_resolve} = require('./libs')
2
-
3
- module.exports = function(RED) {
4
- /** lex */
5
- function lex(config) {
6
- RED.nodes.createNode(this, config);
7
- var node = this;
8
- const awsCreds = RED.nodes.getNode(config.aws);
9
- node.on('input', async function(msg) {
10
- let accessKey, secretAccessKey;
11
- if (awsCreds && awsCreds.credentials) {
12
- accessKey = awsCreds.credentials.accessKey;
13
- secretAccessKey = awsCreds.credentials.secretAccessKey;
14
- }
15
- var eventHook = await new_resolve(RED, config.eventHook, config.eventHookType, node, msg);
16
- var actionHook = await new_resolve(RED, config.actionHook, config.actionHookType, node, msg);
17
- var botId = await new_resolve(RED, config.bot, config.botType, node, msg);
18
- var botAlias = await new_resolve(RED, config.alias, config.aliasType, node, msg);
19
- var locale = await new_resolve(RED, config.locale, config.localeType, node, msg) || 'en_US';
20
- var val = await new_resolve(RED, config.inputTimeout, config.inputTimeoutType, node, msg);
21
- var timeout = /^\d+$/.test(val) ? parseInt(val) : 0;
22
- var slots, intentName;
23
- if (config.specifyIntent) {
24
- intentName = await new_resolve(RED, config.intent, config.intentType, node, msg);
25
- if (intentName) {
26
- slots = await new_resolve(RED, config.slots, config.slotsType, node, msg);
27
- }
28
- }
29
- var metadata = await new_resolve(RED, config.metadata, config.metadataType, node, msg);
30
-
31
- const obj = {
32
- verb: 'lex',
33
- botId,
34
- botAlias,
35
- region: config.region,
36
- locale,
37
- bargein: config.bargein,
38
- passDtmf: config.passDtmf
39
- };
40
- if (accessKey) Object.assign(obj, {credentials: {accessKey, secretAccessKey}});
41
- if (eventHook && eventHook.length > 0) obj.eventHook = eventHook;
42
- if (actionHook && actionHook.length > 0) obj.actionHook = actionHook;
43
- if (!config.specifyIntent && config.welcomeMessage && config.welcomeMessage.length) obj.welcomeMessage = config.welcomeMessage;
44
- if (timeout) obj.noInputTimeout = timeout;
45
- if (config.prompt === 'tts') {
46
- obj.tts = {
47
- vendor: config.vendor,
48
- language: config.lang,
49
- voice: config.voice
50
- };
51
- }
52
-
53
- if (intentName) {
54
- const intent = {name: intentName};
55
- if (slots && typeof slots === 'object' && Object.keys(slots).length > 0) {
56
- intent.slots = slots;
57
- }
58
- obj.intent = intent;
59
- }
60
- if (metadata && typeof metadata === 'object' && Object.keys(metadata).length > 0) {
61
- obj.metadata = metadata;
62
- }
63
-
64
- appendVerb(msg, obj);
65
- node.send(msg);
66
- });
67
- }
68
- RED.nodes.registerType('lex', lex);
69
- }
@@ -1,66 +0,0 @@
1
- <script type="text/javascript">
2
- RED.nodes.registerType('audio in',{
3
- category: 'jambonz',
4
- color: '#f093ea',
5
- defaults: {
6
- name: {value: ''},
7
- path: {value: ''},
8
- bucket: {value: '', required: true},
9
- aws: {value: '', type: 'aws_auth'}
10
- },
11
- inputs:0,
12
- outputs:1,
13
- icon: 'font-awesome/fa-headphones',
14
- label: function() { return this.name || 'audio in';},
15
- });
16
- </script>
17
-
18
- <script type="text/html" data-template-name="audio in">
19
- <div class="form-row ui-state-error">
20
- <strong>Attention:</strong>
21
- This node has been deprecated. <a href="https://github.com/jambonz/node-red-contrib-jambonz/issues/53" target="_blank">See issue #53<i class="fa fa-external-link external-link"></i></a> for more details.
22
- </div>
23
- <div class="form-row">
24
- <label for="node-input-name"><i class="icon-tag"></i> Name</label>
25
- <input type="text" id="node-input-name" placeholder="Name">
26
- </div>
27
- <div class="form-row">
28
- <label for="node-input-aws">AWS credentials</label>
29
- <input type="text" id="node-input-aws">
30
- </div>
31
- <div class="form-row">
32
- <label for="node-input-path">Path</label>
33
- <input type="text" id="node-input-path" placeholder="/socket">
34
- </div>
35
- <div class="form-row">
36
- <label for="node-input-path">S3 Bucket Access Point</label>
37
- <input type="text" id="node-input-bucket" placeholder="arn:aws:s3:us-east-1:1234567890:accesspoint/mybucket">
38
- </div>
39
- </script>
40
-
41
- <script type="text/html" data-help-name="audio in">
42
- <div class="ui-state-error">
43
- <strong>Attention:</strong>
44
- This node has been deprecated.
45
- </div>
46
- <p>Receives audio from jambonz and uploads to S3</p>
47
- <h3>Outputs</h3>
48
- <dl class="message-properties">
49
- <dt>msg.event<span class="property-type">enum</span></dt>
50
- <dd><code>newSession</code> for the start of a new session<br>
51
- <code>partUploaded</code> when a chunk has been uploaded to S3<br>
52
- <code>finishedUpload</code> when the session has ended and the upload completed
53
- </dd>
54
- <dt>msg.payload<span class="property-type">object</span></dt>
55
- <dd>contains details of the newSession (callid, to from etc.) or the details of the upload to S3.
56
- </dd>
57
- </dl>
58
-
59
- <h3>Details</h3>
60
- <dl>
61
- <dt>node-input-path</dt>
62
- <dd>URL path to listen for incoming audio stream on; i.e. a webhook defined in a dial or listen verb</dd>
63
- <dt>S3 Bucket Access Point </dt>dt>
64
- <dd>The access point of an S3 bucket to upload recording to. This must allow public access.</dd>
65
- </dl>
66
- </script>
@@ -1,140 +0,0 @@
1
- module.exports = function(RED) {
2
- var WebSocket = require('ws');
3
- var url = require('url');
4
- var AWS = require('aws-sdk') ;
5
- var S3Stream = require('s3-upload-stream');
6
-
7
- var serverUpgradeAdded = false;
8
- function handleServerUpgrade(request, socket, head) {
9
- const pathname = url.parse(request.url).pathname;
10
- if (listenerNodes.hasOwnProperty(pathname)) {
11
- listenerNodes[pathname].server.handleUpgrade(request, socket, head, function done(ws) {
12
- listenerNodes[pathname].server.emit('connection', ws, request);
13
- });
14
- } else {
15
- // Don't destroy the socket as other listeners may want to handle the
16
- // event.
17
- }
18
- }
19
- var listenerNodes = {};
20
-
21
- // A node red node that sets up a local websocket server
22
- function WebSocketListenerNode(n) {
23
- // Create a RED node
24
- RED.nodes.createNode(this, n);
25
- var node = this;
26
-
27
- node.warn("This node has been deprecated. See https://github.com/jambonz/node-red-contrib-jambonz/issues/53");
28
-
29
- // Get AWS Creds
30
- const awsCreds = RED.nodes.getNode(n.aws);
31
- if (awsCreds && awsCreds.credentials) {
32
- AWS.Credentials({
33
- accessKeyId: awsCreds.credentials.accessKey,
34
- secretAccessKey: awsCreds.credentials.secretAccessKey
35
- });
36
- }
37
-
38
- // Store local copies of the node configuration (as defined in the .html)
39
- [
40
- 'path',
41
- 'bucket'
42
- ].forEach(function(attr) { node[attr] = n[attr];});
43
-
44
- node._clients = {};
45
- node.closing = false;
46
-
47
- function handleConnection(/*socket*/socket) {
48
- var id = (1 + Math.random() * 4294967295).toString(16);
49
- node._clients[id] = socket;
50
- node.emit('opened', {count: Object.keys(node._clients).length, id:id});
51
- socket.on('open', function() {
52
- node.emit('opened', {count:'', id:id});
53
- });
54
- socket.on('close', function() {
55
- delete node._clients[id];
56
- node.emit('closed', {count:Object.keys(node._clients).length,id:id});
57
- });
58
- socket.on('message', function(data) {
59
- // first message is a JSON object containing metadata
60
- try {
61
- socket.removeAllListeners('message');
62
- node.metadata = JSON.parse(data);
63
- var msg = {payload : node.metadata}
64
- msg.event = 'newSession'
65
- node.send(msg)
66
- const md = {
67
- callSid: node.metadata.callSid,
68
- accountSid: node.metadata.accountSid,
69
- applicationSid: node.metadata.applicationSid,
70
- from: node.metadata.from,
71
- to: node.metadata.to,
72
- callId: node.metadata.callId
73
- };
74
- if (node.metadata.parentCallSid) md.parentCallSid = node.metadata.parentCallSid;
75
- const s3Stream = new S3Stream(new AWS.S3());
76
- const upload = s3Stream.upload({
77
- Bucket: node.bucket,
78
- Key: `${node.metadata.callSid}.L16`,
79
- ACL: 'public-read',
80
- ContentType: `audio/L16;rate=${node.metadata.sampleRate};channels=${node.metadata.mixType === 'stereo' ? 2 : 1}`,
81
- Metadata: md
82
- });
83
- upload.on('error', function(err) {
84
- node.error(`Error uploading: ${JSON.stringify(err)}`);
85
- });
86
- upload.on('part', function(details) {
87
- var msg = {payload : details}
88
- msg.event = 'partUploaded'
89
- node.send(msg)
90
- });
91
- upload.on('uploaded', function(details) {
92
- var msg = {payload : details}
93
- msg.event = 'finishedUpload'
94
- node.send(msg)
95
- });
96
- const duplex = WebSocket.createWebSocketStream(socket);
97
- duplex.pipe(upload);
98
- } catch (err) {
99
- node.error(`Error starting upload: ${err.message}`);
100
- }
101
- });
102
- socket.on('error', function(err) {
103
- node.emit('error', {err:err, id:id});
104
- });
105
- }
106
-
107
- if (!serverUpgradeAdded) {
108
- RED.server.on('upgrade', handleServerUpgrade);
109
- serverUpgradeAdded = true;
110
- }
111
-
112
- var path = RED.settings.httpNodeRoot || '/';
113
- path = path + (path.slice(-1) == '/' ? '' : '/') + (node.path.charAt(0) == '/' ?
114
- node.path.substring(1) :
115
- node.path);
116
- node.fullPath = path;
117
-
118
- if (listenerNodes.hasOwnProperty(path)) {
119
- node.error(RED._('websocket.errors.duplicate-path', {path: node.path}));
120
- return;
121
- }
122
- listenerNodes[node.fullPath] = node;
123
- var serverOptions = {
124
- noServer: true
125
- }
126
- if (RED.settings.webSocketNodeVerifyClient) {
127
- serverOptions.verifyClient = RED.settings.webSocketNodeVerifyClient;
128
- }
129
- // Create a WebSocket Server
130
- node.server = new WebSocket.Server(serverOptions);
131
- node.server.setMaxListeners(0);
132
- node.server.on('connection', handleConnection);
133
-
134
- node.on('close', function() {
135
- delete listenerNodes[node.fullPath];
136
- node.server.close();
137
- });
138
- }
139
- RED.nodes.registerType('audio in', WebSocketListenerNode);
140
- }