@jambonz/node-red-contrib-jambonz 2.2.73 → 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 +63 -38
  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 -1044
@@ -1,1044 +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
- startConferenceOnEnter: config.startConferenceOnEnter,
396
- endOnExit: config.endOnExit,
397
- joinMuted : config.joinMuted
398
- });
399
- node.send(msg);
400
- });
401
- }
402
- RED.nodes.registerType('conference', conference);
403
-
404
- /** say */
405
- function say(config) {
406
- RED.nodes.createNode(this, config);
407
- this.text = config.text;
408
- this.early = config.early;
409
- this.loop = config.loop;
410
- var node = this;
411
-
412
- node.on('input', function(msg) {
413
-
414
- node.log(`say config: ${JSON.stringify(config)}, msg.call: ${JSON.stringify(msg.call)}`);
415
- const text = v_text_resolve(node, this.text, this.context(), msg);
416
-
417
- // jambonz say verb
418
- var obj = {
419
- verb: 'say',
420
- text,
421
- loop: parseInt(node.loop),
422
- earlyMedia: node.early
423
- };
424
- if (['aws', 'google'].includes(node.vendor)) {
425
- Object.assign(obj, {
426
- synthesizer: {
427
- vendor: config.vendor,
428
- language: config.lang,
429
- voice: config.voice
430
- }
431
- });
432
- }
433
-
434
- appendVerb(msg, obj);
435
- node.send(msg);
436
- });
437
- }
438
- RED.nodes.registerType('say', say);
439
- require('./http-helpers')(RED);
440
-
441
- /** gather */
442
- function gather(config) {
443
- RED.nodes.createNode(this, config);
444
- var node = this;
445
- node.on('input', function(msg) {
446
-
447
- // simple properties
448
- node.log(`config: ${JSON.stringify(config)}`);
449
-
450
- var obj = {verb: 'gather', input: []};
451
- if (config.actionhook) obj.actionHook = config.actionhook;
452
-
453
- // input
454
- if (config.speechinput) {
455
- obj.input.push('speech');
456
- const recognizer = {
457
- vendor: config.transcriptionvendor,
458
- language: config.recognizerlang
459
- };
460
- if (recognizer.vendor === 'google') {
461
- var hints = v_resolve(config.transcriptionhints, config.transcriptionhintsType, this.context(), msg);
462
- var altlangs = v_resolve(config.recognizeraltlang, config.recognizeraltlangType, this.context(), msg);
463
- var naics = v_resolve(config.naics, config.naicsType, this.context(), msg);
464
- Object.assign(recognizer, {
465
- profanityFilter: config.profanityfilter,
466
- hints: hints.length > 0 ?
467
- hints.split(',').map((w) => w.trim()) :
468
- []
469
- });
470
- if (naics) recognizer.naicsCode = parseInt(naics) || 0;
471
- if (altlangs) {
472
- recognizer.altLanguages = altlangs.split(',').map((e) => e.trim());
473
- }
474
- }
475
- else if (recognizer.vendor === 'aws') {
476
- var vocab = v_resolve(config.vocabularyname, config.vocabularynameType, this.context(), msg);
477
- var vocabFilter = v_resolve(config.vocabularyfiltername, config.vocabularynameType, this.context(), msg);
478
- Object.assign(recognizer, {
479
- vocabularyName: vocab,
480
- vocabularyFilterName: vocabFilter,
481
- filterMethod: config.vocabularyfiltermethod
482
- });
483
- }
484
- obj.recognizer = recognizer;
485
- }
486
- if (config.dtmfinput) {
487
- obj.input.push('digits');
488
- if (config.finishonkey && config.finishonkey.length) obj.finishOnKey = obj.finishonkey;
489
- if (/^\d+$/.test(config.numdigits)) obj.numDigits = parseInt(config.numdigits);
490
- if (/^\d+$/.test(config.timeout)) obj.timeout = parseInt(config.timeout);
491
- }
492
-
493
- // prompt
494
- if (config.prompttype === 'say') {
495
- obj.say = {text: v_text_resolve(node, config.text, this.context(), msg)};
496
- if (['aws', 'google'].includes(config.vendor)) {
497
- Object.assign(obj.say, {
498
- synthesizer: {
499
- vendor: config.vendor,
500
- language: config.lang,
501
- voice: config.voice
502
- }
503
- });
504
- }
505
- }
506
- else obj.play = {url: v_resolve(config.playurl, config.playurlType, this.context(), msg)};
507
-
508
- node.log(`gather: ${JSON.stringify(obj)}`);
509
-
510
- appendVerb(msg, obj);
511
- node.send(msg);
512
- });
513
- }
514
- RED.nodes.registerType('gather', gather);
515
-
516
- function dial(config) {
517
- RED.nodes.createNode(this, config);
518
- var node = this;
519
-
520
- node.on('input', function(msg) {
521
-
522
- node.log(`dial config: ${JSON.stringify(config)}, msg.call: ${JSON.stringify(msg.call)}`);
523
- var target = config.targets.map((t) => {
524
- const obj = Object.assign({}, t);
525
- var dest = v_resolve(obj.dest, obj.varType, this.context(), msg);
526
- node.log(`dial: dest ${t.varType}:${t.dest} resolved to ${dest}`);
527
- switch (t.type) {
528
- case 'phone':
529
- obj.number = dest;
530
- break;
531
- case 'user':
532
- obj.name = dest;
533
- break;
534
- case 'sip':
535
- obj.sipUri = dest;
536
- if (t.user) {
537
- obj.auth = {
538
- username: t.user,
539
- password: t.pass
540
- };
541
- }
542
- delete obj.user;
543
- delete obj.pass;
544
- break;
545
- case 'teams':
546
- obj.number = dest;
547
- break;
548
- }
549
- delete obj.varType;
550
- delete obj.dest;
551
- return obj;
552
- });
553
- var data = {
554
- verb: 'dial',
555
- target,
556
- answerOnBridge: config.answeronbridge,
557
- timeLimit: config.timelimit ? parseInt(config.timelimit) : null,
558
- timeout: config.timeout ? parseInt(config.timeout) : null,
559
- callerId: v_resolve(config.callerid, config.calleridType, this.context(), msg),
560
- actionHook: v_resolve(config.actionhook, config.actionhookType, this.context(), msg),
561
- confirmHook: v_resolve(config.confirmhook, config.confirmhookType, this.context(), msg),
562
- dialMusic: v_resolve(config.dialmusic, config.dialmusicType, this.context(), msg),
563
- dtmfHook: v_resolve(config.dtmfhook, config.dtmfhookType, this.context(), msg)
564
- };
565
-
566
-
567
- // headers
568
- var headers = {};
569
- config.headers.forEach(function(h) {
570
- if (h.h.length && h.v.length) headers[h.h] = h.v;
571
- });
572
- Object.assign(data, {headers});
573
-
574
- // nested listen
575
- if (config.listenurl && config.listenurl.length > 0) {
576
- data.listen = {
577
- url: v_resolve(config.listenurl, config.listenurlType, this.context(), msg),
578
- mixType: 'stereo'
579
- };
580
- }
581
-
582
- // nested transcribe
583
- if (config.transcriptionhook) {
584
- const recognizer = {
585
- vendor: config.transcriptionvendor,
586
- language: config.recognizerlang,
587
- interim: config.interim,
588
- separateRecognitionPerChannel: config.mixtype === 'stereo' && config.separaterecog,
589
- diarization: config.diarization
590
- };
591
- if (recognizer.vendor === 'google') {
592
- var diarizationMin = v_resolve(config.diarizationmin, config.diarizationminType, this.context(), msg);
593
- var diarizationMax = v_resolve(config.diarizationmax, config.diarizationmaxType, this.context(), msg);
594
- var hints = v_resolve(config.transcriptionhints, config.transcriptionhintsType, this.context(), msg);
595
- var altlangs = v_resolve(config.recognizeraltlang, config.recognizeraltlangType, this.context(), msg);
596
- var naics = v_resolve(config.naics, config.naicsType, this.context(), msg);
597
- Object.assign(recognizer, {
598
- profanityFilter: config.profanityfilter,
599
- hints: hints.length > 0 ?
600
- hints.split(',').map((w) => w.trim()) :
601
- [],
602
- punctuation: config.punctuation,
603
- enhancedModel: config.useenhanced,
604
- words: config.words,
605
- interactionType: config.interactiontype,
606
- });
607
- if (recognizer.diarization) {
608
- if (diarizationMin) recognizer.diarizationMinSpeakers = parseInt(diarizationMin) || 0;
609
- if (diarizationMax) recognizer.diarizationMaxSpeakers = parseInt(diarizationMax) || 0;
610
- }
611
- if (naics) recognizer.naicsCode = parseInt(naics) || 0;
612
- if (altlangs) {
613
- recognizer.altLanguages = altlangs.split(',').map((e) => e.trim());
614
- }
615
- }
616
- else if (recognizer.vendor === 'aws') {
617
- var vocab = v_resolve(config.vocabularyname, config.vocabularynameType, this.context(), msg);
618
- var vocabFilter = v_resolve(config.vocabularyfiltername, config.vocabularynameType, this.context(), msg);
619
- Object.assign(recognizer, {
620
- vocabularyName: vocab,
621
- vocabularyFilterName: vocabFilter,
622
- filterMethod: config.vocabularyfiltermethod
623
- });
624
- }
625
- data.transcribe = {
626
- transcriptionHook: v_resolve(config.transcriptionhook, config.transcriptionhookType, this.context(), msg),
627
- recognizer
628
- };
629
- }
630
- // dtmf capture
631
- const dtmfCapture = v_resolve(config.dtmfcapture, config.dtmfcaptureType, this.context(), msg);
632
- if (dtmfCapture && dtmfCapture.length) {
633
- data.dtmfCapture = dtmfCapture.split(',').map((i) => i.trim());
634
- }
635
- else {
636
- delete data.dtmfHook;
637
- }
638
-
639
-
640
- node.log(`dial verb: ${JSON.stringify(data)}`);
641
-
642
- appendVerb(msg, data);
643
- node.log(`dial jambonz: ${JSON.stringify(msg.jambonz)}`);
644
- node.send(msg);
645
- });
646
- }
647
- RED.nodes.registerType('dial', dial);
648
-
649
- /** dialogflow */
650
- function dialogflow(config) {
651
- RED.nodes.createNode(this, config);
652
- var node = this;
653
- node.on('input', function(msg) {
654
- var val = v_resolve(config.inputTimeout, config.inputTimeoutType, this.context(), msg);
655
- var timeout = /^\d+$/.test(val) ? parseInt(val) : 0;
656
- var eventHook = v_resolve(config.eventHook, config.eventHookType, this.context(), msg);
657
- var actionHook = v_resolve(config.actionHook, config.actionHookType, this.context(), msg);
658
- var welcomeEvent = v_resolve(config.welcomeEvent, config.welcomeEventType, this.context(), msg);
659
- var environment = v_resolve(config.environment, config.environmentType, this.context(), msg);
660
- var welcomeEventParams;
661
- if (welcomeEvent && welcomeEvent.length > 0) {
662
- welcomeEventParams = v_resolve(config.welcomeEventParams, config.welcomeEventParamsType, this.context(), msg);
663
- }
664
- var noInputEvent = v_resolve(config.noInputEvent, config.noInputEventType, this.context(), msg);
665
- const obj = {
666
- verb: 'dialogflow',
667
- credentials: v_resolve(config.serviceAccountCredentials,
668
- config.serviceAccountCredentialsType, this.context(), msg),
669
- project: v_resolve(config.project, config.projectType, this.context(), msg),
670
- lang: config.recognizerlang,
671
- bargein: config.bargein
672
- };
673
- if (welcomeEvent) {
674
- obj.welcomeEvent = welcomeEvent;
675
- if (welcomeEventParams) obj.welcomeEventParams = welcomeEventParams;
676
- }
677
- if (environment && environment.length > 0) obj.environment = environment;
678
- if (eventHook && eventHook.length > 0) obj.eventHook = eventHook;
679
- if (actionHook && actionHook.length > 0) obj.actionHook = actionHook;
680
- if (timeout) obj.noInputTimeout = timeout;
681
- if (timeout && noInputEvent) obj.noInputEvent = noInputEvent;
682
- if (config.prompt === 'tts') {
683
- obj.tts = {
684
- vendor: config.vendor,
685
- language: config.lang,
686
- voice: config.voice
687
- };
688
- }
689
-
690
- appendVerb(msg, obj);
691
- node.send(msg);
692
- });
693
- }
694
- RED.nodes.registerType('dialogflow', dialogflow);
695
-
696
- function jambonz_auth(config) {
697
- RED.nodes.createNode(this, config);
698
- this.url = config.url;
699
- this.accountSid = config.accountSid;
700
- this.apiKey = config.apiKey;
701
- }
702
-
703
- RED.nodes.registerType('jambonz_auth', jambonz_auth, {
704
- credentials: {
705
- url: {type: 'text'},
706
- accountSid: {type: 'text'},
707
- apiToken: {type: 'text'}
708
- }
709
- });
710
-
711
- function aws_auth(config) {
712
- RED.nodes.createNode(this, config);
713
- this.accessKey = config.accessKey;
714
- this.secretAccessKey = config.secretAccessKey;
715
- }
716
-
717
- RED.nodes.registerType('aws_auth', aws_auth, {
718
- credentials: {
719
- accessKey: {type: 'text'},
720
- secretAccessKey: {type: 'text'}
721
- }
722
- });
723
-
724
- function doLCC(node, baseUrl, accountSid, apiToken, callSid, opts) {
725
- const post = bent(`${baseUrl}/v1/`, 'POST', 'string', 202, {
726
- 'Authorization': `Bearer ${apiToken}`
727
- });
728
- const url = `Accounts/${accountSid}/Calls/${callSid}`;
729
- node.log(`invoking LCC with callSid ${callSid} at ${baseUrl}/v1/${url}`);
730
- return post(url, opts);
731
- }
732
-
733
- function doCreateCall(baseUrl, accountSid, apiToken, opts) {
734
- const post = bent(`${baseUrl}/v1/`, 'POST', 'json', 201, {
735
- 'Authorization': `Bearer ${apiToken}`
736
- });
737
- return post(`Accounts/${accountSid}/Calls`, opts);
738
- }
739
-
740
- function doCreateMessage(baseUrl, accountSid, apiToken, opts) {
741
- const post = bent(`${baseUrl}/v1/`, 'POST', 'json', 201, {
742
- 'Authorization': `Bearer ${apiToken}`
743
- });
744
- return post(`Accounts/${accountSid}/Messages`, opts);
745
- }
746
-
747
-
748
- /** LCC */
749
- function lcc(config) {
750
- RED.nodes.createNode(this, config);
751
- var node = this;
752
- const server = RED.nodes.getNode(config.server);
753
-
754
- node.on('input', async(msg, send, done) => {
755
- send = send || function() { node.send.apply(node, arguments);};
756
-
757
- const {url, accountSid, apiToken} = server.credentials;
758
- const callSid = v_resolve(config.callSid, config.callSidType, this.context(), msg);
759
- if (!url || !accountSid || !apiToken || !callSid) {
760
- node.log(`invalid / missing credentials or callSid, skipping LCC node: ${JSON.stringify(server.credentials)}`);
761
- send(msg);
762
- if (done) done();
763
- return;
764
- }
765
-
766
- const opts = {};
767
- switch (config.action) {
768
- case 'hangup':
769
- opts.call_status = 'completed';
770
- break;
771
- case 'mute':
772
- opts.mute_status = 'mute';
773
- break;
774
- case 'unmute':
775
- opts.mute_status = 'unmute';
776
- break;
777
- case 'mute_conf':
778
- opts.conf_mute_status = 'mute';
779
- break;
780
- case 'unmute_conf':
781
- opts.conf_mute_status = 'unmute';
782
- break;
783
- case 'pause':
784
- opts.listen_status = 'pause';
785
- break;
786
- case 'resume':
787
- opts.listen_status = 'resume';
788
- break;
789
- case 'redirect':
790
- node.log(`LCC redirect callHook ${config.callHook} callHookType: ${config.callHookType}`);
791
- opts.call_hook = {url: v_resolve(config.callHook, config.callHookType, this.context(), msg)};
792
- break;
793
- case 'hold_conf':
794
- opts.conf_hold_status = 'hold';
795
- opts.wait_hook = {url: v_resolve(config.waitHook, config.waitHookType, this.context(), msg)};
796
- break;
797
- case 'unhold_conf':
798
- opts.conf_hold_status = 'unhold';
799
- case 'whisper':
800
- Object.assign(opts, {
801
- whisper: {
802
- verb: 'say',
803
- text: config.text
804
- }
805
- });
806
- if (['aws', 'google'].includes(config.vendor)) {
807
- Object.assign(opts.whisper, {
808
- synthesizer: {
809
- vendor: config.vendor,
810
- language: config.lang,
811
- voice: config.voice
812
- }
813
- });
814
- }
815
- break;
816
- default:
817
- node.log(`invalid action: ${config.action}`);
818
- send(msg);
819
- if (done) done();
820
- return;
821
- }
822
-
823
- try {
824
- msg.payload = await doLCC(node, url, accountSid, apiToken, callSid, opts);
825
- msg.statusCode = 202;
826
- } catch (err) {
827
- if (err.statusCode) msg.statusCode = err.statusCode;
828
- else {
829
- node.error(`Error sending LCC ${JSON.stringify(err)}`);
830
- if (done) done(err);
831
- else node.error(err, msg);
832
- send(msg);
833
- return;
834
- }
835
- }
836
- send(msg);
837
- if (done) done();
838
- });
839
- }
840
- RED.nodes.registerType('lcc', lcc);
841
-
842
- /** Create call */
843
- function create_call(config) {
844
- RED.nodes.createNode(this, config);
845
- var node = this;
846
- const server = RED.nodes.getNode(config.server);
847
-
848
- node.on('input', async(msg, send, done) => {
849
- send = send || function() { node.send.apply(node, arguments);};
850
-
851
- const {url, accountSid, apiToken} = server.credentials;
852
- if (!url || !accountSid || !apiToken) {
853
- node.log(`invalid / missing credentials, skipping create-call node: ${JSON.stringify(server.credentials)}`);
854
- send(msg);
855
- if (done) done();
856
- return;
857
- }
858
-
859
- var from = v_resolve(config.from, config.fromType, this.context(), msg);
860
- var to = v_resolve(config.to, config.toType, this.context(), msg);
861
-
862
- const opts = {
863
- application_sid: config.application,
864
- from,
865
- to: {
866
- type: config.dest
867
- },
868
- };
869
- if (config.timeout) {
870
- const timeout = parseInt(config.timeout);
871
- if (timeout > 0) opts.timeout = timeout;
872
- }
873
- switch (config.dest) {
874
- case 'phone':
875
- opts.to.number = to;
876
- break;
877
- case 'user':
878
- opts.to.name = to;
879
- break;
880
- case 'sip':
881
- opts.to.sipUri = to;
882
- break;
883
- case 'ms-teams':
884
- opts.to.user = to;
885
- break;
886
- default:
887
- if (done) done(`unknown dest type ${config.dest}`);
888
- else node.error(`unknown dest type ${config.dest}`, msg);
889
- send(msg);
890
- return;
891
- }
892
-
893
- try {
894
- node.log(`sending create call ${JSON.stringify(opts)}`);
895
- const res = await doCreateCall(url, accountSid, apiToken, opts);
896
- msg.statusCode = 202;
897
- msg.callSid = res.sid;
898
- node.log(`successfully launched call with callSid ${msg.callSid}`);
899
- } catch (err) {
900
- if (err.statusCode) {
901
- node.log(`create-call failed with ${err.statusCode}`);
902
- msg.statusCode = err.statusCode;
903
- }
904
- else {
905
- node.log(`Error sending create all ${JSON.stringify(err)}`);
906
- if (done) done(err);
907
- else node.error(err, msg);
908
- send(msg);
909
- return;
910
- }
911
- }
912
- send(msg);
913
- if (done) done();
914
- });
915
- }
916
- RED.nodes.registerType('create-call', create_call);
917
-
918
- /** Create sms */
919
- function create_sms(config) {
920
- RED.nodes.createNode(this, config);
921
- var node = this;
922
- const server = RED.nodes.getNode(config.server);
923
-
924
- node.on('input', async(msg, send, done) => {
925
- send = send || function() { node.send.apply(node, arguments);};
926
-
927
- const {url, accountSid, apiToken} = server.credentials;
928
- if (!url || !accountSid || !apiToken) {
929
- node.log(`invalid / missing credentials, skipping create-message node: ${JSON.stringify(server.credentials)}`);
930
- send(msg);
931
- if (done) done();
932
- return;
933
- }
934
-
935
- var from = v_resolve(config.from, config.fromType, this.context(), msg);
936
- var to = v_resolve(config.to, config.toType, this.context(), msg);
937
- var text = v_resolve(config.text, config.textType, this.context(), msg);
938
- var provider = v_resolve(config.provider, config.providerType, this.context(), msg);
939
-
940
- const opts = {
941
- from,
942
- to,
943
- text,
944
- provider
945
- };
946
-
947
- try {
948
- node.log(`sending create message ${JSON.stringify(opts)}`);
949
- const res = await doCreateMessage(url, accountSid, apiToken, opts);
950
- msg.statusCode = 202;
951
- msg.messageSid = res.sid;
952
- msg.providerResponse = res.providerResponse;
953
- node.log(`successfully launched call with messageSid ${msg.messageSid}`);
954
- } catch (err) {
955
- if (err.statusCode) {
956
- node.log(`create_message failed with ${err.statusCode}`);
957
- msg.statusCode = err.statusCode;
958
- }
959
- else {
960
- node.log(`Error sending create message ${JSON.stringify(err)}`);
961
- if (done) done(err);
962
- else node.error(err, msg);
963
- send(msg);
964
- return;
965
- }
966
- }
967
- send(msg);
968
- if (done) done();
969
- });
970
- }
971
- RED.nodes.registerType('create-sms', create_sms);
972
- };
973
-
974
- // helper functions
975
-
976
- function appendVerb(msg, obj) {
977
- const data = {};
978
- Object.keys(obj).forEach((key) => {
979
- const v = obj[key];
980
- if (null === v || typeof v === 'undefined' ||
981
- (typeof v === 'string' && !v.length) ||
982
- (typeof v === 'object' && Object.keys(v).length === 0)) return;
983
- data[key] = v;
984
- });
985
- msg.jambonz = msg.jambonz || [];
986
- msg.jambonz.push(data);
987
- }
988
-
989
- /**
990
- * resolve a value that may be either a string or a property of the msg, flow, or global context
991
- */
992
- function v_resolve(val, valType, context, msg, asJson) {
993
- if (!val || !valType) return val;
994
- switch (valType) {
995
- case 'str':
996
- case 'num':
997
- return val;
998
- case 'json':
999
- return JSON.parse(val);
1000
-
1001
- case 'msg': return asJson === true ? msg[val] : mustache.render('{{' + val + '}}', msg);
1002
- }
1003
-
1004
- var data = {};
1005
- var dataobject = 'flow' === valType ? context.flow : context.global;
1006
-
1007
- if (asJson === true) return dataobject.get(val);
1008
-
1009
- var keys = dataobject.keys();
1010
- for (var k in keys) {
1011
- data[keys[k]] = dataobject.get(keys[k]);
1012
- }
1013
- return mustache.render('{{' + val + '}}', data);
1014
- }
1015
-
1016
- function v_text_resolve(node, val, context, msg) {
1017
- const flow = {};
1018
- const glob = {};
1019
- let keys = context.flow.keys();
1020
- for (const k in keys) {
1021
- flow[keys[k]] = context.flow.get(keys[k]);
1022
- }
1023
- keys = context.global.keys();
1024
- for (const k in keys) {
1025
- glob[keys[k]] = context.global.get(keys[k]);
1026
- }
1027
-
1028
- const newString = val.trim().replace(/\${([^{}]*)}/g, (a, b) => {
1029
- if (b.startsWith('msg.')) {
1030
- const prop = b.slice(4);
1031
- return msg[prop];
1032
- }
1033
- else if (b.startsWith('flow.')) {
1034
- const prop = b.slice(5);
1035
- return flow[prop];
1036
- }
1037
- else if (b.startsWith('global.')) {
1038
- const prop = b.slice(7);
1039
- return glob[prop];
1040
- }
1041
- return '${' + b + '}';
1042
- });
1043
- return newString;
1044
- }