| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Document</title>
- <script src="./jssip.min.js"></script>
- <script src="./uni.webview.1.5.4.js"></script>
- <script src="./axios.min.js"></script>
- </head>
- <body>
- <script>
- let { extension, password, user_agent, baseURL, ip: server_ip } = getURLParameters(location.search);
- const sockets = [
- { ip: '47.122.16.161', id: 1 },
- ];
- const ws_uri = "wss://" + server_ip;
- const isRemote = 'remote'
- const isLocal = 'local'
- let localStream = new MediaStream() // 本地媒体流
- const localAudio = new Audio // 本地流
- const remoteAudio = new Audio // 远程流
- const emtryRecord = new Audio //来电铃声
- // emtryRecord.src = './wav/ring.wav'
- emtryRecord.src = './wav/emtry.mp3'
- emtryRecord.loop = true;
- emtryRecord.play();
- const ringtone = new Audio //来电铃声
- ringtone.src = './wav/ring.wav'
- ringtone.loop = true
- const ringbacktone = new Audio // 挂断铃声
- ringbacktone.src = './wav/hangup.wav'
- const phoneStatus = {
- isRing: false, // 外呼振铃
- isBusy: false, // 通话(来电外呼通用)
- isIncoming: false, // 来电振铃
- isEnd: false, // 挂断
- isLeisure: true // 空闲
- };
- const phoneStatusInfo = {
- msg: '空闲中',
- color: '#16c4af'
- };
- let currentSession = null; // 当前会话
- let incomingSession = null; // 来电会话
- let lineList = []; // 线路数据
- let calleePhone = ''; // 当前来电的手机号码 (不管外呼来电 都当对方的号码)
- let callerPhone = ''; // 当前主叫的手机号码 (不管外呼来电 都当我方的号码)
- let jsSipStatus = false; // 签入状态
- let redisStatus = false; // redis中的状态
- let registerInfo = null; // 分机的信息
- // 设置jsSip状态
- function setJsSipStatus(status) {
- jsSipStatus = userAgent.isRegistered();
- uni.postMessage({
- data: {
- data: jsSipStatus,
- action: "singInState",
- }
- });
- }
- // 设置线路
- function setLineList(lineList) {
- lineList = lineList;
- }
- function setPhoneStats(key) {
- Object.keys(phoneStatus).forEach(k => {
- if (k === key) {
- phoneStatus[k] = true
- switch (key) {
- case 'isRing':
- phoneStatusInfo.msg = '振铃中'
- phoneStatusInfo.color = '#1890ff'
- break
- case 'isBusy':
- phoneStatusInfo.msg = '通话中'
- phoneStatusInfo.color = '#4b7902'
- break
- case 'isIncoming':
- phoneStatusInfo.msg = '来电中'
- phoneStatusInfo.color = '#aa00ab'
- break
- case 'isEnd':
- phoneStatusInfo.msg = '挂断中'
- phoneStatusInfo.color = '#d9041e'
- break
- case 'isLeisure':
- // 重置手机号码
- phoneStatusInfo.msg = '空闲中'
- phoneStatusInfo.color = '#16c4af'
- break
- default:
- phoneStatusInfo.msg = '其他'
- phoneStatusInfo.color = '#333333'
- break
- }
- } else {
- phoneStatus[k] = false
- }
- });
- uni.postMessage({
- data: {
- data: phoneStatus,
- action: "phoneStatus",
- }
- });
- }
- let userAgent = null;
- function getURLParameters(url) {
- var params = {};
- var queryString = url.split('?')[1];
- if (queryString) {
- var keyValuePairs = queryString.split('&');
- for (var i = 0; i < keyValuePairs.length; i++) {
- var keyValuePair = keyValuePairs[i].split('=');
- var key = decodeURIComponent(keyValuePair[0]);
- var value = decodeURIComponent(keyValuePair[1] || '');
- params[key] = value;
- }
- }
- return params;
- }
- // 处理来电
- function handleCalled({ action }) {
- if (action === "answer") {
- currentSession.answer({
- 'audio': true,
- 'mediaConstraints': {
- 'video': false
- }
- });
- setPhoneStats("isBusy");
- } else if (action === "hangup") {
- ringtone.pause()
- ringbacktone.play()
- currentSession.terminate()
- setPhoneStats('isLeisure');
- }
- }
- function unReg() {
- return new Promise((resolve, reject) => {
- if (!phoneStatus.isLeisure) {
- reject()
- } else {
- if (userAgent) {
- userAgent.on('unregistered', () => {
- setJsSipStatus();
- getRedisStatus();
- setTimeout(() => {
- register();
- }, 2000)
- resolve();
- })
- userAgent.unregister(true)
- userAgent.unregister({ all: true })
- userAgent.terminateSessions()
- userAgent.stop()
- } else {
- setTimeout(() => {
- register();
- }, 2000)
- resolve()
- // Message.info('当前已经是签出状态')
- }
- }
- })
- }
- // 挂断
- function hangup() {
- if (!phoneStatus.isLeisure) {
- userAgent.terminateSessions()
- ringbacktone.play()
- }
- }
- // 获取redis的状态
- async function getRedisStatus() {
- const { data: result } = await axios.get(baseURL + "/sip/JcSip/getExtensionStatus", {
- params: {
- extension
- },
- });
- const { data } = result;
- redisStatus = data != null;
- registerInfo = data;
- uni.postMessage({
- data: {
- data: registerInfo,
- action: "registerInfo",
- }
- });
- }
- function handleCall(params) {
- if (!jsSipStatus || !redisStatus) {
- uni.postMessage({
- data: {
- data: "签入状态异常",
- action: "message",
- type: "error"
- }
- });
- return
- } else if (!phoneStatus.isLeisure) {
- uni.postMessage({
- data: {
- data: phoneStatusInfo.msg + ",请等待状态变为空闲",
- action: "message",
- type: "error"
- }
- });
- return
- }
- const options = {
- 'eventHandlers': {
- //振铃中
- progress: function () {
- console.log("振铃中");
- // success
- setPhoneStats('isBusy');
- callerPhone = params.callPhone;
- calleePhone = params.phone;
- let socket = null;
- if (registerInfo.register) {
- socket = sockets.find(v => v.ip === registerInfo.register['IPv4']);
- }
- if (!socket) {
- // 如果是空的就取第一个
- socket = sockets[0];
- }
- const callParams = {
- id: params.recordingId,// cdr的id
- caseId: params.caseId,
- phone: params.phone,
- callee: params.x_callee, // 加密的被叫
- caller: params.x_callPhone, // 加密主叫
- callType: '2', // 1- 呼入 2-呼出 3-外访
- phoneType: '4', // 1-新生代ip 2-枫软固化 3-同网 4-回龙
- ipAddressId: socket.id // 录音保存的服务器id
- }
- uni.postMessage({
- data: {
- data: callParams,
- action: "outbound",
- }
- });
- },
- //呼叫失败
- failed: function () {
- setPhoneStats('isLeisure');
- },
- //通话结束
- ended: function () {
- // success
- console.log('通话结束')
- setPhoneStats('isEnd');
- },
- //通话中
- confirmed: function () {
- // success
- console.log('已建立通道')
- }
- },
- 'mediaConstraints': { 'audio': true, 'video': false },
- 'extraHeaders': [
- 'X-DIALER-STRING: ' + params.replacedContact,
- 'X-CALLEE: ' + params.x_callee,
- 'X-CALLER: ' + params.x_caller,
- 'X-ORGID: ' + params.orgId,
- 'X-RECORDINGID: ' + params.recordingId,
- 'X-TYPE: ' + '1',
- 'X-SYSTEM-CODE: ' + params.systemCode,
- 'X-BANK-CODE: ' + params.bankCode,
- 'X-BANK-BATCH-CODE: ' + params.bankBatchCode,
- 'X-EXTEN-NUM: ' + params.extension,
- 'X-USER-ID: ' + params.userId,
- 'X-NICK-NAME: ' + params.nickName,
- 'X-DEPT-ID: ' + params.deptId,
- 'X-CALL-PHONE: ' + params.x_callPhone,
- 'X-PERSON-PHONE: ' + params.x_callee
- ],
- 'sessionTimersExpires': 180
- }
- console.log("外呼:" + params.phone);
- userAgent.call(params.phone, options);
- };
- function register() {
- return new Promise((resolve, reject) => {
- const config = {
- sockets: [],
- // uri: 'sip:%s@' + server_ip + ':11450',
- uri: 'sip:%s@' + server_ip,
- // authorization_user: '1005',
- password: '',
- // WebSocket重新连接调度之间的最大间隔(秒)。默认值是30。
- connection_recovery_max_interval: 60,
- // WebSocket重新连接尝试之间的最小间隔(秒)。默认值是2。
- connection_recovery_min_interval: 4,
- // 外显
- // display_name: '10086',
- // 来电后,120秒内未接听,将会拒绝.默认60s
- no_answer_timeout: 120,
- // 会话计时器,默认true
- session_timers: false,
- // 用于刷新会话计时器的SIP方法。有效值是UPDATE或INVITE。默认值是UPDATE。
- session_timers_refresh_method: 'UPDATE',
- // 是否应该在启动时自动注册.默认true
- register: true,
- // 注册过期时间(秒) 默认值是600。
- register_expires: 6000,
- // User-Agent header中的值,默认是JsSIP
- // registrar_server: 'sip:' + server_ip,
- }
- // 补充配置文件中 分机号和分机号密码
- config.uri = config.uri.replace('%s', extension)
- config.password = password
- config.user_agent = user_agent // 给注册添加一个用户账号的识别标识
- // const wsSocket = new JsSIP.WebSocketInterface("wss://172.16.7.100:7565")
- console.log(ws_uri)
- const wsSocket = new JsSIP.WebSocketInterface(ws_uri)
- config.sockets = [wsSocket]
- userAgent = new JsSIP.UA(config)
- // 启动用户代理,开始与服务器进行通信
- userAgent.start()
- //注册成功
- userAgent.on('registered', () => {
- console.log("注册成功");
- setJsSipStatus();
- getRedisStatus();
- resolve();
- // uni.postMessage({
- // data: {
- // data: "注册成功",
- // action: "message",
- // type: "success",
- // }
- // });
- });
- //连接服务断开
- userAgent.on('disconnected', (disconnectedData) => {
- console.log("连接服务断开");
- reject();
- setJsSipStatus();
- getRedisStatus();
- if (disconnectedData.code && disconnectedData.code === 1006) {
- userAgent.stop();
- register().then(() => {
- console.log("重新连接");
- }).catch(() => {
- console.log("重连失败,重新创建");
- uni.postMessage({
- data: {
- action: "disconnected"
- }
- });
- })
-
- return;
- }
- // uni.postMessage({
- // data: {
- // data: "连接服务断开",
- // action: "message",
- // type: "error"
- // }
- // });
- })
- //注册失败
- userAgent.on('registrationFailed', (UnRegisteredEvent) => {
- console.log("注册失败");
- setJsSipStatus();
- getRedisStatus();
- if (UnRegisteredEvent.response) {
- uni.postMessage({
- data: {
- data: '分机号' + extension + ':' + UnRegisteredEvent.response.reason_phrase,
- action: "message",
- type: "error"
- }
- });
- } else {
- uni.postMessage({
- data: {
- data: '分机号' + extension + ':' + "签入失败",
- action: "message",
- type: "error"
- }
- });
- }
- reject();
- });
- //注销回调
- userAgent.on('unregistered', () => {
- console.log("unregistered");
- reject();
- setJsSipStatus();
- getRedisStatus();
- })
- userAgent.on('newRTCSession', function (data) {
- const session = data.session
- const originator = data.originator
- const request = data.request
- currentSession = session;
- session.on('accepted', function (acceptedData) {
- ringtone.pause() // 关闭来电铃声
- currentSession = session;
- if (acceptedData.originator === isLocal) {
- // 来电时触发
- let connection = session.connection
- let receivers = connection.getReceivers() // 处理远程流
- if (receivers) {
- receivers.forEach(item => {
- localStream.addTrack(item.track)
- })
- remoteAudio.srcObject = localStream
- remoteAudio.play()
- remoteAudio.volume = 1
- }
- }
- // else{
- // // 外呼
- // }
- })
- session.on('failed', function (failedData) {
- // 对方来电主动挂断
- if (phoneStatus.isIncoming) {
- calleePhone = "";
- if (failedData.originator === isRemote) {
- // info
- console.log('会话已过期,对方主动挂断电话')
- ringtone.pause()
- ringbacktone.play()
- // state.calleeNotify.close()
- } else {
- // 我方主动挂断
- // info
- // console.log('拒接成功')
- ringtone.pause()
- ringbacktone.play()
- setPhoneStats("isLeisure");
- }
- } else {
- // 等于null不再执行 已接听电话
- if (failedData.message === null) return
- if (['Request Terminated'].includes(failedData.message.reason_phrase)) {
- uni.postMessage({
- data: {
- data: "会话终止,对方可能拒接您的来电",
- action: "message",
- type: "error"
- }
- });
- // error
- } else {
- if (lineList.length > 0 && callerPhone) {
- const line = lineList.find(v => v.callPhone === callerPhone);
- uni.postMessage({
- data: {
- data: '线路:' + callerPhone + (line ? ('(' + line.type + ')') : '') + ',failed:' + failedData.message.reason_phrase,
- action: "message",
- type: "error"
- }
- });
- } else {
- uni.postMessage({
- data: {
- data: 'failed:' + failedData.message.reason_phrase,
- action: "message",
- type: "error"
- }
- });
- }
- // error
- }
- setPhoneStats("isLeisure");
- }
- })
- session.on('ended', function () {
- setPhoneStats("isLeisure");
- if (originator === isRemote) {
- // 来电挂断
- // success
- console.log('来电挂断')
- calleePhone = "";
- // state.calleeNotify.close()
- }
- // else 外呼挂断不再这里处理
- })
- session.on('getusermediafailed', function () {
- console.log('获取用户媒体设备失败')
- // error
- })
- // 处理session
- if (originator === isRemote) {
- incomingSession = session;
- // 处理来电
- const sipUri = incomingSession.remote_identity.uri.toString()
- calleePhone = sipUri.match(/sip:(\d+)@/)[1]
- // 铃声
- ringtone.play();
- setPhoneStats("isIncoming");
- const recordingId = request.getHeader('X-RECORDINGID')
- const ip = request.getHeader('X-IP')
- const callPhone = request.getHeader('X-CALL-PHONE')
- const personPhone = request.getHeader('X-PERSON-PHONE');
- const socket = sockets.find(v => v.ip === ip)
- if (recordingId) {
- const params = {
- id: recordingId,// cdr的id
- phone: calleePhone, // 来电号码(真实未加密)
- callee: callPhone, // 加密的被叫
- caller: personPhone, // 加密主叫
- callType: '1', // 1- 呼入 2-呼出 3-外访
- phoneType: '4', // 1-新生代ip 2-枫软固化 3-同网 4-回龙
- ipAddressId: socket.id // 录音保存的服务器id
- }
- uni.postMessage({
- data: {
- data: params,
- action: "incoming"
- }
- });
- } else {
- uni.postMessage({
- data: {
- data: "来电没有返回录音id,请一定要联系管理员",
- action: "message",
- type: "error"
- }
- });
- }
- // success
- console.log('有来电')
- } else {
- // 外呼时触发
- session.connection.addEventListener('track', (event) => {
- localAudio.srcObject = event.streams[0]
- localAudio.play()
- localAudio.volume = 1
- })
- }
- })
- })
- }
- document.addEventListener('UniAppJSBridgeReady', function () {
- register();
- });
- </script>
- </body>
- </html>
|