@jambonz/node-red-contrib-jambonz 2.2.73 → 2.3.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.
- package/examples/Authenticating sip devices.json +1 -0
- package/examples/Conference with LCC.json +1 -0
- package/{src/examples → examples}/Connecting to a dialogflow bot.json +0 -0
- package/examples/Create Call and Send DTMF.json +1 -0
- package/examples/Get Log Data.json +1 -0
- package/examples/Leave Queue.json +1 -0
- package/examples/SIP Responses.json +1 -0
- package/{src/examples → examples}/SIP trunking outbound call.json +0 -0
- package/examples/Simple IVR.json +1 -0
- package/examples/Simple Queue.json +1 -0
- package/examples/Using Redirect.json +1 -0
- package/examples/Voicemail using Listen into S3 bucket.json +130 -0
- package/examples/config.json +1 -0
- package/examples/gather.json +1 -0
- package/examples/lex.json +1 -0
- package/examples/messages.json +1 -0
- package/examples/rasa.json +1 -0
- package/examples/tag a call.json +1 -0
- package/package.json +52 -14
- package/resources/editor.js +414 -0
- package/src/data/recognizer.json +685 -1
- package/src/data/tts.json +2970 -1
- package/src/nodes/auth.html +108 -0
- package/src/nodes/auth.js +43 -0
- package/src/nodes/conference.html +121 -0
- package/src/nodes/conference.js +31 -0
- package/src/nodes/config.html +484 -0
- package/src/nodes/config.js +74 -0
- package/src/nodes/create_call.html +287 -0
- package/src/nodes/create_call.js +104 -0
- package/src/nodes/create_sms.html +106 -0
- package/src/nodes/create_sms.js +65 -0
- package/src/nodes/dequeue.html +102 -0
- package/src/nodes/dequeue.js +25 -0
- package/src/nodes/dial.html +629 -0
- package/src/nodes/dial.js +141 -0
- package/src/nodes/dialogflow.html +260 -0
- package/src/nodes/dialogflow.js +53 -0
- package/src/nodes/dtmf.html +74 -0
- package/src/nodes/dtmf.js +22 -0
- package/src/nodes/enqueue.html +90 -0
- package/src/nodes/enqueue.js +25 -0
- package/src/nodes/gather.html +326 -0
- package/src/nodes/gather.js +83 -0
- package/src/nodes/get_alerts.html +94 -0
- package/src/nodes/get_alerts.js +47 -0
- package/src/nodes/get_calls.html +59 -0
- package/src/nodes/get_calls.js +38 -0
- package/src/nodes/get_recent_calls.html +95 -0
- package/src/nodes/get_recent_calls.js +47 -0
- package/src/nodes/hangup.html +46 -0
- package/src/nodes/hangup.js +20 -0
- package/src/nodes/jambonz.html +63 -38
- package/src/nodes/lcc.html +256 -0
- package/src/nodes/lcc.js +102 -0
- package/src/nodes/leave.html +40 -0
- package/src/nodes/leave.js +21 -0
- package/src/nodes/lex.html +264 -0
- package/src/nodes/lex.js +75 -0
- package/src/nodes/libs.js +100 -0
- package/src/nodes/listen.html +341 -0
- package/src/nodes/listen.js +82 -0
- package/src/nodes/message.html +95 -0
- package/src/nodes/message.js +32 -0
- package/src/nodes/pause.html +62 -0
- package/src/nodes/pause.js +25 -0
- package/src/nodes/play.html +76 -0
- package/src/nodes/play.js +23 -0
- package/src/nodes/rasa.html +106 -0
- package/src/nodes/rasa.js +22 -0
- package/src/nodes/redirect.html +64 -0
- package/src/nodes/redirect.js +24 -0
- package/src/nodes/s3-upload.html +29 -14
- package/src/nodes/s3-upload.js +24 -9
- package/src/nodes/say.html +119 -0
- package/src/nodes/say.js +38 -0
- package/src/nodes/sip-decline.html +76 -0
- package/src/nodes/sip-decline.js +25 -0
- package/src/nodes/sip-refer.html +116 -0
- package/src/nodes/sip-refer.js +33 -0
- package/src/nodes/sip-request.html +96 -0
- package/src/nodes/sip-request.js +36 -0
- package/src/nodes/tag.html +60 -0
- package/src/nodes/tag.js +22 -0
- package/src/nodes/template.html +63 -0
- package/src/nodes/template.js +9 -0
- package/src/nodes/userauth.html +85 -0
- package/src/nodes/userauth.js +70 -0
- package/src/nodes/webhooks.js +0 -16
- package/src/utils/http-helpers.js +68 -0
- package/src/examples/Authenticating sip devices.json +0 -10
- package/src/nodes/http-helpers.js +0 -26
- package/src/nodes/jambonz.js +0 -1044
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<!-- Javascript -->
|
|
2
|
+
<script type="text/javascript">
|
|
3
|
+
RED.nodes.registerType('play',{
|
|
4
|
+
category: 'jambonz',
|
|
5
|
+
color: '#bbabaa',
|
|
6
|
+
defaults: {
|
|
7
|
+
name: {value: ''},
|
|
8
|
+
url: {required: true},
|
|
9
|
+
urlType: {value: 'str'},
|
|
10
|
+
early: {value: false},
|
|
11
|
+
loop: {value: 1, required: true, validate: RED.validators.number()},
|
|
12
|
+
},
|
|
13
|
+
inputs:1,
|
|
14
|
+
outputs:1,
|
|
15
|
+
icon: "font-awesome/fa-cubes",
|
|
16
|
+
label: function() { return this.name || 'play';},
|
|
17
|
+
oneditprepare: function() {
|
|
18
|
+
$('#node-input-url').typedInput({
|
|
19
|
+
default: $('#node-input-urlType').val(),
|
|
20
|
+
types: ['str','msg', 'flow', 'global', 'jsonata', 'env'],
|
|
21
|
+
typeField: $('#node-input-urlType')
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
<!-- HTML -->
|
|
30
|
+
<script type="text/html" data-template-name="play">
|
|
31
|
+
<div class="form-row">
|
|
32
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
33
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
34
|
+
</div>
|
|
35
|
+
<div class="form-row">
|
|
36
|
+
<label for="node-input-url">Url</label>
|
|
37
|
+
<input type="text" id="node-input-url" placeholder="url returning .mp3 or .wav file">
|
|
38
|
+
<input type="hidden" id="node-input-urlType">
|
|
39
|
+
</div>
|
|
40
|
+
<div class="form-row">
|
|
41
|
+
<label for="node-input-early"><i class="icon-tag"></i>Early media</label>
|
|
42
|
+
<input type="checkbox" id="node-input-early">
|
|
43
|
+
</div>
|
|
44
|
+
<div class="form-row">
|
|
45
|
+
<label for="node-input-loop"><i class="icon-tag"></i>Loop</label>
|
|
46
|
+
<input type="input" id="node-input-loop" placeholder="number of times to repeat">
|
|
47
|
+
</div>
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<!-- Help Text -->
|
|
51
|
+
<script type="text/html" data-help-name="play">
|
|
52
|
+
<p>Play a wav or mp3 file</p>
|
|
53
|
+
<h3>Properties</h3>
|
|
54
|
+
<p><code>Url</code> -
|
|
55
|
+
a single url or array of urls (will play in sequence) to a wav or mp3 file</p>
|
|
56
|
+
<p><code>Early media</code> -
|
|
57
|
+
if checked, play the url over an early media connection</p>
|
|
58
|
+
<p><code>Loop</code> -
|
|
59
|
+
number of times to play the url</p>
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
<h3>Outputs</h3>
|
|
63
|
+
<dl class="message-properties">
|
|
64
|
+
<dt>jambonz<span class="property-type">object</span></dt>
|
|
65
|
+
<dd> <code>msg.jambonz</code> will contain any previous actions provided to the input with the new <code>play</code> action appended </dd>
|
|
66
|
+
</dl>
|
|
67
|
+
|
|
68
|
+
<h3>Details</h3>
|
|
69
|
+
The play action plays an mp3 or wave file into a call. If early media is checked, the audio will be played over an early media connection,
|
|
70
|
+
if the call has not already been answered.
|
|
71
|
+
|
|
72
|
+
<h3>References</h3>
|
|
73
|
+
<ul>
|
|
74
|
+
<li><a href="https://docs.jambonz.org/jambonz/#play">Jambonz play reference</a></li>
|
|
75
|
+
</ul>
|
|
76
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
var {createHash} = require('crypto');
|
|
2
|
+
const bent = require('bent');
|
|
3
|
+
var mustache = require('mustache');
|
|
4
|
+
mustache.escape = function(text) {return text;};
|
|
5
|
+
var {appendVerb, v_resolve} = require('./libs')
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module.exports = function(RED) {
|
|
9
|
+
function play(config) {
|
|
10
|
+
RED.nodes.createNode(this, config);
|
|
11
|
+
var node = this;
|
|
12
|
+
node.on('input', function(msg) {
|
|
13
|
+
appendVerb(msg, {
|
|
14
|
+
verb: 'play',
|
|
15
|
+
url: v_resolve(config.url, config.urlType, this.context(), msg),
|
|
16
|
+
earlyMedia: config.early,
|
|
17
|
+
loop: config.loop
|
|
18
|
+
});
|
|
19
|
+
node.send(msg);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
RED.nodes.registerType('play', play);
|
|
23
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<!-- Javascript -->
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
<script type="text/javascript">
|
|
5
|
+
RED.nodes.registerType('rasa',{
|
|
6
|
+
category: 'jambonz',
|
|
7
|
+
color: '#bbabaa',
|
|
8
|
+
defaults: {
|
|
9
|
+
name: {value: ''},
|
|
10
|
+
url:{required : true},
|
|
11
|
+
urlType:{value: 'str'},
|
|
12
|
+
prompt:{},
|
|
13
|
+
promptType:{value: 'str'},
|
|
14
|
+
eventHook:{},
|
|
15
|
+
eventHookType:{value: 'str'},
|
|
16
|
+
actionHook:{},
|
|
17
|
+
actionHookType:{value: 'str'},
|
|
18
|
+
},
|
|
19
|
+
inputs:1,
|
|
20
|
+
outputs:1,
|
|
21
|
+
icon: "font-awesome/fa-cubes",
|
|
22
|
+
paletteLabel: "rasa",
|
|
23
|
+
label: function() { return this.name || 'rasa';},
|
|
24
|
+
oneditprepare: () => {
|
|
25
|
+
var node = this;
|
|
26
|
+
$('#node-input-url').typedInput({
|
|
27
|
+
types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env'],
|
|
28
|
+
typeField: $('#node-input-urlType')
|
|
29
|
+
});
|
|
30
|
+
$('#node-input-prompt').typedInput({
|
|
31
|
+
types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env'],
|
|
32
|
+
typeField: $('#node-input-promptType')
|
|
33
|
+
});
|
|
34
|
+
$('#node-input-eventHook').typedInput({
|
|
35
|
+
types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env'],
|
|
36
|
+
typeField: $('#node-input-eventHookType')
|
|
37
|
+
});
|
|
38
|
+
$('#node-input-actionHook').typedInput({
|
|
39
|
+
types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env'],
|
|
40
|
+
typeField: $('#node-input-actionHookType')
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
<!-- HTML -->
|
|
50
|
+
|
|
51
|
+
<script type="text/html" data-template-name="rasa">
|
|
52
|
+
<div class="form-row">
|
|
53
|
+
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
|
54
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div class="form-row">
|
|
58
|
+
<label for="node-input-url"><i class="icon-tag"></i> URL</label>
|
|
59
|
+
<input type="text" id="node-input-url" placeholder="http">
|
|
60
|
+
<input type="hidden" id="node-input-urlType">
|
|
61
|
+
</div>
|
|
62
|
+
<div class="form-row">
|
|
63
|
+
<label for="node-input-prompt"><i class="icon-tag"></i> Prompt</label>
|
|
64
|
+
<input type="text" id="node-input-prompt" placeholder="Hello....">
|
|
65
|
+
<input type="hidden" id="node-input-promptType">
|
|
66
|
+
</div>
|
|
67
|
+
<div class="form-row">
|
|
68
|
+
<label for="node-input-eventHook"><i class="icon-tag"></i> Event Hook</label>
|
|
69
|
+
<input type="text" id="node-input-eventHook" placeholder="/event">
|
|
70
|
+
<input type="hidden" id="node-input-eventHookType">
|
|
71
|
+
</div>
|
|
72
|
+
<div class="form-row">
|
|
73
|
+
<label for="node-input-actionHook"><i class="icon-tag"></i> Action Hook</label>
|
|
74
|
+
<input type="text" id="node-input-actionHook" placeholder="/action">
|
|
75
|
+
<input type="hidden" id="node-input-actionHookType">
|
|
76
|
+
</div>
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<!-- Help Text -->
|
|
80
|
+
|
|
81
|
+
<script type="text/html" data-help-name="rasa">
|
|
82
|
+
<p>The rasa verb is used to connect a call to a Rasa assistant.
|
|
83
|
+
|
|
84
|
+
</p>
|
|
85
|
+
<h3>Properties</h3>
|
|
86
|
+
<p><code>url</code> - URL to connect to the Rasa assistant using the Rasa RestInput channel</p>
|
|
87
|
+
<p><code>prompt</code> - an initial greeting to play to the user </p>
|
|
88
|
+
<p><code>eventHook</code> - a webhook that is called when the rasa assistant returns either a user message or a bot message</p>
|
|
89
|
+
<p><code>actionHook </code> - A webhook that is called when the rasa verb completes </p>
|
|
90
|
+
|
|
91
|
+
<h3>Outputs</h3>
|
|
92
|
+
<dl class="message-properties">
|
|
93
|
+
<dt>payload<span class="property-type">object</span></dt>
|
|
94
|
+
<dd> <code>msg.jambonz</code> will contain any previous actions provided to the input with the new <code>rasa</code> action appended </dd>
|
|
95
|
+
</dl>
|
|
96
|
+
|
|
97
|
+
<h3>Details</h3>
|
|
98
|
+
The rasa verb performs speech recognition on the caller audio stream and sends it as text input to the rasa assistant using the rasa RestInput channel. Text returned from the assistant is played to the caller using text-to-speech. As the conversation proceeds, webhook events can be sent to notify of all of the messages being exchanged between the user and the bot, allowing your application to intercede at any point, e.g. to transfer the call to an agent.
|
|
99
|
+
|
|
100
|
+
<h3>References</h3>
|
|
101
|
+
<ul>
|
|
102
|
+
<li><a href="https://www.jambonz.org/docs/webhooks/rasa/">Jambonz rasa reference</a></li>
|
|
103
|
+
</ul>
|
|
104
|
+
</script>
|
|
105
|
+
|
|
106
|
+
</script>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
var {createHash} = require('crypto');
|
|
2
|
+
const bent = require('bent');
|
|
3
|
+
var mustache = require('mustache');
|
|
4
|
+
mustache.escape = function(text) {return text;};
|
|
5
|
+
var {appendVerb, v_resolve, v_text_resolve, doLCC, doCreateCall, doCreateMessage} = require('./libs')
|
|
6
|
+
|
|
7
|
+
module.exports = function(RED) {
|
|
8
|
+
function rasa(config) {
|
|
9
|
+
RED.nodes.createNode(this, config);
|
|
10
|
+
var node = this;
|
|
11
|
+
node.on('input', function(msg) {
|
|
12
|
+
obj = {verb: 'rasa'}
|
|
13
|
+
obj.url = config.url
|
|
14
|
+
config.prompt != '' ? obj.prompt = config.prompt : null
|
|
15
|
+
config.eventHook != '' ? obj.eventHook = config.eventHook : null
|
|
16
|
+
config.actionHook != '' ? obj.actionHook = config.actionHook : null
|
|
17
|
+
appendVerb(msg, obj);
|
|
18
|
+
node.send(msg);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
RED.nodes.registerType('rasa', rasa);
|
|
22
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<!-- Javascript -->
|
|
2
|
+
<script type="text/javascript">
|
|
3
|
+
RED.nodes.registerType('redirect', {
|
|
4
|
+
category: 'jambonz',
|
|
5
|
+
color: '#bbabaa',
|
|
6
|
+
defaults: {
|
|
7
|
+
name: {value: ''},
|
|
8
|
+
hook: {required: true},
|
|
9
|
+
hookType: {value: 'str'}
|
|
10
|
+
},
|
|
11
|
+
inputs:1,
|
|
12
|
+
outputs:1,
|
|
13
|
+
icon: "font-awesome/fa-cubes",
|
|
14
|
+
label: function() {
|
|
15
|
+
return this.name || 'redirect';
|
|
16
|
+
},
|
|
17
|
+
oneditprepare: function() {
|
|
18
|
+
$('#node-input-hook').typedInput({
|
|
19
|
+
types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env'],
|
|
20
|
+
typeField: $('#node-input-hookType')
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<!-- HTML -->
|
|
27
|
+
<script type="text/html" data-template-name="redirect">
|
|
28
|
+
<div class="form-row">
|
|
29
|
+
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
|
30
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
31
|
+
</div>
|
|
32
|
+
<div class="form-row">
|
|
33
|
+
<label for="node-input-hook">Action hook</label>
|
|
34
|
+
<input type="text" id="node-input-hook" placeholder="url">
|
|
35
|
+
<input type="hidden" id="node-input-hookType">
|
|
36
|
+
</div>
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<!-- Help Text -->
|
|
40
|
+
<script type="text/html" data-help-name="audio in">
|
|
41
|
+
<script type="text/html" data-help-name="redirect">
|
|
42
|
+
<p>Redirect a call to a new application URL</p>
|
|
43
|
+
<h3>Inputs</h3>
|
|
44
|
+
<dl class="message-properties">
|
|
45
|
+
<dt>Action hook<span class="property-type">string</span></dt>
|
|
46
|
+
<dd>URL of webhook to retrieve new application from.</dd>
|
|
47
|
+
</dl>
|
|
48
|
+
|
|
49
|
+
<h3>Outputs</h3>
|
|
50
|
+
<dl class="message-properties">
|
|
51
|
+
<dt>jambonz<span class="property-type">object</span></dt>
|
|
52
|
+
<dd> <code>msg.jambonz</code> will contain any previous actions provided to the input with the new <code>redirect</code> action appended </dd>
|
|
53
|
+
</dl>
|
|
54
|
+
|
|
55
|
+
<h3>Details</h3>
|
|
56
|
+
The redirect verb retrieves a new URL to execute for the current call.
|
|
57
|
+
<h3>References</h3>
|
|
58
|
+
<ul>
|
|
59
|
+
<li><a href="https://docs.jambonz.org/jambonz/#redirect">Jambonz redirect reference</a></li>
|
|
60
|
+
</ul>
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
</script>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
var {createHash} = require('crypto');
|
|
2
|
+
const bent = require('bent');
|
|
3
|
+
var mustache = require('mustache');
|
|
4
|
+
mustache.escape = function(text) {return text;};
|
|
5
|
+
var {appendVerb, v_resolve} = require('./libs')
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module.exports = function(RED) {
|
|
9
|
+
/** redirect */
|
|
10
|
+
function redirect(config) {
|
|
11
|
+
RED.nodes.createNode(this, config);
|
|
12
|
+
var node = this;
|
|
13
|
+
node.on('input', function(msg, send, done) {
|
|
14
|
+
var actionHook = v_resolve(config.hook, config.hookType, this.context(), msg);
|
|
15
|
+
appendVerb(msg, {
|
|
16
|
+
verb: 'redirect',
|
|
17
|
+
actionHook
|
|
18
|
+
});
|
|
19
|
+
node.send(msg);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
RED.nodes.registerType('redirect', redirect);
|
|
23
|
+
|
|
24
|
+
}
|
package/src/nodes/s3-upload.html
CHANGED
|
@@ -5,13 +5,11 @@
|
|
|
5
5
|
defaults: {
|
|
6
6
|
name: {value: ''},
|
|
7
7
|
path: {value: ''},
|
|
8
|
-
region: {value: ''},
|
|
9
8
|
bucket: {value: '', required: true},
|
|
10
|
-
|
|
11
|
-
secret: {value: ''}
|
|
9
|
+
aws: {value: '', type: 'aws_auth'}
|
|
12
10
|
},
|
|
13
11
|
inputs:0,
|
|
14
|
-
outputs:
|
|
12
|
+
outputs:1,
|
|
15
13
|
icon: 'font-awesome/fa-headphones',
|
|
16
14
|
label: function() { return this.name || 'audio in';},
|
|
17
15
|
});
|
|
@@ -22,24 +20,41 @@
|
|
|
22
20
|
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
|
23
21
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
24
22
|
</div>
|
|
23
|
+
<div class="form-row">
|
|
24
|
+
<label for="node-input-aws">AWS credentials</label>
|
|
25
|
+
<input type="text" id="node-input-aws">
|
|
26
|
+
</div>
|
|
25
27
|
<div class="form-row">
|
|
26
28
|
<label for="node-input-path">Path</label>
|
|
27
|
-
<input type="text" id="node-input-path" placeholder="
|
|
29
|
+
<input type="text" id="node-input-path" placeholder="/socket">
|
|
28
30
|
</div>
|
|
29
31
|
<div class="form-row">
|
|
30
|
-
<label for="node-input-path">S3 Bucket</label>
|
|
31
|
-
<input type="text" id="node-input-bucket" placeholder="
|
|
32
|
+
<label for="node-input-path">S3 Bucket Access Point</label>
|
|
33
|
+
<input type="text" id="node-input-bucket" placeholder="arn:aws:s3:us-east-1:1234567890:accesspoint/mybucket">
|
|
32
34
|
</div>
|
|
33
35
|
</script>
|
|
34
36
|
|
|
35
37
|
<script type="text/html" data-help-name="audio in">
|
|
36
38
|
<p>Receives audio from jambonz and uploads to S3</p>
|
|
37
|
-
<h3>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
</
|
|
39
|
+
<h3>Outputs</h3>
|
|
40
|
+
<dl class="message-properties">
|
|
41
|
+
<dt>msg.event<span class="property-type">enum</span></dt>
|
|
42
|
+
<dd><code>newSession</code> for the start of a new session<br>
|
|
43
|
+
<code>partUploaded</code> when a chunk has been uploaded to S3<br>
|
|
44
|
+
<code>finishedUpload</code> when the session has ended and the upload completed
|
|
45
|
+
</dd>
|
|
46
|
+
<dt>msg.payload<span class="property-type">object</span></dt>
|
|
47
|
+
<dd>contains details of the newSession (callid, to from etc.) or the details of the upload to S3.
|
|
48
|
+
</dd>
|
|
49
|
+
</dl>
|
|
44
50
|
|
|
51
|
+
|
|
52
|
+
<h3>Details</h3>
|
|
53
|
+
<dl>
|
|
54
|
+
<dt>node-input-path</dt>
|
|
55
|
+
<dd>URL path to listen for incoming audio stream on; i.e. a webhook defined in a dial or listen verb</dd>
|
|
56
|
+
<dt>S3 Bucket Access Point </dt>dt>
|
|
57
|
+
<dd>The access point of an S3 bucket to upload recording to. This must allow public access.</dd>
|
|
58
|
+
</dl>
|
|
59
|
+
|
|
45
60
|
</script>
|
package/src/nodes/s3-upload.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
const { notDeepStrictEqual } = require('assert');
|
|
2
|
+
const { memoryStorage } = require('multer');
|
|
3
|
+
|
|
1
4
|
module.exports = function(RED) {
|
|
2
5
|
var WebSocket = require('ws');
|
|
3
6
|
var url = require('url');
|
|
@@ -26,13 +29,19 @@ module.exports = function(RED) {
|
|
|
26
29
|
RED.nodes.createNode(this, n);
|
|
27
30
|
var node = this;
|
|
28
31
|
|
|
32
|
+
// Get AWS Creds
|
|
33
|
+
const awsCreds = RED.nodes.getNode(n.aws);
|
|
34
|
+
if (awsCreds && awsCreds.credentials) {
|
|
35
|
+
AWS.Credentials({
|
|
36
|
+
accessKeyId: awsCreds.credentials.accessKey,
|
|
37
|
+
secretAccessKey: awsCreds.credentials.secretAccessKey
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
29
41
|
// Store local copies of the node configuration (as defined in the .html)
|
|
30
42
|
[
|
|
31
43
|
'path',
|
|
32
|
-
'bucket'
|
|
33
|
-
'region',
|
|
34
|
-
'accesskey',
|
|
35
|
-
'secret'
|
|
44
|
+
'bucket'
|
|
36
45
|
].forEach(function(attr) { node[attr] = n[attr];});
|
|
37
46
|
|
|
38
47
|
node._clients = {};
|
|
@@ -54,7 +63,9 @@ module.exports = function(RED) {
|
|
|
54
63
|
try {
|
|
55
64
|
socket.removeAllListeners('message');
|
|
56
65
|
node.metadata = JSON.parse(data);
|
|
57
|
-
node.
|
|
66
|
+
var msg = {payload : node.metadata}
|
|
67
|
+
msg.event = 'newSession'
|
|
68
|
+
node.send(msg)
|
|
58
69
|
const md = {
|
|
59
70
|
callSid: node.metadata.callSid,
|
|
60
71
|
accountSid: node.metadata.accountSid,
|
|
@@ -73,18 +84,22 @@ module.exports = function(RED) {
|
|
|
73
84
|
Metadata: md
|
|
74
85
|
});
|
|
75
86
|
upload.on('error', function(err) {
|
|
76
|
-
node.
|
|
87
|
+
node.error(`Error uploading: ${JSON.stringify(err)}`);
|
|
77
88
|
});
|
|
78
89
|
upload.on('part', function(details) {
|
|
79
|
-
|
|
90
|
+
var msg = {payload : details}
|
|
91
|
+
msg.event = 'partUploaded'
|
|
92
|
+
node.send(msg)
|
|
80
93
|
});
|
|
81
94
|
upload.on('uploaded', function(details) {
|
|
82
|
-
|
|
95
|
+
var msg = {payload : details}
|
|
96
|
+
msg.event = 'finishedUpload'
|
|
97
|
+
node.send(msg)
|
|
83
98
|
});
|
|
84
99
|
const duplex = WebSocket.createWebSocketStream(socket);
|
|
85
100
|
duplex.pipe(upload);
|
|
86
101
|
} catch (err) {
|
|
87
|
-
node.
|
|
102
|
+
node.error(`Error starting upload: ${err.message}`);
|
|
88
103
|
}
|
|
89
104
|
});
|
|
90
105
|
socket.on('error', function(err) {
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<!-- Javascript -->
|
|
2
|
+
<script type="text/javascript">
|
|
3
|
+
RED.nodes.registerType('say',{
|
|
4
|
+
category: 'jambonz',
|
|
5
|
+
color: '#bbabaa',
|
|
6
|
+
defaults: {
|
|
7
|
+
name: {value: ''},
|
|
8
|
+
text: {required: true},
|
|
9
|
+
early: {value: false},
|
|
10
|
+
loop: {value: 1},
|
|
11
|
+
vendor: {value: 'default'},
|
|
12
|
+
lang: {value: 'default'},
|
|
13
|
+
xlang: {},
|
|
14
|
+
voice: {value: 'default'},
|
|
15
|
+
xvoice: {}
|
|
16
|
+
},
|
|
17
|
+
inputs:1,
|
|
18
|
+
outputs:1,
|
|
19
|
+
icon: "font-awesome/fa-cubes",
|
|
20
|
+
label: function() { return this.name || 'say';},
|
|
21
|
+
oneditprepare: () => {
|
|
22
|
+
var node = this;
|
|
23
|
+
prepareTtsControls(node);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
<!-- HTML -->
|
|
31
|
+
<script type="text/html" data-template-name="say">
|
|
32
|
+
<div class="form-row">
|
|
33
|
+
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
|
34
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
35
|
+
</div>
|
|
36
|
+
<div class="form-row">
|
|
37
|
+
<label for="node-input-text"><i class="icon-tag"></i> Text</label>
|
|
38
|
+
<textarea id="node-input-text" rows="4" placeholder="Text or SSML to speak" style="width:70%"></textarea>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="form-row">
|
|
41
|
+
<label for="node-input-early"><i class="icon-tag"></i>Early media</label>
|
|
42
|
+
<input type="checkbox" id="node-input-early">
|
|
43
|
+
</div>
|
|
44
|
+
<div class="form-row">
|
|
45
|
+
<label for="node-input-loop"><i class="icon-tag"></i>Loop</label>
|
|
46
|
+
<input type="input" id="node-input-loop" placeholder="number of times to repeat">
|
|
47
|
+
</div>
|
|
48
|
+
<fieldset>
|
|
49
|
+
<legend>Speech synthesis options</legend>
|
|
50
|
+
<div class="form-row">
|
|
51
|
+
<label for="node-input-vendor">Vendor</label>
|
|
52
|
+
<select id="node-input-vendor">
|
|
53
|
+
<option value="default" selected>--application default--</option>
|
|
54
|
+
<option value="google">google</option>
|
|
55
|
+
<option value="aws">aws/polly</option>
|
|
56
|
+
<option value="microsoft">microsoft</option>
|
|
57
|
+
<option value="ibm">ibm</option>
|
|
58
|
+
<option value="nuance">nuance</option>
|
|
59
|
+
</select>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="form-row" style="display: none;">
|
|
62
|
+
<label for="node-input-lang"> Lang</label>
|
|
63
|
+
<input type="text" id="node-input-lang">
|
|
64
|
+
</div>
|
|
65
|
+
<div class="form-row">
|
|
66
|
+
<label for="node-input-xlang">Language</label>
|
|
67
|
+
<select id="node-input-xlang">
|
|
68
|
+
<option value="default" selected>--application default--</option>
|
|
69
|
+
</select>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="form-row" style="display: none;">
|
|
72
|
+
<label for="node-input-voice"> Voice</label>
|
|
73
|
+
<input type="text" id="node-input-voice">
|
|
74
|
+
</div>
|
|
75
|
+
<div class="form-row">
|
|
76
|
+
<label for="node-input-xvoice">Voice</label>
|
|
77
|
+
<select id="node-input-xvoice">
|
|
78
|
+
<option value="default" selected>--application default--</option>
|
|
79
|
+
</select>
|
|
80
|
+
</div>
|
|
81
|
+
</fieldset>
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<!-- Help Text -->
|
|
85
|
+
<script type="text/html" data-help-name="say">
|
|
86
|
+
<p>Synthesize speech from text or SSML</p>
|
|
87
|
+
<h3>Inputs</h3>
|
|
88
|
+
<dl class="message-properties">
|
|
89
|
+
<dt>Text<span class="property-type">string</span></dt>
|
|
90
|
+
<dd>text to speak; may contain SSML tags</dd>
|
|
91
|
+
<dt>Early media<span class="property-type">boolean</span></dt>
|
|
92
|
+
<dd>if checked, play the prompt over an early media connection</dd>
|
|
93
|
+
<dt>Loop<span class="property-type">number</span></dt>
|
|
94
|
+
<dd>number of times to play the prompt</dd>
|
|
95
|
+
<dt>Vendor<span class="property-type">string</span></dt>
|
|
96
|
+
<dd>TTS vendor</dd>
|
|
97
|
+
<dt>Language<span class="property-type">string</span></dt>
|
|
98
|
+
<dd>TTS language</dd>
|
|
99
|
+
<dt>Voice<span class="property-type">string</span></dt>
|
|
100
|
+
<dd>TTS voice</dd>
|
|
101
|
+
</dl>
|
|
102
|
+
|
|
103
|
+
<h3>Outputs</h3>
|
|
104
|
+
<dl class="message-properties">
|
|
105
|
+
<dt>jambonz<span class="property-type">object</span></dt>
|
|
106
|
+
<dd> <code>msg.jambonz</code> will contain any previous actions provided to the input with the new <code>say</code> action appended </dd>
|
|
107
|
+
</dl>
|
|
108
|
+
|
|
109
|
+
<h3>Details</h3>
|
|
110
|
+
The say command is used to send synthesized speech to the remote party.
|
|
111
|
+
The text provided may be either plain text or may use SSML tags.
|
|
112
|
+
|
|
113
|
+
Within the text field you can use mustache syntax to insert properties of the msg, flow or global objects.
|
|
114
|
+
For example if you wanted to insert the value of msg.payload into the text you could put
|
|
115
|
+
<code>The payload is {{msg.payload}}</code>
|
|
116
|
+
<h3>References</h3>
|
|
117
|
+
<ul>
|
|
118
|
+
<li><a href="https://docs.jambonz.org/jambonz/#say">Jambonz say reference</a></li>
|
|
119
|
+
</script>
|
package/src/nodes/say.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
var {createHash} = require('crypto');
|
|
2
|
+
const bent = require('bent');
|
|
3
|
+
var mustache = require('mustache');
|
|
4
|
+
mustache.escape = function(text) {return text;};
|
|
5
|
+
var {appendVerb, v_text_resolve} = require('./libs')
|
|
6
|
+
|
|
7
|
+
module.exports = function(RED) {
|
|
8
|
+
/** say */
|
|
9
|
+
function say(config) {
|
|
10
|
+
RED.nodes.createNode(this, config);
|
|
11
|
+
this.text = config.text;
|
|
12
|
+
this.early = config.early;
|
|
13
|
+
this.loop = config.loop;
|
|
14
|
+
var node = this;
|
|
15
|
+
|
|
16
|
+
node.on('input', function(msg) {
|
|
17
|
+
const text = v_text_resolve(node, this.text, this.context(), msg);
|
|
18
|
+
var obj = {
|
|
19
|
+
verb: 'say',
|
|
20
|
+
text,
|
|
21
|
+
loop: parseInt(node.loop),
|
|
22
|
+
earlyMedia: node.early
|
|
23
|
+
};
|
|
24
|
+
if (config.vendor != 'default') {
|
|
25
|
+
Object.assign(obj, {
|
|
26
|
+
synthesizer: {
|
|
27
|
+
vendor: config.vendor,
|
|
28
|
+
language: config.lang,
|
|
29
|
+
voice: config.voice
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
appendVerb(msg, obj);
|
|
34
|
+
node.send(msg);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
RED.nodes.registerType('say', say);
|
|
38
|
+
}
|