@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
@@ -1,1032 +0,0 @@
1
- var {createHash} = require('crypto');
2
- const bent = require('bent');
3
- var mustache = require('mustache');
4
- mustache.escape = function(text) {return text;};
5
-
6
- /** user auth */
7
- module.exports = function(RED) {
8
- function userauth(config) {
9
- RED.nodes.createNode(this, config);
10
- var node = this;
11
- node.on('input', function(msg) {
12
- var attemptedAuthentication = false;
13
- var auth = msg.authRequest;
14
- var authResponse = {};
15
- var ha1_string;
16
- if (config.ha1 && config.ha1.length) {
17
- ha1_string = v_resolve(config.ha1, config.ha1Type, this.context(), msg);
18
- attemptedAuthentication = true;
19
- node.log(`user auth: validating user ${auth.username} domain ${auth.realm} based on hashed password`);
20
- }
21
- else if (config.password && config.password.length) {
22
- var password = v_resolve(config.password, config.passwordType, this.context(), msg);
23
- var ha1 = createHash('md5');
24
- ha1.update([auth.username, auth.realm, password].join(':'));
25
- ha1_string = ha1.digest('hex');
26
- attemptedAuthentication = true;
27
- node.log(`user auth: validating user ${auth.username} domain ${auth.realm} based on plaintext password`);
28
- }
29
- else {
30
- node.log('user auth: failing due to no password provided');
31
- }
32
-
33
- if (attemptedAuthentication) {
34
- var ha2 = createHash('md5');
35
- ha2.update([auth.method, auth.uri].join(':'));
36
- var response = createHash('md5');
37
- var responseParams = [
38
- ha1_string,
39
- auth.nonce
40
- ];
41
-
42
- if (auth.cnonce) {
43
- responseParams.push(auth.nc);
44
- responseParams.push(auth.cnonce);
45
- }
46
-
47
- if (auth.qop) {
48
- responseParams.push(auth.qop);
49
- }
50
-
51
- responseParams.push(ha2.digest('hex'));
52
- response.update(responseParams.join(':'));
53
-
54
- var calculated = response.digest('hex');
55
- if (calculated === auth.response) {
56
- Object.assign(authResponse, {status: 'ok'});
57
- }
58
- else {
59
- Object.assign(authResponse, {status: 'fail', msg: 'incorrect password'});
60
- }
61
- }
62
- else {
63
- Object.assign(authResponse, {status: 'fail', msg: 'invalid domain or username'});
64
- }
65
-
66
- msg.authResponse = authResponse;
67
- node.send(msg);
68
- });
69
- }
70
- RED.nodes.registerType('user auth', userauth);
71
-
72
- /** play */
73
- function play(config) {
74
- RED.nodes.createNode(this, config);
75
- var node = this;
76
- node.on('input', function(msg) {
77
- appendVerb(msg, {
78
- verb: 'play',
79
- url: v_resolve(config.url, config.urlType, this.context(), msg),
80
- earlyMedia: config.early,
81
- loop: config.loop
82
- });
83
- node.send(msg);
84
- });
85
- }
86
- RED.nodes.registerType('play', play);
87
-
88
- /** pause */
89
- function pause(config) {
90
- RED.nodes.createNode(this, config);
91
- var node = this;
92
- node.on('input', function(msg) {
93
- var val = v_resolve(config.len, config.lenType, this.context(), msg);
94
- var length = /^\d+$/.test(val) ? parseInt(val) : parseFloat(val);
95
- appendVerb(msg, {
96
- verb: 'pause',
97
- length
98
- });
99
- node.send(msg);
100
- });
101
- }
102
- RED.nodes.registerType('pause', pause);
103
-
104
- /** tag */
105
- function tag(config) {
106
- RED.nodes.createNode(this, config);
107
- var node = this;
108
- node.on('input', function(msg) {
109
- var data = v_resolve(config.data, config.dataType, this.context(), msg, true);
110
- appendVerb(msg, {
111
- verb: 'tag',
112
- data
113
- });
114
- node.send(msg);
115
- });
116
- }
117
- RED.nodes.registerType('tag', tag);
118
-
119
- /** leave */
120
- function leave(config) {
121
- RED.nodes.createNode(this, config);
122
- var node = this;
123
- node.on('input', function(msg, send, done) {
124
- appendVerb(msg, {
125
- verb: 'leave'
126
- });
127
- node.send(msg);
128
- });
129
- }
130
- RED.nodes.registerType('leave', leave);
131
-
132
- /** lex */
133
- function lex(config) {
134
- RED.nodes.createNode(this, config);
135
- var node = this;
136
- const awsCreds = RED.nodes.getNode(config.aws);
137
- node.log(`lex config: ${JSON.stringify(config)}`);
138
- node.log(`awsCreds: ${JSON.stringify(awsCreds)}`);
139
- node.on('input', function(msg) {
140
- let accessKey, secretAccessKey;
141
- if (awsCreds && awsCreds.credentials) {
142
- accessKey = awsCreds.credentials.accessKey;
143
- secretAccessKey = awsCreds.credentials.secretAccessKey;
144
- }
145
- var botId = v_resolve(config.bot, config.botType, this.context(), msg);
146
- var botAlias = v_resolve(config.alias, config.aliasType, this.context(), msg);
147
- var locale = v_resolve(config.locale, config.localeType, this.context(), msg) || 'en_US';
148
- var val = v_resolve(config.inputTimeout, config.inputTimeoutType, this.context(), msg);
149
- var timeout = /^\d+$/.test(val) ? parseInt(val) : 0;
150
- var eventHook = v_resolve(config.eventHook, config.eventHookType, this.context(), msg);
151
- var actionHook = v_resolve(config.actionHook, config.actionHookType, this.context(), msg);
152
- var metadata = v_resolve(config.metadata, config.metadataType, this.context(), msg);
153
- var slots, intentName;
154
-
155
- if (config.specifyIntent) {
156
- intentName = v_resolve(config.intent, config.intentType, this.context(), msg);
157
- if (intentName) {
158
- slots = v_resolve(config.slots, config.slotsType, this.context(), msg);
159
- }
160
- }
161
-
162
- const obj = {
163
- verb: 'lex',
164
- botId,
165
- botAlias,
166
- region: config.region,
167
- locale,
168
- bargein: config.bargein,
169
- passDtmf: config.passDtmf
170
- };
171
- if (accessKey) Object.assign(obj, {credentials: {accessKey, secretAccessKey}});
172
- if (eventHook && eventHook.length > 0) obj.eventHook = eventHook;
173
- if (actionHook && actionHook.length > 0) obj.actionHook = actionHook;
174
- if (!config.specifyIntent && config.welcomeMessage && config.welcomeMessage.length) obj.welcomeMessage = config.welcomeMessage;
175
- if (timeout) obj.noInputTimeout = timeout;
176
- if (config.prompt === 'tts') {
177
- obj.tts = {
178
- vendor: config.vendor,
179
- language: config.lang,
180
- voice: config.voice
181
- };
182
- }
183
-
184
- if (intentName) {
185
- const intent = {name: intentName};
186
- if (slots && typeof slots === 'object' && Object.keys(slots).length > 0) {
187
- intent.slots = slots;
188
- }
189
- obj.intent = intent;
190
- }
191
- if (metadata && typeof metadata === 'object' && Object.keys(metadata).length > 0) {
192
- obj.metadata = metadata;
193
- }
194
-
195
- appendVerb(msg, obj);
196
- node.send(msg);
197
- });
198
- }
199
- RED.nodes.registerType('lex', lex);
200
-
201
- /** listen */
202
- function listen(config) {
203
- RED.nodes.createNode(this, config);
204
- var node = this;
205
- node.on('input', function(msg) {
206
- const obj = {
207
- verb: 'listen',
208
- url: v_resolve(config.url, config.urlType, this.context(), msg),
209
- actionHook: v_resolve(config.actionhook, config.actionhookType, this.context(), msg),
210
- finishOnKey: config.finishonkey,
211
- mixType: config.mixtype,
212
- playBeep: config.beep,
213
- sampleRate: config.sampleRate,
214
- };
215
-
216
- if (config.transcriptionhook) {
217
- const recognizer = {
218
- vendor: config.transcriptionvendor,
219
- language: config.recognizerlang,
220
- interim: config.interim,
221
- separateRecognitionPerChannel: config.mixtype === 'stereo' && config.separaterecog,
222
- diarization: config.diarization
223
- };
224
- if (recognizer.vendor === 'google') {
225
- var diarizationMin = v_resolve(config.diarizationmin, config.diarizationminType, this.context(), msg);
226
- var diarizationMax = v_resolve(config.diarizationmax, config.diarizationmaxType, this.context(), msg);
227
- var hints = v_resolve(config.transcriptionhints, config.transcriptionhintsType, this.context(), msg);
228
- var altlangs = v_resolve(config.recognizeraltlang, config.recognizeraltlangType, this.context(), msg);
229
- var naics = v_resolve(config.naics, config.naicsType, this.context(), msg);
230
- Object.assign(recognizer, {
231
- profanityFilter: config.profanityfilter,
232
- hints: hints.length > 0 ?
233
- hints.split(',').map((w) => w.trim()) :
234
- [],
235
- punctuation: config.punctuation,
236
- enhancedModel: config.useenhanced,
237
- words: config.words,
238
- interactionType: config.interactiontype,
239
- });
240
- if (recognizer.diarization) {
241
- if (diarizationMin) recognizer.diarizationMinSpeakers = parseInt(diarizationMin) || 0;
242
- if (diarizationMax) recognizer.diarizationMaxSpeakers = parseInt(diarizationMax) || 0;
243
- }
244
- if (naics) recognizer.naicsCode = parseInt(naics) || 0;
245
- if (altlangs) {
246
- recognizer.altLanguages = altlangs.split(',').map((e) => e.trim());
247
- }
248
- }
249
- else if (recognizer.vendor === 'aws') {
250
- var vocab = v_resolve(config.vocabularyname, config.vocabularynameType, this.context(), msg);
251
- var vocabFilter = v_resolve(config.vocabularyfiltername, config.vocabularynameType, this.context(), msg);
252
- Object.assign(recognizer, {
253
- vocabularyName: vocab,
254
- vocabularyFilterName: vocabFilter,
255
- filterMethod: config.vocabularyfiltermethod
256
- });
257
- }
258
- obj.transcribe = {
259
- transcriptionHook: v_resolve(config.transcriptionhook, config.transcriptionhookType, this.context(), msg),
260
- recognizer
261
- };
262
- }
263
- if (/^\d+$/.test(config.timeout)) obj.timeout = parseInt(config.timeout);
264
- if (/^\d+$/.test(config.maxlength)) obj.maxLength = parseInt(config.maxLength);
265
- if (config.finishonkey.length) obj.finishOnKey = config.finishonkey;
266
-
267
- var data = v_resolve(config.metadata, config.metadataType, this.context(), msg, true);
268
- if (data) obj.metadata = data;
269
-
270
- appendVerb(msg, obj);
271
- node.send(msg);
272
- });
273
- }
274
- RED.nodes.registerType('listen', listen);
275
-
276
- /** message */
277
- function message(config) {
278
- RED.nodes.createNode(this, config);
279
- var node = this;
280
- node.on('input', function(msg, send, done) {
281
- var from = v_resolve(config.from, config.fromType, this.context(), msg);
282
- var to = v_resolve(config.to, config.toType, this.context(), msg);
283
- var text = v_resolve(config.text, config.textType, this.context(), msg);
284
- var provider = v_resolve(config.provider, config.providerType, this.context(), msg);
285
- if ((!provider || 0 === provider.length) && msg.sms.provider) {
286
- provider = msg.sms.provider;
287
- }
288
-
289
- appendVerb(msg, {
290
- verb: 'message',
291
- from,
292
- to,
293
- text,
294
- provider
295
- });
296
- node.send(msg);
297
- });
298
- }
299
- RED.nodes.registerType('message', message);
300
-
301
- /** hangup */
302
- function hangup(config) {
303
- RED.nodes.createNode(this, config);
304
- var node = this;
305
- node.on('input', function(msg) {
306
- appendVerb(msg, {
307
- verb: 'hangup'
308
- });
309
- node.send(msg);
310
- });
311
- }
312
- RED.nodes.registerType('hangup', hangup);
313
-
314
- /** sip:decline */
315
- function sip_decline(config) {
316
- RED.nodes.createNode(this, config);
317
- var node = this;
318
- node.on('input', function(msg) {
319
- var status = v_resolve(config.status, config.statusType, this.context(), msg);
320
- var reason = v_resolve(config.reason, config.reasonType, this.context(), msg);
321
- appendVerb(msg, {
322
- verb: 'sip:decline',
323
- status: parseInt(status),
324
- reason
325
- });
326
- node.send(msg);
327
- });
328
- }
329
- RED.nodes.registerType('sip:decline', sip_decline);
330
-
331
- /** redirect */
332
- function redirect(config) {
333
- RED.nodes.createNode(this, config);
334
- var node = this;
335
- node.on('input', function(msg, send, done) {
336
- var actionHook = v_resolve(config.hook, config.hookType, this.context(), msg);
337
- appendVerb(msg, {
338
- verb: 'redirect',
339
- actionHook
340
- });
341
- node.send(msg);
342
- });
343
- }
344
- RED.nodes.registerType('redirect', redirect);
345
-
346
- /** dequeue */
347
- function dequeue(config) {
348
- RED.nodes.createNode(this, config);
349
- var node = this;
350
- node.on('input', function(msg, send, done) {
351
- appendVerb(msg, {
352
- verb: 'dequeue',
353
- name: v_resolve(config.queue, config.queueType, this.context(), msg),
354
- beep: config.beep,
355
- actionHook: v_resolve(config.actionHook, config.actionHookType, this.context(), msg),
356
- confirmHook: v_resolve(config.confirmHook, config.confirmHookType, this.context(), msg),
357
- timeout: v_resolve(config.timeout, config.timeoutType, this.context(), msg)
358
- });
359
- node.send(msg);
360
- });
361
- }
362
- RED.nodes.registerType('dequeue', dequeue);
363
-
364
- /** enqueue */
365
- function enqueue(config) {
366
- RED.nodes.createNode(this, config);
367
- var node = this;
368
- node.on('input', function(msg, send, done) {
369
- appendVerb(msg, {
370
- verb: 'enqueue',
371
- name: v_resolve(config.queue, config.queueType, this.context(), msg),
372
- actionHook: v_resolve(config.actionHook, config.actionHookType, this.context(), msg),
373
- waitHook: v_resolve(config.waitHook, config.waitHookType, this.context(), msg)
374
- });
375
- node.send(msg);
376
- });
377
- }
378
- RED.nodes.registerType('enqueue', enqueue);
379
-
380
- /** conference */
381
- function conference(config) {
382
- RED.nodes.createNode(this, config);
383
- var node = this;
384
- node.on('input', function(msg) {
385
- var val = v_resolve(config.maxParticipants, config.maxParticipantsType, this.context(), msg);
386
- var maxParticipants = /^\d+$/.test(val) ? parseInt(val) : val;
387
-
388
- appendVerb(msg, {
389
- verb: 'conference',
390
- name: v_resolve(config.conference, config.conferenceType, this.context(), msg),
391
- enterHook: v_resolve(config.enterHook, config.enterHookType, this.context(), msg),
392
- waitHook: v_resolve(config.waitHook, config.waitHookType, this.context(), msg),
393
- maxParticipants,
394
- beep: config.beep,
395
- startOnEnter: config.startOnEnter,
396
- endOnExit: config.endOnExit
397
- });
398
- node.send(msg);
399
- });
400
- }
401
- RED.nodes.registerType('conference', conference);
402
-
403
- /** say */
404
- function say(config) {
405
- RED.nodes.createNode(this, config);
406
- this.text = config.text;
407
- this.early = config.early;
408
- this.loop = config.loop;
409
- var node = this;
410
-
411
- node.on('input', function(msg) {
412
-
413
- node.log(`say config: ${JSON.stringify(config)}, msg.call: ${JSON.stringify(msg.call)}`);
414
- const text = v_text_resolve(node, this.text, this.context(), msg);
415
-
416
- // jambonz say verb
417
- var obj = {
418
- verb: 'say',
419
- text,
420
- loop: parseInt(node.loop),
421
- earlyMedia: node.early
422
- };
423
- if (['aws', 'google'].includes(node.vendor)) {
424
- Object.assign(obj, {
425
- synthesizer: {
426
- vendor: config.vendor,
427
- language: config.lang,
428
- voice: config.voice
429
- }
430
- });
431
- }
432
-
433
- appendVerb(msg, obj);
434
- node.send(msg);
435
- });
436
- }
437
- RED.nodes.registerType('say', say);
438
- require('./http-helpers')(RED);
439
-
440
- /** gather */
441
- function gather(config) {
442
- RED.nodes.createNode(this, config);
443
- var node = this;
444
- node.on('input', function(msg) {
445
-
446
- // simple properties
447
- node.log(`config: ${JSON.stringify(config)}`);
448
-
449
- var obj = {verb: 'gather', input: []};
450
- if (config.actionhook) obj.actionHook = config.actionhook;
451
-
452
- // input
453
- if (config.speechinput) {
454
- obj.input.push('speech');
455
- const recognizer = {
456
- vendor: config.transcriptionvendor,
457
- language: config.recognizerlang
458
- };
459
- if (recognizer.vendor === 'google') {
460
- var hints = v_resolve(config.transcriptionhints, config.transcriptionhintsType, this.context(), msg);
461
- var altlangs = v_resolve(config.recognizeraltlang, config.recognizeraltlangType, this.context(), msg);
462
- var naics = v_resolve(config.naics, config.naicsType, this.context(), msg);
463
- Object.assign(recognizer, {
464
- profanityFilter: config.profanityfilter,
465
- hints: hints.length > 0 ?
466
- hints.split(',').map((w) => w.trim()) :
467
- []
468
- });
469
- if (naics) recognizer.naicsCode = parseInt(naics) || 0;
470
- if (altlangs) {
471
- recognizer.altLanguages = altlangs.split(',').map((e) => e.trim());
472
- }
473
- }
474
- else if (recognizer.vendor === 'aws') {
475
- var vocab = v_resolve(config.vocabularyname, config.vocabularynameType, this.context(), msg);
476
- var vocabFilter = v_resolve(config.vocabularyfiltername, config.vocabularynameType, this.context(), msg);
477
- Object.assign(recognizer, {
478
- vocabularyName: vocab,
479
- vocabularyFilterName: vocabFilter,
480
- filterMethod: config.vocabularyfiltermethod
481
- });
482
- }
483
- obj.recognizer = recognizer;
484
- }
485
- if (config.dtmfinput) {
486
- obj.input.push('digits');
487
- if (config.finishonkey && config.finishonkey.length) obj.finishOnKey = obj.finishonkey;
488
- if (/^\d+$/.test(config.numdigits)) obj.numDigits = parseInt(config.numdigits);
489
- if (/^\d+$/.test(config.timeout)) obj.timeout = parseInt(config.timeout);
490
- }
491
-
492
- // prompt
493
- if (config.prompttype === 'say') {
494
- obj.say = {text: v_text_resolve(node, config.text, this.context(), msg)};
495
- if (['aws', 'google'].includes(config.vendor)) {
496
- Object.assign(obj.say, {
497
- synthesizer: {
498
- vendor: config.vendor,
499
- language: config.lang,
500
- voice: config.voice
501
- }
502
- });
503
- }
504
- }
505
- else obj.play = {url: v_resolve(config.playurl, config.playurlType, this.context(), msg)};
506
-
507
- node.log(`gather: ${JSON.stringify(obj)}`);
508
-
509
- appendVerb(msg, obj);
510
- node.send(msg);
511
- });
512
- }
513
- RED.nodes.registerType('gather', gather);
514
-
515
- function dial(config) {
516
- RED.nodes.createNode(this, config);
517
- var node = this;
518
-
519
- node.on('input', function(msg) {
520
-
521
- node.log(`dial config: ${JSON.stringify(config)}, msg.call: ${JSON.stringify(msg.call)}`);
522
- var target = config.targets.map((t) => {
523
- const obj = Object.assign({}, t);
524
- var dest = v_resolve(obj.dest, obj.varType, this.context(), msg);
525
- node.log(`dial: dest ${t.varType}:${t.dest} resolved to ${dest}`);
526
- switch (t.type) {
527
- case 'phone':
528
- obj.number = dest;
529
- break;
530
- case 'user':
531
- obj.name = dest;
532
- break;
533
- case 'sip':
534
- obj.sipUri = dest;
535
- if (t.user) {
536
- obj.auth = {
537
- username: t.user,
538
- password: t.pass
539
- };
540
- }
541
- delete obj.user;
542
- delete obj.pass;
543
- break;
544
- case 'teams':
545
- obj.number = dest;
546
- break;
547
- }
548
- delete obj.varType;
549
- delete obj.dest;
550
- return obj;
551
- });
552
- var data = {
553
- verb: 'dial',
554
- target,
555
- answerOnBridge: config.answeronbridge,
556
- timeLimit: config.timelimit ? parseInt(config.timelimit) : null,
557
- timeout: config.timeout ? parseInt(config.timeout) : null,
558
- callerId: v_resolve(config.callerid, config.calleridType, this.context(), msg),
559
- actionHook: v_resolve(config.actionhook, config.actionhookType, this.context(), msg),
560
- confirmHook: v_resolve(config.confirmhook, config.confirmhookType, this.context(), msg),
561
- dialMusic: v_resolve(config.dialmusic, config.dialmusicType, this.context(), msg),
562
- dtmfHook: v_resolve(config.dtmfhook, config.dtmfhookType, this.context(), msg)
563
- };
564
-
565
-
566
- // headers
567
- var headers = {};
568
- config.headers.forEach(function(h) {
569
- if (h.h.length && h.v.length) headers[h.h] = h.v;
570
- });
571
- Object.assign(data, {headers});
572
-
573
- // nested listen
574
- if (config.listenurl && config.listenurl.length > 0) {
575
- data.listen = {
576
- url: v_resolve(config.listenurl, config.listenurlType, this.context(), msg),
577
- mixType: 'stereo'
578
- };
579
- }
580
-
581
- // nested transcribe
582
- if (config.transcriptionhook) {
583
- const recognizer = {
584
- vendor: config.transcriptionvendor,
585
- language: config.recognizerlang,
586
- interim: config.interim,
587
- separateRecognitionPerChannel: config.mixtype === 'stereo' && config.separaterecog,
588
- diarization: config.diarization
589
- };
590
- if (recognizer.vendor === 'google') {
591
- var diarizationMin = v_resolve(config.diarizationmin, config.diarizationminType, this.context(), msg);
592
- var diarizationMax = v_resolve(config.diarizationmax, config.diarizationmaxType, this.context(), msg);
593
- var hints = v_resolve(config.transcriptionhints, config.transcriptionhintsType, this.context(), msg);
594
- var altlangs = v_resolve(config.recognizeraltlang, config.recognizeraltlangType, this.context(), msg);
595
- var naics = v_resolve(config.naics, config.naicsType, this.context(), msg);
596
- Object.assign(recognizer, {
597
- profanityFilter: config.profanityfilter,
598
- hints: hints.length > 0 ?
599
- hints.split(',').map((w) => w.trim()) :
600
- [],
601
- punctuation: config.punctuation,
602
- enhancedModel: config.useenhanced,
603
- words: config.words,
604
- interactionType: config.interactiontype,
605
- });
606
- if (recognizer.diarization) {
607
- if (diarizationMin) recognizer.diarizationMinSpeakers = parseInt(diarizationMin) || 0;
608
- if (diarizationMax) recognizer.diarizationMaxSpeakers = parseInt(diarizationMax) || 0;
609
- }
610
- if (naics) recognizer.naicsCode = parseInt(naics) || 0;
611
- if (altlangs) {
612
- recognizer.altLanguages = altlangs.split(',').map((e) => e.trim());
613
- }
614
- }
615
- else if (recognizer.vendor === 'aws') {
616
- var vocab = v_resolve(config.vocabularyname, config.vocabularynameType, this.context(), msg);
617
- var vocabFilter = v_resolve(config.vocabularyfiltername, config.vocabularynameType, this.context(), msg);
618
- Object.assign(recognizer, {
619
- vocabularyName: vocab,
620
- vocabularyFilterName: vocabFilter,
621
- filterMethod: config.vocabularyfiltermethod
622
- });
623
- }
624
- data.transcribe = {
625
- transcriptionHook: v_resolve(config.transcriptionhook, config.transcriptionhookType, this.context(), msg),
626
- recognizer
627
- };
628
- }
629
- // dtmf capture
630
- const dtmfCapture = v_resolve(config.dtmfcapture, config.dtmfcaptureType, this.context(), msg);
631
- if (dtmfCapture && dtmfCapture.length) {
632
- data.dtmfCapture = dtmfCapture.split(',').map((i) => i.trim());
633
- }
634
- else {
635
- delete data.dtmfHook;
636
- }
637
-
638
-
639
- node.log(`dial verb: ${JSON.stringify(data)}`);
640
-
641
- appendVerb(msg, data);
642
- node.log(`dial jambonz: ${JSON.stringify(msg.jambonz)}`);
643
- node.send(msg);
644
- });
645
- }
646
- RED.nodes.registerType('dial', dial);
647
-
648
- /** dialogflow */
649
- function dialogflow(config) {
650
- RED.nodes.createNode(this, config);
651
- var node = this;
652
- node.on('input', function(msg) {
653
- var val = v_resolve(config.inputTimeout, config.inputTimeoutType, this.context(), msg);
654
- var timeout = /^\d+$/.test(val) ? parseInt(val) : 0;
655
- var eventHook = v_resolve(config.eventHook, config.eventHookType, this.context(), msg);
656
- var actionHook = v_resolve(config.actionHook, config.actionHookType, this.context(), msg);
657
- var welcomeEvent = v_resolve(config.welcomeEvent, config.welcomeEventType, this.context(), msg);
658
- var environment = v_resolve(config.environment, config.environmentType, this.context(), msg);
659
- var welcomeEventParams;
660
- if (welcomeEvent && welcomeEvent.length > 0) {
661
- welcomeEventParams = v_resolve(config.welcomeEventParams, config.welcomeEventParamsType, this.context(), msg);
662
- }
663
- var noInputEvent = v_resolve(config.noInputEvent, config.noInputEventType, this.context(), msg);
664
- const obj = {
665
- verb: 'dialogflow',
666
- credentials: v_resolve(config.serviceAccountCredentials,
667
- config.serviceAccountCredentialsType, this.context(), msg),
668
- project: v_resolve(config.project, config.projectType, this.context(), msg),
669
- lang: config.recognizerlang,
670
- bargein: config.bargein
671
- };
672
- if (welcomeEvent) {
673
- obj.welcomeEvent = welcomeEvent;
674
- if (welcomeEventParams) obj.welcomeEventParams = welcomeEventParams;
675
- }
676
- if (environment && environment.length > 0) obj.environment = environment;
677
- if (eventHook && eventHook.length > 0) obj.eventHook = eventHook;
678
- if (actionHook && actionHook.length > 0) obj.actionHook = actionHook;
679
- if (timeout) obj.noInputTimeout = timeout;
680
- if (timeout && noInputEvent) obj.noInputEvent = noInputEvent;
681
- if (config.prompt === 'tts') {
682
- obj.tts = {
683
- vendor: config.vendor,
684
- language: config.lang,
685
- voice: config.voice
686
- };
687
- }
688
-
689
- appendVerb(msg, obj);
690
- node.send(msg);
691
- });
692
- }
693
- RED.nodes.registerType('dialogflow', dialogflow);
694
-
695
- function jambonz_auth(config) {
696
- RED.nodes.createNode(this, config);
697
- this.url = config.url;
698
- this.accountSid = config.accountSid;
699
- this.apiKey = config.apiKey;
700
- }
701
-
702
- RED.nodes.registerType('jambonz_auth', jambonz_auth, {
703
- credentials: {
704
- url: {type: 'text'},
705
- accountSid: {type: 'text'},
706
- apiToken: {type: 'text'}
707
- }
708
- });
709
-
710
- function aws_auth(config) {
711
- RED.nodes.createNode(this, config);
712
- this.accessKey = config.accessKey;
713
- this.secretAccessKey = config.secretAccessKey;
714
- }
715
-
716
- RED.nodes.registerType('aws_auth', aws_auth, {
717
- credentials: {
718
- accessKey: {type: 'text'},
719
- secretAccessKey: {type: 'text'}
720
- }
721
- });
722
-
723
- function doLCC(node, baseUrl, accountSid, apiToken, callSid, opts) {
724
- const post = bent(`${baseUrl}/v1/`, 'POST', 'json', 202, {
725
- 'Authorization': `Bearer ${apiToken}`
726
- });
727
- const url = `Accounts/${accountSid}/Calls/${callSid}`;
728
- node.log(`invoking LCC with callSid ${callSid} at ${baseUrl}/v1/${url}`);
729
- return post(url, opts);
730
- }
731
-
732
- function doCreateCall(baseUrl, accountSid, apiToken, opts) {
733
- const post = bent(`${baseUrl}/v1/`, 'POST', 'json', 201, {
734
- 'Authorization': `Bearer ${apiToken}`
735
- });
736
- return post(`Accounts/${accountSid}/Calls`, opts);
737
- }
738
-
739
- function doCreateMessage(baseUrl, accountSid, apiToken, opts) {
740
- const post = bent(`${baseUrl}/v1/`, 'POST', 'json', 201, {
741
- 'Authorization': `Bearer ${apiToken}`
742
- });
743
- return post(`Accounts/${accountSid}/Messages`, opts);
744
- }
745
-
746
-
747
- /** LCC */
748
- function lcc(config) {
749
- RED.nodes.createNode(this, config);
750
- var node = this;
751
- const server = RED.nodes.getNode(config.server);
752
-
753
- node.on('input', async(msg, send, done) => {
754
- send = send || function() { node.send.apply(node, arguments);};
755
-
756
- const {url, accountSid, apiToken} = server.credentials;
757
- const callSid = v_resolve(config.callSid, config.callSidType, this.context(), msg);
758
- if (!url || !accountSid || !apiToken || !callSid) {
759
- node.log(`invalid / missing credentials or callSid, skipping LCC node: ${JSON.stringify(server.credentials)}`);
760
- send(msg);
761
- if (done) done();
762
- return;
763
- }
764
-
765
- const opts = {};
766
- switch (config.action) {
767
- case 'hangup':
768
- opts.call_status = 'completed';
769
- break;
770
- case 'mute':
771
- opts.mute_status = 'mute';
772
- break;
773
- case 'unmute':
774
- opts.mute_status = 'unmute';
775
- break;
776
- case 'pause':
777
- opts.listen_status = 'pause';
778
- break;
779
- case 'resume':
780
- opts.listen_status = 'resume';
781
- break;
782
- case 'redirect':
783
- node.log(`LCC redirect callHook ${config.callHook} callHookType: ${config.callHookType}`);
784
- opts.call_hook = {url: v_resolve(config.callHook, config.callHookType, this.context(), msg)};
785
- break;
786
- case 'whisper':
787
- Object.assign(opts, {
788
- whisper: {
789
- verb: 'say',
790
- text: config.text
791
- }
792
- });
793
- if (['aws', 'google'].includes(config.vendor)) {
794
- Object.assign(opts.whisper, {
795
- synthesizer: {
796
- vendor: config.vendor,
797
- language: config.lang,
798
- voice: config.voice
799
- }
800
- });
801
- }
802
- break;
803
- default:
804
- node.log(`invalid action: ${config.action}`);
805
- send(msg);
806
- if (done) done();
807
- return;
808
- }
809
-
810
- try {
811
- await doLCC(node, url, accountSid, apiToken, callSid, opts);
812
- msg.statusCode = 202;
813
- node.log(`successfully sent LCC with opts ${JSON.stringify(opts)}`);
814
- } catch (err) {
815
- if (err.statusCode) msg.statusCode = err.statusCode;
816
- else {
817
- node.log(`Error sending LCC ${JSON.stringify(err)}`);
818
- if (done) done(err);
819
- else node.error(err, msg);
820
- send(msg);
821
- return;
822
- }
823
- }
824
- send(msg);
825
- if (done) done();
826
- });
827
- }
828
- RED.nodes.registerType('lcc', lcc);
829
-
830
- /** Create call */
831
- function create_call(config) {
832
- RED.nodes.createNode(this, config);
833
- var node = this;
834
- const server = RED.nodes.getNode(config.server);
835
-
836
- node.on('input', async(msg, send, done) => {
837
- send = send || function() { node.send.apply(node, arguments);};
838
-
839
- const {url, accountSid, apiToken} = server.credentials;
840
- if (!url || !accountSid || !apiToken) {
841
- node.log(`invalid / missing credentials, skipping create-call node: ${JSON.stringify(server.credentials)}`);
842
- send(msg);
843
- if (done) done();
844
- return;
845
- }
846
-
847
- var from = v_resolve(config.from, config.fromType, this.context(), msg);
848
- var to = v_resolve(config.to, config.toType, this.context(), msg);
849
-
850
- const opts = {
851
- application_sid: config.application,
852
- from,
853
- to: {
854
- type: config.dest
855
- },
856
- };
857
- if (config.timeout) {
858
- const timeout = parseInt(config.timeout);
859
- if (timeout > 0) opts.timeout = timeout;
860
- }
861
- switch (config.dest) {
862
- case 'phone':
863
- opts.to.number = to;
864
- break;
865
- case 'user':
866
- opts.to.name = to;
867
- break;
868
- case 'sip':
869
- opts.to.sipUri = to;
870
- break;
871
- case 'ms-teams':
872
- opts.to.user = to;
873
- break;
874
- default:
875
- if (done) done(`unknown dest type ${config.dest}`);
876
- else node.error(`unknown dest type ${config.dest}`, msg);
877
- send(msg);
878
- return;
879
- }
880
-
881
- try {
882
- node.log(`sending create call ${JSON.stringify(opts)}`);
883
- const res = await doCreateCall(url, accountSid, apiToken, opts);
884
- msg.statusCode = 202;
885
- msg.callSid = res.sid;
886
- node.log(`successfully launched call with callSid ${msg.callSid}`);
887
- } catch (err) {
888
- if (err.statusCode) {
889
- node.log(`create-call failed with ${err.statusCode}`);
890
- msg.statusCode = err.statusCode;
891
- }
892
- else {
893
- node.log(`Error sending create all ${JSON.stringify(err)}`);
894
- if (done) done(err);
895
- else node.error(err, msg);
896
- send(msg);
897
- return;
898
- }
899
- }
900
- send(msg);
901
- if (done) done();
902
- });
903
- }
904
- RED.nodes.registerType('create-call', create_call);
905
-
906
- /** Create sms */
907
- function create_sms(config) {
908
- RED.nodes.createNode(this, config);
909
- var node = this;
910
- const server = RED.nodes.getNode(config.server);
911
-
912
- node.on('input', async(msg, send, done) => {
913
- send = send || function() { node.send.apply(node, arguments);};
914
-
915
- const {url, accountSid, apiToken} = server.credentials;
916
- if (!url || !accountSid || !apiToken) {
917
- node.log(`invalid / missing credentials, skipping create-message node: ${JSON.stringify(server.credentials)}`);
918
- send(msg);
919
- if (done) done();
920
- return;
921
- }
922
-
923
- var from = v_resolve(config.from, config.fromType, this.context(), msg);
924
- var to = v_resolve(config.to, config.toType, this.context(), msg);
925
- var text = v_resolve(config.text, config.textType, this.context(), msg);
926
- var provider = v_resolve(config.provider, config.providerType, this.context(), msg);
927
-
928
- const opts = {
929
- from,
930
- to,
931
- text,
932
- provider
933
- };
934
-
935
- try {
936
- node.log(`sending create message ${JSON.stringify(opts)}`);
937
- const res = await doCreateMessage(url, accountSid, apiToken, opts);
938
- msg.statusCode = 202;
939
- msg.messageSid = res.sid;
940
- msg.providerResponse = res.providerResponse;
941
- node.log(`successfully launched call with messageSid ${msg.messageSid}`);
942
- } catch (err) {
943
- if (err.statusCode) {
944
- node.log(`create_message failed with ${err.statusCode}`);
945
- msg.statusCode = err.statusCode;
946
- }
947
- else {
948
- node.log(`Error sending create message ${JSON.stringify(err)}`);
949
- if (done) done(err);
950
- else node.error(err, msg);
951
- send(msg);
952
- return;
953
- }
954
- }
955
- send(msg);
956
- if (done) done();
957
- });
958
- }
959
- RED.nodes.registerType('create-sms', create_sms);
960
- };
961
-
962
- // helper functions
963
-
964
- function appendVerb(msg, obj) {
965
- const data = {};
966
- Object.keys(obj).forEach((key) => {
967
- const v = obj[key];
968
- if (null === v || typeof v === 'undefined' ||
969
- (typeof v === 'string' && !v.length) ||
970
- (typeof v === 'object' && Object.keys(v).length === 0)) return;
971
- data[key] = v;
972
- });
973
- msg.jambonz = msg.jambonz || [];
974
- msg.jambonz.push(data);
975
- }
976
-
977
- /**
978
- * resolve a value that may be either a string or a property of the msg, flow, or global context
979
- */
980
- function v_resolve(val, valType, context, msg, asJson) {
981
- if (!val || !valType) return val;
982
- switch (valType) {
983
- case 'str':
984
- case 'num':
985
- return val;
986
- case 'json':
987
- return JSON.parse(val);
988
-
989
- case 'msg': return asJson === true ? msg[val] : mustache.render('{{' + val + '}}', msg);
990
- }
991
-
992
- var data = {};
993
- var dataobject = 'flow' === valType ? context.flow : context.global;
994
-
995
- if (asJson === true) return dataobject.get(val);
996
-
997
- var keys = dataobject.keys();
998
- for (var k in keys) {
999
- data[keys[k]] = dataobject.get(keys[k]);
1000
- }
1001
- return mustache.render('{{' + val + '}}', data);
1002
- }
1003
-
1004
- function v_text_resolve(node, val, context, msg) {
1005
- const flow = {};
1006
- const glob = {};
1007
- let keys = context.flow.keys();
1008
- for (const k in keys) {
1009
- flow[keys[k]] = context.flow.get(keys[k]);
1010
- }
1011
- keys = context.global.keys();
1012
- for (const k in keys) {
1013
- glob[keys[k]] = context.global.get(keys[k]);
1014
- }
1015
-
1016
- const newString = val.trim().replace(/\${([^{}]*)}/g, (a, b) => {
1017
- if (b.startsWith('msg.')) {
1018
- const prop = b.slice(4);
1019
- return msg[prop];
1020
- }
1021
- else if (b.startsWith('flow.')) {
1022
- const prop = b.slice(5);
1023
- return flow[prop];
1024
- }
1025
- else if (b.startsWith('global.')) {
1026
- const prop = b.slice(7);
1027
- return glob[prop];
1028
- }
1029
- return '${' + b + '}';
1030
- });
1031
- return newString;
1032
- }