@subiz/wsclient 4.0.16 → 4.0.18
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 +16 -1
- package/package.json +2 -2
- package/webphone.js +183 -0
- package/webrtc.js +18 -23
package/common.js
CHANGED
|
@@ -63,4 +63,19 @@ function Pubsub() {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
function randomString(len, base) {
|
|
67
|
+
var str = ''
|
|
68
|
+
if (!len || len < 1) len = 10
|
|
69
|
+
var asciiKey
|
|
70
|
+
for (var i = 0; i < len; i++) {
|
|
71
|
+
if (base) {
|
|
72
|
+
str += base[Math.floor(Math.random() * base.length)]
|
|
73
|
+
} else {
|
|
74
|
+
asciiKey = Math.floor(Math.random() * 25 + 97)
|
|
75
|
+
str += String.fromCharCode(asciiKey)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return str
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {xhrsend, parseJSON, Pubsub, randomString}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@subiz/wsclient",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.18",
|
|
4
4
|
"main": "realtime.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node realtime.test.js"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"homepage": "https://github.com/subiz/wsclient#readme",
|
|
15
15
|
"description": "",
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"tape": "^5.
|
|
17
|
+
"tape": "^5.7.2"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@subiz/flow": "^1.1.0",
|
package/webphone.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
var {xhrsend, parseJSON, Pubsub, randomString} = require('./common.js')
|
|
2
|
+
const WebRTCConn = require('./webrtc.js')
|
|
3
|
+
var Realtime = require('./realtime.js')
|
|
4
|
+
|
|
5
|
+
function WebPhone(access_token, realtime) {
|
|
6
|
+
var pubsub = new Pubsub()
|
|
7
|
+
|
|
8
|
+
var $audio = null
|
|
9
|
+
// chuẩn bị thẻ audio để nghe được
|
|
10
|
+
function createAudioTag() {
|
|
11
|
+
if ($audio) return
|
|
12
|
+
if (!document.createElement) return // mobile
|
|
13
|
+
|
|
14
|
+
$audio = document.createElement('audio')
|
|
15
|
+
$audio.id = 'web_call_audio'
|
|
16
|
+
$audio.style = 'display: none'
|
|
17
|
+
$audio.autoplay = 'autoplay'
|
|
18
|
+
document.body.appendChild($audio)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
createAudioTag()
|
|
22
|
+
|
|
23
|
+
access_token = access_token || ''
|
|
24
|
+
if (!access_token) throw 'invalid_access_token'
|
|
25
|
+
var accid = access_token.split('_')[0]
|
|
26
|
+
if (!accid.startsWith('ac')) throw 'invalid_access_token'
|
|
27
|
+
var agid = access_token.split('_')[1]
|
|
28
|
+
if (!agid.startsWith('ag')) throw 'invalid_access_token'
|
|
29
|
+
|
|
30
|
+
if (!realtime)
|
|
31
|
+
realtime = new Realtime('https://api.subiz.com.vn/rt/0/', {
|
|
32
|
+
getAccessToken: function () {
|
|
33
|
+
return new Promise(function (rs) {
|
|
34
|
+
rs(access_token)
|
|
35
|
+
})
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
var webrtcconn = new WebRTCConn({
|
|
40
|
+
accid: accid,
|
|
41
|
+
agid: agid,
|
|
42
|
+
access_token: access_token,
|
|
43
|
+
realtime,
|
|
44
|
+
env: window,
|
|
45
|
+
onEvent: function (ev) {
|
|
46
|
+
// publish call status event
|
|
47
|
+
if (!(ev.type || '').startsWith('call')) return
|
|
48
|
+
if (!ev.data || !ev.data.call_info) return
|
|
49
|
+
let callid = ev.data.call_info.call_id
|
|
50
|
+
if (!callid) return
|
|
51
|
+
pubsub.emit('event', ev)
|
|
52
|
+
},
|
|
53
|
+
onTrack: function (event) {
|
|
54
|
+
if (event.track.kind != 'audio') return
|
|
55
|
+
$audio.srcObject = event.streams[0]
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
this.onEvent = function (cb) {
|
|
60
|
+
return pubsub.on('event', cb)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.sendDtmf = function (key, callid) {
|
|
64
|
+
return webrtcconn.sendDtmf(key, callid)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.transferCall = function (number, callid) {
|
|
68
|
+
return webrtcconn.transferCall(number, callid)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.hangupCall = function (callid) {
|
|
72
|
+
if (!callid) callid = current_call_id
|
|
73
|
+
return webrtcconn.hangupCall(callid)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
var me = this
|
|
77
|
+
this.isMicAllowed = function () {
|
|
78
|
+
return me.checkMic().then(function (out) {
|
|
79
|
+
return out.result
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
var micStream = undefined
|
|
84
|
+
this.getMicroStream = function () {
|
|
85
|
+
return micStream
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this.checkMic = function () {
|
|
89
|
+
const timeout = new Promise(function (rs, rj) {
|
|
90
|
+
return setTimeout(rs, 500, 'Not_authorized')
|
|
91
|
+
})
|
|
92
|
+
var microPermission = getMicroPermissions()
|
|
93
|
+
return {
|
|
94
|
+
timeout: Promise.race([timeout, microPermission]),
|
|
95
|
+
result: getMicroPermissions(),
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
var getMicroPermissions = function () {
|
|
100
|
+
return new Promise(function (rs) {
|
|
101
|
+
navigator.mediaDevices
|
|
102
|
+
.getUserMedia({audio: true, video: false})
|
|
103
|
+
.then(function (stream) {
|
|
104
|
+
micStream = stream
|
|
105
|
+
rs(micStream)
|
|
106
|
+
})
|
|
107
|
+
.catch(function (err) {
|
|
108
|
+
console.log('REJECT VIDEO PERMISSSIONN', err)
|
|
109
|
+
micStream = undefined
|
|
110
|
+
rs(undefined)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
this.answerWebCall = function (callid) {
|
|
116
|
+
return new Promise(function (rs) {
|
|
117
|
+
getMicroPermissions().then(function (stream) {
|
|
118
|
+
current_call_id = callid
|
|
119
|
+
webrtcconn.answerCall(callid, stream).then(function (out) {
|
|
120
|
+
if (out.error) return rs({error})
|
|
121
|
+
rs(body)
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.getCall = function (callid) {
|
|
128
|
+
return webrtcconn.matchCall(callid)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
var base = '0123456789abcdef'
|
|
132
|
+
this.makeCall = function (number, fromnumber) {
|
|
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)
|
|
144
|
+
|
|
145
|
+
current_call_id = callid
|
|
146
|
+
return webrtcconn.makeCall(number, fromnumber, getMicroPermissions(), callid)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
var current_call_id = ''
|
|
150
|
+
this.getCurrentCallId = function () {
|
|
151
|
+
return current_call_id
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this.getCurrentCall = function () {
|
|
155
|
+
if (current_call_id) {
|
|
156
|
+
var call = webrtcconn.matchCall(current_call_id)
|
|
157
|
+
if (call && call.status != 'ended') return call
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
var myOutgoingCall = Object.values(webrtcconn.matchCall()).find(function (call) {
|
|
161
|
+
var isOutboundCall = call.direction == 'outbound' || call.direction == 'outgoing'
|
|
162
|
+
return (call.status == 'dialing' || call.status == 'active') && isOutboundCall
|
|
163
|
+
})
|
|
164
|
+
if (myOutgoingCall) {
|
|
165
|
+
current_call_id = myOutgoingCall.call_id || ''
|
|
166
|
+
return myOutgoingCall
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
var dialingIncomingCall = Object.values(webrtcconn.matchCall()).find(function (call) {
|
|
170
|
+
return call.status == 'dialing' && (call.direction == 'incoming' || call.direction == 'inbound')
|
|
171
|
+
})
|
|
172
|
+
if (dialingIncomingCall) {
|
|
173
|
+
current_call_id = dialingIncomingCall.call_id || ''
|
|
174
|
+
return dialingIncomingCall
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
current_call_id = ''
|
|
178
|
+
return undefined
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
window.SbzWebPhone1 = WebPhone
|
|
183
|
+
module.exports = WebPhone
|
package/webrtc.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var flow = require('@subiz/flow')
|
|
2
|
-
var {xhrsend} = require('./common.js')
|
|
2
|
+
var {xhrsend, randomString} = require('./common.js')
|
|
3
3
|
const servers = {
|
|
4
4
|
iceServers: [{urls: ['stun:stun.l.google.com:19302']}],
|
|
5
5
|
}
|
|
@@ -59,7 +59,6 @@ function WebRTCConn(options) {
|
|
|
59
59
|
])
|
|
60
60
|
.then((err) => {
|
|
61
61
|
// create peer connection
|
|
62
|
-
|
|
63
62
|
cb(err, connId)
|
|
64
63
|
})
|
|
65
64
|
}
|
|
@@ -109,7 +108,11 @@ function WebRTCConn(options) {
|
|
|
109
108
|
if (!call.hangup_code) call.hangup_code = 'cancel'
|
|
110
109
|
if (call.status != 'ended') {
|
|
111
110
|
call.status = 'ended'
|
|
112
|
-
publish({
|
|
111
|
+
publish({
|
|
112
|
+
created: env.Date.now(),
|
|
113
|
+
type: 'call_ended',
|
|
114
|
+
data: {call_info: {hanup_code: 'outdated', call_id: callid}},
|
|
115
|
+
})
|
|
113
116
|
}
|
|
114
117
|
})
|
|
115
118
|
|
|
@@ -255,7 +258,7 @@ function WebRTCConn(options) {
|
|
|
255
258
|
}
|
|
256
259
|
|
|
257
260
|
if (ev.type == 'webrtc_negotiation_offered') {
|
|
258
|
-
console.log('RT/RECEIVE OFFERED NEGOTIATION', ev)
|
|
261
|
+
// console.log('RT/RECEIVE OFFERED NEGOTIATION', ev)
|
|
259
262
|
let jsonoffer = ev && ev.data && ev.data.webrtc_message && ev.data.webrtc_message.offer
|
|
260
263
|
let connId = ev && ev.data && ev.data.webrtc_message && ev.data.webrtc_message.connection_id
|
|
261
264
|
let peer = peers[connId]
|
|
@@ -311,6 +314,7 @@ function WebRTCConn(options) {
|
|
|
311
314
|
|
|
312
315
|
if (ev.type === 'call_invite_expired') {
|
|
313
316
|
if (Object.keys(peers).find((cid) => cid == call_info.device_id)) return // this call invitation is expired by us => ignore
|
|
317
|
+
serverCalls[callid] = call_info
|
|
314
318
|
publish(Object.assign({}, ev, {type: 'call_ended'})) // fake ended call
|
|
315
319
|
return
|
|
316
320
|
}
|
|
@@ -337,7 +341,7 @@ function WebRTCConn(options) {
|
|
|
337
341
|
from_number: fromnumber,
|
|
338
342
|
}
|
|
339
343
|
dialingRequest[callid] = call
|
|
340
|
-
publish({type: 'call_ringing', data: {call_info: {call_id: callid}}}) // fake dialing
|
|
344
|
+
publish({created: now, type: 'call_ringing', data: {call_info: {call_id: callid}}}) // fake dialing
|
|
341
345
|
return new env.Promise((rs) => {
|
|
342
346
|
streamPm
|
|
343
347
|
.then((stream) => {
|
|
@@ -345,16 +349,18 @@ function WebRTCConn(options) {
|
|
|
345
349
|
if (err) {
|
|
346
350
|
delete dialingRequest[callid]
|
|
347
351
|
endRequest[callid] = env.Date.now()
|
|
348
|
-
publish({type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
352
|
+
publish({created: env.Date.now(), type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
349
353
|
return rs({error: err})
|
|
350
354
|
}
|
|
351
355
|
|
|
352
|
-
let url = `${apiUrl}call?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}&number=${number}&from_number=${fromnumber}
|
|
356
|
+
let url = `${apiUrl}call?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}&number=${number}&from_number=${fromnumber}`
|
|
357
|
+
if (outboundcallentryid) url += `&outbound_call_entry_id=${outboundcallentryid}`
|
|
358
|
+
if (campaignid) url += `&campaign_id=${campaignid}`
|
|
353
359
|
callAPI('post', url, undefined, (body, code) => {
|
|
354
360
|
if (code != 200) {
|
|
355
361
|
delete dialingRequest[callid]
|
|
356
362
|
endRequest[callid] = env.Date.now()
|
|
357
|
-
publish({type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
363
|
+
publish({created: env.Date.now(), type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
358
364
|
return rs({error: err})
|
|
359
365
|
}
|
|
360
366
|
body = parseJSON(body)
|
|
@@ -379,7 +385,7 @@ function WebRTCConn(options) {
|
|
|
379
385
|
}
|
|
380
386
|
call.status = 'ended'
|
|
381
387
|
call.ended = env.Date.now()
|
|
382
|
-
publish({type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
388
|
+
publish({created: env.Date.now(), type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
383
389
|
callAPI('post', `${apiUrl}hangup?x-access-token=${access_token}&call_id=${callid}`)
|
|
384
390
|
}
|
|
385
391
|
|
|
@@ -411,13 +417,13 @@ function WebRTCConn(options) {
|
|
|
411
417
|
let now = env.Date.now()
|
|
412
418
|
if (answerRequest[callid]) return Promise.resolve({body: call})
|
|
413
419
|
answerRequest[callid] = now
|
|
414
|
-
publish({type: 'call_ringing', data: {call_info: {call_id: callid}}}) // fake dialing
|
|
420
|
+
publish({created: env.Date.now(), type: 'call_ringing', data: {call_info: {call_id: callid}}}) // fake dialing
|
|
415
421
|
return new env.Promise((rs) => {
|
|
416
422
|
this.joinCall(callid, stream, (err, connId) => {
|
|
417
423
|
if (err) {
|
|
418
424
|
delete answerRequest[callid]
|
|
419
425
|
endRequest[callid] = now
|
|
420
|
-
publish({type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
426
|
+
publish({created: env.Date.now(), type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
421
427
|
return rs({error: err})
|
|
422
428
|
}
|
|
423
429
|
let url = `${apiUrl}answer?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}`
|
|
@@ -426,7 +432,7 @@ function WebRTCConn(options) {
|
|
|
426
432
|
if (code != 200) {
|
|
427
433
|
delete answerRequest[callid]
|
|
428
434
|
endRequest[callid] = now
|
|
429
|
-
publish({type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
435
|
+
publish({created: env.Date.now(), type: 'call_ended', data: {call_info: {call_id: callid}}})
|
|
430
436
|
return rs({error: body})
|
|
431
437
|
}
|
|
432
438
|
return rs({body})
|
|
@@ -465,15 +471,4 @@ function WebRTCConn(options) {
|
|
|
465
471
|
})
|
|
466
472
|
}
|
|
467
473
|
|
|
468
|
-
function randomString(len) {
|
|
469
|
-
var str = ''
|
|
470
|
-
if (!len || len < 1) len = 10
|
|
471
|
-
var asciiKey
|
|
472
|
-
for (var i = 0; i < len; i++) {
|
|
473
|
-
asciiKey = Math.floor(Math.random() * 25 + 97)
|
|
474
|
-
str += String.fromCharCode(asciiKey)
|
|
475
|
-
}
|
|
476
|
-
return str
|
|
477
|
-
}
|
|
478
|
-
|
|
479
474
|
module.exports = WebRTCConn
|