📜 ⬆️ ⬇️

Dialplan on LUA for Asterisk

Greetings to all. Once upon a time, the topic of using the lua programming language when writing a dialplan in Asterisk was pretty tough for me. The fact is that I strongly dislike working with various GUIs (like FreePBX) when setting up Asterisk.

When I set everything up for the first time, I worked with the usual linear extensions.conf. As time went on, the demand for telephony functionality grew. Language lua gradually learned a little. And here I came to work as an administrator in one large company in our city (one large real estate agency) - at that time there were about 45 branches, about 650 - 700 users, including intercity, etc. Asterisk was already there, but everything was set up using FreePBX.

Almost immediately, the leadership began to overwhelm me with various questions on the tricks of Asterisk. For example, they wanted that when an incoming call was made to a branch, the calls inside the branch would be randomly distributed. They wanted to have a recording of conversations in mp3, they wanted to make a general group, where they could include all branches in general, and when dialing a number, to accidentally get to one of the branches, etc. Tasks seem to be simple, but to sit even solving such issues by means of a graphical interface was not very interesting to me personally.

There was another important point - the quality of telephony in general at that time was just awful. The voice constantly gurgled, the calls were broken, the caller could not be heard, the aster himself often crashed, etc. I look at the dialplan file, and it is 16 mb in size. Opened the text editor - and what to do here? There are several million rows.
')
I decided to remake it, throwing everything on lua. Approximately a couple of days after the start of development, I was able to present the first prototype of a dialplan for lua, quite working, but without the existing “fiches” and “ryushechek”. He replaced the entire old config with them, and then, even during the week, he threw the main bells and whistles that the management wanted to see. Also I updated the aster itself to the 11th version (at that time 11.3.0, it seems). Further, in the process of work, I sometimes looked at the file of the dialplan and filed what the management wanted or wanted. As a result, the aster with dialplan on lua worked much faster and more consistently than the last.

Conditions in which the "station" worked:

cpu: intel xeon e5520 (if not mistaken)
ram: 24gb
and other "iron" parameters, including two gigabit network interfaces and reyd1
number of vn.abonentov: about 700
Number of trunks: about 10 (of which 2 are providers, the rest are gsm addpack gateways).
the number of "city" numbers: about 200 (150 numbers from one provider and about 50 or slightly more from the second).

City numbers here were assigned to each branch. Some branches even have two or three numbers. Since all the calls from the city came to the context, then I did the analysis on did and passed the call to the desired branch.

Means lua implemented the ring groups, made two options for calling the subscriber - random and in order of listing in the group (with the exception of busy subscribers). Screwed lua-sql to record your own database of calls (in addition to cdr). This was done for what: the employee calls the client on the cellular, the client now did not want to talk (busy or something else); after some time, he calls back to the previously defined number and must get to the same employee who had called him before. I recorded the event "call to mobile" in a separate database. When a client calls back from a cell phone, I pick up the last call on the “cell phone call” event and give the client to the right employee. Only one such employee was remembered. Those. if this client is called by another employee. then, accordingly, the call will return to him.

Now I no longer work in that company, and where now I’m changing my old PBX to Asterisk and, of course, I’m using my old work. I remembered that the topic was interesting not only for me. Well, since there is very little information on this topic, I decided to distribute this article, then suddenly someone will come in handy.

Now let's get to the essence of the topic - coding on lua. I will not describe the stage of the inclusion of the pbx_lua module - there is a lot of information here. For example, now I have Centos 6.6, I already have lua in stock. I only threw the lua-devel package and included the pbx_lua module in menuselect.

In addition, if someone is going to use a manual connection to mysql (or to another database), then it is better to drop the lua-sql package by first installing luarocks and downloading the add-on from there.

Further, in the dialplan itself, you can describe the users and the rules for dialing, something like this:

extensions = { }; local_ext = { --  .      . h = function() --    (hangup) app.stopmixmonitor() d_status = channel["DIALSTATUS"]:get() if d_status ~= nil then app.noop("Dial over with status:"..d_status) -- ,    ,       cdr if d_status ~= "ANSWER" then channel["CDR(recordingfile)"]:set("") end app.noop("Good buy!") app.hangup() end; app.hangup() end; ["_14XXX"] = call_local; ["_21XX"] = call_local; ["_4595"] = call_all; --    ,   .         ["_*99"] = function() --       dnd (). local cid, dnd app.answer() cid = channel["CALLERID(num)"]:get() dnd = channel["DB(DND/"..cid.."/)"]:get() app.noop("DND:"..dnd) if dnd == "1" then channel["DB_DELETE(DND/"..cid.."/)"]:get() app.playback("beep") app.playback("beep") app.hangup() else channel["DB(DND/"..cid.."/)"]:set("1") app.playback("beep") app.wait(1) app.hangup() end end; include = {"mobile_out"}; }; 

here ["_XXnumber"] - template. Those. all the same as in the usual extensions.conf.
call_local - the function referenced by this description. Those. when dialing a number, say, 14555, the call_local function will be called. Also, this function can be called when an external call comes in.

 function call_local(ctx,ext) local callerid,cf,uniq,chn local n,j,i n = string.sub(ext,3) --   2   if n == "90" or n == "79" or n == "80" then --    90  ..         j = channel["CALLERID(num)"]:get() app.noop(string.format("Using ring group %s from %s",ext,j)) dial_rg(shuffle(r_group[ext],nil)) --       end --     "",         cf = channel["DB(CF/"..ext.."/"..")"]:get() app.noop("CF:"..cf) if cf ~= "" then app.noop(string.format("Call forward detected from %s to %s",ext,cf)) app.goto("mobile_out",cf,"1") end callerid = channel["CALLERID(num)"]:get() app.noop(string.format("Trying to local call %s from %s",ext,callerid)) if ext ~= "4550" and (CheckChannel(ext)) ~= NOT_INUSE then return end uniq = channel.UNIQUEID:get() chn = channel["CHANNEL"]:get() app.noop(string.format("UNIQUEID: %s",uniq)) app.noop(string.format("CHANNEL: %s",chn)) app.noop(string.format("CALLERID_name: %s",callerid)) app.noop(string.format("EXTEN: %s",ext)) app.noop(string.format("CONTEXT: %s",ctx)) record(string.format("%s-%s-%s",callerid,ext,uniq)) if ext == "4550" then local support = CallSupport(callerid) if support == "failed" then return end end if ext == "4514" or ext == "4592" then app.noop("Redirect!!!") app.dial("SIP/4591,60,tT") end app.dial(string.format("SIP/%s,60,tT",ext)) end 

There are a few checks on some groups and statuses. For example, the 4550 is a technical support group. For it, there is a separate function, in which there is the processing of employees' employment, informing the “In-Client”, recording the log and resetting the warning about a missed call to the technical support via jabber.

If the called party is a group, then mix the list and call a random caller.

Why do I use the random method of calling callers from groups? Branches are, in essence, sales managers. If you include a sequential call to branch employees, then the first on the list will always have more sales than the others (cheating). The situation is similar with the mem-primari method (it seems), in which the user who answered the last time will be ignored. The random mixing method is more honest, puts all salespeople on equal terms. Of course, you can call-all (call everyone at the same time), but then the branches start complaining that at the branch all the phones are yelling at the same time, it’s not convenient, noisy, etc.

You could also use queues for a random call, but I almost never use them. I do not know why, it happened.

Further, incoming from the city, description:

 from_trunk = { h = function() app.noop("BBBBBBBLLLLAAAAHHHHHH!!!!!!!") app.stopmixmonitor() if d_status ~= nil then d_status = channel["DIALSTATUS"]:get() app.noop("Dial over with status:"..d_status) if d_status ~= "ANSWER" then channel["CDR(recordingfile)"]:set("") end exten = "" uniqid = "" app.noop("Good buy!") app.hangup() end app.noop("Some problem!!!") app.hangUP() end; ["f1"] = function(e) --  ,      ... app.goto("local_ext",e,1) end; ["_."] = foo; --  ,     foobar... include = {"local_ext"} } 


Here I am all external incoming I wrap in foo.

 function foo(ctx,ext) local chn tmptab.did = ext tmptab.rg = g_tab[ext] if tmptab.did == "99051000227736" then --        . . app.noop("Skype TEST!!!") app.dial("SIP/14553,,tT,M(bar)") end tmptab.callerid = channel["CALLERID(num)"]:get() if string.find(tmptab.callerid,"88005550678",1) then app.hungup() end -- - ... if string.find(tmptab.callerid,"79",1) then --    , -      tmptab.callerid = "8"..string.sub(tmptab.callerid,2) channel["CALLERID(all)"]:set(tmptab.callerid) end chn = channel["CHANNEL"]:get() app.noop("CHANNEL:"..chn) if string.find(chn,"SIP/gsm_",1) then --      gsm  app.noop("Found channel "..chn) --       mysql       num = sql.mobile_get(tmptab.callerid) if num then app.goto("local_ext",num,1) end end app.noop("CallerID(num):"..tmptab.callerid) app.noop("by context:"..ctx) app.noop("DID:"..tmptab.did) app.set("CDR(did)="..tmptab.did) if tmptab.did == "4595" then call_all(tmptab.did) end -- 4595       app.answer() app.wait(1) j = channel["DB(ENUM/"..tmptab.did.."/)"]:get() app.noop("tag = "..j) if j == "ngs_rec" then dial_rg(shuffle(tmptab.rg),1) else if tmptab.did ~= "3471234" then --  ,    !!! app.playback(mhold.comp_hello) if tmptab.did == "3472345" then app.goto("local_ext","4591",1) end ivr(tmptab.did) -- ,     ,     ... dial_rg(shuffle(tmptab.rg),nil) else app.noop("BLAH DETECTED") dial_rg(tmptab.rg,nil) end end app.noop("hungup?") end 


In this case, determine the cell numbers and external numbers (did). If the caller calls the local cellular (SIM in the gsm gateway), then I take the last caller and send this client to him. There is also a definition from the ngs_rec list. Here the numbers are pre-defined as "advertising". It was the old method (then redid, but in this version of the file from which I take the code of this revision is not). I send all advertising numbers to special numbers in the office and make a note in the database that there was a call to the number indicated in the advertisement.

For now, I think, enough code at the moment. If someone has an interest in the transition from the old dialplan to lua, I think I can continue and explain in more detail some things. Although, if someone already knows how to program in Lua, there will be no problems at all.

In conclusion, I want to say that, of course, today there is a whole bunch of different clever solutions, such as VoxImplant and the like. Many people are not accustomed to working in the console and something to code. But I want to note that when the size of the company is large (from 50 subscribers and above), then building the logic of the “station” with the help of buttons and checkboxes in the graphical interface can eventually lead to problems. Above in the beginning of the article I gave examples about gurgling and precipices. Dialplan on lua weighs only 24kb, 968 lines.

Almost 700 subscribers worked on it without any problems.

Everything. Goodbye everyone!

Source: https://habr.com/ru/post/243125/


All Articles