linux版本中间件

EslGateway.cpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. #include "EslGateway.h"
  2. #include <thread>
  3. #include <regex>
  4. #include "pubdef.h"
  5. #include "Config.h"
  6. #include "Log.h"
  7. #include "Util.h"
  8. #define ESL_CMD_SCAN_INTERNAL "api sofia status profile internal reg\n\n"
  9. #define ESL_HEADER_CALLID "Channel-Call-UUID"
  10. #define ESL_HEADER_CCID "variable_cc_member_session_uuid" // freeswitchs自带的callcenter模块转接坐席后,callid产生变化,可通过该通道变量进行关联
  11. #define ESL_HEADER_CHANID "Unique-ID"
  12. #define ESL_HEADER_CALLER "Caller-Caller-ID-Number"
  13. #define ESL_HEADER_CALLEE "Caller-Callee-ID-Number"
  14. #define ESL_HEADER_DEST_NUM "Caller-Destination-Number"
  15. #define ESL_HEADER_DIRECTION "Call-Direction"
  16. #define ESL_HEADER_SUBCLASS "Event-Subclass"
  17. #define ESL_HEADER_EXTEN_NO "username"
  18. // ESL事件头域常量值
  19. #define ESL_HDR_DIRECTION_INBOUND "inbound"
  20. #define ESL_HDR_DIRECTION_OUTBOUND "outbound"
  21. #define ESL_HDR_SUBCLASS_SIP_REG "sofia::register"
  22. #define ESL_HDR_SUBCLASS_SIP_UNREG "sofia::unregister"
  23. EslGateway::EslGateway() :m_Stop(true), m_pEventThread(nullptr), m_pEsSearch(nullptr)
  24. {
  25. }
  26. EslGateway::~EslGateway()
  27. {
  28. }
  29. bool EslGateway::initDB()
  30. {
  31. auto cfg = CConfig::GetInstance();
  32. return true;
  33. }
  34. bool EslGateway::start()
  35. {
  36. memset(&m_EslHdl4Listen, '\0', sizeof(esl_handle_t));
  37. memset(&m_EslHdl4Send, '\0', sizeof(esl_handle_t));
  38. auto cfg = CConfig::GetInstance();
  39. LOG_INFO("{EslGateway}: 处理客户端连接, ip = %s,port=%d,pwd=%s", cfg->fsIp().c_str(), cfg->fsPort(), cfg->fsPwd().c_str());
  40. esl_connect(&m_EslHdl4Listen, cfg->fsIp().c_str(), cfg->fsPort(), NULL, cfg->fsPwd().c_str());
  41. if (!m_EslHdl4Listen.connected)
  42. return false;
  43. LOG_INFO("{EslGateway}: 处理客户端连接m_EslHdl4Listen OK");
  44. esl_events(&m_EslHdl4Listen, ESL_EVENT_TYPE_PLAIN,
  45. ("CHANNEL_CREATE CHANNEL_ANSWER CHANNEL_HANGUP_COMPLETE CHANNEL_PARK CHANNEL_EXECUTE_COMPLETE DETECTED_SPEECH CUSTOM sofia::register sofia::unregister callcenter::info"));
  46. esl_connect(&m_EslHdl4Send, cfg->fsIp().c_str(), cfg->fsPort(), NULL, cfg->fsPwd().c_str());
  47. if (!m_EslHdl4Send.connected) {
  48. esl_disconnect(&m_EslHdl4Listen);
  49. return false;
  50. }
  51. scanExten();
  52. m_Stop = false;
  53. if (m_pEventThread == nullptr) {
  54. m_pEventThread = std::make_unique<boost::thread>(std::bind(&this_type::__eventThread, this));
  55. }
  56. if (m_pEsSearch == nullptr) {
  57. m_pEsSearch = std::make_unique<EsSearch>();
  58. }
  59. return true;
  60. }
  61. void EslGateway::stop()
  62. {
  63. m_Stop.store(true);
  64. if (m_pEventThread) {
  65. m_pEventThread->interrupt();
  66. m_pEventThread->join();
  67. m_pEventThread.reset();
  68. }
  69. memset(&m_EslHdl4Listen, '\0', sizeof(esl_handle_t));
  70. memset(&m_EslHdl4Send, '\0', sizeof(esl_handle_t));
  71. }
  72. void EslGateway::scanExten()
  73. {
  74. if (esl_send_recv(&m_EslHdl4Send, ESL_CMD_SCAN_INTERNAL) != ESL_SUCCESS)
  75. return;
  76. if (m_EslHdl4Send.last_sr_event == nullptr || m_EslHdl4Send.last_sr_event->body == nullptr)
  77. return;
  78. std::string body = m_EslHdl4Send.last_sr_event->body;
  79. std::list<std::string> rows;
  80. boost::split(rows, body, boost::is_any_of("\n"));
  81. while (!rows.empty()) {
  82. std::string var = rows.front();
  83. rows.pop_front();
  84. std::regex space("\t| ");
  85. var = std::regex_replace(var, space, "");
  86. std::regex reg("IP:(.*)");
  87. if (std::regex_match(var, reg)) {
  88. std::string extenNo, extenIP;
  89. extenIP = std::regex_replace(var, reg, "$1");
  90. while (!rows.empty()) {
  91. var = rows.front();
  92. rows.pop_front();
  93. std::regex space("\t| ");
  94. var = std::regex_replace(var, space, "");
  95. reg = std::regex("Auth-User:(.*)");
  96. if (std::regex_match(var, reg)) {
  97. extenNo = std::regex_replace(var, reg, "$1");
  98. if (!extenNo.empty() && !extenIP.empty()) {
  99. LOG_DEBUG("分机注册:%s %s", extenNo.c_str(), extenIP.c_str());
  100. __addExtenChan(extenNo);
  101. }
  102. break;
  103. }
  104. }
  105. }
  106. }
  107. }
  108. bool EslGateway::execPlay(const std::string & strChanId, const std::string & strFilePath)
  109. {
  110. //return false;
  111. execte("set", "playback_terminators=none", strChanId);
  112. //execte("break", "continue", strChanId);
  113. return execte("playback", strFilePath.c_str(), strChanId);
  114. }
  115. bool EslGateway::excDetectSpeech(const std::string & strChanId)
  116. {
  117. //return true;
  118. //execte("set", "fire_asr_events=true", strChanId);
  119. return execte("detect_speech", "unimrcp:mrcpv2 hello hello", strChanId);
  120. }
  121. bool EslGateway::excDetectSpeechPause(const std::string & strChanId)
  122. {
  123. return execte("detect_speech", "pause", strChanId);
  124. }
  125. bool EslGateway::excDetectSpeechResume(const std::string & strChanId)
  126. {
  127. //return true;
  128. return execte("detect_speech", "resume", strChanId);
  129. }
  130. bool EslGateway::excPlayAndDetectSpeech(const std::string & strChanId, const std::string & strFilePath)
  131. {
  132. /*Format fmt("%s detect:mrcpv2 {start-input-timers=false,no-input-timeout=80000,recognition-timeout=80000}builtin:grammar/boolean?language=en-US;y=1;n=2");
  133. fmt %strFilePath;*/
  134. return execte("play_and_detect_speech", "ivr/say_yes_or_no.wavdetect:unimrcp {start-input-timers=false,no-input-timeout=5000,recognition-timeout=5000}builtin:grammar/boolean?language=en-US;y=1;n=2", strChanId);
  135. }
  136. bool EslGateway::execte(const std::string & strApp, const std::string & strParam, const std::string & strChanId)
  137. {
  138. esl_status_t Status = esl_execute(&m_EslHdl4Send, strApp.c_str(), strParam.c_str(), strChanId.c_str());
  139. return Status == ESL_SUCCESS;
  140. }
  141. void EslGateway::__eventThread(void)
  142. {
  143. esl_event_t* pEvent = nullptr;
  144. esl_handle_t* pListenHandle = &m_EslHdl4Listen;
  145. // 下面的设置保证执行同时多个APP时,后面的不会抢占前面的
  146. pListenHandle->event_lock = 1;
  147. while (!m_Stop)
  148. {
  149. // esl_recv_event会阻塞线程,其第二个参数为1,表示优先检查内部缓存列表,防止遗漏事件。
  150. auto status = esl_recv_event(pListenHandle, 1, nullptr);
  151. if ((status == ESL_SUCCESS || status == ESL_BREAK) && !m_Stop)
  152. {
  153. pEvent = pListenHandle->last_ievent;
  154. if (pEvent != nullptr)
  155. {
  156. __onEslEvent(pEvent);
  157. }
  158. }
  159. else // 连接中断
  160. {
  161. // 连接中断
  162. LOG_WARN_S("FS链接断开...");
  163. do
  164. {
  165. LOG_WARN_S("FS 3秒后自动重连...");
  166. std::this_thread::sleep_for(std::chrono::seconds(3));
  167. LOG_WARN_S("FS开始重连...");
  168. } while (!start() && !m_Stop);
  169. }
  170. }
  171. }
  172. void EslGateway::__onEslEvent(esl_event_t * pEvent)
  173. {
  174. switch (pEvent->event_id)
  175. {
  176. case ESL_EVENT_CHANNEL_CREATE:
  177. case ESL_EVENT_CHANNEL_ANSWER:
  178. case ESL_EVENT_CHANNEL_HANGUP_COMPLETE:
  179. __onEslEvtChanEvent(pEvent);
  180. break;
  181. case ESL_EVENT_DETECTED_SPEECH:
  182. __detectSpeech(pEvent);
  183. break;
  184. case ESL_EVENT_CHANNEL_EXECUTE_COMPLETE:
  185. __onEslEvtChanExecComplete(pEvent);
  186. break;
  187. case ESL_EVENT_CHANNEL_PARK:
  188. __onEslEvtPark(pEvent);
  189. break;
  190. case ESL_EVENT_CUSTOM:
  191. __onEslEvtCustom(pEvent);
  192. break;
  193. default:
  194. break;
  195. }
  196. }
  197. void EslGateway::__onEslEvtChanEvent(esl_event_t * pEvent)
  198. {
  199. // 获取会话ID
  200. std::string strCallID = "";
  201. if ((strCallID = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_CCID))) == "") {
  202. strCallID = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_CALLID));
  203. }
  204. // 获取通道ID
  205. const auto pChanID = esl_event_get_header(pEvent, ESL_HEADER_CHANID);
  206. const std::string strChanID = std::to_string(pChanID);
  207. // 获取主被叫
  208. std::string strCaller = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_CALLER));
  209. std::string strCallee = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_CALLEE));
  210. if (strCallee == "") {
  211. strCallee = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_DEST_NUM));
  212. }
  213. // 获取通道方向
  214. /*CALL_DIRECTION emDirection;
  215. if (strcmp(esl_event_get_header(pEvent, ESL_HEADER_DIRECTION), ESL_HDR_DIRECTION_INBOUND) == 0)
  216. emDirection = CALL_DIRECTION_INBOUND;
  217. else
  218. emDirection = CALL_DIRECTION_OUTBOUND;*/
  219. switch (pEvent->event_id)
  220. {
  221. case ESL_EVENT_CHANNEL_CREATE:
  222. {
  223. LOG_INFO_S(boost::str(Format("ESL通道创建事件[%s][%s]") % strCallID %strChanID));
  224. }
  225. break;
  226. case ESL_EVENT_CHANNEL_ANSWER:
  227. {
  228. LOG_INFO_S(boost::str(Format("ESL通道接听事件[%s][%s]") % strCallID %strChanID));
  229. }
  230. break;
  231. case ESL_EVENT_CHANNEL_HANGUP_COMPLETE:
  232. {
  233. LOG_INFO_S(boost::str(Format("ESL通道挂机事件[%s][%s]") % strCallID %strChanID));
  234. }
  235. break;
  236. default:
  237. break;
  238. }
  239. }
  240. void EslGateway::__hangupEvent(esl_event_t * pEvent)
  241. {
  242. }
  243. void EslGateway::__detectSpeech(esl_event_t * pEvent)
  244. {
  245. /*LOG_DEBUG_S("语音识别消息");
  246. char *pContent = new char[2048];
  247. esl_event_serialize(pEvent, &pContent, ESL_TRUE);
  248. LOG_DEBUG_S(pContent);
  249. if (pContent) {
  250. delete pContent;
  251. pContent = nullptr;
  252. }*/
  253. std::string strCallID = "";
  254. auto pCallID = esl_event_get_header(pEvent, ESL_HEADER_CALLID);
  255. strCallID = pCallID == nullptr ? "" : pCallID;
  256. auto pCallCCID = esl_event_get_header(pEvent, ESL_HEADER_CCID);
  257. if (pCallCCID != nullptr) {
  258. strCallID = pCallCCID;
  259. }
  260. // 获取通道ID
  261. std::string strChanID = "";
  262. auto pChanID = esl_event_get_header(pEvent, ESL_HEADER_CHANID);
  263. strChanID = pChanID == nullptr ? "" : pChanID;
  264. char* speechType = nullptr;
  265. speechType = esl_event_get_header(pEvent, "Speech-Type");
  266. if (speechType == nullptr) {
  267. LOG_WARN_S(boost::str(Format("ESL语音识别事件[%s][%s],Speech-Type 空") % strCallID %strChanID));
  268. return;
  269. }
  270. LOG_INFO_S(boost::str(Format("ESL语音识别事件[%s][%s][%s]") % speechType % strCallID %strChanID));
  271. if (strcmp(speechType, "begin-speaking") == 0) {
  272. }
  273. else if (strcmp(speechType, "detected-speech") == 0)
  274. {
  275. excDetectSpeechPause(strChanID); // 暂停识别
  276. char* result = esl_event_get_body(pEvent);
  277. if (result) {
  278. LOG_DEBUG("%s",result);
  279. std::string strRes, err;
  280. parseASRXml(result, strRes, err);
  281. LOG_INFO_S(boost::str(Format("识别结果:\n[%s]") % strRes));
  282. std::wstring wsRes = string2wstring(strRes);
  283. if (strRes == "") { // 没有识别到说的内容
  284. execPlay(pChanID, "E:\\zhinengwav\\empty.wav");
  285. }
  286. /*else if (wsRes.compare(L"人工") == 0) {*/
  287. else if (strRes.find("人工")!=std::string::npos) {
  288. execte("set", "trunkCallIn=2", strChanID);
  289. execPlay(pChanID, "E:\\zhinengwav\\turnagent.wav");
  290. }
  291. else {
  292. auto answer = m_pEsSearch->Search(strRes);
  293. if (answer == "") { // 未匹配到答案
  294. execPlay(pChanID, "E:\\zhinengwav\\unknown.wav");
  295. }
  296. else {
  297. execPlay(pChanID, "E:\\zhinengwav\\wav\\" + answer);
  298. }
  299. }
  300. }
  301. //excDetectSpeechResume(strChanID); // 识别结束,继续识别
  302. }
  303. }
  304. void EslGateway::__onEslEvtChanExecComplete(esl_event_t * pEvent)
  305. {
  306. /*LOG_DEBUG_S("APP执行完成消息");
  307. char *pContent = new char[2048];
  308. esl_event_serialize(pEvent, &pContent, ESL_TRUE);
  309. LOG_DEBUG_S(pContent);
  310. if (pContent) {
  311. delete pContent;
  312. pContent = nullptr;
  313. }*/
  314. auto isTrunkCallIn = esl_event_get_header(pEvent, "variable_trunkCallIn");
  315. if (isTrunkCallIn == NULL || strcmp(isTrunkCallIn, "1") == 0 || strcmp(isTrunkCallIn, "2") == 0) return;
  316. auto ChanId = esl_event_get_header(pEvent, ESL_HEADER_CHANID);
  317. const char* pApp = esl_event_get_header(pEvent, "Application");
  318. LOG_INFO("通道[%s]执行APP[%s]完成",ChanId,pApp);
  319. if (strcmp(pApp, "playback") == 0) {
  320. LOG_INFO("通道[%s]放音完成,继续识别", ChanId);
  321. excDetectSpeechResume(ChanId); // 放音完成,继续识别
  322. }
  323. }
  324. void EslGateway::__onEslEvtCustom(esl_event_t * pEvent)
  325. {
  326. const char* pSubClass = esl_event_get_header(pEvent, ESL_HEADER_SUBCLASS);
  327. if (pSubClass == nullptr)return;
  328. auto HeaderValue = esl_event_get_header(pEvent, ESL_HEADER_EXTEN_NO);
  329. if (strcmp(pSubClass, ESL_HDR_SUBCLASS_SIP_REG) == 0) { // 分机注册
  330. if (HeaderValue == nullptr) return;
  331. __addExtenChan(std::to_string(HeaderValue));
  332. }
  333. else if (strcmp(pSubClass, ESL_HDR_SUBCLASS_SIP_UNREG) == 0) { // 分机取消注册
  334. if (HeaderValue == nullptr) return;
  335. __delExtenChan(std::to_string(HeaderValue));
  336. }
  337. }
  338. void EslGateway::__onEslEvtPark(esl_event_t * pEvent)
  339. {
  340. // 获取通道ID
  341. auto ChanId = esl_event_get_header(pEvent, ESL_HEADER_CHANID);
  342. if (ChanId == NULL)return;
  343. // 获取通道方向
  344. auto Direct = esl_event_get_header(pEvent, ESL_HEADER_DIRECTION);
  345. if (!Direct || strcmp(Direct, ESL_HDR_DIRECTION_INBOUND) != 0) return;
  346. auto isTrunkCallIn = esl_event_get_header(pEvent, "variable_trunkCallIn");
  347. if (isTrunkCallIn == NULL || strcmp(isTrunkCallIn, "1") != 0) return;
  348. auto strCaller = esl_event_get_header(pEvent, ESL_HEADER_CALLER);
  349. execte("set", "trunkCallIn=0", ChanId);
  350. excDetectSpeech(ChanId);
  351. //excPlayAndDetectSpeech("E:\\zhinengwav\\welcome.wav",ChanId);
  352. LOG_INFO("来电[%s]进入智能语音系统", strCaller);
  353. }
  354. void EslGateway::__addExtenChan(const std::string & strNo)
  355. {
  356. LOG_INFO_S(boost::str(Format("分机[%s]注册") % strNo));
  357. }
  358. void EslGateway::__delExtenChan(const std::string & strNo)
  359. {
  360. LOG_INFO_S(boost::str(Format("分机[%s]注册取消成功") % strNo));
  361. }
  362. std::string EslGateway::__createJson(const std::string & strType, const bool & bOk, const std::string&strDesc)
  363. {
  364. Json::Value root;
  365. root["Type"] = strType;
  366. root["Result"] = bOk;
  367. root["Desc"] = strDesc;
  368. Json::StreamWriterBuilder builder;
  369. return Json::writeString(builder, root);
  370. }