@subiz/wsclient 4.0.11 → 4.0.13
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 +223 -159
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
|
-
}
|
|
50
|
-
|
|
51
|
-
let checkIceConnected = (cb) =>
|
|
52
|
-
flow.loop(
|
|
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))
|
|
36
|
+
let peers = {}
|
|
64
37
|
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
let diff = receivedBytes.filter((n) => n != receivedByte)
|
|
136
|
-
if (diff.length == 0 && receivedBytes.length >= 10 && Object.keys(activeCalls).length == 0) {
|
|
137
|
-
// could tear down
|
|
138
|
-
peer.close()
|
|
139
|
-
peer = undefined
|
|
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}}})
|
|
140
113
|
}
|
|
141
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
|
|
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
|
|
136
|
+
})
|
|
137
|
+
})
|
|
138
|
+
}
|
|
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]
|
|
142
163
|
})
|
|
143
164
|
}
|
|
144
|
-
|
|
145
|
-
env.setInterval(check, 30000)
|
|
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.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,29 +296,38 @@ 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
|
})
|
|
269
324
|
|
|
270
|
-
this.makeCall = (number, fromnumber,
|
|
325
|
+
this.makeCall = (number, fromnumber, streamPm, callid) => {
|
|
271
326
|
if (!callid) callid = 'webcall-' + randomString(30)
|
|
272
327
|
let now = env.Date.now()
|
|
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,28 +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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
340
|
+
streamPm
|
|
341
|
+
.then((stream) => {
|
|
342
|
+
this.joinCall(callid, stream, (err, connId) => {
|
|
343
|
+
if (err) {
|
|
344
|
+
delete dialingRequest[callid]
|
|
345
|
+
endRequest[callid] = env.Date.now()
|
|
346
|
+
publish({type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
347
|
+
return rs({error: err})
|
|
348
|
+
}
|
|
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
|
+
})
|
|
363
|
+
})
|
|
306
364
|
})
|
|
307
|
-
|
|
365
|
+
.catch((err) => rs({error: err}))
|
|
308
366
|
})
|
|
309
367
|
}
|
|
310
368
|
|
|
@@ -312,47 +370,48 @@ function WebRTCConn(options) {
|
|
|
312
370
|
if (!callid) return
|
|
313
371
|
endRequest[callid] = env.Date.now()
|
|
314
372
|
|
|
315
|
-
let call =
|
|
373
|
+
let call = serverCalls[callid]
|
|
316
374
|
if (!call) {
|
|
317
375
|
call = {call_id: callid}
|
|
318
|
-
|
|
376
|
+
serverCalls[callid] = call
|
|
319
377
|
}
|
|
320
378
|
call.status = 'ended'
|
|
321
379
|
call.ended = env.Date.now()
|
|
322
380
|
publish({type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
323
|
-
|
|
324
|
-
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}`)
|
|
325
382
|
}
|
|
326
383
|
|
|
327
384
|
this.listenCall = (callid, cb) => {
|
|
328
|
-
|
|
329
|
-
|
|
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
|
+
})
|
|
330
389
|
}
|
|
331
390
|
|
|
332
391
|
this.joinCall = (callid, stream, cb) => {
|
|
333
|
-
callStream[callid] = stream
|
|
334
392
|
console.time('makecall' + callid)
|
|
335
|
-
|
|
336
|
-
|
|
393
|
+
initSession((err, connId) => {
|
|
394
|
+
call2Connection[callid] = connId
|
|
395
|
+
streams[connId] = stream
|
|
337
396
|
let url = `${apiUrl}listen?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}&talk=true`
|
|
338
397
|
callAPI('post', url, undefined, (body, code) => {
|
|
339
|
-
if (code != 200) return cb(body)
|
|
398
|
+
if (code != 200) return cb(body, connId)
|
|
340
399
|
console.timeEnd('makecall' + callid)
|
|
341
|
-
if (code != 200) return cb(body)
|
|
342
|
-
checkIceConnected(() => cb())
|
|
400
|
+
if (code != 200) return cb(body, connId)
|
|
401
|
+
checkIceConnected(peers[connId], () => cb(undefined, connId))
|
|
343
402
|
})
|
|
344
403
|
})
|
|
345
404
|
}
|
|
346
405
|
|
|
347
406
|
this.answerCall = (callid, stream) => {
|
|
348
|
-
let call =
|
|
407
|
+
let call = serverCalls[callid]
|
|
349
408
|
if (call && call.status == 'ended') return Promise.resolve({error: 'call_ended'})
|
|
350
409
|
let now = env.Date.now()
|
|
351
410
|
if (answerRequest[callid]) return Promise.resolve({body: call})
|
|
352
411
|
answerRequest[callid] = now
|
|
353
412
|
publish({type: 'call_ringing', data: {call_info: {call_id: callid}}}) // fake dialing
|
|
354
413
|
return new env.Promise((rs) => {
|
|
355
|
-
this.joinCall(callid, stream, (err) => {
|
|
414
|
+
this.joinCall(callid, stream, (err, connId) => {
|
|
356
415
|
if (err) {
|
|
357
416
|
delete answerRequest[callid]
|
|
358
417
|
endRequest[callid] = now
|
|
@@ -377,8 +436,11 @@ function WebRTCConn(options) {
|
|
|
377
436
|
this.sendDtmf = (key, callid) =>
|
|
378
437
|
new env.Promise((rs) => {
|
|
379
438
|
if (!callid) return rs({body: {}})
|
|
439
|
+
let connId = call2Connection[callid]
|
|
440
|
+
if (!connId) return rs({body: {}})
|
|
441
|
+
|
|
380
442
|
let url = `${apiUrl}dtml?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}&key={key}`
|
|
381
|
-
|
|
443
|
+
initSession(() =>
|
|
382
444
|
callAPI('post', url, undefined, (body, bode) => {
|
|
383
445
|
body = parseJSON(body)
|
|
384
446
|
return code != 200 ? rs({error: body}) : rs({body})
|
|
@@ -389,8 +451,10 @@ function WebRTCConn(options) {
|
|
|
389
451
|
this.transferCall = (tonumber, callid) =>
|
|
390
452
|
new env.Promise((rs) => {
|
|
391
453
|
if (!callid) return rs({})
|
|
454
|
+
let connId = call2Connection[callid]
|
|
455
|
+
if (!connId) return rs({body: {}})
|
|
392
456
|
let url = `${apiUrl}refer?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}&number={tonumber}`
|
|
393
|
-
|
|
457
|
+
initSession(() =>
|
|
394
458
|
callAPI('post', url, undefined, (body, code) => {
|
|
395
459
|
body = parseJSON(body)
|
|
396
460
|
return code != 200 ? rs({error: body}) : rs({body})
|