Add. Use route_to_bridge module to build routes for ring groups. (#2907)

* Add. Use `route_to_bridge` module to build routes fro ring groups.

This commit has several improvements
1. Select only needed fields. (do not select quite big XML text strings)
2. Filter routes also by context name
3. Filter dialplans also by hostname
4. Handle conditions based not only `destination_number`
5. Handle `break` and `continue` attributes for extensions
6. Escape vars inside dial-string
7. Add log messages similar as FS dialplan do

* Add. `route_to_bridge` set inline vars so it possible use then in next conditions.
Add. `route_to_bridge` can execute basic api commands from allowed lists.
`route_to_bridge` expand all known vars. If var is unknown then it pass as is.
Fix. `export nolocal:` action.

* Fix. Short variable names

* Add. some comments

* Fix. Do not try execute empty string

This produce error messages `[ERR] switch_cpp.cpp:759 No application specified`

* Fix. Export nolocal values.
This commit is contained in:
Alexey Melnichuk
2017-11-17 17:15:47 +03:00
committed by FusionPBX
parent 8e0cf6ae12
commit 679d4e1fb5
2 changed files with 515 additions and 163 deletions

View File

@@ -30,6 +30,9 @@
--include the log
local log = require "resources.functions.log".ring_group
-- include libs
local route_to_bridge = require "resources.functions.route_to_bridge"
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
@@ -118,6 +121,7 @@
uuid = session:getVariable("uuid");
context = session:getVariable("context");
call_direction = session:getVariable("call_direction");
accountcode = session:getVariable("accountcode");
end
--default to local if nil
@@ -170,6 +174,9 @@
-- error();
--end
--get current switchname
hostname = trim(api:execute("switchname", ""))
--get the ring group
ring_group_forward_enabled = "";
ring_group_forward_destination = "";
@@ -187,7 +194,7 @@
missed_call_app = row["ring_group_missed_call_app"];
missed_call_data = row["ring_group_missed_call_data"];
end);
--get the ring group user
sql = "SELECT r.*, u.user_uuid FROM v_ring_groups as r, v_ring_group_users as u ";
sql = sql .. "where r.ring_group_uuid = :ring_group_uuid ";
@@ -304,7 +311,7 @@
--process the ring group
if (ring_group_forward_enabled == "true" and string.len(ring_group_forward_destination) > 0) then
--forward the ring group
session:setVariable("toll_allow",ring_group_forward_toll_allow);
session:setVariable("toll_allow",ring_group_forward_toll_allow);
session:execute("transfer", ring_group_forward_destination.." XML "..context);
else
--get the strategy of the ring group, if random, we use random() to order the destinations
@@ -422,33 +429,9 @@
--get the dialplan data and save it to a table
if (external) then
sql = [[select * from v_dialplans as d, v_dialplan_details as s
where (d.domain_uuid = :domain_uuid or d.domain_uuid is null)
and d.app_uuid = '8c914ec3-9fc0-8ab5-4cda-6c9288bdc9a3'
and d.dialplan_enabled = 'true'
and d.dialplan_uuid = s.dialplan_uuid
order by
d.dialplan_order asc,
d.dialplan_name asc,
d.dialplan_uuid asc,
s.dialplan_detail_group asc,
CASE s.dialplan_detail_tag
WHEN 'condition' THEN 1
WHEN 'action' THEN 2
WHEN 'anti-action' THEN 3
ELSE 100 END,
s.dialplan_detail_order asc
]];
params = {domain_uuid = domain_uuid};
if debug["sql"] then
freeswitch.consoleLog("notice", "[ring group] SQL:" .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dialplans = {};
x = 1;
assert(dbh:query(sql, params, function(row)
dialplans[x] = row;
x = x + 1;
end));
dialplans = route_to_bridge.preload_dialplan(
dbh, domain_uuid, {hostname = hostname, context = context}
)
end
--process the destinations
@@ -580,77 +563,65 @@
extension_uuid = trim(api:executeString(cmd));
--send to user
local dial_string_to_user = "[sip_invite_domain="..domain_name..",call_direction="..call_direction..","..group_confirm.."leg_timeout="..destination_timeout..","..delay_name.."="..destination_delay..",dialed_extension=" .. row.destination_number .. ",extension_uuid="..extension_uuid .. row.record_session .. "]user/" .. row.destination_number .. "@" .. domain_name;
dial_string = dial_string_to_user;
dial_string = dial_string_to_user;
elseif (tonumber(destination_number) == nil) then
--sip uri
dial_string = "[sip_invite_domain="..domain_name..",call_direction="..call_direction..","..group_confirm.."leg_timeout="..destination_timeout..","..delay_name.."="..destination_delay.."]" .. row.destination_number;
else
--external number
y = 0;
dial_string = '';
previous_dialplan_uuid = '';
regex_match = false;
for k, r in pairs(dialplans) do
if (y > 0) then
if (previous_dialplan_uuid ~= r.dialplan_uuid) then
regex_match = false;
bridge_match = false;
square = square .. "]";
y = 0;
dial_string = nil
local session_mt = {__index = function(_, k) return session:getVariable(k) end}
local params = setmetatable({
__api__ = api,
destination_number = destination_number,
user_exists = 'false',
call_direction = 'outbound',
domain_name = domain_name,
domain_uuid = domain_uuid,
destination_timeout = destination_timeout,
destination_delay = destination_delay,
}, session_mt)
local confirm = string.gsub(group_confirm, ',$', '') -- remove `,` from end of string
local route = route_to_bridge.apply_vars({ -- predefined actions
"domain_name=${domain_name}",
"domain_uuid=${domain_uuid}",
"sip_invite_domain=${domain_name}",
"leg_timeout=${destination_timeout}",
delay_name .. "=${destination_delay}",
"ignore_early_media=true",
confirm,
}, params)
route = route_to_bridge(dialplans, domain_uuid, params, route)
if route and route.bridge then
local remove_actions = {
["effective_caller_id_name="] = true;
["effective_caller_id_number="] = true;
['sip_h_X-accountcode='] = true;
}
-- cleanup variables
local i = 1 while i < #route do
-- remove vars from prev variant
if remove_actions[ route[i] ] then
table.remove(route, i)
i = i - 1
-- remove vars with unresolved vars
elseif string.find(route[i], '%${.+}') then
table.remove(route, i)
i = i - 1
-- remove vars with empty values
elseif string.find(route[i], '=$') then
table.remove(route, i)
i = i - 1
end
i = i + 1
end
if (r.dialplan_detail_tag == "condition") then
if (r.dialplan_detail_type == "destination_number") then
dial_string = "regex m:~"..destination_number.."~"..r.dialplan_detail_data
if (api:execute("regex", "m:~"..destination_number.."~"..r.dialplan_detail_data) == "true") then
--get the regex result
destination_result = trim(api:execute("regex", "m:~"..destination_number.."~"..r.dialplan_detail_data.."~$1"));
--set match equal to true
regex_match = true;
end
end
end
--regex_match = true;
--dial_string = r.dialplan_detail_data;
if (r.dialplan_detail_tag == "action") then
if (regex_match) then
--dial_string = 'match';
--replace $1
dialplan_detail_data = r.dialplan_detail_data:gsub("$1", destination_result);
--if the session is set then process the actions
if (y == 0) then
square = "[domain_name="..domain_name..",domain_uuid="..domain_uuid..",sip_invite_domain="..domain_name..",call_direction=outbound,"..group_confirm.."leg_timeout="..destination_timeout..","..delay_name.."="..destination_delay..",ignore_early_media=true,";
end
if (r.dialplan_detail_type == "set") then
--session:execute("eval", dialplan_detail_data);
if (dialplan_detail_data == "sip_h_X-accountcode=${accountcode}") then
if (session) then
accountcode = session:getVariable("accountcode");
if (accountcode) then
square = square .. "sip_h_X-accountcode="..accountcode..",";
end
end
elseif (dialplan_detail_data == "effective_caller_id_name=${outbound_caller_id_name}") then
elseif (dialplan_detail_data == "effective_caller_id_number=${outbound_caller_id_number}") then
else
square = square .. dialplan_detail_data..",";
end
elseif (r.dialplan_detail_type == "bridge") then
if (bridge_match) then
dial_string = dial_string .. delimiter .. square .."]"..dialplan_detail_data;
square = "[";
else
dial_string = square .."]"..dialplan_detail_data;
end
bridge_match = true;
break;
end
--increment the value
y = y + 1;
end
end
previous_dialplan_uuid = r.dialplan_uuid;
dial_string = '[' .. table.concat(route, ',') .. ']' .. route.bridge
end
end
@@ -776,12 +747,16 @@
or session:getVariable("originate_disposition") == "failure"
) then
--execute the time out action
session:execute(ring_group_timeout_app, ring_group_timeout_data);
if ring_group_timeout_app and #ring_group_timeout_app > 0 then
session:execute(ring_group_timeout_app, ring_group_timeout_data);
end
end
else
if (ring_group_timeout_app ~= nil) then
--execute the time out action
session:execute(ring_group_timeout_app, ring_group_timeout_data);
if ring_group_timeout_app and #ring_group_timeout_app > 0 then
session:execute(ring_group_timeout_app, ring_group_timeout_data);
end
else
local sql = "SELECT ring_group_timeout_app, ring_group_timeout_data FROM v_ring_groups ";
sql = sql .. "where ring_group_uuid = :ring_group_uuid";
@@ -791,7 +766,9 @@
end
dbh:query(sql, params, function(row)
--execute the time out action
session:execute(row.ring_group_timeout_app, row.ring_group_timeout_data);
if row.ring_group_timeout_app and #row.ring_group_timeout_app > 0 then
session:execute(row.ring_group_timeout_app, row.ring_group_timeout_data);
end
end);
end
end