@94ai/softphone 5.0.5 → 5.0.7

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.
@@ -148,107 +148,7 @@
148
148
  <script src="./softphone.umd.min.js"></script>
149
149
 
150
150
  <script>
151
- // const pc = new RTCPeerConnection();
152
- // pc.createOffer({offerToReceiveAudio: true}).then(offer => console.log(offer.sdp, 'RTCPeerConnection supports sdp'))
153
-
154
- /**
155
- * 0.确保你的企业 【坐席外呼依赖】配置项 是【标识】 (默认是【登录】,如果不确定联系94运维在管理后台查看以及配置)
156
- * 1. 使用 【https://94ai.yuque.com/staff-kqoz0c/xed39g/vifnf1?singleDoc 《九四智能API开放文档(全)》 密码:wqhg】,该文档有疑问可以找 航航同学
157
- * - 3.1节 获取坐席信息
158
- * - 3.2节 修改坐席状态接口,保证坐席状态在线
159
- * tip: 该文档所有开放接口不支持跨域,如果直接通过ajax需要后端做转接(也可以直接用sdk创建的软电话实例的userAgentManager.requestOpenApi请求,该方法支持跨域同时自带请求签名)
160
- * - example1:呼叫,也可以直接使用`await userAgentManager.callNumber('15018707394')`
161
- * - 请求: http://openapi.94ai.com/v1/task/importAgentCustomer
162
- * - 相当于: userAgentManager.requestOpenApi({
163
- * url: '/v1/task/importAgentCustomer',
164
- * data: {
165
- * agentTag: 'xxx',
166
- * agentId: 'xxx',
167
- * callType: 1001,
168
- * customers: [{ number: 15018707394 }]
169
- * }
170
- * })
171
- * - example2:切换当前坐席小休,也可以直接使用`await userAgentManager.toggleNap(false)`
172
- * - 请求: http://openapi.94ai.com/v1/agent/updateAgentStatus
173
- * - 相当于: userAgentManager.requestOpenApi({
174
- * url: '/v1/agent/updateAgentStatus',
175
- * data: {
176
- * agentTag: 'xxx',
177
- * agentId: 'xxx',
178
- * agentStatus: 3, // 3: 小休 1: 在线
179
- * }
180
- * })
181
- * - example3:获取当前坐席信息,也可以直接使用`const angentInfo = await userAgentManager.getAgentInfo()`
182
- * - 请求: http://openapi.94ai.com/v1/agent/getAgent
183
- * - 相当于: userAgentManager.requestOpenApi({
184
- * url: '/v1/agent/getAgent',
185
- * data: {
186
- * agentTag: 'xxx',
187
- * agentId: 'xxx',
188
- * }
189
- * })
190
- * 2. 点击连接外呼服务器
191
- * 3. 在决策外呼一个任务,确保你的坐席账号在任务配置的坐席组里;或手动外呼一个号码(`await userAgentManager.callNumber('15018707394')`)
192
- * - tip 任务外呼:是先呼用户再呼坐席; 手动外呼:是先呼坐席在呼用户
193
- * 4. 电话过来后会触发签入注册好的onInvite钩子,这个时候就可以接听,挂断,忽略,转人工,静音等等
194
- * 5. 处理响应检测这块:
195
- * - 建议接听/挂断/忽略只做一次性操作(不管失败成功与否无法二次操作,这个sip.js的局限性有一定关系,比如执行一次挂断后sip层的所有状态就被重置了)
196
- * - 建议转人工可以做多次操作(没转成功 可以操作转多次)
197
- * - 通讯电话的大多实现也是这样,没有二次挂断/接听/忽略的操作。
198
- */
199
-
200
151
  document.addEventListener('DOMContentLoaded', async (event) => {
201
- // try {
202
- // await new Promise(((resolve, reject) => {
203
- // navigator.getUserMedia({video: true,audio:true}, function onSuccess(stream) {
204
- // resolve(stream)
205
- // }, function onError(error) {
206
- // reject(error)
207
- // });
208
- // }))
209
- // alert('已点击允许,开启成功');
210
- // } catch (error) {
211
- // alert("错误:" + error);
212
- // }
213
- // const inputList = []
214
- // const outputList = []
215
- // ;(await navigator.mediaDevices.enumerateDevices()).forEach(item => {
216
- // if (!item.label) return
217
- // if (item.kind === 'audioinput') inputList.push(JSON.parse(JSON.stringify(item)))
218
- // else if (item.kind === 'audiooutput') outputList.push(JSON.parse(JSON.stringify(item)))
219
- // });
220
- // alert('输入音频列表: ' + JSON.stringify(inputList))
221
- // alert('输出音频列表: ' + JSON.stringify(outputList))
222
-
223
- // 输入音频列表:
224
- // [
225
- // {"deviceld":"default",
226
- // "kind":"audioinput",
227
- // "label":"Default",
228
- // "groupld":"7a8ef9d53aa7c8f2e5494ea603b1a825696ecfd7b965d69eabbc7b36ca24fde6"
229
- // },
230
- // {
231
- // "deviceld":"dda9a37a56a1c8478824641c3218b30fab09035edccbe047c49f4b43cb273c3d",
232
- // "kind":"audioinput",
233
- // "label":"Speakerphone",
234
- // "groupld":"96c1a27879018de7478bbbd4ac10d6c754c827e2e49b8946ccceea95af5381a7"
235
- // },
236
- // {"deviceld":"577a863742550f83e9fffc15465083087ce67abbcd0d1a69ac8a2d7a469c6f56",
237
- // "kind":"audioinput",
238
- // "label":"Headsetearpiece",
239
- // "groupld": "af14e67d688dc0d4b40cb8162ce9794c3f9a0d15b5f5d34817292be3260f9127"
240
- // }
241
- // ]
242
-
243
- // 输出音频列表:
244
- // [
245
- // {
246
- // "deviceld":"default",
247
- // "kind":"audiooutput",
248
- // "label":"Default",
249
- // "groupld": "default"
250
- // }
251
- // ]
252
152
  const phoneRing = './antique_phone.mp3'
253
153
 
254
154
  /**
@@ -301,169 +201,10 @@ document.addEventListener('DOMContentLoaded', async (event) => {
301
201
  getMedia
302
202
  } = softphone // 👈 sdk
303
203
 
304
- /** 方式一:appKey + appSecret + agentTag 获取软电话链接代理实例 */
305
- // 以下信息采用九四系统的坐席信息接入外呼服务器实现软电话功能
306
- // agentTag: 'agent1111', // 测试线 坐席维度定位 或用agentId
307
- // appKey: 'ed96615038ffe394', // 测试线 企业维度定位
308
- // appSecret: '9fb4aac7b1dbfbb21696bcd7a30cb382', // 测试线 企业维度定位
309
-
310
- // const userAgentManager = UserAgentFactory.getUserAgentManager({
311
- // agentTag: '15018707394', // 生产线 坐席维度定位
312
- // appKey: '032d44009bff1752', // 生产线 企业维度定位
313
- // appSecret: '4c7304c94c5e8725613516d4d6db679b', // 生产线 企业维度定位 这里直接使用appSecret做演示
314
- // refreshSpeekVolumn(value) { // 当来电接通时,实时查看坐席说话的音量实际传送到给对方的音量大小
315
- // volumn.innerText = value
316
- // },
317
- // refreshRequirementCheck(value) { // 权限网络检测
318
- // browserSoftphoneEnv.innerText = JSON.stringify(value, null, 2)
319
- // },
320
- // refreshChat(info) { // 刷新会话记录
321
- // chatInfo.innerText = JSON.stringify(info, null, 2)
322
- // },
323
- // refreshChatErrorCallback(e) {
324
- // console.log(e)
325
- // },
326
- // enableChatInfoPush: true // 开启会话记录实时推送,开启后,当任务外呼时,可以实时获取通话记录信息,实时回调到refreshChat,中途出现任何异常会回调refreshChatErrorCallback
327
- // })
328
-
329
- /**
330
- * 1. 如果是海外环境新增这三个参数
331
- * sgOpen: '1',
332
- * sg: '1',
333
- * openBaseUrl: '1',
334
- * 2. 如果通过sign初始化,记得注册过期回调刷新sign
335
- * sign: 'xxx', // 代替appSecret
336
- * timestamp: 'xxx',
337
- * async signOverdued() {
338
- * return {
339
- * timestamp: 'www',
340
- * sign: 'qqq',
341
- * }
342
- * },
343
- * 3. sign是openApi授权
344
- * token是gatewayApi授权.gatewayApi授权用来获取来电详情,对话记录等信息,如果不需要可以不传
345
- * 获取token可以参考下方注释, 记得注册过期回调刷新token
346
- * token: 'xxx', // 代替appSecret
347
- * async tokenOverdued() {
348
- * return {
349
- * token: 'qqq',
350
- * }
351
- * },
352
- */
353
-
354
- /**
355
- * 获取token & 实时查到tag等自定义注入字段对接步骤:(海外:gateway.sg.94ai.com, 国内:gateway.94ai.com)
356
- * 1. sdk获取来电相关id
357
- * onInvite: (invitation: Invitation) => {
358
- * // invitation.incomingInviteRequest.message.headers['X-Numberid']?.[0].raw
359
- * // invitation.incomingInviteRequest.message.headers['X-Taskid']?.[0].raw
360
- * // 根据id查询tag或业务信息
361
- *
362
- * 客户后端对接94后提供给客户前端接口及时查询tag(后续其他自定义字段也一并在这里)
363
- * 2. post: https://gateway.sg.94ai.com/authority/accessToken/openApi 获取token
364
- * 参数:
365
- * corpId: this.#appKey,
366
- * sid: this.#agentId,
367
- * secret: this.#appSecret,
368
- * seatOnline: false
369
- * xhr.setRequestHeader('x-app-code', 3)
370
- * xhr.setRequestHeader("content-type", "application/json")
371
- *
372
- * 返回: {
373
- * "token": "a1YyMmFtdGRCM0FiY0QvVGd2dXg4OGIzTHNiK2pSM3BWeHZSbVQrSUhaVT0=", // gateway token ,也可以直接传给sdk,之后通过sdk内置可以直接获取来电详情和对话记录等信息
374
- * "userId": 11976,
375
- * "oldId": 8292,
376
- * "companyId": 1576,
377
- * "chainId": 0,
378
- * "orgId": 1576,
379
- * "userType": 4,
380
- * "account": "zoujh",
381
- * "userName": "zjh",
382
- * "defaultPassword": "",
383
- * "userTypeName": "坐席"
384
- * }
385
- *
386
- * 3. post: https://gateway.sg.94ai.com/task-aggre/callCenterNumber/get 获取来电详情
387
- * xhr.setRequestHeader('x-token', token)
388
- * xhr.setRequestHeader('seatsToken', token)
389
- * xhr.setRequestHeader('x-authority-token', token)
390
- * xhr.setRequestHeader('x-app-code', 3)
391
- * xhr.setRequestHeader("content-type", "application/json")
392
- * 参数:
393
- * id: numberId,
394
- * taskId: taskId
395
- *
396
- * 返回:
397
- * {
398
- * "status": 200,
399
- * "code": 200,
400
- * "message": "success",
401
- * "data": {
402
- * "tag": 'xxx', // 这是有相关自定义参数
403
- * "id": 703,
404
- * "number": "15018707394",
405
- * "numberMd5": "77c357c1660cb20b534be8d4ba02cd25",
406
- * "params": "{}", // 这是有相关自定义参数
407
- * "intentTag": "C",
408
- * "state": 10,
409
- * "description": "hangup",
410
- * "recycle": 3,
411
- * "callid": "431a6218-5d40-4312-90d7-bb63e0345774",
412
- * "bill": 4820,
413
- * "duration": 9360,
414
- * "bridgeNumber": "9288",
415
- * "status": "invalid answer",
416
- * "callTotal": 0,
417
- * "smsStatus": 0,
418
- * "repeatCount": 0,
419
- * "lineRepeatCount": 0,
420
- * "invalidRepeatCount": 0,
421
- * "totalRepeatCount": 0,
422
- * "createTime": 1730975822000,
423
- * "keywords": "",
424
- * "gatewayId": 4232,
425
- * "callLog": "[{\"callTime\":\"Nov 07, 2024 10:37:12 AM\",\"status\":\"answer\",\"statusName\":\"已接听\",\"gatewayId\":4232,\"gatewayName\":\"开发人员专用线路-邹家和-1\",\"callServer\":\"server24\"}]",
426
- * "extensionNumber": "9288",
427
- * "sid": 8292,
428
- * "aiBill": 4820,
429
- * "bridgeBill": 0,
430
- * "transferStatus": 20,
431
- * "companyId": 1576,
432
- * "callType": 1,
433
- * "answerTransferType": 2,
434
- * "statusCode": 20,
435
- * "batchId": "1669921261571",
436
- * "config": "{\"expNum\":\"94aikf\",\"whatsappStatus\":0,\"whatsappRead\":2,\"personality\":{},\"templateId\":25662,\"ringAlready\":1,\"originDa\":\"ringback\",\"billByHangup\":0,\"seatsGroupId\":1499}",
437
- * "alterTime": 1730975843000,
438
- * "provinceCode": "440000",
439
- * "cityCode": "440100",
440
- * "telecomOperator": "中国移动"
441
- * }
442
- * }
443
- */
444
-
445
- /** 方式二:extensionNumber + extPassword + wsProtocol + wsRegisterAddress 获取软电话链接代理实例 */
446
- // 即直接通过【分机号】和【分机密码】以及【外呼服务器地址】实现软电话的直接签入
447
- // 九四同学:测试环境不能访问openAPI,可以采用决策系统内部接口获取分机信息接入外呼服务器
448
- // 生产环境如果采用分机维度的分机信息可以通过agentTag调用openapi获取
449
- // const extensionNumber = '2839' // 测试线 分机账号
450
- // const extPassword = 'zjh13542240708' // 测试线 分机密码
451
- // const wsProtocol = 'ws' // 测试线外呼服务器地址协议
452
- // const wsRegisterAddress = '192.168.31.239:5066' // 测试线 外呼服务器地址
453
- // const sipServerHost = `sip:${extensionNumber}@${wsRegisterAddress}` // fs地址
454
- // const wsAddress = `${wsProtocol}://${wsRegisterAddress}` // socket地址
455
-
456
- // const extensionNumber = '2510' // 生产线 分机账号
457
- // const extPassword = 'zengrongzhi123' // 生产线 分机密码
458
- // const wsProtocol = 'ws' // 生产线 外呼服务器地址协议
459
- // const wsRegisterAddress = '192.168.31.239:5066' // 生产线 外呼服务器地址
460
- // const sipServerHost = `sip:${extensionNumber}@${wsRegisterAddress}` // fs地址
461
- // const wsAddress = `${wsProtocol}://${wsRegisterAddress}` // socket地址
462
-
463
- const extensionNumber = '1001' // 生产线 分机账号
464
- const extPassword = '946666' // 生产线 分机密码
465
- const wsProtocol = 'ws' // 生产线 外呼服务器地址协议
466
- const wsRegisterAddress = '192.168.0.92:5066' // 生产线 外呼服务器地址
204
+ const extensionNumber = '1000' // 本地fs 分机账号
205
+ const extPassword = '1234' // 本地fs 分机密码
206
+ const wsProtocol = 'ws' // 本地fs 地址协议
207
+ const wsRegisterAddress = '192.168.59.197:5066' // 本地fs 外呼服务器地址
467
208
  const sipServerHost = `sip:${extensionNumber}@${wsRegisterAddress}` // fs地址
468
209
  const wsAddress = `${wsProtocol}://${wsRegisterAddress}` // socket地址
469
210
 
@@ -477,54 +218,23 @@ document.addEventListener('DOMContentLoaded', async (event) => {
477
218
  logLevel: 'error', // 日志等级,默认error
478
219
  // viaHost, // fp.umd.min.js生成的唯一值,用来配查呼叫问题,可以不传,内部会自动生成
479
220
  contactName: extensionNumber, // sip协议要求,同分机账号即可,注意类型为string, 不传默认同分机账号
480
- /**
481
- * clusterMode
482
- * sdk默认值:
483
- * reClusterRegisterTimeout: 4000 注册超时1s,自动重新注册
484
- * reClusterConnectTimeout: 4000 再超时1s, 执行重连并注册
485
- * clusterMode: false // 默认false为关闭建群重签检测
486
- *
487
- * sass ai-sotphone & 组件库 默认值:
488
- * reClusterRegisterTimeout: 4000
489
- * reClusterConnectTimeout: 4000
490
- * clusterMode: true
491
- */
492
- // clusterMode: true, // 处理集群注册,某个节点注册服务器卡住不返回信令,自动执行重签到另外的节点
493
- // reClusterRegisterTimeout: 1000,
494
- // reClusterConnectTimeout: 1000,
495
- // refreshSpeekVolumn(value) { // 当来电接通时,实时查看坐席说话的音量实际传送到给对方的音量大小
496
- // volumn.innerText = value
497
- // },
498
- // refreshRequirementCheck(value) { // 权限网络检测
499
- // browserSoftphoneEnv.innerText = JSON.stringify(value, null, 2)
500
- // },
501
- // refreshChat(info) {
502
- // chatInfo.innerText = JSON.stringify(info, null, 2)
503
- // },
504
- // refreshChatErrorCallback(e) {
505
- // console.log(e)
506
- // },
507
- // enableChatInfoPush: true // 开启会话记录实时推送,开启后,当任务外呼时,可以实时获取通话记录信息,实时回调到refreshChat,中途出现任何异常会回调refreshChatErrorCallback
221
+ clusterMode: true,
222
+ reClusterRegisterTimeout: 1000,
223
+ reClusterConnectTimeout: 1000,
224
+ sessionDescriptionHandlerFactoryOptions: { // 如果fs开启b2b没模式不需要传,开启p2p模式要传
225
+ iceGatheringTimeout: 2000,
226
+ peerConnectionConfiguration: {
227
+ iceServers: [{ urls: "stun:stun.l.google.com:19302" }], // stun:stun.freeswitch.org
228
+ }
229
+ },
230
+ refreshSpeekVolumn(value) { // 当来电接通时,实时查看坐席说话的音量实际传送到给对方的音量大小
231
+ volumn.innerText = value
232
+ },
233
+ refreshRequirementCheck(value) { // 权限网络检测
234
+ browserSoftphoneEnv.innerText = JSON.stringify(value, null, 2)
235
+ },
508
236
  })
509
237
 
510
- /**
511
- * 获取坐席当前状态信息(注意牵扯到openAPI的信息需要angentId,即方式一。如果通过方式二需要手动使用userAgentManager.requestOpenApi获取,即上述example3
512
- */
513
- // const angentInfo = await userAgentManager.getAgentInfo()
514
- // if (angentInfo.code === 200) { // 正确响应
515
- // info.innerText = `1. ${JSON.stringify(angentInfo.data, null, 2)}`
516
- // if (angentInfo.data.agentStatus === 1) { // 页面按钮的禁用状态
517
- // nap.disabled = false
518
- // online.disabled = true // 可切换在线
519
- // } else {
520
- // nap.disabled = true // 可切换小休
521
- // online.disabled = false
522
- // }
523
- // } else {
524
- // alert(angentInfo.message)
525
- // throw new Error(angentInfo)
526
- // }
527
-
528
238
  /**
529
239
  * 签入
530
240
  */
@@ -806,12 +516,15 @@ document.addEventListener('DOMContentLoaded', async (event) => {
806
516
  * 呼叫
807
517
  */
808
518
  const callPhone = async () => {
809
- const callInfo = await userAgentManager.callNumber(call.value) // 👈 一键
810
- if (callInfo.code === 200) { // 正确响应
811
- alert('呼叫成功')
812
- } else {
813
- alert(callInfo.message)
814
- }
519
+ // const callInfo = await userAgentManager.callNumber() // 👈 一键
520
+ // if (callInfo.code === 200) { // 正确响应
521
+ // alert('呼叫成功')
522
+ // } else {
523
+ // alert(callInfo.message)
524
+ // }
525
+ await userAgentManager.invite({
526
+ extension: call.value
527
+ })
815
528
  }
816
529
 
817
530
  /**