@jambonz/node-red-contrib-jambonz 2.4.18 → 2.4.20

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.
@@ -62,6 +62,30 @@ module.exports = function(RED) {
62
62
  config.record_siprecServerURL != '' ? obj.record.siprecServerURL = new_resolve(RED, config.record_siprecServerURL, config.record_siprecServerURLType, node, msg) : null
63
63
  config.record_recordingID != '' ? obj.record.recordingID = new_resolve(RED, config.record_recordingID, config.record_recordingIDType, node, msg) : null
64
64
  }
65
+
66
+ if (config.listenRequest) {
67
+ obj.listen = {}
68
+ const authUser = new_resolve(RED, config.listenAuthUser, config.listenAuthUserType, node, msg);
69
+ const authPass = new_resolve(RED, config.listenAuthPass, config.listenAuthPassType, node, msg);
70
+ if (authUser && authPass) {
71
+ obj.listen.wsAuth = {
72
+ username: authUser,
73
+ password: authPass
74
+ };
75
+ }
76
+ obj.listen.enable = config.listenEnabled;
77
+ obj.listen.sampleRate = +config.listenSampleRate;
78
+ obj.listen.mixType = config.listenMixType;
79
+ config.url != '' ? obj.listen.url = new_resolve(RED, config.listenUrl, config.listenUrlType, node, msg) : null;
80
+ config.listenMetadata != '' ? obj.listen.metadata = new_resolve(RED, config.listenMetadata, config.listenMetadataType, node, msg) : null;
81
+ if (!Object.keys(obj.listen.metadata).length) {
82
+ delete obj.listen.metadata;
83
+ }
84
+ }
85
+
86
+ if (config.sipRequest) {
87
+ config.sipRequestWithinDialogHook != '' ? obj.sipRequestWithinDialogHook = new_resolve(RED, config.sipRequestWithinDialogHook, config.sipRequestWithinDialogHookType, node, msg) : null;
88
+ }
65
89
  appendVerb(msg, obj);
66
90
  node.send(msg);
67
91
  });
@@ -17,6 +17,8 @@
17
17
  fromType: {value: ''},
18
18
  to: {value: '', required: true},
19
19
  toType: {value: ''},
20
+ trunk: {value: ''},
21
+ trunkType: {value: 'str'},
20
22
  dest: {value: 'phone', required: true},
21
23
  timeout: {validate: RED.validators.regex(/^\d*$/) },
22
24
  tag: {required: true},
@@ -66,9 +68,18 @@
66
68
  var destElem = $('#node-input-dest');
67
69
  var serverElem = $('#node-input-server');
68
70
  var applicationElem = $('#node-input-application');
71
+ var trunkDiv = $('#trunk');
69
72
  prepareTtsControls(node);
70
73
  prepareSttControls(node);
71
74
 
75
+ destElem.change(onDestChanged);
76
+
77
+ var onDestChanged = function () {
78
+ var selectedDest = destElem.find(':selected').val();
79
+ if ('phone' === selectedDest) trunkDiv.show();
80
+ else trunkDiv.hide();
81
+ }
82
+
72
83
  $('#node-input-from').typedInput({
73
84
  default: $('#node-input-fromType').val(),
74
85
  types: ['str','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
@@ -98,7 +109,11 @@
98
109
  types: ['json', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
99
110
  typeField: $('#node-input-tagType')
100
111
  });
101
-
112
+ $('#node-input-trunk').typedInput({
113
+ default: $('#node-input-trunkType').val(),
114
+ types: ['num', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
115
+ typeField: $('#node-input-trunkType')
116
+ });
102
117
  var populateApplications = function() {
103
118
  var serverId = $('#node-input-server option:selected').val();
104
119
  $.ajax({
@@ -224,6 +239,11 @@
224
239
  <input type="text" id="node-input-to" placeholder="called party info">
225
240
  <input type="hidden" id="node-input-toType">
226
241
  </div>
242
+ <div class="form-row" id ="trunk">
243
+ <label for="node-input-trunk">Trunk</label>
244
+ <input type="text" id="node-input-trunk" placeholder="Specify the name of the trunk used for this outbound call">
245
+ <input type="hidden" id="node-input-trunkType">
246
+ </div>
227
247
  <div class="form-row">
228
248
  <label for="node-input-dest">Call type</label>
229
249
  <select id="node-input-dest">
@@ -71,6 +71,10 @@ module.exports = function(RED) {
71
71
 
72
72
  switch (config.dest) {
73
73
  case 'phone':
74
+ if (config.trunk) {
75
+ var trunk = new_resolve(RED, config.trunk, config.trunkType, node, msg);
76
+ opts.to.trunk = trunk;
77
+ }
74
78
  opts.to.number = to;
75
79
  break;
76
80
  case 'user':
@@ -89,26 +93,20 @@ module.exports = function(RED) {
89
93
  return;
90
94
  }
91
95
  try {
92
- node.log(`sending create call ${JSON.stringify(opts)}`);
93
- const res = await doCreateCall(url, accountSid, apiToken, opts);
96
+ const response = await doCreateCall(node, url, accountSid, apiToken, opts);
94
97
  msg.statusCode = 201;
95
- msg.callSid = res.sid;
96
- msg.callId = res.callId;
98
+ msg.callSid = response.sid;
99
+ msg.callId = response.callId;
97
100
  } catch (err) {
98
101
  if (err.statusCode) {
99
- node.log(JSON.stringify(err));
100
- try {
101
- const responseBody = await err.json();
102
- node.error(`create-call failed with ${err.statusCode}. Response ${JSON.stringify(responseBody)}`);
103
- } catch (e) {
104
- node.error(`create-call failed with ${err.statusCode}`);
105
- }
102
+ node.error(`create-call failed with ${err.statusCode}`);
106
103
  msg.statusCode = err.statusCode;
107
- }
108
- else {
109
- node.error(`Error sending create call ${JSON.stringify(err)}`);
110
- if (done) done(err);
111
- else node.error(err, msg);
104
+ msg.errorMessage = err.statusText;
105
+ } else {
106
+ const errorMessage = `Error sending create call ${err.message}`;
107
+ if (done) done(errorMessage);
108
+ else node.error(errorMessage, msg);
109
+ msg.errorMessage = errorMessage;
112
110
  send(msg);
113
111
  return;
114
112
  }
@@ -31,26 +31,20 @@ function create_sms(config) {
31
31
  provider
32
32
  };
33
33
  try {
34
- node.log(`sending create message ${JSON.stringify(opts)}`);
35
- const res = await doCreateMessage(url, accountSid, apiToken, opts);
34
+ const response = await doCreateMessage(node, url, accountSid, apiToken, opts);
36
35
  msg.statusCode = 201;
37
- msg.messageSid = res.sid;
38
- msg.providerResponse = res.providerResponse;
36
+ msg.messageSid = response.sid;
37
+ msg.providerResponse = response.providerResponse;
39
38
  } catch (err) {
40
39
  if (err.statusCode) {
41
- node.log(JSON.stringify(err));
42
- try {
43
- const responseBody = await err.json();
44
- node.error(`create_sms failed with ${err.statusCode}. Response ${JSON.stringify(responseBody)}`);
45
- } catch (e) {
46
- node.error(`create_sms failed with ${err.statusCode}`);
47
- }
40
+ node.error(`create_sms failed with ${err.statusCode}`);
48
41
  msg.statusCode = err.statusCode;
49
- }
50
- else {
51
- node.log(`Error sending create message ${JSON.stringify(err)}`);
52
- if (done) done(err);
53
- else node.error(err, msg);
42
+ msg.errorMessage = err.statusText;
43
+ } else {
44
+ const errorMessage = `Error sending create message ${err.message}`;
45
+ if (done) done(errorMessage);
46
+ else node.error(errorMessage, msg);
47
+ msg.errorMessage = errorMessage;
54
48
  send(msg);
55
49
  return;
56
50
  }
@@ -16,6 +16,7 @@
16
16
  actionhook: {value: ''},
17
17
  actionhookType: {value: 'str'},
18
18
  answeronbridge: {value: false},
19
+ anchormedia: {value: false},
19
20
  callerid: {value: ''},
20
21
  calleridType: {value: ''},
21
22
  callername: {value: ''},
@@ -30,6 +31,8 @@
30
31
  referhookType: {value: 'str'},
31
32
  dtmfhook: {value: ''},
32
33
  dtmfhookType: {value: 'str'},
34
+ onholdhook: {value: ''},
35
+ onholdhookType: {value: 'str'},
33
36
  timelimit: {validate:RED.validators.regex(/^\d*$/) },
34
37
  timeout: {validate:RED.validators.regex(/^\d*$/) },
35
38
  listenurl: {value: ''},
@@ -106,6 +109,10 @@
106
109
  types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
107
110
  typeField: $('#node-input-dtmfhookType')
108
111
  });
112
+ $('#node-input-onholdhook').typedInput({
113
+ types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
114
+ typeField: $('#node-input-onholdhookType')
115
+ });
109
116
  $('#node-input-transcribeurl').typedInput({
110
117
  types: ['str', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
111
118
  typeField: $('#node-input-transcribeurlType')
@@ -408,6 +415,10 @@
408
415
  <label for="node-input-answeronbridge">Answer on bridge</label>
409
416
  <input type="checkbox" id="node-input-answeronbridge">
410
417
  </div>
418
+ <div class="form-row">
419
+ <label for="node-input-anchormedia">Anchor media</label>
420
+ <input type="checkbox" id="node-input-anchormedia">
421
+ </div>
411
422
  <div class="form-row">
412
423
  <label for="node-input-callerid">Caller ID</label>
413
424
  <input type="text" id="node-input-callerid" placeholder="caller id to place on outbound call">
@@ -443,6 +454,11 @@
443
454
  <input type="text" id="node-input-dtmfhook" placeholder="webhook to call when dtmf is captured">
444
455
  <input type="hidden" id="node-input-dtmfhookType">
445
456
  </div>
457
+ <div class="form-row">
458
+ <label for="node-input-onholdhook">Onhold hook</label>
459
+ <input type="text" id="node-input-onholdhook" placeholder="webhook to call when call is on hold">
460
+ <input type="hidden" id="node-input-onholdhookType">
461
+ </div>
446
462
  <div class="form-row">
447
463
  <label for="node-input-timelimit">Time limit</label>
448
464
  <input type="text" id="node-input-timelimit" placeholder="max duration of call in secs">
package/src/nodes/dial.js CHANGED
@@ -54,6 +54,14 @@ module.exports = function(RED) {
54
54
  dtmfHook: new_resolve(RED, config.dtmfhook, config.dtmfhookType, node, msg),
55
55
  };
56
56
 
57
+ if (config.hasOwnProperty('anchormedia')) {
58
+ data.anchorMedia = config.anchormedia;
59
+ }
60
+
61
+ if (config.onholdhook) {
62
+ data.onHoldHook = new_resolve(RED, config.onholdhook, config.onholdhookType, node, msg);
63
+ }
64
+
57
65
  // headers
58
66
  var headers = {};
59
67
  config.headers.forEach(function(h) {
@@ -1,4 +1,4 @@
1
- const bent = require('bent');
1
+ const {fetch} = require('undici');
2
2
  var {new_resolve} = require('./libs');
3
3
 
4
4
  module.exports = function(RED) {
@@ -14,25 +14,36 @@ module.exports = function(RED) {
14
14
  days: new_resolve(RED, config.days, config.daysType, node, msg),
15
15
  }
16
16
  Object.keys(data).forEach((k) => data[k] == null || data[k] == '' && delete data[k]);
17
- const params = new URLSearchParams(data).toString()
18
- const req = bent(`${server.url}/v1/Accounts/${accountSid}/Alerts?${params}`, 'GET', 'json', {
19
- 'Authorization': `Bearer ${apiToken}`
20
- });
17
+ const params = new URLSearchParams(data).toString()
21
18
  try {
22
- const res = await req();
23
- msg.payload = res.data
24
- msg.total = res.total
25
- msg.page_size = res.page_size
26
- msg.page = res.page
19
+ const response = await fetch(`${server.url}/v1/Accounts/${accountSid}/Alerts?${params}`, {
20
+ method: 'GET',
21
+ headers: {
22
+ 'Authorization': `Bearer ${apiToken}`
23
+ }
24
+ });
25
+ if (!response.ok) {
26
+ const error = new Error('Bad response');
27
+ error.statusCode = response.status;
28
+ error.statusText = response.statusText;
29
+ throw error;
30
+ }
31
+ const res = await response.json();
32
+ msg.payload = res.data;
33
+ msg.total = res.total;
34
+ msg.page_size = res.page_size;
35
+ msg.page = res.page;
27
36
  } catch (err) {
28
37
  if (err.statusCode) {
29
38
  node.error(`GetAlerts failed with ${err.statusCode}`);
30
39
  msg.statusCode = err.statusCode;
40
+ msg.errorMessage = err.statusText;
31
41
  }
32
42
  else {
33
- node.error(`Error getting alerts ${JSON.stringify(err)}`);
34
- if (done) done(err);
35
- else node.error(err, msg);
43
+ const errorMessage = `Error getting alerts ${err.message}`;
44
+ if (done) done(errorMessage);
45
+ else node.error(errorMessage, msg);
46
+ msg.errorMessage = errorMessage;
36
47
  send(msg);
37
48
  return;
38
49
  }
@@ -1,4 +1,4 @@
1
- const bent = require("bent");
1
+ const {fetch} = require('undici');
2
2
  var { new_resolve} = require("./libs");
3
3
 
4
4
  module.exports = function (RED) {
@@ -14,25 +14,31 @@ module.exports = function (RED) {
14
14
  else node.error(new Error('CallSid empty'), msg);
15
15
  return;
16
16
  }
17
- const req = bent(
18
- `${server.url}/v1/Accounts/${accountSid}/Calls/${callSid}`,
19
- 'GET',
20
- 'json',
21
- {
22
- Authorization: `Bearer ${apiToken}`,
23
- }
24
- );
25
17
  try {
26
- const res = await req();
18
+ const response = await fetch(`${server.url}/v1/Accounts/${accountSid}/Calls/${callSid}`, {
19
+ method: 'GET',
20
+ headers: {
21
+ 'Authorization': `Bearer ${apiToken}`
22
+ }
23
+ });
24
+ if (!response.ok) {
25
+ const error = new Error('Bad response');
26
+ error.statusCode = response.status;
27
+ error.statusText = response.statusText;
28
+ throw error;
29
+ }
30
+ const res = await response.json();
27
31
  msg.payload = res;
28
32
  } catch (err) {
29
33
  if (err.statusCode) {
30
34
  node.error(`GetCall failed with ${err.statusCode}`);
31
35
  msg.statusCode = err.statusCode;
36
+ msg.errorMessage = err.statusText;
32
37
  } else {
33
- node.error(`Error getting call info ${JSON.stringify(err)}`);
34
- if (done) done(err);
35
- else node.error(err, msg);
38
+ const errorMessage = `Error getting call info ${err.message}`;
39
+ if (done) done(errorMessage);
40
+ else node.error(errorMessage, msg);
41
+ msg.errorMessage = errorMessage;
36
42
  send(msg);
37
43
  return;
38
44
  }
@@ -1,4 +1,4 @@
1
- const bent = require("bent");
1
+ const {fetch} = require('undici');
2
2
  var { new_resolve} = require("./libs");
3
3
 
4
4
  module.exports = function (RED) {
@@ -18,25 +18,31 @@ module.exports = function (RED) {
18
18
  (k) => data[k] == null || (data[k] == '' && delete data[k])
19
19
  );
20
20
  const params = new URLSearchParams(data).toString();
21
- const req = bent(
22
- `${server.url}/v1/Accounts/${accountSid}/Calls${ params ? '?' + params : ''}`,
23
- 'GET',
24
- 'json',
25
- {
26
- Authorization: `Bearer ${apiToken}`,
27
- }
28
- );
29
21
  try {
30
- const res = await req();
22
+ const response = await fetch(`${server.url}/v1/Accounts/${accountSid}/Calls${ params ? '?' + params : ''}`, {
23
+ method: 'GET',
24
+ headers: {
25
+ 'Authorization': `Bearer ${apiToken}`
26
+ }
27
+ });
28
+ if (!response.ok) {
29
+ const error = new Error('Bad response');
30
+ error.statusCode = response.status;
31
+ error.statusText = response.statusText;
32
+ throw error;
33
+ }
34
+ const res = await response.json();
31
35
  msg.payload = res;
32
36
  } catch (err) {
33
37
  if (err.statusCode) {
34
38
  node.error(`GetCalls failed with ${err.statusCode}`);
35
39
  msg.statusCode = err.statusCode;
40
+ msg.errorMessage = err.statusText;
36
41
  } else {
37
- node.error(`Error getting calls ${JSON.stringify(err)}`);
38
- if (done) done(err);
39
- else node.error(err, msg);
42
+ const errorMessage = `Error getting calls ${err.message}`;
43
+ if (done) done(errorMessage);
44
+ else node.error(errorMessage, msg);
45
+ msg.errorMessage = errorMessage;
40
46
  send(msg);
41
47
  return;
42
48
  }
@@ -1,4 +1,4 @@
1
- const bent = require('bent');
1
+ const {fetch} = require('undici');
2
2
  var {new_resolve} = require('./libs');
3
3
 
4
4
  module.exports = function(RED) {
@@ -16,12 +16,21 @@ module.exports = function(RED) {
16
16
  days: new_resolve(RED, config.days, config.daysType, node, msg),
17
17
  }
18
18
  Object.keys(data).forEach((k) => data[k] == null || data[k] == '' && delete data[k]);
19
- const params = new URLSearchParams(data).toString();
20
- const req = bent(`${server.url}/v1/Accounts/${accountSid}/RecentCalls?${params}`, 'GET', 'json', {
21
- 'Authorization': `Bearer ${apiToken}`
22
- });
19
+ const params = new URLSearchParams(data).toString();
23
20
  try {
24
- const res = await req();
21
+ const response = await fetch(`${server.url}/v1/Accounts/${accountSid}/RecentCalls?${params}`, {
22
+ method: 'GET',
23
+ headers: {
24
+ 'Authorization': `Bearer ${apiToken}`
25
+ }
26
+ });
27
+ if (!response.ok) {
28
+ const error = new Error('Bad response');
29
+ error.statusCode = response.status;
30
+ error.statusText = response.statusText;
31
+ throw error;
32
+ }
33
+ const res = await response.json();
25
34
  msg.payload = res.data;
26
35
  msg.total = res.total;
27
36
  msg.page_size = res.page_size;
@@ -30,11 +39,13 @@ module.exports = function(RED) {
30
39
  if (err.statusCode) {
31
40
  node.error(`GetRecentCalls failed with ${err.statusCode}`);
32
41
  msg.statusCode = err.statusCode;
42
+ msg.errorMessage = err.statusText;
33
43
  }
34
44
  else {
35
- node.error(`Error getting recent calls ${JSON.stringify(err)}`);
36
- if (done) done(err);
37
- else node.error(err, msg);
45
+ const errorMessage = `Error getting recent calls ${err.message}`;
46
+ if (done) done(errorMessage);
47
+ else node.error(errorMessage, msg);
48
+ msg.errorMessage = errorMessage;
38
49
  send(msg);
39
50
  return;
40
51
  }
@@ -55,6 +55,15 @@
55
55
  siprecHeaders: {value: []},
56
56
  recordingID: {value: ''},
57
57
  recordingIDType: {value: 'str'},
58
+ dtmfDigit: {value: '', validate: function(v) {
59
+ const action = $('#node-input-action').val();
60
+ return action !== 'send_dtmf' || v.length > 0;
61
+ }},
62
+ dtmfDigitType: {value: 'str'},
63
+ dtmfDuration: {value: ''},
64
+ dtmfDurationType: {value: 'str'},
65
+ tag: {value: ''},
66
+ tagType: {value: 'str'},
58
67
  },
59
68
  inputs:1,
60
69
  outputs:1,
@@ -74,6 +83,8 @@
74
83
  var waitHookDiv = $('#wait-options');
75
84
  var recordDiv = $('#record-options');
76
85
  var sipRequestDiv = $('#sip-request-options');
86
+ var dtmfDiv = $('#dtmf-options');
87
+ var tagDiv = $('#tag-options');
77
88
 
78
89
  $('#node-input-callSid').typedInput({
79
90
  default: $('#node-input-callSidType').val(),
@@ -120,6 +131,21 @@
120
131
  types: ['str','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
121
132
  typeField: $('#node-input-recordingIDType')
122
133
  });
134
+ $('#node-input-dtmfDigit').typedInput({
135
+ default: $('#node-input-dtmfDigitType').val(),
136
+ types: ['str','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
137
+ typeField: $('#node-input-dtmfDigitType')
138
+ });
139
+ $('#node-input-dtmfDuration').typedInput({
140
+ default: $('#node-input-dtmfDurationType').val(),
141
+ types: ['str','msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
142
+ typeField: $('#node-input-dtmfDurationType')
143
+ });
144
+ $('#node-input-tag').typedInput({
145
+ default: $('#node-input-tagType').val(),
146
+ types: ['json', 'msg', 'flow', 'global', 'jsonata', 'env', mustacheType],
147
+ typeField: $('#node-input-tagType')
148
+ });
123
149
 
124
150
  var onActionChanged = function () {
125
151
  var selectedAction = actionElem.find(':selected').val();
@@ -133,6 +159,10 @@
133
159
  else recordDiv.hide();
134
160
  if ('sip_request' === selectedAction) sipRequestDiv.show();
135
161
  else sipRequestDiv.hide();
162
+ if ('send_dtmf' === selectedAction) dtmfDiv.show();
163
+ else dtmfDiv.hide();
164
+ if ('tag' === selectedAction) tagDiv.show();
165
+ else tagDiv.hide();
136
166
  }
137
167
 
138
168
  var onVendorChanged = function() {
@@ -299,6 +329,8 @@
299
329
  <option value="stop_call_recording">stop call recording</option>
300
330
  <option value="pause_call_recording">pause call recording</option>
301
331
  <option value="resume_call_recording">resume call recording</option>
332
+ <option value="send_dtmf">send RFC 2833 DTMF</option>
333
+ <option value="tag">tag call</option>
302
334
  </select>
303
335
  </div>
304
336
  <div id="say-options">
@@ -394,6 +426,25 @@
394
426
  <input type="hidden" id="node-input-sipRequestHeadersType">
395
427
  </div>
396
428
  </div>
429
+ <div id="dtmf-options">
430
+ <div class="form-row">
431
+ <label for="node-input-dtmfDigit">DTMF digit</label>
432
+ <input type="text" id="node-input-dtmfDigit">
433
+ <input type="hidden" id="node-input-dtmfDigitType">
434
+ </div>
435
+ <div class="form-row">
436
+ <label for="node-input-dtmfDuration">DTMF duration</label>
437
+ <input type="text" id="node-input-dtmfDuration" placeholder="250">
438
+ <input type="hidden" id="node-input-dtmfDurationType">
439
+ </div>
440
+ </div>
441
+ <div id="tag-options">
442
+ <div class="form-row">
443
+ <label for="node-input-tag">Data</label>
444
+ <input type="text" id="node-input-tag" placeholder="data object">
445
+ <input type="hidden" id="node-input-tagType">
446
+ </div>
447
+ </div>
397
448
  </script>
398
449
 
399
450
  <!-- Help Text -->
package/src/nodes/lcc.js CHANGED
@@ -113,6 +113,15 @@ function lcc(config) {
113
113
  case 'resume_call_recording':
114
114
  opts.record = { action: 'resumeCallRecording' };
115
115
  break;
116
+ case 'send_dtmf':
117
+ opts.dtmf = {
118
+ digit: new_resolve(RED, config.dtmfDigit, config.dtmfDigitType, node, msg),
119
+ duration: new_resolve(RED, config.dtmfDuration, config.dtmfDurationType, node, msg) || '250'
120
+ };
121
+ break;
122
+ case 'tag':
123
+ opts.tag = new_resolve(RED, config.tag, config.tagType, node, msg);
124
+ break;
116
125
  default:
117
126
  node.log(`invalid action: ${config.action}`);
118
127
  send(msg);
@@ -124,11 +133,14 @@ function lcc(config) {
124
133
  msg.payload = await doLCC(node, url, accountSid, apiToken, callSid, opts);
125
134
  msg.statusCode = config.action === 'sip_request' ? 200 : 202;
126
135
  } catch (err) {
127
- if (err.statusCode) msg.statusCode = err.statusCode;
128
- else {
129
- node.error(`Error sending LCC ${JSON.stringify(err)}`);
130
- if (done) done(err);
131
- else node.error(err, msg);
136
+ if (err.statusCode) {
137
+ msg.statusCode = err.statusCode;
138
+ msg.errorMessage = err.statusText;
139
+ } else {
140
+ const errorMessage = `Error sending LCC ${err.message}`;
141
+ if (done) done(errorMessage);
142
+ else node.error(errorMessage, msg);
143
+ msg.errorMessage = errorMessage;
132
144
  send(msg);
133
145
  return;
134
146
  }
package/src/nodes/libs.js CHANGED
@@ -1,4 +1,4 @@
1
- const bent = require('bent');
1
+ const {fetch} = require('undici');
2
2
  var mustache = require('mustache');
3
3
  mustache.escape = function(text) {return text;};
4
4
 
@@ -110,27 +110,65 @@ exports.appendVerb = (msg, obj) => {
110
110
  return mustache.render(newString, data);
111
111
  }
112
112
 
113
-
114
- exports.doLCC = (node, baseUrl, accountSid, apiToken, callSid, opts) => {
115
- const post = bent(`${baseUrl}/v1/`, 'POST', 'string', [200, 202], {
116
- 'Authorization': `Bearer ${apiToken}`
113
+ exports.doLCC = async (node, baseUrl, accountSid, apiToken, callSid, opts) => {
114
+ const url = `${baseUrl}/v1/Accounts/${accountSid}/Calls/${callSid}`;
115
+ node.log(`invoking LCC with payload ${JSON.stringify(opts)} at ${url}`);
116
+ const response = await fetch(url, {
117
+ method: 'POST',
118
+ headers: {
119
+ 'Content-Type': 'application/json',
120
+ 'Authorization': `Bearer ${apiToken}`
121
+ },
122
+ body: JSON.stringify(opts)
117
123
  });
118
- const url = `Accounts/${accountSid}/Calls/${callSid}`;
119
- node.log(`invoking LCC with payload ${JSON.stringify(opts)} at ${baseUrl}/v1/${url}`);
120
- return post(url, opts);
124
+ if (!response.ok) {
125
+ const error = new Error('Bad response');
126
+ error.statusCode = response.status;
127
+ error.statusText = response.statusText;
128
+ throw error;
129
+ }
130
+ const contentType = response.headers.get('content-type');
131
+ if (contentType && contentType.indexOf('application/json') !== -1) {
132
+ return response.json();
133
+ }
134
+ return response.text();
121
135
  }
122
136
 
123
- exports.doCreateCall = (baseUrl, accountSid, apiToken, opts) => {
124
- const post = bent(`${baseUrl}/v1/`, 'POST', 'json', 201, {
125
- 'Authorization': `Bearer ${apiToken}`
137
+ exports.doCreateCall = async (node, baseUrl, accountSid, apiToken, opts) => {
138
+ node.log(`invoking create call with payload ${JSON.stringify(opts)}`);
139
+ const response = await fetch(`${baseUrl}/v1/Accounts/${accountSid}/Calls`, {
140
+ method: 'POST',
141
+ headers: {
142
+ 'Content-Type': 'application/json',
143
+ 'Authorization': `Bearer ${apiToken}`
144
+ },
145
+ body: JSON.stringify(opts)
126
146
  });
127
- return post(`Accounts/${accountSid}/Calls`, opts);
147
+ if (!response.ok) {
148
+ const error = new Error('Bad response');
149
+ error.statusCode = response.status;
150
+ error.statusText = response.statusText;
151
+ throw error;
152
+ }
153
+ return response.json();
128
154
  }
129
155
 
130
- exports.doCreateMessage = (baseUrl, accountSid, apiToken, opts) => {
131
- const post = bent(`${baseUrl}/v1/`, 'POST', 'json', 201, {
132
- 'Authorization': `Bearer ${apiToken}`
156
+ exports.doCreateMessage = async (node, baseUrl, accountSid, apiToken, opts) => {
157
+ node.log(`invoking create message with payload ${JSON.stringify(opts)}`);
158
+ const response = await fetch(`${baseUrl}/v1/Accounts/${accountSid}/Messages`, {
159
+ method: 'POST',
160
+ headers: {
161
+ 'Content-Type': 'application/json',
162
+ 'Authorization': `Bearer ${apiToken}`
163
+ },
164
+ body: JSON.stringify(opts)
133
165
  });
134
- return post(`Accounts/${accountSid}/Messages`, opts);
166
+ if (!response.ok) {
167
+ const error = new Error('Bad response');
168
+ error.statusCode = response.status;
169
+ error.statusText = response.statusText;
170
+ throw error;
171
+ }
172
+ return response.json();
135
173
  }
136
174