Appearance
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.0 | 2024-03-22 | 马凯特 | |
| 2.1.1 | 2024-04-11 | 马凯特 | |
| 2.1.2 | 2024-05-23 | 马凯特 | |
| 2.1.3 | 2024-05-30 | 马凯特 | |
| 2.1.4 | 2024-06-06 | 马凯特 | |
| 2.1.5 | 2024-06-13 | 马凯特 | |
| 2.2.0 | 2024-07-01 | 马凯特 | |
| 2.2.1 | 2024-07-18 | 马凯特 | |
| 2.2.2 | 2024-08-22 | 马凯特 | |
| 2.2.3 | 2024-09-05 | 马凯特 | |
| 2.2.4 | 2024-09-24 | 马凯特 | |
| 2.2.5 | 2024-10-24 | 马凯特 | |
| 2.2.6 | 2024-11-07 | 马凯特 | |
| 2.3.0 | 2024-12-05 | 马凯特 | |
| 2.3.1 | 2025-01-14 | 增加机器人外呼呼叫类型 | 马凯特 | 
| 2.3.2 | 2025-02-21 | 杨佳 | |
| 2.3.3 | 2025-03-25 | 杨佳 | |
| 2.4.0 | 2025-05-07 | 杨佳 | |
| 2.4.0.1 | 2025-05-20 | 杨佳 | |
| 2.4.1 | 2025-06-04 | 杨佳 | |
| 2.4.2 | 2025-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()后只有在mode为mixin时会触发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()
功能: 将座席和分机从远端服务器上进行退出和注销,无论mode是mixin的模式还是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()
功能: 在初始化时,如果设置了mode为single,可以调用该 API 将座席单独登录在远端服务器上,如果需要注册分机还需要在座席登录完成后再调用分机的注册,建议第三方在没有特殊业务逻辑的情况优先考虑使用初始化mixin模式。
发起者: 第三方
接收者: 点控云呼叫中心
调用方式:
javascript
//因为初始化时已经传递了相关参数,这里执行不需要传递任何参数
dky.agentLogin();回调事件:
事件名称:dky:agentLogin
执行了dky.agentLogin()后会触发dky:agentLogin事件,并且只有在mode为single时触发。
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 - 班长座席 | 
| pjsipPort | PJSIP 分机注册的端口号 | 
| realm | webrtc 分机注册的安全策略域 | 
| webrtcip | webrtc 分机注册的地址 | 
| eventType | 事件类型 | 
| innerAgentId | 系统座席工号 | 
| message | 提示信息 | 
| projectId | 登录注册的企业编码 | 
| requestId | 请求 id 编码 | 
| type | 类型 | 
7.1.4 座席退出登录 
API 名称: agentExit()
功能: 在初始化时,如果设置了mode为single,可以调用该 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事件,并且只有在mode为single时触发。
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()
功能: 在初始化时,如果设置了mode为single,可以调用该 API 将分机单独在远端服务器进行注册,需要先进行座席的登录再进行分机的注册,同时第三方在没有特殊业务逻辑的情况优先考虑使用初始化mixin模式。
发起者: 第三方
接收者: 点控云呼叫中心
调用方式:
javascript
//因为初始化时已经传递了相关参数,这里执行不需要传递任何参数
dky.extensionRegister();回调事件:
事件名称:dky:extensionRegister
执行了dky.extensionRegister()后会触发dky:extensionRegister事件,并且只有在mode为single时触发。
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()
功能: 在初始化时,如果设置了mode为single,可以调用该 API 将分机单独从远端服务器进行注销,建议第三方在没有特殊业务逻辑的情况优先考虑使用初始化mixin模式。(退出时,为了确保分机能够正确的发出和响应退出下线的消息内容,会有一个 2 秒钟的延迟。)
发起者: 第三方
接收者: 点控云呼叫中心
调用方式:
javascript
//因为初始化时已经传递了相关参数,这里执行不需要传递任何参数
dky.extensionCancel();回调事件:
事件名称:dky:extensionCancel
执行了dky.extensionCancel()后会触发dky:extensionCancel事件,并且只有在mode为single时触发。
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 | 登录注册的企业编码 | 
| realm | webrtc 分机注册的安全策略域 | 
| reason | 分机切换描述信息 | 
| requestId | 请求id | 
| webrtcip | webrtc 分机注册的地址 | 
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"
}| 参数名 | 类型 | 备注 | 
|---|---|---|
| crmId | Integer | 客户 crmId | 
| agentNumber | string | 座席编号 | 
| agentName | string | 座席姓名 | 
| currentState | string | 当前座席状态(文字描述):空闲、置忙、整理、外呼发起、外呼振铃中、外呼通话中;发起咨询中、发起咨询振铃中、被发起咨询振铃中、咨询通话中、被咨询通话中;发起咨询转移中、发起咨询转移振铃中、被发起咨询转移振铃中、发起咨询转移通话中、被发起转移通话中;被发起转移振铃中、被转移通话中;发起咨询三方中、发起咨询三方振铃中、被发起咨询三方振铃中、咨询三方通话中、被咨询三方通话中;监听中、耳语中、监听三方中、强插中等等 | 
| currentStateTime | string | 当前状态持续时长(HH:mm:ss) | 
| telb | string | 客户号码 | 
| option | string | 呼叫指向,in - 呼入,out - 呼出,座席没有在通话的情况下为空值 | 
| loginTime | string | 登录时长(HH:mm:ss) | 
| currentExtension | string | 当前分机号 | 
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}×tamp=${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 时,参数对照表
| code | message | 
|---|---|
| 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);参数说明:
| 参数名称 | 要求 | 类型 | 说明 | 
|---|---|---|---|
| agentId | String | 班长座席进行强拆时,必须传递需要强拆的座席号,其他情况可不传 | 
回调事件:
事件名称: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"
}| 参数名称 | 描述 | 
|---|---|
| mode | 0 - 咨询内部坐席, 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"
}| 参数名称 | 描述 | 
|---|---|
| mode | 0 - 咨询内部坐席, 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 参数:
| 参数名称 | 要求 | 备注 | 
|---|---|---|
| mode | 0 - 将电话转接给内部座席,座席必须登录 1 - 将电话转接给外部号码 2 - 将电话转给 IVR 3 - 将电话转入队列, 默认值为 0 | |
| transferNumber | 必传 | 当 mode 为 0 时 传转接的座席的 id(转接的座席必须登录),当 mode 为 1 时,传需要转接的外线号码,当 mode 为 2 时,传需要转接的 IVR 的 id,当 mode 为 3 时,传需要转接的队列号 | 
| ivrNodeId | ivr 节点 id ivrNodeId 和 ivrNodeType 搭配使用两个参数不传 则默认当前 ivrId 起始位置播放两个参数传入 则从对应 ivr 节点开始播放 | |
| ivrNodeType | ivr 节点类型 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 | 监听发起的分机号 | 
| mode | 0 - 咨询内部坐席, 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 | 座席工号 | 
| code | code 状态码 | 
| 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: 信息性响应 
| 响应码 | 响应码名称 | 说明 | 
|---|---|---|
| 100 | Trying | 正在尝试处理请求 | 
| 180 | Ringing | 被叫方正在振铃 | 
| 181 | Call Is Being Forwarded | 呼叫正在被转接 | 
| 182 | Queued | 呼叫已排队 | 
| 183 | Session Progress | 会话正在进行中 | 
7.5.7.2 2xx: 成功响应 
| 响应码 | 响应码名称 | 说明 | 
|---|---|---|
| 200 | OK | 请求成功 | 
| 202 | Accepted | 请求已被接受,但尚未处理 | 
7.5.6.3 3xx: 重定向响应 
| 响应码 | 响应码名称 | 说明 | 
|---|---|---|
| 300 | Multiple Choices | 有多个选择 | 
| 301 | Moved Permanently | 请求的资源已永久移动 | 
| 302 | Moved Temporarily | 请求的资源已临时移动 | 
| 305 | Use Proxy | 必须使用代理来访问请求的资源 | 
| 380 | Alternative Service | 提供了可用的替代服务 | 
7.5.7.4 4xx: 请求错误 
| 响应码 | 响应码名称 | 说明 | 
|---|---|---|
| 400 | Bad Request | 请求无效,服务器无法理解请求 | 
| 401 | Unauthorized | 请求需要身份验证 | 
| 403 | Forbidden | 服务器拒绝请求 | 
| 404 | Not Found | 用户不存在 | 
| 405 | Method Not Allowed | 请求的方法不被允许 | 
| 406 | Not Acceptable | 请求的资源不满足客户端的条件 | 
| 408 | Request Timeout | 请求超时,服务器未收到请求 | 
| 410 | Gone | 用户不再可用 | 
| 413 | Request Entity Too Large | 请求实体太大 | 
| 414 | Request-URI Too Long | 请求 URI 太长 | 
| 415 | Unsupported Media Type | 不支持的媒体类型 | 
| 416 | Unsupported URI Scheme | 不支持的 URL 方案 | 
| 420 | Bad Extension | 扩展不可用 | 
| 421 | Extension Required | 需要特定扩展才能处理请求 | 
| 423 | Interval Too Brief | 间隔时间太短 | 
| 480 | Temporarily Unavailable | 用户临时不可达 | 
| 481 | Call/Transaction Does Not Exist | 呼叫/事务不存在 | 
| 482 | Loop Detected | 检测到循环 | 
| 483 | Too Many Hops | 跳数过多 | 
| 484 | Address Incomplete | 地址不完整 | 
| 485 | Ambiguous | 地址不明确 | 
| 486 | Busy Here | 用户忙 | 
| 487 | Request Terminated | 请求被终止 | 
| 488 | Not Acceptable Here | 此处不可接受 | 
| 491 | Request Pending | 请求挂起 | 
| 493 | Undecipherable | 消息体无法解密 | 
7.5.7.5 5xx: 服务器错误 
| 响应码 | 响应码名称 | 说明 | 
|---|---|---|
| 500 | Server Internal Error | 服务器内部错误 | 
| 501 | Not Implemented | 服务器不支持请求的方法 | 
| 502 | Bad Gateway | 网关错误 | 
| 503 | Service Unavailable | 服务不可用 | 
| 504 | Server Time-out | 服务器超时 | 
| 505 | Version Not Supported | SIP 版本不支持 | 
| 513 | Message Too Large | 消息体过大 | 
7.5.7.6 6xx: 全局失败响应 
| 响应码 | 响应码名称 | 说明 | 
|---|---|---|
| 600 | Busy Everywhere | 全部用户忙 | 
| 603 | Decline | 拒绝 | 
| 604 | Does Not Exist Anywhere | 用户不存在于任何地方 | 
| 606 | Not 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 | 描述 信息 |