首先你得有个获取队列情况的接口。如果使用ami的话太麻烦,接收的数据控制不好断节,所以我用了php+shell的形式获取数据。由于是局域网内使用,所以没加验证,有需求的自己加一下。
vi /var/www/html/cli.php
按i进入插入模式,粘贴上如下代码:
<?php if(isset($_GET["cmd"])){ //先判断是否是预期参数 $fp=popen("asterisk -x \"".$_GET["cmd"]."\"","r"); //asterisk -x单句命令执行 while(!feof($fp)) echo fgets($fp,4096); pclose($fp); } ?>
按Esc输入":wq"保存退出。好了这个接口就可以使用来执行Asterisk CLI命令了。
然后通过你自己的坐席数据库接口,结合ahk写一个客户端。
;数据库配置信息 host = 135.230.71.1 api = http://%host%/sqlapi.php #MaxMem 1024 ;检查连通性 if Not InStr(ping_info := ping(host),"正常") { MsgBox, 4112, 网络错误, % ping_info ExitApp } ;创建GUI gui, add, edit, x0 y0 w800 h400 vshow, gui, show, , 本地呼叫系统坐席队列监控 gui, +AlwaysOnTop +Resize ;循环监控坐席状态 3秒间隔 Loop { out = Queue := QueueStatus() ;获取队列状态数组 tp := get_result_obj("select 队列,业务 from [我是口令呼叫系统数据库].[dbo].[呼叫系统业务分派] where 状态=1 and ((cast(getdate() as time) between 开始时间1 and 结束时间1) or (cast(getdate() as time) between 开始时间2 and 结束时间2));") ;这是一个获取当前正在执行的任务的语句 task := [] for k,v in tp task[v[1]] := v[2] tp := get_result_obj("select 分机,工号,姓名 from [我是口令呼叫系统数据库].[dbo].[呼叫系统分机信息] where [工号] is not null") ;获取已经登记的坐席信息 user := [] for k,v in tp user[v[1]] := {"id":v[2],"name":v[3]} ;格式化输出 for group,gv in Queue { out .= task[group] "(" group ") 策略:" gv["strategy"] " " gv["incall"] "个等待来电 平均摘机时间:" gv["holdtime"] "(秒)平均通话时长:" gv["talktime"] "(秒)`n" for exten,mv in gv["member"] { sp = loop % 20-strlen(user[exten]["name"]) sp .= " " ;很笨的一种汉化替换方法 status := RegExReplace(mv["status"],"In use","通话中") status := RegExReplace(status,"Not in use","空闲") status := RegExReplace(status,"Ringing","振铃") status := RegExReplace(status,"Unavailable","不可用") status := RegExReplace(status,"On hold","保持") out .= " 坐席:" user[exten]["name"] "(" user[exten]["id"] " " exten ")" sp " 类型:" mv["kind"] " 状态:" status " 本次签入后呼叫量:" mv["callcount"] " 最后一次摘机:" mv["lastcalltime"] "秒前`n" } for k,cv in gv["caller"] out .= " 来电:" k "." cv["phone"] " 等待时间:" cv["wait"] "秒`n" out .= "`n" } GuiControl, , show, % out Sleep, 3000 } Return ;界面调整尺寸 GuiSize: GuiControl, move, show, % "w" A_GuiWidth "h" A_GuiHeight Return ;获取状态函数 QueueStatus(){ src := URLDownloadToVar("http://135.230.71.2/cli.php?cmd=queue show") ;你的Asterisk的cli接口 queues := [] Loop, Parse, src, `n, `r { line := A_LoopField if RegExMatch(line,"(\d+) has (\d+) calls \(max unlimited\) in '(\w+)' strategy \((\d+)s holdtime, (\d+)s talktime\), W:\d+, C:\d+, A:\d+, SL:[0-9\.]+% within \d+s",qtm) ;队列标题匹配 { cqueue := qtm1 queues[cqueue] := {"incall":qtm2,"strategy":qtm3,"holdtime":qtm4,"talktime":qtm5,member:{},caller:{}} } else if RegExMatch(line,"\s+sip/(\d+) with penalty 1 \(ringinuse disabled\) \((\w+)\) \((.+)\) has taken (\d+) calls \(last was (\d+) secs ago\)",mcm) ;坐席匹配1 queues[cqueue]["member"][mcm1] := {"kind":mcm2,"status":mcm3,"callcount":mcm4,"lastcalltime":mcm5} else if RegExMatch(line,"\s+sip/(\d+) with penalty 1 \(ringinuse disabled\) \((\w+)\) \((.+)\) has taken no calls yet",mcm) ;坐席匹配2 queues[cqueue]["member"][mcm1] := {"kind":mcm2,"status":mcm3,"callcount":0,"lastcalltime":"NULL"} else if RegExMatch(line,"\s+(\d+). DAHDI/i1/(\d+)-\w+ \(wait: 0:(\d+), prio: 0\)",ccm) ;来电匹配 queues[cqueue]["caller"][ccm1] := {"phone":ccm2,"wait":ccm3} } Return queues } ;返回csv带标题格式查询结果 get_result_with_colname(sql){ global api result := URLDownloadToVar(api "?str=" urlencode(sql "我是口令") "&o=1","UTF-8") return RegExReplace(result,"`n$","") } ;返回csv查询结果 get_result(sql){ global api result := URLDownloadToVar(api "?str=" urlencode(sql "我是口令"),"UTF-8") return RegExReplace(result,"`n$","") } ;返回查询数组 get_result_obj(sql){ global api result := URLDownloadToVar(api "?str=" urlencode(sql "我是口令"),"UTF-8") ;return RegExReplace(result,"`n$","") line := strsplit(result,"`n","`r") for k,v in line { if v line[k] := strsplit(v,",") Else line.remove(k) } return line } ;返回1行数据 get_1_result(sql){ global api result := URLDownloadToVar(api "?str=" urlencode(sql "我是口令"),"UTF-8") return RegExReplace(result,"^([^\n]*)\n.*$","$1") } ;返回执行影响结果 get_rowcount(sql){ global api result := URLDownloadToVar(api "?str=" urlencode(sql "我是口令") "&o=2","UTF-8") return RegExReplace(result,"^([^\n]*)\n.*$","$1") } URLDownloadToVar(url, Encoding = "",Method="GET",postData=""){ ;网址,编码,请求方式,post数据 hObject:=ComObjCreate("WinHttp.WinHttpRequest.5.1") if Method = GET { Try { hObject.Open("GET",url) hObject.Send() } catch e return -1 } else if Method = POST { Try { hObject.Open("POST",url,False) hObject.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded") hObject.Send(postData) } catch e return -1 } if (Encoding && hObject.ResponseBody) { oADO := ComObjCreate("adodb.stream") oADO.Type := 1 oADO.Mode := 3 oADO.Open() oADO.Write(hObject.ResponseBody) oADO.Position := 0 oADO.Type := 2 oADO.Charset := Encoding return oADO.ReadText(), oADO.Close() } return hObject.ResponseText } Ansi2UTF8(sString) { Ansi2Unicode(sString, wString, 0) Unicode2Ansi(wString, zString, 65001) Return zString } UTF82Ansi(zString) { Ansi2Unicode(zString, wString, 65001) Unicode2Ansi(wString, sString, 0) Return sString } Ansi2Unicode(ByRef sString, ByRef wString, CP = 0) { nSize := DllCall("MultiByteToWideChar" , "Uint", CP , "Uint", 0 , "Uint", &sString , "int", -1 , "Uint", 0 , "int", 0) VarSetCapacity(wString, nSize * 2) DllCall("MultiByteToWideChar" , "Uint", CP , "Uint", 0 , "Uint", &sString , "int", -1 , "Uint", &wString , "int", nSize) } Unicode2Ansi(ByRef wString, ByRef sString, CP = 0) { nSize := DllCall("WideCharToMultiByte" , "Uint", CP , "Uint", 0 , "Uint", &wString , "int", -1 , "Uint", 0 , "int", 0 , "Uint", 0 , "Uint", 0) VarSetCapacity(sString, nSize) DllCall("WideCharToMultiByte" , "Uint", CP , "Uint", 0 , "Uint", &wString , "int", -1 , "str", sString , "int", nSize , "Uint", 0 , "Uint", 0) } urlencode(string){ string := Ansi2UTF8(string) StringLen, len, string Loop % len { SetFormat, IntegerFast, hex StringMid, out, string, %A_Index%, 1 hex := Asc(out) hex2 := hex StringReplace, hex, hex, 0x, , All SetFormat, IntegerFast, d hex2 := hex2 If (hex2==33 || (hex2>=39 && hex2 <=42) || hex2==45 || hex2 ==46 || (hex2>=48 && hex2<=57) || (hex2>=65 && hex2<=90) || hex2==95 || (hex2>=97 && hex2<=122) || hex2==126) content .= out Else content .= "`%" hex } Return content } ping(ip){ FileEncoding, RunWait, %ComSpec% /c ping -n 1 %ip% >%A_Temp%\ahk_ping.tmp, , Hide FileRead, content, %A_Temp%\ahk_ping.tmp StringReplace, content, content, `r, , All StringSplit, var, content, `n If content Contains 请求超时,Request timed out Return "请求超时" If content Contains 找不到主机,could not find host Return "找不到主机 " If content Contains 无法访问目标主机,Destination host unreachable Return "无法访问目标主机 " Else { time := RegExReplace(var3, "(来自|Reply from) \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}[\s的回复|]*: (字节|bytes)=\d{1,3}\ (时间|time)[=<](\d{1,3})ms TTL=\d{1,3}","$4") Return "正常 time:" time "ms" } FileEncoding, UTF-8 } ;调试用 输出数组 show_obj(obj,menu_name:=""){ static id if menu_name = { main = 1 id++ menu_name := id } Menu, % menu_name, add, Menu, % menu_name, DeleteAll for k,v in obj { if (IsObject(v)) { id++ submenu_name := id Menu, % submenu_name, add, Menu, % submenu_name, DeleteAll Menu, % menu_name, add, % k ? "【" k "】[obj]" : "", :%submenu_name% show_obj(v,submenu_name) } Else { Menu, % menu_name, add, % k ? "【" k "】" v: "", MenuHandler } } if main = 1 menu,% menu_name, show MenuHandler: return } GuiClose: ExitApp
好了,看下效果:
62 queries in 1.969 seconds |