@subiz/wsclient 4.0.17 → 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 +17 -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.7.
|
|
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]
|
|
@@ -338,7 +341,7 @@ function WebRTCConn(options) {
|
|
|
338
341
|
from_number: fromnumber,
|
|
339
342
|
}
|
|
340
343
|
dialingRequest[callid] = call
|
|
341
|
-
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
|
|
342
345
|
return new env.Promise((rs) => {
|
|
343
346
|
streamPm
|
|
344
347
|
.then((stream) => {
|
|
@@ -346,16 +349,18 @@ function WebRTCConn(options) {
|
|
|
346
349
|
if (err) {
|
|
347
350
|
delete dialingRequest[callid]
|
|
348
351
|
endRequest[callid] = env.Date.now()
|
|
349
|
-
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}}})
|
|
350
353
|
return rs({error: err})
|
|
351
354
|
}
|
|
352
355
|
|
|
353
|
-
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}`
|
|
354
359
|
callAPI('post', url, undefined, (body, code) => {
|
|
355
360
|
if (code != 200) {
|
|
356
361
|
delete dialingRequest[callid]
|
|
357
362
|
endRequest[callid] = env.Date.now()
|
|
358
|
-
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}}})
|
|
359
364
|
return rs({error: err})
|
|
360
365
|
}
|
|
361
366
|
body = parseJSON(body)
|
|
@@ -380,7 +385,7 @@ function WebRTCConn(options) {
|
|
|
380
385
|
}
|
|
381
386
|
call.status = 'ended'
|
|
382
387
|
call.ended = env.Date.now()
|
|
383
|
-
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}}})
|
|
384
389
|
callAPI('post', `${apiUrl}hangup?x-access-token=${access_token}&call_id=${callid}`)
|
|
385
390
|
}
|
|
386
391
|
|
|
@@ -412,13 +417,13 @@ function WebRTCConn(options) {
|
|
|
412
417
|
let now = env.Date.now()
|
|
413
418
|
if (answerRequest[callid]) return Promise.resolve({body: call})
|
|
414
419
|
answerRequest[callid] = now
|
|
415
|
-
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
|
|
416
421
|
return new env.Promise((rs) => {
|
|
417
422
|
this.joinCall(callid, stream, (err, connId) => {
|
|
418
423
|
if (err) {
|
|
419
424
|
delete answerRequest[callid]
|
|
420
425
|
endRequest[callid] = now
|
|
421
|
-
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}}})
|
|
422
427
|
return rs({error: err})
|
|
423
428
|
}
|
|
424
429
|
let url = `${apiUrl}answer?x-access-token=${access_token}&connection_id=${connId}&call_id=${callid}`
|
|
@@ -427,7 +432,7 @@ function WebRTCConn(options) {
|
|
|
427
432
|
if (code != 200) {
|
|
428
433
|
delete answerRequest[callid]
|
|
429
434
|
endRequest[callid] = now
|
|
430
|
-
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}}})
|
|
431
436
|
return rs({error: body})
|
|
432
437
|
}
|
|
433
438
|
return rs({body})
|
|
@@ -466,15 +471,4 @@ function WebRTCConn(options) {
|
|
|
466
471
|
})
|
|
467
472
|
}
|
|
468
473
|
|
|
469
|
-
function randomString(len) {
|
|
470
|
-
var str = ''
|
|
471
|
-
if (!len || len < 1) len = 10
|
|
472
|
-
var asciiKey
|
|
473
|
-
for (var i = 0; i < len; i++) {
|
|
474
|
-
asciiKey = Math.floor(Math.random() * 25 + 97)
|
|
475
|
-
str += String.fromCharCode(asciiKey)
|
|
476
|
-
}
|
|
477
|
-
return str
|
|
478
|
-
}
|
|
479
|
-
|
|
480
474
|
module.exports = WebRTCConn
|