首先从网上下载VaxSIPUserAgentOCX.ocx (版本7.0.4.8) 参见:http://www.pudn.com/Download/item/id/853476.html
然后执行:
regsvr32 /s VaxSIPUserAgentOCX.ocx
进行控件注册,注册后还需要有SIP账号,此版本控件经测试支持局域网拨打,互联网拨打NAT转换地址会有问题,服务器端显示离线。
Gui Add, ActiveX, x0 y0 w0 h0 vVaxSIPUserAgentOCX, {2935849A-3F6A-4DF8-8395-CF9AB3BE1835} Gui, Add, text, x0 y10 w20 h20, IP Gui, Add, Edit, x20 y10 w170 h20 vip, 192.168.1.2 Gui, Add, text, x200 y10 w30 h20, Port Gui, Add, Edit, x230 y10 w70 h20 vport, 5060 Gui, Add, Button, x300 y10 w100 h20 gconnect, 连接 Gui, Add, Button, x400 y10 w100 h20 gclose, 关闭 Gui, Add, Button, x500 y10 w100 h20 ganswer, 接听 Gui, Add, Button, x600 y10 w100 h20 ghangup, 挂断 Gui, Add, Button, x600 y10 w100 h20 greject, 拒接 Gui, Add, Edit, x0 y40 w100 h20 vphone, Gui, Add, Button, x100 y40 w100 h20 gcall, 拨打 Gui, Add, Button, x0 y70 w100 h20 gtransfer, 转接 Gui, Add, Button, x100 y70 w100 h20 ghold, 保持 Gui, Add, ListView, x0 y100 w800 h600 vlv, ID|Type|Msg gui, Show ComObjConnect(VaxSIPUserAgentOCX, VaxSIPUserAgentOCX_Events) LV_ModifyCol(2,100) LV_ModifyCol(3,1000) global id,lv,phone,VaxSIPUserAgentOCX id := 0 holded := false VaxSIPUserAgentOCX.SetLicenceKey("VAXVOIP.COM-191P238P55P253P97P229P51P76P-100.") ;设置LicenceKey ;MsgBox % VaxSIPUserAgentOCX.GetMyIP() ;MsgBox % VaxSIPUserAgentOCX.GetAudioInDevTotal() ;MsgBox % VaxSIPUserAgentOCX.GetAudioInDevName(0) return connect: Gui, Submit, NoHide MyIP := VaxSIPUserAgentOCX.GetMyIP() ;VARIANT_BOOL Initialize(VARIANT_BOOL BindToListenIP, BSTR ListenIP, long ListenPort, BSTR FromURI, BSTR SIPOutBoundProxy, BSTR SIPProxy, BSTR LoginId, BSTR LoginPwd, VARIANT_BOOL UseSoundDevice, long TotalLine); login := VaxSIPUserAgentOCX.Initialize(false,MyIP,5060,"1010<sip:1010@192.168.1.2>","","192.168.1.2","1001","password",True,1) VaxSIPUserAgentOCX.OpenLine(0, False, MyIP, 7000) ;打开1号通道 VaxSIPUserAgentOCX.RegisterToProxy(3600) ;注册间隔 VaxSIPUserAgentOCX.EnableKeepAlive(60) ;保活 MsgBox % (login=-1 ? "登录成功" : "登录失败:" GetErrorMessage()) "!" return hold: holded := !holded if holded VaxSIPUserAgentOCX.HoldLine(0) else VaxSIPUserAgentOCX.UnHoldLine(0) return transfer: GuiControlGet, phone VaxSIPUserAgentOCX.TransferCall(0,"sip:" phone "@192.168.1.2>") return reject: GuiControlGet, phone VaxSIPUserAgentOCX.RejectCall("CLI:" phone) return answer: GuiControlGet, phone VaxSIPUserAgentOCX.AcceptCall(0, "CLI:" phone, 0, 0) return hangup: VaxSIPUserAgentOCX.Disconnect(0) return call: GuiControlGet, phone MsgBox % VaxSIPUserAgentOCX.Connect(0,"sip:" phone "@192.168.1.2>",0,0) return class VaxSIPUserAgentOCX_Events { OnIncomingDiagnostic(MsgSIP,FromIP,FromPort) { ;id++,LV_Add("",id,"OnIncomingDiagnostic:" FromIP,MsgSIP) //调试SIP消息 消息太多不作显示 } OnOutgoingDiagnostic(MsgSIP,ToIP,ToPort) { ;id++,LV_Add("",id,"OnIncomingDiagnostic:" ToIP,MsgSIP) } OnTryingToReRegister() { id++,LV_Add("",id,"OnTryingToReRegister") } OnSuccessToReRegister() { id++,LV_Add("",id,"OnTryingToReRegister") } OnFailToRegister() { id++,LV_Add("",id,"OnFailToRegister") } OnFailToReRegister() { id++,LV_Add("",id,"OnFailToReRegister") } OnIncomingCall(CallId,DisplayName,UserName,FromURI,ToURI) ;来电 { id++,LV_Add("",id,"OnIncomingCall", CallId) GuiControl, , phone, % CallId SoundBeep, , 500 } OnDisconnectCall(LineNo) { id++,LV_Add("",id,"OnDisconnectCall", LineNo) } } GuiClose: close: VaxSIPUserAgentOCX.UnInitialize() ExitApp GetErrorMessage() { ErrNo = VaxSIPUserAgentOCX.GetVaxObjectError() if(ErrNo = 10) return "You are not Online, please click the 'Online' button first." if(ErrNo = 11) return "Cann't open local communication port. Another softphone (x-Ten, x-lite or skype etc) is already running. Please close it first." if(ErrNo = 12) return "License Key is not valid." if(ErrNo = 13) return "Fail to initialize VaxVoIP task window." if(ErrNo = 14) return "Cann't access Input/Mic device or device is already in use." if(ErrNo = 15) return "Cann't access Output/Speaker device or device is already in use." if(ErrNo = 16) return "Input/Mic device is not open." if(ErrNo = 17) return "Output/Speaker device is not open." if(ErrNo = 18) return "Your sound device does not support mic volume." if(ErrNo = 19) return "Your sound device does not support speaker volume." if(ErrNo = 20) return "Recording media initialization fail." if(ErrNo = 21) return "Cann't open the wave file." if(ErrNo = 22) return "Provided SIP URI is not valid." if(ErrNo = 23) return "Codec is not supported." if(ErrNo = 24) return "Error to create SDP (Session Description Protocol) request." if(ErrNo = 25) return "Error to create CONNECTION request. Please check the provided SIP URI is valid." if(ErrNo = 26) return "Error to create REGISTER request. Please check the provided SIP URI is valid." if(ErrNo = 27) return "Error to create UN-REGISTER request. Please check the provided SIP URI is valid." if(ErrNo = 28) return "Error to create DISCONNECT request." if(ErrNo = 29) return "Line No is not valid." if(ErrNo = 30) return "Line is already busy." if(ErrNo = 31) return "Line is not open." if(ErrNo = 32) return "Invalid Call-Id." if(ErrNo = 33) return "Provided value is not valid." if(ErrNo = 34) return "Selected line is not in voice session." if(ErrNo = 35) return "Fail to read wave file." if(ErrNo = 36) return "Fail to write wave file." if(ErrNo = 37) return "Unsupported wave file format." }
以下是该库的接口定义:
// Generated .IDL file (by the OLE/COM Object Viewer) // // typelib filename: VaxSIPUserAgentOCX.ocx [ uuid(148188FB-A6D9-48BC-AE37-65786376CD8E), version(1.0), helpstring("VaxSIPUserAgentOCX ActiveX Control module"), helpfile("VaxSIPUserAgentOCX.hlp"), helpcontext(00000000), custom(DE77BA64-517C-11D1-A2DA-0000F8773CE9, 83951780), custom(DE77BA63-517C-11D1-A2DA-0000F8773CE9, 1246968603) ] library VAXSIPUSERAGENTOCXLib { // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046} importlib("stdole2.tlb"); // Forward declare all types defined in this typelib dispinterface _DVaxSIPUserAgentOCX; dispinterface _DVaxSIPUserAgentOCXEvents; [ uuid(9858F01E-3474-40B6-996B-7867195B6A6C), helpstring("Dispatch interface for VaxSIPUserAgentOCX Control"), hidden ] dispinterface _DVaxSIPUserAgentOCX { properties: methods: [id(0x00000001)] void UnInitialize(); [id(0x00000002)] VARIANT_BOOL SetLicenceKey(BSTR LicenceKey); [id(0x00000003)] long GetVaxObjectError(); [id(0x00000004)] VARIANT_BOOL CloseLine(long LineNo); [id(0x00000005)] VARIANT_BOOL Connect( long LineNo, BSTR ToURI, long InputDeviceId, long OutputDeviceId); [id(0x00000006)] VARIANT_BOOL Disconnect(long LineNo); [id(0x00000007)] VARIANT_BOOL AcceptCall( long LineNo, BSTR CallId, long InputDeviceId, long OutputDeviceId); [id(0x00000008)] VARIANT_BOOL RejectCall(BSTR CallId); [id(0x00000009)] VARIANT_BOOL TransferCall( long LineNo, BSTR ToURI); [id(0x0000000a)] VARIANT_BOOL HoldLine(long LineNo); [id(0x0000000b)] VARIANT_BOOL UnHoldLine(long LineNo); [id(0x0000000c)] VARIANT_BOOL RegisterToProxy(long Expire); [id(0x0000000d)] VARIANT_BOOL UnRegisterToProxy(); [id(0x0000000e)] VARIANT_BOOL IsLineOpen(long LineNo); [id(0x0000000f)] VARIANT_BOOL IsLineHold(long LineNo); [id(0x00000010)] VARIANT_BOOL IsLineBusy(long LineNo); [id(0x00000011)] VARIANT_BOOL EnableKeepAlive(long Seconds); [id(0x00000012)] void DisableKeepAlive(); [id(0x00000013)] void DeselectAllVoiceCodec(); [id(0x00000014)] void SelectAllVoiceCodec(); [id(0x00000015)] VARIANT_BOOL SelectVoiceCodec(long CodecNo); [id(0x00000016)] VARIANT_BOOL DeselectVoiceCodec(long CodecNo); [id(0x00000017)] BSTR GetMyIP(); [id(0x00000018)] VARIANT_BOOL DigitDTMF( long LineNo, BSTR Digit); [id(0x00000019)] VARIANT_BOOL MuteMic(VARIANT_BOOL Mute); [id(0x0000001a)] VARIANT_BOOL MuteSpk(VARIANT_BOOL Mute); [id(0x0000001b)] long GetMicVolume(); [id(0x0000001c)] VARIANT_BOOL SetMicVolume(long Volume); [id(0x0000001d)] long GetSpkVolume(); [id(0x0000001e)] VARIANT_BOOL SetSpkVolume(long Volume); [id(0x0000001f)] VARIANT_BOOL EnableMicBoost(); [id(0x00000020)] VARIANT_BOOL DisableMicBoost(); [id(0x00000021)] VARIANT_BOOL IsMicBoostEnable(); [id(0x00000022)] VARIANT_BOOL EnableAGC(long Level); [id(0x00000023)] VARIANT_BOOL DisableAGC(); [id(0x00000024)] VARIANT_BOOL EnableEchoNoiseCancellation(); [id(0x00000025)] VARIANT_BOOL DisableEchoNoiseCancellation(); [id(0x00000026)] VARIANT_BOOL IsRecording(long LineNo); [id(0x00000027)] VARIANT_BOOL StopRecording(long LineNo); [id(0x00000028)] VARIANT_BOOL ResetRecording(long LineNo); [id(0x00000029)] VARIANT_BOOL SaveRecordingToWaveFile( long LineNo, BSTR FileName); [id(0x0000002a)] VARIANT_BOOL IsWaveFilePlaying(long LineNo); [id(0x0000002b)] VARIANT_BOOL PlayWaveOpen( long LineNo, BSTR FileName); [id(0x0000002c)] VARIANT_BOOL PlayWaveSkipTo( long LineNo, long Seconds); [id(0x0000002d)] long PlayWaveTotalTime(long LineNo); [id(0x0000002e)] VARIANT_BOOL PlayWavePause(long LineNo); [id(0x0000002f)] VARIANT_BOOL PlayWaveStart( long LineNo, VARIANT_BOOL Listen); [id(0x00000030)] VARIANT_BOOL PlayWaveStop(long LineNo); [id(0x00000031)] long PlayWavePosition(long LineNo); [id(0x00000032)] void EnableDonotDisturb(); [id(0x00000033)] void DisableDonotDisturb(); [id(0x00000034)] VARIANT_BOOL SetTOS( long LineNo, long Value); [id(0x00000035)] long GetTOS(long LineNo); [id(0x00000036)] VARIANT_BOOL EnableForceInbandDTMF(long LineNo); [id(0x00000037)] VARIANT_BOOL DisableForceInbandDTMF(long LineNo); [id(0x00000038)] VARIANT_BOOL SetDTMFVolume(long Volume); [id(0x00000039)] long GetDTMFVolume(); [id(0x0000003a)] VARIANT_BOOL Initialize( VARIANT_BOOL BindToListenIP, BSTR ListenIP, long ListenPort, BSTR FromURI, BSTR SIPOutBoundProxy, BSTR SIPProxy, BSTR LoginId, BSTR LoginPwd, VARIANT_BOOL UseSoundDevice, long TotalLine); [id(0x0000003b)] VARIANT_BOOL StartRecording( long LineNo, long RecordVoice, VARIANT_BOOL RecordCompress); [id(0x0000003c)] long GetMicSoundLevel(); [id(0x0000003d)] long GetSpkSoundLevel(); [id(0x0000003e)] long GetOutboundCodec(long LineNo); [id(0x0000003f)] long GetInboundCodec(long LineNo); [id(0x00000040)] VARIANT_BOOL OpenLine( long LineNo, VARIANT_BOOL BindToRTPRxIP, BSTR RTPRxIP, long RTPRxPort); [id(0x00000041)] long GetAudioInDevTotal(); [id(0x00000042)] long GetAudioOutDevTotal(); [id(0x00000043)] BSTR GetAudioOutDevName(long DeviceId); [id(0x00000044)] BSTR GetAudioInDevName(long DeviceId); [id(0x00000045)] BSTR GetVersionSDK(); [id(0x00000046)] BSTR GetVersionFile(); [id(0x00000047)] VARIANT_BOOL JoinTwoLine( long LineNoA, long LineNoB); }; [ uuid(747FAA95-5D00-4EA1-9F1F-035D2A88FDA4), helpstring("Event interface for VaxSIPUserAgentOCX Control") ] dispinterface _DVaxSIPUserAgentOCXEvents { properties: methods: [id(0x00000001)] void OnIncomingDiagnostic( BSTR MsgSIP, BSTR FromIP, long FromPort); [id(0x00000002)] void OnOutgoingDiagnostic( BSTR MsgSIP, BSTR ToIP, long ToPort); [id(0x00000003)] void OnDTMFDigit( long LineNo, BSTR Digit); [id(0x00000004)] void OnPlayWaveDone(long LineNo); [id(0x00000005)] void OnMsgNOTIFY(BSTR Msg); [id(0x00000006)] void OnTryingToUnRegister(); [id(0x00000007)] void OnFailToUnRegister(); [id(0x00000008)] void OnSuccessToUnRegister(); [id(0x00000009)] void OnTryingToRegister(); [id(0x0000000a)] void OnFailToRegister(); [id(0x0000000b)] void OnSuccessToRegister(); [id(0x0000000c)] void OnFailToConnect(long LineNo); [id(0x0000000d)] void OnConnecting(long LineNo); [id(0x0000000e)] void OnIncomingCallRingingStart(BSTR CallId); [id(0x0000000f)] void OnIncomingCallRingingStop(BSTR CallId); [id(0x00000010)] void OnDisconnectCall(long LineNo); [id(0x00000011)] void OnCallTransferAccepted(long LineNo); [id(0x00000012)] void OnProvisionalResponse( long LineNo, long StatusCode, BSTR ReasonPhrase); [id(0x00000013)] void OnRedirectionResponse( long LineNo, long StatusCode, BSTR ReasonPhrase, BSTR Contact); [id(0x00000014)] void OnRequestFailureResponse( long LineNo, long StatusCode, BSTR ReasonPhrase); [id(0x00000015)] void OnServerFailureResponse( long LineNo, long StatusCode, BSTR ReasonPhrase); [id(0x00000016)] void OnGeneralFailureResponse( long LineNo, long StatusCode, BSTR ReasonPhrase); [id(0x00000017)] void OnSuccessToConnect( long LineNo, BSTR ToRTPIP, long ToRTPPort); [id(0x00000018)] void OnIncomingCall( BSTR CallId, BSTR DisplayName, BSTR UserName, BSTR FromURI, BSTR ToURI); [id(0x00000019)] void OnVoiceMailMsg( VARIANT_BOOL IsMsgWaiting, long NewMsgCount, long OldMsgCount, long NewUrgentMsgCount, long OldUrgentMsgCount, BSTR MsgAccount); [id(0x0000001a)] void OnTryingToReRegister(); [id(0x0000001b)] void OnFailToReRegister(); [id(0x0000001c)] void OnSuccessToReRegister(); }; [ uuid(2935849A-3F6A-4DF8-8395-CF9AB3BE1835), helpstring("VaxSIPUserAgentOCX Control"), control ] coclass VaxSIPUserAgentOCX { [default] dispinterface _DVaxSIPUserAgentOCX; [default, source] dispinterface _DVaxSIPUserAgentOCXEvents; }; };
Elastix 2.5的系统是Centos 5.9,Asterisk 11。在测试dialpan拨号方案使用odbc mysql的时候报错驱动/usr/lib64/libmyodbc3_r.so找不到,经验证是系统的mysql-connector-odbc没有安装。
在通过yum安装的时候又发现源已经停止服务了,无法下载,找到一个国外源fq下载完,在另外一台机子上则是选择本地安装yum包,下载地址:
https://centos.pkgs.org/5/centos-x86_64/mysql-connector-odbc-3.51.26r1127-2.el5.x86_64.rpm.html (x64)
https://centos.pkgs.org/5/centos-i386/mysql-connector-odbc-3.51.26r1127-2.el5.i386.rpm.html (x86)
下载完后可以放到当前目录 shell执行:
yum -y localinstall mysql-connector-odbc-3.51.26r1127-2.el5.x86_64.rpm
安装完成后还需配置/etc/odbc.ini:
[MYSQL-asteriskcdrdb] Description = MySQL connection to 'asterisk' database Driver = MySQL Database = asteriskcdrdb Server = localhost Port = 3306 Socket = /var/lib/mysql/mysql.sock User = root Password = MyPass Option = 3
配置完成后可以在shell中测试:
[root@Elastix ~]# isql -v MYSQL-asteriskcdrdb +---------------------------------------+ | Connected! | | | | sql-statement | | help [tablename] | | quit | | | +---------------------------------------+ SQL> show tables; +-----------------------------------------------------------------+ | Tables_in_asteriskcdrdb | +-----------------------------------------------------------------+ | cdr | +-----------------------------------------------------------------+ SQLRowCount returns 1 1 rows fetched
这就算完成了一半,还需在/etc/asterisk/res_odbc.conf中配置dsn:
[asteriskcdrdb] enabled=>yes dsn=>MySQL-asteriskcdrdb pooling=>no limit=>1 pre-connect=>yes share_connections => yes isolation => repeatable_read forcecommit => yes
在/etc/asterisk/func_odbc.conf中配置自定义函数(此处dsn指res_odbc.conf中的[name]):
[SQL] dsn=asteriskcdrdb readsql=${ARG1} synopsis=ODBC_SQL(sql)
具体配置说明详见:https://wiki.asterisk.org/wiki/display/AST/Getting+Asterisk+Connected+to+MySQL+via+ODBC
http://www.asteriskdocs.org/en/3rd_Edition/asterisk-book-html-chunk/getting_funky.html
配置完后注意在Asterisk CLI中执行reload或者shell执行:
asterisk -x reload
最后就可以在dialpan中测试使用了,如下面的例子(/etc/asterisk/extensions.conf)拨打876在asterisk日志中输出结果:
[from-internal] exten => 876,1,Answer exten => 876,n,Noop(${ODBC_SQL("select now()")}) exten => 876,n,Noop(${ODBC_SQL("select top 1 uniquid from cdr order by calldate desc")}) exten => 876,n,Background(star) exten => 876,n,Wait(60) include => from-internal-noxfer include => from-internal-xfer include => bad-number ; auto-generated
首先,我们需要找一个引导,用安装光盘进入恢复模式可能有些问题,所以这次我们用U盘Grub4DOS的方式直接进入系统再修复。
插入U盘到电脑,打开BOOTICE,格式化成FAT格式,然后设置主引导记录:
把下载的grub4dos解压,复制grldr和grub.exe到U盘,创建menu.lst,内容如下(具体的启动内核可能不一样):
default=0 timeout=5 title hd1_371 root (hd1,0) kernel /boot/vmlinuz-2.6.18-371.1.2.el5 ro root=LABEL=/ initrd /boot/initrd-2.6.18-371.1.2.el5.img title hd1_406 root (hd1,0) kernel /boot/vmlinuz-2.6.18-406.el5 ro root=LABEL=/ initrd /boot/initrd-2.6.18-406.el5.img title hd0_371 root (hd0,0) kernel /boot/vmlinuz-2.6.18-371.1.2.el5 ro root=LABEL=/ initrd /boot/initrd-2.6.18-371.1.2.el5.img title hd0_406 root (hd0,0) kernel /boot/vmlinuz-2.6.18-406.el5 ro root=LABEL=/ initrd /boot/initrd-2.6.18-406.el5.img title hd2_371 root (hd2,0) kernel /boot/vmlinuz-2.6.18-371.1.2.el5 ro root=LABEL=/ initrd /boot/initrd-2.6.18-371.1.2.el5.img title hd2_406 root (hd2,0) kernel /boot/vmlinuz-2.6.18-406.el5 ro root=LABEL=/ initrd /boot/initrd-2.6.18-406.el5.img
设置服务器从U盘启动,尝试启动到系统,进入之后进行修复grub操作
grub-install --root-directory=/boot /dev/hda
这就大功告成了!
由于对接的有个网关定期换IP,手动设定很麻烦。所以写了这么一个脚本扔到cron里。
首先需要修改/etc/asterisk/sip.conf,找到#include sip_custom.conf这里,修改成:
;#include sip_custom.conf #include sip_additional.conf #include sip_custom.conf
然后把py脚本整到/bin里:
import re import os part_a = '''[9031] disallow=all username=9031 type=friend secret=9031 qualify=yes insecure=invite host=dynamic dtmfmode=rfc2833 context=from-pstn canreinvite=0 allow=g723 [9031_Out] disallow=all username=9031 type=friend secret=9031 qualify=yes port=7878 host=''' part_b = ''' fromeuser=9031 context=from-pstn allow=g723 ''' res = os.popen('asterisk -x "sip show peers"').read() lines = res.split("\n") for line in lines: if line.find("9031/9031")>-1: inbound = re.match("9031/9031\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+D\s+Yes\s+Yes\s+\d+\s+OK \(\d+ ms\)",line) elif line.find("9031_Out/9031")>-1: outbound = re.match("9031_Out/9031\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+Yes\s+Yes\s+\d+\s+UNREACHABLE",line) if inbound and outbound and inbound.group(1) <> outbound.group(1): print "Change peer IP "+outbound.group(1)+" to "+inbound.group(1) f = open("\etc\asterisk\sip_custom.conf","w") f.write(part_a+inbound.group(1)+part_b) f.close() os.popen('asterisk -x reload') else: print "Peer IP has not changed"
再修改/etc/crontab,添加一个定时任务:
*/10 * * * * root python /bin/check_trunk_ip.py
最后重启下crond服务就Ok啦。
service crond restart
#移除原文件 rm /etc/motd #重新创建 按i插入, Esc退出编辑模式,输入:wq保存退出 vi /etc/motd #设置只读 chmod 444 /etc/motd #不允许删除 chattr +ia /etc/motd reboot
环境Elastix2.5
编辑/etc/asterisk/extensions.conf
[from-internal] exten=>#,1,Answer //这里设置的是#号键 exten=>#,n,MySql(connect connid localhost root 123456 asteriskcdrdb) //连接mysql 操作 ;exten=>#,n,MySql(query resultidr ${connid} insert into test (msg) values ('${CALLERID(num)}')) //测试插入 exten=>#,n,MySql(query resultidr ${connid} select dst as a,uniqueid as b from cdr where src=${CALLERID(num)} order by uniqueid desc limit 1) //获取上一个呼叫记录数据 exten=>#,n,MySql(Fetch foundrow ${resultidr} a b) //取出变量 exten=>#,n,MySql(clear ${resultidr}) //清空结果集 exten=>#,n,MySql(query resultidr ${connid} update cdr set userfield='OK' where uniqueid=${b}) //更新 exten=>#,n,MySql(clear ${resultidr}) exten=>#,n,MySql(disconnect ${connid}) //断开连接 exten=>#,n,saydigits(${a}) //报被叫号码 exten=>#,n,Playback(custom/mark_ok) //“已经标记成功”语音 exten=>#,n,Hangup //挂机 //后面是原来的拨号方案 include => from-internal-noxfer include => from-internal-xfer include => bad-number ; auto-generated
保存完毕后需要重载asterisk生效。
转自cnblogs
在asterisk中,定义了许多变量,或是有些变量能够被其读取。下面给出了它们的列表。在每一个application的帮助文档中,你可以获得更多的信息。所有这些变量都是大写的。
被*标记的变量是内建函数,不能在拨号方案中被设置,只能被读取。对这些变量的赋值将被忽略。
${CDR(accountcode)} * Account co
${BLINDTRANSFER} The name of the channel on the other side of a blind transfer
${BRIDGEPEER} Bridged peer
${BRIDGEPVTCALLID} Bridged peer PVT call ID (SIP Call ID if a SIP call)
${CALLERID(ani)} * Caller ANI (PRI channels)
${CALLERID(ani2)} * ANI2 (Info digits) also called Originating line information or OLI
${CALLERID(all)} * Caller ID
${CALLERID(dnid)} * Dialed Number Identifier
${CALLERID(name)} * Caller ID Name on
${CALLERID(num)} * Caller ID Number on
${CALLERID(rdnis)} * Redirected Dial Number ID Service
${CALLINGANI2} * Caller ANI2 (PRI channels)
${CALLINGPRES} * Caller ID presentation for incoming calls (PRI channels)
${CALLINGTNS} * Transit Network Selector (PRI channels)
${CALLINGTON} * Caller Type of Number (PRI channels)
${CHANNEL} * Current channel name
${CONTEXT} * Current context
${DATETIME} * Current date time in the format: DDMMYYYY-HH:MM:SS
(Deprecated; use ${STRFTIME(${EPOCH},,%d%m%Y-%H:%M:%S)})
${DB_RESULT} Result value of DB_EXISTS() dial plan function
${EPOCH} * Current unix style epoch
${EXTEN} * Current extension
${ENV(VAR)} Environmental variable VAR
${GOTO_ON_BLINDXFR} Transfer to the specified context/extension/priority
after a blind transfer (use ^ characters in place of
| to separate context/extension/priority when setting
this variable from the dialplan)
${HANGUPCAUSE} * Asterisk cause of hangup (inbound/outbound)
${HINT} * Channel hints for this extension
${HINTNAME} * Suggested Caller*ID name for this extension
${INVALID_EXTEN} The invalid called extension (used in the "i" extension)
${LANGUAGE} * Current language (Deprecated; use ${LANGUAGE()})
${LEN(VAR)} * String length of VAR (integer)
${PRIORITY} * Current priority in the dialplan
${PRIREDIRECTREASON} Reason for redirect on PRI, if a call was directed
${TIMESTAMP} * Current date time in the format: YYYYMMDD-HHMMSS
(Deprecated; use ${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)})
${TRANSFER_CONTEXT} Context for transferred calls
${FORWARD_CONTEXT} Context for forwarded calls
${UNIQUEID} * Current call unique identifier
${SYSTEMNAME} * value of the systemname option of asterisk.conf
${ENTITYID} * Global Entity ID set automatically, or from asterisk.conf
当你调用有些application的时候,它们会返回一个值供你读取。对于每一个application,这些状态字段是唯一的。各种状态值,前参考每个application的帮助文档。
${AGISTATUS} * agi()
${AQMSTATUS} * addqueuemember()
${AVAILSTATUS} * chanisavail()
${CHECKGROUPSTATUS} * checkgroup()
${CHECKMD5STATUS} * checkmd5()
${CPLAYBACKSTATUS} * controlplayback()
${DIALSTATUS} * dial()
${DBGETSTATUS} * dbget()
${ENUMSTATUS} * enumlookup()
${HASVMSTATUS} * hasnewvoicemail()
${LOOKUPBLSTATUS} * lookupblacklist()
${OSPAUTHSTATUS} * ospauth()
${OSPLOOKUPSTATUS} * osplookup()
${OSPNEXTSTATUS} * ospnext()
${OSPFINISHSTATUS} * ospfinish()
${PARKEDAT} * parkandannounce()
${PLAYBACKSTATUS} * playback()
${PQMSTATUS} * pausequeuemember()
${PRIVACYMGRSTATUS} * privacymanager()
${QUEUESTATUS} * queue()
${RQMSTATUS} * removequeuemember()
${SENDIMAGESTATUS} * sendimage()
${SENDTEXTSTATUS} * sendtext()
${SENDURLSTATUS} * sendurl()
${SYSTEMSTATUS} * system()
${TRANSFERSTATUS} * transfer()
${TXTCIDNAMESTATUS} * txtcidname()
${UPQMSTATUS} * unpausequeuemember()
${VMSTATUS} * voicmail()
${VMBOXEXISTSSTATUS} * vmboxexists()
${WAITSTATUS} * waitforsilence()
${CURL} * Resulting page content for curl()
${ENUM} * Result of application EnumLookup
${EXITCONTEXT} Context to exit to in IVR menu (app background())
or in the RetryDial() application
${MONITOR} * Set to "TRUE" if the channel is/has been monitored (app monitor())
${MONITOR_EXEC} Application to execute after monitoring a call
${MONITOR_EXEC_ARGS} Arguments to application
${MONITOR_FILENAME} File for monitoring (recording) calls in queue
${QUEUE_PRIO} Queue priority
${QUEUE_MAX_PENALTY} Maximum member penalty allowed to answer caller
${QUEUE_MIN_PENALTY} Minimum member penalty allowed to answer caller
${QUEUESTATUS} Status of the call, on
(TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL)
${RECORDED_FILE} * Recorded file in record()
${TALK_DETECTED} * Result from talkdetect()
${TOUCH_MONITOR} The filename base to use with Touch Monitor (auto record)
${TOUCH_MONITOR_PREF} * The prefix for automonitor recording filenames.
${TOUCH_MONITOR_FORMAT} The audio format to use with Touch Monitor (auto record)
${TOUCH_MONITOR_OUTPUT} * Recorded file from Touch Monitor (auto record)
${TOUCH_MONITOR_MESSAGE_START} Recorded file to play for both channels at start of monitoring session
${TOUCH_MONITOR_MESSAGE_STOP} Recorded file to play for both channels at end of monitoring session
${TXTCIDNAME} * Result of application TXTCIDName
${VPB_GETDTMF} chan_vpb
${MEETME_RECORDINGFILE} Name of file for recording a conference with the "r" option
${MEETME_RECORDINGFORMAT} Format of file to be recorded
${MEETME_EXIT_CONTEXT} Context for exit out of meetme meeting
${MEETME_AGI_BACKGROUND} AGI script for Meetme (DAHDI on
${MEETMESECS} * Number of seconds a user participated in a MeetMe conference
${CONF_LIMIT_TIMEOUT_FILE} File to play when time is up. Used with the L() option.
${CONF_LIMIT_WARNING_FILE} File to play as warning if 'y' is defined. The default is to say the time remaining. Used with the L() option.
${VM_CATEGORY} Sets voicemail category
${VM_NAME} * Full name in voicemail
${VM_DUR} * Voicemail duration
${VM_MSGNUM} * Number of voicemail message in mailbox
${VM_CALLERID} * Voicemail Caller ID (Person leaving vm)
${VM_CIDNAME} * Voicemail Caller ID Name
${VM_CIDNUM} * Voicemail Caller ID Number
${VM_DATE} * Voicemail Date
${VM_MESSAGEFILE} * Path to message left by caller
${AUTH_MAILBOX} * Authenticated mailbox
${AUTH_CONTEXT} * Authenticated mailbox context
${DUNDTECH} * The Technology of the result from a call to DUNDiLookup()
${DUNDDEST} * The Destination of the result from a call to DUNDiLookup()
${ANI2} * The ANI2 Co
${CALLTYPE} * Type of call (Speech, Digital, etc)
${CALLEDTON} * Type of number for incoming PRI extension i.e. 0=unknown, 1=international, 2=domestic, 3=net_specific, 4=subscriber, 6=abbreviated, 7=reserved
${CALLINGSUBADDR} * Called PRI Subaddress
${FAXEXTEN} * The extension called before being redirected to "fax"
${PRIREDIRECTREASON} * Reason for redirect, if a call was directed
${SMDI_VM_TYPE} * When an call is received with an SMDI message, the 'type' of message 'b' or 'u'
${SIPCALLID} * SIP Call-ID: header verbatim (for logging or CDR matching)
${SIPDOMAIN} * SIP destination domain of an inbound call (if appropriate)
${SIPUSERAGENT} * SIP user agent (deprecated)
${SIPURI} * SIP uri
${SIP_CODEC} Set the SIP codec for a call
${SIP_URI_OPTIONS} * additional options to add to the URI for an outgoing call
${RTPAUDIOQOS} RTCP QoS report for the audio of this call
${RTPVIDEOQOS} RTCP QoS report for the video of this call
${AGENTMAXLOGINTRIES} Set the maximum number of failed logins
${AGENTUPDATECDR} Whether to update the CDR record with Agent channel da
${AGENTGOODBYE} Sound file to use for "Good Bye" when agent logs out
${AGENTACKCALL} Whether the agent should acknowledge the incoming call
${AGENTAUTOLOGOFF} Auto logging off for an agent
${AGENTWRAPUPTIME} Setting the time for wrapup between incoming calls
${AGENTNUMBER} * Agent number (username) set at login
${AGENTSTATUS} * Status of login ( fail | on | off )
${AGENTEXTEN} * Extension for logged in agent
${DIALEDPEERNAME} * Dialed peer name
${DIALEDPEERNUMBER} * Dialed peer number
${DIALEDTIME} * Time for the call (seconds). Is on
${ANSWEREDTIME} * Time from answer to hangup (seconds)
${DIALSTATUS} * Status of the call, on
${DYNAMIC_FEATURES} * The list of features (from the [applicationmap] section of features.conf) to activate during the call, with feature names separated by '#' characters
${LIMIT_PLAYAUDIO_CALLER} Soundfile for call limits
${LIMIT_PLAYAUDIO_CALLEE} Soundfile for call limits
${LIMIT_WARNING_FILE} Soundfile for call limits
${LIMIT_TIMEOUT_FILE} Soundfile for call limits
${LIMIT_CONNECT_FILE} Soundfile for call limits
${OUTBOUND_GROUP} Default groups for peer channels (as in SetGroup) * See "show application dial" for more information
${AVAILCHAN} * the name of the available channel if on
${AVAILORIGCHAN} * the canonical channel name that was used to create the channel
${AVAILSTATUS} * Status of requested channel
${MACRO_EXTEN} * The calling extensions
${MACRO_CONTEXT} * The calling context
${MACRO_PRIORITY} * The calling priority
${MACRO_OFFSET} Offset to add to priority at return from macro
${SPYGROUP} * A ':' (colon) separated list of group names. (To be set on spied on channel and matched against the g(grp) option)
${OSPINHANDLE} OSP handle of in_bound call
${OSPINTIMELIMIT} Duration limit for in_bound call
${OSPOUTHANDLE} OSP handle of out_bound call
${OSPTECH} OSP technology
${OSPDEST} OSP destination
${OSPCALLING} OSP calling number
${OSPOUTTOKEN} OSP token to use for out_bound call
${OSPOUTTIMELIMIT} Duration limit for out_bound call
${OSPRESULTS} Number of remained destinations
环境说明:
Elastix 2.5
ln -s /var/spool/asterisk/monitor /var/www/html/
接口文件(php):
<?php $con=mysql_connect("localhost","root","passwd"); if(!$con) echo "没有连接成功!"; mysql_select_db("asteriskcdrdb", $con); mysql_query("SET NAMES UTF8"); if(isset($_GET["phone"])){ $phone=$_GET["phone"]; if(isset($_GET["date"])){ $calldate=$_GET["date"]; $q = "SELECT * FROM `cdr` WHERE `dst`='$phone' and cast(`calldate` as date)='$calldate' order by `calldate` desc limit 100"; //此处注意dst和src }else{ $q = "SELECT * FROM `cdr` WHERE `dst`='$phone' order by `calldate` desc limit 100"; } $result = mysql_query($q, $con); if(mysql_num_rows($result)>0){ while($obj=mysql_fetch_object($result)){ $obj->src; $obj->dst; $obj->channel; $obj->billsec; $obj->calldate; $recordingfile = $obj->recordingfile; if($recordingfile){ $a = explode("-",$recordingfile);; $subdir = substr($a[3],0,4)."/".substr($a[3],4,2)."/".substr($a[3],6,2); $uri = "/monitor/$subdir/$recordingfile"; }else{ $uri=''; } echo $obj->src.",".$obj->dst.",".$obj->channel.",".$obj->dstchannel.",".$obj->disposition.",".$obj->billsec.",".$obj->calldate.",".$uri."\n"; } } } mysql_free_result($result); mysql_close($con); ?>
客户端程序(Autohotkey):
FileCreateDir, %A_ScriptDir%\sox FileCreateDir, c:\temp\ FileInstall, libgomp-1.dll, %A_ScriptDir%\sox\libgomp-1.dll FileInstall, pthreadgc2.dll, %A_ScriptDir%\sox\pthreadgc2.dll FileInstall, sox.exe, %A_ScriptDir%\sox\sox.exe FileInstall, zlib1.dll, %A_ScriptDir%\sox\zlib1.dll gui, Add, text, x0 y0 w60 h20, 号码 gui, Add, edit, x60 y0 w140 h20 vphone, Gui, Add, Checkbox, x200 y0 w100 h20 vcd, 呼叫时间 Gui, add, DateTime, x300 y0 w200 h20 vdate, Gui, Add, Button, x500 y0 w100 h20 Default gsearch, 查询 Gui, Add, Button, x600 y0 w100 h20 glisten, 听取所选 gui, add, ListView, x0 y20 w700 h300, 被叫|主叫|通道|目标通道|状态|通话时长|呼叫时间|录音链接 Gui, Add, ActiveX, x0 w700 h100 vwmp, {6BF52A52-394A-11D3-B153-00C04F79FAA6} gui, show, , 本地呼叫系统录音听取 GuiControl, , cd, 1 wmp.Settings.Volume := 100 return search: Gui, Submit, NoHide if cd=1 { FormatTime, calldate, % date, yyyy-MM-dd search_url := "http://192.168.1.2/monitor.php?phone=" phone "&date=" calldate } else search_url := "http://192.168.1.2/monitor.php?phone=" phone result := URLDownloadToVar(search_url) LV_Delete() loop, Parse, result, `n, `r { obj := StrSplit(A_LoopField,",") LV_Add("",obj*) } LV_ModifyCol() return listen: FocusedRowNumber := LV_GetNext(0, "F") if not FocusedRowNumber { MsgBox, 4144, 提示, 您未选择任何一条记录! Return } LV_GetText(uri, FocusedRowNumber, 8) if uri { url := "http://192.168.1.2" uri if RegExMatch(uri,".*/(.*)\.gsm$",m) { URLDownloadToFile, % url, % "c:\temp\" m1 ".gsm" RunWait, %A_ScriptDir%\sox\sox.exe c:\temp\%m1%.gsm c:\temp\%m1%.wav rate -v, , hide } else { RegExMatch(uri,".*/(.*)\.wav$",m) URLDownloadToFile, % url, % "c:\temp\" m1 ".wav" } wmp.Url := "c:\temp\" m1 ".wav" } else MsgBox, 64, 提示, 录音链接不存在! return GuiClose: ExitApp
# encoding: utf-8 #!/usr/bin/python import os import time import MySQLdb #设置目录 timearray = time.localtiime(time.time()) timedir = time.strftime("%Y/%m/%d/",timearray) monitordir = "/var/spool/asterisk/monitor/"+timedir vi conv.p # 打开数据库连接 db = MySQLdb.connect("localhost","root","Elastixdb","asteriskcdrdb" ) # 使用cursor()方法获取操作游标 cursor = db.cursor() sql = "SELECT recordingfile from cdr where recordingfile like '%.wav';" try: # 执行SQL语句 cursor.execute(sql) # 获取所有记录列表 results = cursor.fetchall() for row in results: recordingfile = row[0] if os.path.exists(monitordir+recordingfile): print "转换:%s" % monitordir+recordingfile os.system("sox "+monitordir+recordingfile+" "+monitordir+recordingfile.replace('wav','gsm')) os.remove(monitordir+recordingfile.replace('gsm','wav')) sql = "update cdr set recordingfile=replace(recordingfile,'wav','gsm') where recordingfile='"+recordingfile+"';" cursor.execute(sql) db.commit() else: print "找不到文件:%s" % monitordir+recordingfile except: print "Error: unable to fecth data" # 关闭数据库连接 db.close()
首先你得有个获取队列情况的接口。如果使用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
好了,看下效果:
50 queries in 1.461 seconds |