Freeswitch环境搭建及pstn网络呼出电话备忘

一,环境准备

1,系统:centos 6.7

2,下载freeswitch及安装前环境准备

  1. git clonehttps://freeswitch.org/stash/scm/fs/freeswitch.git  
  2. # Add the RPM repository   
  3. rpm -ivh http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm  
  4. ### OR a European repository  
  5. rpm -ivh http://mirror.cedia.org.ec/fedora-epel/6/x86_64/epel-release-6-8.noarch.rpm  
  6. # Install necessary components  
  7. yum install git gcc-c++ autoconf automake libtool wget python ncurses-devel zlib-devel libjpeg-devel openssl-devel e2fsprogs-devel sqlite-devel libcurl-devel pcre-devel speex-devel ldns-devel libedit-devel  

ps:执行时间比较久,建议使用screen来多个过程一起来执行

将clone下来的文件放在/usr/src目录下,进入目录执行以下命令

ps:注意官方推荐在centos6.7版本下使用v1.4,所以需要切换到v1.4分支下执行配置安装

  1. ./bootstrap.sh -j  

因为我是用JavaScript做开发的,所以需要打开mod_v8模块,下面会在pstn电话呼入和呼出中使用到v8模块,由于个人比较喜欢写js,所以在做电话呼出路由选择和呼入分机转接的时候就用js来写了。详见下文

  1. vi modules.conf  

找到 languages/mod_v8 取消注释

  1. ./configure -C  
  2. make && make install  

........等待安装完成

安装sounds

  1. make cd-sounds-install  
  2. make cd-moh-install  

创建freeswitch管理账号及软链接

  1. cd /usr/local  
  2. useradd --system --home-dir /usr/local/freeswitch -G daemon freeswitch  
  3. passwd -l freeswitch  
  4. chown -R freeswitch:daemon /usr/local/freeswitch/   
  5. chmod -R 770 /usr/local/freeswitch/  
  6. chmod -R 750 /usr/local/freeswitch/bin/*  
  7. mkdir /var/run/freeswitch  
  8. chown -R freeswitch:daemon  /var/run/freeswitch  
  9. ln -s /usr/local/freeswitch/bin/freeswitch /usr/bin/ # needed by /etc/init.d/freeswitch  

--------------------安装完成-------------------

控制台执行freeswitch命令启动freeswitch

--------------------wss配置------------------

1,编辑sip_profiles/internal.xml

打开 name="ws-binding" value=":5066"注释

打开 name="wss-binding" value=":7443" 注释

2.编辑 vars.xml 找到下面的设置并更改

  1. data="internal_ssl_enable=true"  
  2. data="external_ssl_enable=true"  

创建wss.pem

  1. wget http://files.freeswitch.org/downloads/ssl.ca-0.1.tar.gz  
  2. tar zxfv ssl.ca-0.1.tar.gz  
  3. cd ssl.ca-0.1/  
  4. perl -i -pe 's/md5/sha256/g' *.sh  
  5. perl -i -pe 's/1024/4096/g' *.sh  
  6. ./new-root-ca.sh  
  7. ./new-server-cert.sh self.bkw.org  
  8. ./sign-server-cert.sh self.bkw.org  
  9. cat self.bkw.org.crt self.bkw.org.key > /usr/local/freeswitch/certs/wss.pem  

ps:如果需要走wss协议,需要在入口机器上配置ssl【必须是认证过的证书才能有效,否则会出现握手失败的错误】

----------------------配置pstn呼出-----------------

网关配置在目录:/usr/local/freeswitch/conf/sip_profiles/external

增加一个pstn网关,创建一个xml 如:gw_pstn.xml 编辑

  1. <include>    
  2.   <gateway name="81046873">  
  3.   <param name="realm" value="192.168.1.11:5060"/>  
  4.   <param name="username" value="8798092"/>  
  5.   <param name="password" value="***pwd"/>  
  6.   <param name="register" value="true"/>  
  7.   </gateway>  
  8. </include>  

复制一个网关

  1. sed -e "s/81046865/81046873/" gw_pstn.xml > gw_pstn1.xml  

批量生成网关

  1. for i in `seq 810474 810402`;do sed -e "s/81046865/$i/" gw_pstn.xml > gw_$i.xml;done  

创建一个出局规则,就是说客户端拨打什么样的号码走哪个网关出去

  1. <include>  
  2.   <extension name="call out">  
  3.     <condition field="destination_number" expression="^out(\d+)$">  
  4.       <action application="bridge" data="sofia/gateway/810463/$1"/>  
  5.       <!--<action application="javascript" data="call_out.js $1"/>-->  
  6.     </condition>  
  7.   </extension>  
  8. </include>  
注:上面的配置是说,当sip客户端拨打outXXXXX号码的时候,freeswitch会转到810463网关将电话打出去

----------------------------使用MySQL数据库进行客户端账号注册--------------------------

组建:unixODBC

unixODBC是一个可以让你在Unix/Linux系统下使用ODBC来连接数据库的组件,就像Java中的mysql-connector-java-5.1.6-bin.jar一样,负责连接数据库的

安装:

  1. yum install unixODBC-devel.x86_64  
  2. yum install mysql-connector-odbc.x86_64  

安装后修改两个文件:/etc/odbc.ini,/etc/odbcinst.ini

/etc/odbc.ini 配置要连接的数据库信息

  1. [freeswitch]  
  2. Driver          = /usr/lib64/libmyodbc5.so  
  3. SERVER       = ip  
  4. PORT           = 3306  
  5. DATABASE    = database  
  6. USER            = user  
  7. PASSWORD   = password  

/etc/odbcinst.ini 修改mysq的部分,将驱动包指向正确,这要根据你本身的包安装路径配置
  1. [MySQL]  
  2. Description     = ODBC for MySQL  
  3. Driver          = /usr/lib64/libmyodbc5.so  
  4. Setup           = /usr/lib64/libodbcmyS.so  
  5. Driver64        = /usr/lib64/libmyodbc5.so  
  6. Setup64         = /usr/lib64/libodbcmyS.so  
  7. FileUsage       = 1  

检查是否安装成功,控制台输入命令 isql -v freeswitch 

Freeswitch环境搭建及pstn网络呼出电话备忘
出现以上截图说明安装成功

修改从lua脚本注册用户  /usr/local/freeswitch/conf/autoload_configs/lua.conf.xml

增加以下配置

  1. <param name="xml-handler-script" value="gen_dir_user_xml.lua" />   
  1. <param name="xml-handler-bindings" value="directory" />  
这两句是说用lua脚本来接管用户的注册,脚本的默认路径是  freeswitch/script/
  1. local req_domain = params:getHeader("domain")  
  2. local req_key    = params:getHeader("key")  
  3. local req_user   = params:getHeader("user")  
  4. local req_password   = params:getHeader("pass")  
  5.   
  6. freeswitch.consoleLog("NOTICE", "收到客户端注册请求,请求账号:"..req_user.."\n");  
  7. if req_password ~= nil then  
  8.     freeswitch.consoleLog("NOTICE", "收到客户端注册请求,请求密码:"..req_password.."\n");  
  9. end  
  10.   
  11. freeswitch.consoleLog("NOTICE", "开始连接数据库查找用户信息\r\n");  
  12.   
  13. local dbh = freeswitch.Dbh("freeswitch", "user", "pwd");  
  14. assert(dbh:connected());  
  15. dbh:query("select * from sysUsr where usrCode=" .. req_user, function(row)  
  16.     freeswitch.consoleLog("NOTICE", string.format("%s\n", row.usrCode))  
  17.     req_password = string.format("%s", row.usrCode)  
  18. end);  
  19.   
  20. dbh:release();  
  21. if req_password ~= nil then  
  22.     freeswitch.consoleLog("NOTICE", "info:用户注册信息匹配完成:" .. req_domain .. "--" .. req_key .. "--" .. req_user .. "--" .. req_password .. "\n");  
  23. else  
  24.     freeswitch.consoleLog("error", "用户信息验证失败\r\n");  
  25. end  
  26.   
  27. if req_domain ~= nil and req_key ~= nil and req_user ~= nil and req_password ~= nil then  
  28.     XML_STRING =  
  29.     [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>  
  30.     <document type="freeswitch/xml">  
  31.       <section name="directory">  
  32.         <domain name="]] .. req_domain .. [[">  
  33.           <params>  
  34.         <param name="dial-string"  
  35.         value="{presence_id=${dialed_user}@${dialed_domain}}${sofia_contact(${dialed_user}@${dialed_domain})}"/>  
  36.           </params>  
  37.           <groups>  
  38.         <group name="default">  
  39.           <users>  
  40.             <user id="]] .. req_user .. [[">  
  41. <params>  
  42.             <param name="password" value="]] .. req_password .. [["/>  
  43.             <param name="vm-password" value="]] .. req_password .. [["/>  
  44.               </params>  
  45.               <variables>       
  46. <variable name="toll_allow" value="domestic,international,local"/>  
  47.             <variable name="accountcode" value="]] .. req_user .. [["/>  
  48.             <variable name="user_context" value="default"/>  
  49.             <variable name="directory-visible" value="true"/>  
  50.             <variable name="directory-exten-visible" value="true"/>  
  51.             <variable name="limit_max" value="15"/>  
  52.             <variable name="effective_caller_id_name" value="Extension ]] .. req_user .. [["/>  
  53.             <variable name="effective_caller_id_number" value="]] .. req_user .. [["/>  
  54.             <variable name="outbound_caller_id_name" value="${outbound_caller_name}"/>  
  55.             <variable name="outbound_caller_id_number" value="${outbound_caller_id}"/>  
  56.             <variable name="callgroup" value="techsupport"/>  
  57.               </variables>  
  58.             </user>  
  59.           </users>  
  60.         </group>  
  61. </groups>  
  62.         </domain>  
  63.       </section>  
  64.     </document>]];  
  65.     freeswitch.consoleLog("NOTICE", "用户注册完成\n\n");  
  66. else  
  67.     XML_STRING =  
  68.     [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>  
  69.     <document type="freeswitch/xml">  
  70.       <section name="directory">  
  71.       </section>  
  72.     </document>]]  
  73.     freeswitch.consoleLog("ERROR", "用户注册失败\n\n");  
  74. end  

ps:在配置后启动出现了一个483 Failure! ODBC NOT AVAILABLE! 错误,这是因为在v1.4版本中没有默认加上odbc功能模块,需要进入/usr/src/freeswitch目录 执行以下命令
  1. ./configure --enable-core-odbc-support  
  1. make&&make install  
重启,fs_cli进入fs控制台,注册一个数据库中的用户

Freeswitch环境搭建及pstn网络呼出电话备忘

查看到以上信息,说明lua接管用户注册成功

如果需要去掉默认的用户注册,只需要修改 /usr/local/freeswitch/conf/directory/default.xml文件,删掉以下配置即可

  1. <group name="default">  
  2.         <users>  
  3.           <X-PRE-PROCESS cmd="include" data="default/*.xml"/>  
  4.         </users>  
  5. </group>  
至此,用户使用我们自己的数据库中的用户来进行注册,并且能通过一个pstn网关拨打电话出去了,但是由于网络电话供应商给我们透露了一个消息是一个gatway同一时间只能有一路通话,这样很明显是不行的,我们需要将业务员和想对应的gatway绑定起来,为了能够使业务员在拨打电话的时候可以动态的切换网关【比如说一个业务员在拨打电话的时候使用网关1,但是发现网关1正在被另一个业务员占用,就自动切换到另一个网关来播出电话】我是使用js来实现的,详细的步骤如下

----------------------------js处理电话呼出------------------------

刚才我们说了在呼出电话的时候配置了一个呼出规则,配置文件在  /usr/local/freeswitch/conf/dialplan/default/call_out.xml

  1. <pre name="code" class="html"><include>  
  2.   <extension name="call out">  
  3.     <condition field="destination_number" expression="^out(\d+)$">  
  4.       <span style="font-family: Arial, Helvetica, sans-serif;"><!--</span><span style="font-family: Arial, Helvetica, sans-serif;"><action application="bridge" data="sofia/gateway/810463/$1"/></span><span style="font-family: Arial, Helvetica, sans-serif;">--></span>  
  5.       <action application="javascript" data="call_out.js $1"/>  
  6.     </condition>  
  7.   </extension>  
  8. </include>  

注: 要使用js脚本,需要在 /usr/local/freeswitch/conf/autoload_configs/modules.conf.xml 中将<loadmodule="mod_v8"/>打开 

将 application="bridge" 注释掉,将 application="javascript" 打开,使用call_out.js来处理电话的呼出。注$1是参数 call_out.js 如下:
[javascript] view plain copy Freeswitch环境搭建及pstn网络呼出电话备忘Freeswitch环境搭建及pstn网络呼出电话备忘
  1. use("ODBC");  
  2. function callOut() {  
  3.     var gatway = queryGatway(session.caller_id_number);  
  4.     if (gatway != null) {  
  5.         playFile(session, "/usr/local/freeswitch/sounds/pengshu/zhuanjie.wav");  
  6.         var outsession = new Session("sofia/gateway/" + gatway + "/" + argv[0]);  
  7.   
  8.         if (!outsession.ready()) {  
  9.             outsession = pollGatWay(argv[0]);  
  10.         }  
  11.   
  12.         if (outsession != null&&outsession.ready()) {  
  13.             bridge(session, outsession, logAndStopBridge);  
  14.             session.hangup();  
  15.             outsession.hangup();  
  16.         } else {  
  17.             session.hangup();  
  18.             outsession.hangup();  
  19.         }  
  20.     } else {  
  21.         playFile(session, "/usr/local/freeswitch/sounds/pengshu/gatway-error.wav");  
  22.         console_log("获取网关失败");  
  23.         session.hangup();  
  24.     }  
  25. }  
  26.   
  27. function logAndStopBridge() {  
  28.     session.hangup();  
  29.     session.close();  
  30.     return false;  
  31. }  
  32.   
  33. function playFile(session, file, callBack, callBackArgs) {  
  34.     session.streamFile(file, callBack, callBackArgs);  
  35. }  
  36.   
  37. function queryGatway(account) {  
  38.     console_log("开始查询" + account + "网关");  
  39.     var db = new ODBC("freeswitch_system""account""pwd");  
  40.     db.connect();  
  41.     var sql = "select * from sip_gatway where account=" + account;  
  42.     db.exec(sql);  
  43.     var gatway = null;  
  44.     while (db.nextRow()) {  
  45.         var row = db.getData();  
  46.         console_log("查询到数据:" + row['account'] + "-->" + row['gatway'])  
  47.         gatway = row['gatway'];  
  48.     }  
  49.     return gatway;  
  50. }  
  51.   
  52. function pollGatWay(phone) {  
  53.     var pollSession = null;  
  54.     var db = new ODBC("freeswitch_system""account""pwd");  
  55.     db.connect();  
  56.     console_log("========开始轮询手机========");  
  57.     var sql = "select * from sip_gatway";  
  58.     db.exec(sql);  
  59.     while (db.nextRow()) {  
  60.         var row = db.getData();  
  61.         pollSession = new Session("sofia/gateway/" + row["gatway"] + "/" + phone);  
  62.         if (pollSession.ready()) {  
  63.             return pollSession;  
  64.         }  
  65.     }  
  66.     return null;  
  67. }  
  68.   
  69. callOut();  

-------------------------js处理电话呼入----------------------

我们想电话呼入的时候可以自动的选择来电客户属于哪个业务员,如果业务员坐席没有接听,自动转接到其他业务员坐席,所有业务员都不在坐席的话轮询转接到业务员的手机上,直至有接听未知,若全部都没有接听,则挂断电话

首先,创建呼入规则 /usr/local/freeswitch/conf/dialplan/public/call_in.xml

  1. <include>  
  2.   <extension name="public_did">  
  3.     <!--<condition field="destination_number" expression="^(81046092)$">-->  
  4.     <condition field="destination_number"  expression="^(\d+)$">  
  5.       <!--<action application="transfer" data="10002 XML default"/>-->  
  6.       <action application="record_session" data="$${base_dir}/recordings/archive/callIn/callIn_${strftime(%Y-%m-%d-%H-%M-%S)}_${caller_id_number}_$1.wav"/>  
  7.       <action application="javascript" data="call_in.js"/>  
  8.     </condition>  
  9.   </extension>  
  10. </include>  

创建 call_in.js 【默认目录在freeswitch/scripts】
[javascript] view plain copy Freeswitch环境搭建及pstn网络呼出电话备忘Freeswitch环境搭建及pstn网络呼出电话备忘
  1. use("ODBC");  
  2. function answerCallIn() {  
  3.     session.answer();  
  4.     playFile("zhuanjie.wav");  
  5.     while (session.ready()) {  
  6.         console_log("=============开始同振==============");  
  7.         var num = queryPhone();  
  8.         var firstCallStr = "[leg_timeout=30,origination_caller_id_name='" + session.caller_id_name + "',origination_caller_id_number='" + session.caller_id_number + "']user/" + num + "|";  
  9.         var callStr = firstCallStr + mosaicCallNum(session, num);  
  10.         //同振  
  11.         //callStr.replace(/|/g, ",");  
  12.         bridge(session, new Session(callStr), logAndStopBridge);  
  13.     }  
  14. }  
  15.   
  16. function logAndStopBridge() {  
  17.     session.hangup();  
  18.     mySession.hangup();  
  19.     return false;  
  20. }  
  21.   
  22. function queryPhone() {  
  23.     var result = fetchUrl("http://19.02.4.3/getdata");  
  24.     if (result.success) {  
  25.         console_log("获取http请求数据成功");  
  26.     }  
  27.     console_log("号码查询成功");  
  28.     return "[leg_timeout=30,origination_caller_id_name='" + session.caller_id_name + "',origination_caller_id_number='" + session.caller_id_number + "']user/13524544334" + "|";  
  29. }  
  30.   
  31. function playFile(fileName, callBack, callBackArgs) {  
  32.     session.streamFile("/usr/local/freeswitch/sounds/pengshu/" + fileName, callBack, callBackArgs);  
  33. }  
  34.   
  35. function mosaicCallNum(session, num) {  
  36.     var db = new ODBC("freeswitch_system""root""ps@mysql");  
  37.     var accountStr = "";  
  38.     var phoneStr = "";  
  39.     db.connect();  
  40.     var sql = "select * from sip_gatway";  
  41.     db.exec(sql);  
  42.     while (db.nextRow()) {  
  43.         var row = db.getData();  
  44.         if (row["account"] != num && row["account"] && row["phone"]) {  
  45.             accountStr += "[leg_timeout=30,origination_caller_id_name='" + session.caller_id_name + "',origination_caller_id_number='" + session.caller_id_number + "']user/" + row["account"] + "|"  
  46.             phoneStr += "[leg_timeout=30]sofia/gateway/" + row["gatway"] + "/" + row["phone"] + "|"  
  47.         }  
  48.     }  
  49.     var callStr = accountStr + phoneStr;  
  50.     return callStr.substring(0, callStr.length - 1);  
  51. }  
  52.   
  53. answerCallIn(); 
上一篇:Linux系统下sendmail发送邮件失败的问题


下一篇:安全专家发现 Uber 可免费打车的漏洞,但别高兴太早