| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 |
- package midware.service.init;
- import com.alibaba.fastjson2.JSON;
- import lombok.extern.slf4j.Slf4j;
- import midware.service.eslclient.entity.Agent;
- import midware.service.eslclient.entity.Channel;
- import midware.service.eslclient.EslCommon;
- import midware.service.eslclient.EslEventListener;
- import midware.service.eslclient.entity.Session;
- import midware.util.config.EslClientConfig;
- import midware.util.enums.EslCommandEnum;
- import midware.util.enums.EslEventEnum;
- import midware.util.helper.StringHelper;
- import midware.util.helper.TtsHelper;
- import org.freeswitch.esl.client.inbound.Client;
- import org.freeswitch.esl.client.transport.message.EslMessage;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.core.annotation.Order;
- import org.springframework.stereotype.Service;
- import javax.annotation.PostConstruct;
- import javax.annotation.PreDestroy;
- import java.io.File;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.concurrent.ScheduledThreadPoolExecutor;
- import java.util.concurrent.TimeUnit;
- import java.util.stream.Stream;
- @Slf4j
- @Service
- @Order(1)
- public class EslClientService {
- @Autowired
- private Client client;
- @Autowired
- private EslClientConfig config;
- @PostConstruct
- public void init() {
- client.addEventListener(new EslEventListener());
- //单独起1个线程,定时检测连接状态
- new ScheduledThreadPoolExecutor(1).scheduleAtFixedRate(() -> {
- if (!client.canSend()) {
- try {
- client.connect(config.getHost(), config.getPort(), config.getPassword(), config.getTimeout());
- //移除事件
- client.cancelEventSubscriptions();
- Thread.sleep(500);
- //移除事件
- client.cancelEventSubscriptions();
- //设置监听事件
- client.setEventSubscriptions("plain", "all");
- Stream.of(EslEventEnum.values()).filter(s -> StringHelper.isEmpty(s.getSubclass()))
- .map(Enum::name).forEach(s -> client.addEventFilter("Event-Name", s));
- Stream.of(EslEventEnum.values()).filter(s -> StringHelper.isNotEmpty(s.getSubclass()))
- .map(EslEventEnum::getSubclass).forEach(s -> client.addEventFilter("Event-Subclass", s));
- //扫描已注册的分机
- scanExten();
- } catch (Exception e) {
- log.error("fs reConnect Exception", e);
- try {
- client.close();
- } catch (Exception e1) {
- }
- }
- }
- }, 1, 5000, TimeUnit.MILLISECONDS);
- if (client.canSend()) {
- //删除所有坐席
- delAgentAll();
- //扫描已注册的分机
- scanExten();
- //设置中间件根目录
- String path=new File("").getAbsolutePath();
- client.sendAsyncApiCommand(EslCommandEnum.global_setvar.name(), "md_base_dir=" +path );
- }
- }
- @PreDestroy
- public void destroy() {
- if (EslCommon.agents.size() > 0) {
- for (Agent a : EslCommon.agents) {
- logout(a.getAgent(), a.getGroup());
- }
- }
- }
- //删除所有坐席
- public void delAgentAll() {
- try {
- String command = EslCommandEnum.callcenter_config.name();
- //获取坐席组中所有坐席
- EslMessage message = client.sendSyncApiCommand(command, "tier list");
- if (message != null && message.getBodyLines().size() > 0) {
- for (String line : message.getBodyLines()) {
- if (!line.startsWith("queue") && !line.startsWith("+OK")) {
- String[] lines = line.split("\\|");
- client.sendAsyncApiCommand(command, "agent del " + lines[1]);
- client.sendAsyncApiCommand(command, "tier del " + lines[0] + " " + lines[1]);
- }
- }
- }
- //获取所有坐席
- EslMessage message1 = client.sendSyncApiCommand(command, "agent list");
- if (message1 != null && message1.getBodyLines().size() > 0) {
- for (String line : message1.getBodyLines()) {
- if (!line.startsWith("name") && !line.startsWith("+OK")) {
- String[] lines = line.split("\\|");
- client.sendAsyncApiCommand(command, "agent del " + lines[0]);
- }
- }
- }
- } catch (Exception e) {
- log.error("删除所有坐席失败", e);
- }
- }
- //扫描已注册的分机
- public void scanExten() {
- try {
- //挂断所有
- client.sendSyncApiCommand("hupall", "");
- //获取已注册的分机
- EslMessage message = client.sendSyncApiCommand("sofia status profile internal reg", "");
- if (message != null && message.getBodyLines().size() > 0) {
- for (String line : message.getBodyLines()) {
- if (line.startsWith("Auth-User:")) {
- String exten = line.substring(10).trim();
- Channel chan = EslCommon.getChanByExten(exten);
- if (chan == null) {
- chan = new Channel();
- chan.setType(1);
- chan.setNumber(exten);
- }
- EslCommon.channels.add(chan);
- }
- }
- }
- } catch (Exception e) {
- log.error("扫描已注册的分机失败", e);
- }
- }
- //坐席签入
- public String login(String agent, String ext, String group) {
- String result = "";
- try {
- String command = EslCommandEnum.callcenter_config.name();
- // 添加座席
- String arg = " agent add " + agent + " Callback";
- result = client.sendAsyncApiCommand(command, arg);
- // 设置呼叫字符串
- arg = " agent set contact " + agent + " [call_timeout=30]user/" + ext;
- client.sendAsyncApiCommand(command, arg);
- // 座席登录后默认空闲
- arg = " agent set status " + agent + " Available";
- client.sendAsyncApiCommand(command, arg);
- // 座席登录后默认空闲
- arg = " agent set state " + agent + " Waiting";
- client.sendAsyncApiCommand(command, arg);
- // 最大未接次数,到达次数后不再转接,0禁用
- arg = " agent set max_no_answer " + agent + " 0";
- client.sendAsyncApiCommand(command, arg);
- // 话后处理时间(s)
- //成功处理一个通话后,多久才会有电话进入的等待时长
- arg = " agent set wrap_up_time " + agent + " 20";
- client.sendAsyncApiCommand(command, arg);
- // 挂机间隔时间(s)
- //来电拒接后多久才会有电话进入的等待时长,0禁用
- arg = " agent set reject_delay_time " + agent + " 0";
- client.sendAsyncApiCommand(command, arg);
- // 忙重试间隔时间(s)
- //来电遇忙后多久才会有电话进入的等待时长
- arg = " agent set busy_delay_time " + agent + " 0";
- client.sendAsyncApiCommand(command, arg);
- // 添加梯队到队列等价于坐席组
- group = StringHelper.isEmpty(group) ? "ZXZ" : group;
- arg = " tier add " + group + " " + agent + " 1 1";
- client.sendAsyncApiCommand(command, arg);
-
- } catch (Exception e) {
- log.error(agent + "|" + ext + "|" + group + " 签入失败", e);
- }
- return result;
- }
- //坐席签出
- public String logout(String agent, String group) {
- String result = "";
- try {
- String command = EslCommandEnum.callcenter_config.name();
- // 删除座席
- String arg = " agent del " + agent;
- result = client.sendAsyncApiCommand(command, arg);
- // 删除梯队
- group = StringHelper.isEmpty(group) ? "ZXZ" : group;
- arg = " tier del " + group + " " + agent;
- client.sendAsyncApiCommand(command, arg);
-
- } catch (Exception e) {
- log.error(agent + "|" + group + " 签出失败", e);
- }
- return result;
- }
- //坐席置忙/置闲
- public String setWork(String agent, boolean isWork) {
- String result = "";
- try {
- String command = EslCommandEnum.callcenter_config.name();
- String state = isWork ? "'Available'" : "'On Break'";
- String arg = " agent set status " + agent + " " + state;
- result = client.sendAsyncApiCommand(command, arg);
-
- } catch (Exception e) {
- String state = isWork ? "置闲" : "置忙";
- log.error(agent + "|" + isWork + " " + state + "失败", e);
- }
- return result;
- }
- //删除
- public String kill(String chanId) {
- String result = "";
- try {
- result = client.sendAsyncApiCommand(EslCommandEnum.uuid_kill.name(), chanId);
-
- } catch (Exception e) {
- log.error(chanId + " 删除失败", e);
- }
- return result;
- }
- //分机呼叫
- public String extenCall(String callerNum, String calleeNum) {
- String result = "";
- try {
- String command = EslCommandEnum.originate.name();
- String arg = " {origination_caller_id_number=" + callerNum + ",call_called=" + calleeNum+",record_concat_video=true"
- + ",transfer_ringback=local_stream://moh}user/" + callerNum + " &bridge(" + getCallString(calleeNum)+")";
- result = client.sendAsyncApiCommand(command, arg);
- } catch (Exception e) {
- log.error(callerNum + "|" + calleeNum + " 分机呼叫失败", e);
- }
- return result;
- }
- //协商呼叫
- public String consult(String chanId, String callerNum,String calleeNum) {
- String result = "";
- try {
- String command = EslCommandEnum.uuid_broadcast.name();
- String arg = chanId + " att_xfer::{origination_caller_id_number=" + callerNum+",record_concat_video=true"
- + ",call_called=" + calleeNum + "}" + getCallString(calleeNum);
- result = client.sendAsyncApiCommand(command, arg);
-
- } catch (Exception e) {
- log.error(chanId + "|" + calleeNum + " 协商呼叫失败", e);
- }
- return result;
- }
- //强插
- public String insert(String callerNum,String calleeNum, String sessionId) {
- String result = "";
- try {
- String command = EslCommandEnum.originate.name();
- String arg = " {origination_caller_id_number=" + callerNum + ",cc_member_session_uuid=" + sessionId
- + ",call_called=" + calleeNum + "}user/" + callerNum + " &three_way(" + sessionId + ")";
- result = client.sendAsyncApiCommand(command, arg);
- } catch (Exception e) {
- log.error(callerNum + "|" + calleeNum + "|" + sessionId + " 强插失败", e);
- }
- return result;
- }
- //强截
- public String intercept(String callerNum,String calleeNum, String chanId,String sessionId) {
- String result = "";
- try {
- String command = EslCommandEnum.originate.name();
- String arg = " {origination_caller_id_number=" + callerNum+ ",cc_member_session_uuid=" + sessionId
- + ",call_called=" + calleeNum + "}user/" + callerNum + " &intercept(" + chanId + ")";
- result = client.sendAsyncApiCommand(command, arg);
-
- } catch (Exception e) {
- log.error(callerNum+ "|" +calleeNum + "|" + chanId + "|" +sessionId+ " 强截失败", e);
- }
- return result;
- }
- //监听
- public String listen(String callerNum,String calleeNum, String sessionId) {
- String result = "";
- try {
- String command = EslCommandEnum.originate.name();
- String arg = " {origination_caller_id_number=" + callerNum+ ",cc_member_session_uuid=" + sessionId
- + ",call_called=" + calleeNum+ "}user/" + callerNum + " &eavesdrop(" + sessionId + ")";
- result = client.sendAsyncApiCommand(command, arg);
-
- } catch (Exception e) {
- log.error(callerNum + "|" + sessionId + " 监听失败", e);
- }
- return result;
- }
- //播放排队位置
- public String playPosition(int num, String chanId) {
- String result = "";
- try {
- String command = EslCommandEnum.uuid_broadcast.name();
- //String path = "/home/wav/" + num + ".mp3";
- String path = TtsHelper.TextToSpeechPosition("你当前的位置为" + num, num + "");
- String arg = chanId + " playback::" + path;
- result = client.sendAsyncApiCommand(command, arg);
- } catch (Exception e) {
- log.error(num + "|" + chanId + " 播放坐席工号失败", e);
- }
- return result;
- }
- //播放坐席工号
- public String playAgent(String agent, String chanId) {
- String result = "";
- try {
- // String command = EslCommandEnum.sched_broadcast.name();
- // String path = "/home/wav/8001.wav";
- // //1秒后播放坐席工号
- // String arg = " +1 " + chanId + " playback::" + path + " both";
- String command = EslCommandEnum.uuid_broadcast.name();
- //String path = "/home/wav/8001.wav";
- String at = agent;
- try {
- Integer.parseInt(agent);
- at = agent.replaceAll("(.)", " $1").substring(1);
- } catch (Exception ex) { }
- String path = TtsHelper.TextToSpeechAgent("你好," + at + "号话务员为您服务", agent);
- String arg = chanId + " playback::" + path + " both";
- result = client.sendAsyncApiCommand(command, arg);
- } catch (Exception e) {
- log.error(chanId + " 播放坐席工号失败", e);
- }
- return result;
- }
- //会话加入会议
- public String talkJoinMeeting(String sessionId) {
- String result = "";
- try {
- String command = EslCommandEnum.uuid_transfer.name();
- //String arg = sessionId + " -both " + sessionId + " xml ExtenMeeting";
- String at = "threeway";
- Session session = EslCommon.getSessionById(sessionId);
- if (session != null && session.isVideo()) at = "video-mcu-stereo";
- String arg = sessionId + " -both 'set:hangup_after_bridge=false,set:record_concat_video=true,"
- + "conference:" + sessionId + "@" + at + "' inline";
- result = client.sendAsyncApiCommand(command, arg);
- } catch (Exception e) {
- log.error(sessionId + " 会话加入会议失败", e);
- }
- return result;
- }
- //呼叫号码加入会议
- public String callJoinMeeting(String callerNum, String calleeNum, String meetingId) {
- String result = "";
- try {
- String command = EslCommandEnum.originate.name();
- String at = "threeway", argstr = "";
- Session session = EslCommon.getSessionById(meetingId);
- if (session != null && session.isVideo()) {
- at = "video-mcu-stereo";
- String parentPath = "files/video/meeting/" + new SimpleDateFormat("yyyyMMdd").format(new Date());
- String path = new File(parentPath).getAbsolutePath()+ "/" + meetingId + ".mp4";
- session.setVideoPath(parentPath+ "/" + meetingId + ".mp4");
- argstr = ",record_concat_video=true,conference_auto_record=" + path;
- }
- String arg = " {origination_caller_id_number=" + callerNum + ",cc_member_session_uuid=" + meetingId
- + ",call_called=" + calleeNum + argstr + "}"
- //+ getCallString(calleeNum) + " " + meetingId + " xml ExtenMeeting";
- + getCallString(calleeNum) + " &conference(" + meetingId + "@" + at + ")";
- result = client.sendAsyncApiCommand(command, arg);
- } catch (Exception e) {
- log.error(callerNum + "|" + calleeNum + "|" + meetingId + " 呼叫加入会议失败", e);
- }
- return result;
- }
- //开启/关闭静音
- public String setMute(String chanId, boolean isMute) {
- String result = "";
- try {
- String command = EslCommandEnum.uuid_audio.name();
- String state = isMute ? " start" : " stop";
- String arg = chanId + state + " write mute -4";
- result = client.sendAsyncApiCommand(command, arg);
-
- } catch (Exception e) {
- String state = isMute ? "开启" : "关闭";
- log.error(chanId + " " + state + "静音失败", e);
- }
- return result;
- }
- //开启/关闭保持
- public String setHold(String chanId, boolean isHold) {
- String result = "";
- try {
- String command = EslCommandEnum.uuid_hold.name();
- String state = isHold ? " " : " off";
- String arg = state + " " + chanId;
- result = client.sendAsyncApiCommand(command, arg);
-
- } catch (Exception e) {
- String state = isHold ? "开启" : "关闭";
- log.error(chanId + " " + state + "保持失败", e);
- }
- return result;
- }
- //录音
- public String record(String chanId, String filePath) {
- String result = "";
- try {
- String command = EslCommandEnum.uuid_record.name();
- String arg = chanId + " start " + filePath;
- result = client.sendAsyncApiCommand(command, arg);
- } catch (Exception e) {
- log.error(chanId + "|" + filePath + " 录音失败", e);
- }
- return result;
- }
- //录音停止
- public String recordStop(String chanId, String filePath) {
- String result = "";
- try {
- String command = EslCommandEnum.uuid_record.name();
- String arg = chanId + " stop " + filePath;
- result = client.sendAsyncApiCommand(command, arg);
- } catch (Exception e) {
- log.error(chanId + "|" + filePath + " 录音停止失败", e);
- }
- return result;
- }
- //转满意度
- public String turnMyd(String chanId) {
- String result = "";
- try {
- String command = EslCommandEnum.uuid_transfer.name();
- //String arg = chanId + " turnmyd xml ForExten";
- String arg = chanId + " 'set:hangup_after_bridge=false,ivr:myd' inline";
- result = client.sendAsyncApiCommand(command, arg);
-
- } catch (Exception e) {
- log.error(chanId + " 转满意度失败", e);
- }
- return result;
- }
- //转移号码
- public String transfer(String chanId,String callerNum, String calleeNum) {
- String result = "";
- try {
- String command = EslCommandEnum.uuid_transfer.name();
- //String arg = chanId + " " + calleeNum + " xml ForExten";
- String arg = chanId + " 'set:hangup_after_bridge=false,set:record_concat_video=true,"
- +"bridge:{origination_caller_id_number=" + callerNum + "}" + getCallString(calleeNum) + "' inline";
- result = client.sendAsyncApiCommand(command, arg);
- } catch (Exception e) {
- log.error(chanId + "|" + calleeNum + " 转移失败", e);
- }
- return result;
- }
- //发送按键
- public String sendDtmf(String chanId, String dtmf) {
- String result = "";
- try {
- String command = EslCommandEnum.uuid_send_dtmf.name();
- String arg = chanId + " " + dtmf;
- result = client.sendAsyncApiCommand(command, arg);
-
- } catch (Exception e) {
- log.error(chanId + "|" + dtmf + " 发送按键失败", e);
- }
- return result;
- }
- //设置会议人员离开是否播放声音
- public String setConferenceExitSound(String meetingId, boolean isplay) {
- String result = "";
- try {
- String command = EslCommandEnum.conference.name();
- String arg = meetingId + " exit_sound " + (isplay ? "on" : "off");
- result = client.sendAsyncApiCommand(command, arg);
-
- } catch (Exception e) {
- log.error(meetingId + "|" + isplay + " 设置会议离开失败", e);
- }
- return result;
- }
- //设置会议人员是否静音
- public String setConferenceIsMute(String meetingId, String memberId, boolean isMute) {
- String result = "";
- try {
- String command = EslCommandEnum.conference.name();
- if (isMute) {
- String arg = meetingId + " deaf " + memberId;
- client.sendAsyncApiCommand(command, arg);
- arg = meetingId + " mute " + memberId;
- result = client.sendAsyncApiCommand(command, arg);
- } else {
- String arg = meetingId + " undeaf " + memberId;
- client.sendAsyncApiCommand(command, arg);
- arg = meetingId + " stop all " + memberId;
- client.sendAsyncApiCommand(command, arg);
- arg = meetingId + " unmute " + memberId;
- result = client.sendAsyncApiCommand(command, arg);
- }
-
- } catch (Exception e) {
- log.error(meetingId + "|" + memberId + "|" + isMute + " 设置会议静音失败", e);
- }
- return result;
- }
- //呼叫字符串
- private String getCallString(String calleeNum){
- //呼叫内线分机号码
- String callStr = "user/" + calleeNum;
- //呼叫外线号码
- if (!EslCommon.existExten(calleeNum)) {
- callStr = "sofia/gateway/hykj/" + calleeNum;
- }
- return callStr;
- }
- }
|