Skip to content

DkCloud SDK 对接文档

1. 引言

1.1 编写目的

为开发人员提供一个规范化的接口协议,帮助客户系统平台与点控云 DK-Cloud 平台的集成操作。

1.2 背景

本协议适用于点控云-DK-Cloud 平台与第三方的基于 B/S 架构的系统间的行为、数据及事件的交互与传递。本协议承载于 WebSocket 协议,严格遵守 WebSocket 协议规范。

1.3 参考资料

  • RFC2616 规范
  • HTML5
  • HTTPS 协议
  • WebSocket 协议
  • SIP 协议

2. 概述

企业自己有 CRM 业务系统,业务系统一般为 B/S 架构,在客户来电时需要在自己的业务系统中进行处理来电并进行业务记录,也可以在自己的业务系统中发起外呼,同时企业希望以自己的业务系统为主界面,在自己的业务系统中进行全部操作,点控云呼叫中心系统通过 jsSDK 方式进行对接,从而实现与企业的业务系统的互操作。

3. 规范

严格按照小驼峰定义规范,例如:agentNumber:座席工号。

4. SDK 概述

点控云 Dkcloud SDK 2.X 版本中定义了一个构造函数DKY,座席和分机的所有操作都是调用该构造函数的内部方法,并通过监听内部的事件系统获取到对应操作的回调数据。

javascript
const dky = new DKY();

5. 版本说明

版本号发布时间备注信息修订人
2.1.02024-03-22马凯特
2.1.12024-04-11马凯特
2.1.22024-05-23马凯特
2.1.32024-05-30马凯特
2.1.42024-06-06马凯特
2.1.52024-06-13马凯特
2.2.02024-07-01马凯特
2.2.12024-07-18马凯特
2.2.22024-08-22马凯特
2.2.32024-09-05马凯特
2.2.42024-09-24马凯特
2.2.52024-10-24马凯特
2.2.62024-11-07马凯特
2.3.02024-12-05马凯特
2.3.12025-01-14增加机器人外呼呼叫类型马凯特
2.3.22025-02-21杨佳
2.3.32025-03-25杨佳
2.4.02025-05-07杨佳
2.4.0.12025-05-20杨佳
2.4.12025-06-04杨佳
2.4.22025-07-24当前最新版本杨佳

6. B/S 方式引入 SDK

6.1 引入文件

通过<script>标签的 src 属性引入指定的 js 文件

html
<!--测试环境-->
<script src="https://cc2webuat.diankongcloud.cn/sdk/dkCloud-webrtcSDK.js"></script>

<!--生产环境-->
<script src="https://ccadmin.diankongcloud.cn/sdk/dkCloud-webrtcSDK.js"></script>

6.2 浏览器要求

支持 HTML5、WebSocket、WebRTC,例如:Chrome、Firefox、Microsoft Edge 等,优先推荐使用 Chrome。

6.3 体验地址

bash
# 测试地址
https://cc2webuat.diankongcloud.cn/toolbar/

# 生产地址
https://ccadmin.diankongcloud.cn/toolbar/

6.4 Demo

6.4.1 HTML

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta
      http-equiv="Content-Security-Policy"
      content="upgrade-insecure-requests"
    />
    <title>SDK验证</title>
    <link rel="stylesheet" href="./index.css" />
  </head>
  <body>
    <div class="sdk">
      <div class="left">
        <div class="init-area">
          <fieldset>
            <legend>初始化</legend>
            初始化模式:
            <input
              type="radio"
              value="mixin"
              name="type"
              id="mixin"
              checked
            /><label for="mixin">mixin</label>
            <input type="radio" value="single" name="type" id="single" /><label
              for="single"
              >single</label
            >
            <input
              type="checkbox"
              value="useHanupMusic"
              name="useHanupMusic"
              id="useHanupMusic"
            /><label for="useHanupMusic">不使用挂断音</label>
            <input
              type="checkbox"
              value="hanupMusice"
              name="hanupMusice"
              id="hanupMusice"
            /><label for="hanupMusice">使用自定义挂断音频</label>
            <br />
            初始化类型:
            <input type="radio" value="1" name="type1" id="ipPhone" /><label
              for="ipPhone"
              >IP话机</label
            >
            <input
              type="radio"
              value="2"
              name="type1"
              id="webrtc"
              checked
            /><label for="webrtc">webrtc</label>
            <input type="radio" value="3" name="type1" id="phone" /><label
              for="phone"
              >手机号</label
            >
            <br />
            projectId:
            <input type="text" value="" id="projectId" />
            agentId:
            <input type="text" value="" id="agentId" />
            <br />
            extension:
            <input type="text" value="" id="extension" />
            password:
            <input type="text" value="" id="password" /><input
              type="checkbox"
              value="1"
              name="status"
              id="ciphertextRadio"
            /><label for="ciphertextRadio">密文</label>
            <br />
            token:
            <input type="text" value="" id="token" />
            queue:
            <input type="text" value="" id="queue" />
            <br />
            url:
            <input type="text" value="" id="url" />
            initialstate:
            <input type="text" value="" id="initialstate" />
            <br />
            <button id="start">初始化</button>
            <button id="logout">注销</button>
            <button id="switchExtension">切换分机</button>
            <div class="single">
              <button id="agentLogin">座席登录</button>
              <button id="extensionRegister">分机注册</button>
              <button id="agentOut">座席退出</button>
              <button id="extensionOut">分机注销</button>
            </div>
          </fieldset>
        </div>
        <div class="call-area">
          <fieldset style="padding:15px;">
            <legend>通话相关</legend>
            <input
              type="text"
              id="phone-number"
              value="18234554910"
            /><br /><br />
            <button id="call">拨打</button>
            <button id="answer">接听</button>
            <button id="hangup">挂断</button>
            <button id="reject">拒接</button>
            <button id="hold">保持</button>
            <button id="resume">保持结束</button>
            <button id="mute">静音</button>
            <button id="restore">静音恢复</button>
            <button id="satisfaction">满意度</button>
            <button id="double">二次拨号</button>
            <button id="dismantling">强拆</button>
            <button id="agentState">座席状态</button>
            <button id="continue">继续登录</button>
          </fieldset>
        </div>
        <div class="agent">
          <fieldset>
            <legend>座席状态</legend>
            <input
              type="radio"
              value="2"
              name="status"
              id="free"
              checked
            /><label for="free">空闲</label>
            <input type="radio" value="3" name="status" id="busy" /><label
              for="busy"
              >忙碌</label
            >
            <div>
              <select name="reason" id="reason">
                <option value="">选择忙碌原因</option>
                <option value="private">private</option>
                <option value="public">public</option>
                <option value="teabreak">teabreak</option>
                <option value="meeting">meeting</option>
              </select>
            </div>
            <button id="agent-set">设置</button>
          </fieldset>
        </div>
        <div class="switch">
          <fieldset>
            <legend>转接相关</legend>
            <input
              type="radio"
              value="0"
              name="mode"
              id="inside"
              checked
            /><label for="inside">内部</label>
            <input type="radio" value="1" name="mode" id="outside" /><label
              for="outside"
              >外部</label
            >
            <input type="text" value="9001" id="blindChange-input" />
            <button id="blindChange">盲转</button>
            <button id="atxfer">咨询转接</button>
            <button id="atxferHangup">转接挂断</button>
            <button id="consult">咨询</button><br /><br />
            监听模式:
            <input
              type="radio"
              value="0"
              name="listen"
              id="quiet"
              checked
            /><label for="quiet">耳语模式</label>
            <input type="radio" value="1" name="listen" id="whisper" /><label
              for="whisper"
              >监听(静音)模式</label
            >
            <input type="radio" value="2" name="listen" id="insert" /><label
              for="insert"
              >强插</label
            >
            <input type="radio" value="3" name="listen" id="listenIn" /><label
              for="listenIn"
              >监听三方</label
            >
            <input type="radio" value="4" name="listen" id="tripartite" /><label
              for="tripartite"
              >咨询三方</label
            >
            <br />
            <input
              type="text"
              value=""
              placeholder="监听的座席号"
              id="listen-input"
            />
            <button id="listen">执行</button>
          </fieldset>
        </div>
        <div class="switch">
          <fieldset>
            <legend>加解密</legend>
            <input type="text" value="" id="ciphertext-input" />
            <button id="encrypt">加密</button>
            <button id="decrypt">解密</button>
          </fieldset>
        </div>
      </div>
      <div class="right">
        <div class="status">
          <div class="title">当前状态:</div>
          <div class="info" id="info">等待上线</div>
          <div id="sortOut"></div>
          <div class="delay" id="delay">等待网络连接</div>
        </div>
        <div class="content">
          <div class="content-title">事件信息:</div>
          <textarea
            id="content-info"
            name="sendMessage"
            rows="50"
            cols="170"
            readonly="readonly"
          ></textarea>
        </div>
      </div>
    </div>

    <script src="https://cc2webtest.diankongcloud.cn/sdk/dkCloud-webrtcSDK2.1.0.js"></script>
    <script src="./index.js"></script>
  </body>
</html>

6.4.2 CSS

css
* {
  padding: 0;
  margin: 0;
}

html,
body {
  width: 100%;
  height: 100%;
}

.sdk {
  width: 100%;
  height: 100%;
  display: flex;
}

input:focus {
  outline: 1px solid #1e75fe;
  border: none;
}

fieldset {
  padding: 15px;
}

select {
  width: 200px;
  height: 30px;
  border-radius: 4px;
  display: none;
}

select:focus {
  outline: 1px solid #1e75fe;
  border: none;
}

.sdk .left {
  width: 35%;
  height: 100%;
  border-right: 1px solid #bfbfbf;
}

.sdk .left .init-area,
.call-area,
.agent,
.switch {
  padding: 15px;
}

.sdk .left .init-area .single {
  display: none;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
}

.sdk .left .init-area .single button {
  width: 45%;
  height: 35px;
}

.sdk .left .init-area input {
  height: 16px;
  margin: 10px 0;
  padding: 5px;
}

.sdk .left .init-area button {
  width: 100%;
  height: 35px;
  background-color: #1e75fe;
  color: #fff;
  border: none;
  border-radius: 4px;
  font-size: 16px;
  margin: 5px 0;
}

.sdk .left .init-area button:active {
  background-color: blue;
}

.sdk .left .call-area input,
.switch input {
  height: 16px;
  padding: 5px;
}

.sdk .left .call-area button,
.agent button,
.switch button {
  width: 60px;
  height: 38px;
  background-color: #1e75fe;
  border: none;
  border-radius: 4px;
  color: #fff;
  margin: 5px 0;
}

#answer {
  background-color: #67c23a;
}

#hangup,
#dismantling,
#atxferHangup,
#reject {
  background-color: red;
}

.sdk .right {
  width: 65%;
  padding: 15px 0;
}

.sdk .right .status {
  height: 60px;
  padding-left: 15px;
  display: flex;
  align-items: center;
  border-bottom: 1px solid #bfbfbf;
}

.sdk .right .status .title {
  font-size: 18px;
  font-weight: 600;
}

.sdk .right .status .info {
  font-size: 18px;
  font-weight: 600;
  color: #bfbfbf;
  margin-right: 15px;
}

.sdk .right .status .delay {
  margin: 3px 0 0 25px;
  font-size: 12px;
  font-weight: 600;
  color: red;
}

.sdk .right .content {
  padding: 15px;
}

.sdk .right .content .content-title {
  height: 30px;
  font-size: 18px;
  font-weight: 600;
}

.sdk .right .content .content-info {
  height: 800px;
  margin-top: 10px;
  border-radius: 4px;
}

.agent div {
  margin: 5px 0;
}

textarea {
  width: 1213px;
  height: 913px;
}

6.4.3 JavaScript

javascript
const mixin = document.getElementById("mixin");
const single = document.getElementById("single");
const useHanupMusic = document.getElementById("useHanupMusic");
const hanupMusice = document.getElementById("hanupMusice");
const ipPhone = document.getElementById("ipPhone");
const webrtc = document.getElementById("webrtc");
const phone = document.getElementById("phone");
const projectId = document.getElementById("projectId");
const agentId = document.getElementById("agentId");
const extension = document.getElementById("extension");
const password = document.getElementById("password");
const token = document.getElementById("token");
const queue = document.getElementById("queue");
const url = document.getElementById("url");
const initialstate = document.getElementById("initialstate");
const start = document.getElementById("start");
const logout = document.getElementById("logout");
const agentLogin = document.getElementById("agentLogin");
const extensionRegister = document.getElementById("extensionRegister");
const singleDiv = document.querySelector(".single");
const agentOut = document.getElementById("agentOut");
const extensionOut = document.getElementById("extensionOut");
const switchExtension = document.getElementById("switchExtension");

const phoneNumber = document.getElementById("phone-number");
const call = document.getElementById("call");
const answer = document.getElementById("answer");
const hangup = document.getElementById("hangup");
const reject = document.getElementById("reject");
const dismantling = document.getElementById("dismantling");
const hold = document.getElementById("hold");
const resume = document.getElementById("resume");
const mute = document.getElementById("mute");
const restore = document.getElementById("restore");
const double = document.getElementById("double");

const info = document.getElementById("info");
const contentInfo = document.getElementById("content-info");
const delay = document.getElementById("delay");

const free = document.getElementById("free");
const busy = document.getElementById("busy");
const reason = document.getElementById("reason");
const agentSet = document.getElementById("agent-set");
const satisfaction = document.getElementById("satisfaction");

const quiet = document.getElementById("quiet");
const whisper = document.getElementById("whisper");
const insert = document.getElementById("insert");
const listenIn = document.getElementById("listenIn");
const tripartite = document.getElementById("tripartite");
const listenInput = document.getElementById("listen-input");
const listen = document.getElementById("listen");

const inside = document.getElementById("inside");
const outside = document.getElementById("outside");
const blindChangeInput = document.getElementById("blindChange-input");
const blindChange = document.getElementById("blindChange");
const atxfer = document.getElementById("atxfer");
const atxferHangup = document.getElementById("atxferHangup");
const consult = document.getElementById("consult");
const sortOut = document.getElementById("sortOut");

const agentState = document.getElementById("agentState");
const device = document.getElementById("device");
const continueBtn = document.getElementById("continue");

const ciphertextInput = document.getElementById("ciphertext-input");
const encrypt = document.getElementById("encrypt");
const decrypt = document.getElementById("decrypt");
const ciphertextRadio = document.getElementById("ciphertextRadio");

let timer = null;
let currentState = null;

const dky = new DKY();

//获取版本信息
// console.log('当前SDK版本信息', dky.version())

//初始化
start.addEventListener("click", function () {
  info.innerHTML = "连接中...";
  info.style.color = "#333";
  let config = {
    mode: "mixin",
    projectId: projectId.value,
    agentNumber: agentId.value,
    extensionNumber: extension.value,
    extensionPassword: password.value,
    token: token.value,
    agentWebscoketUrl: url.value,
    initialState: initialstate.value,
    initType: null,
    delayTime: true,
    agentType: 0,
    isEncrypt: false,
    useHanupMusic: true,
    hanupMusic: "",
  };

  if (single.checked) {
    config.mode = single.value;
  } else {
    config.mode = "mixin";
  }

  if (ipPhone.checked) {
    config.initType = 1;
  } else if (webrtc.checked) {
    config.initType = 2;
  } else {
    config.initType = 3;
  }

  if (ciphertextRadio.checked) {
    config.isEncrypt = true;
  }

  if (useHanupMusic.checked) {
    config.useHanupMusic = false;
  }
  if (hanupMusice.checked) {
    config.hanupMusic = "https://example.com/hangupMusic.mp3";
  }

  dky.init(config, eventFuncion);
});

//注销
logout.addEventListener("click", () => {
  dky.logoff();
});

//座席登录
agentLogin.addEventListener("click", () => {
  let init_query = {
    mode: "single",
    projectId: projectId.value,
    agentNumber: agentId.value,
    extensionNumber: extension.value,
    extensionPassword: password.value,
    token: token.value,
    queueNumber: queue.value,
    agentWebscoketUrl: url.value,
    initialState: initialstate.value,
    initType: 2,
    queueNumber: "",
  };
  dky.init(init_query, eventFuncion);
  dky.agentLogin();
});

//座席退出
agentOut.addEventListener("click", () => {
  dky.agentExit();
});

//分机注册
extensionRegister.addEventListener("click", () => {
  dky.extensionRegister();
});

//分机退出
extensionOut.addEventListener("click", () => {
  dky.extensionCancel();
});

//拨号
call.addEventListener("click", () => {
  let call_info = {
    callednum: phoneNumber.value,
  };
  dky.callphone(call_info);
});

//接听
answer.addEventListener("click", () => {
  dky.answer();
});

//挂断
hangup.addEventListener("click", () => {
  dky.hangup();
});

//拒接
reject.addEventListener("click", () => {
  dky.reject();
});

//强拆
dismantling.addEventListener("click", () => {
  dky.forceHangup({ agentId: phoneNumber.value });
});

//保持结束
resume.addEventListener("click", () => {
  dky.unhold();
});

//保持
hold.addEventListener("click", () => {
  dky.hold();
});

//静音
mute.addEventListener("click", () => {
  dky.mute();
});

//静音恢复
restore.addEventListener("click", () => {
  dky.unmute();
});

//满意度
satisfaction.addEventListener("click", (data) => {
  dky.serviceLevel();
});

//二次拨号
double.addEventListener("click", () => {
  let data = {
    digit: phoneNumber.value,
  };
  dky.playdtmf(data);
});

//座席状态
free.addEventListener("change", () => {
  if (free.checked) {
    reason.style.display = "none";
  } else {
    reason.style.display = "block";
  }
});
busy.addEventListener("change", () => {
  if (free.checked) {
    reason.style.display = "none";
    reason.value = "";
  } else {
    reason.style.display = "block";
  }
});
agentSet.addEventListener("click", () => {
  console.log(free.checked, free.value, busy.checked, busy.value, reason.value);
  let query = {
    state: "2",
    reason: "",
  };
  if (free.checked) {
    query.state = free.value;
    query.reason = "";
  }
  if (busy.checked) {
    query.state = busy.value;
    query.reason = reason.value;
  }
  dky.setAgentState(query);
});

//盲转
blindChange.addEventListener("click", () => {
  let data = {
    mode: "",
    transferNumber: blindChangeInput.value,
  };
  if (inside.checked) {
    data.mode = inside.value;
  }
  if (outside.checked) {
    data.mode = outside.value;
  }
  dky.blindMove(data);
});

//咨询转接
atxfer.addEventListener("click", () => {
  let data = {
    mode: "",
    atxferNumber: blindChangeInput.value,
  };
  if (inside.checked) {
    data.mode = inside.value;
  }
  if (outside.checked) {
    data.mode = outside.value;
  }
  dky.atxfer(data);
});

//咨询转接挂断
atxferHangup.addEventListener("click", () => {
  dky.atxferHangup();
});

consult.addEventListener("click", () => {
  let data = {
    mode: "",
    atxferNumber: blindChangeInput.value,
  };
  if (inside.checked) {
    data.mode = inside.value;
  }
  if (outside.checked) {
    data.mode = outside.value;
  }
  dky.consult(data);
});

//监听咨询
listen.addEventListener("click", () => {
  let data = null;
  if (quiet.checked || whisper.checked || insert.checked || listenIn.checked) {
    data = {
      mode: "",
      agentId: listenInput.value,
      point: 0,
      monitorNumber: agentId.value,
    };
  } else {
    data = {
      mode: 0,
      meetNumber: listenInput.value,
    };
  }
  if (quiet.checked) {
    data.mode = quiet.value;
    dky.chanspy(data);
  }
  if (whisper.checked) {
    data.mode = whisper.value;
    dky.chanspy(data);
  }
  if (insert.checked) {
    data.mode = insert.value;
    dky.chanspy(data);
  }
  if (listenIn.checked) {
    data.mode = listenIn.value;
    dky.chanspy(data);
  }
  if (tripartite.checked) {
    dky.meet(data);
  }
});

mixin.addEventListener("click", () => {
  start.style.display = "block";
  logout.style.display = "block";
  singleDiv.style.display = "none";
});

single.addEventListener("click", () => {
  start.style.display = "none";
  logout.style.display = "none";
  singleDiv.style.display = "flex";
});

//座席状态
agentState.addEventListener("click", () => {
  dky.agentState();
});

//加密
encrypt.addEventListener("click", () => {
  let ciphertext = dky.encrypt(ciphertextInput.value, token.value);
  contentInfo.value =
    contentInfo.value +
    "\n" +
    getCurrentTimes() +
    "      (加密)" +
    "\n" +
    "原文 ==>   " +
    ciphertextInput.value +
    "      " +
    "密文 ==>    " +
    ciphertext +
    "\n";
});

//解密
decrypt.addEventListener("click", () => {
  let text = dky.decrypt(ciphertextInput.value, token.value);
  contentInfo.value =
    contentInfo.value +
    "\n" +
    getCurrentTimes() +
    "      (解密)" +
    "\n" +
    "密文 ==>   " +
    ciphertextInput.value +
    "      " +
    "原文 ==>    " +
    text +
    "\n";
});

//继续登录
continueBtn.addEventListener("click", () => {
  dky.continueLogin();
});

//切换分机
switchExtension.addEventListener("click", () => {
  let data = {
    type: 2,
    extensionNumber: extension.value,
    extensionPassword: password.value,
  };

  if (ipPhone.checked) {
    data.type = 1;
  } else if (webrtc.checked) {
    data.type = 2;
  } else {
    data.type = 3;
  }

  dky.switchExtension(data);
});

function eventFuncion() {
  dky.on("dky:init", (data) => {
    if (data.state == "init") {
      info.innerHTML = "已上线";
      info.style.color = "#67C23A";
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (初始化)" +
        "\n" +
        JSON.stringify(data) +
        "\n";
    } else {
      info.innerHTML = data.description;
      info.style.color = "red";
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (初始化)" +
        "\n" +
        JSON.stringify(data) +
        "\n";
    }
  });
  dky.on("dky:logoff", (e) => {
    if (e.state == "error") {
      info.innerHTML = e.description;
      info.style.color = "red";
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (注销)" +
        "\n" +
        JSON.stringify(e) +
        "\n";

      return;
    }
    info.innerHTML = "已下线";
    info.style.color = "red";
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (注销)" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
  dky.on("dky:forceDisconnectCall", (e) => {
    console.log(e, "强拆触发");
  });
  dky.on("dky:squeeze", (data) => {
    info.innerHTML = "被挤下线";
    info.style.color = "red";
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (被挤下线)" +
      "\n" +
      JSON.stringify(data) +
      "\n";
  });
  dky.on("dky:agentLogin", (data) => {
    if (data.state == "error") {
      info.innerHTML = data.description;
      info.style.color = "red";
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (初始化)" +
        "\n" +
        JSON.stringify(data) +
        "\n";
    } else {
      info.innerHTML = "座席登录成功";
      info.style.color = "#67C23A";
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (座席登录)" +
        "\n" +
        JSON.stringify(data) +
        "\n";
    }
  });
  dky.on("dky:agentExit", (e) => {
    if (e.state == "error") {
      info.innerHTML = e.message;
      info.style.color = "red";
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (座席退出)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
      return;
    }
    info.innerHTML = "座席已退出";
    info.style.color = "red";
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (座席退出)" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
  dky.on("dky:extensionRegister", (data) => {
    info.innerHTML = "分机注册完成";
    info.style.color = "#67C23A";
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (分机注册)" +
      "\n" +
      JSON.stringify(data) +
      "\n";
  });
  dky.on("dky:extensionCancel", (data) => {
    info.innerHTML = "分机已注销";
    info.style.color = "red";
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (分机注销)" +
      "\n" +
      JSON.stringify(data) +
      "\n";
  });
  dky.on("dky:callphone", (data) => {
    if (data.code == 200) {
      info.innerHTML = "呼叫中...";
      info.style.color = "#67C23A";
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (拨打)" +
        "\n" +
        JSON.stringify(data) +
        "\n";
    } else {
      info.innerHTML = "拨号失败";
      info.style.color = "red";
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (拨打 - 失败)" +
        "\n" +
        JSON.stringify(data) +
        "\n";
    }
  });
  dky.on("dky:ring", (e) => {
    if (e.code == 200) {
      currentState = e.data.callType;
      if (e.data.option == "out" && e.data.callLeg == "customer") {
        info.innerHTML = "客户响铃中...";
        contentInfo.value =
          contentInfo.value +
          "\n" +
          getCurrentTimes() +
          "      (客户响铃)" +
          "\n" +
          JSON.stringify(e) +
          "\n";
      } else {
        if (e.data.option == "in" && e.data.callLeg == "agent") {
          info.innerHTML = "座席响铃中...";
          contentInfo.value =
            contentInfo.value +
            "\n" +
            getCurrentTimes() +
            "      (座席响铃)" +
            "\n" +
            JSON.stringify(e) +
            "\n";
        } else if (e.data.callType == "11") {
          let maxTime = null;
          let target = null;
          if (Object.keys(e.data.transferMessage).length > 1) {
            for (let item in e.data.transferMessage) {
              if (maxTime == null) {
                maxTime = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
              } else {
                let time = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
                if (maxTime < time) {
                  maxTime = item;
                  target = e.data.transferMessage[item];
                }
              }
            }
            if (target.initiateAgentId != e.data.agentNumber) {
              info.innerHTML = "被转转接的座席响铃中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (转接响铃)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          } else {
            for (let item in e.data.transferMessage) {
              target = e.data.transferMessage[item];
            }
            if (target.initiateAgentId != e.data.agentNumber) {
              info.innerHTML = "被转转接的座席响铃中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (转接响铃)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          }
        } else if (e.data.callType == 15) {
          let maxTime = null;
          let target = null;
          if (Object.keys(e.data.transferMessage).length > 1) {
            for (let item in e.data.transferMessage) {
              if (maxTime == null) {
                maxTime = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
              } else {
                let time = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
                if (maxTime < time) {
                  maxTime = time;
                  target = e.data.transferMessage[item];
                }
              }
            }
            if (target.initiateAgentId == e.data.agentNumber) {
              info.innerHTML = "咨询转接的座席响铃中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (被咨询转接响铃)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            } else {
              info.innerHTML = "被咨询转接的座席响铃中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (咨询转接响铃)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          } else {
            for (let item in e.data.transferMessage) {
              target = e.data.transferMessage[item];
            }
            if (target.initiateAgentId == e.data.agentNumber) {
              info.innerHTML = "咨询转接的座席响铃中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (被咨询转接响铃)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            } else {
              info.innerHTML = "被咨询转接的座席响铃中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (咨询转接响铃)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          }
        } else if (e.data.callType == 19) {
          let maxTime = null;
          let target = null;
          if (Object.keys(e.data.transferMessage).length > 1) {
            for (let item in e.data.transferMessage) {
              if (maxTime == null) {
                maxTime = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
              } else {
                let time = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
                if (maxTime < time) {
                  maxTime = time;
                  target = e.data.transferMessage[item];
                }
              }
            }
            if (target.initiateAgentId == e.data.agentNumber) {
              info.innerHTML = "咨询的座席响铃中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (被咨询响铃)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            } else {
              info.innerHTML = "被咨询的座席响铃中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (咨询响铃)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          } else {
            for (let item in e.data.transferMessage) {
              target = e.data.transferMessage[item];
            }
            if (target.initiateAgentId == e.data.agentNumber) {
              info.innerHTML = "咨询的座席响铃中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (被咨询响铃)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            } else {
              info.innerHTML = "被咨询的座席响铃中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (咨询响铃)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          }
        }
      }
      info.style.color = "#67C23A";
    } else {
      info.innerHTML = "系统异常,请联系开发人员";
      info.style.color = "red";
    }
  });
  dky.on("dky:answer", (e) => {
    if (e.code == 200) {
      if (e.data.option == "out" && e.data.callLeg == "customer") {
        info.innerHTML = "通话中...";
        contentInfo.value =
          contentInfo.value +
          "\n" +
          getCurrentTimes() +
          "      (客户接听)" +
          "\n" +
          JSON.stringify(e) +
          "\n";
      } else {
        if (e.data.option == "in" && e.data.callLeg == "agent") {
          info.innerHTML = "通话中...";
          contentInfo.value =
            contentInfo.value +
            "\n" +
            getCurrentTimes() +
            "      (座席接听)" +
            "\n" +
            JSON.stringify(e) +
            "\n";
        } else if (e.data.callType == "11") {
          let maxTime = null;
          let target = null;
          if (Object.keys(e.data.transferMessage).length > 1) {
            for (let item in e.data.transferMessage) {
              if (maxTime == null) {
                maxTime = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
              } else {
                let time = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
                if (maxTime < time) {
                  maxTime = item;
                  target = e.data.transferMessage[item];
                }
              }
            }
            if (target.initiateAgentId != e.data.agentNumber) {
              info.innerHTML = "被转转接的座席通话中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (转接通话中...)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          } else {
            for (let item in e.data.transferMessage) {
              target = e.data.transferMessage[item];
            }
            if (target.initiateAgentId != e.data.agentNumber) {
              info.innerHTML = "被转转接的座席通话中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (转接通话中...)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          }
        } else if (e.data.callType == 15) {
          let maxTime = null;
          let target = null;
          if (Object.keys(e.data.transferMessage).length > 1) {
            for (let item in e.data.transferMessage) {
              if (maxTime == null) {
                maxTime = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
              } else {
                let time = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
                if (maxTime < time) {
                  maxTime = time;
                  target = e.data.transferMessage[item];
                }
              }
            }
            if (target.initiateAgentId == e.data.agentNumber) {
              info.innerHTML = "咨询转接的座席通话中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (被咨询转接通话中...)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            } else {
              info.innerHTML = "被咨询转接的座席通话中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (咨询转接通话中...)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          } else {
            for (let item in e.data.transferMessage) {
              target = e.data.transferMessage[item];
            }
            if (target.initiateAgentId == e.data.agentNumber) {
              info.innerHTML = "咨询转接的座席通话中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (被咨询转接通话中...)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            } else {
              info.innerHTML = "被咨询转接的座席通话中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (咨询转接通话中...)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          }
        } else if (e.data.callType == 19) {
          let maxTime = null;
          let target = null;
          if (Object.keys(e.data.transferMessage).length > 1) {
            for (let item in e.data.transferMessage) {
              if (maxTime == null) {
                maxTime = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
              } else {
                let time = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
                if (maxTime < time) {
                  maxTime = time;
                  target = e.data.transferMessage[item];
                }
              }
            }
            if (target.initiateAgentId == e.data.agentNumber) {
              info.innerHTML = "咨询的座席通话中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (被咨询通话中...)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            } else {
              info.innerHTML = "被咨询的座席通话中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (咨询通话中...)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          } else {
            for (let item in e.data.transferMessage) {
              target = e.data.transferMessage[item];
            }
            if (target.initiateAgentId == e.data.agentNumber) {
              info.innerHTML = "咨询的座席通话中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (被咨询通话中...)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            } else {
              info.innerHTML = "被咨询的座席通话中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (咨询通话中...)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          }
        }
      }
      info.style.color = "#67C23A";
    } else {
      info.innerHTML = "系统异常,请联系开发人员";
      info.style.color = "red";
    }
  });
  dky.on("dky:hangup", (e) => {
    if (e.code == 200) {
      if (e.data.callType == "11") {
        let maxTime = null;
        let target = null;
        if (Object.keys(e.data.transferMessage).length > 1) {
          for (let item in e.data.transferMessage) {
            if (maxTime == null) {
              maxTime = new Date(
                e.data.transferMessage[item].transferTime
              ).getTime();
            } else {
              let time = new Date(
                e.data.transferMessage[item].transferTime
              ).getTime();
              if (maxTime < time) {
                maxTime = item;
                target = e.data.transferMessage[item];
              }
            }
          }
          info.innerHTML = "被转转接的座席挂断";
          contentInfo.value =
            contentInfo.value +
            "\n" +
            getCurrentTimes() +
            "      (转接挂断)" +
            "\n" +
            JSON.stringify(e) +
            "\n";
        } else {
          for (let item in e.data.transferMessage) {
            target = e.data.transferMessage[item];
          }
          info.innerHTML = "被转转接的座席挂断";
          contentInfo.value =
            contentInfo.value +
            "\n" +
            getCurrentTimes() +
            "      (转接挂断)" +
            "\n" +
            JSON.stringify(e) +
            "\n";
        }
        info.style.color = "red";
        setTimeout(() => {
          info.innerHTML = "空闲中...";
          info.style.color = "#67C23A";
        }, 3000);
      } else if (e.data.callType == 15) {
        let maxTime = null;
        let target = null;
        if (Object.keys(e.data.transferMessage).length > 1) {
          for (let item in e.data.transferMessage) {
            if (maxTime == null) {
              maxTime = new Date(
                e.data.transferMessage[item].transferTime
              ).getTime();
            } else {
              let time = new Date(
                e.data.transferMessage[item].transferTime
              ).getTime();
              if (maxTime < time) {
                maxTime = time;
                target = e.data.transferMessage[item];
              }
            }
          }
          if (target.initiateAgentId == e.data.agentNumber) {
            info.innerHTML = "咨询转接的座席挂断";
            contentInfo.value =
              contentInfo.value +
              "\n" +
              getCurrentTimes() +
              "      (被咨询转接挂断)" +
              "\n" +
              JSON.stringify(e) +
              "\n";
          } else {
            info.innerHTML = "被咨询转接的座席挂断";
            contentInfo.value =
              contentInfo.value +
              "\n" +
              getCurrentTimes() +
              "      (咨询转接挂断)" +
              "\n" +
              JSON.stringify(e) +
              "\n";
          }
        } else {
          for (let item in e.data.transferMessage) {
            target = e.data.transferMessage[item];
          }
          if (target.initiateAgentId == e.data.agentNumber) {
            info.innerHTML = "咨询转接的座席挂断";
            contentInfo.value =
              contentInfo.value +
              "\n" +
              getCurrentTimes() +
              "      (被咨询转接挂断)" +
              "\n" +
              JSON.stringify(e) +
              "\n";
          } else {
            info.innerHTML = "被咨询转接的座席挂断";
            contentInfo.value =
              contentInfo.value +
              "\n" +
              getCurrentTimes() +
              "      (咨询转接挂断)" +
              "\n" +
              JSON.stringify(e) +
              "\n";
          }
        }
        info.style.color = "red";
        setTimeout(() => {
          info.innerHTML = "空闲中...";
          info.style.color = "#67C23A";
        }, 3000);
      } else if (e.data.callType == 19) {
        let maxTime = null;
        let target = null;
        if (Object.keys(e.data.transferMessage).length > 1) {
          for (let item in e.data.transferMessage) {
            if (maxTime == null) {
              maxTime = new Date(
                e.data.transferMessage[item].transferTime
              ).getTime();
            } else {
              let time = new Date(
                e.data.transferMessage[item].transferTime
              ).getTime();
              if (maxTime < time) {
                maxTime = time;
                target = e.data.transferMessage[item];
              }
            }
          }
          if (target.initiateAgentId == e.data.agentNumber) {
            info.innerHTML = "咨询的座席挂断";
            contentInfo.value =
              contentInfo.value +
              "\n" +
              getCurrentTimes() +
              "      (被咨询挂断)" +
              "\n" +
              JSON.stringify(e) +
              "\n";
          } else {
            info.innerHTML = "被咨询的座席挂断";
            contentInfo.value =
              contentInfo.value +
              "\n" +
              getCurrentTimes() +
              "      (咨询挂断)" +
              "\n" +
              JSON.stringify(e) +
              "\n";
          }
        } else {
          for (let item in e.data.transferMessage) {
            target = e.data.transferMessage[item];
          }
          if (target.initiateAgentId == e.data.agentNumber) {
            info.innerHTML = "咨询的座席挂断";
            contentInfo.value =
              contentInfo.value +
              "\n" +
              getCurrentTimes() +
              "      (被咨询挂断)" +
              "\n" +
              JSON.stringify(e) +
              "\n";
            setTimeout(() => {
              info.innerHTML = "通话中...";
              info.style.color = "#67C23A";
            }, 3000);
          } else {
            info.innerHTML = "被咨询的座席挂断";
            contentInfo.value =
              contentInfo.value +
              "\n" +
              getCurrentTimes() +
              "      (咨询通挂断)" +
              "\n" +
              JSON.stringify(e) +
              "\n";
            setTimeout(() => {
              info.innerHTML = "空闲中...";
              info.style.color = "#67C23A";
            }, 3000);
          }
        }
        info.style.color = "red";
      } else {
        if (e.data.hasOwnProperty("transferMessage")) {
          let maxTime = null;
          let target = null;
          if (Object.keys(e.data.transferMessage).length > 1) {
            for (let item in e.data.transferMessage) {
              if (maxTime == null) {
                maxTime = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
              } else {
                let time = new Date(
                  e.data.transferMessage[item].transferTime
                ).getTime();
                if (maxTime < time) {
                  maxTime = time;
                  target = e.data.transferMessage[item];
                }
              }
            }
            if (target.initiateAgentId == e.data.agentNumber) {
              info.innerHTML = "通话结束";
              info.style.color = "red";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (挂断)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
              processSortout(e.data.wrapupTime);
            } else {
              info.innerHTML = "通话中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (挂断)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          } else {
            for (let item in e.data.transferMessage) {
              target = e.data.transferMessage[item];
            }
            if (target.initiateAgentId == e.data.agentNumber) {
              info.innerHTML = "通话结束";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (挂断)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
              processSortout(e.data.wrapupTime);
            } else {
              info.innerHTML = "通话中...";
              contentInfo.value =
                contentInfo.value +
                "\n" +
                getCurrentTimes() +
                "      (挂断)" +
                "\n" +
                JSON.stringify(e) +
                "\n";
            }
          }
        } else {
          if (e.data.callLeg == "agent") {
            processSortout(e.data.wrapupTime);
          }
        }
        contentInfo.value =
          contentInfo.value +
          "\n" +
          getCurrentTimes() +
          "      (挂断)" +
          "\n" +
          JSON.stringify(e) +
          "\n";
        // dky.logoff()
      }
    } else {
      info.innerHTML = "系统异常,请联系开发人员";
      info.style.color = "red";
    }
  });
  dky.on("dky:agentState", (data) => {
    if (data.state == 2) {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (座席状态:空闲)" +
        "\n" +
        JSON.stringify(data) +
        "\n";
    }
    if (data.state == 3) {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (座席状态:忙碌)" +
        "\n" +
        JSON.stringify(data) +
        "\n";
    }
  });
  dky.on("dky:hold", (e) => {
    if (e.state == "error") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (通话保持 - 错误)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
      return;
    }
    if (e.mode == "hold") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (通话保持开启)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
    }
    if (e.mode == "unhold") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (通话保持结束)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
    }
  });
  dky.on("dky:agentMute", (e) => {
    if (e.state == "error") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (静音 - 错误)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
      return;
    }
    if (e.mode == "mute") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (静音开启)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
    } else {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (静音结束)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
    }
  });
  dky.on("dky:service_level", (data) => {
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (满意度)" +
      "\n" +
      JSON.stringify(data) +
      "\n";
  });
  dky.on("dky:play_dtmf", (data) => {
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (二次拨号)" +
      "\n" +
      JSON.stringify(data) +
      "\n";
  });
  dky.on("dky:blindMove", (e) => {
    if (e.state == "error") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (盲转 - 错误)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
      return;
    }
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (盲转)" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
  dky.on("dky:incomingNotConnectedHangup", (data) => {
    console.log("监听到呼入非接通挂断", data);
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (呼入非接通挂断的置忙)" +
      "\n" +
      JSON.stringify(data) +
      "\n";
  });
  dky.on("dky:networkDelay", (data) => {
    delay.innerHTML = "网络延迟:" + data.delayTime + "ms";
    // contentInfo.value =
    // contentInfo.value +
    // "\n" +
    // getCurrentTimes() +
    // "      (座席websocket开启事件)" +
    // "\n" +
    // JSON.stringify(data) +
    // "\n";
    if (data.delayTime < 100) {
      delay.style.color = "#67C23A";
    } else {
      delay.style.color = "red";
    }
  });
  dky.on("dky:agentWebsocketOpen", (e) => {
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (座席websocket开启事件)" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
  dky.on("dky:agentWebsocketClose", (e) => {
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (座席websocket关闭事件)" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
  dky.on("dky:reconnect", (e) => {
    console.log("出发了");
    if (e.state == "reconnected") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (重连成功)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
    } else if (e.state == "reconnecting") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (第" +
        e.num +
        "次重连)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
    } else {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (重连十次,重连失败)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
    }
  });
  dky.on("dky:networkSituation", (e) => {
    if (e.state == "networkDisconnected") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (网络断开)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
    } else if (e.state == "networkConnected") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (网络连接/恢复)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
    }
  });
  dky.on("dky:serviceLevel", (e) => {
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (满意度)" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
  dky.on("dky:chanspy", (e) => {
    if (e.mode == 0) {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (监听/咨询 - 耳语模式)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
    }
    if (e.mode == 1) {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (监听/咨询 - 静音模式)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
    }
    if (e.state == "error") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (监听 - 错误)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
    }
  });
  dky.on("dky:meet", (e) => {
    if (e.state == "error") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (咨询三方 - 错误)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
      return;
    }
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (咨询三方)" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
  dky.on("dky:atxfer", (e) => {
    if (e.state == "error") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (咨询转接 - 错误)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
      return;
    }
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (咨询转接)" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
  dky.on("dky:forceOffline", (e) => {
    if (e.state == "error") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (强制下线 - 错误)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
      return;
    }
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (强制下线)" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
  dky.on("dky:realTimeData", (e) => {
    if (e.state == "error") {
      contentInfo.value =
        contentInfo.value +
        "\n" +
        getCurrentTimes() +
        "      (通话数据 - 错误)" +
        "\n" +
        JSON.stringify(e) +
        "\n";
      return;
    }
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (通话数据)" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
  dky.on("dky:consult", (e) => {
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (咨询)" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
  dky.on("dky:networkSituation", (e) => {
    console.log("监听网络断开", e);
  });
  dky.on("dky:agentState", (e) => {
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (座席状态)" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
  dky.on("dky:deviceDetection", (e) => {
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (媒体设备)" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
  dky.on("dky:switchExtension", (e) => {
    contentInfo.value =
      contentInfo.value +
      "\n" +
      getCurrentTimes() +
      "      (" +
      e.description +
      ")" +
      "\n" +
      JSON.stringify(e) +
      "\n";
  });
}

//处理挂断后的整理状态
function processSortout(time) {
  info.innerHTML = "通话结束";
  info.style.color = "red";
  processCountdown(time);
  timer = setInterval(() => {
    info.innerHTML = "整理中...";
    info.style.color = "yellow";
    time = parseInt(time - 1);
    sortOut.innerHTML = processCountdown(time);
    if (time == 0) {
      clearInterval(timer);
      timer = null;
      info.innerHTML = "空闲中...";
      info.style.color = "#67C23A";
      sortOut.innerHTML = "";
    }
  }, 1000);
}

//获取当前时间
function getCurrentTimes(type) {
  let date = new Date();
  let year = date.getFullYear();
  let month =
    date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth();
  let days = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
  let h = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
  let m = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
  let s = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
  let ms =
    date.getMilliseconds() > 9
      ? date.getMilliseconds() > 99
        ? date.getMilliseconds()
        : "0" + date.getMilliseconds()
      : "00" + date.getMilliseconds();

  return (
    year + "-" + month + "-" + days + " " + h + ":" + m + ":" + s + ":" + ms
  );
}

//处理整理的倒计时
function processCountdown(time) {
  let s = time % 60,
    m = parseInt(time / 60);

  if (m >= 60) {
    h = parseInt(m / 60);
    m = m % 60;
  } else {
    h = parseInt(m / 60);
  }

  if (time >= 0 && time < 10) {
    return "0" + h + ":" + "0" + m + ":" + "0" + s;
  } else if (time >= 10 && time < 60) {
    return "0" + h + ":" + "0" + m + ":" + s;
  } else {
    if (s >= 0 && s < 10 && m >= 0 && m < 10 && h >= 0 && h < 10) {
      return "0" + h + ":" + "0" + m + ":" + "0" + s;
    } else if (s >= 10 && s < 60 && m >= 0 && m < 10 && h >= 0 && h < 10) {
      return "0" + h + ":" + "0" + m + ":" + s;
    } else if (s >= 0 && s < 10 && m >= 10 && m < 60 && h >= 0 && h < 10) {
      return "0" + h + ":" + m + ":" + "0" + s;
    } else if (s >= 10 && s < 60 && m >= 10 && m < 60 && h >= 0 && h < 10) {
      return "0" + h + ":" + m + ":" + s;
    } else if (s >= 0 && s < 10 && m >= 0 && m < 10 && h >= 10 && h < 60) {
      return h + ":" + "0" + m + ":" + "0" + s;
    } else if (s >= 0 && s < 10 && m >= 10 && m < 0 && h >= 10 && h < 60) {
      return h + ":" + m + ":" + "0" + s;
    } else if (s >= 10 && s < 60 && m >= 10 && m < 0 && h >= 10 && h < 60) {
      return h + ":" + m + ":" + s;
    }
  }
}

7. JavaScript-API

7.1 登录注册相关

7.1.1 初始化

API 名称: init()

功能: 将座席和分机登录并注册在远端服务器上,为了便于客户对接,init()将座席登录和分机注册进行了合并封装,第三方在进行对接时只需要传递指定的参数,而不需要关注座席和分机的逻辑关系。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
let object = {
  mode: "mixin",
  projectId: "10010010010",
  agentNumber: "1001",
  extensionNumber: "1001",
  extensionPassword: "123456",
  token: "ADSFASF5SDF45WEF524EF5W4EF",
  agentWebscoketUrl: "ip地址",
  initialState: 1,
  initType: null,
  delayTime: false,
  agentType: 0,
  useHanupMusic: true,
  hanupMusic: "https://example.com/hangupMusic.mp3",
};

function eventFuncion() {
  dky.on("dky:init", (e) => {
    console.log("初始化的事件", e);
  });
  //...其他的事件监听
}

dky.init(object, eventFunction);

参数说明:

object 参数:

参数名称是否为必传备注
mode取值为mixin/single ,默认为mixin
agentNumber座席号(工号)
agentType座席号类型(0:默认座席工号 1:自定义座席工号)不传默认 0
extensionNumber分机号
extensionPassword分机注册密码
projectId企业编码
token创建企业时,系统生成的指定 token
agentWebscoketUrl座席登录的 webScoekt 地址
initType初始化类型:1 - IP 话机类型,2 - webrtc 类型,3 - 手机号
initialState初始状态,取值:2 - 空闲,3 - 忙碌,默认为空闲状态
delayTime是否接收网络延迟检测事件,true 为接收,false 为不接收,默认为 false
isEncrypt分机密码是否为加密密文,取值:true/false,默认为 false
useHanupMusic是否使用系统默认挂断音频,取值:true/false,默认为 true
hanupMusic自定义挂断音频文件地址,仅支持使用 https 域名地址,使用时,需确保 useHanupMusic 的值为 true

eventFunction:事件监听回调函数,如果不传递,将监听不到任何事件。

回调事件:

事件名称:dky:init

执行了dky.init()后只有在modemixin时会触发dky:init事件。

javascript
//eventFunciton函数的内部

dky.on("dky:init", (e) => {
  if (e.state == "init") {
    console.log("初始化完成");
    if (e.agentRole == "monitor") {
      console.log("这是一个班长座席");
    } else {
      console.log("这是一个普通座席");
    }
  } else {
    console.log("初始化失败,失败原因:", e.description);
  }
});

回调事件参数 e 说明:

返回示例:

json
{
  "mode": "mixin",
  "state": "init",
  "agentId": "1001",
  "agentRole": "monitor",
  "description": "系统初始化完成"
}
参数名称描述
mode初始化的模式
state状态:init - 初始化成功,error - 初始化发生错误
agentId座席工号
agentRole座席角色:ordinary - 普通座席,monitor - 班长座席
description状态描述

7.1.2 退出

API 名称: logoff()

功能: 将座席和分机从远端服务器上进行退出和注销,无论modemixin的模式还是single模式都会将座席和分机同时进行退出和注销。(退出时,为了确保分机能够正确的发出和响应退出下线的消息内容,会有一个 2 秒钟的延迟。)

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
let object = {
  agentId: "1002",
  bindState: "unbind",
};

//如果只是退出当前座席,可以不用传递object对象
dky.logoff();

//如果是班长座席指定某个普通座席的下线,必须要传递object对象
dky.logoff(object);

参数说明:

参数名称是否为必传备注
agentId班长座席强制其他座席进行下线的指定座席号
bindState在使用非 webrtc 分机进行退出时为必传,使用 webrtc 分机时为非必传参数bindState 可取值为:bind - 退出时不解绑绑定的分机或者手机号,unbind - 退出时解绑绑定的分机或者手机号,默认值为 unbind

回调事件:

事件名称:dky:logoff

执行了dky.logoff()后会触发dky:logoff事件。

javascript
//eventFunciton函数的内部

dky.on("dky:logoff", (e) => {
  console.log("退出/注销的事件", e);
  if (e.state == "error") {
    console.log("退出发生错误,错误原因:", e.description);
    return;
  }
  console.log("退出成功!");
});

回调事件参数 e 说明:

返回示例:

json
{
  "mode": "mixin",
  "state": "logoff",
  "description": "系统注销完成"
}
参数名称描述
mode初始化的模式
state状态:logoff
description状态描述(中文):系统注销完成

7.1.3 座席登录

API 名称: agentLogin()

功能: 在初始化时,如果设置了modesingle,可以调用该 API 将座席单独登录在远端服务器上,如果需要注册分机还需要在座席登录完成后再调用分机的注册,建议第三方在没有特殊业务逻辑的情况优先考虑使用初始化mixin模式。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
//因为初始化时已经传递了相关参数,这里执行不需要传递任何参数
dky.agentLogin();

回调事件:

事件名称:dky:agentLogin

执行了dky.agentLogin()后会触发dky:agentLogin事件,并且只有在modesingle时触发。

javascript
//eventFunciton函数的内部

dky.on("dky:agentLogin", (e) => {
  if (e.code == 200) {
    console.log("mode为single时的座席登录成功", e);
  } else {
    console.log("mode为single时的座席登录失败,失败原因:", e.message);
  }
});

回调事件参数说明:

返回示例:

json
{
  "agentId": "100100100101001",
  "code": "200",
  "createTime": 1720693864036,
  "crmId": "",
  "data": {
    "agentRole": 1,
    "crmId": "",
    "agentId": "1001",
    "pjsipPort": "12345",
    "realm": "realm域地址",
    "webrtcip": "分机注册地址"
  },
  "eventType": "AgentLogin",
  "extension": "100100100101001",
  "innerAgentId": "100100100101001",
  "message": "成功",
  "projectId": "10010010010",
  "requestId": "99c9bcfb64b54fef9d3f8da2fabf3b2e",
  "type": "agentlogin"
}
参数名称描述
agentId登录注册的座席号
code状态码,200 为成功,非 200 为失败
createTime调用时间,10 位的时间戳
crmId自定义座席工号
agentRole座席角色,1 - 普通座席,2 - 班长座席
pjsipPortPJSIP 分机注册的端口号
realmwebrtc 分机注册的安全策略域
webrtcipwebrtc 分机注册的地址
eventType事件类型
innerAgentId系统座席工号
message提示信息
projectId登录注册的企业编码
requestId请求 id 编码
type类型

7.1.4 座席退出登录

API 名称: agentExit()

功能: 在初始化时,如果设置了modesingle,可以调用该 API 将座席单独从远端服务器退出登录,并且不会对分机产生任何影响。建议第三方在没有特殊业务逻辑的情况优先考虑使用初始化mixin模式。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
//使用webrtc分机在mode为single时,进行座席的单独下线,可以不传递参数
dky.agentExit();

//在使用非webrtc分机在mode为single时,进行座席的单独下线,参数为必传值
dky.agentExit("unbind");

参数说明:

参数名称要求备注
bindState在使用非 webrtc 分机进行退出时为必传bindState 可取值为:bind - 退出时不解绑绑定的分机或者手机号,unbind - 退出时解绑绑定的分机或者手机号,默认值为 unbind

回调事件:

事件名称:dky:agentExit

执行了dky.agentExit()后会触发dky:agentExit事件,并且只有在modesingle时触发。

javascript
//eventFunciton函数的内部

dky.on("dky:agentExit", (e) => {
  if (e.code == 200) {
    console.log("mode为single时的座席退出成功");
  } else {
    console.log("mode为single时的座席退出失败,失败原因:", e.message);
  }
});

回调事件参数说明:

返回示例:

json
{
  "agentId": "100100100101001",
  "code": "200",
  "createTime": 1720695193326,
  "crmId": "abc123",
  "data": {},
  "eventType": "AgentInterface",
  "extension": "100100100101001",
  "innerAgentId": "100100100101001",
  "message": "成功",
  "projectId": "10010010010",
  "requestId": "a401bf7492504581b8c8d6f6cd022de7",
  "type": "agentlogout"
}
参数名称描述
agentId登录注册的座席号
code状态码,200 为成功,非 200 为失败
createTime调用时间,10 位的时间戳
crmId自定义座席工号
eventType事件类型
extension登录注册的分机号
innerAgentId系统座席工号
message提示信息
projectId登录注册的企业编码
requestId请求 id 编码
type类型

7.1.5 分机注册

API 名称: extensionRegister()

功能: 在初始化时,如果设置了modesingle,可以调用该 API 将分机单独在远端服务器进行注册,需要先进行座席的登录再进行分机的注册,同时第三方在没有特殊业务逻辑的情况优先考虑使用初始化mixin模式。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
//因为初始化时已经传递了相关参数,这里执行不需要传递任何参数
dky.extensionRegister();

回调事件:

事件名称:dky:extensionRegister

执行了dky.extensionRegister()后会触发dky:extensionRegister事件,并且只有在modesingle时触发。

javascript
//eventFunciton函数的内部

dky.on("dky:extensionRegister", (e) => {
  if (e.state == "registered") {
    console.log("mode为single时的分机注册成功");
  } else {
    console.log("mode为single时的分机注册失败,失败原因:", e.description);
  }
});

回调事件参数说明:

返回示例:

json
{
  "mode": "single",
  "state": "registered",
  "description": "分机注册完成"
}
参数名称描述
mode初始化的模式
state状态
description状态的描述

7.1.6 分机注销

API 名称: extensionCancel()

功能: 在初始化时,如果设置了modesingle,可以调用该 API 将分机单独从远端服务器进行注销,建议第三方在没有特殊业务逻辑的情况优先考虑使用初始化mixin模式。(退出时,为了确保分机能够正确的发出和响应退出下线的消息内容,会有一个 2 秒钟的延迟。)

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
//因为初始化时已经传递了相关参数,这里执行不需要传递任何参数
dky.extensionCancel();

回调事件:

事件名称:dky:extensionCancel

执行了dky.extensionCancel()后会触发dky:extensionCancel事件,并且只有在modesingle时触发。

javascript
//eventFunciton函数的内部

dky.on("dky:extensionCancel", (e) => {
  if (e.state == "extensionCancel") {
    console.log("mode为single时的分机注销成功");
  } else {
    console.log("mode为single时的分机注销失败,失败原因:", e.description);
  }
});

回调事件参数说明:

返回示例:

json
{
  "mode": "single",
  "state": "extensionCancel",
  "description": "分机注销完成"
}
参数名称描述
mode初始化的模式
state状态
description状态的描述

7.1.7 切换座席绑定分机

功能: 在DKCloud系统切换分机时 或 在工具条登录状态下非通话中切换座席绑定的分机(IP话机、webrtc、手机号)。

发起者: 第三方

接收者: 点控云呼叫中心

回调事件:

事件名称:dky:extensionBindUpdate

javascript
// phoneNumber:当前绑定的号码
dky.on('dky:extensionBindUpdate', (data) => {
  if(phoneNumber === data['extension'] || phoneNumber === data.extension.replace(data.projectId, '')) {
    console.log('工具条修改绑定电话')
  } else {
    console.log('在后台修改绑定电话')
  }
  
 if (data['extensionType'] === 1) {
    console.log('更新页面展示信息 IP话机')
  } else if(data['extensionType'] === 2) {
    console.log('更新页面展示信息 webrtc')
  } else if (data['extensionType'] === 3) {
    console.log('更新页面展示信息 手机号')
  }
})

回调事件参数说明:

返回示例:

json
{
  "agentNumber": "xxxxxxxxxx",
  "agentTelNumber": "extensionCancel",
  "eventType": "EXTENSION_BIND_UPDATE",
  "extension": "",
  "extensionStateTime": "",
  "extensionType": "",
  "projectId": "",
  "realm": "",
  "reason": "",
  "requestId": "",
  "webrtcip": "",
}
参数名称描述
agentNumber座席号
agentTelNumber座席侧分机号码
eventType事件类型
extension分机号
extensionStateTime事件发生时间
extensionType分机类型 1:IP话机 2:webrtc 3:手机号
projectId登录注册的企业编码
realmwebrtc 分机注册的安全策略域
reason分机切换描述信息
requestId请求id
webrtcipwebrtc 分机注册的地址

7.2 座席状态相关

7.2.1 设置座席的置忙/置闲

API 名称: setAgentState()

功能: 设置座席的当前状态为置忙/置闲

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
//设置当前登录座席的闲状态
let object = {
  state: 2,
};
dky.setAgentState(object);

//设置当前登录座席的忙状态
let object = {
  state: 3,
  reason: "", //取值于点控云cc管理后台设定的值,如果不传递或者传递为空,将使用系统默认的原因
};
dky.setAgentState(object);

//班长座席修改某个普通座席的座席状态
//置闲
let object = {
  agentId: "1002",
  state: 2,
};
dky.setAgentState(object);
//置忙
let object = {
  agentId: "1002",
  state: 3,
  reason: "", //取值于点控云cc管理后台设定的值,如果不传递或者传递为空,将使用系统默认的原因
};
dky.setAgentState(object);

参数说明:

object 参数:

参数名称是否为必传备注
agentId班长座席调整的指定座席的座席号
state取值:2 - 空闲,3 - 忙碌 设置为忙碌时,只影响接听电话,不影响外呼电话。
reason取值于管理系统设置的自定义置忙原因,比如:meeting

回调事件:

事件名称:dky:agentState

执行了dky.setAgentState()后会触发dky:agentState事件。

javascript
//eventFunciton函数的内部

dky.on("dky:agentState", (e) => {
  if (e.state == 2) {
    console.log("设置座席状态为置闲");
  }
  if (e.state == 3) {
    console.log("设置座席状态为置忙,置忙原因为:", e.reason);
  }
});

回调事件参数说明:

返回示例:

json
{
  "reason": "busy",
  "extension": "100100100101001",
  "requestId": "0a7477ed1f844bdcb22f0fc325145ece",
  "state": 3,
  "agentNumber": "100100100101001",
  "projectId": "10010010010"
}
参数描述描述
agentNumber座席工号
projectId企业编码
extension分机号
state座席状态:2 - 空闲,3 - 忙碌
reason座席状态原因
requestId请求唯一值

7.2.2 查询座席状态

API 名称: agentState()

功能: 查询当前座席的当前状态

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.agentState();

回调事件:

事件名称:dky:agentState

执行了dky.agentState()后会触发dky:agentState事件。

javascript
//eventFunciton函数的内部

dky.on("dky:agentState", (e) => {
  if (e.option == "in") {
    console.log("当前座席通话方向为呼入,座席状态为:", e.currentState);
  }
  if (e.option == "out") {
    console.log("当前座席通话方向为呼出,座席状态为:", e.currentState);
  }
  if (e.option == "") {
    console.log("当前座席不在通话中,座席状态为:", e.currentState);
  }
});

返回示例:

json
{
  "crmId": "abc123",
  "telb": "18112345678",
  "lastLoginTime": "2024-07-11 19:19:09",
  "loginTime": "00:05:46",
  "currentStateTime": "00:05:42",
  "currentExtension": "1001",
  "agentName": "abc",
  "agentNumber": "1001",
  "currentState": "置忙",
  "option": "in"
}
参数名类型备注
crmIdInteger客户 crmId
agentNumberstring座席编号
agentNamestring座席姓名
currentStatestring当前座席状态(文字描述):空闲、置忙、整理、外呼发起、外呼振铃中、外呼通话中;发起咨询中、发起咨询振铃中、被发起咨询振铃中、咨询通话中、被咨询通话中;发起咨询转移中、发起咨询转移振铃中、被发起咨询转移振铃中、发起咨询转移通话中、被发起转移通话中;被发起转移振铃中、被转移通话中;发起咨询三方中、发起咨询三方振铃中、被发起咨询三方振铃中、咨询三方通话中、被咨询三方通话中;监听中、耳语中、监听三方中、强插中等等
currentStateTimestring当前状态持续时长(HH:mm:ss)
telbstring客户号码
optionstring呼叫指向,in - 呼入,out - 呼出,座席没有在通话的情况下为空值
loginTimestring登录时长(HH:mm:ss)
currentExtensionstring当前分机号

7.2.3 使用后端接口外呼时设置座席的自动接听状态

API 名称: setApiCallAutoAnswer()

功能: 在使用后端接口进行外呼时,但其他操作仍然调用 SDK 的 API 时,需要在调用后端接口前调用该 API,设置座席的外呼自动接听状态。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
function call() {
  dky.setApiCallAutoAnswer("autoAnswer");
  let timestamp = parseInt(new Date().getTime() / 1000); //时间戳
  let projectid = "10010010010"; //机构编码
  let token = "ADSFASF5SDF45WEF524EF5W4EF"; //token
  let md = md5(projectid + token + timestamp); //md5加密内容
  let data = {
    agentId: "", //发起外呼的座席号
    callednum: "", //被叫号码
  };
  axios
    .post(
      `https://ip/gw2/dky-open-api/version-2.0.0/placecall?projectId=${projectid}&timestamp=${timestamp}&sign=${md}`,
      data
    )
    .then((res) => {
      console.log("外呼接口成功返回的值");
    })
    .catch((err) => {
      console.log("外呼接口失败返回的值");
      dky.setApiCallAutoAnswer("recovery"); //这里在没有开启呼叫失败的语音提示时进行调用,开启了语音提示则不需要调用
    });
}
参数名称是否为必传备注
option取值:autoAnswer - 座席外呼自动接听,recovery - 恢复原有状态

7.2.4 强制下线事件

回调事件:

事件名称:dky:forceOffline

当座席被班长座席强制指定下线后,会触发dky:forceOffline事件。

javascript
//eventFunciton函数的内部

dky.on("dky:forceOffline", (e) => {
  console.log("当前座席被" + e.eventMessage);
});

返回示例:

json
{
  "eventMessage": "班长执行强制下线,系统已退出",
  "extension": "100100100101001",
  "monitorAgentId": "100100100101001",
  "requestId": "3596f52a68284c4daddd94254a6da148",
  "agentNumber": "100100100101001",
  "projectId": "10010010010",
  "description": "当前座席被强制下线!"
}
参数名称描述
eventMessage事件描述信息
agentNumber当前的座席号
extension当前的分机号
monitorAgentId班长的座席号
projectId企业编号
requestId请求唯一值
description描述信息

7.2.5 座席通话质量检测事件

座席完成登录后,通过该事件可以实时获取到当前通话网络的变化情况(每55-65秒随机检测并发送一次事件)

回调事件

事件名称:dky:sipOptions

js
//eventFunciton函数的内部

dky.on("dky:sipOptions", (data) => {
  console.log("sip的网络检测", data);
});

返回示例

正常检测返回示例:

json
{
    "state":"succeeded",
    "sendTime":1747712350931,
    "backTime":1747712350970,
    "delayTime":39,
    "extensionState":"yes"
}

发送错误时的返回示例

json
{
    "state":"failed",
    "message":"网络异常或者分机连接异常",
    "cause":"Connect error"
}
参数名称描述
state事件返回状态,succeed - 成功,failed - 失败
sendTime发送消息的时间戳,毫秒
backTime接收响应消息的时间戳,毫秒
delayTime网络延迟时间,毫秒
extensionState检测时当前分机的状态,yes - 正常在线,no - 下线
message发生错误时的提示
cause错误原因

通话质检标准参考表

延迟时间(单位毫秒)通话质量
[ 0, 40 )
[ 40, 60 )良好
[ 60, 80 )一般
[ 80, ∞ )较差

7.3 呼叫操作相关

7.3.1 外呼

API 名称: callphone()

功能: 发起呼叫

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.callphone(object);
let object = {
  // 参数示例
};

参数说明:

object 参数:

参数名称是否为必传备注
callednum要外呼的被叫号码,例如:18212345678
userfield用户自定义数据,定制对接。255 位长字符串 不含特殊符号 推荐 base64
tag风控标签
mobileTelx指定手机外显 (若指定外显 手机外显/固话外显/中继组集合外显/中继组 id 四个选项只能选一)
fixedTelx指定固话外显(若指定外显 手机外显/固话外显/中继组集合外显/中继组 id 四个选项只能选一)
trunkNumberGroups指定中继组集合外显(若指定外显 手机外显/固话外显/中继组集合外显/中继组 id 四个选项只能选一)
callStrategy指定中继组集合/中继组 id/号码组 ID 外显 呼叫策略 1-本地打本地 2-省会托底 3-随机(选择中继组/中继组 id/号码组 id 时需要配合该策略使用 默认本地打本地) 注:当只传递策略 不指定中继组或号码组时,按照策略匹配该机构下所有符合规则的号码
trunkNumberGroupId指定中继组 id 外显 (若指定外显 手机外显/固话外显/中继组集合外显/中继组 id 四个选项只能选一)
numberGroupId号码组 ID 可单独指定 也可和中继组集合/中继组 ID 搭配使用
rasrPush当前通话是否推送rasr 0:否 1:是

回调事件:

事件名称:dky:callphone

执行了dky.callphone()后会触发dky:callphone事件。

javascript
dky.on("dky:callphone", (data) => {
  console.log("外呼操作的事件", data);
});

回调事件参数说明:

返回示例:

json
{
  "agentId": "20020001001777777",
  "code": "200",
  "createTime": 1720698464649,
  "crmId": "",
  "data": {
    "linkedId": "cti2-1720698464832.100038"
  },
  "eventType": "AgentInterface",
  "extension": "20020001001777777",
  "innerAgentId": "20020001001777777",
  "message": "成功",
  "projectId": "20020001001",
  "requestId": "f88e7d8d81f643b1bb6a5832db8fed99",
  "type": "placecall"
}
参数名称描述
agentId登录注册的座席号
code状态码,200 为成功,非 200 为失败
createTime调用时间,10 位的时间戳
crmId自定义座席工号
linkedId通话唯一标识
eventType事件类型
extension登录注册的分机号
innerAgentId系统座席工号
message提示信息
projectId登录注册的企业编码
requestId请求 id 编码
type类型

外呼 code 码非 200 时,参数对照表

codemessage
400参数相关错误提示
403无权限,请联系管理员该号码不在白名单中未开启呼入手动满意度未开启呼出手动满意度座席仅限外呼国内号码座席仅限外呼国际号码
10000外呼号码为黑名单号码
26外呼号码触发风控
500系统繁忙,请稍后再试!当前通话未结束 呼叫失败号码资源相关错误(直接返回 dialresource 项目返回的错误提示)当前无通话监听失败,当前座席无通话监听失败,当前座席通话不是三方模式咨询失败,当前坐席处于通话中该时间段不可呼叫
501自备卡号未在白名单中
502队列/技能相关错误登录失败,当前座席未绑定队列登录失败,当前座席未绑定对应技能登陆失败,座席未绑定队列为 %s 的技能相同队列号已存在,请重新添加该优先级已存在队列,请重新添加队列【123】已在语音导航中配置,无法删除
503数据重复
504该数据不存在
20001超出座席登陆并发数
20002分机未绑定
20003分机不存在
20004分机不可用
20005号码:{}不可用,已被座席:{},工号:{}使用
20006未找到该座席编号
20007该座席未启用
20008座席未登录转接失败,当前座席未登录咨询失败,当前座席未登录监听失败,当前座席未登录
20009分机未注册转接失败,当前座席分机未注册咨询失败,当前座席分机未注册

7.3.2 接听

API 名称: answer()

功能: 接起座席的进电

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.answer();

回调事件:

事件名称:dky:answer

执行了dky.answer()后会触发dky:answer事件。

javascript
dky.on("dky:answer", (e) => {
  console.log("接听事件", e);
});

返回示例:

json
{
  "agentId": "700370010019001",
  "code": "200",
  "createTime": null,
  "crmId": "z9001",
  "data": {
    "forwardEvent": "0",
    "linkedId": "cti2-1720747495469.100005",
    "sipCallId": "e6613c6c-eb68-476f-b7d9-f8b799336970",
    "callLeg": "agent",
    "dialCode": "200",
    "eventType": "ANSWERED",
    "agentNumber": "700370010019001",
    "callType": "0",
    "customerDisplayNumber": "01056016014",
    "wrapupTime": 0,
    "crmId": "",
    "customerTelNumber": "18234554910",
    "agentTelNumber": "700370010019001",
    "customerTelCity": "山西|长治",
    "requestId": "e6613c6c-eb68-476f-b7d9-f8b799336970",
    "eventTime": "2024-07-12 09:24:56",
    "channelName": "PJSIP/regserver-00000885",
    "originateUserField": "",
    "projectId": "70037001001",
    "channelId": "cti2-1720747495469.100005",
    "option": "out"
  },
  "eventType": "ANSWERED",
  "extension": "700370010019001",
  "innerAgentId": "700370010019001",
  "message": "SUCCESS",
  "projectId": "70037001001",
  "requestId": "4ce1006af8364c5f8c3d0241df88fb32",
  "type": "ANSWERED"
}
参数名称描述
agentId登录注册的座席号
code状态码,200 为成功,非 200 为失败
createTime调用时间,10 位的时间戳
crmId自定义工号
forwardEvent
linkedId通话唯一标识
sipCallId
callLeg角色:agent - 座席侧,customer - 客户侧
dialCode信令状态码
eventType事件类型
agentNumber座席工号
callType通话类型:0 - 单呼,2 - 双呼,4 - 预测外呼,5 - IVR 语音,7 - webcall,8 - 监听,9 - 强插,10 - 耳语,11 - 转接座席,12 - 转外线,13 - 转 IVR,14 - 转队列,15 - 咨询转座席,16 - 咨询转外线,17 - 咨询三方,18 - 监听三方,19 - 咨询座席,20 - 咨询线,21 - 强拆,24 - 机器人外呼,30 - 呼入直呼节点,31 - 呼入进队列,32 - 预测外呼溢出到队列
customerDisplayNumber客户侧外显号码
wrapupTime整理时长
customerTelNumber被叫号码(客户号码)
agentTelNumber
customerTelCity被叫归属地
eventTime事件发出时间
requestId请求 id 编码
channelName通道名称
originateUserField自定义参数字段
projectId登录注册的企业编码
channelId通道 id
option呼叫方向:in - 呼入,out - 呼出
extension登录注册的分机号
innerAgentId系统座席号
type类型

7.3.3 挂断

API 名称: hangup()

功能: 座席主动结束当前正在进行的通话

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.hangup(agentId);

参数说明:

参数名称要求类型说明
agentIdString班长座席进行强拆时,必须传递需要强拆的座席号,其他情况可不传

回调事件:

事件名称:dky:hangup

执行了dky.hangup()后会触发dky:hangup事件。

javascript
dky.on("dky:hangup", (e) => {
  console.log("挂断事件", e);
});

回调事件参数说明:

返回示例:

json
{
  "agentId": "700370010019001",
  "code": "200",
  "createTime": null,
  "crmId": "z9001",
  "data": {
    "answerTime": "2024-07-12 12:20:34",
    "dialCode": "200",
    "answerStatusCode": "1",
    "callType": "0",
    "customerDisplayNumber": "01056016014",
    "duration": 17,
    "customerTelCity": "山西|长治",
    "eventTime": "2024-07-12 12:20:41",
    "originateUserField": "",
    "startTime": "2024-07-12 12:20:24",
    "billsec": 7,
    "channelId": "cti2-1720758024133.100017",
    "forwardEvent": "1",
    "linkedId": "cti2-1720758024133.100017",
    "ringTime": "2024-07-12 12:20:26",
    "sipCallId": "16317161-f4af-481a-9e63-7c7d58759ed1",
    "callLeg": "customer",
    "enterSatisfaction": "",
    "eventType": "HANGUP",
    "agentNumber": "700370010019001",
    "wrapupTime": 30,
    "crmId": "",
    "customerTelNumber": "18234554910",
    "agentTelNumber": "700370010019001",
    "channelName": "PJSIP/gateway-0000088b",
    "endTime": "2024-07-12 12:20:41",
    "projectId": "70037001001",
    "option": "out"
  },
  "eventType": "HANGUP",
  "extension": "18234554910",
  "innerAgentId": "700370010019001",
  "message": "SUCCESS",
  "projectId": "70037001001",
  "requestId": "d5e4bc6052e640f6aa1031994aca54e7",
  "type": "HANGUP"
}
参数名称描述
agentId登录注册的座席号
code状态码,200 为成功,非 200 为失败
createTime调用时间,10 位的时间戳
crmId自定义工号
answerTime应答时间
answerStatusCode
dialCode信令状态码
callType通话类型:0 - 单呼,2 - 双呼,4 - 预测外呼,5 - IVR 语音,7 - webcall,8 - 监听,9 - 强插,10 - 耳语,11 - 转接座席,12 - 转外线,13 - 转 IVR,14 - 转队列,15 - 咨询转座席,16 - 咨询转外线,17 - 咨询三方,18 - 监听三方,19 - 咨询座席,20 - 咨询线,21 - 强拆,24 - 机器人外呼,30 - 呼入直呼节点,31 - 呼入进队列,32 - 预测外呼溢出到队列
customerDisplayNumber客户侧外显号码
duration通话时长
customerTelCity被叫归属地
eventTime事件发出时间
originateUserField自定义参数字段
startTime开始时间
billsec
channelId通道 id
forwardEvent是否是转发过来的事件
linkedId通话唯一标识
ringTime响铃时间
sipCallId
callLeg角色:agent - 座席侧,customer - 客户侧
enterSatisfaction是否进入满意度,非空为进入满意度,空值为没有进入满意度
eventType事件类型
agentNumber座席工号
wrapupTime整理时长
customerTelNumber被叫号码(客户号码)
agentTelNumber座席分机号
requestId请求 id 编码
channelName通道名称
endTime结束时间
projectId登录注册的企业编码
channelId通道 id
option呼叫方向:in - 呼入,out - 呼出
extension登录注册的分机号
innerAgentId系统座席号
type类型

7.3.4 响铃事件

回调事件:

事件名称:dky:ring

座席进电或者外呼后会自动触发dky:ring事件。

javascript
dky.on("dky:ring", (data) => {
  console.log("响铃事件", data);
});

返回示例:

json
{
  "agentId": "700370010019001",
  "code": "200",
  "createTime": null,
  "crmId": "z9001",
  "data": {
    "forwardEvent": "0",
    "linkedId": "cti2-1720759437227.100019",
    "sipCallId": "acb6cf37-6cd7-4b95-b72c-e31bee6aea2d",
    "callLeg": "customer",
    "dialCode": "183",
    "eventType": "RINGING",
    "agentNumber": "700370010019001",
    "callType": "0",
    "customerDisplayNumber": "01056016014",
    "wrapupTime": 0,
    "crmId": "",
    "customerTelNumber": "18234554910",
    "agentTelNumber": "700370010019001",
    "customerTelCity": "山西|长治",
    "requestId": "75d7ed29b9b54225979c38afe20ad3c3",
    "eventTime": "2024-07-12 12:43:59",
    "channelName": "PJSIP/gateway-0000088d",
    "originateUserField": "",
    "customerDecodeTelNumber": "18234554910",
    "projectId": "70037001001",
    "channelId": "cti2-1720759437.4376",
    "option": "out"
  },
  "eventType": "RINGING",
  "extension": "18234554910",
  "innerAgentId": "700370010019001",
  "message": "SUCCESS",
  "projectId": "70037001001",
  "requestId": "4a96ff2d4dbd4128a58235c7e965270d",
  "type": "RINGING"
}
参数名称描述
agentId登录注册的座席号
code状态码,200 为成功,非 200 为失败
createTime调用时间,10 位的时间戳
crmId自定义工号
forwardEvent
linkedId通话唯一标识
sipCallId
callLeg角色:agent - 座席侧,customer - 客户侧
dialCode信令状态码
eventType事件类型
agentNumber座席工号
callType通话类型:0 - 单呼,2 - 双呼,4 - 预测外呼,5 - IVR 语音,7 - webcall,8 - 监听,9 - 强插,10 - 耳语,11 - 转接座席,12 - 转外线,13 - 转 IVR,14 - 转队列,15 - 咨询转座席,16 - 咨询转外线,17 - 咨询三方,18 - 监听三方,19 - 咨询座席,20 - 咨询线,21 - 强拆,24 - 机器人外呼,30 - 呼入直呼节点,31 - 呼入进队列,32 - 预测外呼溢出到队列
customerDisplayNumber客户侧外显号码
wrapupTime整理时长
customerTelNumber被叫号码(客户号码)
agentTelNumber
customerTelCity被叫归属地
eventTime事件发出时间
requestId请求 id 编码
channelName通道名称
originateUserField自定义参数字段
projectId登录注册的企业编码
channelId通道 id
option呼叫方向:in - 呼入,out - 呼出
extension登录注册的分机号
innerAgentId系统座席号
type类型

7.3.5 保持

API 名称: hold()

功能: 座席保持当前通话不中断,此时座席侧和客户侧同时会放起等待音乐

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.hold();

回调事件:

事件名称:dky:hold

执行dky.hold()会触发dky:hold事件。

javascript
dky.on("dky:hold", (data) => {
  console.log("呼叫保持的事件", data);
});

返回示例:

json
{
  "mode": "hold",
  "extension": "700370010019001",
  "requestId": "398139f987a94c2cb2ca2b6c555ea364",
  "agentNumber": "700370010019001",
  "projectId": "70037001001",
  "description": "通话保持开启"
}
参数名称描述
mode状态:hold- 通话保持开启,unhold - 通话保持结束
agentNumber座席号/工号
extension分机号
projectId企业编码
requestId请求唯一值
description描述信息

7.3.6 保持接回

API 名称: unhold()

功能: 将座席侧和客户侧保持状态下等待通话接回,只有在保持状态下可用。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.unhold();

回调事件:

事件名称:dky:hold

执行dky.unhold()会触发dky:hold事件。

javascript
dky.on("dky:hold", (data) => {
  console.log("取消呼叫保持的事件", data);
});

返回示例:

json
{
  "mode": "unhold",
  "extension": "700370010019001",
  "requestId": "2defa869a9a047af9cb503701ab6edda",
  "agentNumber": "700370010019001",
  "projectId": "70037001001",
  "description": "通话保持结束"
}
参数名称描述
mode状态:hold- 通话保持开启,unhold - 通话保持结束
agentNumber座席号/工号
extension分机号
projectId企业编码
requestId请求唯一值
description描述信息

7.3.7 静音

API 名称: mute()

功能: 通话过程中将座席侧进行静音,通话保持不断开。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.mute();

回调事件:

事件名称:dky:agentMute

执行dky.mute()会触发dky:agentMute事件。

javascript
dky.on("dky:agentMute", (data) => {
  console.log("座席静音的事件", data);
});

返回示例:

json
{
  "mode": "mute",
  "extension": "700370010019001",
  "requestId": "7b8459314cb344179f35a6839fa77c48",
  "agentNumber": "700370010019001",
  "projectId": "70037001001",
  "description": "座席侧静音开启"
}
参数名称描述
mode状态:mute - 静音开启,unmute - 静音结束
agentNumber座席号/工号
extension分机号
projectId企业编码
requestId请求唯一值
description描述信息

7.3.8 取消静音

API 名称: unmute()

功能: 通话过程中座席侧的静音取消,恢复正常通话。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.unmute();

回调事件:

事件名称:dky:agentMute

执行dky.unmute()会触发dky:agentMute事件。

javascript
dky.on("dky:agentMute", (data) => {
  console.log("座席取消静音的事件", data);
});

返回示例:

json
{
  "mode": "unmute",
  "extension": "700370010019001",
  "requestId": "cafdb942a6154597af3cf85e312ccfbf",
  "agentNumber": "700370010019001",
  "projectId": "70037001001",
  "description": "座席侧静音结束"
}
参数名称描述
mode状态:mute - 静音开启,unmute - 静音结束
agentNumber座席号/工号
extension分机号
projectId企业编码
requestId请求唯一值
description描述信息

7.3.9 咨询

API 名称: consult()

功能: 通话过程中当前座席向其他座席进行咨询,被咨询座席在接受当前座席咨询时,不能够主动挂断咨询来电。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.consult(object);

参数说明:

object 参数:

参数名称要求备注
mode必传0 - 默认 咨询内部座席, 1 - 咨询外线号码
atxferNumber必传需要咨询的座席号:mode 为 0 时 传转接的座席 id(被咨询的座席必须登录),mode 为 1 时,传外线号码

回调事件:

事件名称:dky:consult

执行dky.consult()会触发dky:consult事件。

javascript
dky.on("dky:consult", (e) => {
  console.log("咨询的事件", e);
});

返回示例:

json
{
  "mode": "0",
  "extension": "700370010019001",
  "requestId": "fcf8bd473b4a40ccaeca33d56afdcc49",
  "atxferNumber": "70037001001998",
  "agentNumber": "700370010019001",
  "projectId": "70037001001",
  "atxferAgentId": "70037001001998"
}
参数名称描述
mode0 - 咨询内部坐席, 1 - 咨询外线号码
extension监听发起的分机号
requestId请求 id 编码
atxferNumber被咨询的座席号
agentNumber监听发起的座席号
projectId企业编号
atxferAgentId被咨询的座席号

7.3.10 咨询转接

API 名称: atxfer()

功能: 通话过程中当前座席将座席当前通话转接给其他坐席或外线,在和其他转接座席侧交谈后,当前座席如果选择挂断,则客户和转接座席侧实现通话,当前座席如果选择接回,则恢复客户与当前座席的通话。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.atxfer(object);

参数说明:

object 参数:

参数名称要求备注
mode必传0 - 默认 咨询内部座席, 1 - 咨询外线号码
atxferNumber必传需要咨询转接的座席号:mode 为 0 时 传转接的座席 id(被咨询转接座席必须登录),mode 为 1 时,传外线号码

回调事件:

事件名称:dky:atxfer

执行dky.atxfer()会触发dky:atxfer事件。

javascript
dky.on("dky:atxfer", (e) => {
  console.log("咨询转接的事件", e);
});

返回示例:

json
{
  "mode": "0",
  "extension": "700370010019001",
  "requestId": "dae01f1f87ee46d9abc08c8e0dc035e4",
  "atxferNumber": "70037001001998",
  "agentNumber": "700370010019001",
  "projectId": "70037001001",
  "atxferAgentId": "70037001001998"
}
参数名称描述
mode0 - 咨询内部坐席, 1 - 咨询外线号码
extension监听发起的分机号
requestId请求 id 编码
atxferNumber被咨询的座席号
agentNumber监听发起的座席号
projectId企业编号
atxferAgentId被咨询的座席号

7.3.11 咨询转接(咨询)挂断(接回)

API 名称: atxferHangup()

功能: 咨询转接/转接发出后,在被转接座席未接通前或者接通后,将通话进行接回,恢复和客户侧的正常通话。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.atxferHangup();

回调事件:

事件名称:dky:hangup

执行dky.atxferHangup()会触发dky:hangup事件,具体事件信息参考 7.3.3 挂断相关事件信息。

通话状态中且未处于咨询通话执行 dky.atxferHangup(),会触发dky:atxferHangup事件

javascript
dky.on('dky:meetHangup', (data) => {
  console.log('未处于咨询通话状态调用接回', data)
})

返回试例:

javascript
{
  "state":"error",
  "description":"当前坐席未处于咨询通话无法调用咨询接回"
}
参数名称描述
state状态:error
description错误信息描述

7.3.12 座席转接

API 名称: blindMove()

功能: 将当前座席的当前通话转接给其他座席或者外线号码,转接座席侧响铃,当前座席自动挂断。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.blindMove(object);

参数说明:

object 参数:

参数名称要求备注
mode0 - 将电话转接给内部座席,座席必须登录 1 - 将电话转接给外部号码 2 - 将电话转给 IVR 3 - 将电话转入队列, 默认值为 0
transferNumber必传当 mode 为 0 时 传转接的座席的 id(转接的座席必须登录),当 mode 为 1 时,传需要转接的外线号码,当 mode 为 2 时,传需要转接的 IVR 的 id,当 mode 为 3 时,传需要转接的队列号
ivrNodeIdivr 节点 id ivrNodeId 和 ivrNodeType 搭配使用两个参数不传 则默认当前 ivrId 起始位置播放两个参数传入 则从对应 ivr 节点开始播放
ivrNodeTypeivr 节点类型 ivrNodeId 和 ivrNodeType 搭配使用两个参数不传 则默认当前 ivrId 起始位置播放两个参数传入 则从对应 ivr 节点开始播放

回调事件:

事件名称:dky:blindMove

执行dky.blindMove()会触发dky:blindMove事件。

javascript
dky.on("dky:blindMove", (e) => {
  console.log("座席转接的事件", e);
});
json
{
  "mode": "0",
  "extension": "700370010019001",
  "requestId": "5c575d1ef2c74b85ac2d39251563dd93",
  "agentNumber": "700370010019001",
  "transferNumber": "70037001001998",
  "projectId": "70037001001",
  "transferAgentId": "70037001001998"
}
参数名称描述
agentNumber监听发起的座席号
extension监听发起的分机号
mode转接模式:0 - 将电话转接给内部座席,座席必须登录 1 - 将电话转接给外部号码 2 - 将电话转给 IVR 3 - 将电话转入队列
projectId企业编号
requestId请求唯一值

7.3.13 咨询三方

API 名称: meet()

功能: 当前座席发起对另一个座席或者外线号码的咨询请求,被发起座席或者外线号码接听后加入到当前通话中进行通话。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.meet(object);

参数说明:

object 参数:

参数名称要求备注
mode必传0 - 默认 咨询内部坐席, 1 - 咨询外线号码
agentId必传座席号码
meetrNumber必传需要咨询三方的座席号:mode 为 0 时 传转接的座席 id(被咨询的三方座席必须登录),mode 为 1 时,传外线号码

回调事件:

事件名称:dky:meet

执行dky.meet()会触发dky:meet事件。

javascript
dky.on("dky:meet", (e) => {
  console.log("咨询三方的事件", e);
});

返回示例:

json
{
  "mode": "0",
  "extension": "700370010019001",
  "meetAgentId": "70037001001998",
  "requestId": "ced23ba0070e43fdba2ea2c974c4b0df",
  "agentNumber": "700370010019001",
  "projectId": "70037001001"
}
参数名称描述
agentNumber监听发起的座席号
extension监听发起的分机号
mode0 - 咨询内部坐席, 1 - 咨询外线号码
projectId企业编号
requestId请求唯一值

7.3.14 咨询三方挂断/接回

API 名称: meetHangup()

功能: 当前座席发起对另一个座席或者外线号码的咨询请求,被发起座席或者外线号码接听后加入到当前通话中进行通话。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.meetHangup(type);

参数说明:

参数名称要求备注
type必传hangup - 咨询三方挂断,retrieve - 咨询三方接回

回调事件:

咨询三方接回后,会触发dky:answer接听事件,具体事件信息请参考 7.3.2 接听事件内容。

通话中且未处于咨询三方通话状态执行dky.meetHangup('retrieve'),会触发dky:meetHangup事件

javascript
dky.on('dky:meetHangup', (data) => {
  console.log('未处于咨询三方通话状态调用接回', data)
})

返回试例:

javascript
{
  "state":"error",
  "description":"当前坐席未处于咨询三方通话无法调用咨询三方接回"
}
参数名称描述
state状态:error
description错误信息描述

7.3.15 满意度

API 名称: serviceLevel()

功能: 座席与客户通话结束后,座席通过调用后,给客户播放满意度评价录音,客户按键,完成此次通话的评价。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.serviceLevel();

回调事件:

事件名称:dky:serviceLevel

执行dky.serviceLevel()会触发dky:serviceLevel事件。

返回示例:

json
{
  "agentId": "700370010019100",
  "code": "200",
  "createTime": 1720768241775,
  "crmId": "",
  "data": {},
  "eventType": "AgentInterface",
  "extension": "700370010019100",
  "innerAgentId": "700370010019100",
  "message": "成功",
  "projectId": "70037001001",
  "requestId": "e2411ca0e7b44f9caba194c732f5462e",
  "type": "servicelevel"
}
参数名称描述
agentId座席工号
codecode 状态码
createTime调用时间
crmId自定义工号
eventType事件类型
extension分机号
innerAgentId系统座席工号
message描述信息
projectId企业编码
requestId请求 id 编码
type类型

7.3.16 二次拨号

API 名称: playdtmf()

功能: 座席与客户的号码接通后,座席可能听到对方是一段语音,“直拨分机号码,查号请按 0”。这时需要调用此接口,将需要的按键,如 802 发送到对方的线路上,相当于在电话上按了 802。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.playdtmf(object);

参数说明:

object 参数:

参数名称要求备注
digit必传按键值,例如:0123456789*#
agentId必传根据 agentType 传入对应内容:坐席工号/客户 crmId(需要创建坐席时指定)
agentType坐席类型 0 - 系统坐席工号(默认) ,1 - 客户 crmId

7.3.17 设置外呼接通提示音

API 名称: callAnswerPrompt()

功能: SDK外呼接听提示音参数。当客户外呼通话接通时,系统可向座席播放一段自定义提示音,支持客户上传自定义音频文件作为提示音,如果未配置自定义文件,系统则使用默认“嘟”声。此功能仅适用于外呼接听场景,不涉及通话转接环节。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.on("dky:init", (data) => {
  // 默认提示音 dky.callAnswerPrompt()
  // 自定义提示音
  const url = 'https://example.com/toot.mp3'
  dky.callAnswerPrompt(url)
})

参数说明:

参数名称是否为必传备注
url非必传自定义外呼接通提示音文件地址,仅支持使用 https 域名地址。

7.4 监听相关

7.4.1 监听

API 名称: chanspy()

功能: 操作班长座席的监听或者咨询功能,根据参数不同可以做到以下功能:

  • 静音模式 (班长可以听到座席和客户的声音,座席及客户听不到班长的声音)
  • 耳语模式(班长说话,只能座席听到)
  • 强插(班长强行接入通话)
  • 监听三方(班长监听咨询三方通话)

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.chanspy(object);

参数说明:

object 参数:

参数名称要求备注
mode必传监听模式:0 - 耳语模式, 1 - 监听模式, 2-强插,3-监听三方
agentId必传监听的座席号码
point指定班长侧号码方式:0 - 默认值 班长号为内部坐席 1 - 班长号为外线号码
monitorNumber必传班长的号码 point 为 0 时传班长坐席号 point 为 1 时传外线号码

回调事件:

事件名称:dky:chanspy

执行dky.chanspy()会触发dky:chanspy事件。

javascript
dky.on("dky:chanspy", (data) => {
  console.log("监听/咨询的事件", data);
});

返回示例:

json
{
  "agentId": "9100",
  "mode": 1,
  "monitorNumber": "998",
  "point": 0
}
参数名称描述
agentId监听的座席号
mode监听模式:0 - 耳语模式, 1 - 静音模式,2-强插,3-监听三方
monitorNumber班长座席号
point班长侧号码方式:0 - 默认值 班长号为内部坐席 1 - 班长号为外线号码

7.5 其他

7.5.1 强制下线事件

回调事件:

事件名称:dky:forceOffline

当座席被班长座席强制指定下线后,会触发dky:forceOffline事件。

javascript
dky.on("dky:forceOffline", (e) => {
  console.log("强制下线事件", e);
});

返回示例:

json
{
  "eventMessage": "班长执行强制下线,系统已退出",
  "extension": "700370010019100",
  "monitorAgentId": "70037001001998",
  "requestId": "3596f52a68284c4daddd94254a6da148",
  "agentNumber": "700370010019100",
  "projectId": "70037001001",
  "description": "当前座席被强制下线!"
}
参数名称描述
eventMessage事件描述信息
agentNumber当前的座席号
extension当前的分机号
monitorAgentId班长的座席号
projectId企业编号
requestId请求唯一值
state错误状态:success - 操作成功,error - 操作失败
description描述信息

7.5.2 实时数据事件

回调事件:

事件名称:dky:realTimeData

首次登录和每次通话结束后,会主动推送一次dky:realTimeData实时数据事件。

javascript
dky.on("dky:realTimeData", (e) => {
  console.log("实时数据事件", e);
});

返回示例:

json
{
  "effectiveCallDuration": 136,
  "effectiveCallCount": 3,
  "callCount": 3
}
参数名称描述
callCount外呼次数
effectiveCallCount有效外呼次数
effectiveCallDuration有效外呼时长,单位:秒

7.5.3 呼入非接通挂断置忙事件

回调事件:

事件名称:dky:incomingNotConnectedHangup

在呼入场景中,当座席收到客户来电并进入振铃状态时,如果座席拒接电话,系统将自动将座席状态改为“置忙”状态。如果座席振铃时间达到超时设定且座席还未接听客户来电,系统同样会将座席状态改为“置忙”。并触发dky:incomingNotConnectedHangup事件。

javascript
dky.on("dky:incomingNotConnectedHangup", (data) => {
  console.log("呼入非接通挂断置忙事件", data);
});

返回示例:

json
{
  "agentId": "700370010019100",
  "agentState": 3,
  "linkedId": "cti1-1720769862.13706",
  "message": "呼叫超时,座席状态已自动置忙",
  "extension": "700370010019100"
}
参数名称描述
agentId座席号/工号
agentState座席状态:3 - 忙碌
linkedId当前通话唯一值
extension分机号
message描述 信息

7.5.4 座席/分机掉线重连事件

回调事件:

事件名称:dky:reconnect

当座席或者分机因网络原因或者其他非正常推出导致的座席或者分机发生下线时,会接触发dky:reconnect重连事件,SDK内部会自动发起座席或者分机的重连,重连次数上限为10,如果重连10次仍未能正常连接,那么SDK会主动结束连接并退出系统。

js
dky.on('dky:reconnect`', (e) => {
    console.log('座席/分机掉线重连事件', e)
})

返回示例:

js
{
	"state":"reconnecting",
    "num":1,
    "type":"agent",
    "description":"座席掉线重连中"
}

参数说明:

参数名称描述
state当前状态:reconnecting:掉线重连中,reconnected:重连成功,reconnectFail:重连失败
num当前重连的次数
type类型:agent:座席掉线,sip:分机掉线
description事件描述信息

7.5.5 音频设备检测

API 名称: checkMedia()

功能: 获取到当前浏览器音频设备是否可以正常进行使用。

发起者: 第三方

接收者: 点控云呼叫中心

调用方式:

javascript
dky.checkMedia();

回调事件:

事件名称:dky:deviceDetection

执行dky.checkMedia()会触发dky:deviceDetection事件。

javascript
dky.on("dky:deviceDetection", (e) => {
  console.log("音频设备检测事件", e);
});

返回示例:

json
{
  "state": "success",
  "info": "[object MediaStream]"
}
参数名称描述
state设备状态,success为正常,error为错误
info获取到的设备信息

7.5.6 网络延迟时间

回调事件:

事件名称:dky:networkDelay

点控云 SDK2 版本提供了网络延迟时间监听事件,当初始化参数 delayTime 设置为 true 时,通过dky:networkDelay事件便可以实时监听当前座席的网络延迟时间,如果初始化参数 delayTime 设置为 false,将不会接收到网络延迟时间的事件信息。

javascript
dky.on("dky:networkDelay", (data) => {
  console.log("网络延迟时间的事件", data);
});

返回示例:

json
{
  "mode": "mixin",
  "delayTime": 3,
  "description": "网络延迟时间"
}
参数名称描述
mode初始化的模式
delayTime网络延迟时间,单位毫秒
description状态的描述

7.5.7 SIP 响应码

7.5.7.1 1xx: 信息性响应
响应码响应码名称说明
100Trying正在尝试处理请求
180Ringing被叫方正在振铃
181Call Is Being Forwarded呼叫正在被转接
182Queued呼叫已排队
183Session Progress会话正在进行中
7.5.7.2 2xx: 成功响应
响应码响应码名称说明
200OK请求成功
202Accepted请求已被接受,但尚未处理
7.5.6.3 3xx: 重定向响应
响应码响应码名称说明
300Multiple Choices有多个选择
301Moved Permanently请求的资源已永久移动
302Moved Temporarily请求的资源已临时移动
305Use Proxy必须使用代理来访问请求的资源
380Alternative Service提供了可用的替代服务
7.5.7.4 4xx: 请求错误
响应码响应码名称说明
400Bad Request请求无效,服务器无法理解请求
401Unauthorized请求需要身份验证
403Forbidden服务器拒绝请求
404Not Found用户不存在
405Method Not Allowed请求的方法不被允许
406Not Acceptable请求的资源不满足客户端的条件
408Request Timeout请求超时,服务器未收到请求
410Gone用户不再可用
413Request Entity Too Large请求实体太大
414Request-URI Too Long请求 URI 太长
415Unsupported Media Type不支持的媒体类型
416Unsupported URI Scheme不支持的 URL 方案
420Bad Extension扩展不可用
421Extension Required需要特定扩展才能处理请求
423Interval Too Brief间隔时间太短
480Temporarily Unavailable用户临时不可达
481Call/Transaction Does Not Exist呼叫/事务不存在
482Loop Detected检测到循环
483Too Many Hops跳数过多
484Address Incomplete地址不完整
485Ambiguous地址不明确
486Busy Here用户忙
487Request Terminated请求被终止
488Not Acceptable Here此处不可接受
491Request Pending请求挂起
493Undecipherable消息体无法解密
7.5.7.5 5xx: 服务器错误
响应码响应码名称说明
500Server Internal Error服务器内部错误
501Not Implemented服务器不支持请求的方法
502Bad Gateway网关错误
503Service Unavailable服务不可用
504Server Time-out服务器超时
505Version Not SupportedSIP 版本不支持
513Message Too Large消息体过大
7.5.7.6 6xx: 全局失败响应
响应码响应码名称说明
600Busy Everywhere全部用户忙
603Decline拒绝
604Does Not Exist Anywhere用户不存在于任何地方
606Not Acceptable不可接受

7.5.8 弱网检测分机掉线

回调事件:

事件名称:dky:weakSignalDetection

当分机因网络原因导致分机发生掉线时但座席状态正常,这种情况下外呼会接触发dky:weakSignalDetection事件,SDK内部会自动发起座席或者分机的重连,重连次数上限为10,如果重连10次仍未能正常连接,那么SDK会主动结束连接并退出系统。分机重连的事件请参考7.5.4

js
dky.on('dky:weakSignalDetection', (data) => {
    console.log(data)
})

返回示例:

js
{
  message: "检测到网络异常,正在尝试重连,请稍后。",
  state: 'error',
  time: '',
}
参数名称描述
state状态:'error'/'success'
time时间
message描述 信息