@subiz/wsclient 4.0.21 → 4.0.23
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/demo.html +129 -0
- package/demo.js +207 -0
- package/package.json +6 -4
- package/vite.config.js +16 -0
- package/webphone.js +80 -13
- package/webrtc.js +14 -1
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.
|
|
3
|
+
"version": "4.0.23",
|
|
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.
|
|
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, 120000)
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
'
|
|
143
|
-
|
|
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
|
-
|
|
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
|
@@ -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
|
|
@@ -371,7 +379,11 @@ function WebRTCConn(options) {
|
|
|
371
379
|
body = parseJSON(body)
|
|
372
380
|
if (body) serverCalls[callid] = body
|
|
373
381
|
delete dialingRequest[callid]
|
|
374
|
-
|
|
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)))
|
|
375
387
|
})
|
|
376
388
|
})
|
|
377
389
|
})
|
|
@@ -389,6 +401,7 @@ function WebRTCConn(options) {
|
|
|
389
401
|
serverCalls[callid] = call
|
|
390
402
|
}
|
|
391
403
|
call.status = 'ended'
|
|
404
|
+
call.hangup_code = 'cancel'
|
|
392
405
|
call.ended = env.Date.now()
|
|
393
406
|
publish({created: env.Date.now(), type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
394
407
|
callAPI('post', `${apiUrl}hangup?x-access-token=${access_token}&call_id=${callid}`)
|