From 6fc6e231c8d615f4bb663ce682cd5cace08035f9 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Tue, 20 Jun 2017 18:23:23 +0300 Subject: [PATCH] Add. BLF indication for call forward. (#2664) * Add. BLF indication for call forward. To display general status of CallForward use blf like `forward+` To display status for specific phone number use blf like `forward+/` * Fix. Escape variable in regex expression. * Fix. Sync DND and CF blf. * Add. `blf_subscribe` service which can handle all custom subscriptions. To run for specific protocol it possible run as `luarun blf_subscribe.lua ` --- .../switch/conf/dialplan/500_call-forward.xml | 5 + resources/install/scripts/blf_subscribe.lua | 174 ++++++++++++++++++ .../install/scripts/call_flow_subscribe.lua | 125 +------------ resources/install/scripts/call_forward.lua | 25 ++- resources/install/scripts/do_not_disturb.lua | 21 ++- .../functions/basic_event_service.lua | 60 ++++++ .../scripts/resources/functions/blf.lua | 46 +++++ .../conf/autoload_configs/lua.conf.xml | 8 +- 8 files changed, 329 insertions(+), 135 deletions(-) create mode 100644 resources/install/scripts/blf_subscribe.lua create mode 100644 resources/install/scripts/resources/functions/basic_event_service.lua create mode 100644 resources/install/scripts/resources/functions/blf.lua diff --git a/app/dialplan/resources/switch/conf/dialplan/500_call-forward.xml b/app/dialplan/resources/switch/conf/dialplan/500_call-forward.xml index ddcad28397..5f29871ee3 100644 --- a/app/dialplan/resources/switch/conf/dialplan/500_call-forward.xml +++ b/app/dialplan/resources/switch/conf/dialplan/500_call-forward.xml @@ -13,5 +13,10 @@ + + + + + \ No newline at end of file diff --git a/resources/install/scripts/blf_subscribe.lua b/resources/install/scripts/blf_subscribe.lua new file mode 100644 index 0000000000..a5050cca70 --- /dev/null +++ b/resources/install/scripts/blf_subscribe.lua @@ -0,0 +1,174 @@ +local proto = argv[1] or 'all' + +local service_name +if proto == 'all' then + service_name = 'blf' +else + service_name = proto +end + +require "resources.functions.config" +require "resources.functions.split" + +local log = require "resources.functions.log"[service_name] +local presence_in = require "resources.functions.presence_in" +local Database = require "resources.functions.database" +local BasicEventService = require "resources.functions.basic_event_service" + +local find_call_flow do + +local find_call_flow_sql = [[select t1.call_flow_uuid, t1.call_flow_status +from v_call_flows t1 inner join v_domains t2 on t1.domain_uuid = t2.domain_uuid +where t2.domain_name = :domain_name and (t1.call_flow_feature_code = :feature_code +or t1.call_flow_feature_code = :short_feature_code) +]] + +function find_call_flow(user) + local ext, domain_name = split_first(user, '@', true) + local _, short = split_first(ext, '+', true) + if not domain_name then return end + local dbh = Database.new('system') + if not dbh then return end + local row = dbh:first_row(find_call_flow_sql, { + domain_name = domain_name, feature_code = ext, short_feature_code = short + }) + dbh:release() + if not row then return end + return row.call_flow_uuid, row.call_flow_status +end + +end + +local find_dnd do + +local find_dnd_sql = [[select t1.do_not_disturb +from v_extensions t1 inner join v_domains t2 on t1.domain_uuid = t2.domain_uuid +where t2.domain_name = :domain_name and (t1.extension = :extension or t1.number_alias=:extension)]] + +find_dnd = function(user) + local ext, domain_name = split_first(user, '@', true) + if not domain_name then return end + local dbh = Database.new('system') + if not dbh then return end + local dnd = dbh:first_value(find_dnd_sql, {domain_name = domain_name, extension = ext}) + dbh:release() + return dnd +end + +end + +local find_call_forward do + +local find_call_forward_sql = [[select t1.forward_all_destination, t1.forward_all_enabled +from v_extensions t1 inner join v_domains t2 on t1.domain_uuid = t2.domain_uuid +where t2.domain_name = :domain_name and (t1.extension = :extension or t1.number_alias=:extension)]] + +find_call_forward = function(user) + local ext, domain_name, number = split_first(user, '@', true) + if not domain_name then return end + ext, number = split_first(ext, '/', true) + local dbh = Database.new('system') + if not dbh then return end + local row = dbh:first_row(find_call_forward_sql, {domain_name = domain_name, extension = ext}) + dbh:release() + if not (row and row.forward_all_enabled) then return end + if row.forward_all_enabled ~= 'true' then return 'false' end + if number then + return number == row.forward_all_destination and 'true' or 'false', + row.forward_all_destination + end + return 'true', row.forward_all_destination +end + +end + +local protocols = {} + +protocols.flow = function(event) + local from, to = event:getHeader('from'), event:getHeader('to') + local expires = tonumber(event:getHeader('expires')) + if expires and expires > 0 then + local call_flow_uuid, call_flow_status = find_call_flow(to) + if call_flow_uuid then + log.noticef("Find call flow: %s staus: %s", to, tostring(call_flow_status)) + presence_in.turn_lamp(call_flow_status == "false", to, call_flow_uuid) + else + log.warningf("Can not find call flow: %s", to) + end + else + log.noticef("%s UNSUBSCRIBE from %s", from, to) + end +end + +protocols.dnd = function(event) + local from, to = event:getHeader('from'), event:getHeader('to') + local expires = tonumber(event:getHeader('expires')) + if expires and expires > 0 then + local proto, user = split_first(to, '+', true) + user = user or proto + local dnd_status = find_dnd(user) + if dnd_status then + log.noticef("Find DND: %s staus: %s", to, tostring(dnd_status)) + presence_in.turn_lamp(dnd_status == "true", to) + else + log.warningf("Can not find DND: %s", to) + end + else + log.noticef("%s UNSUBSCRIBE from %s", from, to) + end +end + +protocols.forward = function(event) + local from, to = event:getHeader('from'), event:getHeader('to') + local expires = tonumber(event:getHeader('expires')) + if expires and expires > 0 then + local proto, user = split_first(to, '+', true) + user = user or proto + local status, number = find_call_forward(user) + if status then + if status == 'true' then + log.noticef("CF: %s to number %s", to, tostring(number)) + else + log.noticef("CF: %s disabled", to) + end + presence_in.turn_lamp(status == "true", to) + else + log.warningf("Can not find CF: %s", to) + end + else + log.noticef("%s UNSUBSCRIBE from %s", from, to) + end +end + +if proto ~= 'all' then + for name in pairs(protocols) do + if proto ~= name then + protocols[name] = nil + end + end +end + +if not next(protocols) then + log.errorf('Unknown subscribe protocol: %s', proto) + return +end + +for name in pairs(protocols) do + log.noticef('add subscribe protocol: %s', name) +end + +local service = BasicEventService.new(log, service_name) + +-- FS receive SUBSCRIBE to BLF from device +service:bind("PRESENCE_PROBE", function(self, name, event) + local proto = event:getHeader('proto') + local handler = proto and protocols[proto] + if not handler then return end + return handler(event) +end) + +log.notice("start") + +service:run() + +log.notice("stop") diff --git a/resources/install/scripts/call_flow_subscribe.lua b/resources/install/scripts/call_flow_subscribe.lua index b756200c16..12ea57ffa2 100644 --- a/resources/install/scripts/call_flow_subscribe.lua +++ b/resources/install/scripts/call_flow_subscribe.lua @@ -1,123 +1,4 @@ -require "resources.functions.config" -require "resources.functions.split" +-- For backward compatibility +argv[1] = 'flow' +require "blf_subscribe" -local log = require "resources.functions.log".call_flow_subscribe -local EventConsumer = require "resources.functions.event_consumer" -local presence_in = require "resources.functions.presence_in" -local Database = require "resources.functions.database" - -local find_call_flow do - -local find_call_flow_sql = [[select t1.call_flow_uuid, t1.call_flow_status -from v_call_flows t1 inner join v_domains t2 on t1.domain_uuid = t2.domain_uuid -where t2.domain_name = :domain_name and (t1.call_flow_feature_code = :feature_code -or t1.call_flow_feature_code = :short_feature_code) -]] - -function find_call_flow(user) - local ext, domain_name = split_first(user, '@', true) - local _, short = split_first(ext, '+', true) - if not domain_name then return end - local dbh = Database.new('system') - if not dbh then return end - local row = dbh:first_row(find_call_flow_sql, { - domain_name = domain_name, feature_code = ext, short_feature_code = short - }) - dbh:release() - if not row then return end - return row.call_flow_uuid, row.call_flow_status -end - -end - -local find_dnd do - -local find_dnd_sql = [[select t1.do_not_disturb -from v_extensions t1 inner join v_domains t2 on t1.domain_uuid = t2.domain_uuid -where t2.domain_name = :domain_name and (t1.extension = :extension or t1.number_alias=:extension)]] - -find_dnd = function(user) - local ext, domain_name = split_first(user, '@', true) - if not domain_name then return end - local dbh = Database.new('system') - if not dbh then return end - local dnd = dbh:first_value(find_dnd_sql, {domain_name = domain_name, extension = ext}) - dbh:release() - return dnd -end - -end - -local service_name = "call_flow" -local pid_file = scripts_dir .. "/run/" .. service_name .. ".tmp" - -local events = EventConsumer.new(pid_file) - --- FS shutdown -events:bind("SHUTDOWN", function(self, name, event) - log.notice("shutdown") - return self:stop() -end) - --- Control commands from FusionPBX -events:bind("CUSTOM::fusion::service::control", function(self, name, event) - if service_name ~= event:getHeader('service-name') then return end - - local command = event:getHeader('service-command') - if command == "stop" then - log.notice("get stop command") - return self:stop() - end - - log.warningf('Unknown service command: %s', command or '') -end) - -local protocols = {} - -protocols.flow = function(event) - local from, to = event:getHeader('from'), event:getHeader('to') - local expires = tonumber(event:getHeader('expires')) - if expires and expires > 0 then - local call_flow_uuid, call_flow_status = find_call_flow(to) - if call_flow_uuid then - log.noticef("Find call flow: %s staus: %s", to, tostring(call_flow_status)) - presence_in.turn_lamp(call_flow_status == "false", to, call_flow_uuid) - else - log.warningf("Can not find call flow: %s", to) - end - else - log.noticef("%s UNSUBSCRIBE from %s", from, to) - end -end - -protocols.dnd = function(event) - local from, to = event:getHeader('from'), event:getHeader('to') - local expires = tonumber(event:getHeader('expires')) - if expires and expires > 0 then - local proto, user = split_first(to, '+', true) - user = user or proto - local dnd_status = find_dnd(user) - if dnd_status then - log.noticef("Find DND: %s staus: %s", to, tostring(dnd_status)) - presence_in.turn_lamp(dnd_status == "true", to) - else - log.warningf("Can not find DND: %s", to) - end - else - log.noticef("%s UNSUBSCRIBE from %s", from, to) - end -end - --- FS receive SUBSCRIBE to BLF from device -events:bind("PRESENCE_PROBE", function(self, name, event) - local proto = event:getHeader('proto') - local handler = proto and protocols[proto] - if not handler then return end - return handler(event) -end) - -log.notice("start") - -events:run() - -log.notice("stop") diff --git a/resources/install/scripts/call_forward.lua b/resources/install/scripts/call_forward.lua index 3535218d5e..f8307321b8 100644 --- a/resources/install/scripts/call_forward.lua +++ b/resources/install/scripts/call_forward.lua @@ -44,6 +44,7 @@ local Database = require "resources.functions.database" local Settings = require "resources.functions.lazy_settings" local route_to_bridge = require "resources.functions.route_to_bridge" + local blf = require "resources.functions.blf" --include json library local json @@ -69,6 +70,7 @@ local domain_name = session:getVariable("domain_name"); local extension_uuid = session:getVariable("extension_uuid"); local request_id = session:getVariable("request_id"); + local forward_all_destination = session:getVariable("forward_all_destination") or ''; local extension, dial_string --set the sounds path for the language, dialect and voice @@ -144,14 +146,24 @@ local number_alias = row.number_alias or ''; local accountcode = row.accountcode; local forward_all_enabled = row.forward_all_enabled; - local forward_all_destination = row.forward_all_destination; + local last_forward_all_destination = row.forward_all_destination; local follow_me_uuid = row.follow_me_uuid; local toll_allow = row.toll_allow or ''; local forward_caller_id_uuid = row.forward_caller_id_uuid; --toggle enabled if enabled == "toggle" then - enabled = (forward_all_enabled == "true") and "false" or "true"; + -- if we toggle CF and specify new destination number then just enable it + if (#forward_all_destination == 0) or (forward_all_destination == row.forward_all_destination) then + enabled = (forward_all_enabled == "true") and "false" or "true"; + else + enabled = 'true' + end + end + +-- get destination number form database if it not provided + if enabled == 'true' and #forward_all_enabled == 0 then + forward_all_destination = row.forward_all_destination end if not session:ready() then return end @@ -373,3 +385,12 @@ --end the call session:hangup(); end + +-- BLF for display CF status + blf.forward(enabled == 'true', extension, number_alias, + last_forward_all_destination, forward_all_destination, domain_name) + +-- turn off DND BLF + if enabled == 'true' then + blf.dnd(false, extension, number_alias, domain_name) + end diff --git a/resources/install/scripts/do_not_disturb.lua b/resources/install/scripts/do_not_disturb.lua index 2e64a56d20..b2ca95dc9c 100644 --- a/resources/install/scripts/do_not_disturb.lua +++ b/resources/install/scripts/do_not_disturb.lua @@ -43,7 +43,7 @@ --include config.lua require "resources.functions.config"; - local presence_in = require "resources.functions.presence_in" + local blf = require "resources.functions.blf" --check if the session is ready if ( session:ready() ) then @@ -96,6 +96,8 @@ accountcode = row.accountcode; follow_me_uuid = row.follow_me_uuid; do_not_disturb = row.do_not_disturb; + forward_all_destination = row.forward_all_destination + forward_all_enabled = row.forward_all_enabled if toggle then enabled = (do_not_disturb == 'true') and 'false' or 'true' end @@ -154,11 +156,11 @@ if (enabled == "true") then sql = sql .. "dial_string = :dial_string, "; sql = sql .. "do_not_disturb = 'true', "; + sql = sql .. "forward_all_enabled = 'false' "; else sql = sql .. "dial_string = null, "; - sql = sql .. "do_not_disturb = 'false', "; + sql = sql .. "do_not_disturb = 'false' "; end - sql = sql .. "forward_all_enabled = 'false' "; sql = sql .. "where domain_uuid = :domain_uuid "; sql = sql .. "and extension_uuid = :extension_uuid "; local params = {dial_string = dial_string, domain_uuid = domain_uuid, extension_uuid = extension_uuid}; @@ -217,13 +219,12 @@ session:hangup(); -- BLF for display DND status - local function dnd_blf(enabled, id, domain) - local user = string.format('dnd+%s@%s', id, domain) - presence_in.turn_lamp(enabled, user) - end + blf.dnd(enabled == "true", extension, number_alias, domain_name) - dnd_blf(enabled == "true", extension, domain_name) - if number_alias and #number_alias > 0 then - dnd_blf(enabled == "true", number_alias, domain_name) + -- Turn off BLF for call forward + if forward_all_enabled == 'true' and enabled == 'true' then + blf.forward(false, extension, number_alias, + forward_all_destination, nil, domain_name + ) end end diff --git a/resources/install/scripts/resources/functions/basic_event_service.lua b/resources/install/scripts/resources/functions/basic_event_service.lua new file mode 100644 index 0000000000..3d0a552e3b --- /dev/null +++ b/resources/install/scripts/resources/functions/basic_event_service.lua @@ -0,0 +1,60 @@ +require "resources.functions.config" + +local EventConsumer = require "resources.functions.event_consumer".EventConsumer + +local function class(base) + local t = base and setmetatable({}, base) or {} + t.__index = t + t.__class = t + t.__base = base + + function t.new(...) + local o = setmetatable({}, t) + if o.__init then + if t == ... then -- we call as Class:new() + return o:__init(select(2, ...)) + else -- we call as Class.new() + return o:__init(...) + end + end + return o + end + + return t +end + +local BasicEventService = class(EventConsumer) do + +function BasicEventService:__init(log, service_name, timeout) + local pid_file = scripts_dir .. "/run/" .. service_name .. ".tmp" + + self = BasicEventService.__base.__init(self, pid_file, timeout) + + -- FS shutdown + self:bind("SHUTDOWN", function(self, name, event) + log.notice("shutdown") + return self:stop() + end) + + -- Control commands from FusionPBX + self:bind("CUSTOM::fusion::service::control", function(self, name, event) + if service_name ~= event:getHeader('service-name') then return end + + local command = event:getHeader('service-command') + if command == "stop" then + log.notice("get stop command") + return self:stop() + end + + log.warningf('Unknown service command: %s', command or '') + end) + + return self +end + +end + +return { + BasicEventService = BasicEventService; + new = BasicEventService.new; +} \ No newline at end of file diff --git a/resources/install/scripts/resources/functions/blf.lua b/resources/install/scripts/resources/functions/blf.lua new file mode 100644 index 0000000000..b38b2405d4 --- /dev/null +++ b/resources/install/scripts/resources/functions/blf.lua @@ -0,0 +1,46 @@ +local presence_in = require "resources.functions.presence_in" + +local function blf(enabled, proto, id, domain) + local user = string.format('%s+%s@%s', proto, id, domain) + presence_in.turn_lamp(enabled, user) +end + +local function dnd(enabled, extension, number_alias, domain) + blf(enabled, 'dnd', extension, domain) + if number_alias and #number_alias > 0 then + blf(enabled, 'dnd', number_alias, domain) + end +end + +local function forward(enabled, extension, number_alias, number, domain) + if number then + extension = extension .. '/' .. number + if number_alias and #number_alias > 0 then + number_alias = number_alias .. '/' .. number + end + end + blf(enabled, 'forward', extension, domain) + if number_alias and #number_alias > 0 then + blf(enabled, 'forward', number_alias, domain) + end +end + +local function forward_toggle(enabled, extension, number_alias, old_number, new_number, domain) + -- turn off previews BLF number + if old_number and #old_number > 0 and old_number ~= new_number then + forward(false, extension, number_alias, old_number, domain) + end + + -- set common BLF status + forward(enabled, extension, number_alias, nil, domain) + + -- set destination specifc status + if new_number and #new_number > 0 then + forward(enabled, extension, number_alias, new_number, domain) + end +end + +return { + dnd = dnd; + forward = forward_toggle; +} diff --git a/resources/templates/conf/autoload_configs/lua.conf.xml b/resources/templates/conf/autoload_configs/lua.conf.xml index a63de3ffee..4225ff6d2c 100644 --- a/resources/templates/conf/autoload_configs/lua.conf.xml +++ b/resources/templates/conf/autoload_configs/lua.conf.xml @@ -39,8 +39,14 @@ 1 - Monitor - ignore SUBSCRIBE and just send NOTIFY each X seconds 2 - Event handler - handle each SUBSCRIBE request --> - + + + + + + +