@jambonz/node-red-contrib-jambonz 2.4.32 → 2.5.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/package.json +8 -7
- package/resources/editor.js +5 -0
- package/src/nodes/alert.html +75 -0
- package/src/nodes/alert.js +23 -0
- package/src/nodes/auth.html +15 -3
- package/src/nodes/auth.js +10 -9
- package/src/nodes/conference.html +6 -1
- package/src/nodes/conference.js +2 -1
- package/src/nodes/create_call.html +121 -13
- package/src/nodes/create_call.js +41 -3
- package/src/nodes/dial.html +16 -10
- package/src/nodes/dial.js +6 -3
- package/src/nodes/get_alerts.js +13 -2
- package/src/nodes/get_call.js +13 -2
- package/src/nodes/get_calls.js +12 -2
- package/src/nodes/get_recent_calls.js +12 -2
- package/src/nodes/lcc.js +4 -2
- package/src/nodes/s3-upload.html +8 -0
- package/src/nodes/s3-upload.js +2 -0
- package/src/utils/http-helpers.js +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jambonz/node-red-contrib-jambonz",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "Node-RED nodes for jambonz platform",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node-red"
|
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
"dialogflow": "src/nodes/dialogflow.js",
|
|
42
42
|
"leave": "src/nodes/leave.js",
|
|
43
43
|
"rasa": "src/nodes/rasa.js",
|
|
44
|
+
"answer": "src/nodes/answer.js",
|
|
45
|
+
"dub": "src/nodes/dub.js",
|
|
46
|
+
"generic": "src/nodes/generic-verb.js",
|
|
47
|
+
"alert": "src/nodes/alert.js",
|
|
44
48
|
"userauth": "src/nodes/userauth.js",
|
|
45
49
|
"create call": "src/nodes/create_call.js",
|
|
46
50
|
"create sms": "src/nodes/create_sms.js",
|
|
@@ -48,10 +52,7 @@
|
|
|
48
52
|
"get_alerts": "src/nodes/get_alerts.js",
|
|
49
53
|
"get_call": "src/nodes/get_call.js",
|
|
50
54
|
"get_calls": "src/nodes/get_calls.js",
|
|
51
|
-
"get_recent_calls": "src/nodes/get_recent_calls.js"
|
|
52
|
-
"answer": "src/nodes/answer.js",
|
|
53
|
-
"dub": "src/nodes/dub.js",
|
|
54
|
-
"generic": "src/nodes/generic-verb.js"
|
|
55
|
+
"get_recent_calls": "src/nodes/get_recent_calls.js"
|
|
55
56
|
}
|
|
56
57
|
},
|
|
57
58
|
"author": "Dave Horton",
|
|
@@ -64,9 +65,9 @@
|
|
|
64
65
|
"hash-sum": "^2.0.0",
|
|
65
66
|
"is-utf8": "^0.2.1",
|
|
66
67
|
"media-typer": "^1.1.0",
|
|
67
|
-
"multer": "^2.0.
|
|
68
|
+
"multer": "^2.0.2",
|
|
68
69
|
"mustache": "^4.2.0",
|
|
69
|
-
"on-headers": "^1.0
|
|
70
|
+
"on-headers": "^1.1.0",
|
|
70
71
|
"raw-body": "^2.5.2",
|
|
71
72
|
"s3-upload-stream": "^1.0.7",
|
|
72
73
|
"undici": "^5.28.5",
|
package/resources/editor.js
CHANGED
|
@@ -123,9 +123,14 @@
|
|
|
123
123
|
|
|
124
124
|
function testCredentials() {
|
|
125
125
|
let baseUrl
|
|
126
|
+
|
|
126
127
|
const accountSid = $("#node-config-input-accountSid").val();
|
|
127
128
|
const token = $("#node-config-input-apiToken").val();
|
|
128
129
|
const status = $("#node-config-test-status");
|
|
130
|
+
if (!['str', 'https://api.jambonz.cloud'].includes($("#node-config-input-urlType").val()) || ($("#node-config-input-apiTokenType").val() != 'str') || ($("#node-config-input-accountSidType").val() != 'str')){
|
|
131
|
+
status.text('Cannot test credentials using TypedInputs');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
129
134
|
status.text('');
|
|
130
135
|
if ($("#node-config-input-urlType").val() == 'str'){
|
|
131
136
|
baseUrl = $("#node-config-input-url").val();
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<!-- Javascript -->
|
|
2
|
+
<script type="text/javascript">
|
|
3
|
+
var mustacheType = {
|
|
4
|
+
value: "mustache",
|
|
5
|
+
label: "mustache",
|
|
6
|
+
hasvalue: true,
|
|
7
|
+
icon: "resources/@jambonz/node-red-contrib-jambonz/icons/mustache.svg",
|
|
8
|
+
};
|
|
9
|
+
RED.nodes.registerType("alert", {
|
|
10
|
+
category: "jambonz",
|
|
11
|
+
color: "#bbabaa",
|
|
12
|
+
defaults: {
|
|
13
|
+
name: { value: "" },
|
|
14
|
+
message: { value: "" },
|
|
15
|
+
messageType: { value: "str" },
|
|
16
|
+
},
|
|
17
|
+
inputs: 1,
|
|
18
|
+
outputs: 1,
|
|
19
|
+
icon: "font-awesome/fa-cubes",
|
|
20
|
+
label: function () {
|
|
21
|
+
return this.name || "alert";
|
|
22
|
+
},
|
|
23
|
+
oneditprepare: function () {
|
|
24
|
+
$("#node-input-message").typedInput({
|
|
25
|
+
default: $("#node-input-messageType").val(),
|
|
26
|
+
types: ["str", "msg", "flow", "global", mustacheType],
|
|
27
|
+
typeField: $("#node-input-messageType"),
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<!-- HTML -->
|
|
34
|
+
<script type="text/html" data-template-name="alert">
|
|
35
|
+
<div class="form-row">
|
|
36
|
+
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
|
37
|
+
<input type="text" id="node-input-name" placeholder="Name" />
|
|
38
|
+
</div>
|
|
39
|
+
<div class="form-row">
|
|
40
|
+
<label for="node-input-message">Message</label>
|
|
41
|
+
<input
|
|
42
|
+
type="text"
|
|
43
|
+
id="node-input-message"
|
|
44
|
+
placeholder="Alert message"
|
|
45
|
+
/>
|
|
46
|
+
<input type="hidden" id="node-input-messageType" />
|
|
47
|
+
</div>
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<!-- Help Text -->
|
|
51
|
+
<script type="text/html" data-help-name="alert">
|
|
52
|
+
<p>Raise an alert in the jambonz platform.</p>
|
|
53
|
+
<h3>Properties</h3>
|
|
54
|
+
<p><code>Message</code> - an alert message</p>
|
|
55
|
+
|
|
56
|
+
<h3>Outputs</h3>
|
|
57
|
+
<dl class="message-properties">
|
|
58
|
+
<dt>jambonz<span class="property-type">object</span></dt>
|
|
59
|
+
<dd>
|
|
60
|
+
<code>msg.jambonz</code> will contain any previous actions provided
|
|
61
|
+
to the input with the new <code>alert</code> action appended
|
|
62
|
+
</dd>
|
|
63
|
+
</dl>
|
|
64
|
+
|
|
65
|
+
<h3>Details</h3>
|
|
66
|
+
The alert verb raises an alert in the jambonz platform
|
|
67
|
+
<h3>References</h3>
|
|
68
|
+
<ul>
|
|
69
|
+
<li>
|
|
70
|
+
<a href="https://docs.jambonz.org/verbs/verbs/alert"
|
|
71
|
+
>Jambonz alert reference</a
|
|
72
|
+
>
|
|
73
|
+
</li>
|
|
74
|
+
</ul>
|
|
75
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
var { appendVerb, new_resolve } = require("./libs");
|
|
2
|
+
|
|
3
|
+
module.exports = function (RED) {
|
|
4
|
+
/** alert */
|
|
5
|
+
function alert(config) {
|
|
6
|
+
RED.nodes.createNode(this, config);
|
|
7
|
+
var node = this;
|
|
8
|
+
node.on("input", async function (msg) {
|
|
9
|
+
appendVerb(msg, {
|
|
10
|
+
verb: "alert",
|
|
11
|
+
message: await new_resolve(
|
|
12
|
+
RED,
|
|
13
|
+
config.message,
|
|
14
|
+
config.messageType,
|
|
15
|
+
node,
|
|
16
|
+
msg
|
|
17
|
+
),
|
|
18
|
+
});
|
|
19
|
+
node.send(msg);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
RED.nodes.registerType("alert", alert);
|
|
23
|
+
};
|
package/src/nodes/auth.html
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
defaults: {
|
|
6
6
|
name: {},
|
|
7
7
|
urlType: {},
|
|
8
|
-
url: {type: 'text'}
|
|
8
|
+
url: {type: 'text'},
|
|
9
|
+
accountSidType: {value: 'str'},
|
|
10
|
+
apiTokenType: {value: 'str'}
|
|
9
11
|
},
|
|
10
12
|
credentials: {
|
|
11
13
|
accountSid: {type: 'text'},
|
|
@@ -15,10 +17,17 @@
|
|
|
15
17
|
oneditprepare: function() {
|
|
16
18
|
$('#btn-test-credentials').on('click', testCredentials);
|
|
17
19
|
$('#node-config-input-url').typedInput({
|
|
18
|
-
types: ['str',
|
|
19
|
-
{value: 'https://api.jambonz.cloud', label : 'jambonz.cloud', hasValue: false}],
|
|
20
|
+
types: ['str', 'env', 'msg', 'flow', 'global', {value: 'https://api.jambonz.cloud', label : 'jambonz.cloud', hasValue: false}],
|
|
20
21
|
typeField: $('#node-config-input-urlType')
|
|
21
22
|
});
|
|
23
|
+
$('#node-config-input-accountSid').typedInput({
|
|
24
|
+
types: ['str', 'env', 'msg', 'flow', 'global'],
|
|
25
|
+
typeField: $('#node-config-input-accountSidType')
|
|
26
|
+
});
|
|
27
|
+
$('#node-config-input-apiToken').typedInput({
|
|
28
|
+
types: ['str', 'env', 'msg', 'flow', 'global'],
|
|
29
|
+
typeField: $('#node-config-input-apiTokenType')
|
|
30
|
+
});
|
|
22
31
|
},
|
|
23
32
|
oneditsave: function() {
|
|
24
33
|
const baseUrl = $('#node-config-input-url').val();
|
|
@@ -72,10 +81,13 @@
|
|
|
72
81
|
<div class="form-row">
|
|
73
82
|
<label for="node-config-input-accountSid">Account SID</label>
|
|
74
83
|
<input type="text" id="node-config-input-accountSid">
|
|
84
|
+
<input type="hidden" id="node-config-input-accountSidType">
|
|
75
85
|
</div>
|
|
76
86
|
<div class="form-row">
|
|
77
87
|
<label for="node-config-input-apiToken">API token</label>
|
|
78
88
|
<input type="text" id="node-config-input-apiToken">
|
|
89
|
+
<input type="hidden" id="node-config-input-apiTokenType">
|
|
90
|
+
|
|
79
91
|
</div>
|
|
80
92
|
<div class="form-row">
|
|
81
93
|
<button id="btn-test-credentials"><i class="fa fa-lock"></i> Test Credentials</button>
|
package/src/nodes/auth.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
module.exports = function(RED) {
|
|
2
2
|
function jambonz_auth(config) {
|
|
3
3
|
RED.nodes.createNode(this, config);
|
|
4
|
-
|
|
5
|
-
this.apiKey = config.apiKey;
|
|
4
|
+
var node = this;
|
|
6
5
|
this.name = config.name;
|
|
7
|
-
this.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
this.url = config.url;
|
|
7
|
+
this.urlType = config.urlType;
|
|
8
|
+
this.accountSid = config.accountSid;
|
|
9
|
+
this.accountSidType = config.accountSidType;
|
|
10
|
+
this.apiToken = config.apiToken;
|
|
11
|
+
this.apiTokenType = config.apiTokenType;
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
RED.nodes.registerType('jambonz_auth', jambonz_auth, {
|
|
@@ -17,7 +16,9 @@ module.exports = function(RED) {
|
|
|
17
16
|
url: {type: 'text'},
|
|
18
17
|
urlType: {},
|
|
19
18
|
accountSid: {type: 'text'},
|
|
20
|
-
|
|
19
|
+
accountSidType: {},
|
|
20
|
+
apiToken: {type: 'text'},
|
|
21
|
+
apiTokenType: {}
|
|
21
22
|
}
|
|
22
23
|
});
|
|
23
24
|
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
actionHookType: {value: 'str'},
|
|
28
28
|
statusHook: {},
|
|
29
29
|
statusHookType: {value: 'str'},
|
|
30
|
-
joinMuted : {value: false}
|
|
30
|
+
joinMuted : {value: false},
|
|
31
|
+
distributeDtmf: {value: false}
|
|
31
32
|
},
|
|
32
33
|
inputs:1,
|
|
33
34
|
outputs:1,
|
|
@@ -110,6 +111,10 @@
|
|
|
110
111
|
<div class="form-row">
|
|
111
112
|
<label for="node-input-joinMuted">Join muted</label>
|
|
112
113
|
<input type="checkbox" id="node-input-joinMuted">
|
|
114
|
+
</div>
|
|
115
|
+
<div class="form-row">
|
|
116
|
+
<label for="node-input-distributeDtmf">Distribute DTMF</label>
|
|
117
|
+
<input type="checkbox" id="node-input-distributeDtmf">
|
|
113
118
|
</div>
|
|
114
119
|
<div class="form-row">
|
|
115
120
|
<label for="node-input-maxParticipants">Max participants</label>
|
package/src/nodes/conference.js
CHANGED
|
@@ -19,7 +19,8 @@ module.exports = function(RED) {
|
|
|
19
19
|
beep: config.beep,
|
|
20
20
|
startConferenceOnEnter: config.startConferenceOnEnter,
|
|
21
21
|
endConferenceOnExit: config.endConferenceOnExit,
|
|
22
|
-
joinMuted: config.joinMuted
|
|
22
|
+
joinMuted: config.joinMuted,
|
|
23
|
+
...(config.distributeDtmf && {'distributeDtmf': true})
|
|
23
24
|
});
|
|
24
25
|
node.send(msg);
|
|
25
26
|
});
|
|
@@ -19,6 +19,12 @@
|
|
|
19
19
|
toType: {value: ''},
|
|
20
20
|
trunk: {value: ''},
|
|
21
21
|
trunkType: {value: 'str'},
|
|
22
|
+
sipauth_user: {value: ''},
|
|
23
|
+
sipauth_userType: {value: 'str'},
|
|
24
|
+
sipauth_password: {value: ''},
|
|
25
|
+
sipauth_passwordType: {value: 'str'},
|
|
26
|
+
sip_proxy: {value: ''},
|
|
27
|
+
sip_proxyType: {value: 'str'},
|
|
22
28
|
dest: {value: 'phone', required: true},
|
|
23
29
|
timeout: {value: '' },
|
|
24
30
|
timeoutType: {value: 'num'},
|
|
@@ -56,7 +62,17 @@
|
|
|
56
62
|
return(true)
|
|
57
63
|
}
|
|
58
64
|
}},
|
|
59
|
-
recognizerlang: {value: 'default'}
|
|
65
|
+
recognizerlang: {value: 'default'},
|
|
66
|
+
amd_actionHook : {},
|
|
67
|
+
amd_actionHookType: {value: 'str'},
|
|
68
|
+
amd_recognizer_vendor: {value: 'default'},
|
|
69
|
+
amd_recognizer_lang: {value: 'default'},
|
|
70
|
+
amd_thresholdWordCount: {},
|
|
71
|
+
amd_timers_decisionTimeoutMs: {},
|
|
72
|
+
amd_timers_greetingCompletionTimeoutMs:{},
|
|
73
|
+
amd_timers_noSpeechTimeoutMs: {},
|
|
74
|
+
amd_timers_toneTimeoutMs: {},
|
|
75
|
+
amd_digitCount: {}
|
|
60
76
|
},
|
|
61
77
|
inputs:1,
|
|
62
78
|
outputs:1,
|
|
@@ -70,6 +86,7 @@
|
|
|
70
86
|
var serverElem = $('#node-input-server');
|
|
71
87
|
var applicationElem = $('#node-input-application');
|
|
72
88
|
var trunkDiv = $('#trunk');
|
|
89
|
+
var sipDiv = $('#sip');
|
|
73
90
|
prepareTtsControls(node);
|
|
74
91
|
prepareSttControls(node);
|
|
75
92
|
|
|
@@ -77,6 +94,8 @@
|
|
|
77
94
|
var selectedDest = destElem.find(':selected').val();
|
|
78
95
|
if ('phone' === selectedDest) trunkDiv.show();
|
|
79
96
|
else trunkDiv.hide();
|
|
97
|
+
if ('sip' === selectedDest) sipDiv.show();
|
|
98
|
+
else sipDiv.hide();
|
|
80
99
|
}
|
|
81
100
|
|
|
82
101
|
destElem.change(onDestChanged);
|
|
@@ -119,19 +138,41 @@
|
|
|
119
138
|
default: $('#node-input-timeoutType').val(),
|
|
120
139
|
types: ['num', 'msg', 'flow', 'global', 'jsonata', 'env'],
|
|
121
140
|
typeField: $('#node-input-timeoutType')
|
|
141
|
+
});
|
|
142
|
+
$('#node-input-amd_actionHook').typedInput({
|
|
143
|
+
default: $('#node-input-amd_actionHookType').val(),
|
|
144
|
+
types: ['str','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
|
|
145
|
+
typeField: $('#node-input-amd_actionHookType')
|
|
146
|
+
});
|
|
147
|
+
$('#node-input-sipauth_user').typedInput({
|
|
148
|
+
default: $('#node-input-sipauth_userType').val(),
|
|
149
|
+
types: ['str','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
|
|
150
|
+
typeField: $('#node-input-sipauth_userType')
|
|
151
|
+
});
|
|
152
|
+
$('#node-input-sipauth_password').typedInput({
|
|
153
|
+
default: $('#node-input-sipauth_passwordType').val(),
|
|
154
|
+
types: ['str','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
|
|
155
|
+
typeField: $('#node-input-sipauth_passwordType')
|
|
156
|
+
});
|
|
157
|
+
$('#node-input-sip_proxy').typedInput({
|
|
158
|
+
default: $('#node-input-sip_proxyType').val(),
|
|
159
|
+
types: ['str','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
|
|
160
|
+
typeField: $('#node-input-sip_proxyType')
|
|
122
161
|
});
|
|
123
162
|
var populateApplications = function() {
|
|
124
163
|
var serverId = $('#node-input-server option:selected').val();
|
|
125
164
|
$.ajax({
|
|
126
165
|
url: `_jambonz/applications/${serverId}`,
|
|
127
166
|
dataType: 'json',
|
|
128
|
-
timeout:
|
|
129
|
-
|
|
130
|
-
|
|
167
|
+
timeout: 5000,
|
|
168
|
+
error: (err) => {
|
|
169
|
+
applicationElem.find('option').remove();
|
|
170
|
+
applicationElem.append('<option value="msg.application_sid">--msg.application_sid--</option>');
|
|
171
|
+
console.log(`Unable to get application list ${err.statusText} ${err}`);
|
|
131
172
|
},
|
|
132
173
|
success: (res) => {
|
|
133
174
|
applicationElem.find('option').remove();
|
|
134
|
-
var options = '';
|
|
175
|
+
var options = '<option value="msg.application_sid">--msg.application_sid--</option>';
|
|
135
176
|
res.forEach((app) => {
|
|
136
177
|
if (node.application === app.application_sid) {
|
|
137
178
|
options += `<option value="${app.application_sid}" selected>${app.name}</option>`;
|
|
@@ -240,9 +281,18 @@
|
|
|
240
281
|
<input type="text" id="node-input-callername" placeholder="calling party name">
|
|
241
282
|
<input type="hidden" id="node-input-callernameType">
|
|
242
283
|
</div>
|
|
284
|
+
<div class="form-row">
|
|
285
|
+
<label for="node-input-dest">Dest Type</label>
|
|
286
|
+
<select id="node-input-dest">
|
|
287
|
+
<option value="phone">phone number</option>
|
|
288
|
+
<option value="user">registered sip device/user</option>
|
|
289
|
+
<option value="sip">sip endpoint</option>
|
|
290
|
+
<option value="ms-teams">Microsoft teams</option>
|
|
291
|
+
</select>
|
|
292
|
+
</div>
|
|
243
293
|
<div class="form-row">
|
|
244
294
|
<label for="node-input-to">To</label>
|
|
245
|
-
<input type="text" id="node-input-to" placeholder="
|
|
295
|
+
<input type="text" id="node-input-to" placeholder="destination">
|
|
246
296
|
<input type="hidden" id="node-input-toType">
|
|
247
297
|
</div>
|
|
248
298
|
<div class="form-row" id ="trunk">
|
|
@@ -250,15 +300,24 @@
|
|
|
250
300
|
<input type="text" id="node-input-trunk" placeholder="Specify the name of the trunk used for this outbound call">
|
|
251
301
|
<input type="hidden" id="node-input-trunkType">
|
|
252
302
|
</div>
|
|
303
|
+
<div id="sip">
|
|
253
304
|
<div class="form-row">
|
|
254
|
-
<label for="node-input-
|
|
255
|
-
<
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
305
|
+
<label for="node-input-sipauth_user">SIP Auth Username</label>
|
|
306
|
+
<input type="text" id="node-input-sipauth_user" placeholder="Username for SIP Authentication">
|
|
307
|
+
<input type="hidden" id="node-input-sipauth_userType">
|
|
308
|
+
</div>
|
|
309
|
+
<div class="form-row">
|
|
310
|
+
<label for="node-input-sipauth_password">SIP Auth Password</label>
|
|
311
|
+
<input type="text" id="node-input-sipauth_password" placeholder="Passwrd for SIP Authentication">
|
|
312
|
+
<input type="hidden" id="node-input-sipauth_passwordType">
|
|
313
|
+
</div>
|
|
314
|
+
<div class="form-row">
|
|
315
|
+
<label for="node-input-sip_proxy">SIP Proxy</label>
|
|
316
|
+
<input type="text" id="node-input-sip_proxy" placeholder="SIP Proxy">
|
|
317
|
+
<input type="hidden" id="node-input-sip_proxyType">
|
|
261
318
|
</div>
|
|
319
|
+
</div>
|
|
320
|
+
|
|
262
321
|
<div class="form-row">
|
|
263
322
|
<label for="node-input-timeout">Ring timeout</label>
|
|
264
323
|
<input type="text" id="node-input-timeout" placeholder="ring no answer timeout in secs (default: 60)">
|
|
@@ -292,6 +351,55 @@
|
|
|
292
351
|
<ol id="node-input-headers-container"></ol>
|
|
293
352
|
</div>
|
|
294
353
|
</fieldset>
|
|
354
|
+
<fieldset>
|
|
355
|
+
<legend>Answering Machine Detection</legend>
|
|
356
|
+
<div class="form-row">
|
|
357
|
+
<label for="node-input-amd_actionHook">actionHook</label>
|
|
358
|
+
<input type="text" id="node-input-amd_actionHook">
|
|
359
|
+
<input type="hidden" id="node-input-amd_actionHookType">
|
|
360
|
+
</div>
|
|
361
|
+
<div class="form-row">
|
|
362
|
+
<label for="node-input-amd_recognizer_vendor">Vendor</label>
|
|
363
|
+
<select id="node-input-amd_recognizer_vendor">
|
|
364
|
+
<option value="default" selected>--application default--</option>
|
|
365
|
+
<option value="google">Google</option>
|
|
366
|
+
<option value="aws">AWS</option>
|
|
367
|
+
<option value="deepgram">deepgram</option>
|
|
368
|
+
<option value="microsoft">microsoft</option>
|
|
369
|
+
<option value="ibm">ibm</option>
|
|
370
|
+
<option value="nuance">nuance</option>
|
|
371
|
+
</select>
|
|
372
|
+
</div>
|
|
373
|
+
<div class="form-row">
|
|
374
|
+
<label for="node-input-amd_recognizer_lang">Language</label>
|
|
375
|
+
<select id="node-input-amd_recognizer_lang">
|
|
376
|
+
</select>
|
|
377
|
+
</div>
|
|
378
|
+
<div class="form-row">
|
|
379
|
+
<label for="node-input-amd_thresholdWordCount">Threshold Word Count</label>
|
|
380
|
+
<input type="text" id="node-input-amd_thresholdWordCount">
|
|
381
|
+
</div>
|
|
382
|
+
<div class="form-row">
|
|
383
|
+
<label for="node-input-amd_digitCount">Digit Count</label>
|
|
384
|
+
<input type="text" id="node-input-amd_digitCount">
|
|
385
|
+
</div>
|
|
386
|
+
<div class="form-row">
|
|
387
|
+
<label for="node-input-amd_timers_decisionTimeoutMs">Decision Timeout</label>
|
|
388
|
+
<input type="text" id="node-input-amd_timers_decisionTimeoutMs">
|
|
389
|
+
</div>
|
|
390
|
+
<div class="form-row">
|
|
391
|
+
<label for="node-input-amd_timers_greetingCompletionTimeoutMs">Greeting Completion Timeout</label>
|
|
392
|
+
<input type="text" id="node-input-amd_timers_greetingCompletionTimeoutMs">
|
|
393
|
+
</div>
|
|
394
|
+
<div class="form-row">
|
|
395
|
+
<label for="node-input-amd_timers_noSpeechTimeoutMs">No-Speech Timeout</label>
|
|
396
|
+
<input type="text" id="node-input-amd_timers_noSpeechTimeoutMs">
|
|
397
|
+
</div>
|
|
398
|
+
<div class="form-row">
|
|
399
|
+
<label for="node-input-amd_timers_toneTimeoutMs">Tone Timeout</label>
|
|
400
|
+
<input type="text" id="node-input-amd_timers_toneTimeoutMs">
|
|
401
|
+
</div>
|
|
402
|
+
</fieldset>
|
|
295
403
|
<div id="url-options">
|
|
296
404
|
<fieldset>
|
|
297
405
|
<legend>Webhook options</legend>
|
package/src/nodes/create_call.js
CHANGED
|
@@ -10,8 +10,10 @@ module.exports = function(RED) {
|
|
|
10
10
|
node.on('input', async (msg, send, done) => {
|
|
11
11
|
send = send || function() { node.send.apply(node, arguments);};
|
|
12
12
|
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
const url = await new_resolve(RED, server.url, server.urlType, node, msg);
|
|
14
|
+
const accountSid = await new_resolve(RED, server.credentials.accountSid, server.accountSidType, node, msg);
|
|
15
|
+
const apiToken = await new_resolve(RED, server.credentials.apiToken, server.apiTokenType, node, msg);
|
|
16
|
+
|
|
15
17
|
if (!url || !accountSid || !apiToken) {
|
|
16
18
|
node.error(`invalid / missing credentials, skipping create-call node: ${JSON.stringify(server.credentials)}`);
|
|
17
19
|
send(msg);
|
|
@@ -46,7 +48,11 @@ module.exports = function(RED) {
|
|
|
46
48
|
|
|
47
49
|
switch (config.mode) {
|
|
48
50
|
case 'app':
|
|
49
|
-
|
|
51
|
+
if (config.application == 'msg.application_sid'){
|
|
52
|
+
opts.application_sid = msg.application_sid
|
|
53
|
+
} else{
|
|
54
|
+
opts.application_sid = config.application;
|
|
55
|
+
}
|
|
50
56
|
break
|
|
51
57
|
case 'url':
|
|
52
58
|
opts.call_hook = {
|
|
@@ -82,7 +88,12 @@ module.exports = function(RED) {
|
|
|
82
88
|
opts.to.name = to;
|
|
83
89
|
break;
|
|
84
90
|
case 'sip':
|
|
91
|
+
var sipauth_user = await new_resolve(RED, config.sipauth_user, config.sipauth_userType, node, msg);
|
|
92
|
+
var sipauth_password = await new_resolve(RED, config.sipauth_password, config.sipauth_passwordType, node, msg);
|
|
93
|
+
var sip_proxy = await new_resolve(RED, config.sip_proxy, config.sip_proxyType, node, msg);
|
|
85
94
|
opts.to.sipUri = to;
|
|
95
|
+
sip_proxy ? opts.to.proxy = sip_proxy : null
|
|
96
|
+
sipauth_user ? opts.to.auth = { username :sipauth_user, password: sipauth_password} : null
|
|
86
97
|
break;
|
|
87
98
|
case 'ms-teams':
|
|
88
99
|
opts.to.user = to;
|
|
@@ -93,6 +104,33 @@ module.exports = function(RED) {
|
|
|
93
104
|
send(msg);
|
|
94
105
|
return;
|
|
95
106
|
}
|
|
107
|
+
|
|
108
|
+
// AMD
|
|
109
|
+
const _amd_actionHook = await new_resolve(RED, config.amd_actionHook, config.amd_actionHookType, node, msg)
|
|
110
|
+
if (_amd_actionHook){
|
|
111
|
+
opts.amd = {}
|
|
112
|
+
opts.amd.actionHook = _amd_actionHook;
|
|
113
|
+
config.amd_thresholdWordCount && (opts.amd.thresholdWordCount = Number(config.amd_thresholdWordCount))
|
|
114
|
+
config.amd_digitCount && (opts.amd.digitCount = Number(config.amd_digitCount))
|
|
115
|
+
opts.amd.timers = {
|
|
116
|
+
...(config.amd_timers_decisionTimeoutMs && {decisionTimeoutMs: Number(config.amd_timers_decisionTimeoutMs)}),
|
|
117
|
+
...(config.amd_timers_greetingCompletionTimeoutMs && {greetingCompletionTimeoutMs: Number(config.amd_timers_greetingCompletionTimeoutMs)}),
|
|
118
|
+
...(config.amd_timers_noSpeechTimeoutMs && {noSpeechTimeoutMs: Number(config.amd_timers_noSpeechTimeoutMs)}),
|
|
119
|
+
...(config.amd_timers_toneTimeoutMs && {toneTimeoutMs: Number(config.amd_timers_toneTimeoutMs)}),
|
|
120
|
+
}
|
|
121
|
+
//If none of the timer values are set remove the object
|
|
122
|
+
if (Object.keys(opts.amd.timers).length == 0){
|
|
123
|
+
delete(opts.amd.timers)
|
|
124
|
+
}
|
|
125
|
+
//If custom recogniser is used
|
|
126
|
+
if (config.amd_recognizer_vendor != 'default'){
|
|
127
|
+
opts.amd.recognizer = {
|
|
128
|
+
...(config.amd_recognizer_vendor && {vendor : config.amd_recognizer_vendor}),
|
|
129
|
+
...(config.amd_recognizer_lang && {language : config.amd_recognizer_lang})
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
96
134
|
try {
|
|
97
135
|
const response = await doCreateCall(node, url, accountSid, apiToken, opts);
|
|
98
136
|
msg.statusCode = 201;
|
package/src/nodes/dial.html
CHANGED
|
@@ -373,7 +373,7 @@
|
|
|
373
373
|
addItem: function(container, i, opt) {
|
|
374
374
|
var header = opt;
|
|
375
375
|
if (!header.hasOwnProperty('h')) {
|
|
376
|
-
header = {h: '', v: ''};
|
|
376
|
+
header = {h: '', v: '', vType: 'str'};
|
|
377
377
|
}
|
|
378
378
|
container.css({
|
|
379
379
|
overflow: 'hidden',
|
|
@@ -382,20 +382,23 @@
|
|
|
382
382
|
let fragment = document.createDocumentFragment();
|
|
383
383
|
var row1 = $('<div/>',{style:"display:flex;"}).appendTo(fragment);
|
|
384
384
|
$('<input/>', {
|
|
385
|
-
class:"node-input-header-property-name",
|
|
386
|
-
type:"text",
|
|
385
|
+
class:"node-input-header-property-name",
|
|
386
|
+
type:"text",
|
|
387
387
|
placeholder: 'SIP Header'
|
|
388
388
|
})
|
|
389
389
|
.appendTo(row1);
|
|
390
390
|
$('<input/>', {
|
|
391
|
-
class:"node-input-value-property-name",
|
|
392
|
-
type:"text",
|
|
391
|
+
class:"node-input-value-property-name",
|
|
392
|
+
type:"text",
|
|
393
393
|
placeholder: 'value'
|
|
394
394
|
})
|
|
395
|
-
.appendTo(row1)
|
|
395
|
+
.appendTo(row1)
|
|
396
|
+
.typedInput({types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType]});
|
|
396
397
|
|
|
397
398
|
row1.find('.node-input-header-property-name').val(header.h);
|
|
398
|
-
row1.find('.node-input-value-property-name')
|
|
399
|
+
var valueField = row1.find('.node-input-value-property-name');
|
|
400
|
+
valueField.typedInput('type', header.vType || 'str');
|
|
401
|
+
valueField.typedInput('value', header.v);
|
|
399
402
|
|
|
400
403
|
container[0].appendChild(fragment);
|
|
401
404
|
},
|
|
@@ -406,6 +409,7 @@
|
|
|
406
409
|
var header = {
|
|
407
410
|
h: '',
|
|
408
411
|
v: '',
|
|
412
|
+
vType: 'str'
|
|
409
413
|
};
|
|
410
414
|
this.headers = [header];
|
|
411
415
|
}
|
|
@@ -457,11 +461,13 @@
|
|
|
457
461
|
var header = $(this);
|
|
458
462
|
console.log(`header: ${JSON.stringify(header)}`);
|
|
459
463
|
var h = header.find(".node-input-header-property-name").val();
|
|
460
|
-
var
|
|
461
|
-
|
|
464
|
+
var vField = header.find(".node-input-value-property-name");
|
|
465
|
+
var v = vField.typedInput('value');
|
|
466
|
+
var vType = vField.typedInput('type');
|
|
467
|
+
console.log(`added ${h}: ${v} (type: ${vType})`);
|
|
462
468
|
var obj = {};
|
|
463
469
|
obj[h] = v;
|
|
464
|
-
headers.push({h, v});
|
|
470
|
+
headers.push({h, v, vType});
|
|
465
471
|
});
|
|
466
472
|
node.headers = headers;
|
|
467
473
|
console.log(`saved headers ${JSON.stringify(node.headers)}`);
|
package/src/nodes/dial.js
CHANGED
|
@@ -74,9 +74,12 @@ module.exports = function(RED) {
|
|
|
74
74
|
|
|
75
75
|
// headers
|
|
76
76
|
const headers = {};
|
|
77
|
-
config.headers
|
|
78
|
-
if (h.h.length && h.v.length)
|
|
79
|
-
|
|
77
|
+
for (const h of config.headers) {
|
|
78
|
+
if (h.h.length && h.v.length) {
|
|
79
|
+
const resolvedValue = await new_resolve(RED, h.v, h.vType || 'str', node, msg);
|
|
80
|
+
if (resolvedValue) headers[h.h] = resolvedValue;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
80
83
|
Object.assign(data, {headers});
|
|
81
84
|
|
|
82
85
|
// nested listen
|
package/src/nodes/get_alerts.js
CHANGED
|
@@ -6,8 +6,19 @@ module.exports = function(RED) {
|
|
|
6
6
|
RED.nodes.createNode(this, config);
|
|
7
7
|
var node = this;
|
|
8
8
|
const server = RED.nodes.getNode(config.server);
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
node.on('input', async (msg, send, done) => {
|
|
11
|
+
const url = await new_resolve(RED, server.url, server.urlType, node, msg);
|
|
12
|
+
const accountSid = await new_resolve(RED, server.credentials.accountSid, server.accountSidType, node, msg);
|
|
13
|
+
const apiToken = await new_resolve(RED, server.credentials.apiToken, server.apiTokenType, node, msg);
|
|
14
|
+
|
|
15
|
+
if (!url || !accountSid || !apiToken) {
|
|
16
|
+
node.error(`invalid / missing credentials ${JSON.stringify(server.credentials)}`);
|
|
17
|
+
send(msg);
|
|
18
|
+
if (done) done();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
11
22
|
const data = {
|
|
12
23
|
page: await new_resolve(RED, config.page, config.pageType, node, msg),
|
|
13
24
|
count: await new_resolve(RED, config.count, config.countType, node, msg),
|
|
@@ -16,7 +27,7 @@ module.exports = function(RED) {
|
|
|
16
27
|
Object.keys(data).forEach((k) => data[k] == null || data[k] == '' && delete data[k]);
|
|
17
28
|
const params = new URLSearchParams(data).toString()
|
|
18
29
|
try {
|
|
19
|
-
const response = await fetch(`${
|
|
30
|
+
const response = await fetch(`${url}/v1/Accounts/${accountSid}/Alerts?${params}`, {
|
|
20
31
|
method: 'GET',
|
|
21
32
|
headers: {
|
|
22
33
|
'Authorization': `Bearer ${apiToken}`
|
package/src/nodes/get_call.js
CHANGED
|
@@ -6,8 +6,19 @@ module.exports = function (RED) {
|
|
|
6
6
|
RED.nodes.createNode(this, config);
|
|
7
7
|
var node = this;
|
|
8
8
|
const server = RED.nodes.getNode(config.server);
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
node.on("input", async (msg, send, done) => {
|
|
11
|
+
const url = await new_resolve(RED, server.url, server.urlType, node, msg);
|
|
12
|
+
const accountSid = await new_resolve(RED, server.credentials.accountSid, server.accountSidType, node, msg);
|
|
13
|
+
const apiToken = await new_resolve(RED, server.credentials.apiToken, server.apiTokenType, node, msg);
|
|
14
|
+
|
|
15
|
+
if (!url || !accountSid || !apiToken) {
|
|
16
|
+
node.error(`invalid / missing credentials ${JSON.stringify(server.credentials)}`);
|
|
17
|
+
send(msg);
|
|
18
|
+
if (done) done();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
11
22
|
const callSid = await new_resolve(RED, config.callSid, config.callSidType, node, msg);
|
|
12
23
|
if (!callSid) {
|
|
13
24
|
if (done) done(new Error('CallSid empty'));
|
|
@@ -15,7 +26,7 @@ module.exports = function (RED) {
|
|
|
15
26
|
return;
|
|
16
27
|
}
|
|
17
28
|
try {
|
|
18
|
-
const response = await fetch(`${
|
|
29
|
+
const response = await fetch(`${url}/v1/Accounts/${accountSid}/Calls/${callSid}`, {
|
|
19
30
|
method: 'GET',
|
|
20
31
|
headers: {
|
|
21
32
|
'Authorization': `Bearer ${apiToken}`
|
package/src/nodes/get_calls.js
CHANGED
|
@@ -6,8 +6,18 @@ module.exports = function (RED) {
|
|
|
6
6
|
RED.nodes.createNode(this, config);
|
|
7
7
|
var node = this;
|
|
8
8
|
const server = RED.nodes.getNode(config.server);
|
|
9
|
-
const { accountSid, apiToken } = server.credentials;
|
|
10
9
|
node.on("input", async (msg, send, done) => {
|
|
10
|
+
const url = await new_resolve(RED, server.url, server.urlType, node, msg);
|
|
11
|
+
const accountSid = await new_resolve(RED, server.credentials.accountSid, server.accountSidType, node, msg);
|
|
12
|
+
const apiToken = await new_resolve(RED, server.credentials.apiToken, server.apiTokenType, node, msg);
|
|
13
|
+
|
|
14
|
+
if (!url || !accountSid || !apiToken) {
|
|
15
|
+
node.error(`invalid / missing credentials ${JSON.stringify(server.credentials)}`);
|
|
16
|
+
send(msg);
|
|
17
|
+
if (done) done();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
const data = {
|
|
12
22
|
direction: await new_resolve(RED, config.direction, config.directionType, node, msg),
|
|
13
23
|
from: await new_resolve(RED, config.from, config.fromType, node, msg),
|
|
@@ -19,7 +29,7 @@ module.exports = function (RED) {
|
|
|
19
29
|
);
|
|
20
30
|
const params = new URLSearchParams(data).toString();
|
|
21
31
|
try {
|
|
22
|
-
const response = await fetch(`${
|
|
32
|
+
const response = await fetch(`${url}/v1/Accounts/${accountSid}/Calls${ params ? '?' + params : ''}`, {
|
|
23
33
|
method: 'GET',
|
|
24
34
|
headers: {
|
|
25
35
|
'Authorization': `Bearer ${apiToken}`
|
|
@@ -6,8 +6,18 @@ module.exports = function(RED) {
|
|
|
6
6
|
RED.nodes.createNode(this, config);
|
|
7
7
|
var node = this;
|
|
8
8
|
const server = RED.nodes.getNode(config.server);
|
|
9
|
-
const {accountSid, apiToken} = server.credentials;
|
|
10
9
|
node.on('input', async (msg, send, done) => {
|
|
10
|
+
const url = await new_resolve(RED, server.url, server.urlType, node, msg);
|
|
11
|
+
const accountSid = await new_resolve(RED, server.credentials.accountSid, server.accountSidType, node, msg);
|
|
12
|
+
const apiToken = await new_resolve(RED, server.credentials.apiToken, server.apiTokenType, node, msg);
|
|
13
|
+
|
|
14
|
+
if (!url || !accountSid || !apiToken) {
|
|
15
|
+
node.error(`invalid / missing credentials ${JSON.stringify(server.credentials)}`);
|
|
16
|
+
send(msg);
|
|
17
|
+
if (done) done();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
const data = {
|
|
12
22
|
direction: await new_resolve(RED, config.direction, config.directionType, node, msg),
|
|
13
23
|
trunk: await new_resolve(RED, config.trunk, config.trunkType, node, msg),
|
|
@@ -18,7 +28,7 @@ module.exports = function(RED) {
|
|
|
18
28
|
Object.keys(data).forEach((k) => data[k] == null || data[k] == '' && delete data[k]);
|
|
19
29
|
const params = new URLSearchParams(data).toString();
|
|
20
30
|
try {
|
|
21
|
-
const response = await fetch(`${
|
|
31
|
+
const response = await fetch(`${url}/v1/Accounts/${accountSid}/RecentCalls?${params}`, {
|
|
22
32
|
method: 'GET',
|
|
23
33
|
headers: {
|
|
24
34
|
'Authorization': `Bearer ${apiToken}`
|
package/src/nodes/lcc.js
CHANGED
|
@@ -11,9 +11,11 @@ function lcc(config) {
|
|
|
11
11
|
node.on('input', async (msg, send, done) => {
|
|
12
12
|
send = send || function() { node.send.apply(node, arguments);};
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
const
|
|
14
|
+
const url = await new_resolve(RED, server.url, server.urlType, node, msg);
|
|
15
|
+
const accountSid = await new_resolve(RED, server.credentials.accountSid, server.accountSidType, node, msg);
|
|
16
|
+
const apiToken = await new_resolve(RED, server.credentials.apiToken, server.apiTokenType, node, msg);
|
|
16
17
|
const callSid = await new_resolve(RED, config.callSid, config.callSidType, node, msg);
|
|
18
|
+
|
|
17
19
|
if (!url || !accountSid || !apiToken || !callSid) {
|
|
18
20
|
node.log(`invalid / missing credentials or callSid, skipping LCC node: ${JSON.stringify(server.credentials)}`);
|
|
19
21
|
send(msg);
|
package/src/nodes/s3-upload.html
CHANGED
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
</script>
|
|
17
17
|
|
|
18
18
|
<script type="text/html" data-template-name="audio in">
|
|
19
|
+
<div class="form-row ui-state-error">
|
|
20
|
+
<strong>Attention:</strong>
|
|
21
|
+
This node has been deprecated. <a href="https://github.com/jambonz/node-red-contrib-jambonz/issues/53" target="_blank">See issue #53<i class="fa fa-external-link external-link"></i></a> for more details.
|
|
22
|
+
</div>
|
|
19
23
|
<div class="form-row">
|
|
20
24
|
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
|
21
25
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
@@ -35,6 +39,10 @@
|
|
|
35
39
|
</script>
|
|
36
40
|
|
|
37
41
|
<script type="text/html" data-help-name="audio in">
|
|
42
|
+
<div class="ui-state-error">
|
|
43
|
+
<strong>Attention:</strong>
|
|
44
|
+
This node has been deprecated.
|
|
45
|
+
</div>
|
|
38
46
|
<p>Receives audio from jambonz and uploads to S3</p>
|
|
39
47
|
<h3>Outputs</h3>
|
|
40
48
|
<dl class="message-properties">
|
package/src/nodes/s3-upload.js
CHANGED
|
@@ -24,6 +24,8 @@ module.exports = function(RED) {
|
|
|
24
24
|
RED.nodes.createNode(this, n);
|
|
25
25
|
var node = this;
|
|
26
26
|
|
|
27
|
+
node.warn("This node has been deprecated. See https://github.com/jambonz/node-red-contrib-jambonz/issues/53");
|
|
28
|
+
|
|
27
29
|
// Get AWS Creds
|
|
28
30
|
const awsCreds = RED.nodes.getNode(n.aws);
|
|
29
31
|
if (awsCreds && awsCreds.credentials) {
|
|
@@ -55,8 +55,9 @@ module.exports = function(RED) {
|
|
|
55
55
|
RED.httpAdmin.get('/_jambonz/applications/:serverId', (req, res) => {
|
|
56
56
|
const conn = RED.nodes.getNode(req.params.serverId);
|
|
57
57
|
if (conn && conn.credentials && conn.credentials.apiToken) {
|
|
58
|
-
const
|
|
59
|
-
const
|
|
58
|
+
const url = RED.util.evaluateNodeProperty( conn.url, conn.urlType, conn, {});
|
|
59
|
+
const apiToken = RED.util.evaluateNodeProperty(conn.credentials.apiToken, conn.apiTokenType, conn, {});
|
|
60
|
+
|
|
60
61
|
fetch(`${url}/v1/Applications`, {
|
|
61
62
|
method: 'GET',
|
|
62
63
|
headers: {
|