Mod lua

From FreeSWITCH Wiki

Jump to: navigation, search

mod_lua is supported as of RC4

Contents

Features

Write IVR scripts in lua

It has a very easy to use syntax, see the Hello Lua script.

Serve configs (the same way xml_curl does it)

Examples

Make API calls directly from Lua code

Examples

Lightweight

Stripped mod_lua.so is 272k

Highly Embeddable

As far as embeddability goes - python ranks a 2, perl ranks a 4, js is a 5, and lua is a 10!

luarun at the CLI

You can issue a "luarun /path/to/script.lua" and launch a thread which your lua script will run in. The "lua" command is for inline lua like from the dialplan ie ${lua(codehere)}. "luarun" will spawn a thread while "lua" will block till the code is complete.

Configuring

For IVR use

Nothing should be needed here.

For making API calls

api = freeswitch.API();
r = api:execute("regex", "testing1234|/(\\d+)/|$1");

io.write(r .. "\n");

For serving configuration

For serving configuration with mod_lua:

  • You can bind a script to the xml req, like you do with url in xml_curl
  • When something looks up sections in the xml registry, it calls your script
  • Your script does any db lookups or whatever it needs, and it returns the XML string.

The file conf/autoload_configs/lua.conf.xml is used in the default FreeSWITCH™ setup.

Here is a minimum configuration file, it will fetch a dialplan from LUA script.

<configuration name="lua.conf" description="LUA Configuration">
  <settings>
    <!--<param name="xml-handler-script" value="/dp.lua"/>-->
    <!--<param name="xml-handler-bindings" value="dialplan"/>-->
  </settings>
</configuration>


This is a sample dp.lua, whatever is in XML_STRING will be returned to freeswitch once the script is finished

-- params is the event passed into us we can use params:getHeader to grab things we want.
io.write("TEST\n" .. params:serialize("xml") .. "\n");  

mydialplan = [[
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="freeswitch/xml">
  <section name="dialplan" description="RE Dial Plan For FreeSwitch">
    <context name="default">
      <extension name="freeswitch_public_conf_via_sip">
        <condition field="destination_number" expression="^9(888|1616)$">
          <action application="bridge" data="sofia/${use_profile}/$1@conference.freeswitch.org"/>
        </condition>
      </extension>
    </context>
  </section>
</document>
]]

XML_STRING = mydialplan

Lua scripts at startup

Here is a minimum configuration file:

<configuration name="lua.conf" description="LUA Configuration">
  <settings>
    <!--
	The following options identifies a lua script that is launched
	at startup and may live forever in the background.
	You can define multiple lines, one for each script you 
	need to run.
    -->
    <!--<param name="startup-script" value="startup_script_1.lua"/>-->
    <!--<param name="startup-script" value="startup_script_2.lua"/>-->
  </settings>
</configuration>


The startup-script values represent lua scripts (located inside the scripts/ directory) that are launched when FreeSWITCH is started. The scripts live in their own thread. You can use them to run simple tasks (and then let them finish) or looping forever, watching (for example) for events, generating calls or whatever you like.

Sample Dialplan

<action application="lua" data="helloworld.lua"/> 

NOTE: for looking up the location of the helloworld.lua file, it looks in prefix/scripts by default

Sample IVR's

Hello Lua

-- answer the call
session:answer();

-- play a file
session:streamFile("/path/to/blah.wav");

-- hangup
session:hangup();

More Samples

See scripts/lua directory on the filessytem, and Example IVRs


LUA Regex Example

using this method, you can execute regex conditions from inside your lua scripts. ( thanks bkw_ )

session:execute("set", "some_chan_variable=${regex(" .. destination .. "|^([0-9]{10})$)}")

if destination is a lua variable.. with your destination number in it... and your regex was ^([0-9]{10})$ the result will be put in "some_chan_variable" that you can get thorough a session:getVariable

you can also do

session:execute("set", "some_chan_variable=${regex(" .. destination .. "|^(0|61)([2,3,7,8][0-9]{8})$|$2)}")

to match and return parts of your regex..

FAQ

Where do I put 3rd part lua scripts/modules?

Q: Where do I need to stick my lua classes for FS to see them. I am running a lua script from within the /scripts folder, but it includes (requires) another lua file. FS.

A: (short answer) /usr/local/share/lua/5.1/

Alternatively, you can install a system lua (think apt-get or yum), and the lua embedded in freeswitch will look in the add-ons directory.

To do this in the mod_lua.conf file (currently not possible), the following development would be needed

* install them to a nonstandard
* push a nonstandard place into the path of where it looks


How can I make it use the "system lua"

Q: I have lua installed, but mod_lua seems to ignore the LUA binary.

A: Lua is so small that the whole ball of wax is statically linked into the module!

Can I access a database via ODBC?

Yes, see luasql

First you need to download and install Lua, and download LuaSQL, and compile and install whichever database modules you want to use. Next you can do something like:

#!/usr/local/bin/lua
require "luasql.mysql"

env = assert (luasql.mysql())
con = assert (env:connect("database","username","password","localhost"))
cur = assert (con:execute"SELECT * FROM table")
row = cur:fetch ({}, "a")

session:setVariable("varname", tostring(row.column));

cur:close()
con:close()
env:close()

Note: you need to symlink the shared object (ie: mysql.so) to /usr/local/lib/lua/5.1/luasql/mysql.so

API

event:getHeader

This is a generic API call

event:getHeader("Caller-Caller-ID-Name")

or, This can be used inside of a dialplan.lua to get certain information

params:getHeader("variable_sip_req_uri")

event:setPriority

event:fire

event:serialize

Use this to dump all available Headers to the console

io.write(params:serialize());

Or this to display them as an info message

freeswitch.consoleLog("info",params:serialize())

event:addHeader

event:delHeader

event:addBody

event:getBody

event:getType

session:answer

Answer the session:

session:answer();

session:preAnswer

Pre answer the session:

session:preAnswer();

session:streamFile

Stream a file to the session

session:streamFile("/tmp/blah.wav");

session:transfer

Transfer the current session. The arguments are extensions, dialplan and context.

session:transfer("3000", "XML", "default");

session:sleep

session:sleep(3000); 

-- This will allow callbacks to DTMF to occur and session:execute("sleep", "5000"), won't..

session:read

Play a file and get digits.

digits = session:read(5, 10, "/sr8k.wav", 3000, "#");                                                                                                           
freeswitch.consoleLog("info", "Got dtmf: ".. digits .."\n");         

session:read has 5 arguments: <min digits> <max digits> <file to play> <inter-digit timeout> <terminators>

session:playAndGetDigits

Play a file and get digits:

digits = session:playAndGetDigits(2, 5, 3, 3000, "#", "/sr8k.wav", "", "\\d+");
freeswitch.consoleLog("info", "Got dtmf: ".. digits .."\n");              

This has 8 arguments: min_digits, max_digits, max_tries, timeout, terminators, audio_files, bad_input_audio_files, digits_regex

session:getDigits

digits = session:getDigits(5, "#", 3000);
freeswitch.consoleLog("info", "Got dtmf: ".. digits .."\n");

session:setVariable

Set a variable on a session:

session:setVariable("varname", "varval");


session:getVariable

To get system variables such as ${hold_music}

local moh = session:getVariable("hold_music")


session:recordFile

syntax is session:recordFile(file_name, max_len, silence_threshold, silence_secs)

Example:

session:recordFile("/tmp/blah.wav", 30000, 10, 10);  -- pressing # ends the recording                                                                                                           
session:streamFile("/tmp/blah.wav"); 

session:setCallerData

session:ready

while (session:ready() == true) do                                                                                                                              
   -- do something here                                                                                                                                              
end 


session:setHangupHook

In your lua code, you can use setHangupHook to define the function to call when the session hangs up.


function myHangupHook(s, status, arg)
    freeswitch.consoleLog("NOTICE", "myHangupHook: " .. status .. "\n")
    -- close db_conn and terminate
    db_conn:close()
    error()
end

blah="w00t";

session:setHangupHook("myHangupHook", "blah")

session:execute

session:execute(app, data)

local mySound = "/usr/local/freeswitch/sounds/music/16000/partita-no-3-in-e-major-bwv-1006-1-preludio.wav"

session:execute("playback", mySound)

Callbacks (DTMF and friends) CAN NOT EXECUTE during an execute.

session:setAutoHangup

By default, lua script hangs up when it is done executing. If you need to run the next action in your dialplan after the lua script, you will need to setAutoHangup to false. The default value is true.


 session:setAutoHangup(false)

session:flushEvents

session:flushDigits

session:sendEvent

session:speak

session:set_tts_parms("flite", "kal");
session:speak("Please say the name of the person you're trying to contact");

session:setInputCallback

function my_cb(s, type, obj, arg)                                                                                                                                  
                                                                                                                                                                
   if (arg) then                                                                                                                                                
      io.write("type: " .. type .. "\n" .. "arg: " .. arg .. "\n");                                                                                             
   else                                                                                                                                                         
      io.write("type: " .. type .. "\n");                                                                                                                       
   end                                                                                                                                                          
                                                                                                                                                                
   if (type == "dtmf") then                                                                                                                                     
      io.write("digit: [" .. obj['digit'] .. "]\nduration: [" .. obj['duration'] .. "]\n");                                                                     
   else                                                                                                                                                         
      io.write(obj:serialize("xml"));                                                                                                                           
                                                                                                                                                                
      e = freeswitch.Event("message");                                                                                                                          
      e:add_body("you said " .. obj:get_body());                                                                                                                
      session:sendEvent(e);                                                                                                                                     
   end                                                                                                                                                          
end                                                                                                                                                             

blah="w00t";                                                                                                                                                                
                                                                                                                                                                
session:answer();                                                                                                                                               
session:setInputCallback("my_cb", "blah");                                                                                                                      
session:streamFile("/tmp/swimp.raw");          

session:hangup

You can hang up a session and provide an optional Hangup causes.

session:hangup("USER_BUSY");

or 

session:hangup(); -- default normal_clearing

freeswitch.Session

local session = freeswitch.Session("sofia/10.0.1.100/1001");
session:transfer("3000", "XML", "default"); 


freeswitch.consoleLog

Log something to the freeswitch logger. Arguments are loglevel, message.

freeswitch.consoleLog("info","lua rocks\n");

freeswitch.consoleCleanLog

freeswitch.consoleCleanLog("This Rocks!!!\n");

freeswitch.EventConsumer

con = freeswitch.EventConsumer("all");                                                                                                                          
                                                                                                                                                                
session = freeswitch.Session("sofia/default/dest@host.com");                                                                                                                                                                                                                                                                    

while session:ready() do                                                                                                                                        
   session:execute("sleep", "1000");                                                                                                                            
   for e in (function() return con:pop() end) do                                                                                                                
      print("event\n" .. e:serialize("xml"));                                                                                                                      
   end                                                                                                                                                          
end      

freeswitch.Event

This is firing a custom event my::event.

local event = freeswitch.Event("custom", "my::event"); 
event:addHeader("My-Header", "test");
event:fire();

freeswitch.IVRMenu

hash = {                                                                                                                                                        
   ["main"] = undef,                                                                                                                                            
   ["name"] = "top",                                                                                                                                            
   ["greet_long"] = "phrase:demo_ivr_main_menu",                                                                                                                
   ["greet_short"] = "phrase:demo_ivr_main_menu_short",                                                                                                            
   ["invalid_sound"] = "/tmp/wtf.wav",                                                                                                                          
   ["exit_sound"] = "voicemail/vm-goodbye.wav",                                                                                                                 
   ["confirm_macro"] = "undef",                                                                                                                                 
   ["confirm_key"] = "undef",                                                                                                                                   
   ["confirm_attempts"] = "3",                                                                                                                                  
   ["inter_digit_timeout"] = "2000",                                                                                                                            
   ["digit_len"] = "1",                                                                                                                                         
   ["timeout"] = "10000",                                                                                                                                       
   ["max_failures"] = "3"                                                                                                                                       
}                                                                                                                                                               
                                                                                                                                                                
top = freeswitch.IVRMenu(hash["main"],                                                                                                                          
                         hash["name"],                                                                                                                          
                         hash["greet_long"],                                                                                                                    
                         hash["greet_short"],                                                                                                                   
                         hash["invalid_sound"],                                                                                                                 
                         hash["exit_sound"],                                                                                                                    
                         hash["confirm_macro"],                                                                                                                 
                         hash["confirm_key"],                                                                                                                   
                         hash["confirm_attempts"],                                                                                                              
                         hash["inter_digit_timeout"],                                                                                                           
                         hash["digit_len"],                                                                                                                     
                         hash["timeout"],                                                                                                                       
                         hash["max_failures"]);                                                                                                                 
                                                                                                                                                                
top:bindAction("menu-exec-app", "playback /tmp/swimp.raw", "2");                                                                                                
top:execute(session, "top");                          

stream:write

stream:write("Content-Type: text/html\n\n");
stream:write("<title>FreeSWITCH Command Portal</title>");
stream:write("<h2>FreeSWITCH Command Portal</h2>");
stream:write("<form method=post><input name=command size=40> ");
stream:write("<input type=submit value=\"Execute\">");
stream:write("</form><hr noshade size=1><br>");

command = env:getHeader("command");

if (command)  then
   api = freeswitch.API();
   reply = api:executeString(command);

   if (reply) then
      stream:write("<br><B>Command Result</b><br>" .. reply .. "\n");
   end

end

env:addHeader("cool", "true");
stream:write(env:serialize() .. "\n\n");

Sending an Event

Using luarun to execute this code you can toggle the MWI on a registered phone on and off.

local event = freeswitch.Event("message_waiting");                                                                                                              
event:addHeader("MWI-Messages-Waiting", "no");                                                                                                                 
event:addHeader("MWI-Message-Account", "sip:1002@10.0.1.100");                                                                                                 
event:fire();  
Personal tools