@subiz/wsclient 4.0.12 → 4.0.14
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 +1 -1
- package/webrtc.js +216 -154
package/package.json
CHANGED
package/webrtc.js
CHANGED
|
@@ -8,15 +8,13 @@ const servers = {
|
|
|
8
8
|
// env = {RTCPeerConnection, RTCIceCandidate, RTCSessionDescription, setTimeout, setInterval, jsonify, parseJSON}
|
|
9
9
|
// set env._is_webrtc_local to true for local testing
|
|
10
10
|
function WebRTCConn(options) {
|
|
11
|
-
let {apiUrl, access_token, accid, agid,
|
|
11
|
+
let {apiUrl, access_token, accid, agid, realtime, callAPI, env, onTrack, onEvent} = options
|
|
12
12
|
if (!apiUrl) {
|
|
13
13
|
apiUrl = 'https://callcenter.subiz.com.vn/wc/'
|
|
14
14
|
if (env._is_webrtc_local) apiUrl = 'http://localhost:8008/'
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
callAPI = callAPI || xhrsend // allow hook
|
|
18
|
-
let callStream = {} // callid => stream
|
|
19
|
-
|
|
20
18
|
let parseJSON = (str) => {
|
|
21
19
|
try {
|
|
22
20
|
return env.JSON.parse(str)
|
|
@@ -27,69 +25,58 @@ function WebRTCConn(options) {
|
|
|
27
25
|
return env.JSON.stringify(str)
|
|
28
26
|
} catch (e) {}
|
|
29
27
|
}
|
|
30
|
-
let
|
|
31
|
-
let
|
|
32
|
-
let activeCalls = {}
|
|
28
|
+
let streams = {}
|
|
29
|
+
let serverCalls = {}
|
|
33
30
|
|
|
31
|
+
let call2Connection = {}
|
|
34
32
|
let dialingRequest = {}
|
|
35
33
|
let answerRequest = {}
|
|
36
34
|
let endRequest = {}
|
|
37
35
|
|
|
38
|
-
let
|
|
39
|
-
`webrtc.account.${accid}.agent.${agid}`,
|
|
40
|
-
`webrtc.account.${accid}.agent.${agid}.webrtc_connection.${connId}`,
|
|
41
|
-
])
|
|
42
|
-
|
|
43
|
-
let peer
|
|
44
|
-
// makeSure only returns when stream is added and negotiation is finished
|
|
45
|
-
// its safe to call this function multiple times at once
|
|
46
|
-
let makeSure = (cb) => {
|
|
47
|
-
initConnection()
|
|
48
|
-
subscribe.then(cb)
|
|
49
|
-
}
|
|
36
|
+
let peers = {}
|
|
50
37
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
() =>
|
|
54
|
-
new env.Promise((rs) => {
|
|
55
|
-
let pass = peer && (peer.iceConnectionState == 'connected' || peer.iceConnectionState == 'completed')
|
|
56
|
-
if (!pass) return env.setTimeout((_) => rs(true), 100) // keep looping util connected
|
|
57
|
-
cb && cb()
|
|
58
|
-
rs(false)
|
|
59
|
-
}),
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
let readyToSendCandidateResolve
|
|
63
|
-
let readyToSendCandidate = new env.Promise((rs) => (readyToSendCandidateResolve = rs))
|
|
64
|
-
|
|
65
|
-
let initConnection = () => {
|
|
66
|
-
let needConnect =
|
|
67
|
-
!peer ||
|
|
68
|
-
peer.connectionState === 'disconnected' ||
|
|
69
|
-
peer.connectionState === 'closed' ||
|
|
70
|
-
peer.connectionState === 'failed'
|
|
38
|
+
// to receive incomming call
|
|
39
|
+
realtime.subscribe([`webrtc.account.${accid}.agent.${agid}`])
|
|
71
40
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
let
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (peer != mypeer) return // outdated
|
|
79
|
-
onTrack && onTrack(event)
|
|
80
|
-
}
|
|
41
|
+
let initSession = (cb) => {
|
|
42
|
+
let connId = 'webrtc' + randomString(20)
|
|
43
|
+
let peer = new env.RTCPeerConnection(servers)
|
|
44
|
+
peers[connId] = peer
|
|
45
|
+
peer.ontrack = (event) => onTrack && onTrack(event)
|
|
46
|
+
waitForRemoteDescription[connId] = new env.Promise((rs) => (readyToSendCandidateResolve[connId] = rs))
|
|
81
47
|
|
|
82
48
|
peer.onicecandidate = (event) => {
|
|
83
49
|
if (!event.candidate) return
|
|
84
|
-
if (peer != mypeer) return // outdated
|
|
85
50
|
let url = `${apiUrl}ice-candidates?x-access-token=${access_token}&connection_id=${connId}`
|
|
86
51
|
callAPI('post', url, jsonify(event.candidate))
|
|
87
52
|
}
|
|
88
53
|
|
|
89
54
|
peer.onnegotiationneeded = (ev) => console.log('NNNEED RE-NEGOTIATION', ev)
|
|
90
|
-
|
|
55
|
+
realtime
|
|
56
|
+
.subscribe([
|
|
57
|
+
`webrtc.account.${accid}.agent.${agid}`,
|
|
58
|
+
`webrtc.account.${accid}.agent.${agid}.webrtc_connection.${connId}`,
|
|
59
|
+
])
|
|
60
|
+
.then((err) => {
|
|
61
|
+
// create peer connection
|
|
62
|
+
|
|
63
|
+
cb(err, connId)
|
|
64
|
+
})
|
|
91
65
|
}
|
|
92
66
|
|
|
67
|
+
let checkIceConnected = (peer, cb) =>
|
|
68
|
+
flow.loop(
|
|
69
|
+
() =>
|
|
70
|
+
new env.Promise((rs) => {
|
|
71
|
+
let pass = peer && (peer.iceConnectionState == 'connected' || peer.iceConnectionState == 'completed')
|
|
72
|
+
if (!pass) return env.setTimeout((_) => rs(true), 100) // keep looping util connected
|
|
73
|
+
cb && cb()
|
|
74
|
+
rs(false)
|
|
75
|
+
}),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
let readyToSendCandidateResolve = {}
|
|
79
|
+
let waitForRemoteDescription = {}
|
|
93
80
|
let stopStream = (stream) => {
|
|
94
81
|
if (!stream) return
|
|
95
82
|
stream.getTracks().forEach((track) => {
|
|
@@ -98,64 +85,109 @@ function WebRTCConn(options) {
|
|
|
98
85
|
})
|
|
99
86
|
}
|
|
100
87
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
let receivedBytes = []
|
|
104
|
-
let check = () => {
|
|
105
|
-
let url = `${apiUrl}calls?x-access-token=${access_token}&connection_id=${connId}`
|
|
88
|
+
let syncCallWithServer = () => {
|
|
89
|
+
let url = `${apiUrl}calls?x-access-token=${access_token}`
|
|
106
90
|
callAPI('get', url, undefined, function (body, code) {
|
|
107
|
-
if (code
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
let call = activeCalls[callid]
|
|
115
|
-
if (!serverCall[callid] && call && call.status != 'ended')
|
|
116
|
-
publish({type: 'call_ended', data: {call_info: {hanup_code: 'outdated', call_id: callid}}})
|
|
117
|
-
})
|
|
118
|
-
activeCalls = serverCall
|
|
119
|
-
}
|
|
91
|
+
if (code != 200) return
|
|
92
|
+
body = parseJSON(body) || []
|
|
93
|
+
let newCalls = {}
|
|
94
|
+
if (!body.map) return
|
|
95
|
+
body.map((call) => {
|
|
96
|
+
newCalls[call.call_id] = call
|
|
97
|
+
})
|
|
120
98
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
let
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
99
|
+
// end all local calls that not found on the server
|
|
100
|
+
Object.keys(serverCalls).map((i) => {
|
|
101
|
+
let call = serverCalls[i]
|
|
102
|
+
if (!call) return delete serverCalls[i]
|
|
103
|
+
let callid = call.call_id
|
|
104
|
+
if (newCalls[callid]) return
|
|
105
|
+
|
|
106
|
+
// calls that are missing on the server
|
|
107
|
+
newCalls[callid] = call
|
|
108
|
+
if (!call.ended) call.ended = env.Date.now()
|
|
109
|
+
if (!call.hangup_code) call.hangup_code = 'cancel'
|
|
110
|
+
if (call.status != 'ended') {
|
|
111
|
+
call.status = 'ended'
|
|
112
|
+
publish({type: 'call_ended', data: {call_info: {hanup_code: 'outdated', call_id: callid}}})
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
serverCalls = {}
|
|
117
|
+
Object.values(newCalls).map((call) => {
|
|
118
|
+
if (!call) return false
|
|
119
|
+
if (call.status == 'ended') {
|
|
120
|
+
// receive ended call
|
|
121
|
+
serverCalls[call.call_id] = call
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
// keep all dialing incomming calls
|
|
125
|
+
if (call.status == 'dialing' && (call.direction == 'incoming' || call.direction == 'inbound')) {
|
|
126
|
+
serverCalls[call.call_id] = call
|
|
127
|
+
return
|
|
140
128
|
}
|
|
129
|
+
|
|
130
|
+
let connId = call.device_id
|
|
131
|
+
let peer = peers[connId]
|
|
132
|
+
if (!peer) return false // ignore active call that not in our peer
|
|
133
|
+
call2Connection[call.call_id] = connId
|
|
134
|
+
serverCalls[call.call_id] = call
|
|
135
|
+
return true
|
|
141
136
|
})
|
|
142
137
|
})
|
|
143
138
|
}
|
|
144
|
-
|
|
145
|
-
env.setInterval(
|
|
139
|
+
syncCallWithServer()
|
|
140
|
+
env.setInterval(syncCallWithServer, 10000)
|
|
141
|
+
|
|
142
|
+
let cleanEndedCall = () => {
|
|
143
|
+
Object.keys(endRequest).map((callid) => {
|
|
144
|
+
if (env.Date.now() - endRequest[callid] > 60000) delete endRequest[callid]
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
Object.keys(answerRequest).map((callid) => {
|
|
148
|
+
if (env.Date.now() - endRequest[callid] > 60000) delete answerRequest[callid]
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
Object.keys(serverCalls).map((callid) => {
|
|
152
|
+
let call = serverCalls[callid]
|
|
153
|
+
if (!call) return delete serverCalls[callid]
|
|
154
|
+
if (call.status != 'ended') return
|
|
155
|
+
if (env.Date.now() - call.ended > 60000) delete serverCalls[callid]
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
Object.keys(call2Connection).map((callid) => {
|
|
159
|
+
let call = this.matchCall(callid)
|
|
160
|
+
if (!call) return delete call2Connection[callid]
|
|
161
|
+
if (call.status != 'ended') return
|
|
162
|
+
if (env.Date.now() - call.ended > 60000) delete call2Connection[callid]
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
env.setInterval(cleanEndedCall, 120000)
|
|
146
166
|
|
|
147
167
|
let publish = (ev) => {
|
|
148
168
|
if (ev.type == 'call_ended') {
|
|
149
|
-
let callid =
|
|
150
|
-
|
|
151
|
-
|
|
169
|
+
let callid = ev.data && ev.data.call_info && ev.data.call_info.call_id
|
|
170
|
+
let connId = call2Connection[callid]
|
|
171
|
+
let peer = peers[connId]
|
|
172
|
+
if (peer) {
|
|
173
|
+
delete peers[connId]
|
|
174
|
+
peer.close()
|
|
175
|
+
}
|
|
176
|
+
stopStream(streams[connId])
|
|
177
|
+
// do not clean up this now or some functions cannot work after the call ended
|
|
178
|
+
// DONOT: delete call2Connection[callid]
|
|
179
|
+
delete streams[connId]
|
|
152
180
|
}
|
|
181
|
+
|
|
182
|
+
let direction = ev.data && ev.data.call_info && ev.data.call_info.direction
|
|
183
|
+
if (direction == 'incoming') ev.data.call_info.direction = 'inbound'
|
|
184
|
+
if (direction == 'outgoing') ev.data.call_info.direction = 'outbound'
|
|
153
185
|
onEvent && onEvent(ev)
|
|
154
186
|
}
|
|
155
187
|
|
|
156
188
|
this.matchCall = (callid) => {
|
|
157
189
|
if (!callid || callid == '*' || callid == '-') {
|
|
158
|
-
let calls = Object.assign({},
|
|
190
|
+
let calls = Object.assign({}, serverCalls)
|
|
159
191
|
Object.keys(dialingRequest).map((callid) => {
|
|
160
192
|
let call = calls[callid]
|
|
161
193
|
if (!call && dialingRequest[callid]) {
|
|
@@ -180,10 +212,16 @@ function WebRTCConn(options) {
|
|
|
180
212
|
if (!call) return
|
|
181
213
|
call.answer_requested = answerRequest[callid]
|
|
182
214
|
})
|
|
215
|
+
|
|
216
|
+
Object.values(calls).map((call) => {
|
|
217
|
+
let direction = call && call.direction
|
|
218
|
+
if (direction == 'incoming') call.direction = 'inbound'
|
|
219
|
+
if (direction == 'outgoing') call.direction = 'outbound'
|
|
220
|
+
})
|
|
183
221
|
return calls
|
|
184
222
|
}
|
|
185
223
|
|
|
186
|
-
let call =
|
|
224
|
+
let call = serverCalls[callid]
|
|
187
225
|
if (!call && dialingRequest[callid]) call = dialingRequest[callid]
|
|
188
226
|
if (endRequest[callid] && call && call.status != 'ended') {
|
|
189
227
|
call = Object.assign({}, call, {
|
|
@@ -194,47 +232,54 @@ function WebRTCConn(options) {
|
|
|
194
232
|
})
|
|
195
233
|
}
|
|
196
234
|
if (answerRequest[callid]) call = Object.assign({}, call, {answer_requested: answerRequest[callid]})
|
|
235
|
+
let direction = call && call.direction
|
|
236
|
+
if (direction == 'incoming') call.direction = 'inbound'
|
|
237
|
+
if (direction == 'outgoing') call.direction = 'outbound'
|
|
238
|
+
|
|
197
239
|
return call
|
|
198
240
|
}
|
|
199
241
|
|
|
200
242
|
realtime.onEvent((ev) => {
|
|
201
243
|
if (!ev || !ev.type || !ev.data) return
|
|
202
|
-
// if (ev.type.startsWith('call')) console.log('EEEEEEE', ev)
|
|
203
244
|
if (env._is_webrtc_local && ev.user_id != '8') return // local
|
|
204
245
|
if (!env._is_webrtc_local && ev.user_id != '9') return // prod
|
|
205
246
|
|
|
206
247
|
if (ev.type == 'webrtc_candidate_added') {
|
|
207
|
-
let ice_candidate = ev.data.ice_candidate
|
|
248
|
+
let ice_candidate = ev && ev.data && ev.data.webrtc_message && ev.data.webrtc_message.ice_candidate
|
|
249
|
+
let connId = ev && ev.data && ev.data.webrtc_message && ev.data.webrtc_message.connection_id
|
|
250
|
+
if (!connId) return
|
|
208
251
|
if (!ice_candidate) return
|
|
209
|
-
|
|
252
|
+
let peer = peers[connId]
|
|
253
|
+
|
|
254
|
+
return waitForRemoteDescription[connId].then(() => peer && peer.addIceCandidate(parseJSON(ice_candidate)))
|
|
210
255
|
}
|
|
211
256
|
|
|
212
257
|
if (ev.type == 'webrtc_negotiation_offered') {
|
|
213
258
|
console.log('RT/RECEIVE OFFERED NEGOTIATION', ev)
|
|
259
|
+
let jsonoffer = ev && ev.data && ev.data.webrtc_message && ev.data.webrtc_message.offer
|
|
260
|
+
let connId = ev && ev.data && ev.data.webrtc_message && ev.data.webrtc_message.connection_id
|
|
261
|
+
let peer = peers[connId]
|
|
214
262
|
if (!peer) return
|
|
215
263
|
let negid = ev.id
|
|
216
|
-
|
|
217
|
-
let offer = parseJSON(
|
|
264
|
+
// let negid = ev && ev.data && ev.data.webrtc_message && ev.data.webrtc_message.negotiation_id
|
|
265
|
+
let offer = parseJSON(jsonoffer)
|
|
266
|
+
if (!offer) return
|
|
218
267
|
|
|
219
268
|
peer
|
|
220
269
|
.setRemoteDescription(offer)
|
|
221
270
|
.then(() => {
|
|
222
|
-
readyToSendCandidateResolve()
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
replaced = true
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
if (!replaced) peer.addTrack(track, currentStream)
|
|
271
|
+
readyToSendCandidateResolve[connId]()
|
|
272
|
+
let stream = streams[connId]
|
|
273
|
+
stream && stream.getTracks().forEach((track) => {
|
|
274
|
+
let replaced = false
|
|
275
|
+
peer.getSenders().forEach((sender) => {
|
|
276
|
+
if (!sender.track) return
|
|
277
|
+
if (sender.track.kind != track.kind) return
|
|
278
|
+
sender.replaceTrack(track)
|
|
279
|
+
replaced = true
|
|
235
280
|
})
|
|
236
|
-
|
|
237
|
-
|
|
281
|
+
if (!replaced) peer.addTrack(track, stream)
|
|
282
|
+
})
|
|
238
283
|
return peer.createAnswer()
|
|
239
284
|
})
|
|
240
285
|
.then((answer) =>
|
|
@@ -251,18 +296,28 @@ function WebRTCConn(options) {
|
|
|
251
296
|
let call_info = ev.data.call_info || {}
|
|
252
297
|
let callid = call_info.call_id
|
|
253
298
|
if (!callid) return
|
|
299
|
+
let type = ev.type
|
|
300
|
+
if (type === 'call_invited') {
|
|
301
|
+
if (ev.type === 'call_invited' && agid == call_info.from_number) return // ignore our own invite
|
|
302
|
+
serverCalls[callid] = call_info
|
|
303
|
+
return publish(ev)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (type == 'call_ended') {
|
|
307
|
+
serverCalls[callid] = call_info
|
|
308
|
+
return publish(ev)
|
|
309
|
+
}
|
|
254
310
|
|
|
255
311
|
if (ev.type === 'call_invite_expired') {
|
|
256
|
-
|
|
257
|
-
if (device_id == connId) return // ignore since we have accepted the call
|
|
312
|
+
if (Object.keys(peers).find((cid) => cid == call_info.device_id)) return // this call invitation is expired by us => ignore
|
|
258
313
|
publish(Object.assign({}, ev, {type: 'call_ended'})) // fake ended call
|
|
259
|
-
|
|
314
|
+
return
|
|
260
315
|
}
|
|
261
316
|
|
|
262
|
-
let
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
|
|
317
|
+
let connId = call_info.device_id
|
|
318
|
+
if (!Object.keys(peers).find((cid) => cid == connId)) return
|
|
319
|
+
if (type == 'call_ringing' || type == 'call_joined') {
|
|
320
|
+
serverCalls[callid] = call_info
|
|
266
321
|
return publish(ev)
|
|
267
322
|
}
|
|
268
323
|
})
|
|
@@ -273,7 +328,6 @@ function WebRTCConn(options) {
|
|
|
273
328
|
let call = {
|
|
274
329
|
account_id: accid,
|
|
275
330
|
call_id: callid,
|
|
276
|
-
device_id: connId,
|
|
277
331
|
direction: 'outbound',
|
|
278
332
|
member_id: agid,
|
|
279
333
|
started: now,
|
|
@@ -283,30 +337,32 @@ function WebRTCConn(options) {
|
|
|
283
337
|
dialingRequest[callid] = call
|
|
284
338
|
publish({type: 'call_ringing', data: {call_info: {call_id: callid}}}) // fake dialing
|
|
285
339
|
return new env.Promise((rs) => {
|
|
286
|
-
streamPm
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
endRequest[callid] = env.Date.now()
|
|
291
|
-
publish({type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
292
|
-
return rs({error: err})
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
let url = `${apiUrl}call?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}&number=${number}&from_number=${fromnumber}`
|
|
296
|
-
callAPI('post', url, undefined, (body, code) => {
|
|
297
|
-
if (code != 200) {
|
|
340
|
+
streamPm
|
|
341
|
+
.then((stream) => {
|
|
342
|
+
this.joinCall(callid, stream, (err, connId) => {
|
|
343
|
+
if (err) {
|
|
298
344
|
delete dialingRequest[callid]
|
|
299
345
|
endRequest[callid] = env.Date.now()
|
|
300
346
|
publish({type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
301
347
|
return rs({error: err})
|
|
302
348
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
349
|
+
|
|
350
|
+
let url = `${apiUrl}call?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}&number=${number}&from_number=${fromnumber}`
|
|
351
|
+
callAPI('post', url, undefined, (body, code) => {
|
|
352
|
+
if (code != 200) {
|
|
353
|
+
delete dialingRequest[callid]
|
|
354
|
+
endRequest[callid] = env.Date.now()
|
|
355
|
+
publish({type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
356
|
+
return rs({error: err})
|
|
357
|
+
}
|
|
358
|
+
body = parseJSON(body)
|
|
359
|
+
if (body) serverCalls[callid] = body
|
|
360
|
+
delete dialingRequest[callid]
|
|
361
|
+
rs({body})
|
|
362
|
+
})
|
|
307
363
|
})
|
|
308
364
|
})
|
|
309
|
-
|
|
365
|
+
.catch((err) => rs({error: err}))
|
|
310
366
|
})
|
|
311
367
|
}
|
|
312
368
|
|
|
@@ -314,47 +370,48 @@ function WebRTCConn(options) {
|
|
|
314
370
|
if (!callid) return
|
|
315
371
|
endRequest[callid] = env.Date.now()
|
|
316
372
|
|
|
317
|
-
let call =
|
|
373
|
+
let call = serverCalls[callid]
|
|
318
374
|
if (!call) {
|
|
319
375
|
call = {call_id: callid}
|
|
320
|
-
|
|
376
|
+
serverCalls[callid] = call
|
|
321
377
|
}
|
|
322
378
|
call.status = 'ended'
|
|
323
379
|
call.ended = env.Date.now()
|
|
324
380
|
publish({type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
325
|
-
|
|
326
|
-
callAPI('post', `${apiUrl}hangup?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}`)
|
|
381
|
+
callAPI('post', `${apiUrl}hangup?x-access-token=${access_token}&call_id=${callid}`)
|
|
327
382
|
}
|
|
328
383
|
|
|
329
384
|
this.listenCall = (callid, cb) => {
|
|
330
|
-
|
|
331
|
-
|
|
385
|
+
initSession((err, connId) => {
|
|
386
|
+
let url = `${apiUrl}listen?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}`
|
|
387
|
+
callAPI('post', url, undefined, (body, code) => (code != 200 ? cb(body) : cb()))
|
|
388
|
+
})
|
|
332
389
|
}
|
|
333
390
|
|
|
334
391
|
this.joinCall = (callid, stream, cb) => {
|
|
335
|
-
callStream[callid] = stream
|
|
336
392
|
console.time('makecall' + callid)
|
|
337
|
-
|
|
338
|
-
|
|
393
|
+
initSession((err, connId) => {
|
|
394
|
+
call2Connection[callid] = connId
|
|
395
|
+
streams[connId] = stream
|
|
339
396
|
let url = `${apiUrl}listen?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}&talk=true`
|
|
340
397
|
callAPI('post', url, undefined, (body, code) => {
|
|
341
|
-
if (code != 200) return cb(body)
|
|
398
|
+
if (code != 200) return cb(body, connId)
|
|
342
399
|
console.timeEnd('makecall' + callid)
|
|
343
|
-
if (code != 200) return cb(body)
|
|
344
|
-
checkIceConnected(() => cb())
|
|
400
|
+
if (code != 200) return cb(body, connId)
|
|
401
|
+
checkIceConnected(peers[connId], () => cb(undefined, connId))
|
|
345
402
|
})
|
|
346
403
|
})
|
|
347
404
|
}
|
|
348
405
|
|
|
349
406
|
this.answerCall = (callid, stream) => {
|
|
350
|
-
let call =
|
|
407
|
+
let call = serverCalls[callid]
|
|
351
408
|
if (call && call.status == 'ended') return Promise.resolve({error: 'call_ended'})
|
|
352
409
|
let now = env.Date.now()
|
|
353
410
|
if (answerRequest[callid]) return Promise.resolve({body: call})
|
|
354
411
|
answerRequest[callid] = now
|
|
355
412
|
publish({type: 'call_ringing', data: {call_info: {call_id: callid}}}) // fake dialing
|
|
356
413
|
return new env.Promise((rs) => {
|
|
357
|
-
this.joinCall(callid, stream, (err) => {
|
|
414
|
+
this.joinCall(callid, stream, (err, connId) => {
|
|
358
415
|
if (err) {
|
|
359
416
|
delete answerRequest[callid]
|
|
360
417
|
endRequest[callid] = now
|
|
@@ -379,8 +436,11 @@ function WebRTCConn(options) {
|
|
|
379
436
|
this.sendDtmf = (key, callid) =>
|
|
380
437
|
new env.Promise((rs) => {
|
|
381
438
|
if (!callid) return rs({body: {}})
|
|
439
|
+
let connId = call2Connection[callid]
|
|
440
|
+
if (!connId) return rs({body: {}})
|
|
441
|
+
|
|
382
442
|
let url = `${apiUrl}dtml?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}&key={key}`
|
|
383
|
-
|
|
443
|
+
initSession(() =>
|
|
384
444
|
callAPI('post', url, undefined, (body, bode) => {
|
|
385
445
|
body = parseJSON(body)
|
|
386
446
|
return code != 200 ? rs({error: body}) : rs({body})
|
|
@@ -391,8 +451,10 @@ function WebRTCConn(options) {
|
|
|
391
451
|
this.transferCall = (tonumber, callid) =>
|
|
392
452
|
new env.Promise((rs) => {
|
|
393
453
|
if (!callid) return rs({})
|
|
454
|
+
let connId = call2Connection[callid]
|
|
455
|
+
if (!connId) return rs({body: {}})
|
|
394
456
|
let url = `${apiUrl}refer?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}&number={tonumber}`
|
|
395
|
-
|
|
457
|
+
initSession(() =>
|
|
396
458
|
callAPI('post', url, undefined, (body, code) => {
|
|
397
459
|
body = parseJSON(body)
|
|
398
460
|
return code != 200 ? rs({error: body}) : rs({body})
|