5.0.8 • Published 8 months ago

@94ai/softphone v5.0.8

Weekly downloads
-
License
ISC
Repository
-
Last release
8 months ago

前言

94ai软电话前端SDK,框架无关(支持vue,react,angular等)

项目参考demo: https://gitee.com/softphone-demo(有权限问题,具体找94智能开发同事拉入团队组织即可)

本地调试: yarn add sip.js@portal:../SIP修改v2.js

安装

94智能sdk目前暂不在公网公布sdk源码,如有需要,可以联系94智能开发同事加入团队,具体步骤:

获取加入团队邀请链接

  1. 向94智能开发同事申请到【加入到团队的连接】,如: https://account-devops.aliyun.com/account/invite?sign=ab965d478dbed5dad3cc145a5a5b406c&next_url=https%3A%2F%2Fpackages.aliyun.com%3ForgId%3D644f755a97d94d909e43534c

  2. 进入上述链接后,进到阿里云登录页

  • 如果你是主账号,直接登录即可:

    img.png

  • 如果你是RAM子账号,选择下面RAM用户登录:

    img_1.png

    进入RAM 用户登录页登录即可:

    img_2.png

阿里的数字安全码

登录过程会提示你填写 验证虚拟MFA设备数字安全码

1.如果以前使用过阿里云产品并绑定过

可以直接在【阿里云app】或使用【虚拟MFA验证小程序】获取【数字安全码】填写即可,如下:

  • 下载阿里云app

1.jpg

  • 定位首页的mfa图标

2.jpg

  • 找到对应账号的数字安全码

3.jpg

2.如果以前没有绑定过

  • 如下面选择 虚拟 MFA 设备

    img_5.png

  • 然后在手机安装【阿里云app】或使用【小程序虚拟MFA验证】,然后扫码绑定设备

    img_6.png

  • 在设备添加账号点确定

    img_7.png

  • 之后会每隔一小段时间会刷新获取到最新的【数字安全码】

    img_18.png

3.如果使用小程序

在微信搜索mfa二次验证如下,具体可以百度下:

20230501-205150.jpg

4.设置昵称申请加入

登录成功后会提示你加入团队,设置号昵称点加入

img_4.png

img_7.png

查看个人账号信息

等待审核通过后,刷新页面,点击进入企业

img_8.png

1.选择角色

选择作为研发者,开始工作

img_9.png

2.进入制品仓库

在工作台选择【制品仓库】进入

Snipaste_2023-05-01_21-22-33.png

3.查看账号密码

选择设置查看个人账号信息

Snipaste_2023-05-01_21-24-27.png

Snipaste_2023-05-01_21-26-20.png

设置私库源

1.新建rc文件

在需要使用94智能sdk的项目根目录,新建一个rc文件:

  • 如果你使用的npm或pnpm,新建.npmrc,内容如下:
@94ai:registry=https://packages.aliyun.com/644f755a97d94d909e43534c/npm/npm-registry/
//packages.aliyun.com/644f755a97d94d909e43534c/npm/npm-registry/:username=username
//packages.aliyun.com/644f755a97d94d909e43534c/npm/npm-registry/:_password=password
//packages.aliyun.com/644f755a97d94d909e43534c/npm/npm-registry/:always-auth=true
  • 如果你使用的yarn1,新建.yarnrc,内容如下:
"@94ai:registry" "https://packages.aliyun.com/644f755a97d94d909e43534c/npm/npm-registry/"
"//packages.aliyun.com/644f755a97d94d909e43534c/npm/npm-registry/:username" "username"
"//packages.aliyun.com/644f755a97d94d909e43534c/npm/npm-registry/:_password" "password"
"//packages.aliyun.com/644f755a97d94d909e43534c/npm/npm-registry/:always-auth" true
  • 如果你使用的yarn2~3,新建.yarnrc.yml,内容如下:
enableImmutableInstalls: false
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-3.1.1.cjs
#npmRegistryServer: "https://nexusdev.k8s.94ai.pro/repository/npm-group/"
npmScopes:
  94ai:
    npmRegistryServer: "https://packages.aliyun.com/644f755a97d94d909e43534c/npm/npm-registry/"
    npmAlwaysAuth: true
    npmAuthIdent: "username:password"
#unsafeHttpWhitelist:
#  - 192.168.20.9

2.配置个人账号信息

  1. 把对应rc文件username替换成你的账号名称

  2. 如果你使用的是yarn1,npm或pnpm,则把你的账号密码做base64加密之后替换掉对应rc文件password,如在这里在线base64加密获取base64加密的密码

img_10.png

  1. 如果你使用的是yarn2~3,则把你的账号密码直接替换掉对应rc文件password

3.安装依赖

之后该项目只要是@94ai域的私包都会经过94智能私服抓取

其他包【包括@94ai域私包的子包(非@94ai域)】都会经由个人电脑配置的npm包registry源抓取

执行install命令即可成功安装@94ai域的所有私包,包括sdk,如

$ yarn add @94ai/softphone
# or
$ npm i @94ai/softphone
# or
$ pnpm add @94ai/softphone

使用

初始化

1.作为NPM包使用

// Example:
// 1.softphone.ts
import { UserAgent, UserAgentManager, URI } from '@94ai/softphone'
import FingerprintJS from '@fingerprintjs/fingerprintjs'
import useToast from '@/utils/useToast'

export function createRandomToken(size, base = 32) {
  let token = "";
  for (let i = 0; i < size; i++) {
    const r = Math.floor(Math.random() * base);
    token += r.toString(base);
  }
  return token;
}

// transport层配置
export interface TransportOptions {
  /** websocket协商地址, server和wsServers 必须二选一*/
  server?: string
  /** 多个地址开启负载均衡模式 */
  wsServers?: string | string[] | {
    /** websocket协商地址 */
    ws_uri: string,
    /** 权重 */
    weight: number
  }[]
  /**
   * websocke初始化连接等待超时时间
   * @default 5
   */
  connectionTimeout?: number
  /**
   * transport层客户端保活最大重连尝试次数
   * @default 3
   */
  maxReconnectionAttempts?: number
  /**
   * transport层客户端保活重连动作执行间隔时间,单位秒,同UserAgentOptions.reconnectionDelay
   * @default 4
   */
  reconnectionTimeout?: number
  /**
   * transport层The time (Number) in seconds to wait in between CLRF keepAlive sequences are sent.
   * @default 0
   */
  keepAliveInterval?: number
  /**
   * transport层The time (Number) in seconds to debounce sending CLRF keepAlive sequences by
   * @default 10
   */
  keepAliveDebounce?: number
  /**
   * transport层If true, messages sent and received by the transport are logged.
   * @default false
   */
  traceSip?: boolean
}
// sip层配置
export interface UserAgentOptions {
  /**
   * 通过openApi获取坐席账号分机密码
   * @default ''
   */
  authorizationPassword: string,
  /**
   * 通过openApi获取坐席账号分机用户名
   * @default ''
   */
  authorizationUsername: string,
  /**
   * 指纹,唯一标志,用来排查线路故障,默认随机指纹,可选
   * @default createRandomToken(12) + ".invalid"
   */
  viaHost?: string,
  /**
   * sip服务地址,必填,需要服务可达的地址
   * @default new URI("sip", "anonymous." + createRandomToken(6), "anonymous.invalid") })
   */
  uri: URI,
  /**
   * sip日志查看等级,一般情况下生产开error,开发用debugger
   * @default 'log'
   */
  logLevel?: 'debug' | 'log' | 'warn' | 'error',
  /**
   * 一般设置同authorizationUsername,相当于MicroSIP的显示名称
   * @default createRandomToken(8)
   */
  contactName?: string
  /**
   * 签入来电后多长时间不执行接听会话自动结束会话,单位秒
   * @default 60
   */
  noAnswerTimeout?: number,
  /**
   * transport层协议配置
   */
  transportOptions: TransportOptions,
  /**
   * sip层 - 重连尝试间隔,为了兼容sipjs,同reconnectionInterval
   * @default 3
   */
  reconnectionDelay?: number,
  /**
   * sip层 - 重连尝试间隔,单位秒
   * @default 3
   */
  reconnectionInterval?: number,
  /**
   * sip层 - 重连失败最大尝试次数
   * @default 100
   */
  reconnectionAttempts?: number,
  /**
   * sip层 - 重连成功后尝试重新注册检间隔,单位秒
   * @default 3
   */
  registerInterval?: number,
  /**
   * sip层 - 重连成功最大尝试注册次数
   * @default 3
   */
  registerAttempts?: number
  /**
   * sip层 - ping动作间隔,单位秒
   * @default 8
   */
  optionsPingInterval?: number
  /**
   * sip层 - ping最大失败尝试次数后开始重连,防止网络抖动引起非必要重连
   * @default 3
   */
  optionsPingAttempts?: number
  /**
   * sip层 - 自定义通讯header
   */
  sipHeaders?: Array<string>;
}

export class SoftphoneManager {
  static #getDeviceId: () => Promise<string>
  #userAgentManager: InstanceType<typeof UserAgentManager>

  static {
    /**
     * 获取指纹
     */
    SoftphoneManager.#getDeviceId = (() => {
      let visitorId = ''
      return async (): Promise<string> => {
        if (visitorId) return visitorId
        const fp = await FingerprintJS.load()
        const result = await fp.get()
        visitorId = result.visitorId
        return visitorId
      }
    })()
  }
  public async getUserAgentManager = () => {
    if (!this.#userAgentManager) {
      this.#userAgentManager = UserAgentFactory.getUserAgentManager(await this.initSeatsInfo()) // 👈 得到softphone代理对象
    }
    return this.#userAgentManager
  }
  public getUserAgentManagerSync = (config?: UserAgentOptions) => {
    if (!this.#userAgentManager) {
      this.#userAgentManager = UserAgentFactory.getUserAgentManager(config) // 👈 得到softphone代理对象
    }
    return this.#userAgentManager
  }
  /**
   * 通过openApi获取分机信息
   */
  public async initSeatsInfo = (): Promise<UserAgentOptions> => {
    const result = await getSeatsInfo()
    if (result.code === 200) {
      const {
        extPassword, // 分机密码
        extensionNumber, // 分机号
        wsRegisterAddress, // socket地址
        wsProtocol // socket协议
      } = result.data
      return {
        authorizationPassword: extPassword,
        authorizationUsername: extensionNumber,
        viaHost: `${await SoftphoneManager.#getDeviceId()}.sip`,
        uri: UserAgent.makeURI(`sip:${extensionNumber}@${wsRegisterAddress}`),
        logLevel: 'error',
        transportOptions: {
          server: `${wsProtocol}://${wsRegisterAddress}`
        },
        contactName: extensionNumber
      }
    }
    useToast.showToast('获取分机信息失败')
    throw new Error('获取分机信息失败')
  }
  /**
   * 签入
   */
  public connect = async () => {
    try {
      await this.getUserAgentManagerSync().prepareUserAgent(
        /** config */
        {
          /** 当软电话状态变化时会实时刷新这个方法 */
          refresh: (path: keyof typeof userAgentStatusDefault, value: boolean) => {

          }
        },
        /** event */
        {
          /** 当有外呼过来 */
          onInvite: async (invitation: Invitation) => {

          }
        })
    } catch (e) {
      const errorInfo = e as Error
      if (errorInfo.message === 'sip register fail with code 503' ||
              errorInfo.message.indexOf('WebSocket closed') > -1 && errorInfo.message.indexOf('code: 1006') > -1) {
        useToast.showToast('软电话服务器异常,签入失败')
      } else {
        useToast.showToast(errorInfo.message)
      }
      throw e
    }
  }
  // ...
}
export default new SoftphoneManager()

// 2.App.vue
import commonSoftphone from './softphone.ts'
onBeforeMount(async () => {
  await commonSoftphone.getUserAgentManager()
})

// 3. softphone.vue
// <button @click="connect">签入</button>
import commonSoftphone from './softphone.ts'
// 签入
const connect = async () => {
  await commonSoftphone.connect()
}

2.直接在JavaScript中使用

  1. 获取umd文件
  • 确保安装了node,随手新建一个目录,打开cmd或bash。
  • 设置@4ai域账号授权,依次执行执行如下,其中:username=xxxx替换你的账号名字,:_password=xxxx替换你的账号密码在做base64加密后的结果
npm config set @94ai:registry=https://packages.aliyun.com/644f755a97d94d909e43534c/npm/npm-registry/
npm config set //packages.aliyun.com/644f755a97d94d909e43534c/npm/npm-registry/:username=xxx
npm config set //packages.aliyun.com/644f755a97d94d909e43534c/npm/npm-registry/:_password=xxx
npm config set //packages.aliyun.com/644f755a97d94d909e43534c/npm/npm-registry/:always-auth=true
  • 最后执行npx -y @94ai/softphone -- sdk-umd,之后在当前执行命令路径下即可获取到softphone.umd.min.js
  1. 在index.html引入softphone.umd.min.js文件
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
  <title>softphone</title>
  <style>
  #softphone-app {
    text-align: center;
    padding-top: 50px;
  }
  </style>
</head>
<body>
<div id="softphone-app">
  <button id="connectToServer">连接服务器</button>
  <br>
  <br>
  <button id="disconnect" disabled>断开连接服务器</button>
  <br>
  <br>
  <button id="ignore" disabled>忽略</button>
  <br>
  <br>
  <button id="answer" disabled>接听</button>
  <br>
  <br>
  <button id="hangUp" disabled>挂断</button>
  <br>
  <br>
  <button id="press" disabled>按*转人工</button>
  <br>
  <br>
  <button id="localVoiceAccess" disabled>本地声音接入</button>
  <br>
  <br>
  <button id="remoteSoundAccess" disabled>远程声音接入</button>
  <br>
  <br>
  <button id="localSoundDisconnection" disabled>本地声音断开</button>
  <br>
  <br>
  <button id="remoteSoundDisconnection" disabled>远程声音断开</button>
  <br>
  <br>
  <audio
          id="remoteAudio"
          controls
          style="display:none"
  >
    <p>Your browser doesn't support HTML5 audio - remoteAudio</p>
  </audio>
  <audio
          id="localAudio"
          style="display:none"
          src="./antique_phone.mp3"
          controls
          loop
  >
    <p>Your browser doesn't support HTML5 audio - localAudio</p>
  </audio>
</div>
<script src="../lib/softphone.umd.min.js"></script>
<script src="./fp.umd.min.js"></script>
<script>
/**
 *  1.确保你的企业 【坐席外呼依赖】配置项 是【标识】 (默认是【登录】,如果不确定联系94运维在管理后台查看以及配置)
 *  2. 使用 【https://94ai.yuque.com/staff-kqoz0c/xed39g/obnqst?singleDoc# 《SG-九四智能API开放文档(全)》 密码:tvgm】 3.1 获取坐席信息,以及3.2 修改坐席状态接口,保证坐席状态在线
 *  3. 点击连接服务器
 *  4. 在决策外呼一个任务,确保你的坐席账号在任务配置的坐席组里
 *  5. 电话过来后会触发签入注册好的onInvite,这个时候就可以接听,挂断,忽略,转人工,静音等等
 *  6. 《SG-九四智能API开放文档(全)》该文档获取授权调用接口的问题找 航航同学
 */

const getDeviceId = (() => {
  let visitorId = ''
  return async () => {
    if (visitorId) return visitorId
    const fp = await FingerprintJS.load()
    const result = await fp.get()
    visitorId = result.visitorId
    return visitorId
  }
})()
document.addEventListener('DOMContentLoaded', async (event) => {
  const phoneRing = './antique_phone.mp3' // 👈 铃声自己当以一个音频即可
  const ignore = document.getElementById('ignore')
  const answer = document.getElementById('answer')
  const hangUp = document.getElementById('hangUp')
  const connectToServer = document.getElementById('connectToServer')
  const disconnect = document.getElementById('disconnect')
  const press = document.getElementById('press')
  const localVoiceAccess = document.getElementById('localVoiceAccess')
  const remoteSoundAccess = document.getElementById('remoteSoundAccess')
  const localSoundDisconnection = document.getElementById('localSoundDisconnection')
  const remoteSoundDisconnection = document.getElementById('remoteSoundDisconnection')
  const localAudio = document.getElementById('localAudio')
  const remoteAudio = document.getElementById('remoteAudio')
  const viaHost = `${await getDeviceId()}.sip`
  /** 接口获取开始 */
  /** 通过接口获取  https://94ai.yuque.com/staff-kqoz0c/xed39g/obnqst?singleDoc# 《SG-九四智能API开放文档(全)》 密码:tvgm 👈 不知道怎么调接口找 航航同学 */
  /** 3.1坐席相关接口查询坐席信息 */
  const extensionNumber = '9288'
  const extPassword = 'zjh13542240708'
  const wsProtocol = 'wss'
  const wsRegisterAddress = 's28.94ai.com:7443'
  const sipServerHost = `sip:${extensionNumber}@${wsRegisterAddress}`
  const wsAddress = `${wsProtocol}://${wsRegisterAddress}`
  /** 3.2修改坐席账号状态 */
  const request = {
    post(url) {
      console.log('写你的调用的openapi改坐席状态,要写好它')
    }
  }
  const editSeats = async (params) => {
    return request.post('/seats/editLineStatus')
  }
  await editSeats({ lineStatus: 1 })
  /** 接口获取结束 */

  const {
    UserAgentFactory,
    UserAgent,
    playMedia,
    pauseMedia,
    SessionState,
    getMedia
  } = softphone // 👈 sdk

  /**
   * 开启电话响铃
   */
  const phoneRings = () => {
    remoteAudio.src = phoneRing
    playMedia('localAudio')
  }

  /**
   * 关闭电话响铃
   */
  const stopPhoneRings = () => {
    remoteAudio.src = ''
    pauseMedia('localAudio')
  }

  /**
   * 获取软电话代理实例
   */
  const userAgentManager = UserAgentFactory.getUserAgentManager({
    authorizationPassword: extPassword,
    authorizationUsername: extensionNumber,
    viaHost,
    uri: UserAgent.makeURI(sipServerHost),
    logLevel: 'error',
    transportOptions: {
      server: wsAddress
    },
    contactName: extensionNumber
  })

  /**
   * 签入
   */
  const connect = () => {
    userAgentManager.prepareUserAgent(
            { // config
              refresh (path, value) { // 当软电话状态变化时会实时刷新这个方法
                console.log(path, value)
              }
            },
            { // event
              onInvite (invitation) { // 当有外呼过来
                syncSipMessage(invitation.incomingInviteRequest.message.headers)
                phoneRings() // 播放模拟来电响铃
                ignore.disabled = false
                answer.disabled = false
                localSoundDisconnection.disabled = false
                remoteSoundDisconnection.disabled = false
                invitation.stateChange.addListener((state) => { // 一旦接听(执行accept),监听会话的生命周期
                  switch (state) {
                    case SessionState.Initial:
                      break
                    case SessionState.Establishing:
                      break
                    case SessionState.Established: // session建立后就可拿到webrtc的各种基础api,如userAgentManager.getPeerConnection(),如userAgentManager.getSenders()等等
                      const mediaElement1 = getMedia('remoteAudio')
                      mediaElement1.srcObject = userAgentManager.getStream() // 获取流
                      mediaElement1.play() // 把softphone流导入到audio接入用户语音
                      hangUp.disabled = false
                      break
                    case SessionState.Terminating:
                    case SessionState.Terminated: // 在挂断电话时候会执行
                      ignore.disabled = true
                      answer.disabled = true
                      hangUp.disabled = true
                      localSoundDisconnection.disabled = true
                      remoteSoundDisconnection.disabled = true
                      localVoiceAccess.disabled = true
                      remoteSoundAccess.disabled = true
                      const mediaElement2 = getMedia('remoteAudio') // 获取audio dom
                      mediaElement2.srcObject = null
                      mediaElement2.pause() // 释放audio
                      stopPhoneRings()
                      break
                    default:
                      throw new Error('Unknown session state.')
                  }
                })
              }
            }
    )
    disconnect.disabled = false
    connectToServer.disabled = true
    ignore.disabled = true
    answer.disabled = true
    hangUp.disabled = true
    localSoundDisconnection.disabled = true
    remoteSoundDisconnection.disabled = true
    localVoiceAccess.disabled = true
    remoteSoundAccess.disabled = true
  }

  /**
   * 签出
   */
  const disConnect = () => {
    userAgentManager.dispose() // 👈 一个方法安全销毁
    connectToServer.disabled = false
    disconnect.disabled = true
    ignore.disabled = true
    answer.disabled = true
    hangUp.disabled = true
    localSoundDisconnection.disabled = true
    remoteSoundDisconnection.disabled = true
    localVoiceAccess.disabled = true
    remoteSoundAccess.disabled = true
  }

  /**
   * 先监听后接听
   */
  let monitorFirstAnswerAfter = true // 先监听任务需要 按*号转人工 才可以让用户听到坐席声音
  /**
   * sip协议通讯
   */
  const syncSipMessage = (headers) => {
    monitorFirstAnswerAfter = !(String(headers['X-Aftertransferlabour'] && headers['X-Aftertransferlabour'][0].raw) === '1')
  }

  /**
   * 按*号转人工
   */

  const sendStarDtmf = () => {
    if (monitorFirstAnswerAfter) {
      userAgentManager.sendStarDtmf()
      press.disabled = true
    }
  }

  /**
   * 挂断
   */
  const hangUpInvite = () => {
    stopPhoneRings() // 暂停来电铃声
    userAgentManager.hangUpInvite() // 👈
    ignore.disabled = true
    answer.disabled = true
    hangUp.disabled = true
    localSoundDisconnection.disabled = true
    remoteSoundDisconnection.disabled = true
    localVoiceAccess.disabled = true
    remoteSoundAccess.disabled = true
  }
  /**
   * 忽略
   */
  const ignoreInvite = () => {
    stopPhoneRings() // 暂停来电铃声
    userAgentManager.ignoreInvite()
    ignore.disabled = true
    answer.disabled = true
    hangUp.disabled = true
    localSoundDisconnection.disabled = true
    remoteSoundDisconnection.disabled = true
    localVoiceAccess.disabled = true
    remoteSoundAccess.disabled = true
  }
  /**
   * 接听
   */
  const acceptInvite = () => {
    stopPhoneRings() // 暂停来电铃声
    userAgentManager.acceptInvite()
    if (monitorFirstAnswerAfter) {
      press.disabled = false
    }
    hangUp.disabled = false
    answer.disabled = true
    ignore.disabled = true
  }

  /**
   * 传输本地声音到远端
   */
  const unMuteLocalAudio = () => {
    localVoiceAccess.disabled = true
    localSoundDisconnection.disabled = false
    userAgentManager.unMuteLocalAudio()
  }
  /**
   * 不传输本地声音到远端
   */
  const muteLocalAudio = () => {
    remoteSoundAccess.disabled = true
    remoteSoundDisconnection.disabled = false
    userAgentManager.muteLocalAudio()
  }
  /**
   * 接听远端声音
   */
  const unMuteRemoteAudio = () => {
    localSoundDisconnection.disabled = true
    localVoiceAccess.disabled = false
    userAgentManager.unMuteRemoteAudio()
  }
  /**
   * 不接听远端声音
   */
  const muteRemoteAudio = () => {
    remoteSoundDisconnection.disabled = true
    remoteSoundAccess.disabled = false
    userAgentManager.muteRemoteAudio()
  }

  /** 注册事件开始 */
  ignore.addEventListener('click', ignoreInvite)
  answer.addEventListener('click', acceptInvite)
  hangUp.addEventListener('click', hangUpInvite)
  connectToServer.addEventListener('click', connect)
  disconnect.addEventListener('click', disConnect)
  press.addEventListener('click', sendStarDtmf)
  localVoiceAccess.addEventListener('click', unMuteLocalAudio)
  remoteSoundAccess.addEventListener('click', muteLocalAudio)
  localSoundDisconnection.addEventListener('click', unMuteRemoteAudio)
  remoteSoundDisconnection.addEventListener('click', muteRemoteAudio)
  /** 注册事件结束 */
})


</script>
</body>
</html>

  1. tip 获取fingerprintjs: https://unpkg.com/@fingerprintjs/fingerprintjs@3.4.1/dist/fp.umd.min.js
  2. const phoneRing = './antique_phone.mp3' // 👈 铃声自己当以一个音频即可
  3. ￳直接双击打开的html的demo打电话这个模拟不了线上域名部署环境可能出现的问题,可以实现看各种接听挂断签入转人工等效果,file协议和localhost默认在浏览器安全域名内。注意线上环境浏览器是有限制要https协议的,http是使用不了浏览器webrtc的api的,要配置下浏览器安全上下文域名白名单

签入

import { playMedia, getMedia, SessionState } from '@94ai/softphone'
import { ref } from 'vue'
import popNotice from './usePopNotice.ts'  // 通知工具,见下面demo说明

const userAgentStatus = ref({ // 视图层响应式
  connectStatus: false, // 软电话是否已签入
  registerStatus: false, // 软电话是否已注册
  invitatingStatus: false, // 软电话是否正拨出
  incomingStatus: false, // 软电话是否正来电
  answerStatus: false, // 软电话是否正接听
  reconnectStatus: false, // 软电话是否正重连
})

userAgentManager.prepareUserAgent(
        { // config
          refresh(path, value) { // 当软电话状态变化时会实时刷新这个方法
            userAgentStatus[path] = value // 外部想要响应状态可以实时更新外部的userAgentStatus
          }
        },
        { // event
          onInvite(invitation) { // 当有外呼过来
            playMedia('localAudio') // 播放模拟来电响铃
            popNotice.popNotification('来电了') // 提示通知   来电了
            invitation.stateChange.addListener((state) => { // 一旦接听(执行accept),监听会话的生命周期
              switch (state) {
                case SessionState.Initial:
                  break
                case SessionState.Establishing:
                  break
                case SessionState.Established: // session建立后就可拿到webrtc的各种基础api,如userAgentManager.getPeerConnection(),如userAgentManager.getSenders()等等
                  const mediaElement1 = getMedia('remoteAudio')
                  mediaElement1.srcObject = userAgentManager.getStream() // 获取流
                  mediaElement1.play() // 把softphone流导入到audio接入用户语音
                  break
                case SessionState.Terminating:
                case SessionState.Terminated: // 在挂断电话时候会执行
                  const mediaElement2 = getMedia('remoteAudio') // 获取audio dom
                  mediaElement2.srcObject = null
                  mediaElement2.pause() // 释放audio
                  break
                default:
                  throw new Error('Unknown session state.')
              }
            })
          }
        })

接听

import { refreshShowTime, pauseMedia } from '@94ai/softphone'

const acceptInvite = () => {
  refreshShowTime() // 重新计算通话时长(非必须)
  pauseMedia('localAudio') // 暂停来电铃声(非必须)
  userAgentManager.acceptInvite() // 👈 接入电话流
}

忽略

import { pauseMedia } from '@94ai/softphone'

const ignoreInvite = () => {
  pauseMedia('localAudio') // 暂停来电铃声(非必须)
  userAgentManager.ignoreInvite() // 👈 忽略
}

挂断

import { refreshShowTime } from '@94ai/softphone'

const hangUpInvite = () => {
  refreshShowTime() // 重新计算通话时长(非必须)
  userAgentManager.hangUpInvite() // 👈 挂断
}

按*号转人工

import { refreshShowTime } from '@94ai/softphone'

const sendStarDtmf = () => {
  refreshShowTime() // 重新计算通话时长(非必须)
  userAgentManager.sendStarDtmf() // 👈 按*号转人工
}

销毁

import { refreshShowTime } from '@94ai/softphone'

const disconnect = () => {
  refreshShowTime() // 重新计算通话时长(非必须)
  userAgentManager.dispose() // 👈 一个方法安全销毁
}

传输本地声音到远端

const unMuteLocalAudio = () => {
  userAgentManager.unMuteLocalAudio() // 👈 通过webrtc的方式控制 本地音频流 推送到 远端
}

不传输本地声音到远端

const muteLocalAudio = () => {
  userAgentManager.muteLocalAudio() // 👈 通过webrtc的方式控制 本地音频流 禁用推送
}

接听远端声音

const unMuteRemoteAudio = () => {
  userAgentManager.unMuteRemoteAudio() // 👈 通过webrtc的方式控制 是否接受 远端音频流
} 

不接听远端声音

const muteRemoteAudio = () => {
  userAgentManager.muteRemoteAudio() // 👈 通过webrtc的方式控制 是否接受 远端音频流
}

获取webrtc基础api

const senders = userAgentManager.getSenders()
const receivers = userAgentManager.getReceivers()
const peerConnection = userAgentManager.getPeerConnection()

获取代理用户相关sip协议通讯实例

const userAgent = userAgentManager.getUserAgent()
const sessionDescriptionHandler = userAgentManager.getSessionDescriptionHandler()
const currentInvitation = userAgentManager.getCurrentInvitation()
const currentInviter = userAgentManager.getCurrentInviter()
const localMixedMediaStream = userAgentManager.getStream()

查看代理用户目前状态

interface userAgentStatus {
  connectStatus: boolean, // 软电话是否已签入
  registerStatus: boolean, // 软电话是否已注册
  invitatingStatus: boolean, // 软电话是否正拨出
  incomingStatus: boolean, // 软电话是否正来电
  answerStatus: boolean, // 软电话是否正接听
  reconnectStatus: boolean, // 软电话是否正在重连,网络断掉,服务器重启,宕机等引起服务不可达时软电话代理会尝试重新连接并签入。这个时候用户拨出等动作可以通过此状态做拦截,通知用户软电话服务器可能正重启或断网等导致服务不可用
}
const userAgentStatus: userAgentStatus = userAgentManager.getUserAgentStatue()

软电话状态可以根据企业特定业务流程定制扩展额外的状态,如94智能决策系统 额外扩展了 【监听中】,【整理中】,【小休中】等状态

5.0.8

8 months ago

5.0.7

8 months ago

5.0.6

11 months ago

5.0.5

11 months ago

5.0.4

11 months ago

5.0.3

11 months ago

5.0.2

11 months ago

5.0.1

1 year ago

4.0.1

1 year ago

4.0.0

1 year ago

3.0.10

1 year ago

1.0.10

3 years ago

1.0.9

3 years ago

1.0.8

3 years ago

1.0.7

3 years ago

1.0.6

3 years ago

1.0.5

3 years ago

1.0.4

3 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago