So, it’s been a while since I posted on here. Fortunately however this is exclusively due to the massive amount of progress we have made over the last few months and not the lack of it!
In Dan’s last post (18th of June) he detailed how we intended to use Lua snippets from within liquid studio to control the calls within Freeswitch servers from liquid.
In summary this theory has worked very well for us and we have spent several months collecting a variety of Lua modules that are now used in a variety of our clients campaigns.
Lua has proven to be a very powerful language and found it relatively easy to pick up. there are plenty of resources on the freeswitch Wiki http://wiki.freeswitch.org/wiki/Mod_lua to help should anyone wish to give this a go.
Below I’ve taken a sample script which is a slightly abbreviated version of one of the scripts we use in a lot of our clients standard liquid campaigns.
This script will make a call to a call centre agent (session1), make a second call to a Customer (session2), join the two calls together and record the conversation. I’ve annotated the code to give a little more detail on what’s happening.
– Set variables with Arguments that are passed into Lua script from Liquid
UserCCode=argv[1];
UserNumber=argv[2];
CustomerCCode=argv[3];
CustomerNumber=argv[4];
callerid=argv[5];
UserGateway=argv[6];
UserGateway2=argv[7];
UserPrivacy=argv[8];
CustomerGateway=argv[9];
CustomerGateway2=argv[10]
CustomerPrivacy=argv[11];
Record=argv[12];
Recname=argv[13];
Liquid_Call_ID=argv[14];
Liquid_Customer_ID=argv[15];
Liquid_Company_ID=argv[16];
Liquid_Campaign_ID=argv[17];
Liquid_User_ID=argv[18];
Filename=argv[19];
Uuids=argv[20];
CallsKilled=0;
– Set API so that we can make API calls directly to Freeswitch later in the script
api = freeswitch.API();
– Specify split function, currently used as multiple arguments are passed in as one variable,
– There seems to be a limit on the number arguments that can be passed into a lua script,
– we still have to establish why this is the case
function split(pString, pPattern)
local Table = {}
local fpat = “(.-)” .. pPattern
local last_end = 1
local s, e, cap = pString:find(fpat, 1)
while s do
if s ~= 1
or cap ~= “”
then
table.insert(Table,cap)
end
last_end = e+1
s, e, cap = pString:find(fpat, last_end)
end
if last_end <= #pString then
cap = pString:sub(last_end)
table.insert(Table, cap)
end
return Table
end
– Split Uuids variable into a table and assign Uuids to variables for later use. The Uuids are assigned to calls from freeswitch so they can be controlled later
UuidList=split(Uuids,“,”)
s1uuid=UuidList[1]
s2uuid=UuidList[2]
s3uuid=UuidList[3]
s4uuid=UuidList[4]
– Start the Agent Call Leg,
session1 = freeswitch.Session(‘{origination_uuid=’..s1uuid..‘,sip_cid_type=pid,leg_timeout=15,bypass_media=false,RECORD_APPEND=true,RECORD_STEREO=true,origination_caller_id_number=’..callerid..‘,origination_privacy=’..UserPrivacy..‘,ignore_early_media=true} sofia/gateway/’..UserGateway..‘/00′..UserCCode..UserNumber);
local s1Cause = session1:hangupCause()
– in certain call failure scenarios the script immediately attempts the same call through an alternative SIP gateway to provide live failover in case our main provider encounters an issue with their service.
if ( s1Cause == “UNSPECIFIED”
or s1Cause == “ALLOTTED_TIMEOUT”
or s1Cause == “USER_BUSY”
or s1Cause == “NO_ROUTE_TRANSIT_NET”
or s1Cause == “NO_ROUTE_DESTINATION”
or s1Cause == “CHANNEL_UNACCEPTABLE”
or s1Cause == “CALL_REJECTED”
or s1Cause == “REDIRECTION_TO_NEW_DESTINATION”
or s1Cause == “DESTINATION_OUT_OF_ORDER”
or s1Cause == “INVALID_NUMBER_FORMAT”
or s1Cause == “NORMAL_UNSPECIFIED”
or s1Cause == “NORMAL_CIRCUIT_CONGESTION”
or s1Cause == “NETWORK_OUT_OF_ORDER”
or s1Cause == “NORMAL_TEMPORARY_FAILURE”
or s1Cause == “SERVICE_UNAVAILABLE”
or s1Cause == “INVALID_CALL_REFERENCE”
or s1Cause == “INCOMPATIBLE_DESTINATION” ) then
session1 = freeswitch.Session(‘{origination_uuid=’..s2uuid..‘,sip_cid_type=pid,leg_timeout=15,bypass_media=false,RECORD_APPEND=true,RECORD_STEREO=true,origination_caller_id_number=’..callerid..‘,origination_privacy=’..UserPrivacy..‘,ignore_early_media=true} sofia/gateway/’..UserGateway2..‘/00′..UserCCode..UserNumber);
end
– Set Variables for session 1, these variables are then recorded in the CDR call History.
if (session1:ready() == true) then
session1:setAutoHangup(false);
session1:setVariable(“Liquid_Call_ID”, Liquid_Call_ID);
session1:setVariable(“Liquid_Customer_ID”, Liquid_Customer_ID);
session1:setVariable(“Liquid_Company_ID”, Liquid_Company_ID);
session1:setVariable(“Liquid_Campaign_ID”, Liquid_Campaign_ID);
session1:setVariable(“Liquid_User_ID”, Liquid_User_ID);
session1:setVariable(“Filename”, Filename);
session1:setVariable(“CountryCode”, CustomerCCode);
session1:setVariable(“National_number”, CustomerNumber);
session1:setVariable(“Call_Reference”, Recname);
s1uuid=session1.uuid;
session1:execute(“info”,“notice”);
– Start Leg to the customer
session2 = freeswitch.Session(‘{origination_uuid=’..s3uuid..‘,sip_cid_type=pid,bypass_media=false,RECORD_APPEND=true,RECORD_STEREO=true,return_ring_ready=true,origination_caller_id_number=’..callerid..‘,origination_privacy=’..CustomerPrivacy..‘} sofia/gateway/’..CustomerGateway..‘/00′..CustomerCCode..CustomerNumber);
local s2Cause = session2:hangupCause()
if ( s2Cause == “UNSPECIFIED”
or s2Cause == “NO_ROUTE_TRANSIT_NET”
or s2Cause == “NO_ROUTE_DESTINATION”
or s2Cause == “CHANNEL_UNACCEPTABLE”
or s2Cause == “CALL_REJECTED”
or s2Cause == “REDIRECTION_TO_NEW_DESTINATION”
or s2Cause == “DESTINATION_OUT_OF_ORDER”
or s2Cause == “INVALID_NUMBER_FORMAT”
or s2Cause == “NORMAL_UNSPECIFIED”
or s2Cause == “NORMAL_CIRCUIT_CONGESTION”
or s2Cause == “NETWORK_OUT_OF_ORDER”
or s2Cause == “NORMAL_TEMPORARY_FAILURE”
or s2Cause == “SERVICE_UNAVAILABLE”
or s2Cause == “INVALID_CALL_REFERENCE”
or s2Cause == “INCOMPATIBLE_DESTINATION” ) then
session2 = freeswitch.Session(‘{origination_uuid=’..s4uuid..‘,sip_cid_type=pid,bypass_media=false,RECORD_APPEND=true,RECORD_STEREO=true,return_ring_ready=true,origination_caller_id_number=’..callerid..‘,origination_privacy=’..CustomerPrivacy..‘} sofia/gateway/’..CustomerGateway2..‘/00′..CustomerCCode..CustomerNumber);
end
– Start Call Recording on the second leg
session2:execute(‘record_session’, ‘${base_dir}\\recordings\\’..Liquid_Company_ID..‘-’..Recname..‘.wav’);
if (session1:ready() == true
and session2:ready() == true) then
session2:setAutoHangup(false);
session2:setVariable(“Liquid_Call_ID”, Liquid_Call_ID);
session2:setVariable(“Liquid_Customer_ID”, Liquid_Customer_ID);
session2:setVariable(“Liquid_Company_ID”, Liquid_Company_ID);
session2:setVariable(“Liquid_Campaign_ID”, Liquid_Campaign_ID);
session2:setVariable(“Liquid_User_ID”, Liquid_User_ID);
session2:setVariable(“Filename”, Filename);
session2:setVariable(“CountryCode”, CustomerCCode);
session2:setVariable(“National_number”, CustomerNumber);
session2:setVariable(“Call_Reference”, Recname);
s2uuid=session2.uuid;
session2:execute(“info”,“notice”);
– Bridge session 1 and session 2.
freeswitch.bridge(session1, session2);
else
local obCause = session2:hangupCause()
– Set location of useful Wav Files to be played to the agent if required
local busymsg = “${base_dir}\\sounds\\en\\uk\\network_msgs\\busy.wav”
local badnum = “${base_dir}\\sounds\\en\\uk\\network_msgs\\numberdiallednotrecognised.wav”
freeswitch.consoleLog(“info”, “session2:hangupCause() = “ .. obCause )
– The following tells Freeswitch what to do should the customer leg not connect correctly
if ( obCause == “USER_BUSY” ) then
session1:execute(“playback”, busymsg);
kill1 = api:executeString(“uuid_kill “..s1uuid);
kill2 = api:executeString(“uuid_kill “..s2uuid);
end
if ( obCause == “UNALLOCATED_NUMBER”
or obCause == “INVALID_NUMBER_FORMAT” ) then
session1:execute(“playback”, badnum);
kill1 = api:executeString(“uuid_kill “..s1uuid);
kill2 = api:executeString(“uuid_kill “..s2uuid);
end
kill1 = api:executeString(“uuid_kill “..s1uuid);
kill2 = api:executeString(“uuid_kill “..s2uuid);
kill3 = api:executeString(“uuid_kill “..s3uuid);
kill4 = api:executeString(“uuid_kill “..s4uuid);
end
– This while loop continues whilst the call is in progress to clear up sessions should the session they are bridge to fail / disconnect
while (session2:answered() == true
and CallsKilled == 0) do
freeswitch.consoleLog(“info”, “in while loop”)
if (session1:ready() == false) then
kill1 = api:executeString(“uuid_kill “..s1uuid);
kill2 = api:executeString(“uuid_kill “..s2uuid);
kill3 = api:executeString(“uuid_kill “..s3uuid);
kill4 = api:executeString(“uuid_kill “..s4uuid);
CallsKilled=1;
elseif (session2:ready() == false) then
kill1 = api:executeString(“uuid_kill “..s1uuid);
kill2 = api:executeString(“uuid_kill “..s2uuid);
kill3 = api:executeString(“uuid_kill “..s3uuid);
kill4 = api:executeString(“uuid_kill “..s4uuid);
CallsKilled=1;
end
end
end