首先你得有个获取队列情况的接口。如果使用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
好了,看下效果:
60 queries in 2.313 seconds |