--example usage --basic --condition destination_number 5900 --action set park_extension=5901 --advanced --condition destination_number ^59(\d{2})$ --action set park_extension=$1 --additional settings --action set park_range=5 --action set park_direction=in (in/out/both) --action set park_announce=true (not implemented yet) --action set park_timeout_seconds=30 (not implemented yet) --action set park_timeout_extension=1001 (not implemented yet) --action set park_music=$${hold_music} --action lua park.lua --connect to the database --ODBC - data source name --local dbh = freeswitch.Dbh("name","user","pass"); --FreeSWITCH core db local dbh = freeswitch.Dbh("core:park"); --get the session variables sounds_dir = session:getVariable("sounds_dir"); park_direction = session:getVariable("park_direction"); uuid = session:getVariable("uuid"); domain_name = session:getVariable("domain_name"); park_extension = session:getVariable("park_extension"); park_range = session:getVariable("park_range"); park_announce = session:getVariable("park_announce"); park_timeout_type = session:getVariable("park_timeout_type"); park_timeout_destination = session:getVariable("park_timeout_destination"); park_timeout_seconds = session:getVariable("park_timeout_seconds"); park_music = session:getVariable("park_music"); --add the explode function function explode ( seperator, str ) local pos, arr = 0, {} for st, sp in function() return string.find( str, seperator, pos, true ) end do -- for each divider found table.insert( arr, string.sub( str, pos, st-1 ) ) -- attach chars left of current divider pos = sp + 1 -- jump past current divider end table.insert( arr, string.sub( str, pos ) ) -- attach chars right of last divider return arr end --add the trim function function trim(s) return s:gsub("^%s+", ""):gsub("%s+$", "") end --if park_timeout_seconds is not defined set the timeout to 5 minutes if (not park_timeout_seconds) then park_timeout_seconds = 300; end --if park_timeout_type is not defined set to transfer if (not park_timeout_type) then park_timeout_type = "transfer"; end --prepare the api api = freeswitch.API(); --answer the call session:answer(); --database --exits the script if we didn't connect properly assert(dbh:connected()); --create the table if it doesn't exist --pgsql dbh:test_reactive("SELECT * FROM park", "", "CREATE TABLE park (id SERIAL, lot TEXT, domain TEXT, uuid TEXT, CONSTRAINT park_pk PRIMARY KEY(id))"); --sqlite dbh:test_reactive("SELECT * FROM park", "", "CREATE TABLE park (id INTEGER PRIMARY KEY, lot TEXT, domain TEXT, uuid TEXT)"); --mysql dbh:test_reactive("SELECT * FROM park", "", "CREATE TABLE park (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, lot TEXT, domain TEXT, uuid TEXT)"); --if park_range is defined then loop through the range to find an available parking lot if (park_range) then park_extension_start = park_extension; park_extension_end = ((park_extension+park_range)-1); extension = park_extension_start; while true do --exit the loop at the end of the range if (tonumber(extension) > park_extension_end) then break; end --check the database for an available slot lot_status = "available"; sql = "SELECT count(*) as count FROM park WHERE lot = '"..extension.."' and domain = '"..domain_name.."' "; dbh:query(sql, function(result) --for key, val in pairs(result) do -- freeswitch.consoleLog("NOTICE", "parking result "..key.." "..val.."\n"); --end count = result.count; end); --if count is 0 then the parking lot is available end the loop if (count == "0") then lot_status = "available"; park_extension = ""..extension; break; end --increment the value extension = extension + 1; end end --check the database to see if the slot is available or unavailable lot_status = "available"; sql = "SELECT id, lot, uuid FROM park WHERE lot = '"..park_extension.."' and domain = '"..domain_name.."' "; dbh:query(sql, function(row) lot_uuid = row.uuid; lot_status = "unavailable"; end); --if park direction is set to out then unpark by bridging it to the caller if (park_direction == "out") then if (lot_uuid) then --set the park status cmd = "uuid_setvar "..lot_uuid.." park_status unparked"; result = trim(api:executeString(cmd)); freeswitch.consoleLog("NOTICE", "Park Status: unparked "..park_extension.."\n"); --unpark the call with bridge cmd = "uuid_bridge "..uuid.." "..lot_uuid; result = trim(api:executeString(cmd)); end else --check if the uuid_exists, if it does not exist then delete the uuid from the db and set presence to terminated if (lot_uuid) then cmd = "uuid_exists "..lot_uuid; result = trim(api:executeString(cmd)); if (result == "false") then --set presence out event = freeswitch.Event("PRESENCE_IN"); event:addHeader("proto", "sip"); event:addHeader("event_type", "presence"); event:addHeader("alt_event_type", "dialog"); event:addHeader("Presence-Call-Direction", "outbound"); event:addHeader("state", "Active (1 waiting)"); event:addHeader("from", park_extension.."@"..domain_name); event:addHeader("login", park_extension.."@"..domain_name); event:addHeader("unique-id", lot_uuid); event:addHeader("answer-state", "terminated"); event:fire(); --delete from the database dbh:query("DELETE from park WHERE lot = '"..park_extension.."' and domain = '"..domain_name.."' "); --freeswitch.consoleLog("NOTICE", "Park - Affected rows: " .. dbh:affected_rows() .. "\n"); --set the status to available lot_status = "available"; end end --check if the parking lot is available, if it is then add it to the db, set presenence to confirmed and park the call if (lot_status == "available") then --park the call cmd = "uuid_park "..uuid; result = trim(api:executeString(cmd)); if (park_music) then cmd = "uuid_broadcast "..uuid.." "..park_music.." aleg"; result = trim(api:executeString(cmd)); end --set the park status cmd = "uuid_setvar "..uuid.." park_status parked"; result = trim(api:executeString(cmd)); freeswitch.consoleLog("NOTICE", "Park Status: parked "..park_extension.."\n"); --add to the database dbh:query("INSERT INTO park (lot, domain, uuid) VALUES ('"..park_extension.."', '"..domain_name.."', '"..uuid.."')"); --set presence in event = freeswitch.Event("PRESENCE_IN"); event:addHeader("proto", "sip"); --park event:addHeader("login", park_extension.."@"..domain_name); event:addHeader("from", park_extension.."@"..domain_name); event:addHeader("status", "Active (1 waiting)"); event:addHeader("rpid", "unknown"); event:addHeader("event_type", "presence"); event:addHeader("alt_event_type", "dialog"); event:addHeader("event_count", "1"); event:addHeader("unique-id", uuid); --event:addHeader("Presence-Call-Direction", "outbound") event:addHeader("answer-state", "confirmed"); event:fire(); else --bridge the current call to the call that is parked --set the presence to terminated event = freeswitch.Event("PRESENCE_IN"); event:addHeader("proto", "sip"); event:addHeader("event_type", "presence"); event:addHeader("alt_event_type", "dialog"); event:addHeader("Presence-Call-Direction", "outbound"); --event:addHeader("state", "Active (1 waiting)"); event:addHeader("from", park_extension.."@"..domain_name); event:addHeader("login", park_extension.."@"..domain_name); event:addHeader("unique-id", uuid); event:addHeader("answer-state", "terminated"); event:fire(); --delete the lot from the database dbh:query("DELETE from park WHERE lot = '"..park_extension.."' and domain = '"..domain_name.."' "); --freeswitch.consoleLog("NOTICE", "Park 200- Affected rows: " .. dbh:affected_rows() .. "\n"); --set the park status cmd = "uuid_setvar "..lot_uuid.." park_status unparked"; result = trim(api:executeString(cmd)); freeswitch.consoleLog("NOTICE", "Park Status: unparked "..park_extension.."\n"); --connect the calls cmd = "uuid_bridge "..uuid.." "..lot_uuid; result = trim(api:executeString(cmd)); end --continue running when the session ends session:setAutoHangup(false); --start the fifo monitor on its own so that it doesn't block the script execution api = freeswitch.API(); cmd = "luarun park_monitor.lua "..uuid.." "..domain_name.." "..park_extension.." "..park_timeout_type.." "..park_timeout_seconds.." "..park_timeout_destination; result = api:executeString(cmd); end