一,环境准备
1,系统:centos 6.7
2,下载freeswitch及安装前环境准备
- git clonehttps://freeswitch.org/stash/scm/fs/freeswitch.git
- # Add the RPM repository
- rpm -ivh http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm
- ### OR a European repository
- rpm -ivh http://mirror.cedia.org.ec/fedora-epel/6/x86_64/epel-release-6-8.noarch.rpm
- # Install necessary components
- 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分支下执行配置安装
- ./bootstrap.sh -j
因为我是用JavaScript做开发的,所以需要打开mod_v8模块,下面会在pstn电话呼入和呼出中使用到v8模块,由于个人比较喜欢写js,所以在做电话呼出路由选择和呼入分机转接的时候就用js来写了。详见下文
- vi modules.conf
找到 languages/mod_v8 取消注释
- ./configure -C
- make && make install
........等待安装完成
安装sounds
- make cd-sounds-install
- make cd-moh-install
创建freeswitch管理账号及软链接
- cd /usr/local
- useradd --system --home-dir /usr/local/freeswitch -G daemon freeswitch
- passwd -l freeswitch
- chown -R freeswitch:daemon /usr/local/freeswitch/
- chmod -R 770 /usr/local/freeswitch/
- chmod -R 750 /usr/local/freeswitch/bin/*
- mkdir /var/run/freeswitch
- chown -R freeswitch:daemon /var/run/freeswitch
- 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 找到下面的设置并更改
- data="internal_ssl_enable=true"
- data="external_ssl_enable=true"
创建wss.pem
- wget http://files.freeswitch.org/downloads/ssl.ca-0.1.tar.gz
- tar zxfv ssl.ca-0.1.tar.gz
- cd ssl.ca-0.1/
- perl -i -pe 's/md5/sha256/g' *.sh
- perl -i -pe 's/1024/4096/g' *.sh
- ./new-root-ca.sh
- ./new-server-cert.sh self.bkw.org
- ./sign-server-cert.sh self.bkw.org
- 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 编辑
- <include>
- <gateway name="81046873">
- <param name="realm" value="192.168.1.11:5060"/>
- <param name="username" value="8798092"/>
- <param name="password" value="***pwd"/>
- <param name="register" value="true"/>
- </gateway>
- </include>
复制一个网关
- sed -e "s/81046865/81046873/" gw_pstn.xml > gw_pstn1.xml
批量生成网关
- for i in `seq 810474 810402`;do sed -e "s/81046865/$i/" gw_pstn.xml > gw_$i.xml;done
创建一个出局规则,就是说客户端拨打什么样的号码走哪个网关出去
- <include>
- <extension name="call out">
- <condition field="destination_number" expression="^out(\d+)$">
- <action application="bridge" data="sofia/gateway/810463/$1"/>
- <!--<action application="javascript" data="call_out.js $1"/>-->
- </condition>
- </extension>
- </include>
----------------------------使用MySQL数据库进行客户端账号注册--------------------------
组建:unixODBC
unixODBC是一个可以让你在Unix/Linux系统下使用ODBC来连接数据库的组件,就像Java中的mysql-connector-java-5.1.6-bin.jar一样,负责连接数据库的
安装:
- yum install unixODBC-devel.x86_64
- yum install mysql-connector-odbc.x86_64
安装后修改两个文件:/etc/odbc.ini,/etc/odbcinst.ini
/etc/odbc.ini 配置要连接的数据库信息
- [freeswitch]
- Driver = /usr/lib64/libmyodbc5.so
- SERVER = ip
- PORT = 3306
- DATABASE = database
- USER = user
- PASSWORD = password
/etc/odbcinst.ini 修改mysq的部分,将驱动包指向正确,这要根据你本身的包安装路径配置
- [MySQL]
- Description = ODBC for MySQL
- Driver = /usr/lib64/libmyodbc5.so
- Setup = /usr/lib64/libodbcmyS.so
- Driver64 = /usr/lib64/libmyodbc5.so
- Setup64 = /usr/lib64/libodbcmyS.so
- FileUsage = 1
检查是否安装成功,控制台输入命令 isql -v freeswitch
出现以上截图说明安装成功
修改从lua脚本注册用户 /usr/local/freeswitch/conf/autoload_configs/lua.conf.xml
增加以下配置
- <param name="xml-handler-script" value="gen_dir_user_xml.lua" />
- <param name="xml-handler-bindings" value="directory" />
- local req_domain = params:getHeader("domain")
- local req_key = params:getHeader("key")
- local req_user = params:getHeader("user")
- local req_password = params:getHeader("pass")
- freeswitch.consoleLog("NOTICE", "收到客户端注册请求,请求账号:"..req_user.."\n");
- if req_password ~= nil then
- freeswitch.consoleLog("NOTICE", "收到客户端注册请求,请求密码:"..req_password.."\n");
- end
- freeswitch.consoleLog("NOTICE", "开始连接数据库查找用户信息\r\n");
- local dbh = freeswitch.Dbh("freeswitch", "user", "pwd");
- assert(dbh:connected());
- dbh:query("select * from sysUsr where usrCode=" .. req_user, function(row)
- freeswitch.consoleLog("NOTICE", string.format("%s\n", row.usrCode))
- req_password = string.format("%s", row.usrCode)
- end);
- dbh:release();
- if req_password ~= nil then
- freeswitch.consoleLog("NOTICE", "info:用户注册信息匹配完成:" .. req_domain .. "--" .. req_key .. "--" .. req_user .. "--" .. req_password .. "\n");
- else
- freeswitch.consoleLog("error", "用户信息验证失败\r\n");
- end
- if req_domain ~= nil and req_key ~= nil and req_user ~= nil and req_password ~= nil then
- XML_STRING =
- [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
- <document type="freeswitch/xml">
- <section name="directory">
- <domain name="]] .. req_domain .. [[">
- <params>
- <param name="dial-string"
- value="{presence_id=${dialed_user}@${dialed_domain}}${sofia_contact(${dialed_user}@${dialed_domain})}"/>
- </params>
- <groups>
- <group name="default">
- <users>
- <user id="]] .. req_user .. [[">
- <params>
- <param name="password" value="]] .. req_password .. [["/>
- <param name="vm-password" value="]] .. req_password .. [["/>
- </params>
- <variables>
- <variable name="toll_allow" value="domestic,international,local"/>
- <variable name="accountcode" value="]] .. req_user .. [["/>
- <variable name="user_context" value="default"/>
- <variable name="directory-visible" value="true"/>
- <variable name="directory-exten-visible" value="true"/>
- <variable name="limit_max" value="15"/>
- <variable name="effective_caller_id_name" value="Extension ]] .. req_user .. [["/>
- <variable name="effective_caller_id_number" value="]] .. req_user .. [["/>
- <variable name="outbound_caller_id_name" value="${outbound_caller_name}"/>
- <variable name="outbound_caller_id_number" value="${outbound_caller_id}"/>
- <variable name="callgroup" value="techsupport"/>
- </variables>
- </user>
- </users>
- </group>
- </groups>
- </domain>
- </section>
- </document>]];
- freeswitch.consoleLog("NOTICE", "用户注册完成\n\n");
- else
- XML_STRING =
- [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
- <document type="freeswitch/xml">
- <section name="directory">
- </section>
- </document>]]
- freeswitch.consoleLog("ERROR", "用户注册失败\n\n");
- end
ps:在配置后启动出现了一个483 Failure! ODBC NOT AVAILABLE! 错误,这是因为在v1.4版本中没有默认加上odbc功能模块,需要进入/usr/src/freeswitch目录 执行以下命令
- ./configure --enable-core-odbc-support
- make&&make install
查看到以上信息,说明lua接管用户注册成功
如果需要去掉默认的用户注册,只需要修改 /usr/local/freeswitch/conf/directory/default.xml文件,删掉以下配置即可
- <group name="default">
- <users>
- <X-PRE-PROCESS cmd="include" data="default/*.xml"/>
- </users>
- </group>
----------------------------js处理电话呼出------------------------
刚才我们说了在呼出电话的时候配置了一个呼出规则,配置文件在 /usr/local/freeswitch/conf/dialplan/default/call_out.xml
- <pre name="code" class="html"><include>
- <extension name="call out">
- <condition field="destination_number" expression="^out(\d+)$">
- <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>
- <action application="javascript" data="call_out.js $1"/>
- </condition>
- </extension>
- </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 如下:- use("ODBC");
- function callOut() {
- var gatway = queryGatway(session.caller_id_number);
- if (gatway != null) {
- playFile(session, "/usr/local/freeswitch/sounds/pengshu/zhuanjie.wav");
- var outsession = new Session("sofia/gateway/" + gatway + "/" + argv[0]);
- if (!outsession.ready()) {
- outsession = pollGatWay(argv[0]);
- }
- if (outsession != null&&outsession.ready()) {
- bridge(session, outsession, logAndStopBridge);
- session.hangup();
- outsession.hangup();
- } else {
- session.hangup();
- outsession.hangup();
- }
- } else {
- playFile(session, "/usr/local/freeswitch/sounds/pengshu/gatway-error.wav");
- console_log("获取网关失败");
- session.hangup();
- }
- }
- function logAndStopBridge() {
- session.hangup();
- session.close();
- return false;
- }
- function playFile(session, file, callBack, callBackArgs) {
- session.streamFile(file, callBack, callBackArgs);
- }
- function queryGatway(account) {
- console_log("开始查询" + account + "网关");
- var db = new ODBC("freeswitch_system", "account", "pwd");
- db.connect();
- var sql = "select * from sip_gatway where account=" + account;
- db.exec(sql);
- var gatway = null;
- while (db.nextRow()) {
- var row = db.getData();
- console_log("查询到数据:" + row['account'] + "-->" + row['gatway'])
- gatway = row['gatway'];
- }
- return gatway;
- }
- function pollGatWay(phone) {
- var pollSession = null;
- var db = new ODBC("freeswitch_system", "account", "pwd");
- db.connect();
- console_log("========开始轮询手机========");
- var sql = "select * from sip_gatway";
- db.exec(sql);
- while (db.nextRow()) {
- var row = db.getData();
- pollSession = new Session("sofia/gateway/" + row["gatway"] + "/" + phone);
- if (pollSession.ready()) {
- return pollSession;
- }
- }
- return null;
- }
- callOut();
-------------------------js处理电话呼入----------------------
我们想电话呼入的时候可以自动的选择来电客户属于哪个业务员,如果业务员坐席没有接听,自动转接到其他业务员坐席,所有业务员都不在坐席的话轮询转接到业务员的手机上,直至有接听未知,若全部都没有接听,则挂断电话
首先,创建呼入规则 /usr/local/freeswitch/conf/dialplan/public/call_in.xml
- <include>
- <extension name="public_did">
- <!--<condition field="destination_number" expression="^(81046092)$">-->
- <condition field="destination_number" expression="^(\d+)$">
- <!--<action application="transfer" data="10002 XML default"/>-->
- <action application="record_session" data="$${base_dir}/recordings/archive/callIn/callIn_${strftime(%Y-%m-%d-%H-%M-%S)}_${caller_id_number}_$1.wav"/>
- <action application="javascript" data="call_in.js"/>
- </condition>
- </extension>
- </include>
创建 call_in.js 【默认目录在freeswitch/scripts】
- use("ODBC");
- function answerCallIn() {
- session.answer();
- playFile("zhuanjie.wav");
- while (session.ready()) {
- console_log("=============开始同振==============");
- var num = queryPhone();
- var firstCallStr = "[leg_timeout=30,origination_caller_id_name='" + session.caller_id_name + "',origination_caller_id_number='" + session.caller_id_number + "']user/" + num + "|";
- var callStr = firstCallStr + mosaicCallNum(session, num);
- //同振
- //callStr.replace(/|/g, ",");
- bridge(session, new Session(callStr), logAndStopBridge);
- }
- }
- function logAndStopBridge() {
- session.hangup();
- mySession.hangup();
- return false;
- }
- function queryPhone() {
- var result = fetchUrl("http://19.02.4.3/getdata");
- if (result.success) {
- console_log("获取http请求数据成功");
- }
- console_log("号码查询成功");
- return "[leg_timeout=30,origination_caller_id_name='" + session.caller_id_name + "',origination_caller_id_number='" + session.caller_id_number + "']user/13524544334" + "|";
- }
- function playFile(fileName, callBack, callBackArgs) {
- session.streamFile("/usr/local/freeswitch/sounds/pengshu/" + fileName, callBack, callBackArgs);
- }
- function mosaicCallNum(session, num) {
- var db = new ODBC("freeswitch_system", "root", "ps@mysql");
- var accountStr = "";
- var phoneStr = "";
- db.connect();
- var sql = "select * from sip_gatway";
- db.exec(sql);
- while (db.nextRow()) {
- var row = db.getData();
- if (row["account"] != num && row["account"] && row["phone"]) {
- accountStr += "[leg_timeout=30,origination_caller_id_name='" + session.caller_id_name + "',origination_caller_id_number='" + session.caller_id_number + "']user/" + row["account"] + "|"
- phoneStr += "[leg_timeout=30]sofia/gateway/" + row["gatway"] + "/" + row["phone"] + "|"
- }
- }
- var callStr = accountStr + phoneStr;
- return callStr.substring(0, callStr.length - 1);
- }
- answerCallIn();