daiqx 发表于 2017-7-25 10:38:36

freeswitch 折腾了一个星期 lua 实现的 act 程序,抛砖引玉

---上次发帖说用 sip做一个呼叫系统,折腾得好痛苦,如果有意的坛友可以参考一下,节省点时间和表情。肯定可以运行。如果有其中的高手,请指教一下。代码比较精糙。。哈

dbh=freeswitch.Dbh("odbc://fwsql");
laststat="";   --最后状态 1.hasline 2.notline 有否空内线 2.5 linenot有空线到最后被用3.linebusy4.lineready 有空线是否能接入
badinline="";
function waittoinc(tmpsession)
local waitTimes = 10;
while tmpsession:ready() == false and waitTimes > 0 do
   waitTimes = waitTimes - 1;
end
return tmpsession:ready()

end

function playwavs(tmpsession,wavfiles)

tmpsession:sleep(50);
    if tmpsession:ready() then
   freeswitch.consoleLog("err", "\n starting play wav");
   tmpsession:streamFile(wavfiles);
   freeswitch.consoleLog("err", "\n play wav end");
   end


end

function getoneinline(notline)   --获得一条空闲内线在 inlineinfo 表内
local reinline="";
   
dbh:query("select top 1 inline from inlineinfo where busy=0 and inline<>'"..notline.."' order by asc,calltime asc"
,function(row) reinline=row.inlineend )
if reinline==nil or reinline=="" then
    laststat="notline";
    return "";
end
    laststat="hasline";
    badinline=reinline;
return reinline;
end

function zoneinline(notline,calldh,callid) --获内线后,设置为忙

local reinline;
local revv;
local ktnow=os.date("%Y-%m-%d %H:%M:%S", os.time());
reinline=getoneinline(notline);
if reinline==nil then
    reinline="";
    return reinline;
end

dbh:query("update inlineinfo set busy=1,outline='"..calldh.."',callid='"..callid.."',calltime='"..ktnow.."' where inline='"..reinline.."' and busy=0");
revv=dbh:affected_rows();
badinline=reinline;
if revv>0 then
   return reinline;
else
   laststat="linenot";
    return "";
end
end

function callinline(notline,calldh,callid)
local reinline=zoneinline(notline,calldh,callid);

   if reinline==nil or reinline=="" then
      return "",nil;
   end
badinline=reinline;
local tmpnum=reinline;
local tmpsession= freeswitch.Session("user/"..tmpnum);

   if waittoinc(tmpsession) then
      laststat="lineready";
      return tmpnum,tmpsession;
    else

      dbh:query("update inlineinfo set busy=0,=(select isnull(count(*),0) from inlineinfo where busy=0)+1 where inline='"..reinline.."'")
   laststat="linebusy";
   return "",nil;
   end

end


local session1 = session;

local cid=session1:getVariable("caller_id_number");
local dest_num=session1:getVariable("destination_number");
local kt=os.date("%Y-%m-%d %H:%M:%S", os.time());
local ktms=os.time();

dbh:query("insert into new通话(电话,kt,zt)values('"..cid.."','"..kt.."','"..dest_num.."')");

--playwavs(session1,"d:\\ffg\\wel.wav");
--如果座席所在的那条通道还没准备好,则进行等待
--session1:sleep(1800);
--if session1:ready() then
-- session1:answer();
-- end


local session2;
local inline;
local timess=0;
inline,session2=callinline("",dest_num,cid);--toonecall();

if inline=="" then
   
   session1:sleep(50);
    if (laststat=='linenot' or laststat=='linebusy') and badinline~='' and session1:ready() then
      freeswitch.consoleLog("err", "\n channels2 is not ready, try next. laststat:"..laststat);
      
      inline,session2=callinline(badinline,dest_num,cid);
    end
   
end

if inline=="" then
   session1:sleep(50);
   
    if (laststat=='linenot' or laststat=='linebusy') and badinline~='' and session1:ready() then
      freeswitch.consoleLog("err", "\n channels2 is not ready, try next. laststat:"..laststat);
      
      inline,session2=callinline(badinline,dest_num,cid);
    end
   
end

ifinline=="" then
      
   -- 接通客户与座席
    -- session2:setVariable("destination_number",cid);
      
    freeswitch.consoleLog("err", "\n channels2 is not ready, please check it. laststat:"..laststat);
   timess=-1;
   playwavs(session1,"d:\\ffg\\busy.wav");
else
    session1:sleep(50);
   
    if session1:ready() then
   
      freeswitch.bridge(session1, session2);
   
    else
      timess=-2;
    end
end



local et=os.date("%Y-%m-%d %H:%M:%S", os.time())
local etms=os.time();

if timess==0 then
timess=etms-ktms;

end
dbh:query("update inlineinfo set busy=0,=(select isnull(count(*),0) from inlineinfo where busy=0)+1 where inline='"..inline.."'")
dbh:query("insert into 通话(电话,kt,zt,st)values('"..cid.."','"..kt.."','"..et.."',"..timess..")")
dbh:release()


----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
最后说明,用的是 ms sql上面是代码,下面表结构
USE
GO

/****** Object:Table .    Script Date: 07/25/2017 10:32:40 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE .(
        IDENTITY(1,1) NOT NULL,
        (40) NOT NULL,
        (40) NULL,
        (20) NULL,
        NOT NULL,
        NULL,
        (40) NOT NULL,
CONSTRAINT PRIMARY KEY CLUSTERED
(
        ASC
)WITH (PAD_INDEX= OFF, STATISTICS_NORECOMPUTE= OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS= ON, ALLOW_PAGE_LOCKS= ON) ON
) ON

GO

SET ANSI_PADDING OFF
GO


USE
GO

/****** Object:Table .[通话]    Script Date: 07/25/2017 10:33:31 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE .[通话](
        IDENTITY(1,1) NOT NULL,
        [电话] (20) NULL,
        (20) NULL,
        (20) NULL,
        NULL,
CONSTRAINT PRIMARY KEY CLUSTERED
(
        ASC
)WITH (PAD_INDEX= OFF, STATISTICS_NORECOMPUTE= OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS= ON, ALLOW_PAGE_LOCKS= ON) ON
) ON

GO

SET ANSI_PADDING OFF
GO

USE
GO

/****** Object:Table .    Script Date: 07/25/2017 10:33:23 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE .(
        IDENTITY(1,1) NOT NULL,
        [电话] (20) NULL,
        (20) NULL,
        (20) NULL,
CONSTRAINT PRIMARY KEY CLUSTERED
(
        ASC
)WITH (PAD_INDEX= OFF, STATISTICS_NORECOMPUTE= OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS= ON, ALLOW_PAGE_LOCKS= ON) ON
) ON

GO

SET ANSI_PADDING OFF
GO









Jmhh247 发表于 2017-7-25 11:30:23

楼主,论坛有插入代码功能,建议以后发帖使用代码模式,阅读体验会好很多

static void do_xxx(void)
{

}

daiqx 发表于 2017-7-25 11:34:58

Jmhh247 发表于 2017-7-25 11:30
楼主,论坛有插入代码功能,建议以后发帖使用代码模式,阅读体验会好很多

...

哈,是的是的,代码量不多将就一下

huangqi412 发表于 2017-7-26 15:47:51

代码比较精糙。。    =精辟+粗糙?哈哈

Excellence 发表于 2017-7-26 16:08:33

sql语言,lua部分呢?

daiqx 发表于 2017-7-26 16:15:15

Excellence 发表于 2017-7-26 16:08
sql语言,lua部分呢?

上面 lua 下面 sql 表结构

Excellence 发表于 2017-7-26 17:28:34

daiqx 发表于 2017-7-26 16:15
上面 lua 下面 sql 表结构

谢谢分享。。。。

snowy 发表于 2017-7-26 23:38:21

我最近也在玩这个,从移动要了个IMS固话号码,现在gateway和inbound都配置通了,可以打进来了,就是outbound不知道怎么配置,总打不出去,楼主可吧指导一下么?中国的拨号规则怎么写?Outbound应该怎么配置?

daiqx 发表于 2017-7-28 00:37:04

拔号计划会设置么?会的话剩下的就是IMS的网关格式问题了。

daiqx 发表于 2017-7-28 01:25:28

snowy 发表于 2017-7-26 23:38
我最近也在玩这个,从移动要了个IMS固话号码,现在gateway和inbound都配置通了,可以打进来了,就是outboun ...

就是使用网关的呼出字串格式,这个对的话就写到拔号计划,不过这个比较简单,所以有可能在getway上,有可能不对。还有你留意一下控制台的输出,应该有提示

snowy 发表于 2017-7-28 09:44:20

daiqx 发表于 2017-7-28 01:25
就是使用网关的呼出字串格式,这个对的话就写到拔号计划,不过这个比较简单,所以有可能在getway上,有可 ...

谢谢 楼主,后来通过抓包分析,问题确实出在网关设置上,Caller ID In From 要设置为false,否则呼出时会使用本地号码和域名invite,IMS是不接受这样的不合法请求。

daiqx 发表于 2017-7-28 20:26:14

snowy 发表于 2017-7-28 09:44
谢谢 楼主,后来通过抓包分析,问题确实出在网关设置上,Caller ID In From 要设置为false,否则呼出时会 ...

人生在于折腾,你可以将ims的网关\拔号计划发出来,有人也折腾这个时也可以参考一下。我比较喜欢使用脚本去代替拔号计划的逻辑。编程多于配置。

daiqx 发表于 2017-7-28 20:37:48

snowy 发表于 2017-7-28 09:44
谢谢 楼主,后来通过抓包分析,问题确实出在网关设置上,Caller ID In From 要设置为false,否则呼出时会 ...

另外经过一段时间的折腾,fs灵活地用必须用脚本。比如lua。然后用脚本或更熟练地配置文件还是不够的,因为它们都是阻塞执行的不能异步。比如上面的代码其实还有一点问题,比如要整一个欢迎语,一边发送回铃音边桥接就不行了。最后就要折腾esl了。

snowy 发表于 2017-7-28 21:22:06

本帖最后由 snowy 于 2017-7-28 21:24 编辑

daiqx 发表于 2017-7-28 20:26
人生在于折腾,你可以将ims的网关\拔号计划发出来,有人也折腾这个时也可以参考一下。我比较喜欢使用脚本 ...

响应号召,对接移动IMS的主要参数,请根据所在地移动提供的信息修改。拨号计划等玩转了,弄个比较完善的发上来。
<include>
<gateway name="+86***********">
<!--/// account username *required* ///-->
<param name="username" value="+86***********"/>
<!--/// auth username *required* ///-->
<param name="auth-username" value="+86***********@ims.js.chinamobile.com"/>
<!--/// auth realm: *optional* same as gateway name, if blank ///-->
<param name="realm" value="ims.js.chinamobile.com"/>
<!--/// username to use in from: *optional* same asusername, if blank ///-->
<!--<param name="from-user" value="cluecon"/>-->
<!--/// domain to use in from: *optional* same asrealm, if blank ///-->
<!--<param name="from-domain" value="asterlink.com"/>-->
<!--/// account password *required* ///-->
<param name="password" value="**********"/>
<!--/// extension for inbound calls: *optional* same as username, if blank ///-->
<!--<param name="extension" value="cluecon"/>-->
<!--/// proxy host: *optional* same as realm, if blank ///-->
<!--<param name="proxy" value="asterlink.com"/>-->
<!--/// send register to this proxy: *optional* same as proxy, if blank ///-->
<param name="register-proxy" value="sbc.chinamobile.com"/>
<!--/// send outbound data to this proxy: *optional* same as proxy, if blank ///-->
<param name="outbound-proxy" value="sbc.chinamobile.com"/>
<!--/// expire in seconds: *optional* 3600, if blank ///-->
<param name="expire-seconds" value="1800"/>
<!--/// do not register ///-->
<param name="register" value="true"/>
<!-- which transport to use for register -->
<param name="register-transport" value="udp"/>
<!--How many seconds before a retry when a failure or timeout occurs -->
<param name="retry-seconds" value="30"/>
<!--Use the callerid of an inbound call in the from field on outbound calls via this gateway -->
<param name="caller-id-in-from" value="false"/>
<!--extra sip params to send in the contact-->
<!--<param name="contact-params" value="tport=tcp"/>-->
<!--send an options ping every x seconds, failure will unregister and/or mark it down-->
<param name="ping" value="25"/>
<!--</gateway>-->
<!--rfc5626 : Abilitazione rfc5626 ///-->
<!--<param name="rfc-5626" value="true"/>-->
<!--rfc5626 : extra sip params to send in the contact-->
<!--<param name="reg-id" value="1"/>-->
</include>

snowy 发表于 2017-7-28 21:27:56

本帖最后由 snowy 于 2017-7-28 21:30 编辑

daiqx 发表于 2017-7-28 20:37
另外经过一段时间的折腾,fs灵活地用必须用脚本。比如lua。然后用脚本或更熟练地配置文件还是不够的,因 ...

后来我发现了FusionPBX(底层是FreeSwitch,安装这个最好有爱国路由器带着,不然非常慢),这个有比较友好的图形化界面(很好看),而且全部功能都是免费开放的,不像FreePBX很多要付费的组件。您可以试试。

daiqx 发表于 2017-7-28 21:52:37

snowy 发表于 2017-7-28 21:27
后来我发现了FusionPBX(底层是FreeSwitch,安装这个最好有爱国路由器带着,不然非常慢),这个有比较友 ...

谢谢分享,如果先知道fusionpbx的话,可能会减少很多时间,我先在ubunut安装fs,很多周折没有成功,就装win版的,安装非常容易,后来发现没有mod_lua模块,找编互联网也没有现成的,再下载源码,又发现只能vs2015才能编译,最后编译成功。linux版的有mod_luawin版的没有,真气死。现在由纯手工配置入门也好应该会扎实很多,哈哈

snowy 发表于 2017-7-29 13:47:34

daiqx 发表于 2017-7-28 21:52
谢谢分享,如果先知道fusionpbx的话,可能会减少很多时间,我先在ubunut安装fs,很多周折没有成功,就装w ...

一般来说LINUX下的开源软件在WIN下编译比LINUX下麻烦多了。

FusionPBX的出局路由:


Tag        Type        Data        Break        Inline        Group        Order       
condition        destination_number        ^(\+?\d{5,15})$                        0        5

长度在5位以上的,直接送到网关出局,4位以内保留给分机,软交换的好处是不像传统交换机那样要加拨数字。

^(\+?\d{5,15})$这个REGEX基本搞定了01088888888075588888888 +8688888888888 10000 10086之类的情况了,如果要拨110之类的,可以加拨区号来解决或者再加条3位数^(\d{3})$ 的优先路由。

daiqx 发表于 2017-7-29 16:27:47

封装后拔号规则简洁了,不过本来原装的配置也不复杂

daiqx 发表于 2017-8-3 23:20:33

snowy 发表于 2017-7-29 13:47
一般来说LINUX下的开源软件在WIN下编译比LINUX下麻烦多了。

FusionPBX的出局路由:


你好!现在你的freeswitch进展如何了?现在我已经确认用ESL也搞不定自定义 Ringback 的问题。就是别人打入电话时交换机给他播放的回铃音,做成彩铃一样可自定义的。你有搞定这个需求吗?

snowy 发表于 2017-8-4 08:36:31

本帖最后由 snowy 于 2017-8-4 08:50 编辑

你好,基本可以用了,外线的彩铃应该是运营商那边开吧?自己能做的应该只有内线拨内线的彩铃音。我现在拨打出去有些对方手机或电话的彩铃可以听到,有些听不到,不知道为什么,不过这个关系不大。

我遇到一个问题是DID被叫转移到外线,转不出去,看日志是超时。
例如:1. 分机1000拨打10086(其它外线号码都可以),接通后按转移键,输入另一分机号1001,可以呼出转移成功。
         2. 分机1000拨打10086(其它外线号码都可以),接通后按转移键,输入另一外线13888888,可以呼出转移成功。
         3.分机1001呼叫分机1000,1000接通后按转移键,输入另一外线号1388888,可以呼入转移到外线成功。
         4. 外线拨打1000分机的DID号码,1000接通后按转移键,输入另一外线号,转移失败(FreePBX是可以成功的)。

您可以帮忙测试一下第4项的情况么?谢谢!
         

daiqx 发表于 2017-8-4 12:42:53

snowy 发表于 2017-8-4 08:36
你好,基本可以用了,外线的彩铃应该是运营商那边开吧?自己能做的应该只有内线拨内线的彩铃音。我现在拨打 ...

这种情况我都会用脚本实现的,先拨出外线成功后和现在的会话桥接,应该很好实现。用xml配置的不擅长。

snowy 发表于 2017-8-4 19:58:44

daiqx 发表于 2017-8-4 12:42
这种情况我都会用脚本实现的,先拨出外线成功后和现在的会话桥接,应该很好实现。用xml配置的不擅长。 ...

FusionPBX不支持IAX2,我下载一份源码编译试试。

daiqx 发表于 2017-8-4 21:25:37

snowy 发表于 2017-8-4 19:58
FusionPBX不支持IAX2,我下载一份源码编译试试。

如果fs可以支持,直接上配置文件就行,一定要Fusionpbx吗?有什么绝对的优势? 我用一个迂回的方法搞定播放自定义的早期媒体了。用execute_on_answer=lua 属性在桥接成功时调用 lua 发送一个自定义事件。 然后在主lua 接收到事件再playback 。 坑了两星期这个问题,因为资料太少纯靠google加折腾。在这过程中我更觉得如果不靠脚本或esl之类的调用,很难使用强大灵活的功能。走了很多弯路,lua->阻塞式,在桥接时居然不能播放语音,然后就想上python的esl , 可惜在win下 esl的库死活编译不过,直接上c#的 socket 控制,发现资料更少。又回到lua,将就使用。 lua虽然 顺序阻塞式执行不能使用状态机等不足。但使用简单,功能足够强。在fs下强烈推荐。

snowy 发表于 2017-8-22 11:42:10

我现在也开始折腾编译FREESWITCH了,主要是想集成进IAX2协议,并且试试自己 写LUA。楼主用的什么版本的LINUX和FREESWITCH的哪个分支?我尝试了一下,遇到不少问题,我用的V1.8和CENTOS7,目前遇到一个怎么也解决不了的问题(不编译这个模块就可以成功了):
making all mod_opus
make: Entering directory `/root/freeswitch.git/src/mod/codecs/mod_opus'
Makefile:899: *** You must install libopus-dev to build mod_opus.Stop.

libopus-dev 这个组件,我用yum install 安装后还是提示这个错误,下载opus的代码编译安装后也还是提示,请问您遇到过没?

daiqx 发表于 2017-8-22 12:46:23

snowy 发表于 2017-8-22 11:42
我现在也开始折腾编译FREESWITCH了,主要是想集成进IAX2协议,并且试试自己 写LUA。楼主用的什么版本的LINU ...

在实际应用需求的情况下,走的路线总是相似的。我用的是window版的1.68 ,只要是因为windows 版没有 mod_lua才去折腾源码编译。linux 下的安装我都没搞通呢,你这个出错,有可能libopus-dev版本不对,删除后找其它版本再试了。到最后不行换一个linux发行版。在linux的许多组件不同版本真的搞死人。

snowy 发表于 2017-8-23 18:54:45

换成ubuntu一路坑,终于编译通过了,不过IAX2的支持还是不行,先玩玩LUA吧,您有没有相关脚本的集成入门指南?

daiqx 发表于 2017-8-23 20:45:05

snowy 发表于 2017-8-23 18:54
换成ubuntu一路坑,终于编译通过了,不过IAX2的支持还是不行,先玩玩LUA吧,您有没有相关脚本的集成入门指 ...

我当时在linux 下安装 fs 搞得焦燥,然后用lua也搞得超焦燥,基本是上面代码的几个指令,语法百度一下即可。
一般类似这样
http://blog.csdn.net/u010317005/article/details/51889022
还有 https://www.baidu.com/s?ie=utf-8&f=3&rsv_bp=1&rsv_idx=2&tn=baiduhome_pg&wd=freeswitch%20lua&rsv_spt=1&oq=freeswitch%2520lua&rsv_pq=9b5b9ab700068560&rsv_t=892a1qlxe4r65zZjPCAOOriOfqlSZLzJeS0yts1KLzo68o1aV%2BYRAtlAgQsWca7G8U3U&rqlang=cn&rsv_enter=0&prefixsug=freeswitch%2520lua&rsp=0&rsv_sug=2

还有 google   下freeswitch lua   
一边搜索一边踩坑,反正 lua 可以热更新不用 reloadxml ,不断尝试

fengyunyu 发表于 2017-8-25 07:50:08

楼主用的哪个sip协议栈?

daiqx 发表于 2017-8-25 09:01:57

fengyunyu 发表于 2017-8-25 07:50
楼主用的哪个sip协议栈?

freeswitch 默认的
页: [1]
查看完整版本: freeswitch 折腾了一个星期 lua 实现的 act 程序,抛砖引玉