@94ai/softphone 5.0.11 → 5.0.12

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.
@@ -201,20 +201,13 @@ document.addEventListener('DOMContentLoaded', async (event) => {
201
201
  getMedia
202
202
  } = softphone // 👈 sdk
203
203
 
204
- const extensionNumber = '1000' // 本地fs 分机账号
205
- const extPassword = '1234' // 本地fs 分机密码
204
+ const extensionNumber = '3376' // 本地fs 分机账号
205
+ const extPassword = 'zjh13542240708' // 本地fs 分机密码
206
206
  const wsProtocol = 'ws' // 本地fs 地址协议
207
- const wsRegisterAddress = '192.168.59.197:5066' // 本地fs 外呼服务器地址
207
+ const wsRegisterAddress = '192.168.31.239:5066' // 本地fs 外呼服务器地址
208
208
  const sipServerHost = `sip:${extensionNumber}@${wsRegisterAddress}` // fs地址
209
209
  const wsAddress = `${wsProtocol}://${wsRegisterAddress}` // socket地址
210
210
 
211
- // const extensionNumber = '2839'
212
- // const extPassword = 'zjh13542240708'
213
- // const wsProtocol = 'ws'
214
- // const wsRegisterAddress = '192.168.31.239:5066'
215
- // const sipServerHost = `sip:${extensionNumber}@${wsRegisterAddress}`
216
- // const wsAddress = `${wsProtocol}://${wsRegisterAddress}`
217
-
218
211
  const userAgentManager = UserAgentFactory.getUserAgentManager({
219
212
  authorizationPassword: extPassword, // 分机密码
220
213
  authorizationUsername: extensionNumber, // 分机账号
@@ -0,0 +1,613 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport"
6
+ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
7
+ <title>softphone</title>
8
+ <style>
9
+ #softphone-app {
10
+ padding-top: 50px;
11
+ overflow: hidden;
12
+ }
13
+ </style>
14
+ </head>
15
+ <body>
16
+ <div id="softphone-app">
17
+ <div style="float: left;margin-left: 20px">
18
+ <div style="margin-bottom: 15px">
19
+ 一、当前坐席信息:
20
+ </div>
21
+ <pre id="info"></pre>
22
+ <div>
23
+ 2. 当前坐席交流传递到远端的音量大小:<span id="volumn">0.00</span>
24
+ <div>- (强度比例0.00~1.00,用以实现如下音量反馈效果)</div>
25
+ <img src="./tapd_35238004_base64_1695010473_859.gif" style="width: 460px" />
26
+ </div>
27
+ <div style="margin-bottom: 15px">
28
+ 3. 当前浏览器状态(通过权限网络检测获取):
29
+ </div>
30
+ <pre id="browserSoftphoneEnv"></pre>
31
+ </div>
32
+ <div style="float: left;margin-left: 10px">
33
+ <div style="margin-bottom: 15px">
34
+ 二、调试步骤:
35
+ </div>
36
+ <div>1. 获取坐席是否在线
37
+ <div>
38
+ - agentStatus === 1 -> 在线
39
+ </div>
40
+ <div>
41
+ - agentStatus === 3 -> 小休
42
+ </div>
43
+ <div>
44
+ - tip: 能外呼的条件是:
45
+ <div style="margin-left: 20px;">
46
+ <div>
47
+ 1. 软电话签入
48
+ </div>
49
+ <div>
50
+ 2. 坐席在线
51
+ </div>
52
+ <div>
53
+ 3. 同时确保坐席所在企业配置 【坐席外呼依赖】配置项 是【标识】 (默认是【登录】)
54
+ </div>
55
+ <div style="margin-left: 20px;">
56
+ - 配置项 【登录】:只有登录了94决策系统才可外呼。
57
+ </div>
58
+ <div style="margin-left: 30px;">
59
+ (tip: 如果不确定联系94运维在管理后台查看以及配置)
60
+ </div>
61
+ </div>
62
+ </div>
63
+
64
+ </div>
65
+ <div>2. 设置坐席在线</div>
66
+ <div>3. 连接外呼服务器(软电话签入)</div>
67
+ <div>4. 手动呼叫或任务外呼</div>
68
+ <div>- 手动呼叫,外呼服务器会先呼叫坐席,坐席接听后才会正式呼叫用户</div>
69
+ <div>- 任务外呼,外呼服务器会先呼叫用户,用户接听后才会正式呼叫坐席,坐席接听前有ai和用户对话</div>
70
+ <div>5. 接听 </div>
71
+ <div>6. 转人工(根据任务的配置:是否是监听任务,手动外呼不是任务外呼,不用转人工) </div>
72
+ <div>- tip:监听任务:接听后只可以听到ai和用户的对话,转人工后才可以和用户直接对话 </div>
73
+ <div>7. 挂断</div>
74
+ </div>
75
+ <div style="float: left;margin-left: 20px">
76
+ <div style="margin-bottom: 15px">
77
+ 三、具体步骤:
78
+ </div>
79
+ <button id="ordinary-microphone" disabled>切换麦克风音频推送</button>
80
+ <br>
81
+ <br>
82
+ <button id="pcm-button-player" disabled>切换推送pcm音频</button>
83
+ <br>
84
+ <br>
85
+ <button id="nap">设置当前坐席进入小休状态</button>
86
+ <br>
87
+ <br>
88
+ <button id="online">1. 设置当前坐席在线</button>
89
+ <br>
90
+ <br>
91
+ <button id="connectToServer">2. 连接外呼服务器</button>
92
+ <br>
93
+ <br>
94
+ <button id="disconnect" disabled>断开连接服务器</button>
95
+ <br>
96
+ <br>
97
+ <button id="callButton">3. 手动呼叫</button>
98
+ : <input id="call" />
99
+ <br>
100
+ <br>
101
+ <button id="ignore" disabled>忽略</button>
102
+ <br>
103
+ <br>
104
+ <button id="answer" disabled>4. 接听</button>
105
+ <br>
106
+ <br>
107
+ <button id="hangUp" disabled>5. 挂断</button>
108
+ <br>
109
+ <br>
110
+ <button id="sendStarDTMF" disabled>按*转人工</button>
111
+ <br>
112
+ <br>
113
+ <button id="localVoiceAccess" disabled>本地声音接入(默认接入)</button>
114
+ <br>
115
+ <br>
116
+ <button id="remoteSoundAccess" disabled>远程声音接入(默认接入)</button>
117
+ <br>
118
+ <br>
119
+ <button id="localSoundDisconnection" disabled>本地声音断开</button>
120
+ <br>
121
+ <br>
122
+ <button id="remoteSoundDisconnection" disabled>远程声音断开</button>
123
+ <br>
124
+ <br>
125
+ <button id="check">权限网络检测</button>
126
+ <br>
127
+ <br>
128
+ <audio
129
+ id="remoteAudio"
130
+ controls
131
+ style="display:none"
132
+ >
133
+ <p>Your browser doesn't support HTML5 audio - remoteAudio</p>
134
+ </audio>
135
+ <audio
136
+ id="localAudio"
137
+ style="display:none"
138
+ src="./antique_phone.mp3"
139
+ controls
140
+ loop
141
+ >
142
+ <p>Your browser doesn't support HTML5 audio - localAudio</p>
143
+ </audio>
144
+ </div>
145
+ </div>
146
+ <div>
147
+ 四、当前聊天记录信息:
148
+ <pre id="chatInfo">
149
+ </pre>
150
+ </div>
151
+
152
+ <script src="./fp.umd.min.js"></script>
153
+ <script src="./CryptoJS.js"></script>
154
+ <script src="./softphone.umd.min.js"></script>
155
+ <script src="./WebrtcDiver.js"></script>
156
+
157
+ <script>
158
+ document.addEventListener('DOMContentLoaded', async (event) => {
159
+ const webrtcDiverInstance = new WebrtcDiver()
160
+ webrtcDiverInstance.init()
161
+
162
+ const phoneRing = './antique_phone.mp3'
163
+
164
+ /**
165
+ * 开启电话响铃
166
+ */
167
+ const phoneRings = () => {
168
+ remoteAudio.src = phoneRing
169
+ playMedia('localAudio')
170
+ }
171
+
172
+ /**
173
+ * 关闭电话响铃
174
+ */
175
+ const stopPhoneRings = () => {
176
+ remoteAudio.src = ''
177
+ pauseMedia('localAudio')
178
+ }
179
+
180
+ /**
181
+ * 获取dom
182
+ */
183
+ const info = document.getElementById('info')
184
+ const ignore = document.getElementById('ignore')
185
+ const answer = document.getElementById('answer')
186
+ const hangUp = document.getElementById('hangUp')
187
+ const connectToServer = document.getElementById('connectToServer')
188
+ const call = document.getElementById('call')
189
+ const callButton = document.getElementById('callButton')
190
+ const nap = document.getElementById('nap')
191
+ const chatInfo = document.getElementById('chatInfo')
192
+ const online = document.getElementById('online')
193
+ const disconnect = document.getElementById('disconnect')
194
+ const sendStarDTMF = document.getElementById('sendStarDTMF')
195
+ const localVoiceAccess = document.getElementById('localVoiceAccess')
196
+ const remoteSoundAccess = document.getElementById('remoteSoundAccess')
197
+ const localSoundDisconnection = document.getElementById('localSoundDisconnection')
198
+ const remoteSoundDisconnection = document.getElementById('remoteSoundDisconnection')
199
+ const check = document.getElementById('check')
200
+ const volumn = document.getElementById('volumn')
201
+ const browserSoftphoneEnv = document.getElementById('browserSoftphoneEnv')
202
+ const localAudio = document.getElementById('localAudio')
203
+ const remoteAudio = document.getElementById('remoteAudio')
204
+
205
+ const {
206
+ UserAgentFactory,
207
+ UserAgent,
208
+ playMedia,
209
+ pauseMedia,
210
+ SessionState,
211
+ getMedia
212
+ } = softphone // 👈 sdk
213
+
214
+ const userAgentManager = UserAgentFactory.getUserAgentManager({
215
+ agentTag: '15018707394', // 生产线 坐席维度定位
216
+ appKey: '032d44009bff1752', // 生产线 企业维度定位
217
+ appSecret: '4c7304c94c5e8725613516d4d6db679b', // 生产线 企业维度定位 这里直接使用appSecret做演示
218
+ })
219
+
220
+
221
+ const angentInfo = await userAgentManager.getAgentInfo()
222
+ if (angentInfo.code === 200) { // 正确响应
223
+ info.innerText = `1. ${JSON.stringify(angentInfo.data, null, 2)}`
224
+ if (angentInfo.data.agentStatus === 1) { // 页面按钮的禁用状态
225
+ nap.disabled = false
226
+ online.disabled = true // 可切换在线
227
+ } else {
228
+ nap.disabled = true // 可切换小休
229
+ online.disabled = false
230
+ }
231
+ } else {
232
+ alert(angentInfo.message)
233
+ throw new Error(angentInfo)
234
+ }
235
+
236
+ /**
237
+ * 签入
238
+ */
239
+ const connect = async () => {
240
+ try {
241
+ await userAgentManager.prepareUserAgent(
242
+ { // config
243
+ refresh(path, value) { // 当软电话状态变化时会实时刷新这个方法
244
+ console.log(path, value)
245
+ },
246
+ registererOptions: { // 设置会话多长时间过期,然后自动重签, 单位秒
247
+ expires: 10 * 60
248
+ }
249
+ },
250
+ { // event
251
+ onInvite(invitation) { // 当有外呼过来
252
+ // const info = userAgentManager.getCallNumberDetail({
253
+ // id: Number(userAgentManager.businessAttribute.numberId), // 根据你需要哪通电话来,这里在来电时获取当前来电详情
254
+ // taskId: userAgentManager.businessAttribute.taskId, // 如果不是任务外呼不用传
255
+ // })
256
+ // console.log(info, '========')
257
+ setTimeout(() => {
258
+ acceptInvite()
259
+ }, 0)
260
+ console.log(userAgentManager.businessAttribute)
261
+ phoneRings() // 播放模拟来电响铃
262
+ invitation.stateChange.addListener((state) => { // 一旦接听(执行accept),监听会话的生命周期
263
+ switch (state) {
264
+ case SessionState.Initial:
265
+ break
266
+ case SessionState.Establishing:
267
+ break
268
+ case SessionState.Established: // session建立后就可拿到webrtc的各种基础api,如userAgentManager.getPeerConnection(),如userAgentManager.getSenders()等等
269
+ const mediaElement1 = getMedia('remoteAudio')
270
+ mediaElement1.srcObject = userAgentManager.getStream() // 获取流
271
+ mediaElement1.play() // 把softphone流导入到audio接入用户语音
272
+ hangUp.disabled = false
273
+ webrtcDiverInstance.openPcmData() // 会话建立向对方推送pcm音频
274
+ document.getElementById('ordinary-microphone').disabled = false
275
+ document.getElementById('pcm-button-player').disabled = true
276
+ break
277
+ case SessionState.Terminating:
278
+ case SessionState.Terminated: // 在挂断电话时候会执行
279
+ webrtcDiverInstance.microphoneTransfer() // 会话挂断,切换回麦克风
280
+ document.getElementById('ordinary-microphone').disabled = true
281
+ document.getElementById('pcm-button-player').disabled = true
282
+ const mediaElement2 = getMedia('remoteAudio') // 获取audio dom
283
+ mediaElement2.srcObject = null
284
+ mediaElement2.pause() // 释放audio
285
+ stopPhoneRings()
286
+
287
+ ignore.disabled = true
288
+ answer.disabled = true
289
+ hangUp.disabled = true
290
+ localSoundDisconnection.disabled = true
291
+ remoteSoundDisconnection.disabled = true
292
+ localVoiceAccess.disabled = true
293
+ remoteSoundAccess.disabled = true
294
+ break
295
+ default:
296
+ throw new Error('Unknown session state.')
297
+ }
298
+ })
299
+ ignore.disabled = false
300
+ answer.disabled = false
301
+ localSoundDisconnection.disabled = false
302
+ remoteSoundDisconnection.disabled = false
303
+ }
304
+ }
305
+ ) // 👈 一键签入
306
+ } catch (errorInfo) { // 签入容错提示处理
307
+ if (errorInfo.message.indexOf('Invalid WebSocket Server URL') > -1
308
+ || errorInfo.message.indexOf('Invalid scheme in WebSocket Server URL') > -1) {
309
+ alert('签入配置地址不正确,无法连通完成签入')
310
+ } else if (errorInfo.message === 'sip register fail with code 503' ||
311
+ (errorInfo.message.indexOf('WebSocket closed') > -1 && errorInfo.message.indexOf('code: 1006') > -1)) {
312
+ alert('软电话服务器不可用,签入失败')
313
+ } else {
314
+ alert(errorInfo.message)
315
+ }
316
+ throw errorInfo
317
+ }
318
+ disconnect.disabled = false
319
+ connectToServer.disabled = true
320
+ ignore.disabled = true
321
+ answer.disabled = true
322
+ hangUp.disabled = true
323
+ localSoundDisconnection.disabled = true
324
+ remoteSoundDisconnection.disabled = true
325
+ localVoiceAccess.disabled = true
326
+ remoteSoundAccess.disabled = true
327
+ }
328
+
329
+ /**
330
+ * 签出
331
+ */
332
+ const disConnect = async () => {
333
+ await userAgentManager.dispose() // 👈 一个方法安全销毁,一键签出
334
+ connectToServer.disabled = false
335
+ disconnect.disabled = true
336
+ ignore.disabled = true
337
+ answer.disabled = true
338
+ hangUp.disabled = true
339
+ localSoundDisconnection.disabled = true
340
+ remoteSoundDisconnection.disabled = true
341
+ localVoiceAccess.disabled = true
342
+ remoteSoundAccess.disabled = true
343
+ }
344
+
345
+ /**
346
+ * 按*号转人工,返回promise,可以做loading效果做点击锁
347
+ */
348
+
349
+ const sendStarDtmf = async () => {
350
+ // 由于 【坐席外呼类型】 没有配置要不要转人工的入口
351
+ // 而:【坐席快速外呼】 不需要 转人工;【坐席ai外呼】 一定要 转人工;其他外呼类型根据userAgentManager.notNeedSendStarDtmf(true不需要,false需要,即本质是根据配置),如任务类型外呼根据任务的配置
352
+ // userAgentManager.getFastOutboundCall判断是否坐席快速外呼(true是false否)
353
+ // userAgentManager.getSitOutboundCall判断是否坐席ai外呼(true是false否)
354
+ if ((!userAgentManager.notNeedSendStarDtmf() && !userAgentManager.getFastOutboundCall()) || userAgentManager.getSitOutboundCall()) {
355
+ await new Promise((resolve, reject) => {
356
+ const fn = async () => {
357
+ await userAgentManager.sendStarDtmf({
358
+ requestDelegate: {
359
+ onAccept () { // 👈 转人工响应成功
360
+ resolve(true)
361
+ },
362
+ async onReject (info) { //👈 转人工响应超时或失败
363
+ alert('转人工响应失败:' + info.message?.statusCode + ':' + info.message?.reasonPhrase)
364
+ reject(new Error(info.message.statusCode + ':' + info.message.reasonPhrase))
365
+ }
366
+ }
367
+ }) // 👈 一键转人工,await的下行代码被执行说明成功发出转人工信令
368
+ sendStarDTMF.disabled = true
369
+ }
370
+ fn()
371
+ })
372
+ }
373
+ }
374
+
375
+ /**
376
+ * 挂断
377
+ */
378
+ const hangUpInvite = async () => {
379
+ await new Promise((resolve, reject) => {
380
+ const fn = async () => {
381
+ await userAgentManager.hangUpInvite({
382
+ scoutResponse: true, // 标识是手动挂断,手动挂断无需等待响应,信号成功发出立即重置会话状态。
383
+ byeOptions: {
384
+ requestDelegate: {
385
+ onAccept (response) { // 👈 挂断响应成功
386
+ resolve(response)
387
+ },
388
+ onReject: (response) => { // 👈 挂断响应超时或失败
389
+ alert('挂断响应失败: ' + response?.message?.statusCode + ': ' + response?.message?.reasonPhrase) // 会话终止
390
+ reject(new Error(response?.message?.statusCode + ': ' + response?.message?.reasonPhrase))
391
+ }
392
+ }
393
+ },
394
+ }) // 👈 一键挂断, 下行代码执行,说明挂端信令成功发出
395
+ stopPhoneRings() // 暂停来电铃声,正常挂断不需要处理铃声(因为已经接听,会话已建立,才能挂断),这里做个兜底
396
+ ignore.disabled = true
397
+ answer.disabled = true
398
+ hangUp.disabled = true
399
+ localSoundDisconnection.disabled = true
400
+ remoteSoundDisconnection.disabled = true
401
+ localVoiceAccess.disabled = true
402
+ remoteSoundAccess.disabled = true
403
+ }
404
+ fn()
405
+ })
406
+ }
407
+ /**
408
+ * 忽略
409
+ */
410
+ const ignoreInvite = async () => {
411
+ stopPhoneRings() // 暂停来电铃声
412
+ await userAgentManager.ignoreInvite() // 👈 一键忽略,下行代码被执行说明忽略信令成功发出
413
+ ignore.disabled = true
414
+ answer.disabled = true
415
+ hangUp.disabled = true
416
+ localSoundDisconnection.disabled = true
417
+ remoteSoundDisconnection.disabled = true
418
+ localVoiceAccess.disabled = true
419
+ remoteSoundAccess.disabled = true
420
+ }
421
+ /**
422
+ * 接听
423
+ */
424
+ const acceptInvite = async () => {
425
+ stopPhoneRings() // 暂停来电铃声
426
+ try {
427
+ await new Promise((resolve, reject) => {
428
+ const fn = async () => {
429
+ try {
430
+ await userAgentManager.acceptInvite({
431
+ onAck (info) { // 👈 接听响应成功
432
+ resolve(info)
433
+ },
434
+ onAckTimeout () { // 👈 接听响应失败
435
+ reject(new Error('接听响应失败'))
436
+ }
437
+ }) // 👈 一键接听,下行代码被执行说明接听信令被成功发出
438
+ } catch (e) {
439
+ console.log('accept error:', e.message)
440
+ reject(e)
441
+ throw e
442
+ }
443
+ console.log('接听信令被成功发出')
444
+ }
445
+ fn()
446
+ })
447
+ // 接听信令不能被发出,可能原因如下:
448
+ } catch (error) { // 接听容错提示处理
449
+ if (error.message === 'Requested device not found'
450
+ || error.message === 'The object can not be found here.') { // 设备问题
451
+ alert('未检测到耳麦设备,已自动断开来电')
452
+ throw error
453
+ } else if (error.message === 'The request is not allowed by the user agent or the platform in the current context.'
454
+ || error.message === 'Media devices not available in insecure contexts.') { // 设备权限和浏览器api权限问题
455
+ if (navigator.userAgent.indexOf('Firefox') > -1) {
456
+ alert('软电话功能需要获取麦克风权限,如果没有麦克风权限或者http域名未配置insecure contexts都会引起连接会话立即终止,如果当前浏览器访问的地址是https协议,大部分连接会话终止的原因是没有麦克风权限')
457
+ } else {
458
+ alert('浏览器webrtc功能默认需要网站域名升级到https,如果当前域名为http,请配置浏览器insecure以指定域名白名单使用webrtc api,此时才可获取设备等权限以使用软电话功能')
459
+ }
460
+ throw error
461
+ } else if (error.message === 'Permission denied') { // 麦克风权限问题
462
+ alert('没有麦克风权限,请开启')
463
+ throw error
464
+ } else if (error.message === 'RTCPeerConnection is not defined') { // 浏览器版本问题
465
+ alert('当前浏览器版本过低不支持webrtc,推荐使用新版的谷歌或360或edge浏览器')
466
+ throw error
467
+ }
468
+ throw error
469
+ }
470
+ // 由于 【坐席外呼类型】 没有配置要不要转人工的入口
471
+ // 而:【坐席快速外呼】 不需要 转人工;【坐席ai外呼】 一定要 转人工;其他外呼类型根据userAgentManager.notNeedSendStarDtmf(true不需要,false需要,根据配置),如任务类型外呼根据任务配置
472
+ // userAgentManager.getFastOutboundCall判断是否坐席快速外呼(true是false否)
473
+ // userAgentManager.getSitOutboundCall判断是否坐席ai外呼(true是false否)
474
+ if ((!userAgentManager.notNeedSendStarDtmf() && !userAgentManager.getFastOutboundCall()) || userAgentManager.getSitOutboundCall()) {
475
+ sendStarDTMF.disabled = false // 打开页面转人工功能
476
+ }
477
+ hangUp.disabled = false
478
+ answer.disabled = true
479
+ ignore.disabled = true
480
+ }
481
+
482
+ /**
483
+ * 本地声音推流通道 -> 默认开
484
+ * 手动打开 本地声音推流通道(打开本地声音发送)
485
+ */
486
+ const unMuteLocalAudio = () => {
487
+ localVoiceAccess.disabled = true
488
+ localSoundDisconnection.disabled = false
489
+ userAgentManager.unMuteLocalAudio() // 👈 一键
490
+ }
491
+ /**
492
+ * 本地声音推流通道 -> 默认开
493
+ * 手动关闭 本地声音推流通道(关闭本地声音发送)
494
+ */
495
+ const muteLocalAudio = () => {
496
+ remoteSoundAccess.disabled = true
497
+ remoteSoundDisconnection.disabled = false
498
+ userAgentManager.muteLocalAudio() // 👈 一键
499
+ }
500
+ /**
501
+ * 远端声音拉流通道 -> 默认开
502
+ * 手动打开 远端声音拉流通道(用以打开远端声音接收)
503
+ */
504
+ const unMuteRemoteAudio = () => {
505
+ localSoundDisconnection.disabled = true
506
+ localVoiceAccess.disabled = false
507
+ userAgentManager.unMuteRemoteAudio() // 👈 一键
508
+ }
509
+ /**
510
+ * 远端声音拉流通道 -> 默认开
511
+ * 手动关闭 远端声音拉流通道(用以关闭远端声音接收)
512
+ */
513
+ const muteRemoteAudio = () => {
514
+ remoteSoundDisconnection.disabled = true
515
+ remoteSoundAccess.disabled = false
516
+ userAgentManager.muteRemoteAudio() // 👈 一键
517
+ }
518
+
519
+ /**
520
+ * 呼叫
521
+ * - 手机号外呼用openapi,如 userAgentManager.callNumber 或 userAgentManager.requestOpenApi({
522
+ * url: '/v1/task/importAgentCustomer',
523
+ * data: {
524
+ * agentTag: 'xxx',
525
+ * agentId: 'xxx',
526
+ * callType: 1001,
527
+ * customers: [{ number: 15018707394 }]
528
+ * }
529
+ * })
530
+ * - 分机外呼用invite, 如await userAgentManager.invite({
531
+ * // uri: 'xxx' // 如果是不同外呼服务器的分机,使用uri,格式同上 sipServerHost
532
+ * extension: call.value // 同个外呼服务器地址的分机 例如上述分机账号 extensionNumber
533
+ * })
534
+ *
535
+ */
536
+ const callPhone = async () => {
537
+ const callInfo = await userAgentManager.callNumber(call.value) // 👈 一键
538
+ // const callInfo = await userAgentManager.requestOpenApi({ // 主动外呼
539
+ // url: '/v1/task/importAgentCustomer',
540
+ // data: {
541
+ // agentTag: '15018707394',
542
+ // callType: 1001,
543
+ // customers: [{
544
+ // tag: '测试tag',
545
+ // number: 18617381945
546
+ // }]
547
+ // }
548
+ // })
549
+ // if (callInfo.code === 200) { // 正确响应
550
+ // alert('呼叫成功')
551
+ // } else {
552
+ // alert(callInfo.message)
553
+ // }
554
+ // await userAgentManager.invite({
555
+ // extension: call.value
556
+ // })
557
+ }
558
+
559
+ /**
560
+ * 切换坐席进入小休
561
+ */
562
+ const toggleNap = async () => {
563
+ const napInfo = await userAgentManager.toggleNap(true) // 👈 一键
564
+ if (napInfo.code === 200) { // 正确响应
565
+ nap.disabled = true
566
+ online.disabled = false
567
+ alert('进入小休成功')
568
+ } else {
569
+ alert(napInfo.message)
570
+ }
571
+ }
572
+
573
+ /**
574
+ * 切换坐席进入在线
575
+ */
576
+ const toggleOnline = async () => {
577
+ const napInfo = await userAgentManager.toggleNap(false) // 👈 一键
578
+ if (napInfo.code === 200) { // 正确响应
579
+ nap.disabled = false
580
+ online.disabled = true
581
+ alert('设置坐席在线成功')
582
+ } else {
583
+ alert(napInfo.message)
584
+ }
585
+ }
586
+
587
+ /**
588
+ * 环境检测
589
+ */
590
+ const envCheck = () => {
591
+ userAgentManager.softphoneEnvCheck() // 👈 一键检测,检测出结果后会回调在生成软电话实例时注册的钩子 refreshRequirementCheck
592
+ }
593
+
594
+ /** 各种操作按钮 注册事件开始 */
595
+ ignore.addEventListener('click', ignoreInvite) // 忽略
596
+ answer.addEventListener('click', acceptInvite) // 接听
597
+ hangUp.addEventListener('click', hangUpInvite) // 挂断
598
+ connectToServer.addEventListener('click', connect) // 签入
599
+ disconnect.addEventListener('click', disConnect) // 签出
600
+ sendStarDTMF.addEventListener('click', sendStarDtmf) // 转人工
601
+ localVoiceAccess.addEventListener('click', unMuteLocalAudio) // 打开本地声音发送
602
+ remoteSoundAccess.addEventListener('click', muteLocalAudio) // 关闭本地声音发送
603
+ localSoundDisconnection.addEventListener('click', unMuteRemoteAudio) // 打开远端声音接收
604
+ remoteSoundDisconnection.addEventListener('click', muteRemoteAudio) // 关闭远端声音接收
605
+ callButton.addEventListener('click', callPhone) // 呼叫
606
+ nap.addEventListener('click', toggleNap) // 切换坐席进入小休
607
+ online.addEventListener('click', toggleOnline) // 切换坐席进入在线
608
+ check.addEventListener('click', envCheck) // 执行软电话必备的权限&网络环境检测
609
+ /** 注册事件结束 */
610
+ })
611
+ </script>
612
+ </body>
613
+ </html>
@@ -201,13 +201,20 @@ document.addEventListener('DOMContentLoaded', async (event) => {
201
201
  getMedia
202
202
  } = softphone // 👈 sdk
203
203
 
204
- const extensionNumber = '3376' // 本地fs 分机账号
205
- const extPassword = 'zjh13542240708' // 本地fs 分机密码
204
+ const extensionNumber = '1000' // 本地fs 分机账号
205
+ const extPassword = '1234' // 本地fs 分机密码
206
206
  const wsProtocol = 'ws' // 本地fs 地址协议
207
- const wsRegisterAddress = '192.168.31.239:5066' // 本地fs 外呼服务器地址
207
+ const wsRegisterAddress = '192.168.59.197:5066' // 本地fs 外呼服务器地址
208
208
  const sipServerHost = `sip:${extensionNumber}@${wsRegisterAddress}` // fs地址
209
209
  const wsAddress = `${wsProtocol}://${wsRegisterAddress}` // socket地址
210
210
 
211
+ // const extensionNumber = '2839'
212
+ // const extPassword = 'zjh13542240708'
213
+ // const wsProtocol = 'ws'
214
+ // const wsRegisterAddress = '192.168.31.239:5066'
215
+ // const sipServerHost = `sip:${extensionNumber}@${wsRegisterAddress}`
216
+ // const wsAddress = `${wsProtocol}://${wsRegisterAddress}`
217
+
211
218
  const userAgentManager = UserAgentFactory.getUserAgentManager({
212
219
  authorizationPassword: extPassword, // 分机密码
213
220
  authorizationUsername: extensionNumber, // 分机账号