#include "FsProxy.h" #include "EslGateway.h" #include "Config.h" #include "ChanTrunk.h" #include "ChanExten.h" #include "Session.h" #include "Config.h" #include "OperationReactor.h" #include "JsonStringMaker.h" #include #include #include "SoftAuth.h" CFsProxy::CFsProxy() : m_Gateway(this) { } CFsProxy::~CFsProxy() { release(); } bool CFsProxy::init() { CConfig *cfg = CConfig::GetInstance(); tts.Init(cfg->ttsPath()); // 初始化tts路径 if (!JdbcHelper::GetInstance()->init(cfg->dbAddr(), cfg->dbUser(), cfg->dbPwd(), cfg->dbDatabase())) { LOG_ERROR("程序运行失败,数据库驱动加载失败"); return false; } if (JdbcHelper::GetInstance()->jdbc_connect(false)) { LOG_ERROR("程序运行失败,数据库连接失败"); return false; } LOG_INFO("数据库连接成功。"); //std::string sql = "update agent set exten = ''"; std::string sql = "delete from agent "; JdbcHelper::GetInstance()->jdbc_executeUpdate(sql, [](sql::PreparedStatement* stmt) { }, [sql](sql::SQLException &e) { Format fmt("Sql执行失败,错误信息:[%s],Sql[%s]"); fmt % e.what() % sql; LOG_ERROR(fmt.str().c_str()); }); if (!m_Server.init(cfg->wsPort())) { LOG_ERROR("程序运行失败,请检查端口[%d]是否已被占用", cfg->wsPort()); return false; } LOG_INFO("websocket服务打开成功,占用端口[%d]", cfg->wsPort()); m_Server.setCallbackRecvMsg(std::bind(&CFsProxy::__recvMsgFun, this, std::placeholders::_1, std::placeholders::_2)); m_Server.setCallbackClose(std::bind(&CFsProxy::__closeFun, this, std::placeholders::_1)); m_AutoCall.setDetalTaskFun(std::bind(&CFsProxy::__doAutoTask,this, std::placeholders::_1)); m_AutoCall.startTask(); return __init(); } void CFsProxy::release(void) { m_Server.stop(); m_AutoCall.stopTask(); m_Gateway.delAgentAll(); m_Gateway.stop(); __freeAgent(); __freeSession(); __freeExten(); __freeTrunkChan(); JdbcHelper::GetInstance()->jdbc_close_connect(); } void CFsProxy::run(void) { m_Server.run(); } /***************************************************************** **【函数名称】 onChanRegist **【函数功能】 通道注册的处理函数 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::onChanRegist(DEV_RES_TYPE ChanType, uint32_t ChanNo, CHAN_LOGIC_STATE ChanState) { Format fmt("通道注册: ChanType = %d,ChanNo = %d,ChanState = %d"); fmt % ChanType % ChanNo % ChanState; LOG_INFO_S(fmt); } /***************************************************************** **【函数名称】 onExtenDestroy **【函数功能】 分机销毁的处理函数 **【参数】 **【返回值】 ****************************************************************/ void CFsProxy::onExtenDestroy(uint32_t ExtenNo) { } /***************************************************************** **【函数名称】 onExtenDirectOp **【函数功能】 分机直接操作启动 **【参数】 OpType 启动的操作类型 pHostChan 触发事件的通道 pNotify 触发操作事件内容 **【返回值】 ****************************************************************/ void CFsProxy::onExtenDirectOp(DEV_OP OpType, VirtualChan * pHostChan, PCHAN_EVENT_NOTIFY pNotify) { switch ((int)OpType) { case DEV_OP_CALL_OUT: { if (!COperationReactor::GetInstance()->onExtenCallFromDev(pHostChan, pNotify)) __kill(FS_LINK_JOBID_INVALID, pHostChan->chanId()); } break; } // end switch } /***************************************************************** **【函数名称】 onChanStateUpdate **【函数功能】 通道状态更新处理 **【参数】 **【返回值】 *****************************************************************/ void CFsProxy::onChanStateUpdate(long OpInstance, VirtualChan * pChan) { if (OpInstance != FS_LINK_INSTANCE_INVALID && pChan != nullptr) COperationReactor::GetInstance()->onEslEvtChanState(OpInstance, pChan); if (pChan == nullptr) return; // 发送通道/线路状态到坐席 DEV_RES_TYPE devType = pChan->type(); // 通道类型 std::string chanNo = std::to_string(pChan->no()); // 通道号 中继号或分机号 //CHAN_LOGIC_STATE uint32_t chanState = pChan->state(); // 通道状态 if ((pChan->state() & HELD_STATE_IND_MASK) == INNER_STATE_HELD) { chanState = (pChan->state() & HELD_STATE_IND_MASK); } std::string lineType = "未知"; if (devType == DEV_RES_TYPE_EXT) { lineType = std::string("内线"); } else if (devType == DEV_RES_TYPE_TRUNK) { lineType = std::string("外线"); } std::string state; switch (chanState) { case CHAN_LOGIC_STATE_DISABLED: // 不可用 state = "不可用"; break; case CHAN_LOGIC_STATE_FREE: // 空闲 state = "空闲"; break; case CHAN_LOGIC_STATE_INIT: // 摘机等待拨号 state = "摘机等待拨号"; break; case CHAN_LOGIC_STATE_DIALING: // 拨号 state = "拨号"; break; case CHAN_LOGIC_STATE_RING_BACK: // 呼出振铃 state = "呼出振铃"; break; case CHAN_LOGIC_STATE_ALERTING: // 来电振铃 state = "来电振铃"; break; case CHAN_LOGIC_STATE_TALKING: // 通话中 state = "通话中"; break; case CHAN_LOGIC_STATE_STANDBY: // 中继通道可响应APP(此状态仅用作DevLink侧,不可传至CTI) break; case CHAN_LOGIC_STATE_HELD: // 通话保持中(保持状态的使用须与原通道状态按位或和与) state = "通话保持中"; break; } Format fmt("lineType = %s[%s],lineState = %s,chanState = %d"); fmt % lineType % chanNo % state % chanState; LOG_DEBUG_S(fmt.str()) if (devType == DEV_RES_TYPE_EXT) { map::iterator it; { std::unique_locklock(m_AgentLock); it = m_MapAgent.find(chanNo); } if (it != m_MapAgent.end()) { if (it->second == nullptr) { LOG_DEBUG("查不到分机[%s]", chanNo.c_str()); } else { LOG_DEBUG("查到分机[%s]", chanNo.c_str()); } if (chanState == CHAN_LOGIC_STATE_FREE && !it->second->isRepose()) it->second->setState(AGENT_STATE_FREE); else if (chanState == CHAN_LOGIC_STATE_TALKING) it->second->setState(AGENT_STATE_BUSY); else if(chanState == CHAN_LOGIC_STATE_RING_BACK || chanState == CHAN_LOGIC_STATE_ALERTING) it->second->setState(AGENT_STATE_REQUESTED); if (it->second->isRepose()) { state = "小休"; } if (!state.empty()) // 过滤掉保持状态 { std::string data = creatJson("LineState", state); m_Server.sendMsg(it->second->hdl(), data); data = creatJson("Monitor", state, it->second->id()); m_MonitAgents.loopAgent(m_Server, data); // 通知班长坐席监控 } // 坐席空闲且没有置忙 执行外呼 if (it->second->state() == AGENT_STATE_FREE && !it->second->isRepose()) { LOG_DEBUG_S("进自动外呼等待"); m_AutoCall.waitTask(it->second->id()); LOG_DEBUG_S("出自动外呼等待"); } } else { LOG_WARN("线路状态通知失败,找不到分机[%s]对应的坐席", chanNo.c_str()); } } } void CFsProxy::onChanPoor(Session * pSession, PCHAN_EVENT_NOTIFY pNotify) { LOG_INFO_S(boost::str(Format("空闲通道枯竭,主动挂断,ChanId = %s ,caller = %s") % pNotify->ChanId % pNotify->Caller).c_str()); __kill(FS_LINK_JOBID_INVALID, pNotify->ChanId); } void CFsProxy::onEslDisconnect(void) { // esl 断开重连 m_Gateway.stop(); __freeSession(); __freeExten(); __freeTrunkChan(); LOG_WARN_S("ESL断开重新初始化..."); while (!__init()) { LOG_WARN_S("ESL重连中...") this_thread::sleep_for(std::chrono::seconds(5)); } LOG_INFO_S("ESL重新初始化成功..."); } void CFsProxy::onEslExtenReg(uint32_t ExtenNo, string ExtenIp) { ChanExten* pExten = getExten(ExtenNo); if (pExten == nullptr) { __addExten(ExtenNo); } else { if (pExten->isVoid()) // 若被丢弃,则改变丢弃状态 pExten->discard(false); } } void CFsProxy::onEslExtenUnreg(uint32_t ExtenNo) { std::string agentId; { std::unique_locklock(m_AgentLock); auto it = m_MapAgent.find(std::to_string(ExtenNo)); if (it != m_MapAgent.end()) { agentId = it->second->id(); it->second->removeAgent("分机掉线"); //m_Gateway.delAgent(it->second->id()); // callcenter模块中删除 m_Gateway.delAgent(it->second->id(), it->second->groups()); m_Server.sendMsg(it->second->hdl(), creatJson("Logout", "分机掉线")); m_MapAgent.erase(it); // 从签入列表中删除 } } if (!agentId.empty()) { std::string data = creatJson("Monitor", "签出", agentId); m_MonitAgents.loopAgent(m_Server, data); // 通知班长坐席监控 } __delExten(ExtenNo); } void CFsProxy::onEslEvtBgJobDone(PBG_JOB_NOTIFY pNotify) { COperationReactor::GetInstance()->onEslEvtBgJobDone(pNotify); } void CFsProxy::onEslEvtChannel(PCHAN_EVENT_NOTIFY pNotify) { Session* pSession = __getSession(pNotify); if (pSession == nullptr) { Format fmt("{FsProxy}: 通道事件,没有找到会话,EslEventId=%d,ChanId[%s],CallId[%s],Caller[%s],Callee[%s],CcId[%s]"); fmt % pNotify->EventId % pNotify->ChanId % pNotify->CallId % pNotify->Caller % pNotify->Callee % pNotify->CcId; LOG_WARN_S(fmt.str()); return; } pSession->onChanEvent(pNotify); if (pSession->isVoid()) __delSession(pSession->id()); } void CFsProxy::onEslEvtDtmf(PDTMF_NOTIFY pNotify) { } void CFsProxy::onEslEvtHold(PHOLD_NOTIFY pNotify) { if (pNotify == nullptr) return; Session* pSession = __getSession(pNotify->CallId); if (pSession != nullptr) pSession->onChanHold(pNotify); } bool CFsProxy::send2Agent(std::string ExtenNo, std::string Data) { std::unique_locklock(m_AgentLock); auto it = m_MapAgent.find(ExtenNo); if (it != m_MapAgent.end()) { if (m_Server.sendMsg(it->second->hdl(), Data)) { LOG_DEBUG("成功发送到坐席[%s]-分机[%s],msg[%s]", it->second->id().c_str(), ExtenNo.c_str(), Data.c_str()); return true; } } Format fmt("坐席通知失败,找不到分机对应的坐席,分机[%s],msg[%s]"); fmt % ExtenNo % Data; LOG_WARN_S(fmt.str()); return false; } ChanExten * CFsProxy::getExten(uint32_t ExtenNo) { auto it = m_MapChanExt.find(ExtenNo); if (it != m_MapChanExt.end()) return it->second; return nullptr; } ChanTrunk * CFsProxy::getTrunk(uint32_t TrunkNo) { if (TrunkNo <= 0 || TrunkNo > m_ArrayTrunk.size()) return nullptr; return m_ArrayTrunk[--TrunkNo]; } void CFsProxy::delChan(VirtualChan * pChan) { if (pChan->type() == DEV_RES_TYPE_EXT) __delExten(pChan->no()); } ChanTrunk * CFsProxy::getFreeTrunk(void) { for (size_t i = 0; i < m_ArrayTrunk.size(); ++i) { ChanTrunk* pTrunk = m_ArrayTrunk[i]; if (pTrunk != nullptr) { if (pTrunk->isFree()) { return pTrunk; } } } LOG_INFO_S("没有空闲中继通道"); return nullptr; } VirtualChan * CFsProxy::getAssoChanInSession(VirtualChan * pChan) { Session* pSession = __getSession(pChan->sessionId()); if (pSession != nullptr) return pSession->getAssoChan(pChan); else return nullptr; } VirtualChan * CFsProxy::getBusyChan(string ChanId) { auto it = m_MapBusyChan.find(ChanId); if (it != m_MapBusyChan.end()) return it->second; return nullptr; } void CFsProxy::regBusyChan(VirtualChan * pChan) { m_MapBusyChan[pChan->chanId()] = pChan; } void CFsProxy::unregBusyChan(VirtualChan * pChan) { m_MapBusyChan.erase(pChan->chanId()); } std::string CFsProxy::getAgentByExten(std::string ExtenNo) { std::unique_locklock(m_AgentLock); auto it = m_MapAgent.find(ExtenNo); if (it->second == nullptr) return ""; return it->second->id(); } std::string CFsProxy::getAgentByExten(std::string ExtenNo, std::string &Groups) { std::unique_locklock(m_AgentLock); auto it = m_MapAgent.find(ExtenNo); if (it->second == nullptr) return ""; Groups = it->second->group(); return it->second->id(); } std::string CFsProxy::getExtenByAgent(std::string AgentId) { std::unique_locklock(m_AgentLock); auto it = m_MapAgent.begin(); while (it != m_MapAgent.end()) { if (it->second->id() == AgentId) return it->first; ++it; } return ""; } bool CFsProxy::ExtenCall(long JobId, VirtualChan * pChan, std::string CallerNum, std::string CalleeNum) { /*Format fmt("bgapi originate {origination_caller_id_number=%lu }user/%lu %s XML %s\r\n%s: %ld"); fmt % pChan->no() % pChan->no() % CalleeNum % CConfig::GetInstance()->extContext() % ESL_HEADER_JOB_UUID % JobId;*/ std::string called; if (getExten(atoi(CalleeNum.c_str())) == nullptr) { std::string prefix = CConfig::GetInstance()->gateWayPrefix(); called = CalleeNum.substr(prefix.length(), CalleeNum.length()- prefix.length()); } else { called = CalleeNum; } Format fmt("bgapi originate {origination_caller_id_number=%lu,%s=%s}user/%lu %s XML %s\r\n%s: %ld"); fmt % pChan->no() % ESL_VAR_MALL_CALL_CALLEE %called % pChan->no() % CalleeNum % CConfig::GetInstance()->extContext() % ESL_HEADER_JOB_UUID % JobId; return m_Gateway.sendCmd(fmt.str()); } bool CFsProxy::PredictionCall(long JobId, std::string CallerNum, std::string CalleeNum) { std::string CallString; if (!m_CallStringMaker.makeCallString(CallerNum, CalleeNum, CallString)) return false; std::string EslCmd; EslCmd = boost::str(Format("bgapi originate {origination_caller_id_number=%s,%s=%ld}%s %s XML %s\r\n%s: %ld") %CallerNum % ESL_VAR_OP_INSTANCE % JobId % CallString % CalleeNum % CConfig::GetInstance()->extContext() % ESL_HEADER_JOB_UUID % JobId); return m_Gateway.sendCmd(EslCmd); } bool CFsProxy::AutoCall(long JobId, std::string CallerNum, std::string CalleeNum, int OpType, long TaskId) { std::string CallString; std::string Caller; if (!m_CallStringMaker.makeCallString(Caller, CallerNum, CallString)) return false; std::string EslCmd; /*EslCmd = boost::str(Format("bgapi originate {origination_caller_id_number=%s,%s=%ld,%s=%ld}%s %s XML %s\r\n%s: %ld") % CallerNum % ESL_VAR_OP_TYPE % OpType % ESL_VAR_TASK_ID % TaskId % CallString % CalleeNum % CConfig::GetInstance()->extContext() % ESL_HEADER_JOB_UUID % JobId);*/ std::string called; if (getExten(atoi(CalleeNum.c_str())) == nullptr) { std::string prefix = CConfig::GetInstance()->gateWayPrefix(); called = CalleeNum.substr(prefix.length(), CalleeNum.length() - prefix.length()); } else { called = CalleeNum; } EslCmd = boost::str(Format("bgapi originate {origination_caller_id_number=%s,%s=%ld,%s=%ld,,%s=%s}%s %s XML %s\r\n%s: %ld") % CallerNum % ESL_VAR_OP_TYPE % OpType % ESL_VAR_TASK_ID % TaskId % ESL_VAR_MALL_CALL_CALLEE %called % CallString % CalleeNum % CConfig::GetInstance()->extContext() % ESL_HEADER_JOB_UUID % JobId); return m_Gateway.sendCmd(EslCmd); } bool CFsProxy::kill(long JobId, VirtualChan * pChan) { return __kill(JobId, pChan->chanId()); } /***************************************************************** **【函数名称】 consult **【函数功能】 协商呼叫 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::consult(long JobId, VirtualChan * pChan, std::string DestNum) { std::string CallerNum; std::string CallString; if (!m_CallStringMaker.makeCallString(CallerNum, DestNum, CallString)) return false; std::string EslCmd; EslCmd = boost::str(Format("bgapi uuid_broadcast %s att_xfer::%s\r\n%s: %ld") % pChan->chanId() % CallString % ESL_HEADER_JOB_UUID % JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 insert **【函数功能】 强插 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::insert(long JobId, VirtualChan * pChan, std::string DestSessionId) { std::string EslCmd; EslCmd = boost::str(Format("bgapi originate {origination_caller_id_number=%lu}user/%lu &three_way(%s)\r\n%s: %ld") % pChan->no() % pChan->no() % DestSessionId% ESL_HEADER_JOB_UUID% JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 intercept **【函数功能】 强截 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::intercept(long JobId, VirtualChan * pChan, std::string DestChanId) { std::string EslCmd; EslCmd = boost::str(Format("bgapi originate {origination_caller_id_number=%lu}user/%lu &intercept(%s)\r\n%s: %ld") % pChan->no() % pChan->no() % DestChanId % ESL_HEADER_JOB_UUID % JobId); return m_Gateway.sendCmd(EslCmd); } /***************************************************************** **【函数名称】 listen **【函数功能】 监听 **【参数】 **【返回值】 ****************************************************************/ bool CFsProxy::listen(long JobId, VirtualChan * pChan, std::string DestChanId) { std::string EslCmd; EslCmd = boost::str(Format("bgapi originate {origination_caller_id_number=%lu}user/%lu &eavesdrop(%s)\r\n%s: %ld") % pChan->no() % pChan->no() % DestChanId% ESL_HEADER_JOB_UUID% JobId); return m_Gateway.sendCmd(EslCmd); } bool CFsProxy::playAgentNo(VirtualChan * pChan) { std::string filePath; std::string welcomeWords; /*Format fmt("您好, %s 号话务员为您服务."); fmt % pChan->no(); if (!tts.TextToAudio(fmt.str(), filePath))*/ std::string agent = getAgentByExten(std::to_string(pChan->no())); if (agent.empty()) welcomeWords = (boost::str(Format("您好, %s 号话机为您服务.") % pChan->no())); else welcomeWords = (boost::str(Format("您好, %s 号话务员为您服务.") % agent)); if (!tts.TextToAudio(welcomeWords, filePath)) { LOG_WARN("{FsProxy}: 中继通道[%lu]放音时TTS文本转换失败", pChan->no()); return false; } std::this_thread::sleep_for(std::chrono::milliseconds(50)); // 暂停50ms,防止太快导致分机放音失败 Format EslCmd("bgapi uuid_broadcast %s %s both"); EslCmd % pChan->chanId() % filePath; return m_Gateway.sendCmd(EslCmd.str()); } // 播报坐席工号 bool CFsProxy::playAgentNo(long JobId, VirtualChan * pChan) { std::string filePath; Format fmt("%ld 话务员为您服务"); fmt % pChan->no(); if (!tts.TextToAudio(fmt.str(), filePath)) { LOG_WARN("{FsProxy}: 中继通道[%lu]放音时TTS文本转换失败", pChan->no()); return false; } std::this_thread::sleep_for(std::chrono::milliseconds(50)); // 暂停50ms,防止太快导致分机放音失败 Format EslCmd("bgapi uuid_broadcast %s %s both\r\n%s: %ld"); EslCmd % pChan->chanId() % filePath % ESL_HEADER_JOB_UUID % JobId; return m_Gateway.sendCmd(EslCmd.str()); } bool CFsProxy::meeting(long JobId, std::string CallerNum, std::string DestNum, std::string MeetingId) { std::string CallString; if (!m_CallStringMaker.makeCallString(CallerNum, DestNum, CallString)) return false; LOG_INFO(("{FsProxy}: 三方会议呼叫字符串: %s"), CallString.c_str()); std::string EslCmd; EslCmd = boost::str(Format("bgapi originate {origination_caller_id_number=%s,%s=%ld}%s %s XML %s\r\n%s: %ld") % CallerNum % ESL_VAR_OP_INSTANCE % JobId % CallString % MeetingId % CConfig::GetInstance()->meetingContext() % ESL_HEADER_JOB_UUID % JobId); return m_Gateway.sendCmd(EslCmd); } bool CFsProxy::muteOn(long JobId, VirtualChan * pChan) { std::string EslCmd; EslCmd = boost::str(Format("bgapi uuid_audio %s start write mute -4\r\n%s: %ld") % pChan->chanId() % ESL_HEADER_JOB_UUID % JobId); return m_Gateway.sendCmd(EslCmd); } bool CFsProxy::muteOff(long JobId, VirtualChan * pChan) { std::string EslCmd; EslCmd = boost::str(Format("bgapi uuid_audio %s stop write mute -4\r\n%s: %ld") % pChan->chanId() % ESL_HEADER_JOB_UUID % JobId); return m_Gateway.sendCmd(EslCmd); } bool CFsProxy::holdon(long JobId, VirtualChan * pChan) { std::string EslCmd; EslCmd = boost::str(Format("bgapi uuid_hold %s\r\n%s: %ld") % pChan->chanId() % ESL_HEADER_JOB_UUID % JobId); return m_Gateway.sendCmd(EslCmd); } bool CFsProxy::takeBack(long JobId, VirtualChan * pChan) { std::string EslCmd; EslCmd = boost::str(Format("bgapi uuid_hold off %s\r\n%s: %ld") % pChan->chanId() % ESL_HEADER_JOB_UUID % JobId); return m_Gateway.sendCmd(EslCmd); } bool CFsProxy::record(long JobId, VirtualChan * pChan, std::string RcdFile) { std::string EslCmd; EslCmd = boost::str(Format("bgapi uuid_record %s start %s\r\n%s: %ld") % pChan->chanId() % RcdFile % ESL_HEADER_JOB_UUID % JobId); return m_Gateway.sendCmd(EslCmd); } bool CFsProxy::turnIvr(long JobId, VirtualChan * pChan) { Format EslCmd("bgapi uuid_transfer %s %s xml %s\r\n%s: %ld"); EslCmd % pChan->chanId() % "turnmyd" % CConfig::GetInstance()->extContext() % ESL_HEADER_JOB_UUID % JobId; return m_Gateway.sendCmd(EslCmd.str()); //m_Gateway.Execte("ivr", "myd", pChan->chanId().c_str()); } bool CFsProxy::transfer(long JobId, VirtualChan * pChan, std::string DestNum) { /*Format EslCmd("bgapi uuid_transfer %s %s xml %s\r\n%s: %ld"); EslCmd % pChan->chanId() % DestNum % CConfig::GetInstance()->extContext() % ESL_HEADER_JOB_UUID % JobId; return m_Gateway.sendCmd(EslCmd.str());*/ //{%s = %ld} % ESL_VAR_OP_INSTANCE % JobId std::string Param; Param = boost::str(Format("{%s=%ld}") % ESL_VAR_OP_INSTANCE % JobId); m_Gateway.Execte(ESL_APP_SET, Param.c_str(), pChan->chanId().c_str()); Format EslCmd("bgapi uuid_transfer %s %s xml %s\r\n%s: %ld"); EslCmd % pChan->chanId() % DestNum % CConfig::GetInstance()->extContext() % ESL_HEADER_JOB_UUID % JobId; return m_Gateway.sendCmd(EslCmd.str()); } bool CFsProxy::transfer2Context(long JobId, std::string DestChanId, std::string Exten, std::string Context, bool BothSide) { std::string EslCmd; if (BothSide) EslCmd = boost::str(Format("bgapi uuid_transfer %s -both %s xml %s\r\n%s: %ld") % DestChanId % Exten % Context % ESL_HEADER_JOB_UUID % JobId); else EslCmd = boost::str(Format("bgapi uuid_transfer %s %s xml %s\r\n%s: %ld") % DestChanId % Exten % Context % ESL_HEADER_JOB_UUID % JobId); return m_Gateway.sendCmd(EslCmd); } std::string CFsProxy::creatJson(std::string type, std::string result) { // Json::Value root; // Json::StreamWriterBuilder jsrocd; // root["Type"] = type; // root["Result"] = result; // return Json::writeString(jsrocd, root); // jsoncpp中文乱码 Format fmt("{ \"Type\":\"%s\",\"Result\":\"%s\" }"); fmt %type %result; return fmt.str(); } std::string CFsProxy::creatJson(std::string type, bool result) { Json::Value root; Json::StreamWriterBuilder jsrocd; std::unique_ptr write(jsrocd.newStreamWriter()); Json::OStringStream os; root["Type"] = type; root["Result"] = result; write->write(root, &os); return os.str(); } std::string CFsProxy::creatJson(std::string type, std::string result, std::string agentId) { Format fmt("{ \"Type\":\"%s\",\"Result\":\"%s\",\"AgentID\":\"%s\" }"); fmt %type %result %agentId; return fmt.str(); } std::string CFsProxy::creatJsonIncoming(std::string callid, std::string caller, std::string callee, std::string trunkNumber, int incomintType) { Json::Value root; Json::StreamWriterBuilder jsrocd; root["Type"] = "Incoming"; root["Result"] = true; root["CallID"] = callid; // callid root["Number"] = caller; // 主叫 root["CalleeNumber"] = callee; root["TrunkNumber"] = trunkNumber; // 中继号 root["IncomintType"] = incomintType; // 来电类型,来电,外呼,自动外呼 return Json::writeString(jsrocd, root); } bool CFsProxy::__init() { __initTrunkChan(); if (m_Gateway.start()) { m_Gateway.hangupAll(); static bool del = m_Gateway.delAgentAll(); // 使用static 保证delagentall运行后在fs连接断开后重连成功后不会再一次执行 boost::ignore_unused(del); // 取消del不使用的警告 if (m_Gateway.scanExten()) { LOG_INFO_S("{FsProxy}: FreeSWITCH代理初始化成功"); return true; } else { LOG_ERROR_S("{FsProxy}: FreeSWITCH代理初始化失败, 扫描分机资源失败"); return false; } } else { LOG_ERROR_S("{FsProxy}: FreeSWITCH代理初始化失败, ESL网关启动失败"); return false; } } uint32_t CFsProxy::__transLogicState2CtiState(DEV_RES_TYPE ChanType, CHAN_LOGIC_STATE State) { if (ChanType == DEV_RES_TYPE_EXT) { uint32_t HoldMask = State & HELD_STATE_IND_MASK; switch (State & HELD_STATE_FILTER_MASK) { case CHAN_LOGIC_STATE_DISABLED: return INNER_STATE_DISABLED; // 不可用 case CHAN_LOGIC_STATE_FREE: return INNER_STATE_FREE | HoldMask; // 空闲 case CHAN_LOGIC_STATE_INIT: return INNER_STATE_INIT | HoldMask; // 摘机等待拨号 case CHAN_LOGIC_STATE_DIALING: return INNER_STATE_DIALING | HoldMask; // 拨号 case CHAN_LOGIC_STATE_RING_BACK: return INNER_STATE_RING_BACK | HoldMask; // 呼出振铃 case CHAN_LOGIC_STATE_ALERTING: return INNER_STATE_ALERTING | HoldMask; // 来电振铃 case CHAN_LOGIC_STATE_TALKING: return INNER_STATE_TALKING | HoldMask; // 通话中 default: return INNER_STATE_DISABLED; } } else { switch (State) { case CHAN_LOGIC_STATE_DISABLED: return TRUNK_STATE_DISABLED; // 不可用 case CHAN_LOGIC_STATE_FREE: return TRUNK_STATE_FREE; // 空闲 case CHAN_LOGIC_STATE_DIALING: return TRUNK_STATE_DIALING; // 拨号 case CHAN_LOGIC_STATE_RING_BACK: return TRUNK_STATE_RING_BACK; // 呼出振铃 case CHAN_LOGIC_STATE_ALERTING: return TRUNK_STATE_ALERTING; // 来电振铃 case CHAN_LOGIC_STATE_STANDBY: case CHAN_LOGIC_STATE_TALKING: return TRUNK_STATE_TALKING; // 通话中 default: return TRUNK_STATE_UNKNOWN; } } } void CFsProxy::__addExten(uint32_t ExtenNo) { ChanExten* pExten = new ChanExten(this, ExtenNo); m_MapChanExt[ExtenNo] = pExten; pExten->regist(); } void CFsProxy::__delExten(uint32_t ExtenNo) { auto it = m_MapChanExt.find(ExtenNo); if (it != m_MapChanExt.end()) { if (it->second->isFree()) { FS_LINK_DELETE(it->second); m_MapChanExt.erase(it); } else { it->second->discard(true); } } } void CFsProxy::__freeExten(void) { auto it = m_MapChanExt.begin(); while (it != m_MapChanExt.end()) { FS_LINK_DELETE(it->second); ++it; } m_MapChanExt.clear(); } void CFsProxy::__initTrunkChan(void) { /*for (int i = 1; i <= CConfig::GetInstance()->trunkCount(); ++i)*/ for (int i = 1; i <= SoftAuth::GetInstance()->trunkNum(); ++i) { ChanTrunk* pTrunk = new ChanTrunk(this, i); m_ArrayTrunk.emplace_back(pTrunk); pTrunk->regist(); } } void CFsProxy::__freeTrunkChan(void) { ChanTrunk* pTrunk = nullptr; for (size_t i = 0; i < m_ArrayTrunk.size(); ++i) { pTrunk = m_ArrayTrunk[i]; FS_LINK_DELETE(pTrunk); } m_ArrayTrunk.clear(); } Session * CFsProxy::__getSession(string SessionId) { auto it = m_MapSession.find(SessionId); if (it != m_MapSession.end()) return it->second; return nullptr; } Session * CFsProxy::__getSession(PCHAN_EVENT_NOTIFY pNotify) { Session* pSession = nullptr; pSession = __getSession(pNotify->CallId); if (pSession == nullptr) { if (!pNotify->CcId.empty()) { LOG_INFO(boost::str(Format("Callid查不到,CcId不为空,使用CcId代替CallId进一步查找Session,CallId[%s],CcId[%s]") % pNotify->CallId % pNotify->CcId).c_str()); pSession = __getSession(pNotify->CcId); if (pSession != nullptr) return pSession; LOG_WARN(boost::str(Format("CallId,CcId都查不到session,CallId[%s],CcId[%s]") % pNotify->CallId % pNotify->CcId).c_str()); } /*if (pNotify->EventId == CHANNEL_EVENT_HANGUP_COMPLETE) { LOG_WARN("{CFsProxy}:接收到的挂机事件在会话中找不到,不再创建新会话, CallId[%s], CcId[%s]", pNotify->CallId.c_str(), pNotify->CcId.c_str()); return nullptr; }*/ pSession = new Session(this, pNotify->CallId); pSession->prepare(pNotify); m_MapSession[pNotify->CallId] = pSession; Format fmt("{CFsProxy}:Add Session[%s],ChanId[%s],EventId[%d]"); fmt % pSession->id() % pNotify->ChanId % pNotify->EventId; LOG_INFO_S(fmt.str()); } return pSession; } void CFsProxy::__delSession(std::string SessionId) { if (SessionId.empty()) return; auto it = m_MapSession.find(SessionId); if (it != m_MapSession.end()) { Format fmt("{FsProxy}: Delete Session[%s]"); fmt % it->second->id(); LOG_INFO_S(fmt.str()); FS_LINK_DELETE(it->second); m_MapSession.erase(it); } } void CFsProxy::__freeSession(void) { auto it = m_MapSession.begin(); while (it != m_MapSession.end()) { FS_LINK_DELETE(it->second); ++it; } m_MapSession.clear(); } bool CFsProxy::__kill(long JobId, string ChanId) { string EslCmd; if (JobId == FS_LINK_JOBID_INVALID) EslCmd = boost::str(Format("bgapi uuid_kill %s") % ChanId); else EslCmd = boost::str(Format("bgapi uuid_kill %s\r\n%s: %ld") % ChanId % ESL_HEADER_JOB_UUID % JobId); return m_Gateway.sendCmd(EslCmd); } std::string CFsProxy::__getExtenByAgent(std::string agentId) { std::unique_locklock(m_AgentLock); auto it = m_MapAgent.begin(); while (it != m_MapAgent.end()) { if (it->second->id() == agentId) { return it->first; } ++it; } return ""; } bool CFsProxy::__login(std::string AgentID, std::string ExtenNo) { bool ret = true; std::string cmd; cmd = boost::str(Format("bgapi callcenter_config agent add %1% Callback") % AgentID); // 添加座席 ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi callcenter_config agent set contact %1% [call_timeout=30]user/%2%") % AgentID % ExtenNo); // 设置呼叫字符串 ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi callcenter_config agent set status %1% Available ") % AgentID); // 座席登录后默认空闲 ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi callcenter_config agent set state %1% Waiting") % AgentID); // 座席登录后默认空闲 ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi callcenter_config agent set max_no_answer %1% 0") % AgentID); // 0禁用 ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi callcenter_config agent set wrap_up_time %1% 20") % AgentID); // 话后处理时长 ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi callcenter_config agent set reject_delay_time %1% 0") % AgentID); ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi allcenter_config agent set busy_delay_time %1% 0") % AgentID); ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi callcenter_config tier add support@default %1% 1 1") % AgentID); // 添加梯队 到队列等价于坐席组 ret &= m_Gateway.sendCmd(cmd); return ret; } bool CFsProxy::__login(std::string AgentID, std::string ExtenNo, std::list Group) { bool ret = true; std::string cmd; cmd = boost::str(Format("bgapi callcenter_config agent add %1% Callback") % AgentID); // 添加座席 ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi callcenter_config agent set contact %1% [call_timeout=30]user/%2%") % AgentID % ExtenNo); // 设置呼叫字符串 ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi callcenter_config agent set status %1% Available ") % AgentID); // 座席登录后默认空闲 ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi callcenter_config agent set state %1% Waiting") % AgentID); // 座席登录后默认空闲 ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi callcenter_config agent set max_no_answer %1% 0") % AgentID); // 0禁用 ret &= m_Gateway.sendCmd(cmd); //cmd = boost::str(Format("bgapi callcenter_config agent set wrap_up_time %1% 20") % AgentID); // 话后处理时长 cmd = boost::str(Format("bgapi callcenter_config agent set wrap_up_time %1% %2%") % AgentID %CConfig::GetInstance()->postProcessTime()); ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi callcenter_config agent set reject_delay_time %1% 0") % AgentID); ret &= m_Gateway.sendCmd(cmd); cmd = boost::str(Format("bgapi allcenter_config agent set busy_delay_time %1% 0") % AgentID); ret &= m_Gateway.sendCmd(cmd); for (std::string &var : Group) { cmd = boost::str(Format("bgapi callcenter_config tier add %2% %1% 1 1") % AgentID %var); // 添加梯队 到队列等价于坐席组 ret &= m_Gateway.sendCmd(cmd); } return ret; } bool CFsProxy::__logout(std::string AgentID, std::string AgentExten) { bool ret = true; std::string cmd; cmd = boost::str(Format("bgapi callcenter_config agent del %1% ") % AgentID); ret &= m_Gateway.sendCmd(cmd); /*cmd = boost::str(Format("bgapi callcenter_config tier del support@default %1% ") % AgentID); ret &= m_Gateway.sendCmd(cmd);*/ std::unique_locklock(m_AgentLock); auto it = m_MapAgent.find(AgentExten); if (ret && it != m_MapAgent.end()) { for (std::string &var : it->second->groups()) { cmd = boost::str(Format("bgapi callcenter_config tier del %2% %1% ") % AgentID %var); ret &= m_Gateway.sendCmd(cmd); } it->second->removeAgent(AgentID); FS_LINK_DELETE(it->second); m_MapAgent.erase(it); } return ret; } bool CFsProxy::__freeAgent() { std::unique_locklock(m_AgentLock); auto it = m_MapAgent.begin(); while (it != m_MapAgent.end()) { // __logout(it->second->id()); //FS_LINK_DELETE(it->second); Agent* agent = it->second; FS_LINK_DELETE(agent); m_MapAgent.erase(it++); } return false; } bool CFsProxy::__setState(std::string AgentID, bool isFree) { Format fmt("bgapi callcenter_config agent set status %1% '%2%'"); if (isFree) fmt %AgentID %"Available"; // 置闲 else fmt %AgentID %"On Break"; // 置忙 return m_Gateway.sendCmd(fmt.str()); } void CFsProxy::__recvMsgFun(websocketpp::connection_hdl hdl, string msg) { Json::Value root; Json::CharReaderBuilder builder; std::unique_ptr reader(builder.newCharReader()); std::string errs; bool ret = reader->parse(msg.c_str(), msg.c_str() + msg.length(), &root, &errs); if (!ret || !errs.empty()) { if (errs.empty()) errs = "空"; Format fmt("Json 解析错误,errs = %s ,msg = %s"); fmt %errs %msg; LOG_WARN_S(fmt.str()); return; } if (!root.isMember("Type")) { Format fmt("该条Json消息格式不能识别 ,msg = %s"); fmt %msg; LOG_WARN_S(fmt.str()); return; } LOG_DEBUG_S(msg); const string type = root["Type"].asString(); const string AgentID = root["AgentID"].asString(); const string AgentExten = root["AgentExten"].asString(); uint32_t ExtenNo = 0; if (sscanf(AgentExten.c_str(), "%u", &ExtenNo) != 1) { Format fmt("分机号为非UInt,msg = %s"); fmt % msg; LOG_WARN_S(fmt.str()); m_Server.sendMsg(hdl, fmt.str()); return; } if ("Login" == type) { // 签入 if (SoftAuth::GetInstance()->acdNum() <= m_MapAgent.size()) { Format fmt("ACD授权数量已达上限,Agent = %s,Exten = %d"); fmt % AgentID % AgentExten; LOG_INFO_S(fmt.str()); m_Server.sendMsg(hdl, creatJson(type, std::string("签入失败,ACD授权数量已达上限"))); return; } if (getExten(ExtenNo) == nullptr) { Format fmt("分机不存在,Agent = %s,Exten = %d"); fmt % AgentID % AgentExten; LOG_INFO_S(fmt.str()); m_Server.sendMsg(hdl, creatJson(type, std::string("分机号不存在"))); return; } auto it = m_MapAgent.find(AgentExten); if (it != m_MapAgent.end()) { Format fmt("分机已被绑定,Agent = %s,Exten = %s"); fmt % AgentID % AgentExten; LOG_INFO_S(fmt.str()); m_Server.sendMsg(hdl, creatJson(type, std::string("分机已被绑定"))); return; } std::string AgentGroup = root["AgentGroup"].asString(); Agent *agent = new Agent(AgentID, AgentExten, AgentGroup); agent->hdl() = hdl; { std::unique_locklock(m_AgentLock); m_MapAgent[AgentExten] = agent; // 分机号作为key } if (!__login(AgentID, AgentExten, agent->groups())) { m_MapAgent.erase(AgentExten); Format fmt("座席签入失败,Agent = %s,Exten = %d, %s"); fmt % AgentID % AgentExten % hdl.lock().get(); LOG_INFO_S(fmt.str()); m_Server.sendMsg(hdl, creatJson(type, false)); return; } Format fmt("座席签入成功,Agent = %s,Exten = %d,AgentGroup = %s %s"); fmt % AgentID % AgentExten % AgentGroup % hdl.lock().get(); LOG_INFO_S(fmt.str()); m_Server.sendMsg(hdl, creatJson(type, true)); } else if ("Logout" == type) { // 签出 bool ret = __logout(AgentID, AgentExten); Format fmt("座席签出[%d],Agent = %s,Exten = %d, %s"); fmt %ret % AgentID % AgentExten % hdl.lock().get(); LOG_INFO_S(fmt.str()); m_Server.sendMsg(hdl, creatJson(type, ret)); std::string data = creatJson("Monitor", "签出", AgentID); m_MonitAgents.loopAgent(m_Server, data); // 通知班长坐席监控 } else if ("SayBusy" == type) { // 置忙 bool ret = __setState(AgentID, false); std::unique_locklock(m_AgentLock); auto it = m_MapAgent.find(AgentExten); if (it != m_MapAgent.end()) it->second->setState(AGENT_STATE_REPOSE); m_Server.sendMsg(hdl, creatJson(type, ret)); std::string data = creatJson("Monitor", "小休", AgentID); m_MonitAgents.loopAgent(m_Server, data); // 通知班长坐席监控 } else if ("SayFree" == type) { // 置闲 bool ret = __setState(AgentID, true); std::unique_locklock(m_AgentLock); auto it = m_MapAgent.find(AgentExten); if (it != m_MapAgent.end()) it->second->setState(AGENT_STATE_FREE); m_Server.sendMsg(hdl, creatJson(type, ret)); std::string data = creatJson("Monitor", "空闲", AgentID); m_MonitAgents.loopAgent(m_Server, data); // 通知班长坐席监控 } else if ("Hold" == type) { // 保持 bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_HOLD, ExtenNo, nullptr); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("Retrieve" == type) { // 接回保持 bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_TAKEBACK, ExtenNo, nullptr); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("MakeCall" == type) { // 外呼 const string DestinationNumber = root["DestinationNumber"].asString(); // 目标号码 LineOpParam param; param.szParam1 = AgentExten; param.szParam2 = DestinationNumber; bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_MAKE_CALL, ExtenNo, ¶m); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("Transfer" == type) { // 转移 const string DestinationNumber = root["DestinationNumber"].asString(); // 目标号码 LineOpParam param; param.szParam1 = AgentExten; param.szParam2 = DestinationNumber; bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_TRANSFER, ExtenNo, ¶m); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("Meeting" == type) { // 会议 const string DestinationNumber = root["DestinationNumber"].asString(); // 目标号码 auto it = getExten(ExtenNo); std::string meetingID; if (it != nullptr) { meetingID = it->sessionId(); } LOG_DEBUG("会议ID[%s]", meetingID.c_str()); LineOpParam param; param.nParam1 = meetingID; // 会议id param.szParam1 = AgentExten; param.szParam2 = DestinationNumber; bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_CONFERENCE, ExtenNo, ¶m); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("DropCall" == type) { // 挂机 bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_HANG_UP, ExtenNo, nullptr); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("Break" == type) { // 强拆 const string TargetAgentID = root["TargetAgentID"].asString(); // 目标号码 const string Exten = __getExtenByAgent(TargetAgentID); LineOpParam param; param.szParam1 = AgentExten; bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_HANG_UP, atoi(Exten.c_str()), ¶m); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("Listen" == type) { // 监听 const string TargetAgentID = root["TargetAgentID"].asString(); // 目标号码 const string Exten = __getExtenByAgent(TargetAgentID); LineOpParam param; param.szParam1 = AgentExten; param.szParam2 = Exten; bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_LISTEN, ExtenNo, ¶m); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("Insert" == type) { // 强插 const string TargetAgentID = root["TargetAgentID"].asString(); // 目标号码 const string Exten = __getExtenByAgent(TargetAgentID); LineOpParam param; param.szParam1 = AgentExten; param.szParam2 = Exten; bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_INSERT, ExtenNo, ¶m); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("Instead" == type) { // 代接 const string TargetAgentID = root["TargetAgentID"].asString(); // 目标号码 const string Exten = __getExtenByAgent(TargetAgentID); LineOpParam param; param.szParam1 = AgentExten; param.szParam2 = Exten; bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_INSTEAD, ExtenNo, ¶m); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("Intercept" == type) { // 强截 const string TargetAgentID = root["TargetAgentID"].asString(); // 目标号码 const string Exten = __getExtenByAgent(TargetAgentID); LineOpParam param; param.szParam1 = AgentExten; param.szParam2 = Exten; bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_GRAB, ExtenNo, ¶m); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("Record" == type) { // 录音 bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_RECORD, ExtenNo, nullptr); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("MuteOn" == type) { // 静音 bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_MUTE_BEGIN, ExtenNo, nullptr); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("MuteOff" == type) { // 取消静音 bool ret = COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_MUTE_END, ExtenNo, nullptr); if (!ret) m_Server.sendMsg(hdl, creatJson(type, std::string("操作失败"))); } else if ("Monitor" == type) { // 监控 m_MonitAgents.addAgent(AgentID, hdl); m_Server.sendMsg(hdl, creatJson(type, true)); std::unique_locklock(m_AgentLock); auto it = m_MapAgent.begin(); while (it != m_MapAgent.end()) { std::string state = it->second->state_s(); std::string agentId = it->second->id(); std::string data = creatJson("Monitor", state, agentId); m_Server.sendMsg(hdl, data); ++it; } } else if ("MonitorCancel" == type) { // 取消监控 m_MonitAgents.delAgent(AgentID); m_Server.sendMsg(hdl, creatJson(type, true)); } else if ("TurnIvr" == type) { // 转满意度 COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_TURNIVR, ExtenNo, nullptr); } else if ("AutoCall" == type) { // 自动外呼 m_AutoCall.addTask(); // 从数据库刷新任务 m_Server.sendMsg(hdl, creatJson(type, true)); } else if ("Heart" == type) { // 心跳 m_Server.sendMsg(hdl, msg); } } void CFsProxy::__closeFun(websocketpp::connection_hdl hdl) { std::string agentId; std::unique_locklock(m_AgentLock); auto it = m_MapAgent.begin(); while (it != m_MapAgent.end()) { if (!(it->second->hdl().expired())) { auto sp = it->second->hdl().lock(); stringstream agent_hdl; agent_hdl << sp.get(); stringstream local_hdl; local_hdl << hdl.lock().get(); if (agent_hdl.str() == local_hdl.str()) { agentId = it->second->id(); // 保存坐席id Format fmt("座席掉线,agentId = %s,extenNo = %s,IP = %s"); fmt %it->second->id() % it->second->assoExten() % local_hdl.str(); LOG_WARN_S(fmt.str()); //m_Gateway.delAgent(agentId); m_Gateway.delAgent(agentId,it->second->groups()); it->second->removeAgent("坐席掉线"); FS_LINK_DELETE(it->second); m_MapAgent.erase(it); break; } } ++it; } if (!agentId.empty()) { std::string data = creatJson("Monitor", "签出", agentId); m_MonitAgents.loopAgent(m_Server, data); // 通知班长坐席监控 m_MonitAgents.delAgent(agentId); } } void CFsProxy::__doAutoTask(Task & task) { switch (task.type) { case VOICE_CALL: // 语音外呼 { __voicCall(task); } break; case AGENT_CALL: // 指定坐席外呼 set 在当前 channel 上设置变量,而 export 在(a-leg 和 b-leg) 两个 channel 上都设置 { LineOpParam param; param.nParam1 = task.agent; // 坐席号 param.nParam2 = task.type; // 自动外呼类型 param.nParam3 = task.id; // 自动外呼任务id param.szParam1 = getExtenByAgent(task.agent); // 坐席对应的分机号 param.szParam2 = task.number; COperationReactor::GetInstance()->procOperation(opInstance++, LINE_OP_AUTO_CALL, atoi(param.szParam1.c_str()), ¶m); } break; case AUTO_CALL: // 系统自动分配坐席外呼 break; default: break; } if (task.id != 0) m_AutoCall.updateTask(std::to_string(task.id)); // 更新任务 } bool CFsProxy::__voicCall(Task & task) { std::string content = task.content; std::string filePath; if (!tts.TextToAudio(content, filePath)) { LOG_WARN_S("文本转语音失败"); return false; }; std::string callee = task.number; std::string caller; std::string CallString; m_CallStringMaker.makeCallString(caller, callee, CallString); Format fmt("bgapi originate {origination_caller_id_number=%s,%s=%ld,%s=%s,%s=%ld}%s VOICECALL XML %s\r\n%s: %ld"); fmt %caller % ESL_VAR_OP_TYPE % task.type %"filePath" %filePath % ESL_VAR_TASK_ID % task.id % CallString % "AutoCall" % ESL_HEADER_JOB_UUID % task.uniqueId(); return m_Gateway.sendCmd(fmt.str()); } CFsProxy CFsProxy::instance;