@subiz/wsclient 4.0.20 → 4.0.22

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/common.js CHANGED
@@ -1,3 +1,41 @@
1
+ function logError(accid, userid, message, line) {
2
+ // 24-01-11 04:07:05 app ERROR api-8948967b6-qtpfx subiz /app/vendor/github.com/subiz/log/error.go:271 {"id":141534190473829746,"class":500,"code":"locked_account,internal","number":"8f2b97e9","fields":{"ip":"\"171.224.84.93\"","no_report":"true","user_agent":"\"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36\""}}
3
+ let ts = displayUTCLogTime(new Date())
4
+ let msg = JSON.stringify({
5
+ id: Date.now(),
6
+ class: 400,
7
+ code: 'frontend_error',
8
+ number: line
9
+ .split('')
10
+ .map((c) => c.charCodeAt(0).toString(16).padStart(2, '0'))
11
+ .join(''),
12
+ fields: {
13
+ message: JSON.stringify(message),
14
+ user_id: userid,
15
+ url: JSON.stringify(location.href),
16
+ },
17
+ })
18
+
19
+ xhrsend('POST', 'https://track.subiz.net/collect', `${ts} app ERROR dashboard ${accid} ${line} ${msg}`)
20
+ }
21
+
22
+ function displayUTCLogTime(date) {
23
+ let yyyy = date.getUTCFullYear().toString()
24
+ let yy = yyyy.substr(2, 2)
25
+ let mm = date.getUTCMonth() + 1
26
+ let dd = date.getUTCDate()
27
+ let hh = date.getUTCHours()
28
+ let MM = date.getUTCMinutes()
29
+ let ss = date.getUTCSeconds()
30
+
31
+ return `${yy}-${pad(mm)}-${pad(dd)} ${pad(hh)}:${pad(MM)}:${pad(ss)}`
32
+ }
33
+
34
+ function pad(v) {
35
+ if (v < 10) return `0${v}`
36
+ return `${v}`
37
+ }
38
+
1
39
  function dofetch(method, url, body, cb) {
2
40
  let headers = {}
3
41
  if (body) headers['content-type'] = 'text/plain'
@@ -78,4 +116,4 @@ function randomString(len, base) {
78
116
  return str
79
117
  }
80
118
 
81
- module.exports = {xhrsend, parseJSON, Pubsub, randomString}
119
+ module.exports = {xhrsend, parseJSON, Pubsub, randomString, logError}
package/demo.html ADDED
@@ -0,0 +1,129 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link
6
+ rel="stylesheet"
7
+ href="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css"
8
+ />
9
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
10
+ <title>Subiz WebPhone Demo</title>
11
+ <style>
12
+ #info {
13
+ background: whitesmoke;
14
+ border-radius: 5px;
15
+ }
16
+ label {
17
+ font-weight: 600;
18
+ margin-right: 10px;
19
+ }
20
+ .info_line {
21
+ display: flex;
22
+ align-items: flex-start;
23
+ }
24
+ </style>
25
+ </head>
26
+ <body class="container pt-3">
27
+ <img
28
+ width="80"
29
+ src="https://storage.googleapis.com/sbz-email/images/d009afe944c1948998544e103ab0fef2_logo.png"
30
+ />
31
+ <h1 class="mt-4">Subiz call demo</h1>
32
+ <button
33
+ class="btn btn-primary"
34
+ style="width: 120px"
35
+ id="btnHangup"
36
+ onClick="inputToken()"
37
+ >
38
+ Nhập token
39
+ </button>
40
+ <div id="tokenStatus" class="mt-2 text-danger">Chưa nhập token</div>
41
+
42
+ <div class="mt-3" style="width: 250px">
43
+ <div>
44
+ <label>Đầu số</label>
45
+ <input
46
+ class="form-control"
47
+ id="fromNumber"
48
+ type="text"
49
+ value="842473021368"
50
+ />
51
+ </div>
52
+ <div class="mt-3">
53
+ <label>Số khách</label>
54
+ <input
55
+ class="form-control"
56
+ placeholder="036..."
57
+ id="toNumber"
58
+ type="text"
59
+ />
60
+ </div>
61
+ <div class="mt-3">
62
+ <button
63
+ class="btn btn-success mr-3"
64
+ style="width: 150px"
65
+ id="btnCall"
66
+ disabled
67
+ onClick="makeCall()"
68
+ >
69
+ Gọi
70
+ </button>
71
+ <button
72
+ class="btn btn-outline-danger"
73
+ style="width: 79px"
74
+ id="btnHangup"
75
+ onClick="hangupCall()"
76
+ >
77
+ Ngắt
78
+ </button>
79
+ </div>
80
+ </div>
81
+ <h3 class="mt-4">Thông tin cuộc gọi</h3>
82
+ <div class="p-2" id="info">
83
+ <div class="info_line">
84
+ <label>Call ID:</label>
85
+ <div id="infoCallId"></div>
86
+ </div>
87
+ <div class="info_line">
88
+ <label>Conversation ID:</label>
89
+ <div id="infoConversationId"></div>
90
+ </div>
91
+ <div class="info_line">
92
+ <label>Ghi âm:</label>
93
+ <a href="#" id="infoRecordedUrl" target="_blank"></a>
94
+ </div>
95
+ <div class="info_line">
96
+ <label>Gọi từ số:</label>
97
+ <div id="infoFromNumber"></div>
98
+ </div>
99
+ <div class="info_line">
100
+ <label>Gọi đến số:</label>
101
+ <div id="infoToNumber"></div>
102
+ </div>
103
+ <div class="info_line">
104
+ <label>Trạng thái:</label>
105
+ <div id="infoStatus"></div>
106
+ </div>
107
+ <div class="info_line">
108
+ <label>Gọi lúc:</label>
109
+ <div id="infoCreated"></div>
110
+ </div>
111
+ <div class="info_line">
112
+ <label>Thời lượng (giây):</label>
113
+ <div id="infoDuration"></div>
114
+ </div>
115
+ <div class="info_line">
116
+ <label>Mã ngắt máy:</label>
117
+ <div id="infoHangupCode"></div>
118
+ </div>
119
+ </div>
120
+ <div class="mt-4">
121
+ <h3>Sự kiện realtime</h3>
122
+ <pre
123
+ style="padding: 10px 15px; border-radius: 5px; background: whitesmoke"
124
+ id="infoLog"
125
+ ></pre>
126
+ </div>
127
+ <script type="module" src="./demo.js"></script>
128
+ </body>
129
+ </html>
package/demo.js ADDED
@@ -0,0 +1,207 @@
1
+ const SubizWebPhone = require('./webphone.js')
2
+
3
+ let webphone
4
+ var mycallid = 'x'
5
+ let accid
6
+ let eventLogs = {}
7
+
8
+ // setTokenStatus renders whether token is valid or invalid
9
+ function setTokenStatus(me) {
10
+ let $tokenStatus = document.getElementById('tokenStatus')
11
+ if (me == 'loading') {
12
+ $tokenStatus.innerHTML = 'Đang kiểm tra'
13
+ $tokenStatus.classList.add('text-muted')
14
+ $tokenStatus.classList.remove('text-danger')
15
+ $tokenStatus.classList.remove('text-success')
16
+ return
17
+ }
18
+
19
+ if (!me || !me.account_id) {
20
+ $tokenStatus.innerHTML = 'Token rỗng hoặc không hợp lệ. Vui lòng nhập lại'
21
+ $tokenStatus.classList.remove('text-danger')
22
+ $tokenStatus.classList.remove('text-success')
23
+ $tokenStatus.classList.add('text-muted')
24
+ return
25
+ }
26
+
27
+ $tokenStatus.innerHTML = 'Token hợp lệ. Xin chào ' + me.fullname
28
+ $tokenStatus.classList.add('text-success')
29
+ $tokenStatus.classList.remove('text-danger')
30
+ $tokenStatus.classList.remove('text-muted')
31
+ }
32
+
33
+ function init() {
34
+ setTokenStatus('loading')
35
+ fetch(`https://api.subiz.com.vn/4.0/me?x-access-token=${access_token}`)
36
+ .then((response) => {
37
+ return response.json()
38
+ })
39
+ .then((me) => {
40
+ setTokenStatus(me)
41
+ if (!me || !me.account_id) return null
42
+ return me
43
+ })
44
+ .then((me) => {
45
+ if (!me) return
46
+ // save token
47
+ try {
48
+ webphone = new SubizWebPhone(access_token)
49
+ accid = access_token.split('_')[0]
50
+ } catch (e) {
51
+ alert('ERROR2: ' + e)
52
+ // setTimeout(main)
53
+ return
54
+ }
55
+
56
+ document.getElementById('btnCall').disabled = false
57
+
58
+ webphone.onEvent(function (ev) {
59
+ let call = ev.data.call_info
60
+ let callid = call.call_id
61
+ eventLogs[callid] = eventLogs[callid] || []
62
+ if (callid != mycallid) return
63
+ eventLogs[callid].push(ev)
64
+
65
+ // render log
66
+ let logM = eventLogs[callid]
67
+ if (logM) {
68
+ let log = logM
69
+ .map((ev) => {
70
+ return `${new Date(ev.created).toLocaleString()} | ${ev.type} | ${JSON.stringify(ev.data.call_info)}`
71
+ })
72
+ .join('\n')
73
+ document.getElementById('infoLog').innerHTML = log
74
+ }
75
+
76
+ if (call.status == 'dialing') {
77
+ document.getElementById('infoRecordedUrl').innerHTML = ''
78
+ document.getElementById('infoRecordedUrl').href = undefined
79
+ document.getElementById('info').style['background-color'] = '#fffed3'
80
+ }
81
+
82
+ if (call.status == 'active') {
83
+ document.getElementById('info').style['background-color'] = '#8cff6b'
84
+ }
85
+
86
+ if (call.status == 'ended' || !call.status) {
87
+ document.getElementById('info').style['background-color'] = '#ffc6c6'
88
+ }
89
+
90
+ if (call.status == 'ended' && ev.id) {
91
+ // from server, not fake
92
+ document.getElementById('infoRecordedUrl').innerHTML = 'Đang tải...'
93
+ document.getElementById('infoRecordedUrl').href = undefined
94
+
95
+ fetchRecordedUrl(callid).then((url) => {
96
+ if (url == 'unavailable') {
97
+ document.getElementById('infoRecordedUrl').innerHTML = 'Không khả dụng'
98
+ document.getElementById('infoRecordedUrl').href = undefined
99
+ return
100
+ }
101
+ document.getElementById('infoRecordedUrl').innerHTML = url
102
+ document.getElementById('infoRecordedUrl').href = url
103
+ })
104
+ }
105
+ })
106
+ })
107
+ }
108
+
109
+ // inputToken asks access token from the agent
110
+ function inputToken() {
111
+ access_token = prompt('Vui lòng nhập access token')
112
+ localStorage.setItem('access_token', access_token)
113
+ if (access_token) init()
114
+ }
115
+
116
+ // fetchRecordedUrl attemps to get the recorded audio url
117
+ // since we dont know when the file is uploaded, we must retry multiple times.
118
+ // this code is only for demo purpose
119
+ function fetchRecordedUrl(callid) {
120
+ // try maximum 4 times
121
+ return new Promise((rs) => {
122
+ fetchConversation(callid).then((convo) => {
123
+ if (convo.call.recorded_audio && convo.call.recorded_audio.url) {
124
+ return rs(convo.call.recorded_audio.url)
125
+ }
126
+ setTimeout(() => {
127
+ fetchConversation(callid).then((convo) => {
128
+ if (convo.call.recorded_audio && convo.call.recorded_audio.url) {
129
+ return rs(convo.call.recorded_audio.url)
130
+ }
131
+
132
+ setTimeout(() => {
133
+ fetchConversation(callid).then((convo) => {
134
+ if (convo.call.recorded_audio && convo.call.recorded_audio.url) {
135
+ return rs(convo.call.recorded_audio.url)
136
+ }
137
+
138
+ setTimeout(() => {
139
+ fetchConversation(callid).then((convo) => {
140
+ if (convo.call.recorded_audio && convo.call.recorded_audio.url) {
141
+ return rs(convo.call.recorded_audio.url)
142
+ }
143
+ rs('unavailable')
144
+ })
145
+ }, 4000)
146
+ })
147
+ }, 4000)
148
+ })
149
+ }, 4000)
150
+ })
151
+ })
152
+ }
153
+
154
+ // fetchConversation sends a request API to get the latest conversation
155
+ function fetchConversation(callid) {
156
+ let call = webphone.getCall(callid)
157
+ if (!call) return Promise.resolve(null)
158
+ let convoid = call.conversation_id
159
+ if (!convoid) return Promise.resolve(null)
160
+ return fetch(
161
+ `https://api.subiz.com.vn/4.0/accounts/${accid}/conversations/${convoid}?x-access-token=${access_token}`,
162
+ ).then((response) => {
163
+ return response.json()
164
+ })
165
+ }
166
+
167
+ function makeCall() {
168
+ if (!webphone) return
169
+ let fromNumber = document.getElementById('fromNumber').value
170
+ let toNumber = document.getElementById('toNumber').value
171
+ //webphone.makeCall(toNumber, fromNumber);
172
+
173
+ webphone.makeCall('0364821895', ['935022139843821727', '842473021368'])
174
+ mycallid = webphone.getCurrentCallId()
175
+ }
176
+
177
+ let access_token = localStorage.getItem('access_token')
178
+ if (access_token) init()
179
+
180
+ // update loop
181
+ setInterval(function () {
182
+ if (!webphone) return
183
+ let call = webphone.getCall(mycallid)
184
+ if (!call) return
185
+ document.getElementById('infoCallId').innerHTML = call.call_id
186
+ document.getElementById('infoFromNumber').innerHTML = call.from_number
187
+ document.getElementById('infoConversationId').innerHTML = call.conversation_id
188
+ document.getElementById('infoToNumber').innerHTML = call.to_number
189
+ document.getElementById('infoStatus').innerHTML = call.status
190
+ document.getElementById('infoCreated').innerHTML = new Date(call.started).toLocaleString()
191
+ if (call.status == 'active')
192
+ document.getElementById('infoDuration').innerHTML = Math.floor((Date.now() - call.started) / 1000) || 0
193
+ else {
194
+ document.getElementById('infoDuration').innerHTML = Math.floor((call.ended - call.started) / 1000) || 0
195
+ }
196
+ if (call.hangup_code) document.getElementById('infoHangupCode').innerHTML = call.hangup_code
197
+ }, 500)
198
+
199
+ function hangupCall() {
200
+ if (!webphone) return
201
+ webphone.hangupCall(mycallid)
202
+ }
203
+
204
+ // expose to window so we can call it in HTML
205
+ window.makeCall = makeCall
206
+ window.hangupCall = hangupCall
207
+ window.inputToken = inputToken
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@subiz/wsclient",
3
- "version": "4.0.20",
3
+ "version": "4.0.22",
4
4
  "main": "realtime.js",
5
5
  "scripts": {
6
+ "dev": "vite",
6
7
  "test": "node realtime.test.js"
7
8
  },
8
9
  "repository": {
@@ -14,10 +15,11 @@
14
15
  "homepage": "https://github.com/subiz/wsclient#readme",
15
16
  "description": "",
16
17
  "devDependencies": {
17
- "tape": "^5.7.2"
18
+ "tape": "^5.7.3",
19
+ "vite": "^5.0.4",
20
+ "vite-plugin-commonjs": "^0.10.1"
18
21
  },
19
22
  "dependencies": {
20
- "@subiz/flow": "^1.1.0",
21
- "request": "^2.88.2"
23
+ "@subiz/flow": "^1.1.0"
22
24
  }
23
25
  }
package/vite.config.js ADDED
@@ -0,0 +1,16 @@
1
+ import {defineConfig} from 'vite'
2
+ import commonjs from 'vite-plugin-commonjs'
3
+
4
+ export default defineConfig({
5
+ plugins: [
6
+ commonjs({
7
+ filter(id) {
8
+ if (id.includes('node_modules/redux-storage/build-es')) {
9
+ return true
10
+ }
11
+ },
12
+ }),
13
+ // Other plugins
14
+ ],
15
+ // Other configurations
16
+ })
package/webphone.js CHANGED
@@ -56,6 +56,31 @@ function WebPhone(access_token, realtime) {
56
56
  },
57
57
  })
58
58
 
59
+ var _numbers = []
60
+ var _fetchNumbers = function () {
61
+ xhrsend(
62
+ 'get',
63
+ `https://api.subiz.com.vn/4.0/integrations?x-access-token=${access_token}`,
64
+ undefined,
65
+ function (body, code) {
66
+ setTimeout(_fetchNumbers, 120_000)
67
+ if (code != 200) return
68
+ var integrations = parseJSON(body)
69
+ if (!integrations || !integrations.length) return
70
+ var numbers = []
71
+ for (var i = 0; i < integrations.length; i++) {
72
+ var integration = integrations[i] || {}
73
+ if (integration.connector_type == 'call' && integration.state == 'activated') {
74
+ var number = integration.id.split('.')[1]
75
+ numbers.push(number)
76
+ }
77
+ }
78
+ _numbers = numbers
79
+ },
80
+ )
81
+ }
82
+ _fetchNumbers()
83
+
59
84
  this.onEvent = function (cb) {
60
85
  return pubsub.on('event', cb)
61
86
  }
@@ -129,23 +154,54 @@ function WebPhone(access_token, realtime) {
129
154
  }
130
155
 
131
156
  var base = '0123456789abcdef'
132
- this.makeCall = function (number, fromnumber, streamPm) {
133
- // '11edc52b-2918-4d71-9058-f7285e29d894'
134
- var callid =
135
- randomString(8, base) +
136
- '-' +
137
- randomString(4, base) +
138
- '-' +
139
- randomString(4, base) +
140
- '-' +
141
- randomString(4, base) +
142
- '-' +
143
- randomString(12, base)
157
+ // makeCall(number, [] || undefined || '', streamPm) // fallback all number
158
+ // makeCall(number, ['a','b','c'], streamPm) // fallback specific number
159
+ // makeCall(number, function() {}, streamPm) //
160
+ this.makeCall = function (number, fromnumbers, streamPm, _a, _b, _state) {
161
+ if (!_state) _state = {}
162
+ if (!fromnumbers || (Array.isArray(fromnumbers) && fromnumbers.length == 0)) {
163
+ fromnumbers = _numbers
164
+ }
165
+
166
+ if (typeof fromnumbers === 'string' || fromnumbers instanceof String || typeof value === 'number') {
167
+ fromnumbers = [fromnumbers + '']
168
+ }
144
169
 
170
+ // '11edc52b-2918-4d71-9058-f7285e29d894'
171
+ var callid = `${randomString(8, base)}-${randomString(4, base)}-${randomString(4, base)}`
172
+ callid += `-${randomString(4, base)}-${randomString(12, base)}`
145
173
  current_call_id = callid
146
174
 
147
175
  if (!streamPm) streamPm = getMicroPermissions()
148
- return webrtcconn.makeCall(number, fromnumber, streamPm, callid)
176
+
177
+ let callnumber = chooseCallNumber(fromnumbers, _state.called)
178
+ if (!callnumber)
179
+ return (
180
+ _state.last_result || {
181
+ start: Date.now(),
182
+ ended: Date.now(),
183
+ direction: 'outbound',
184
+ account_id: accid,
185
+ to_number: number,
186
+ error: 'out_of_number',
187
+ id: callid,
188
+ hangup_code: 'failure',
189
+ status: 'ended',
190
+ }
191
+ )
192
+ return webrtcconn.makeCall(number, callnumber, streamPm, callid).then((out) => {
193
+ if (out.error) return out
194
+ if (out.hangup_code == 'cancel') return out
195
+ if (out.answered > 0) return out
196
+
197
+ if (out.hangup_code == 'terminated' || out.hangup_code == 'failure' || out.hangup_code == 'congestion') {
198
+ if (!_state.called) _state.called = {}
199
+ _state.called[callnumber] = true
200
+ _state.last_result = out
201
+ return this.makeCall(number, fromnumbers, streamPm, _a, _b, _state)
202
+ }
203
+ return out
204
+ })
149
205
  }
150
206
 
151
207
  var current_call_id = ''
@@ -183,3 +239,14 @@ function WebPhone(access_token, realtime) {
183
239
 
184
240
  window.SbzWebPhone1 = WebPhone
185
241
  module.exports = WebPhone
242
+
243
+ function chooseCallNumber(numbers, called) {
244
+ if (!called) called = {}
245
+ if (!numbers || !numbers.length) return ''
246
+ for (var i = 0; i < numbers.length; i++) {
247
+ var number = numbers[i]
248
+ if (called[number]) continue
249
+ return number
250
+ }
251
+ return ''
252
+ }
package/webrtc.js CHANGED
@@ -1,5 +1,5 @@
1
1
  var flow = require('@subiz/flow')
2
- var {xhrsend, randomString} = require('./common.js')
2
+ var {logError, xhrsend, randomString} = require('./common.js')
3
3
  const servers = {
4
4
  iceServers: [{urls: ['stun:stun.l.google.com:19302']}],
5
5
  }
@@ -168,6 +168,7 @@ function WebRTCConn(options) {
168
168
  }
169
169
  env.setInterval(cleanEndedCall, 120000)
170
170
 
171
+ let endcallCb = {}
171
172
  let publish = (ev) => {
172
173
  if (ev.type == 'call_ended') {
173
174
  let callid = ev.data && ev.data.call_info && ev.data.call_info.call_id
@@ -181,6 +182,13 @@ function WebRTCConn(options) {
181
182
  // do not clean up this now or some functions cannot work after the call ended
182
183
  // DONOT: delete call2Connection[callid]
183
184
  delete streams[connId]
185
+
186
+ Object.keys(endcallCb).map((callid) => {
187
+ let arr = endcallCb[callid]
188
+ if (!arr || arr.length == 0 || !arr.map) return
189
+ arr.map((cb) => cb())
190
+ delete endcallCb[callid]
191
+ })
184
192
  }
185
193
 
186
194
  let direction = ev.data && ev.data.call_info && ev.data.call_info.direction
@@ -291,7 +299,10 @@ function WebRTCConn(options) {
291
299
  peer.setLocalDescription(answer).then(() => {
292
300
  let url = `${apiUrl}finish?x-access-token=${access_token}&connection_id=${connId}&version=2&negotiation_id=${negid}`
293
301
  callAPI('post', url, jsonify(answer), function (body, code) {
294
- if (code !== 200) return console.error('CANNOT FINISH', body)
302
+ if (code !== 200) {
303
+ logError(accid, agid, body, 'finish:295')
304
+ return console.error('CANNOT FINISH', body)
305
+ }
295
306
  return
296
307
  })
297
308
  }),
@@ -303,7 +314,7 @@ function WebRTCConn(options) {
303
314
  if (!callid) return
304
315
  let type = ev.type
305
316
  if (type === 'call_invited') {
306
- if (ev.type === 'call_invited' && agid == call_info.from_number) return // ignore our own invite
317
+ if (agid == call_info.from_number) return // ignore our own invite
307
318
  serverCalls[callid] = call_info
308
319
  return publish(ev)
309
320
  }
@@ -359,6 +370,7 @@ function WebRTCConn(options) {
359
370
  if (campaignid) url += `&campaign_id=${campaignid}`
360
371
  callAPI('post', url, undefined, (body, code) => {
361
372
  if (code != 200) {
373
+ logError(accid, agid, body, 'makeCall:365')
362
374
  delete dialingRequest[callid]
363
375
  endRequest[callid] = env.Date.now()
364
376
  publish({created: env.Date.now(), type: 'call_ended', data: {call_info: {call_id: callid}}})
@@ -367,7 +379,11 @@ function WebRTCConn(options) {
367
379
  body = parseJSON(body)
368
380
  if (body) serverCalls[callid] = body
369
381
  delete dialingRequest[callid]
370
- rs({body})
382
+
383
+ let call = this.matchCall(callid)
384
+ if (call && call.status == 'ended') return rs(call)
385
+ if (!endcallCb[callid]) endcallCb[callid] = []
386
+ endcallCb[callid].push(() => rs(this.matchCall(callid)))
371
387
  })
372
388
  })
373
389
  })
@@ -385,6 +401,7 @@ function WebRTCConn(options) {
385
401
  serverCalls[callid] = call
386
402
  }
387
403
  call.status = 'ended'
404
+ call.hangup_code = 'cancel'
388
405
  call.ended = env.Date.now()
389
406
  publish({created: env.Date.now(), type: 'call_ended', data: {call_info: {call_id: callid}}})
390
407
  callAPI('post', `${apiUrl}hangup?x-access-token=${access_token}&call_id=${callid}`)
@@ -395,6 +412,7 @@ function WebRTCConn(options) {
395
412
  initSession((err, connId) => {
396
413
  let url = `${apiUrl}listen?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}`
397
414
  callAPI('post', url, undefined, (body, code) => {
415
+ if (code != 200) logError(accid, agid, body, 'listenCall:399')
398
416
  collect('listen_call', callid, env.Date.now() - start)
399
417
  return code != 200 ? cb(body) : cb()
400
418
  })
@@ -408,10 +426,9 @@ function WebRTCConn(options) {
408
426
  streams[connId] = stream
409
427
  let url = `${apiUrl}listen?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}&talk=true`
410
428
  callAPI('post', url, undefined, (body, code) => {
429
+ if (code != 200) logError(accid, agid, body, 'joinCall:413')
411
430
  collect('join_call', callid, env.Date.now() - start)
412
431
  if (code != 200) return cb(body, connId)
413
- console.timeEnd('makecall' + callid)
414
- if (code != 200) return cb(body, connId)
415
432
  checkIceConnected(peers[connId], () => cb(undefined, connId))
416
433
  })
417
434
  })
@@ -436,6 +453,7 @@ function WebRTCConn(options) {
436
453
  callAPI('post', url, undefined, (body, code) => {
437
454
  body = parseJSON(body)
438
455
  if (code != 200) {
456
+ logError(accid, agid, body, 'answerCall:442')
439
457
  delete answerRequest[callid]
440
458
  endRequest[callid] = now
441
459
  publish({created: env.Date.now(), type: 'call_ended', data: {call_info: {call_id: callid}}})
@@ -454,7 +472,8 @@ function WebRTCConn(options) {
454
472
  if (!connId) return rs({body: {}})
455
473
  let url = `${apiUrl}dtml?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}&key={key}`
456
474
  initSession(() =>
457
- callAPI('post', url, undefined, (body, bode) => {
475
+ callAPI('post', url, undefined, (body, code) => {
476
+ if (code != 200) logError(accid, agid, body, 'sendDtmf:463')
458
477
  body = parseJSON(body)
459
478
  return code != 200 ? rs({error: body}) : rs({body})
460
479
  }),
@@ -469,7 +488,8 @@ function WebRTCConn(options) {
469
488
  let url = `${apiUrl}refer?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}&number={tonumber}`
470
489
  initSession(() =>
471
490
  callAPI('post', url, undefined, (body, code) => {
472
- collect('listen_call', callid, env.Date.now() - start)
491
+ if (code != 200) logError(accid, agid, body, 'transferCall:478')
492
+ collect('transfer_call', callid, env.Date.now() - start)
473
493
  body = parseJSON(body)
474
494
  return code != 200 ? rs({error: body}) : rs({body})
475
495
  }),