Move the scripts to app/scripts/resources/scripts

This commit is contained in:
markjcrane
2020-01-18 00:30:13 +00:00
parent 1dd99599ac
commit ca5a824bb5
194 changed files with 4 additions and 4 deletions

View File

@@ -17,7 +17,7 @@
The Initial Developer of the Original Code is
Mark J Crane <markjcrane@fusionpbx.com>
Portions created by the Initial Developer are Copyright (C) 2008-2017
Portions created by the Initial Developer are Copyright (C) 2008-2020
the Initial Developer. All Rights Reserved.
Contributor(s):
@@ -104,16 +104,16 @@ if (!class_exists('scripts')) {
$source_directory = '/usr/share/examples/fusionpbx/scripts';
}
else {
$source_directory = $_SERVER["DOCUMENT_ROOT"].PROJECT_PATH.'/resources/install/scripts';
$source_directory = $_SERVER["DOCUMENT_ROOT"].PROJECT_PATH.'/app/resources/scripts';
}
if (is_readable($source_directory)) {
//copy the main scripts
recursive_copy($source_directory,$destination_directory);
recursive_copy($source_directory, $destination_directory);
unset($source_directory);
//copy the app/*/resource/install/scripts
$app_scripts = glob($_SERVER["DOCUMENT_ROOT"].PROJECT_PATH.'app/*/resource/install/scripts');
foreach ($app_scripts as $app_script){
foreach ($app_scripts as $app_script) {
recursive_copy($app_script, $destination_directory);
}
unset($app_scripts);

View File

@@ -0,0 +1,48 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--include config.lua
require "resources.functions.config";
--get the argv values
script_name = argv[0];
app_name = argv[1];
--example use command
--luarun app.lua app_name 'a' 'b 123' 'c'
--for loop through arguments
arguments = "";
for key,value in pairs(argv) do
if (key > 1) then
arguments = arguments .. " '" .. value .. "'";
--freeswitch.consoleLog("notice", "[app.lua] argv["..key.."]: "..argv[key].."\n");
end
end
--route the request to the application
--freeswitch.consoleLog("notice", "["..app_name.."]".. scripts_dir .. "/app/" .. app_name .. "/index.lua\n");
loadfile(scripts_dir .. "/app/" .. app_name .. "/index.lua")(argv);

View File

@@ -0,0 +1,221 @@
--set default variables
max_digits = 15;
digit_timeout = 5000;
debug["sql"] = true;
--general functions
require "resources.functions.trim";
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
local presence_in = require "resources.functions.presence_in"
--set the api
api = freeswitch.API();
--get the argv values
action = argv[2];
--get the session variables
if (session:ready()) then
session:answer();
end
--get the session variables
if (session:ready()) then
--general variables
domain_uuid = session:getVariable("domain_uuid");
domain_name = session:getVariable("domain_name");
context = session:getVariable("context");
uuid = session:get_uuid();
agent_authorized = session:getVariable("agent_authorized");
agent_id = session:getVariable("agent_id");
agent_password = session:getVariable("agent_password");
--set the sounds path for the language, dialect and voice
default_language = session:getVariable("default_language");
default_dialect = session:getVariable("default_dialect");
default_voice = session:getVariable("default_voice");
if (not default_language) then default_language = 'en'; end
if (not default_dialect) then default_dialect = 'us'; end
if (not default_voice) then default_voice = 'callie'; end
end
--set default as access denied
if (agent_authorized == nil or agent_authorized ~= 'true') then
agent_authorized = 'false';
end
--define the sounds directory
sounds_dir = session:getVariable("sounds_dir");
sounds_dir = sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice;
--get the agent_id from the caller
if (agent_id == nil) then
min_digits = 2;
max_digits = 20;
max_tries = 3;
agent_id = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", "phrase:voicemail_enter_id:#", "", "\\d+");
end
--get the pin number from the caller
if (agent_password == nil and agent_authorized ~= 'true') then
min_digits = 3;
max_digits = 20;
max_tries = 3;
agent_password = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", "phrase:voicemail_enter_pass:#", "", "\\d+");
end
--get the agent password
local params = {domain_uuid = domain_uuid, agent_id = agent_id}
local sql = "SELECT * FROM v_call_center_agents ";
sql = sql .. "WHERE domain_uuid = :domain_uuid ";
sql = sql .. "AND agent_id = :agent_id ";
if (agent_authorized ~= 'true') then
sql = sql .. "AND agent_password = :agent_password ";
params.agent_password = agent_password;
end
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[user status] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--set the variables
agent_uuid = row.call_center_agent_uuid;
agent_name = row.agent_name;
agent_id = row.agent_id;
user_uuid = row.user_uuid;
--authorize the user
agent_authorized = 'true';
end);
--show the results
if (agent_id) then
freeswitch.consoleLog("notice", "[user status][login] agent id: " .. agent_id .. " authorized: " .. agent_authorized .. "\n");
end
if (agent_password and debug["password"]) then
freeswitch.consoleLog("notice", "[user status][login] agent password: " .. agent_password .. "\n");
end
--get the user_uuid
if (agent_authorized == 'true') then
--get the agent status from mod_callcenter
cmd = "callcenter_config agent get status "..agent_uuid.."";
freeswitch.consoleLog("notice", "[user status][login] "..cmd.."\n");
user_status = trim(api:executeString(cmd));
--get the user info
if (user_status == "Available") then
action = "logout";
status = 'Logged Out';
else
action = "login";
status = 'Available';
end
--send a login or logout to mod_callcenter
cmd = "callcenter_config agent set status "..agent_uuid.." '"..status.."'";
freeswitch.consoleLog("notice", "[user status][login] "..cmd.."\n");
result = api:executeString(cmd);
--update the user status
if (user_uuid ~= nil and user_uuid ~= '') then
local sql = "SELECT user_status FROM v_users ";
sql = sql .. "WHERE user_uuid = :user_uuid ";
sql = sql .. "AND domain_uuid = :domain_uuid ";
local params = {user_uuid = user_uuid, domain_uuid = domain_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[call_center] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--set the user_status in the users table
local sql = "UPDATE v_users SET ";
sql = sql .. "user_status = :status ";
sql = sql .. "WHERE user_uuid = :user_uuid ";
local params = {status = status, user_uuid = user_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[call_center] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
end);
end
--set the presence to terminated - turn the lamp off:
if (action == "logout") then
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", agent_name.."@"..domain_name);
event:addHeader("login", agent_name.."@"..domain_name);
event:addHeader("unique-id", agent_uuid);
event:addHeader("answer-state", "terminated");
event:fire();
end
--set presence in - turn lamp on
if (action == "login") then
event = freeswitch.Event("PRESENCE_IN");
event:addHeader("proto", "sip");
event:addHeader("login", agent_name.."@"..domain_name);
event:addHeader("from", agent_name.."@"..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", agent_uuid);
event:addHeader("Presence-Call-Direction", "outbound");
event:addHeader("answer-state", "confirmed");
event:fire();
end
if (action == "login") then
blf_status = "false"
end
if string.find(agent_name, 'agent+', nil, true) ~= 1 then
presence_in.turn_lamp( blf_status,
'agent+'..agent_name.."@"..domain_name
);
end
end
--unauthorized
if (agent_authorized == 'false') then
result = session:streamFile(sounds_dir.."/voicemail/vm-fail_auth.wav");
status = "Invalid ID or Password";
end
--set the status and presence
if (session:ready()) then
if (action == "login") then
session:execute("playback", sounds_dir.."/ivr/ivr-you_are_now_logged_in.wav");
end
if (action == "logout") then
session:execute("playback", sounds_dir.."/ivr/ivr-you_are_now_logged_out.wav");
end
end
--send the status to the display
if (status ~= nil) then
reply = api:executeString("uuid_display "..uuid.." '"..status.."'");
end
--set the session sleep to give time to see the display
if (session:ready()) then
session:execute("sleep", "2000");
end

View File

@@ -0,0 +1,19 @@
--subscribe to the events
events = freeswitch.EventConsumer("CUSTOM","avmd::beep");
--prepare the api object
api = freeswitch.API();
--get the events
for event in (function() return events:pop(1) end) do
--serialize the data for the console
--freeswitch.consoleLog("notice","event:" .. event:serialize("xml") .. "\n");
--freeswitch.consoleLog("notice","event:" .. event:serialize("json") .. "\n");
--get the uuid
local uuid = event:getHeader("Unique-ID");
freeswitch.consoleLog("[avmd] uuid: ", uuid .. "\n");
--end the call
reply = api:executeString("uuid_kill "..uuid.." NORMAL_CLEARING");
end

View File

@@ -0,0 +1,220 @@
--
-- FusionPBX
-- Version: MPL 1.1
--
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
--
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
--
-- The Original Code is FusionPBX
--
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Copyright (C) 2019
-- the Initial Developer. All Rights Reserved.
--
-- Contributor(s):
-- Mark J Crane <markjcrane@fusionpbx.com>
--set the debug level
debug["sql"] = false;
--includes
local cache = require"resources.functions.cache";
local log = require"resources.functions.log"["call_block"];
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson";
end
--include functions
require "resources.functions.trim";
require "resources.functions.explode";
require "resources.functions.file_exists";
--get the variables
if (session:ready()) then
--session:setAutoHangup(false);
domain_uuid = session:getVariable("domain_uuid");
caller_id_name = session:getVariable("caller_id_name");
caller_id_number = session:getVariable("caller_id_number");
context = session:getVariable("context");
call_block = session:getVariable("call_block");
extension_uuid = session:getVariable("extension_uuid");
end
--set default variables
api = freeswitch.API();
--set the dialplan cache key
local call_block_cache_key = "call_block:" .. caller_id_number;
--get the cache
cached_value, err = cache.get(call_block_cache_key);
if (debug['cache']) then
if cached_value then
log.notice(call_block_cache_key.." source: cache");
elseif err ~= 'NOT FOUND' then
log.notice("error cache: " .. err);
end
end
--disable the cache
cached_value = nil;
--run call block one time
if (call_block == nil and call_block ~= 'true') then
--set the cache
if (not cached_value) then
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson";
end
--exits the script if we didn't connect properly
assert(dbh:connected());
--check to see if the call should be blocked
sql = "select * from v_call_block ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and call_block_enabled = 'true' ";
sql = sql .. "and ( ";
sql = sql .. " (call_block_name = :call_block_name and call_block_number = :call_block_number) ";
sql = sql .. " or (call_block_name is null and call_block_number = :call_block_number) ";
sql = sql .. " or (call_block_name = :call_block_name and call_block_number is null) ";
sql = sql .. ") ";
if (extension_uuid == nil) then
sql = sql .. "and extension_uuid is null ";
else
sql = sql .. "and (extension_uuid is null or extension_uuid = :extension_uuid) ";
end
if (extension_uuid ~= nil) then
params = {domain_uuid = domain_uuid, call_block_name = caller_id_name, call_block_number = caller_id_number, extension_uuid = extension_uuid};
else
params = {domain_uuid = domain_uuid, call_block_name = caller_id_name, call_block_number = caller_id_number};
end
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[dialplan] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
local found = false;
dbh:query(sql, params, function(row)
found = true;
--get the values from the database
call_block_uuid = row.call_block_uuid;
call_block_app = row.call_block_app;
call_block_data = row.call_block_data;
call_block_count = row.call_block_count;
extension_uuid = row.extension_uuid;
--cached_value = domain_uuid..','..caller_id_number;
end);
--set call block default to false
call_block = false;
if (call_block_app ~= nil) then
call_block = true;
if (session:ready()) then
session:execute('set', 'call_block=true');
end
end
--call block action
if (call_block_app ~= nil and call_block_app == 'busy') then
if (session:ready()) then
session:execute("respond", '486');
session:execute('set', 'call_block_uuid='..call_block_uuid);
session:execute('set', 'call_block_app=busy');
freeswitch.consoleLog("notice", "[call_block] caller id number " .. caller_id_number .. " action: Busy\n");
end
end
if (call_block_app ~= nil and call_block_app == 'hold') then
if (session:ready()) then
session:execute("respond", '607');
session:execute('set', 'call_block_uuid='..call_block_uuid);
session:execute('set', 'call_block_app=hold');
freeswitch.consoleLog("notice", "[call_block] caller id number " .. caller_id_number .. " action: Hold\n");
end
end
if (call_block_app ~= nil and call_block_app == 'reject') then
if (session:ready()) then
session:execute("respond", '607');
session:execute('set', 'call_block_uuid='..call_block_uuid);
session:execute('set', 'call_block_app=reject');
freeswitch.consoleLog("notice", "[call_block] caller id number " .. caller_id_number .. " action: Reject\n");
end
end
if (call_block_app ~= nil and call_block_data ~= nil) then
if (call_block_app == 'extension') then
if (session:ready()) then
session:execute('set', 'call_block_uuid='..call_block_uuid);
session:execute('set', 'call_block_app='..call_block_app);
session:execute('set', 'call_block_data='..call_block_data);
session:execute("transfer", call_block_data..' XML '.. context);
freeswitch.consoleLog("notice", "[call_block] caller id number " .. caller_id_number .. " action: extension ".. call_block_data.."\n");
end
end
if (call_block_app == 'voicemail') then
if (session:ready()) then
session:execute('set', 'call_block_uuid='..call_block_uuid);
session:execute('set', 'call_block_app='..call_block_app);
session:execute('set', 'call_block_data='..call_block_data);
session:execute("transfer", '*99'..call_block_data..' XML '.. context);
freeswitch.consoleLog("notice", "[call_block] caller id number " .. caller_id_number .. " action: voicemail *99".. call_block_data.."\n");
end
end
end
--update the call block count
if (call_block) then
sql = "update v_call_block ";
sql = sql .. "set call_block_count = :call_block_count ";
sql = sql .. "where call_block_uuid = :call_block_uuid ";
local params = {call_block_uuid = call_block_uuid, call_block_count = call_block_count + 1};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[dialplan] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
end
--close the database connection
dbh:release();
--set the cache
if (cached_value ~= nil) then
local ok, err = cache.set(call_block_cache_key, cached_value, '3600');
end
if debug["cache"] then
if ok then
freeswitch.consoleLog("notice", "[call_block] " .. call_block_cache_key .. " stored in the cache\n");
else
freeswitch.consoleLog("warning", "[call_block] " .. call_block_cache_key .. " can not be stored in the cache: " .. tostring(err) .. "\n");
end
end
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[call_block] " .. call_block_cache_key .. " source: database\n");
end
else
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[call_block] " .. call_block_cache_key .. " source: cache\n");
end
end
end

View File

@@ -0,0 +1,787 @@
-- conference_center/index.lua
-- Part of FusionPBX
-- Copyright (C) 2013 - 2015 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED AS ''IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--
-- Contributor(s):
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Luis Daniel Lucio Quiroz <dlucio@okay.com.mx>
--set variables
flags = "";
max_tries = 3;
digit_timeout = 5000;
--debug
debug["sql"] = false;
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--prepare the api object
api = freeswitch.API();
--general functions
require "resources.functions.base64";
require "resources.functions.trim";
require "resources.functions.file_exists";
require "resources.functions.explode";
require "resources.functions.format_seconds";
require "resources.functions.mkdir";
--get the session variables
uuid = session:getVariable("uuid");
--answer the call
session:answer();
--get record_ext
record_ext = session:getVariable("record_ext");
if (not record_ext) then
record_ext = "wav";
end
--define a function to send email
function send_email(email, attachment, default_language, default_dialect)
--require the email address to send the email
if (string.len(email) > 2) then
--format the message length and date
--message_length_formatted = format_seconds(message_length);
--if (debug["info"]) then
-- freeswitch.consoleLog("notice", "[conference_center] message length: " .. message_length .. "\n");
--end
local conference_date_end = os.date("%A, %d %b %Y %I:%M %p");
--os.time();
--prepare the files
file_subject = scripts_dir.."/app/conference_center/resources/templates/"..default_language.."/"..default_dialect.."email_subject.tpl";
file_body = scripts_dir.."/app/conference_center/resources/templates/"..default_language.."/"..default_dialect.."/email_body.tpl";
if (not file_exists(file_subject)) then
file_subject = scripts_dir.."/app/conference_center/resources/templates/en/us/email_subject.tpl";
file_body = scripts_dir.."/app/conference_center/resources/templates/en/us/email_body.tpl";
end
--get the moderator_pin
local sql = "SELECT moderator_pin FROM v_meetings WHERE meeting_uuid = :meeting_uuid";
local params = {meeting_uuid = meeting_uuid}
freeswitch.consoleLog("notice", "[voicemail] sql: " .. sql .. "; params:" .. json.encode(params) .. "\n");
dbh:query(sql, params, function(row)
moderator_pin = string.lower(row["moderator_pin"]);
end);
--get the link_address
link_address = http_protocol.."://"..domain_name..project_path;
--prepare the headers
headers = '{"X-FusionPBX-Domain-UUID":"'..domain_uuid..'",';
headers = headers..'"X-FusionPBX-Domain-Name":"'..domain_name..'",';
headers = headers..'"X-FusionPBX-Call-UUID":"na",';
headers = headers..'"X-FusionPBX-Email-Type":"conference"}';
--prepare the subject
local f = io.open(file_subject, "r");
local subject = f:read("*all");
f:close();
subject = subject:gsub("${moderator_pin}", moderator_pin);
subject = subject:gsub("${conference_date_end}", conference_date_end);
--subject = subject:gsub("${conference_duration}", message_length_formatted);
subject = subject:gsub("${domain_name}", domain_name);
subject = subject:gsub("${link_address}", link_address);
subject = trim(subject);
subject = '=?utf-8?B?'..base64.enc(subject)..'?=';
--prepare the body
local f = io.open(file_body, "r");
local body = f:read("*all");
f:close();
body = body:gsub("${moderator_pin}", moderator_pin);
body = body:gsub("${conference_date_end}", conference_date_end);
body = body:gsub("${conference_uuid}", conference_session_uuid);
--body = body:gsub("${conference_duration}", message_length_formatted);
body = body:gsub("${domain_name}", domain_name);
body = body:gsub("${link_address}", link_address);
body = body:gsub(" ", "&nbsp;");
body = body:gsub("%s+", "");
body = body:gsub("&nbsp;", " ");
body = body:gsub("\n", "");
body = body:gsub("\n", "");
body = body:gsub("'", "&#39;");
body = body:gsub([["]], "&#34;");
body = trim(body);
--send the email
if (string.len(attachment) > 4) then
cmd = "luarun email.lua "..email.." "..email.." '"..headers.."' '"..subject.."' '"..body.."' '"..attachment.."'";
else
cmd = "luarun email.lua "..email.." "..email.." '"..headers.."' '"..subject.."' '"..body.."'";
end
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] cmd: " .. cmd .. "\n");
end
result = api:executeString(cmd);
end
end
--define the session hangup
function session_hangup_hook()
--get the session variables
conference_session_detail_uuid = api:executeString("create_uuid");
--conference_name = session:getVariable("conference_name");
conference_session_uuid = session:getVariable("conference_uuid");
--conference_recording = session:getVariable("conference_recording");
conference_moderator = session:getVariable("conference_moderator");
default_language = session:getVariable("default_language");
default_dialect = session:getVariable("default_dialect");
--recording = session:getVariable("recording");
domain_name = session:getVariable("domain_name");
--set the end epoch
end_epoch = os.time();
--connect to the database
dbh = Database.new('system');
--get the conference sessions
if (conference_session_uuid) then
local sql = [[SELECT count(*) as num_rows
FROM v_conference_sessions
WHERE conference_session_uuid = :conference_session_uuid]];
local params = {conference_session_uuid = conference_session_uuid};
dbh:query(sql, params, function(row)
num_rows = string.lower(row["num_rows"]);
end);
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference center] SQL: " .. sql .. "; params:" .. json.encode(params) .. "; Rows: "..num_rows.."\n");
end
if (tonumber(num_rows) == 0) then
local sql = {}
table.insert(sql, "INSERT INTO v_conference_sessions ");
table.insert(sql, "(");
table.insert(sql, "conference_session_uuid, ");
table.insert(sql, "domain_uuid, ");
table.insert(sql, "meeting_uuid, ");
--if (conference_recording) then
-- table.insert(sql, "recording, ");
--end
--if (wait_mod) then
-- table.insert(sql, "wait_mod, ");
--end
--table.insert(sql, "start_epoch, ");
table.insert(sql, "profile ");
table.insert(sql, ") ");
table.insert(sql, "VALUES ");
table.insert(sql, "( ");
table.insert(sql, ":conference_session_uuid, ");
table.insert(sql, ":domain_uuid, ");
table.insert(sql, ":meeting_uuid, ");
--if (conference_recording) then
-- table.insert(sql, ":conference_recording, ");
--end
--if (wait_mod) then
-- table.insert(sql, ":wait_mod, ");
--end
--table.insert(sql, ":start_epoch, ");
table.insert(sql, ":profile ");
table.insert(sql, ") ");
sql = table.concat(sql, "\n");
local params = {
conference_session_uuid = conference_session_uuid;
domain_uuid = domain_uuid;
meeting_uuid = meeting_uuid;
-- conference_recording = conference_recording;
-- wait_mod = wait_mod;
-- start_epoch = start_epoch;
profile = profile;
};
dbh:query(sql, params);
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference center] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
end
end
--add the conference sessions details
if (conference_session_uuid) then
local sql = {}
table.insert(sql, "INSERT INTO v_conference_session_details ");
table.insert(sql, "(");
table.insert(sql, "conference_session_detail_uuid, ");
table.insert(sql, "domain_uuid, ");
table.insert(sql, "conference_session_uuid, ");
table.insert(sql, "meeting_uuid, ");
table.insert(sql, "username, ");
table.insert(sql, "caller_id_name, ");
table.insert(sql, "caller_id_number, ");
table.insert(sql, "network_addr, ");
table.insert(sql, "uuid, ");
if (conference_moderator) then
table.insert(sql, "moderator, ");
end
table.insert(sql, "start_epoch, ");
table.insert(sql, "end_epoch ");
table.insert(sql, ") ");
table.insert(sql, "VALUES ");
table.insert(sql, "( ");
table.insert(sql, ":conference_session_detail_uuid, ");
table.insert(sql, ":domain_uuid, ");
table.insert(sql, ":conference_session_uuid, ");
table.insert(sql, ":meeting_uuid, ");
table.insert(sql, ":username, ");
table.insert(sql, ":caller_id_name, ");
table.insert(sql, ":caller_id_number, ");
table.insert(sql, ":network_addr, ");
table.insert(sql, ":uuid, ");
if (conference_moderator) then
table.insert(sql, ":conference_moderator, ");
end
table.insert(sql, ":start_epoch, ");
table.insert(sql, ":end_epoch ");
table.insert(sql, ") ");
sql = table.concat(sql, "\n");
local params = {
conference_session_detail_uuid = conference_session_detail_uuid;
domain_uuid = domain_uuid;
conference_session_uuid = conference_session_uuid;
meeting_uuid = meeting_uuid;
username = username;
caller_id_name = caller_id_name;
caller_id_number = caller_id_number;
network_addr = network_addr;
uuid = uuid;
conference_moderator = conference_moderator;
start_epoch = start_epoch;
end_epoch = end_epoch;
};
dbh:query(sql, params);
end
--if the conference is empty
if (conference_session_uuid) then
cmd = "conference "..meeting_uuid.."@"..domain_name.." xml_list";
result = trim(api:executeString(cmd));
if (string.sub(result, -9) == "not found") then
--get the conference start_epoch
local sql = [[SELECT start_epoch
FROM v_conference_session_details
WHERE conference_session_uuid = :conference_session_uuid
ORDER BY start_epoch ASC
LIMIT 1]];
local params = {conference_session_uuid = conference_session_uuid};
dbh:query(sql, params, function(row)
start_epoch = string.lower(row["start_epoch"]);
end);
--freeswitch.consoleLog("notice", "[conference center] <conference_start_epoch> sql: " .. sql .. "; params:" .. json.encode(params) .. "\n");
--set the conference_recording
conference_recording = recordings_dir.."/archive/"..os.date("%Y", start_epoch).."/"..os.date("%b", start_epoch).."/"..os.date("%d", start_epoch) .."/"..conference_session_uuid;
freeswitch.consoleLog("notice", "[conference center] conference_recording: "..conference_recording.."\n");
--conference has ended set the end_epoch
local sql = {}
table.insert(sql, "update v_conference_sessions set ");
table.insert(sql, "recording = :conference_recording, ");
table.insert(sql, "start_epoch = :start_epoch, ");
table.insert(sql, "end_epoch = :end_epoch ");
table.insert(sql, "where conference_session_uuid = :conference_session_uuid ");
sql = table.concat(sql, "\n");
local params = {
conference_recording = conference_recording;
start_epoch = start_epoch;
end_epoch = end_epoch;
conference_session_uuid = conference_session_uuid;
};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference center] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--convert the wav to an mp3
if (record == "true") then
--cmd = "sox "..conference_recording..".wav -r 16000 -c 1 "..conference_recording..".mp3";
cmd = "/usr/bin/lame -b 32 --resample 8 -a "..conference_recording..".wav "..conference_recording..".mp3";
freeswitch.consoleLog("notice", "[conference center] cmd: " .. cmd .. "\n");
os.execute(cmd);
--if (file_exists(conference_recording..".mp3")) then
-- cmd = "rm "..conference_recording..".wav";
-- os.execute(cmd);
--end
end
--send the email addresses
--sql = [[SELECT c.contact_email FROM v_users as u, v_meeting_users as m, v_contacts as c
-- WHERE m.domain_uuid = ']] .. domain_uuid ..[['
-- AND u.user_uuid = m.user_uuid
-- AND m.meeting_uuid = ']] .. meeting_uuid ..[['
-- and u.contact_uuid = c.contact_uuid]];
--if (debug["sql"]) then
-- freeswitch.consoleLog("notice", "[conference center] <email> SQL: " .. sql .. "\n");
--end
--status = dbh:query(sql, function(row)
-- if (row["contact_email"] ~= nil) then
-- contact_email = string.lower(row["contact_email"]);
-- if (string.len(contact_email) > 3) then
-- freeswitch.consoleLog("notice", "[conference center] contact_email: " .. contact_email .. "\n");
-- if (record == "true") then
-- if (file_exists(conference_recording..".wav")) then
-- send_email(contact_email, "", default_language, default_dialect);
-- end
-- end
-- end
-- end
--end);
end
end
--close the database connection
if (conference_session_uuid) then
dbh:release();
end
end
--make sure the session is ready
if (session:ready()) then
--answer the call
session:preAnswer();
--set the session sleep
session:sleep(1000);
--get session variables
sounds_dir = session:getVariable("sounds_dir");
hold_music = session:getVariable("hold_music");
domain_name = session:getVariable("domain_name");
pin_number = session:getVariable("pin_number");
domain_uuid = session:getVariable("domain_uuid");
destination_number = session:getVariable("destination_number");
caller_id_number = session:getVariable("caller_id_number");
--freeswitch.consoleLog("notice", "[conference center] destination_number: " .. destination_number .. "\n");
--freeswitch.consoleLog("notice", "[conference center] caller_id_number: " .. caller_id_number .. "\n");
--add the domain name to the recordings directory
recordings_dir = recordings_dir .. "/"..domain_name;
--set the sounds path for the language, dialect and voice
default_language = session:getVariable("default_language");
default_dialect = session:getVariable("default_dialect");
default_voice = session:getVariable("default_voice");
if (not default_language) then default_language = 'en'; end
if (not default_dialect) then default_dialect = 'us'; end
if (not default_voice) then default_voice = 'callie'; end
--get the domain_uuid
if (domain_name ~= nil and domain_uuid == nil) then
local sql = "SELECT domain_uuid FROM v_domains ";
sql = sql .. "WHERE domain_name = :domain_name ";
local params = {domain_name = domain_name};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference center] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(rows)
domain_uuid = string.lower(rows["domain_uuid"]);
end);
end
--conference center details
local sql = [[SELECT * FROM v_conference_centers
WHERE domain_uuid = :domain_uuid
AND conference_center_extension = :destination_number]];
local params = {domain_uuid = domain_uuid, destination_number = destination_number};
dbh:query(sql, params, function(row)
conference_center_uuid = string.lower(row["conference_center_uuid"]);
conference_center_greeting = row["conference_center_greeting"];
end);
if (conference_center_greeting == '') then
conference_center_greeting = sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-pin.wav";
end
--connect to the switch database
local dbh_switch = Database.new('switch')
--check if someone has already joined the conference
local_hostname = trim(api:execute("switchname", ""));
freeswitch.consoleLog("notice", "[conference center] local_hostname is " .. local_hostname .. "\n");
sql = "SELECT hostname FROM channels WHERE application = 'conference' "
.. "AND dest = :destination_number AND cid_num <> :caller_id_number LIMIT 1";
params = {destination_number = destination_number, caller_id_number = caller_id_number};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference center] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh_switch:query(sql, params, function(rows)
conference_hostname = rows["hostname"];
end);
--close the database connection
dbh_switch:release();
--if conference hosntame exist, then we bridge there
if (conference_hostname ~= nil) then
freeswitch.consoleLog("notice", "[conference center] conference_hostname is " .. conference_hostname .. "\n");
if (conference_hostname ~= local_hostname) then
session:execute("bridge","sofia/internal/" .. destination_number .. "@" .. domain_name .. ";fs_path=sip:" .. conference_hostname);
end
end
--call not bridged, so we answer
session:answer();
--set the hangup hook function
session:setHangupHook("session_hangup_hook");
--add the domain to the recording directory
freeswitch.consoleLog("notice", "[conference center] domain_count: " .. domain_count .. "\n");
--sounds
enter_sound = "tone_stream://v=-20;%(100,1000,100);v=-20;%(90,60,440);%(90,60,620)";
exit_sound = "tone_stream://v=-20;%(90,60,620);/%(90,60,440)";
--get the variables
username = session:getVariable("username");
caller_id_name = session:getVariable("caller_id_name");
caller_id_number = session:getVariable("caller_id_number");
callee_id_name = session:getVariable("callee_id_name");
callee_id_number = session:getVariable("callee_id_number");
dialplan = session:getVariable("dialplan");
network_addr = session:getVariable("network_addr");
uuid = session:getVariable("uuid");
--context = session:getVariable("context");
chan_name = session:getVariable("chan_name");
--define the function get_pin_number
function get_pin_number(domain_uuid, prompt_audio_file)
--if the pin number is provided then require it
if (not pin_number) then
min_digits = 2;
max_digits = 20;
max_tries = 1;
digit_timeout = 5000;
pin_number = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", prompt_audio_file, "", "\\d+");
end
--use the pin_number to find the conference room
if (pin_number ~= "") then
local sql = [[SELECT * FROM v_conference_rooms as r, v_meetings as m
WHERE r.domain_uuid = :domain_uuid
AND r.meeting_uuid = m.meeting_uuid
AND m.domain_uuid = :domain_uuid
AND (m.moderator_pin = :pin_number or m.participant_pin = :pin_number)
AND r.enabled = 'true'
AND m.enabled = 'true'
AND (
( r.start_datetime <> '' AND r.start_datetime is not null AND r.start_datetime <= :timestam ) OR
( r.start_datetime = '' OR r.start_datetime is null )
)
AND (
( r.stop_datetime <> '' AND r.stop_datetime is not null AND r.stop_datetime > :timestam ) OR
( r.stop_datetime = '' OR r.stop_datetime is null )
) ]];
local params = {
domain_uuid = domain_uuid;
pin_number = pin_number;
timestam = os.date("%Y-%m-%d %X");
};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference center] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
conference_room_uuid = string.lower(row["conference_room_uuid"]);
end);
end
--if the conference room was not found then return nil
if (conference_room_uuid == nil) then
return nil;
else
return pin_number;
end
end
--get the pin
pin_number = session:getVariable("pin_number");
pin_number = get_pin_number(domain_uuid, conference_center_greeting);
if (pin_number == nil) then
pin_number = get_pin_number(domain_uuid, conference_center_greeting);
end
if (pin_number == nil) then
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-bad-pin.wav");
pin_number = get_pin_number(domain_uuid, conference_center_greeting);
end
if (pin_number == nil) then
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-bad-pin.wav");
pin_number = get_pin_number(domain_uuid, conference_center_greeting);
end
if (pin_number ~= nil) then
local sql = [[SELECT * FROM v_conference_rooms as r, v_meetings as m
WHERE r.domain_uuid = :domain_uuid
AND r.meeting_uuid = m.meeting_uuid
AND r.conference_center_uuid = :conference_center_uuid
AND m.domain_uuid = :domain_uuid
AND (m.moderator_pin = :pin_number or m.participant_pin = :pin_number)
AND r.enabled = 'true'
AND m.enabled = 'true'
]];
local params = {
domain_uuid = domain_uuid;
conference_center_uuid = conference_center_uuid;
pin_number = pin_number;
};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference center] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
conference_room_uuid = string.lower(row["conference_room_uuid"]);
meeting_uuid = string.lower(row["meeting_uuid"]);
record = string.lower(row["record"]);
profile = string.lower(row["profile"]);
max_members = row["max_members"];
wait_mod = row["wait_mod"];
moderator_pin = row["moderator_pin"];
participant_pin = row["participant_pin"];
announce = row["announce"];
mute = row["mute"];
sounds = row["sounds"];
created = row["created"];
created_by = row["created_by"];
enabled = row["enabled"];
description = row["description"];
return pin_number;
end);
freeswitch.consoleLog("INFO","conference_room_uuid: " .. conference_room_uuid .. "\n");
end
--set the member type
if (pin_number == moderator_pin) then
member_type = "moderator";
end
if (pin_number == participant_pin) then
member_type = "participant";
end
--close the database connection
dbh:release();
--set the meeting uuid
if (meeting_uuid) then
session:setVariable("meeting_uuid", meeting_uuid);
end
if (meeting_uuid == nil) then
--invalid pin number
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-bad-pin.wav");
session:hangup("NORMAL_CLEARING");
else
if (meeting_uuid) then
--check if the conference exists
cmd = "conference "..meeting_uuid.."@"..domain_name.." xml_list";
result = trim(api:executeString(cmd));
if (string.sub(result, -9) == "not found") then
conference_exists = false;
else
conference_exists = true;
end
--check if the conference is locked
if (string.find(result, [[locked="true"]]) == nil) then
conference_locked = false;
else
conference_locked = true;
end
--set a conference parameter
if (max_members ~= nil) then
if (tonumber(max_members) > 0) then
--max members must be 2 or more
session:execute("set","conference_max_members="..max_members);
if (conference_exists) then
cmd = "conference "..meeting_uuid.."@"..domain_name.." get count";
count = trim(api:executeString(cmd));
if (count ~= nil) then
if (tonumber(count) >= tonumber(max_members)) then
session:execute("playback", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-locked.wav");
session:hangup("CALL_REJECTED");
end
end
end
end
end
--announce the caller
if (conference_locked) then
announce = "false";
end
if (announce == "true") then
--prompt for the name of the caller
session:execute("playback", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-say_name.wav");
session:execute("playback", "tone_stream://v=-7;%%(500,0,500.0)");
--record the response
max_len_seconds = 5;
silence_threshold = "500";
silence_secs = "3";
session:recordFile(temp_dir:gsub("\\","/") .. "/conference-"..uuid..".wav", max_len_seconds, silence_threshold, silence_secs);
end
--play a message that the conference is being a recorded
--if (record == "true") then
--session:execute("playback", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-recording_started.wav");
--end
--wait for moderator
if (wait_mod == "true") then
if (conference_exists) then
--continue
else
if (member_type == "participant") then
profile = "wait-mod";
end
end
end
--set the exit sound
if (sounds == "true") then
session:execute("set","conference_exit_sound="..exit_sound);
end
--set flags and moderator controls
if (wait_mod == "true") then
if (member_type == "participant") then
flags = flags .. "wait-mod";
end
end
if (mute == "true") then
if (member_type == "participant") then
flags = flags .. "|mute";
end
end
if (member_type == "moderator") then
--set as the moderator
flags = flags .. "|moderator";
--when the moderator leaves end the conference
--flags = flags .. "|endconf";
--set the moderator controls
session:execute("set","conference_controls=moderator");
end
--get the conference xml_list
cmd = "conference "..meeting_uuid.."@"..domain_name.." xml_list";
freeswitch.consoleLog("INFO","" .. cmd .. "\n");
result = trim(api:executeString(cmd));
--get the content to the <conference> tag
result = string.match(result,[[<conference (.-)>]],1);
--get the uuid out of the xml tag contents
if (result ~= nil) then
conference_session_uuid = string.match(result,[[uuid="(.-)"]],1);
end
--log entry
if (conference_session_uuid ~= nil) then
freeswitch.consoleLog("INFO","conference_session_uuid: " .. conference_session_uuid .. "\n");
end
--set the start epoch
start_epoch = os.time();
--set the recording variable
if (conference_session_uuid ~= nil) then
if (record == "true") then
recordings_dir_2 = recordings_dir.."/archive/"..os.date("%Y", start_epoch).."/"..os.date("%b", start_epoch).."/"..os.date("%d", start_epoch);
mkdir(recordings_dir_2);
recording = recordings_dir_2.."/"..conference_session_uuid;
session:execute("set","recording="..recording);
session:execute("set","conference_session_uuid="..conference_session_uuid);
end
end
--record the conference
if (record == "true") then
--play a message that the conference is being a recorded
session:execute("playback", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-recording_started.wav");
--play a message that the conference is being a recorded
--cmd = "conference "..meeting_uuid.."@"..domain_name.." play "..sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-recording_started.wav";
--freeswitch.consoleLog("notice", "[conference center] ".. cmd .."\n");
--response = api:executeString(cmd);
end
--announce the caller
if (announce == "true") then
--announce the caller - play the recording
cmd = "conference "..meeting_uuid.."@"..domain_name.." play " .. temp_dir:gsub("\\", "/") .. "/conference-"..uuid..".wav";
--freeswitch.consoleLog("notice", "[conference center] ".. cmd .."\n");
response = api:executeString(cmd);
--play has entered the conference
cmd = "conference "..meeting_uuid.."@"..domain_name.." play "..sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-has_joined.wav";
--freeswitch.consoleLog("notice", "[conference center] ".. cmd .."\n");
response = api:executeString(cmd);
else
if (not conference_locked) then
if (sounds == "true") then
cmd = "conference "..meeting_uuid.."@"..domain_name.." play "..enter_sound;
response = api:executeString(cmd);
end
end
end
--get the conference member count
cmd = "conference "..meeting_uuid.."@"..domain_name.." list count";
--freeswitch.consoleLog("notice", "[conference center] cmd: ".. cmd .."\n");
member_count = api:executeString(cmd);
if (string.sub(trim(member_count), -9) == "not found") then
member_count = "0";
end
--play member count
if (member_count == "1") then
--there is one other member in this conference
session:execute("playback", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-one_other_member_conference.wav");
elseif (member_count == "0") then
--conference profile defines the alone sound file
else
--say the count
session:execute("say", default_language.." number pronounced "..member_count);
--members in this conference
session:execute("playback", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-members_in_conference.wav");
end
--record the conference
if (record == "true") then
cmd="sched_api +5 none lua "..scripts_dir.."/app/conference_center/resources/scripts/start_recording.lua "..meeting_uuid.." "..domain_name.." "..record_ext;
api:executeString(cmd);
end
--send the call to the conference
cmd = meeting_uuid.."@"..domain_name.."@"..profile.."+flags{".. flags .."}";
freeswitch.consoleLog("INFO","[conference center] conference " .. cmd .. "\n");
session:execute("conference", cmd);
end
end
end

View File

@@ -0,0 +1,16 @@
--get the argv values
script_name = argv[0];
--options all, last, non_moderator, member_id
data = argv[1];
--prepare the api object
api = freeswitch.API();
--get the session variables
conference_name = session:getVariable("conference_name");
--send the conferenc mute command
cmd = "conference " .. conference_name .. " mute " .. data;
response = api:executeString(cmd);

View File

@@ -0,0 +1,52 @@
--get the scripts directory and include the config.lua
require "resources.functions.config";
--additional includes
require "resources.functions.file_exists";
require "resources.functions.trim";
require "resources.functions.mkdir";
--get the argv values
script_name = argv[0];
--options all, last, non_moderator, member_id
meeting_uuid = argv[1];
domain_name = argv[2];
record_ext = argv[3];
--prepare the api object
api = freeswitch.API();
--check if the conference exists
cmd = "conference "..meeting_uuid.."@"..domain_name.." xml_list";
freeswitch.consoleLog("INFO","" .. cmd .. "\n");
result = trim(api:executeString(cmd));
if (string.sub(result, -9) == "not found") then
conference_exists = false;
else
conference_exists = true;
end
--start the recording
if (conference_exists) then
--get the conference session uuid
result = string.match(result,[[<conference (.-)>]],1);
conference_session_uuid = string.match(result,[[uuid="(.-)"]],1);
freeswitch.consoleLog("INFO","[start-recording] conference_session_uuid: " .. conference_session_uuid .. "\n");
--get the current time
start_epoch = os.time();
--add the domain name to the recordings directory
recordings_dir = recordings_dir .. "/"..domain_name;
recordings_dir = recordings_dir.."/archive/"..os.date("%Y", start_epoch).."/"..os.date("%b", start_epoch).."/"..os.date("%d", start_epoch);
mkdir(recordings_dir);
recording = recordings_dir.."/"..conference_session_uuid;
--send a command to record the conference
if (not file_exists(recording.."."..record_ext)) then
cmd = "conference "..meeting_uuid.."@"..domain_name.." record "..recording.."."..record_ext;
freeswitch.consoleLog("notice", "[start-recording] cmd: " .. cmd .. "\n");
response = api:executeString(cmd);
end
end

View File

@@ -0,0 +1,16 @@
--get the argv values
script_name = argv[0];
--options all, last, non_moderator, member_id
data = argv[1];
--prepare the api object
api = freeswitch.API();
--get the session variables
conference_name = session:getVariable("conference_name");
--send the conferenc mute command
cmd = "conference " .. conference_name .. " unmute " .. data;
response = api:executeString(cmd);

View File

@@ -0,0 +1,7 @@
<font face="arial">
<p>The conference moderator pin is ${moderator_pin} and it ended on ${conference_date_end}.</p>
<p>You can download your conference recording by clicking <a href='${link_address}/app/conference_centers/conference_session_details.php?uuid=${conference_uuid}'>here</a>.</p>
<hr noshade="noshade" size="1"/>
Moderator PIN: ${moderator_pin}
Ended: ${conference_date_end}<br/>
</font>

View File

@@ -0,0 +1 @@
New recording available for conference ${moderator_pin} ${conference_date_end}.

View File

@@ -0,0 +1,107 @@
-- FusionPBX
-- Version: MPL 1.1
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
-- The Original Code is FusionPBX
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Portions created by the Initial Developer are Copyright (C) 2014
-- the Initial Developer. All Rights Reserved.
--add functions
require "resources.functions.file_exists";
require "resources.functions.trim";
--set the api object
api = freeswitch.API();
--windows (/ad show only directories)
--dir "C:\program files\fusionpbx" /b
--unix
-- dir /usr/local/freeswitch/scripts -1
--set local variables
uuid = session:getVariable("uuid");
context = session:getVariable("context");
destination_number = session:getVariable("destination_number");
call_direction = session:getVariable("call_direction");
domain_name = session:getVariable("domain_name");
--determine the call direction
if (call_direction == nil) then
--get the call directory
if (context == "public") then
call_direction = "inbound";
else
if (string.sub(context, 0, 9) == "outbound@") then
call_direction = "outbound";
else
if (string.len(destination_number) > 6) then
call_direction = "outbound";
else
call_direction = "local";
end
end
end
--set the call direction as a session variable
session:setVariable("call_direction", call_direction);
--freeswitch.consoleLog("notice", "[app:dialplan] set call_direction " .. call_direction .. "\n");
end
--determine the directory to include
if (context == "public") then
dialplan_dir = "inbound";
else
if (string.sub(context, 0, 9) == "outbound@") then
dialplan_dir = "outbound";
else
if (string.len(destination_number) > 6) then
dialplan_dir = "outbound";
else
dialplan_dir = "local";
end
end
end
--include before
result = assert (io.popen ("dir " ..scripts_dir.."/app/dialplan/resources/before /b -1"));
for file in result:lines() do
if (string.sub(file, -4) == ".lua") then
if file_exists(scripts_dir.."/app/dialplan/resources/before/"..file) then
dofile(scripts_dir.."/app/dialplan/resources/before/"..file);
end
--freeswitch.consoleLog("notice", "[app:dialplan] lua: before/" .. file .. "\n");
end
end
--include the dialplans
result = assert (io.popen ("dir " ..scripts_dir.."/app/dialplan/resources/"..dialplan_dir.." /b -1"));
for file in result:lines() do
if (string.sub(file, -4) == ".lua") then
if file_exists(scripts_dir.."/app/dialplan/resources/"..dialplan_dir.."/"..file) then
dofile(scripts_dir.."/app/dialplan/resources/"..dialplan_dir.."/"..file);
end
--freeswitch.consoleLog("notice", "[app:dialplan] lua: "..dialplan_dir.."/" .. file .. "\n");
end
end
--include after
result = assert (io.popen ("dir " ..scripts_dir.."/app/dialplan/resources/after /b -1"));
for file in result:lines() do
if (string.sub(file, -4) == ".lua") then
if file_exists(scripts_dir.."/app/dialplan/resources/after/"..file) then
dofile(scripts_dir.."/app/dialplan/resources/after/"..file);
end
--freeswitch.consoleLog("notice", "[app:dialplan] lua: after/" .. file .. "\n");
end
end

View File

@@ -0,0 +1,87 @@
--set the user_exists to true or false
if (context ~= "public") then
--set the default
record_ext = "wav";
--get record_ext
if (session:getVariable("record_ext")) then
record_ext = session:getVariable("record_ext");
end
--set the recording path
path = recordings_dir
if (domain_count > 1) then
path = path.."/"..domain_name;
end
path = path.."/archive/"..(os.date("%Y")).."/"..(os.date("%b")).."/"..(os.date("%d"));
--add functions
require "resources.functions.mkdir";
--make sure the recordings directory exists
mkdir(path);
--check whether to record the to user
if (user_exists == "true") then
if (session:ready()) then
--get user_variable -> record
cmd = "user_data ".. destination_number .."@"..domain_name.." var user_record";
user_record = trim(api:executeString(cmd));
--freeswitch.consoleLog("notice", "[app:dialplan] " .. cmd .. "\n");
--set the record_session variable
--uuid_record,<uuid> [start|stop|mask|unmask] <path> [<limit>],Record session audio,mod_commands
if (user_record == "all") then
record_session = true;
end
if (user_record == "inbound" and call_direction == "inbound") then
record_session = true;
end
if (user_record == "outbound" and call_direction == "outbound") then
record_session = true;
end
if (user_record == "local" and call_direction == "local") then
record_session = true;
end
end
end
--check whether to record the from user
if (not record_session) then
--get the from user
sip_from_user = session:getVariable("sip_from_user");
sip_from_uri = session:getVariable("sip_from_uri");
sip_from_host = session:getVariable("sip_from_host");
--get user exists true or false
cmd = "user_exists id ".. sip_from_user .." "..sip_from_host;
user_exists = trim(api:executeString(cmd));
--get user_variable -> record
cmd = "user_data ".. sip_from_user .."@"..sip_from_host.." var user_record";
freeswitch.consoleLog("notice", "[app:dialplan] " .. cmd .. "\n");
user_record = trim(api:executeString(cmd));
--set the record_session variable
--uuid_record,<uuid> [start|stop|mask|unmask] <path> [<limit>],Record session audio,mod_commands
if (user_record == "all") then
record_session = true;
end
if (user_record == "inbound" and call_direction == "inbound") then
record_session = true;
end
if (user_record == "outbound" and call_direction == "outbound") then
record_session = true;
end
if (user_record == "local" and call_direction == "local") then
record_session = true;
end
end
--record the session
if (record_session) then
cmd = "uuid_record "..uuid.." start "..path.."/"..uuid.."."..record_ext;
session:execute("set", "api_on_answer="..cmd);
--freeswitch.consoleLog("notice", "[app:dialplan] "..cmd.."\n");
end
end

View File

@@ -0,0 +1,9 @@
--set the user_exists to true or false
if (context ~= "public") then
if (user_exists == nil) then
--get user exists true or false
cmd = "user_exists id ".. destination_number .." "..domain_name;
user_exists = trim(api:executeString(cmd));
--freeswitch.consoleLog("notice", "[app:dialplan] "..cmd.." user_exists: "..user_exists.."\n");
end
end

View File

@@ -0,0 +1,116 @@
-- FusionPBX
-- Version: MPL 1.1
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
-- The Original Code is FusionPBX
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Portions created by the Initial Developer are Copyright (C) 2014-2019
-- the Initial Developer. All Rights Reserved.
--set defaults
expire = {}
expire["get_domain"] = "3600";
source = "";
--include cache library
local cache = require "resources.functions.cache"
--get the variables
local destination_number = session:getVariable("destination_number");
--remove the plus if it exists
if (string.sub(destination_number, 0, 1) == "+") then
destination_number = string.sub(destination_number, 2, (string.len(destination_number)));
end
--connect to the database
require "resources.functions.database_handle";
dbh = database_handle('system');
--get the cache
if (cache.support() and destination_number) then
local key, err = "app:dialplan:inbound:get_domain:" .. destination_number;
cache, err = cache.get(key);
end
--get the ring group destinations
if (cache == "-ERR NOT FOUND") then
sql = "SELECT d.domain_uuid, d.domain_name, n.destination_number, n.destination_context "
sql = sql .. "FROM v_destinations as n, v_domains as d "
sql = sql .. "WHERE n.destination_number = '"..destination_number.."' "
sql = sql .. "AND n.destination_type = 'inbound' "
sql = sql .. "AND n.domain_uuid = d.domain_uuid "
--freeswitch.consoleLog("notice", "SQL:" .. sql .. "\n");
dbh:query(sql, function(row)
--set the local variables
domain_uuid = row.domain_uuid;
domain_name = row.domain_name;
--local destination_number = row.destination_number;
--local destination_context = row.destination_context;
--set the cache
domain = "domain_uuid=" .. domain_uuid .. "&domain_name=" .. domain_name;
if cache.support() then
local key = app:dialplan:inbound:get_domain:" .. destination_number .. " '"..domain.."' "..expire["get_domain"];
if debug['cache'] then
freeswitch.consoleLog("notice", "[dialplan][cache] set key: " .. key .. "\n")
end
local ok, err = cache.set(key, XML_STRING, expire["directory"])
if debug["cache"] and not ok then
freeswitch.consoleLog("warning", "[dialplan][cache] set key: " .. key .. " fail: " .. tostring(err) .. "\n");
end
end
--set the source
source = "database";
end);
else
--add the function
require "resources.functions.explode";
--parse the cache
array = explode("&", cache);
--define the array/table and variables
local var = {}
local key = "";
local value = "";
--parse the cache
key_pairs = explode("&", cache);
for k,v in pairs(key_pairs) do
f = explode("=", v);
key = f[1];
value = f[2];
var[key] = value;
end
--set the variables
domain_uuid = var["domain_uuid"];
domain_name = var["domain_name"];
--set the source
source = "cache";
end
if (domain_name ~= nil) then
--set the call direction as a session variable
session:setVariable("domain_name", domain_name);
session:setVariable("domain", domain_name);
session:setVariable("domain_uuid", domain_uuid);
--send information to the console
freeswitch.consoleLog("notice", "[app:dialplan:inbound:get_domain] " .. cache .. " source: ".. source .."\n");
end

View File

@@ -0,0 +1,132 @@
--
-- FusionPBX
-- Version: MPL 1.1
--
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
--
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
--
-- The Original Code is FusionPBX
--
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Copyright (C) 2010-2015
-- the Initial Developer. All Rights Reserved.
--
-- Contributor(s):
-- KonradSC <konrd@yahoo.com>
--
-- Instructions:
-- Simply add an action to your emergency outbound route. Make sure
-- the order is a lower number than your bridge statement.
--
-- Tag: action
-- Type: lua
-- Data: app.lua emergency_notify [to_email_address] [from_email_address]
--
--debug
debug["info"] = false;
debug["sql"] = false;
--include config.lua
require "resources.functions.config";
require "resources.functions.explode";
require "resources.functions.trim";
require "resources.functions.base64";
--get arguments
to_email = argv[2];
from_email = argv[3];
--check the missed calls
function send_mail()
--set the sounds path for the language, dialect and voice
default_language = session:getVariable("default_language");
default_dialect = session:getVariable("default_dialect");
default_voice = session:getVariable("default_voice");
if (not default_language) then default_language = 'en'; end
if (not default_dialect) then default_dialect = 'us'; end
if (not default_voice) then default_voice = 'callie'; end
--prepare the files
file_subject = scripts_dir.."/app/emergency_notify/resources/templates/"..default_language.."/"..default_dialect.."/email_subject.tpl";
file_body = scripts_dir.."/app/emergency_notify/resources/templates/"..default_language.."/"..default_dialect.."/email_body.tpl";
if (not file_exists(file_subject)) then
file_subject = scripts_dir.."/app/emergency_notify/resources/templates/en/us/email_subject.tpl";
file_body = scripts_dir.."/app/emergency_notify/resources/templates/en/us/email_body.tpl";
end
--prepare the headers
headers = '{"X-FusionPBX-Domain-UUID":"'..domain_uuid..'",';
headers = headers..'"X-FusionPBX-Domain-Name":"'..domain_name..'",';
headers = headers..'"X-FusionPBX-Call-UUID":"'..uuid..'",';
headers = headers..'"X-FusionPBX-Email-Type":"emergency_call"}';
--prepare the subject
local f = io.open(file_subject, "r");
local subject = f:read("*all");
f:close();
subject = subject:gsub("${caller_id_name}", caller_id_name);
subject = subject:gsub("${caller_id_number}", caller_id_number);
subject = subject:gsub("${sip_to_user}", sip_to_user);
subject = subject:gsub("${caller_destination}", caller_destination);
subject = trim(subject);
subject = '=?utf-8?B?'..base64.encode(subject)..'?=';
--prepare the body
local f = io.open(file_body, "r");
local body = f:read("*all");
f:close();
body = body:gsub("${caller_id_name}", caller_id_name);
body = body:gsub("${caller_id_number}", caller_id_number);
body = body:gsub("${sip_to_user}", sip_to_user);
body = body:gsub("${caller_destination}", caller_destination);
body = body:gsub(" ", "&nbsp;");
body = body:gsub("%s+", "");
body = body:gsub("&nbsp;", " ");
body = body:gsub("\n", "");
body = body:gsub("\n", "");
body = body:gsub("'", "&#39;");
body = body:gsub([["]], "&#34;");
body = trim(body);
--send the email
cmd = "luarun email.lua "..to_email.." "..from_email.." "..headers.." '"..subject.."' '"..body.."'";
if (debug["info"]) then
freeswitch.consoleLog("notice", "[emergency call] cmd: " .. cmd .. "\n");
end
api = freeswitch.API();
result = api:executeString(cmd);
end
--handle originate_disposition
if (session ~= nil and session:ready()) then
uuid = session:getVariable("uuid");
domain_uuid = session:getVariable("domain_uuid");
domain_name = session:getVariable("domain_name");
context = session:getVariable("context");
caller_id_name = session:getVariable("outbound_caller_id_name");
caller_id_number = session:getVariable("caller_id_number");
sip_to_user = session:getVariable("sip_to_user");
caller_destination = session:getVariable("caller_destination");
if (debug["info"] == true) then
freeswitch.consoleLog("INFO", "[emergency_notify] caller_id_number: " .. tostring(caller_id_number) .. "\n");
freeswitch.consoleLog("INFO", "[emergency_notify] caller_id_number: " .. tostring(caller_id_number) .. "\n");
freeswitch.consoleLog("INFO", "[emergency_notify] caller_destination: " .. tostring(caller_destination) .. "\n");
freeswitch.consoleLog("INFO", "[emergency_notify] to_email: " .. tostring(to_email) .. "\n");
freeswitch.consoleLog("INFO", "[emergency_notify] from_email: " .. tostring(from_email) .. "\n");
end
send_mail();
end

View File

@@ -0,0 +1 @@
${caller_id_name} &lt;${caller_id_number}&gt; made an emergency call to ${sip_to_user}

View File

@@ -0,0 +1 @@
${caller_id_name} <${caller_id_number}> Made An Emergency Call

View File

@@ -0,0 +1,192 @@
--
-- event_notify
-- Version: MPL 1.1
--
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
--
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
--
-- The Original Code is FusionPBX - event_notify
--
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Copyright (C) 2013 - 2018
-- the Initial Developer. All Rights Reserved.
--
-- Contributor(s):
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Errol Samuels <voiptology@gmail.com>
--define the explode function
require "resources.functions.explode";
--usage
--luarun app.lua event_notify internal reboot 1003@domain.fusionpbx.com yealink
--set the args as variables
profile = argv[2];
command = argv[3];
user = argv[4];
vendor = argv[5];
--log the args
--freeswitch.consoleLog("notice", "[event_notify] profile "..profile.."\n");
--freeswitch.consoleLog("notice", "[event_notify] command "..command.."\n");
--freeswitch.consoleLog("notice", "[event_notify] user "..user.."\n");
--freeswitch.consoleLog("notice", "[event_notify] vendor "..vendor.."\n");
--get the user and domain name from the user argv user@domain
user_table = explode("@",user);
user = user_table[1];
domain = user_table[2];
--create the event notify object
local event = freeswitch.Event('NOTIFY');
--add the headers
event:addHeader('profile', profile);
event:addHeader('user', user);
event:addHeader('host', domain);
event:addHeader('content-type', 'application/simple-message-summary');
--aastra
if (vendor == "aastra") then
if (command == "reboot") then
event:addHeader('event-string', 'check-sync;reboot=true');
end
if (command == "check_sync") then
event:addHeader('event-string', 'check-sync;reboot=true');
end
end
--cisco
if (vendor == "cisco") then
if (command == "reboot") then
event:addHeader('event-string', 'check-sync');
end
if (command == "check_sync") then
event:addHeader('event-string', 'check-sync');
end
end
--cisco-spa
if (vendor == "cisco-spa") then
if (command == "reboot") then
event:addHeader('event-string', 'reboot=true');
end
if (command == "check_sync") then
event:addHeader('event-string', 'reboot=true');
end
end
--digium
if (vendor == "digium") then
if (command == "reboot") then
event:addHeader('event-string', 'check-sync');
end
if (command == "check_sync") then
event:addHeader('event-string', 'check-sync');
end
end
--fanvil
if (vendor == "fanvil") then
if (command == "reboot") then
event:addHeader('event-string', 'check-sync;reboot=true');
end
if (command == "check_sync") then
event:addHeader('event-string', 'resync');
end
end
--grandstream
if (vendor == "grandstream") then
if (command == "reboot") then
event:addHeader('event-string', 'check-sync;reboot=true');
end
if (command == "check_sync") then
event:addHeader('event-string', 'resync');
end
end
--htek
if (vendor == "htek") then
if (command == "reboot") then
event:addHeader('event-string', 'check-sync;reboot=true');
end
if (command == "check_sync") then
event:addHeader('event-string', 'resync');
end
end
--sangoma
if (vendor == "sangoma") then
if (command == "reboot") then
event:addHeader('event-string', 'check-sync;reboot=true');
end
if (command == "check_sync") then
event:addHeader('event-string', 'resync');
end
end
--linksys
if (vendor == "linksys") then
if (command == "reboot") then
event:addHeader('event-string', 'reboot=true');
end
if (command == "check_sync") then
event:addHeader('event-string', 'reboot=true');
end
end
--panasonic
if (vendor == "panasonic") then
if (command == "reboot") then
event:addHeader('event-string', 'check-sync;reboot=true');
end
if (command == "check_sync") then
event:addHeader('event-string', 'check-sync;reboot=true');
end
end
--polycom
if (vendor == "polycom") then
if (command == "reboot") then
event:addHeader('event-string', 'check-sync');
end
if (command == "check_sync") then
event:addHeader('event-string', 'check-sync');
end
end
--snom
if (vendor == "snom") then
if (command == "reboot") then
event:addHeader('event-string', 'check-sync;reboot=true');
end
if (command == "check_sync") then
event:addHeader('event-string', 'check-sync;reboot=false');
end
end
--yealink
if (vendor == "yealink") then
if (command == "reboot") then
event:addHeader('event-string', 'check-sync;reboot=true');
end
if (command == "check_sync") then
event:addHeader('event-string', 'check-sync;reboot=false');
end
end
--send the event
event:fire();
--log the event
freeswitch.consoleLog("notice", "[event_notify] command "..command.." "..user.."@"..domain.." vendor "..tostring(vendor).."\n");

View File

@@ -0,0 +1,253 @@
--
-- FusionPBX
-- Version: MPL 1.1
--
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
--
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
--
-- The Original Code is FusionPBX
--
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Copyright (C) 2010-2018
-- the Initial Developer. All Rights Reserved.
--
-- Contributor(s):
-- Salvatore Caruso <salvatore.caruso@nems.it>
-- Riccardo Granchi <riccardo.granchi@nems.it>
-- Luis Daniel Lucio Quiroz <dlucio@okay.com.mx>
--debug
debug["info"] = false;
debug["sql"] = false;
--include config.lua
require "resources.functions.config";
require "resources.functions.explode";
require "resources.functions.trim";
require "resources.functions.base64";
--load libraries
require 'resources.functions.send_mail'
--check the missed calls
function missed()
if (missed_call_app ~= nil and missed_call_data ~= nil) then
if (missed_call_app == "email") then
--set the sounds path for the language, dialect and voice
default_language = session:getVariable("default_language");
default_dialect = session:getVariable("default_dialect");
default_voice = session:getVariable("default_voice");
if (not default_language) then default_language = 'en'; end
if (not default_dialect) then default_dialect = 'us'; end
if (not default_voice) then default_voice = 'callie'; end
--connect to the database
local Database = require "resources.functions.database";
local dbh = Database.new('system');
--get the templates
local sql = "SELECT * FROM v_email_templates ";
sql = sql .. "WHERE (domain_uuid = :domain_uuid or domain_uuid is null) ";
sql = sql .. "AND template_language = :template_language ";
sql = sql .. "AND template_category = 'missed' "
sql = sql .. "AND template_enabled = 'true' "
sql = sql .. "ORDER BY domain_uuid DESC "
local params = {domain_uuid = domain_uuid, template_language = default_language.."-"..default_dialect};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
subject = row["template_subject"];
body = row["template_body"];
end);
--prepare the headers
local headers = {}
headers["X-FusionPBX-Domain-UUID"] = domain_uuid;
headers["X-FusionPBX-Domain-Name"] = domain_name;
headers["X-FusionPBX-Call-UUID"] = uuid;
headers["X-FusionPBX-Email-Type"] = 'missed';
--prepare the subject
subject = subject:gsub("${caller_id_name}", caller_id_name);
subject = subject:gsub("${caller_id_number}", caller_id_number);
subject = subject:gsub("${sip_to_user}", sip_to_user);
subject = subject:gsub("${dialed_user}", dialed_user);
subject = trim(subject);
subject = '=?utf-8?B?'..base64.encode(subject)..'?=';
--prepare the body
body = body:gsub("${caller_id_name}", caller_id_name);
body = body:gsub("${caller_id_number}", caller_id_number);
body = body:gsub("${sip_to_user}", sip_to_user);
body = body:gsub("${dialed_user}", dialed_user);
body = body:gsub(" ", "&nbsp;");
body = body:gsub("%s+", "");
body = body:gsub("&nbsp;", " ");
body = body:gsub("\n", "");
body = body:gsub("\n", "");
body = body:gsub("'", "&#39;");
body = body:gsub([["]], "&#34;");
body = trim(body);
--send the emails
send_mail(headers,
missed_call_data,
{subject, body}
);
if (debug["info"]) then
freeswitch.consoleLog("notice", "[missed call] " .. caller_id_number .. "->" .. sip_to_user .. "emailed to " .. missed_call_data .. "\n");
end
end
end
end
--handle originate_disposition
if (session ~= nil and session:ready()) then
uuid = session:getVariable("uuid");
domain_uuid = session:getVariable("domain_uuid");
domain_name = session:getVariable("domain_name");
context = session:getVariable("context");
originate_disposition = session:getVariable("originate_disposition");
originate_causes = session:getVariable("originate_causes");
hangup_on_subscriber_absent = session:getVariable("hangup_on_subscriber_absent");
hangup_on_call_reject = session:getVariable("hangup_on_call_reject");
caller_id_name = session:getVariable("caller_id_name");
caller_id_number = session:getVariable("caller_id_number");
call_direction = session:getVariable("call_direction");
if (caller_direction == "local") then
caller_id_name = session:getVariable("effective_caller_id_name");
end
sip_to_user = session:getVariable("sip_to_user");
dialed_user = session:getVariable("dialed_user");
missed_call_app = session:getVariable("missed_call_app");
missed_call_data = session:getVariable("missed_call_data");
sip_code = session:getVariable("last_bridge_proto_specific_hangup_cause");
if (debug["info"] == true) then
freeswitch.consoleLog("INFO", "[failure_handler] originate_causes: " .. tostring(originate_causes) .. "\n");
freeswitch.consoleLog("INFO", "[failure_handler] originate_disposition: " .. tostring(originate_disposition) .. "\n");
freeswitch.consoleLog("INFO", "[failure_handler] hangup_on_subscriber_absent: " .. tostring(hangup_on_subscriber_absent) .. "\n");
freeswitch.consoleLog("INFO", "[failure_handler] hangup_on_call_reject: " .. tostring(hangup_on_call_reject) .. "\n");
freeswitch.consoleLog("INFO", "[failure_handler] sip_code: " .. tostring(sip_code) .. "\n");
end
if (originate_causes ~= nil) then
array = explode("|",originate_causes);
if (string.find(array[1], "USER_BUSY")) or (sip_code == "sip:486") then
originate_disposition = "USER_BUSY";
session:setVariable("originate_disposition", originate_disposition);
end
end
if (originate_disposition ~= nil) then
if (originate_disposition == 'USER_BUSY') then
--handle USER_BUSY
dialed_extension = session:getVariable("dialed_extension");
last_busy_dialed_extension = session:getVariable("last_busy_dialed_extension");
if (debug["info"] ) then
freeswitch.consoleLog("INFO", "[failure_handler] last_busy_dialed_extension: " .. tostring(last_busy_dialed_extension) .. "\n");
end
--transfer to the forward busy destination
if (dialed_extension ~= nil and dialed_extension ~= last_busy_dialed_extension) then
forward_busy_enabled = session:getVariable("forward_busy_enabled");
if (forward_busy_enabled == "true") then
forward_busy_destination = session:getVariable("forward_busy_destination");
if (forward_busy_destination ~= nil and string.len(forward_busy_destination) > 0) then
--handle USER_BUSY - forwarding to number
session:setVariable("last_busy_dialed_extension", dialed_extension);
if (forward_busy_destination == nil) then
freeswitch.consoleLog("NOTICE", "[failure_handler] forwarding on busy to hangup\n");
session:hangup("USER_BUSY");
else
freeswitch.consoleLog("NOTICE", "[failure_handler] forwarding on busy to: " .. forward_busy_destination .. "\n");
session:transfer(forward_busy_destination, "XML", context);
end
else
--send missed call notification
missed();
--handle USER_BUSY - hangup
freeswitch.consoleLog("NOTICE", "[failure_handler] forward on busy with empty destination: hangup(USER_BUSY)\n");
session:hangup("USER_BUSY");
end
end
end
elseif (originate_disposition == "NO_ANSWER") or (sip_code == "sip:480") then
--handle NO_ANSWER
forward_no_answer_enabled = session:getVariable("forward_no_answer_enabled");
if (forward_no_answer_enabled == "true") then
forward_no_answer_destination = session:getVariable("forward_no_answer_destination");
if (forward_no_answer_destination == nil) then
freeswitch.consoleLog("NOTICE", "[failure_handler] forwarding no answer to hangup\n");
session:hangup("NO_ANSWER");
else
freeswitch.consoleLog("NOTICE", "[failure_handler] forwarding no answer to: " .. forward_no_answer_destination .. "\n");
session:transfer(forward_no_answer_destination, "XML", context);
end
else
--send missed call notification
missed();
end
if (debug["info"] ) then
freeswitch.consoleLog("NOTICE", "[failure_handler] - NO_ANSWER\n");
end
elseif (originate_disposition == "USER_NOT_REGISTERED") then
--handle USER_NOT_REGISTERED
forward_user_not_registered_enabled = session:getVariable("forward_user_not_registered_enabled");
if (forward_user_not_registered_enabled == "true") then
forward_user_not_registered_destination = session:getVariable("forward_user_not_registered_destination");
if (forward_user_not_registered_destination == nil) then
freeswitch.consoleLog("NOTICE", "[failure_handler] forwarding user not registered to hangup\n");
session:hangup("NO_ANSWER");
else
freeswitch.consoleLog("NOTICE", "[failure_handler] forwarding user not registerd to: " .. forward_user_not_registered_destination .. "\n");
session:transfer(forward_user_not_registered_destination, "XML", context);
end
else
--send missed call notification
missed();
end
--send missed call notification
--missed();
--handle USER_NOT_REGISTERED
if (debug["info"] ) then
freeswitch.consoleLog("NOTICE", "[failure_handler] - USER_NOT_REGISTERED - Doing nothing\n");
end
elseif (originate_disposition == "SUBSCRIBER_ABSENT" and hangup_on_subscriber_absent == "true") then
--send missed call notification
missed();
--handle SUBSCRIBER_ABSENT
freeswitch.consoleLog("NOTICE", "[failure_handler] - SUBSCRIBER_ABSENT - hangup(UNALLOCATED_NUMBER)\n");
session:hangup("UNALLOCATED_NUMBER");
elseif (originate_disposition == "CALL_REJECTED" and hangup_on_call_reject =="true") then
--send missed call notification
missed();
--handle CALL_REJECT
freeswitch.consoleLog("NOTICE", "[failure_handler] - CALL_REJECT - hangup()\n");
session:hangup();
end
end
end

View File

@@ -0,0 +1,11 @@
text = text or {};
text['message-send_success'] = {}
text['message-send_success']['en-us'] = "We are happy to report the fax was sent successfully. It has been attached for your records."
text['message-send_success']['ru-ru'] = "Мы рады сообщить, что факс был отправлен успешно. Он был прикреплен к вашим записям."
text['message-send_fail'] = {}
text['message-send_fail']['en-us'] = "We are sorry the fax failed to go through. It has been attached. Please check the number, and if it was correct you might consider emailing it instead."
text['message-send_fail']['ru-ru'] = "Нам жаль, что факс не прошел. Он был прикреплен. Пожалуйста, проверьте номер, и если он был правильным, вы можете отправить его с помощью электронной почты."
return text

View File

@@ -0,0 +1,42 @@
local sleep_interval = 60;
--include config.lua
require "resources.functions.config";
--general functions
require "resources.functions.file_exists";
require "resources.functions.mkdir";
require "resources.functions.sleep";
local log = require "resources.functions.log".fax_queue_monitor
local Next = require "app.fax.resources.scripts.queue.next"
mkdir(scripts_dir .. "/run");
--define the run file
local run_file = scripts_dir .. "/run/fax_queue.tmp";
--used to stop the lua service
local file = assert(io.open(run_file, "w"));
file:write("remove this file to stop the script");
file:close()
log.notice("Start")
while true do
local ok, err = pcall(function()
Next.poll_once()
end)
if not ok then
log.errf("fail poll queue: %s", tostring(err))
end
if not file_exists(run_file) then
break;
end
sleep(sleep_interval * 1000)
end
log.notice("Stop")

View File

@@ -0,0 +1 @@
require "app.fax.resources.scripts.queue.next".poll_once()

View File

@@ -0,0 +1,440 @@
--
-- FusionPBX
-- Version: MPL 1.1
--
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
--
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
--
-- The Original Code is FusionPBX
--
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Copyright (C) 2015-2019
-- the Initial Developer. All Rights Reserved.
--
-- Contributor(s):
-- Mark J. Crane
--set the debug options
debug["sql"] = true;
--create the api object
api = freeswitch.API();
--include config.lua
require "resources.functions.config";
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--define the explode function
require "resources.functions.explode";
--array count
require "resources.functions.count";
local IS_WINDOWS = (package.config:sub(1,1) == '\\')
local function quote(s)
local q = IS_WINDOWS and '"' or "'"
if s:find('%s') or s:find(q, nil, true) then
s = q .. s:gsub(q, q..q) .. q
end
return s
end
-- escape shell arguments to prevent command injection
local function shell_esc(x)
return (x:gsub('\\', '\\\\')
:gsub('\'', '\\\''))
end
-- set channel variables to lua variables
domain_uuid = env:getHeader("domain_uuid");
domain_name = env:getHeader("domain_name");
--get the domain_uuid using the domain name required for multi-tenant
if (domain_name ~= nil) then
sql = "SELECT domain_uuid FROM v_domains ";
sql = sql .. "WHERE domain_name = :domain_name ";
dbh:query(sql, {domain_name = domain_name}, function(rows)
domain_uuid = rows["domain_uuid"];
end);
end
--settings
require "resources.functions.settings";
settings = settings(domain_uuid);
storage_type = "";
storage_path = "";
if (settings['fax'] ~= nil) then
if (settings['fax']['storage_type'] ~= nil) then
if (settings['fax']['storage_type']['text'] ~= nil) then
storage_type = settings['fax']['storage_type']['text'];
end
end
if (settings['fax']['storage_path'] ~= nil) then
if (settings['fax']['storage_path']['text'] ~= nil) then
storage_path = settings['fax']['storage_path']['text'];
storage_path = storage_path:gsub("${domain_name}", domain_name);
storage_path = storage_path:gsub("${voicemail_id}", voicemail_id);
storage_path = storage_path:gsub("${voicemail_dir}", voicemail_dir);
end
end
end
-- show all channel variables
serialized = env:serialize()
freeswitch.consoleLog("INFO","[fax]\n" .. serialized .. "\n")
-- example channel variables relating to fax
--variable_fax_success: 0
--variable_fax_result_code: 49
--variable_fax_result_text: The%20call%20dropped%20prematurely
--variable_fax_ecm_used: off
--variable_fax_local_station_id: SpanDSP%20Fax%20Ident
--variable_fax_document_transferred_pages: 0
--variable_fax_document_total_pages: 0
--variable_fax_image_resolution: 0x0
--variable_fax_image_size: 0
--variable_fax_bad_rows: 0
--variable_fax_transfer_rate: 14400
-- set channel variables to lua variables
fax_uuid = env:getHeader("fax_uuid");
uuid = env:getHeader("uuid");
fax_success = env:getHeader("fax_success");
fax_result_text = env:getHeader("fax_result_text");
fax_local_station_id = env:getHeader("fax_local_station_id");
fax_ecm_used = env:getHeader("fax_ecm_used");
fax_uri = env:getHeader("fax_uri");
fax_extension_number = env:getHeader("fax_extension_number");
caller_id_name = env:getHeader("caller_id_name");
caller_id_number = env:getHeader("caller_id_number");
fax_bad_rows = env:getHeader("fax_bad_rows");
fax_transfer_rate = env:getHeader("fax_transfer_rate");
sip_to_user = env:getHeader("sip_to_user");
bridge_hangup_cause = env:getHeader("bridge_hangup_cause");
fax_result_code = env:getHeader("fax_result_code");
fax_remote_station_id = env:getHeader("fax_remote_station_id");
fax_document_total_pages = env:getHeader("fax_document_total_pages");
hangup_cause_q850 = tonumber(env:getHeader("hangup_cause_q850"));
fax_file = env:getHeader("fax_file");
-- prevent nil errors
if (fax_file == nil) then
fax_file = env:getHeader("fax_filename");
end
if (fax_uri == nil) then
fax_uri = "";
end
if (fax_remote_station_id == nil) then
fax_remote_station_id = "";
end
if (caller_id_name == nil) then
caller_id_name = env:getHeader("Caller-Caller-ID-Name");
end
if (caller_id_number == nil) then
caller_id_number = env:getHeader("Caller-Caller-ID-Number");
end
--set default values
if (not fax_success) then
fax_success = "0";
fax_result_code = 2;
end
if (hangup_cause_q850 == "17") then
fax_success = "0";
fax_result_code = 2;
end
if (not fax_result_text) then
fax_result_text = "FS_NOT_SET";
end
--get the fax settings from the database
local sql = [[SELECT * FROM v_fax
WHERE fax_uuid = :fax_uuid
AND domain_uuid = :domain_uuid]];
local params = {fax_uuid = fax_uuid, domain_uuid = domain_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[fax] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
dialplan_uuid = row["dialplan_uuid"];
fax_extension = row["fax_extension"];
fax_accountcode = row["accountcode"];
fax_destination_number = row["fax_destination_number"];
fax_name = row["fax_name"];
fax_prefix = row["fax_prefix"];
fax_email = row["fax_email"];
fax_email_connection_type = row["fax_email_connection_type"];
fax_email_connection_host = row["fax_email_connection_host"];
fax_email_connection_port = row["fax_email_connection_port"];
fax_email_connection_security = row["fax_email_connection_security"];
fax_email_connection_validate = row["fax_email_connection_validate"];
fax_email_connection_username = row["fax_email_connection_username"];
fax_email_connection_password = row["fax_email_connection_password"];
fax_email_connection_mailbox = row["fax_email_connection_mailbox"];
fax_email_inbound_subject_tag = row["fax_email_inbound_subject_tag"];
fax_email_outbound_subject_tag = row["fax_email_outbound_subject_tag"];
fax_email_outbound_authorized_senders = row["fax_email_outbound_authorized_senders"];
fax_caller_id_name = row["fax_caller_id_name"];
fax_caller_id_number = row["fax_caller_id_number"];
fax_forward_number = row["fax_forward_number"];
fax_description = row["fax_description"];
end);
--get the values from the fax file
if (fax_file ~= nil) then
array = explode("/", fax_file);
fax_file_name = array[count(array)];
end
--fax to email
-- cmd = "lua" .. " " .. quote(scripts_dir .. "/fax_to_email.lua") .. " ";
cmd = quote(shell_esc(php_dir).."/"..shell_esc(php_bin)).." "..quote(shell_esc(document_root).."/secure/fax_to_email.php").." ";
cmd = cmd .. "email="..quote(shell_esc(fax_email)).." ";
cmd = cmd .. "extension="..quote(shell_esc(fax_extension)).." ";
cmd = cmd .. "name="..quote(shell_esc(fax_file)).." ";
cmd = cmd .. "messages=" .. quote("result:"..shell_esc(fax_result_text).." sender:"..shell_esc(fax_remote_station_id).." pages:"..shell_esc(fax_document_total_pages)).." ";
cmd = cmd .. "domain="..quote(shell_esc(domain_name)).." ";
cmd = cmd .. "caller_id_name=" .. quote(shell_esc(caller_id_name) or '') .. " ";
cmd = cmd .. "caller_id_number=" .. quote(shell_esc(caller_id_number) or '') .. " ";
if #fax_forward_number > 0 then
cmd = cmd .. "fax_relay=true ";
else
cmd = cmd .. "fax_relay=false ";
end
if #fax_prefix > 0 then
cmd = cmd .. "fax_prefix=true ";
else
cmd = cmd .. "fax_prefix=false ";
end
freeswitch.consoleLog("notice", "[fax] command: " .. cmd .. "\n");
result = api:execute("system", cmd);
--add to fax logs
sql = "insert into v_fax_logs ";
sql = sql .. "(";
sql = sql .. "fax_log_uuid, ";
sql = sql .. "domain_uuid, ";
if (fax_uuid ~= nil) then
sql = sql .. "fax_uuid, ";
end
sql = sql .. "fax_success, ";
sql = sql .. "fax_result_code, ";
sql = sql .. "fax_result_text, ";
sql = sql .. "fax_file, ";
if (fax_ecm_used ~= nil) then
sql = sql .. "fax_ecm_used, ";
end
if (fax_local_station_id ~= nil) then
sql = sql .. "fax_local_station_id, ";
end
sql = sql .. "fax_document_transferred_pages, ";
sql = sql .. "fax_document_total_pages, ";
if (fax_image_resolution ~= nil) then
sql = sql .. "fax_image_resolution, ";
end
if (fax_image_size ~= nil) then
sql = sql .. "fax_image_size, ";
end
if (fax_bad_rows ~= nil) then
sql = sql .. "fax_bad_rows, ";
end
if (fax_transfer_rate ~= nil) then
sql = sql .. "fax_transfer_rate, ";
end
if (fax_uri ~= nil) then
sql = sql .. "fax_uri, ";
end
sql = sql .. "fax_date, ";
sql = sql .. "fax_epoch ";
sql = sql .. ") ";
sql = sql .. "values ";
sql = sql .. "(";
sql = sql .. ":uuid, ";
sql = sql .. ":domain_uuid, ";
if (fax_uuid ~= nil) then
sql = sql .. ":fax_uuid, ";
end
sql = sql .. ":fax_success, ";
sql = sql .. ":fax_result_code, ";
sql = sql .. ":fax_result_text, ";
sql = sql .. ":fax_file, ";
if (fax_ecm_used ~= nil) then
sql = sql .. ":fax_ecm_used, ";
end
if (fax_local_station_id ~= nil) then
sql = sql .. ":fax_local_station_id, ";
end
sql = sql .. ":fax_document_transferred_pages, ";
sql = sql .. ":fax_document_total_pages, ";
if (fax_image_resolution ~= nil) then
sql = sql .. ":fax_image_resolution, ";
end
if (fax_image_size ~= nil) then
sql = sql .. ":fax_image_size, ";
end
if (fax_bad_rows ~= nil) then
sql = sql .. ":fax_bad_rows, ";
end
if (fax_transfer_rate ~= nil) then
sql = sql .. ":fax_transfer_rate, ";
end
if (fax_uri ~= nil) then
sql = sql .. ":fax_uri, ";
end
if (database["type"] == "sqlite") then
sql = sql .. ":fax_date, ";
else
sql = sql .. "now(), ";
end
sql = sql .. ":fax_time ";
sql = sql .. ")";
local params = {
uuid = uuid;
domain_uuid = domain_uuid;
fax_uuid = fax_uuid;
fax_success = fax_success;
fax_result_code = fax_result_code;
fax_result_text = fax_result_text;
fax_file = fax_file;
fax_ecm_used = fax_ecm_used;
fax_local_station_id = fax_local_station_id;
fax_document_transferred_pages = fax_document_transferred_pages or '0';
fax_document_total_pages = fax_document_total_pages or '0';
fax_image_resolution = fax_image_resolution;
fax_image_size = fax_image_size;
fax_bad_rows = fax_bad_rows;
fax_transfer_rate = fax_transfer_rate;
fax_uri = fax_uri;
fax_date = os.date("%Y-%m-%d %X");
fax_time = os.time();
};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[fax] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--add the fax files
if (fax_success ~= nil) then
if (fax_success =="1") then
if (storage_type == "base64") then
--include the file io
local file = require "resources.functions.file"
--read file content as base64 string
fax_base64 = assert(file.read_base64(fax_file));
end
local sql = {}
table.insert(sql, "insert into v_fax_files ");
table.insert(sql, "(");
table.insert(sql, "fax_file_uuid, ");
table.insert(sql, "fax_uuid, ");
table.insert(sql, "fax_mode, ");
table.insert(sql, "fax_file_type, ");
table.insert(sql, "fax_file_path, ");
if (caller_id_name ~= nil) then
table.insert(sql, "fax_caller_id_name, ");
end
if (caller_id_number ~= nil) then
table.insert(sql, "fax_caller_id_number, ");
end
table.insert(sql, "fax_date, ");
table.insert(sql, "fax_epoch, ");
if (storage_type == "base64") then
table.insert(sql, "fax_base64, ");
end
table.insert(sql, "domain_uuid");
table.insert(sql, ") ");
table.insert(sql, "values ");
table.insert(sql, "(");
table.insert(sql, ":uuid, ");
table.insert(sql, ":fax_uuid, ");
table.insert(sql, "'rx', ");
table.insert(sql, "'tif', ");
table.insert(sql, ":fax_file, ");
if (caller_id_name ~= nil) then
table.insert(sql, ":caller_id_name, ");
end
if (caller_id_number ~= nil) then
table.insert(sql, ":caller_id_number, ");
end
if (database["type"] == "sqlite") then
table.insert(sql, ":fax_date, ");
else
table.insert(sql, "now(), ");
end
table.insert(sql, ":fax_time, ");
if (storage_type == "base64") then
table.insert(sql, ":fax_base64, ");
end
table.insert(sql, ":domain_uuid");
table.insert(sql, ")");
sql = table.concat(sql, "\n");
local params = {
uuid = uuid;
domain_uuid = domain_uuid;
fax_uuid = fax_uuid;
fax_file = fax_file;
caller_id_name = caller_id_name;
caller_id_number = caller_id_number;
fax_base64 = fax_base64;
fax_date = os.date("%Y-%m-%d %X");
fax_time = os.time();
};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[fax] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
if (storage_type == "base64") then
local dbh = Database.new('system', 'base64');
dbh:query(sql, params);
dbh:release();
else
result = dbh:query(sql, params);
end
end
end
-- send the selected variables to the console
if (fax_success ~= nil) then
freeswitch.consoleLog("INFO","fax_success: '" .. fax_success .. "'\n");
end
freeswitch.consoleLog("INFO","domain_uuid: '" .. domain_uuid .. "'\n");
freeswitch.consoleLog("INFO","domain_name: '" .. domain_name .. "'\n");
freeswitch.consoleLog("INFO","fax_uuid: '" .. fax_uuid .. "'\n");
freeswitch.consoleLog("INFO","fax_extension: '" .. fax_extension .. "'\n");
freeswitch.consoleLog("INFO","fax_result_text: '" .. fax_result_text .. "'\n");
freeswitch.consoleLog("INFO","fax_file: '" .. fax_file .. "'\n");
freeswitch.consoleLog("INFO","uuid: '" .. uuid .. "'\n");
--freeswitch.consoleLog("INFO","fax_ecm_used: '" .. fax_ecm_used .. "'\n");
freeswitch.consoleLog("INFO","fax_uri: '" .. fax_uri.. "'\n");
if (caller_id_name ~= nil) then
freeswitch.consoleLog("INFO","caller_id_name: " .. caller_id_name .. "\n");
end
if (caller_id_number ~= nil) then
freeswitch.consoleLog("INFO","caller_id_number: " .. caller_id_number .. "\n");
end
freeswitch.consoleLog("INFO","fax_result_code: ".. fax_result_code .."\n");
--freeswitch.consoleLog("INFO","mailfrom_address: ".. from_address .."\n");
--freeswitch.consoleLog("INFO","mailto_address: ".. email_address .."\n");
freeswitch.consoleLog("INFO","hangup_cause_q850: '" .. hangup_cause_q850 .. "'\n");

View File

@@ -0,0 +1,187 @@
-- @usage without queue
-- api: originate {fax_file='',wav_file='',fax_dtmf=''}user/108@domain.local &lua(app/fax/resources/scripts/queue/exec.lua)
-- @usage with queue task
-- api: originate {fax_task_uuid=''}user/108@domain.local &lua(app/fax/resources/scripts/queue/exec.lua)
-- @fax_dtmf
-- 0-9*# - dtmf symbols
-- @200 - dtmf duration in ms
-- p - pause 500 ms
-- P - pause 1000 ms
--
-- example: pause 5 sec dial 008 pause 2 sec paly greeting
-- PPPPP008@300PP
--
require "resources.functions.config"
local log = require "resources.functions.log".fax_task
-- If we handle queue task
local fax_task_uuid = session:getVariable('fax_task_uuid')
local task if fax_task_uuid then
local Tasks = require "app.fax.resources.scripts.queue.tasks"
task = Tasks.select_task(fax_task_uuid)
if not task then
log.warningf("Can not found fax task: %q", tostring(fax_task_uuid))
return
end
end
if task then
local str = 'Queue task :'
for k, v in pairs(task) do
str = str .. ('\n %q = %q'):format(k, v)
end
log.info(str)
else
log.info('Not queued task')
end
local function empty(t) return (not t) or (#t == 0) end
local function not_empty(t) if not empty(t) then return t end end
local dtmf, wav_file, fax_file
if task then
dtmf = not_empty(task.dtmf)
wav_file = not_empty(task.wav_file) or not_empty(task.greeting)
fax_file = not_empty(task.fax_file)
else
dtmf = not_empty(session:getVariable('fax_dtmf'))
wav_file = not_empty(session:getVariable('wav_file'))
fax_file = not_empty(session:getVariable('fax_file'))
end
if not (wav_file or fax_file) then
log.warning("No fax or wav file")
return
end
local function decode_dtmf(dtmf)
local r, sleep, seq = {}
dtmf:gsub('P', 'pp'):gsub('.', function(ch)
if ch == ';' or ch == ',' then
r[#r + 1] = sleep or seq
sleep, seq = nil
elseif ch == 'p' then
r[#r + 1] = seq
sleep = (sleep or 0) + 500
seq = nil
else
r[#r + 1] = sleep
seq = (seq or '') .. ch
sleep = nil
end
end)
r[#r + 1] = sleep or seq
return r
end
local function send_fax()
session:execute("txfax", fax_file)
end
local function start_fax_detect(detect_duration)
if not tone_detect_cb then
function tone_detect_cb(s, type, obj, arg)
if type == "event" then
if obj:getHeader('Event-Name') == 'DETECTED_TONE' then
return "false"
end
end
end
end
log.notice("Start detecting fax")
detect_duration = detect_duration or 60000
session:setInputCallback("tone_detect_cb")
session:execute("tone_detect", "txfax 2100 r +" .. tostring(detect_duration) .. " set remote_fax_detected=txfax")
session:execute("tone_detect", "rxfax 1100 r +" .. tostring(detect_duration) .. " set remote_fax_detected=rxfax")
session:setVariable("sip_api_on_image", "uuid_break " .. session:getVariable("uuid") .. " all")
end
local function stop_fax_detect()
session:unsetInputCallback()
session:execute("stop_tone_detect")
session:setVariable("sip_api_on_image", "")
end
local function fax_deteced()
if session:getVariable('has_t38') == 'true' then
log.noticef('Detected t38')
session:setVariable('remote_fax_detected', 'txfax')
end
if fax_file and session:getVariable('remote_fax_detected') then
log.noticef("Detected %s", session:getVariable('remote_fax_detected'))
if session:getVariable('remote_fax_detected') == 'txfax' then
send_fax()
else
log.warning('Remote fax try send fax')
end
return true
end
end
local function check()
if not session:ready() then return false end
if fax_deteced() then return false end
return true
end
local function task()
local session_uuid = session:getVariable('uuid')
session:setVariable('fax_queue_task_session', session_uuid)
log.infof("SESSION UUID: %s", session_uuid)
session:waitForAnswer(session)
while not session:answered() do
if not session:ready() then return end
session:sleep(500)
end
if not (session:ready() and session:answered()) then return end
if fax_file and wav_file then
start_fax_detect()
end
if dtmf then
dtmf = decode_dtmf(dtmf)
for _, element in ipairs(dtmf) do
if type(element) == 'number' then
session:streamFile("silence_stream://" .. tostring(element))
else
session:execute("send_dtmf", element)
end
if not check() then return end
end
end
if wav_file then
session:streamFile(wav_file)
if not check() then return end
end
if fax_file then
if wav_file then
stop_fax_detect()
end
send_fax()
end
end
log.noticef("START TASK")
log.notice("Fax:" .. tostring(fax_file))
log.notice("Wav:" .. tostring(wav_file))
task()
log.noticef("STOP TASK")
log.notice("Ready: " .. tostring(session:ready()))
log.notice("Answered: " .. tostring(session:answered()))

View File

@@ -0,0 +1,98 @@
require "resources.functions.config"
require "resources.functions.sleep"
require "resources.functions.file_exists"
local log = require "resources.functions.log".next_fax_task
local Tasks = require "app.fax.resources.scripts.queue.tasks"
local Esl = require "resources.functions.esl"
local FAX_OPTIONS = {
"fax_use_ecm=false,fax_enable_t38=true,fax_enable_t38_request=true,fax_disable_v17=default";
"fax_use_ecm=true,fax_enable_t38=true,fax_enable_t38_request=true,fax_disable_v17=false";
"fax_use_ecm=true,fax_enable_t38=false,fax_enable_t38_request=false,fax_disable_v17=false";
"fax_use_ecm=true,fax_enable_t38=true,fax_enable_t38_request=true,fax_disable_v17=true";
"fax_use_ecm=false,fax_enable_t38=false,fax_enable_t38_request=false,fax_disable_v17=false";
}
local function task_send_mail(task, err)
local number_dialed = task.uri:match("/([^/]-)%s*$")
local Text = require "resources.functions.text"
local text = Text.new("app.fax.app_languages")
local env = {
destination_number = number_dialed:match("^([^@]*)");
hangup_cause = err;
message = text['message-send_fail'];
}
local body = Tasks.build_template(task, 'outbound/fail/body', env)
local subject = Tasks.build_template(task, 'outbound/fail/subject', env)
if not subject then
log.warning("Can not find template for email")
subject = "Fax to: " .. number_dialed .. " FAILED"
end
Tasks.send_mail_task(task, {subject, body}, nil, file_exists(task.fax_file))
end
local function next_task()
local task, err = Tasks.next_task()
if not task then
if err then
log.noticef('Can not select next task: %s', tostring(err))
else
log.notice("No task")
end
return
end
local esl
local ok, err = pcall(function()
local mode = (task.retry_counter % #FAX_OPTIONS) + 1
local dial_string = '{' ..
task.dial_string .. "api_hangup_hook='lua app/fax/resources/scripts/queue/retry.lua'," ..
FAX_OPTIONS[mode] ..
'}' .. task.uri
local originate = 'originate ' .. dial_string .. ' &lua(app/fax/resources/scripts/queue/exec.lua)'
log.notice(originate)
esl = assert(Esl.new())
local ok, status, info = esl:api(originate)
if not ok then
Tasks.wait_task(task, false, info)
if task.status ~= 0 then
Tasks.remove_task(task)
task_send_mail(task, tostring(info))
end
log.noticef('Can not originate to `%s` cause: %s: %s ', task.uri, tostring(status), tostring(info))
else
log.noticef("originate successfuly: %s", tostring(info))
end
end)
if esl then esl:close() end
if not ok then
Tasks.release_task(task)
log.noticef("Error execute task: %s", tostring(err))
end
return true
end
local function poll_once()
Tasks.cleanup_tasks()
while next_task() do
sleep(5000)
end
Tasks.release_db()
end
return {
poll_once = poll_once;
}

View File

@@ -0,0 +1,418 @@
-- include libraries
require "resources.functions.config";
require "resources.functions.split";
require "resources.functions.file_exists";
local log = require "resources.functions.log".fax_retry
local Database = require "resources.functions.database"
local Settings = require "resources.functions.lazy_settings"
local Tasks = require "app.fax.resources.scripts.queue.tasks"
local send_mail = require "resources.functions.send_mail"
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
local fax_task_uuid = env:getHeader('fax_task_uuid')
if not fax_task_uuid then
log.warning("No [fax_task_uuid] channel variable")
return
end
local task = Tasks.select_task(fax_task_uuid)
if not task then
log.warningf("Can not find fax task: %q", tostring(fax_task_uuid))
return
end
-- show all channel variables
if debug["fax_serialize"] then
log.noticef("info:\n%s", env:serialize())
end
local dbh = Database.new('system')
-- Global environment
default_language = env:getHeader("default_language")
default_dialect = env:getHeader("default_dialect")
-- Channel/FusionPBX variables
local uuid = env:getHeader("uuid")
local fax_queue_task_session = env:getHeader('fax_queue_task_session')
local domain_uuid = env:getHeader("domain_uuid") or task.domain_uuid
local domain_name = env:getHeader("domain_name") or task.domain_name
local origination_caller_id_name = env:getHeader("origination_caller_id_name") or '000000000000000'
local origination_caller_id_number = env:getHeader("origination_caller_id_number") or '000000000000000'
local accountcode = env:getHeader("accountcode") or domain_name
local duration = tonumber(env:getHeader("billmsec")) or 0
local sip_to_user = env:getHeader("sip_to_user")
local bridge_hangup_cause = env:getHeader("bridge_hangup_cause")
local hangup_cause_q850 = tonumber(env:getHeader("hangup_cause_q850"))
local answered = duration > 0
-- fax variables
local fax_success = env:getHeader('fax_success')
local has_t38 = env:getHeader('has_t38') or 'false'
local t38_broken_boolean = env:getHeader('t38_broken_boolean') or ''
local fax_result_code = tonumber(env:getHeader('fax_result_code')) or 2
local fax_result_text = env:getHeader('fax_result_text') or 'FS_NOT_SET'
local fax_ecm_used = env:getHeader('fax_ecm_used') or ''
local fax_local_station_id = env:getHeader('fax_local_station_id') or ''
local fax_document_transferred_pages = env:getHeader('fax_document_transferred_pages') or nil
local fax_document_total_pages = env:getHeader('fax_document_total_pages') or nil
local fax_image_resolution = env:getHeader('fax_image_resolution') or ''
local fax_image_size = env:getHeader('fax_image_size') or nil
local fax_bad_rows = env:getHeader('fax_bad_rows') or nil
local fax_transfer_rate = env:getHeader('fax_transfer_rate') or nil
local fax_v17_disabled = env:getHeader('fax_v17_disabled') or ''
local fax_ecm_requested = env:getHeader('fax_ecm_requested') or ''
local fax_remote_station_id = env:getHeader('fax_remote_station_id') or ''
local fax_options = ("fax_use_ecm=%s,fax_enable_t38=%s,fax_enable_t38_request=%s,fax_disable_v17=%s"):format(
env:getHeader('fax_use_ecm') or '',
env:getHeader('fax_enable_t38') or '',
env:getHeader('fax_enable_t38_request') or '',
env:getHeader('fax_disable_v17') or ''
)
-- Fax task params
local fax_uri = env:getHeader("fax_uri") or task.uri
local fax_file = env:getHeader("fax_file") or task.fax_file
local wav_file = env:getHeader("wav_file") or task.wav_file
local fax_uuid = task.fax_uuid
local pdf_file = fax_file and string.gsub(fax_file, '(%.[^\\/]+)$', '.pdf')
-- Email variables
local number_dialed = fax_uri:match("/([^/]-)%s*$")
log.noticef([[<<< CALL RESULT >>>
uuid: = '%s'
task_session_uuid: = '%s'
answered: = '%s'
fax_file: = '%s'
wav_file: = '%s'
fax_uri: = '%s'
sip_to_user: = '%s'
accountcode: = '%s'
origination_caller_id_name: = '%s'
origination_caller_id_number: = '%s'
mailto_address: = '%s'
hangup_cause_q850: = '%s'
fax_options = '%s'
]],
tostring(uuid) ,
tostring(fax_queue_task_session) ,
tostring(answered) ,
tostring(fax_file) ,
tostring(wav_file) ,
tostring(fax_uri) ,
tostring(sip_to_user) ,
tostring(accountcode) ,
tostring(origination_caller_id_name) ,
tostring(origination_caller_id_number) ,
tostring(task.reply_address) ,
tostring(hangup_cause_q850) ,
fax_options
)
if fax_success then
log.noticef([[<<< FAX RESULT >>>
fax_success = '%s'
has_t38 = '%s'
t38_broken_boolean = '%s'
fax_result_code = '%s'
fax_result_text = '%s'
fax_ecm_used = '%s'
fax_local_station_id = '%s'
fax_document_transferred_pages = '%s'
fax_document_total_pages = '%s'
fax_image_resolution = '%s'
fax_image_size = '%s'
fax_bad_rows = '%s'
fax_transfer_rate = '%s'
fax_v17_disabled = '%s'
fax_ecm_requested = '%s'
fax_remote_station_id = '%s'
'%s'
]],
fax_success ,
has_t38 ,
t38_broken_boolean ,
fax_result_code ,
fax_result_text ,
fax_ecm_used ,
fax_local_station_id ,
fax_document_transferred_pages ,
fax_document_total_pages ,
fax_image_resolution ,
fax_image_size ,
fax_bad_rows ,
fax_transfer_rate ,
fax_v17_disabled ,
fax_ecm_requested ,
fax_remote_station_id ,
'---------------------------------'
)
end
log.debug([[<<< DEBUG >>>
domain_name = '%s'
domain_uuid = '%s'
task.domain_name = '%s'
task.domain_uuid = '%s'
]],
tostring(domain_name ),
tostring(domain_uuid ),
tostring(task.domain_name ),
tostring(task.domain_uuid )
)
assert(fax_uuid, 'no fax server uuid')
assert(domain_name, 'no domain name')
assert(domain_uuid, 'no domain uuid')
assert(domain_uuid:lower() == task.domain_uuid:lower(), 'invalid domain uuid')
assert(domain_name:lower() == task.domain_name:lower(), 'invalid domain name')
--settings
local settings = Settings.new(dbh, domain_name, domain_uuid)
local keep_local = settings:get('fax', 'keep_local', 'boolean')
local storage_type = (keep_local == "false") and "" or settings:get('fax', 'storage_type', 'text')
local function opt(v, default)
if v then return "'" .. v .. "'" end
return default or 'NULL'
end
local function now_sql()
return (database["type"] == "sqlite") and "'"..os.date("%Y-%m-%d %X").."'" or "now()";
end
--add to fax logs
do
local fields = {
"fax_log_uuid";
"domain_uuid";
"fax_uuid";
"fax_success";
"fax_result_code";
"fax_result_text";
"fax_file";
"fax_ecm_used";
"fax_local_station_id";
"fax_document_transferred_pages";
"fax_document_total_pages";
"fax_image_resolution";
"fax_image_size";
"fax_bad_rows";
"fax_transfer_rate";
"fax_retry_attempts";
"fax_retry_limit";
"fax_retry_sleep";
"fax_uri";
"fax_epoch";
}
local params = {
fax_log_uuid = uuid;
domain_uuid = domain_uuid;
fax_uuid = fax_uuid or dbh.NULL;
fax_success = fax_success or dbh.NULL;
fax_result_code = fax_result_code or dbh.NULL;
fax_result_text = fax_result_text or dbh.NULL;
fax_file = fax_file or dbh.NULL;
fax_ecm_used = fax_ecm_used or dbh.NULL;
fax_local_station_id = fax_local_station_id or dbh.NULL;
fax_document_transferred_pages = fax_document_transferred_pages or "'0'";
fax_document_total_pages = fax_document_total_pages or "'0'";
fax_image_resolution = fax_image_resolution or dbh.NULL;
fax_image_size = fax_image_size or dbh.NULL;
fax_bad_rows = fax_bad_rows or dbh.NULL;
fax_transfer_rate = fax_transfer_rate or dbh.NULL;
fax_retry_attempts = fax_retry_attempts or dbh.NULL;
fax_retry_limit = fax_retry_limit or dbh.NULL;
fax_retry_sleep = fax_retry_sleep or dbh.NULL;
fax_uri = fax_uri or dbh.NULL;
fax_epoch = os.time();
}
local values = ":" .. table.concat(fields, ",:")
fields = table.concat(fields, ",") .. ",fax_date"
if database["type"] == "sqlite" then
params.fax_date = os.date("%Y-%m-%d %X");
values = values .. ",:fax_date"
else
values = values .. ",now()"
end
local sql = "insert into v_fax_logs(" .. fields .. ")values(" .. values .. ")"
if (debug["sql"]) then
log.noticef("SQL: %s; params: %s", sql, json.encode(params, dbh.NULL));
end
dbh:query(sql, params);
end
-- add the fax files
if fax_success == "1" then
if storage_type == "base64" then
--include the file io
local file = require "resources.functions.file"
--read file content as base64 string
fax_base64 = file.read_base64(fax_file);
if not fax_base64 then
log.waitng("Can not find file %s", fax_file)
storage_type = nil
end
end
-- build SQL
local sql do
local fields = {
"fax_file_uuid";
"fax_uuid";
"fax_mode";
"fax_destination";
"fax_file_type";
"fax_file_path";
"fax_caller_id_name";
"fax_caller_id_number";
"fax_epoch";
"fax_base64";
"domain_uuid";
}
local params = {
fax_file_uuid = uuid;
fax_uuid = fax_uuid or dbh.NULL;
fax_mode = "tx";
fax_destination = sip_to_user or dbh.NULL;
fax_file_type = "tif";
fax_file_path = fax_file or dbh.NULL;
fax_caller_id_name = origination_caller_id_name or dbh.NULL;
fax_caller_id_number = origination_caller_id_number or dbh.NULL;
fax_epoch = os.time();
fax_base64 = fax_base64 or dbh.NULL;
domain_uuid = domain_uuid or dbh.NULL;
}
local values = ":" .. table.concat(fields, ",:")
fields = table.concat(fields, ",") .. ",fax_date"
if database["type"] == "sqlite" then
params.fax_date = os.date("%Y-%m-%d %X");
values = values .. ",:fax_date"
else
values = values .. ",now()"
end
local sql = "insert into v_fax_files(" .. fields .. ")values(" .. values .. ")"
if (debug["sql"]) then
log.noticef("SQL: %s; params: %s", sql, json.encode(params, dbh.NULL));
end
if storage_type == "base64" then
local dbh = Database.new('system', 'base64');
dbh:query(sql, params);
dbh:release();
else
dbh:query(sql, params)
end
end
end
if fax_success == "1" then
--Success
log.infof("RETRY STATS SUCCESS: GATEWAY[%s]", fax_options);
Tasks.remove_task(task)
local Text = require "resources.functions.text"
local text = Text.new("app.fax.app_languages")
local env = {
fax_options = fax_options;
destination_number = number_dialed:match("^([^@]*)");
document_transferred_pages = fax_document_transferred_pages;
document_total_pages = fax_document_total_pages;
message = text['message-send_success'];
}
local body = Tasks.build_template(task, 'outbound/success/body', env)
local subject = Tasks.build_template(task, 'outbound/success/subject', env)
if not subject then
log.warning("Can not find template for email")
subject = "Fax to: " .. number_dialed .. " SENT"
end
local attachment = pdf_file and file_exists(pdf_file) or fax_file and file_exists(fax_file)
Tasks.send_mail_task(task, {subject, body}, uuid, attachment)
if keep_local == "false" then
os.remove(pdf_file);
os.remove(fax_file);
end
end
if fax_success ~= "1" then
if not answered then
log.noticef("no answer: %d", hangup_cause_q850)
else
if not fax_success then
log.noticef("Fax not detected: %s", fax_options)
else
log.noticef("fax fail %s", fax_options)
end
end
-- if task use group call then retry.lua will be called multiple times
-- here we check eathre that channel which execute `exec.lua`
-- Note that if there no one execute `exec.lua` we do not need call this
-- becase it should deal in `next.lua`
if fax_queue_task_session == uuid then
Tasks.wait_task(task, answered, hangup_cause_q850)
if task.status ~= 0 then
Tasks.remove_task(task)
local Text = require "resources.functions.text"
local text = Text.new("app.fax.app_languages")
local env = {
fax_options = fax_options;
destination_number = number_dialed:match("^([^@]*)");
document_transferred_pages = fax_document_transferred_pages;
document_total_pages = fax_document_total_pages;
hangup_cause = hangup_cause;
hangup_cause_q850 = hangup_cause_q850;
fax_result_code = fax_result_code;
fax_result_text = fax_result_text;
message = text['message-send_fail'];
}
local body = Tasks.build_template(task, 'outbound/fail/body', env)
local subject = Tasks.build_template(task, 'outbound/fail/subject', env)
if not subject then
log.warning("Can not find template for email")
subject = "Fax to: " .. number_dialed .. " FAILED"
end
local attachment = pdf_file and file_exists(pdf_file) or fax_file and file_exists(fax_file)
Tasks.send_mail_task(task, {subject, body}, uuid, attachment)
if keep_local == "false" then
os.remove(pdf_file);
os.remove(fax_file);
end
end
end
end

View File

@@ -0,0 +1,330 @@
local Database = require "resources.functions.database"
local Settings = require "resources.functions.lazy_settings"
local send_mail = require "resources.functions.send_mail"
local db
local date_utc_now_sql
local now_add_sec_sql
if database.type == 'pgsql' then
date_utc_now_sql = "NOW() at time zone 'utc'"
now_add_sec_sql = "NOW() at time zone 'utc' + interval '%s second'"
elseif database.type == 'mysql' then
date_utc_now_sql = "UTC_TIMESTAMP()"
now_add_sec_sql = "DATE_ADD(UTC_TIMESTAMP(), INTERVAL %s SECOND)"
elseif database.type == 'sqlite' then
date_utc_now_sql = "datetime('now')"
now_add_sec_sql = "datetime('now', '%s seconds')"
else
error("unsupported database type: " .. database.type)
end
-- Broken on FS 1.4 with native postgresql
-- Fixed on 1.6.0
-- Also works with ODBC
local ignore_affected_rows = true
if dbh_affected_rows_broken ~= nil then
ignore_affected_rows = dbh_affected_rows_broken
end
local Q850_TIMEOUT = {
[17] = 60;
}
local select_task_common_sql = [[
select
t1.fax_task_uuid as uuid,
t1.fax_uuid as fax_uuid,
t3.domain_name,
t3.domain_uuid,
t1.task_status as status,
t1.task_uri as uri,
t1.task_dial_string as dial_string,
t1.task_dtmf as dtmf,
t1.task_fax_file as fax_file,
t1.task_wav_file as wav_file,
t1.task_reply_address as reply_address,
t1.task_no_answer_counter as no_answer_counter,
t1.task_no_answer_retry_counter as no_answer_retry_counter,
t1.task_retry_counter as retry_counter,
t2.fax_send_greeting as greeting
from v_fax_tasks t1
inner join v_fax t2 on t2.fax_uuid = t1.fax_uuid
inner join v_domains t3 on t2.domain_uuid = t3.domain_uuid
where t1.task_interrupted <> 'true'
]]
local next_task_sql = select_task_common_sql .. [[
and t1.task_status = 0 and t1.task_next_time < ]] .. date_utc_now_sql .. [[
and t2.fax_send_channels > (select count(*) from v_fax_tasks as tasks
where tasks.fax_uuid = t1.fax_uuid and
tasks.task_status > 0 and tasks.task_status <= 2
)
order by t1.task_next_time
]]
local select_task_sql = select_task_common_sql .. "and t1.fax_task_uuid='%s'"
local aquire_task_sql = [[
update v_fax_tasks set task_status = 1, task_lock_time = ]] .. date_utc_now_sql .. [[
where fax_task_uuid = '%s' and task_status = 0
]]
local wait_task_sql = [[
update v_fax_tasks
set task_status = %s,
task_lock_time = NULL,
task_no_answer_counter = %s,
task_no_answer_retry_counter = %s,
task_retry_counter = %s,
task_next_time = ]] .. now_add_sec_sql .. [[
where fax_task_uuid = '%s'
]]
local remove_task_task_sql = [[
delete from v_fax_tasks
where fax_task_uuid = '%s'
]]
local release_task_sql = [[
update v_fax_tasks
set task_status = 0, task_lock_time = NULL,
task_next_time = ]] .. now_add_sec_sql .. [[
where fax_task_uuid = '%s'
]]
local release_stuck_tasks_sql = [[
update v_fax_tasks
set task_status = 0, task_lock_time = NULL,
task_next_time = ]] .. date_utc_now_sql .. [[
where task_lock_time < ]] .. now_add_sec_sql:format('-3600') .. [[
]]
local remove_finished_tasks_sql = [[
delete from v_fax_tasks where task_status > 3
]]
local function serialize(task, header)
local str = header or ''
for k, v in pairs(task) do
str = str .. ('\n %q = %q'):format(tostring(k), tostring(v))
end
return str
end
local function get_db()
if not db then
db = assert(Database.new('system'))
end
return db
end
local function next_task()
local db = get_db()
while true do
local task, err = db:first_row(next_task_sql)
if not task then return nil, err end
local ok, err = db:query( aquire_task_sql:format(task.uuid) )
if not ok then return nil, err end
local rows = db:affected_rows()
if ignore_affected_rows then
rows = 1
end
if rows == 1 then
task.no_answer_counter = tonumber(task.no_answer_counter)
task.no_answer_retry_counter = tonumber(task.no_answer_retry_counter)
task.retry_counter = tonumber(task.retry_counter)
return task
end
end
end
local function select_task(fax_task_uuid)
local db = get_db()
local task, err = db:first_row(select_task_sql:format(fax_task_uuid))
if not task then return nil, err end
task.no_answer_counter = tonumber(task.no_answer_counter)
task.no_answer_retry_counter = tonumber(task.no_answer_retry_counter)
task.retry_counter = tonumber(task.retry_counter)
return task
end
local function wait_task(task, answered, q850)
local db = get_db()
local interval = 30
local settings = Settings.new(db, task.domain_name, task.domain_uuid)
task.status = 0
if not answered then
interval = Q850_TIMEOUT[q850 or 17] or interval
end
if not answered then
local fax_send_no_answer_retry_limit = tonumber(settings:get('fax', 'send_no_answer_retry_limit', 'numeric')) or 0
task.no_answer_retry_counter = task.no_answer_retry_counter + 1
if task.no_answer_retry_counter >= fax_send_no_answer_retry_limit then
task.no_answer_retry_counter = 0
task.no_answer_counter = task.no_answer_counter + 1
local fax_send_no_answer_limit = tonumber(settings:get('fax', 'send_no_answer_limit', 'numeric')) or 0
if task.no_answer_counter >= fax_send_no_answer_limit then
task.status = 4
else
interval = tonumber(settings:get('fax', 'send_no_answer_interval', 'numeric')) or interval
end
else
interval = tonumber(settings:get('fax', 'send_no_answer_retry_interval', 'numeric')) or interval
end
else
task.retry_counter = task.retry_counter + 1
local fax_send_retry_limit = tonumber(settings:get('fax', 'send_retry_limit', 'numeric')) or 0
if task.retry_counter >= fax_send_retry_limit then
task.status = 4
else
interval = tonumber(settings:get('fax', 'send_retry_interval', 'numeric')) or interval
task.task_seq_call_counter = 0
end
end
local sql = wait_task_sql:format(
tostring(task.status),
tostring(task.no_answer_counter),
tostring(task.no_answer_retry_counter),
tostring(task.retry_counter),
tostring(interval),
task.uuid
)
local ok, err = db:query( sql )
if not ok then return nil, err end
return task
end
local function remove_task(task)
local db = get_db()
local sql = remove_task_task_sql:format(task.uuid)
local ok, err = db:query( sql )
if not ok then return nil, err end
return db:affected_rows()
end
local function release_task(task)
local db = get_db()
local interval = 30
local sql = release_task_sql:format(
tostring(interval),
task.uuid
)
local ok, err = db:query( sql )
if not ok then return nil, err end
return task
end
local function cleanup_tasks()
local db = get_db()
db:query(release_stuck_tasks_sql)
db:query(remove_finished_tasks_sql)
end
local function send_mail_task(task, message, call_uuid, file)
if not task.reply_address or #task.reply_address == 0 then
return
end
local mail_x_headers = {
["X-FusionPBX-Domain-UUID"] = task.domain_uuid;
["X-FusionPBX-Domain-Name"] = task.domain_name;
["X-FusionPBX-Call-UUID"] = call_uuid;
["X-FusionPBX-Email-Type"] = 'email2fax';
}
return send_mail(mail_x_headers, task.reply_address, message, file)
end
local function read_file(name, mode)
local f, err, code = io.open(name, mode or 'rb')
if not f then return nil, err, code end
local data = f:read("*all")
f:close()
return data
end
local function read_template(app, name, lang)
local default_language_path = 'en/us'
local full_path_tpl = scripts_dir .. '/app/' .. app .. '/resources/templates/{lang}/' .. name .. '.tpl'
local path
if lang then
path = file_exists((full_path_tpl:gsub('{lang}', lang)))
end
if (not path) and (lang ~= default_language_path) then
path = file_exists((full_path_tpl:gsub('{lang}', default_language_path)))
end
if path then
return read_file(path)
end
end
local function build_template(task, templ, env)
local lang
if default_language and default_dialect then
lang = (default_language .. '/' .. default_dialect):lower()
else
local settings = Settings.new(get_db(), task.domain_name, task.domain_uuid)
lang = settings:get('domain', 'language', 'code')
if lang then lang = lang:gsub('%-', '/'):lower() end
end
local body = read_template('fax', templ, lang)
body = body:gsub("[^\\](${.-})", function(name)
name = name:sub(3, -2)
if type(env) == 'table' then
return env[name] or ''
end
if type(env) == 'function' then
return env(name) or ''
end
end)
return body
end
return {
release_db = function()
if db then
db:release()
db = nil
end
end;
next_task = next_task;
wait_task = wait_task;
select_task = select_task;
remove_task = remove_task;
release_task = release_task;
cleanup_tasks = cleanup_tasks;
send_mail_task = send_mail_task;
build_template = build_template;
}

View File

@@ -0,0 +1,51 @@
<html>
<table width="400" border="0" cellspacing="0" cellpadding="0" align="center"
style="border: 1px solid #cbcfd5;-moz-border-radius: 4px;
-webkit-border-radius: 4px; border-radius: 4px;">
<tr>
<td valign="middle" align="center" bgcolor="#ff7174" style="background-color: #ff7174;
color: #000; font-family: Arial; font-size: 14px; padding: 7px;-moz-border-radius: 4px;
-webkit-border-radius: 4px; border-radius: 4px;">
<strong>Send fax fail</strong>
</td>
</tr>
<tr>
<td valign="top" style="padding: 15px;">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>To</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${destination_number}
</td>
</tr>
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>Pages</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${document_transferred_pages}/${document_total_pages}
</td>
</tr>
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>Message</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${message}
</td>
</tr>
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>Error</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${hangup_cause}/${fax_result_code}/${fax_result_text}
</td>
</tr>
</table>
</td>
</tr>
</table>
</html>

View File

@@ -0,0 +1 @@
Fax to: ${destination_number} FAILED

View File

@@ -0,0 +1,51 @@
<html>
<table width="400" border="0" cellspacing="0" cellpadding="0" align="center"
style="border: 1px solid #cbcfd5;-moz-border-radius: 4px;
-webkit-border-radius: 4px; border-radius: 4px;">
<tr>
<td valign="middle" align="center" bgcolor="#e5e9f0" style="background-color: #e5e9f0;
color: #000; font-family: Arial; font-size: 14px; padding: 7px;-moz-border-radius: 4px;
-webkit-border-radius: 4px; border-radius: 4px;">
<strong>Send fax successfully</strong>
</td>
</tr>
<tr>
<td valign="top" style="padding: 15px;">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>To</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${destination_number}
</td>
</tr>
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>Pages</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${document_transferred_pages}
</td>
</tr>
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>Message</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${message}
</td>
</tr>
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>Options</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${fax_options}
</td>
</tr>
</table>
</td>
</tr>
</table>
</html>

View File

@@ -0,0 +1 @@
Fax to: ${destination_number} sent

View File

@@ -0,0 +1,454 @@
--
-- event_notify
-- Version: MPL 1.1
--
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
--
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
--
-- The Original Code is FusionPBX - event_notify
--
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Copyright (C) 2013 - 2014
-- the Initial Developer. All Rights Reserved.
--
-- Contributor(s):
-- Mark J Crane <markjcrane@fusionpbx.com>
-- KonradSC <konrd@yahoo.com>
-- Start the script
-- /etc/freeswitch/autoload_configs/lua.conf.xml
-- <!-- Subscribe to events -->
-- <hook event="PHONE_FEATURE_SUBSCRIBE" subclass="" script="app.lua feature_event"/>
--Enable Feature Sync
-- Default Settings:
-- Device -> feature_sync = true
--Yealink
-- Web Interface -> Features -> General Information -> Feature Key Synchronization set to Enabled
-- Config Files -> bw.feature_key_sync = 1
--Polycom
-- reg.{$row.line_number}.serverFeatureControl.cf="1"
-- reg.{$row.line_number}.serverFeatureControl.dnd="1"
-- Cisco SPA
-- <Feature_Key_Sync_1_ group="Ext_1/Call_Feature_Settings">Yes</Feature_Key_Sync_1_>
--prepare the api object
api = freeswitch.API();
--define the functions
require "resources.functions.trim";
require "resources.functions.explode";
--connect to the database
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"
local cache = require "resources.functions.cache"
local notify = require "app.feature_event.resources.functions.feature_event_notify"
dbh = Database.new('system');
local settings = Settings.new(dbh, domain_name, domain_uuid);
--set debug
debug["sql"] = true;
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
local function empty(t)
return (not t) or (#t == 0)
end
--get the events
--if (user == nil) then
--serialize the data for the console
--freeswitch.consoleLog("notice","[events] " .. event:serialize("xml") .. "\n");
--freeswitch.consoleLog("notice","[evnts] " .. event:serialize("json") .. "\n");
--get the event variables
user = event:getHeader("user");
host = event:getHeader("host");
domain_name = event:getHeader("host");
contact = event:getHeader("contact");
feature_action = event:getHeader("Feature-Action");
feature_enabled = event:getHeader("Feature-Enabled");
action_name = event:getHeader("Action-Name");
action_value = event:getHeader("Action-Value")
ring_count = event:getHeader("ringCount")
--send to the log
--freeswitch.consoleLog("notice","[events] user: " .. user .. "\n");
--freeswitch.consoleLog("notice","[events] host: " .. host .. "\n");
--if (feature_action ~= nil) then freeswitch.consoleLog("notice","[events] feature_action: " .. feature_action .. "\n"); end
--if (feature_enabled ~= nil) then freeswitch.consoleLog("notice","[events] feature_enabled: " .. feature_enabled .. "\n"); end
--if (action_name ~= nil) then freeswitch.consoleLog("notice","[events] action_name: " .. action_name .. "\n"); end
--if (action_value ~= nil) then freeswitch.consoleLog("notice","[events] action_value: " .. action_value .. "\n"); end
--end
--get the domain uuid from the host
local sql = "select * from v_domains ";
sql = sql .. "where domain_name = :domain_name ";
local params = {domain_name = domain_name};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[feature_event] " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
domain_uuid = row.domain_uuid;
end);
--get extension information
if (user ~= nil and domain_name ~= nil) then
do_not_disturb, forward_all_enabled, forward_all_destination, forward_busy_enabled, forward_busy_destination, forward_no_answer_enabled, forward_no_answer_destination, call_timeout = notify.get_db_values(user, domain_name)
end
--get sip profile
if (user ~= nil and host ~= nil) then
sip_profile = notify.get_profile(user, host);
end
--DND
--DND enabled
if (feature_action == "SetDoNotDisturb" and feature_enabled == "true" and sip_profile ~= nil) then
--set a variable
dial_string = "error/user_busy";
do_not_disturb = "true";
--update the extension
sql = "update v_extensions set ";
sql = sql .. "do_not_disturb = :do_not_disturb, ";
sql = sql .. "forward_all_enabled = 'false', ";
sql = sql .. "dial_string = :dial_string ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and extension_uuid = :extension_uuid ";
local params = {domain_uuid = domain_uuid, extension_uuid = extension_uuid, do_not_disturb = do_not_disturb, dial_string = dial_string};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[feature_event] "..sql.."; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--update follow me
if (follow_me_uuid ~= nil) then
if (string.len(follow_me_uuid) > 0) then
local sql = "update v_follow_me set ";
sql = sql .. "follow_me_enabled = 'false' ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and follow_me_uuid = :follow_me_uuid ";
local params = {domain_uuid = domain_uuid, follow_me_uuid = follow_me_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[feature_event] "..sql.."; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
end
end
--send notify to the phone
notify.dnd(user, host, sip_profile, do_not_disturb);
end
--DND disabled
if (feature_action == "SetDoNotDisturb" and feature_enabled == "false" and sip_profile ~= nil ) then
--set a variable
do_not_disturb = "false";
--update the extension
sql = "update v_extensions set ";
sql = sql .. "do_not_disturb = :do_not_disturb, ";
sql = sql .. "forward_all_enabled = 'false', ";
sql = sql .. "dial_string = null ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and extension_uuid = :extension_uuid ";
local params = {domain_uuid = domain_uuid, extension_uuid = extension_uuid, do_not_disturb = do_not_disturb};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[feature_event] "..sql.."; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--send notify to the phone
notify.dnd(user, host, sip_profile, do_not_disturb);
end
--Call Forward
--Call Formward All enabled
if (feature_action == "SetCallForward" and feature_enabled == "true" and action_name == "forward_immediate" and sip_profile ~= nil) then
--set a variable
forward_all_destination = action_value;
forward_all_enabled = "true";
forward_immediate_destination = action_value;
forward_immediate_enabled = "true";
--get the caller_id for outbound call
local forward_caller_id = ""
if not empty(forward_caller_id_uuid) then
local sql = "select destination_number, destination_description,"..
"destination_caller_id_number, destination_caller_id_name " ..
"from v_destinations where domain_uuid = :domain_uuid and " ..
"destination_type = 'inbound' and destination_uuid = :destination_uuid";
local params = {domain_uuid = domain_uuid; destination_uuid = forward_caller_id_uuid}
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[feature_event] "..sql.."; params:" .. json.encode(params) .. "\n");
end
local row = dbh:first_row(sql, params)
if row then
local caller_id_number = row.destination_caller_id_number
if empty(caller_id_number) then
caller_id_number = row.destination_number
end
local caller_id_name = row.destination_caller_id_name
if empty(caller_id_name) then
caller_id_name = row.destination_description
end
if not empty(caller_id_number) then
forward_caller_id = forward_caller_id ..
",outbound_caller_id_number=" .. caller_id_number ..
",origination_caller_id_number=" .. caller_id_number
end
if not empty(caller_id_name) then
forward_caller_id = forward_caller_id ..
",outbound_caller_id_name=" .. caller_id_name ..
",origination_caller_id_name=" .. caller_id_name
end
end
end
--set the dial string
if feature_enabled == "true" then
local destination_extension, destination_number_alias
--used for number_alias to get the correct user
local sql = "select extension, number_alias from v_extensions ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and number_alias = :number_alias ";
local params = {domain_uuid = domain_uuid; number_alias = forward_all_destination}
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[feature_event] "..sql.."; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
destination_user = row.extension;
destination_extension = row.extension;
destination_number_alias = row.number_alias or '';
end);
if (destination_user ~= nil) then
cmd = "user_exists id ".. destination_user .." "..domain_name;
else
cmd = "user_exists id ".. forward_all_destination .." "..domain_name;
end
local user_exists = trim(api:executeString(cmd));
end
--update the extension
sql = "update v_extensions set ";
sql = sql .. "do_not_disturb = 'false', ";
sql = sql .. "forward_all_enabled = 'true', ";
sql = sql .. "forward_all_destination = :forward_all_destination, ";
sql = sql .. "dial_string = null ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and extension_uuid = :extension_uuid ";
local params = {domain_uuid = domain_uuid, extension_uuid = extension_uuid, forward_all_destination = forward_all_destination};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[feature_event] "..sql.."; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--update follow me
if (follow_me_uuid ~= nil) then
if (string.len(follow_me_uuid) > 0) then
local sql = "update v_follow_me set ";
sql = sql .. "follow_me_enabled = 'false' ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and follow_me_uuid = :follow_me_uuid ";
local params = {domain_uuid = domain_uuid, follow_me_uuid = follow_me_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[feature_event] "..sql.."; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
end
end
--send notify to the phone
notify.forward_immediate(user, host, sip_profile, forward_immediate_enabled, forward_immediate_destination);
end
--Call Formward All disable
if (feature_action == "SetCallForward" and feature_enabled == "false" and action_name == "forward_immediate" and sip_profile ~= nil) then
--set a variable
forward_all_destination = action_value;
forward_all_enabled = "false";
forward_immediate_enabled = "false";
forward_immediate_destination = action_value;
--update the extension
sql = "update v_extensions set ";
sql = sql .. "do_not_disturb = 'false', ";
sql = sql .. "forward_all_enabled = 'false', ";
if (forward_all_destination ~= nil) then
sql = sql .. "forward_all_destination = :forward_all_destination, ";
else
sql = sql .. "forward_all_destination = null, ";
end
sql = sql .. "dial_string = null ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and extension_uuid = :extension_uuid ";
local params = {domain_uuid = domain_uuid, extension_uuid = extension_uuid, forward_all_destination = forward_all_destination};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[feature_event] "..sql.."; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--send notify to the phone
if (forward_immediate_destination == nil) then
forward_immediate_destination = " ";
end
notify.forward_immediate(user, host, sip_profile, forward_immediate_enabled, forward_immediate_destination);
end
--Call Formward BUSY enable
if (feature_action == "SetCallForward" and feature_enabled == "true" and action_name == "forward_busy" and sip_profile ~= nil) then
--set a variable
forward_busy_destination = action_value;
forward_busy_enabled = "true";
--update the extension
sql = "update v_extensions set ";
sql = sql .. "do_not_disturb = 'false', ";
sql = sql .. "forward_busy_enabled = 'true', ";
sql = sql .. "forward_busy_destination = :forward_busy_destination ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and extension_uuid = :extension_uuid ";
local params = {domain_uuid = domain_uuid, extension_uuid = extension_uuid, forward_busy_destination = forward_busy_destination};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[feature_event] "..sql.."; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--send notify to the phone
notify.forward_busy(user, host, sip_profile, forward_busy_enabled, forward_busy_destination);
end
--Call Formward BUSY disable
if (feature_action == "SetCallForward" and feature_enabled == "false" and action_name == "forward_busy" and sip_profile ~= nil) then
--set a variable
forward_busy_destination = action_value;
forward_busy_enabled = "false";
--update the extension
sql = "update v_extensions set ";
sql = sql .. "do_not_disturb = 'false', ";
sql = sql .. "forward_busy_enabled = 'false', ";
if (forward_busy_destination ~= nil) then
sql = sql .. "forward_busy_destination = :forward_busy_destination ";
else
sql = sql .. "forward_busy_destination = null ";
end
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and extension_uuid = :extension_uuid ";
local params = {domain_uuid = domain_uuid, extension_uuid = extension_uuid, forward_busy_destination = forward_busy_destination};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[feature_event] "..sql.."; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--send notify to the phone
notify.forward_busy(user, host, sip_profile, forward_busy_enabled, forward_busy_destination);
end
--Call Formward NO ANSWER enable
if (feature_action == "SetCallForward" and feature_enabled == "true" and action_name == "forward_no_answer" and sip_profile ~= nil) then
--set a variable
forward_no_answer_destination = action_value;
forward_no_answer_enabled = "true";
call_timeout = ring_count * 6;
--update the extension
sql = "update v_extensions set ";
sql = sql .. "do_not_disturb = 'false', ";
sql = sql .. "call_timeout = :call_timeout, ";
sql = sql .. "forward_no_answer_enabled = 'true', ";
sql = sql .. "forward_no_answer_destination = :forward_no_answer_destination ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and extension_uuid = :extension_uuid ";
local params = {domain_uuid = domain_uuid, extension_uuid = extension_uuid, forward_no_answer_destination = forward_no_answer_destination, call_timeout = call_timeout};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[feature_event] "..sql.."; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--send notify to the phone
notify.forward_no_answer(user, host, sip_profile, forward_no_answer_enabled, forward_no_answer_destination, ring_count);
end
--Call Formward NO ANSWER disable
if (feature_action == "SetCallForward" and feature_enabled == "false" and action_name == "forward_no_answer" and sip_profile ~= nil) then
--set a variable
forward_no_answer_destination = action_value;
forward_no_answer_enabled = "false";
--update the extension
sql = "update v_extensions set ";
sql = sql .. "do_not_disturb = 'false', ";
sql = sql .. "forward_no_answer_enabled = 'false', ";
if (forward_no_answer_destination ~= nil) then
sql = sql .. "forward_no_answer_destination = :forward_no_answer_destination ";
else
sql = sql .. "forward_no_answer_destination = null ";
end
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and extension_uuid = :extension_uuid ";
local params = {domain_uuid = domain_uuid, extension_uuid = extension_uuid, forward_no_answer_destination = forward_no_answer_destination, call_timeout = call_timeout};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[feature_event] "..sql.."; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--send notify to the phone
notify.forward_no_answer(user, host, sip_profile, forward_no_answer_enabled, forward_no_answer_destination, ring_count);
end
--No feature event (phone boots): Send all values
if (feature_enabled == nil) then
--Do Not Disturb
--notify.dnd(user, host, sip_profile, do_not_disturb);
--Forward all
forward_immediate_enabled = forward_all_enabled;
forward_immediate_destination = forward_all_destination;
--notify.forward_immediate(user, host, sip_profile, forward_immediate_enabled, forward_immediate_destination);
--Forward busy
--notify.forward_busy(user, host, sip_profile, forward_busy_enabled, forward_busy_destination);
--Forward No Answer
ring_count = math.ceil (call_timeout / 6);
--notify.forward_no_answer(user, host, sip_profile, forward_no_answer_enabled, forward_no_answer_destination, ring_count);
notify.init(user,
host,
sip_profile,
forward_immediate_enabled,
forward_immediate_destination,
forward_busy_enabled,
forward_busy_destination,
forward_no_answer_enabled,
forward_no_answer_destination,
ring_count,
do_not_disturb);
end
-- feature_event_notify.init(user, host, sip_profile, forward_immediate_enabled, forward_immediate_destination, forward_busy_enabled, forward_busy_destination, forward_no_answer_enabled, forward_no_answer_destination, ring_count, do_not_disturb)
--clear the cache
if (feature_enabled ~= nil) then
cache.del("directory:"..user.."@"..host)
end

View File

@@ -0,0 +1,149 @@
local feature_event_notify = {}
function feature_event_notify.get_db_values(user, domain_name)
--get the domain uuid from the host
local Database = require "resources.functions.database";
local dbh = Database.new('system');
local sql = "select * from v_domains ";
sql = sql .. "where domain_name = :domain_name ";
local params = {domain_name = domain_name};
-- if (debug["sql"]) then
-- freeswitch.consoleLog("notice", "[feature_event] " .. sql .. "; params:" .. json.encode(params) .. "\n");
-- end
dbh:query(sql, params, function(row)
domain_uuid = row.domain_uuid;
end);
--get extension information
local sql = "select * from v_extensions ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and (extension = :extension or number_alias = :extension) ";
local params = {domain_uuid = domain_uuid, extension = user};
-- if (debug["sql"]) then
-- freeswitch.consoleLog("notice", "[feature_event] " .. sql .. "; params:" .. json.encode(params) .. "\n");
-- end
dbh:query(sql, params, function(row)
extension_uuid = row.extension_uuid;
extension = row.extension;
number_alias = row.number_alias or '';
accountcode = row.accountcode;
follow_me_uuid = row.follow_me_uuid;
do_not_disturb = row.do_not_disturb;
forward_all_enabled = row.forward_all_enabled;
forward_all_destination = row.forward_all_destination;
forward_busy_enabled = row.forward_busy_enabled;
forward_busy_destination = row.forward_busy_destination;
forward_no_answer_enabled = row.forward_no_answer_enabled;
forward_no_answer_destination = row.forward_no_answer_destination;
forward_user_not_registered_enabled = row.forward_user_not_registered_enabled;
forward_user_not_registered_destination = row.forward_user_not_registered_destination;
forward_caller_id_uuid = row.forward_caller_id_uuid;
toll_allow = row.toll_allow
call_timeout = row.call_timeout
--freeswitch.consoleLog("NOTICE", "[feature_event] extension "..row.extension.."\n");
--freeswitch.consoleLog("NOTICE", "[feature_event] accountcode "..row.accountcode.."\n");
end);
--set some defaults if values in database are NULL
if (forward_all_enabled == "") then forward_all_enabled = "false"; end
--if (forward_all_destination == "") then forward_all_destination = nil; end
if (forward_busy_enabled == "") then forward_busy_enabled = "false"; end
if (forward_no_answer_enabled == "") then forward_no_answer_enabled = "false"; end
if (do_not_disturb == "") then do_not_disturb = "false"; end
if (call_timeout == "") then call_timeout = "30"; end
return do_not_disturb, forward_all_enabled, forward_all_destination, forward_busy_enabled, forward_busy_destination, forward_no_answer_enabled, forward_no_answer_destination, call_timeout
end
function feature_event_notify.get_profile(user, host)
--includes
require "resources.functions.explode"
require "resources.functions.trim"
local account = user.."@"..host
--create the api object
api = freeswitch.API();
local sofia_contact = trim(api:executeString("sofia_contact */"..account));
local array = explode("/", sofia_contact);
local sip_profile = array[2];
return sip_profile
end
function feature_event_notify.dnd(user, host, sip_profile, do_not_disturb)
--set the event and send it
local event = freeswitch.Event("SWITCH_EVENT_PHONE_FEATURE")
event:addHeader("profile", sip_profile)
event:addHeader("user", user)
event:addHeader("host", host)
event:addHeader("device", "")
event:addHeader("Feature-Event", "DoNotDisturbEvent")
event:addHeader("doNotDisturbOn", do_not_disturb)
--freeswitch.consoleLog("notice","[events] " .. event:serialize("xml") .. "\n");
event:fire()
end
function feature_event_notify.forward_immediate(user, host, sip_profile, forward_immediate_enabled, forward_immediate_destination)
--set the event and send it
local event = freeswitch.Event("SWITCH_EVENT_PHONE_FEATURE")
event:addHeader("profile", sip_profile)
event:addHeader("user", user)
event:addHeader("host", host)
event:addHeader("device", "")
event:addHeader("Feature-Event", "ForwardingEvent")
event:addHeader("forward_immediate_enabled", forward_immediate_enabled)
event:addHeader("forward_immediate", forward_immediate_destination);
freeswitch.consoleLog("notice","[events] " .. event:serialize("xml") .. "\n");
event:fire()
end
function feature_event_notify.forward_busy(user, host, sip_profile, forward_busy_enabled, forward_busy_destination)
--set the event and send it
local event = freeswitch.Event("SWITCH_EVENT_PHONE_FEATURE")
event:addHeader("profile", sip_profile)
event:addHeader("user", user)
event:addHeader("host", host)
event:addHeader("device", "")
event:addHeader("Feature-Event", "ForwardingEvent")
event:addHeader("forward_busy", forward_busy_destination)
event:addHeader("forward_busy_enabled", forward_busy_enabled)
event:fire()
end
function feature_event_notify.forward_no_answer(user, host, sip_profile, forward_no_answer_enabled, forward_no_answer_destination, ring_count)
--set the event and send it
local event = freeswitch.Event("SWITCH_EVENT_PHONE_FEATURE")
event:addHeader("profile", sip_profile)
event:addHeader("user", user)
event:addHeader("host", host)
event:addHeader("device", "")
event:addHeader("Feature-Event", "ForwardingEvent")
event:addHeader("forward_no_answer", forward_no_answer_destination)
event:addHeader("forward_no_answer_enabled", forward_no_answer_enabled)
event:addHeader("ringCount", ring_count)
event:fire()
end
function feature_event_notify.init(user, host, sip_profile, forward_immediate_enabled, forward_immediate_destination, forward_busy_enabled, forward_busy_destination, forward_no_answer_enabled, forward_no_answer_destination, ring_count, do_not_disturb)
--set the event and send it
local event = freeswitch.Event("SWITCH_EVENT_PHONE_FEATURE")
event:addHeader("profile", sip_profile)
event:addHeader("user", user)
event:addHeader("host", host)
event:addHeader("device", "")
event:addHeader("Feature-Event", "init")
event:addHeader("forward_immediate_enabled", forward_immediate_enabled)
event:addHeader("forward_immediate", forward_immediate_destination);
event:addHeader("forward_busy", forward_busy_destination)
event:addHeader("forward_busy_enabled", forward_busy_enabled)
event:addHeader("Feature-Event", "ForwardingEvent")
event:addHeader("forward_no_answer", forward_no_answer_destination)
event:addHeader("forward_no_answer_enabled", forward_no_answer_enabled)
event:addHeader("ringCount", ring_count)
event:addHeader("Feature-Event", "DoNotDisturbEvent")
event:addHeader("doNotDisturbOn", do_not_disturb)
event:fire()
end
return feature_event_notify

View File

@@ -0,0 +1,472 @@
-- FusionPBX
-- Version: MPL 1.1
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
-- The Original Code is FusionPBX
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Portions created by the Initial Developer are Copyright (C) 2019
-- the Initial Developer. All Rights Reserved.
--includes
local Database = require "resources.functions.database";
local route_to_bridge = require "resources.functions.route_to_bridge"
require "resources.functions.trim";
--get the variables
if (session:ready()) then
domain_name = session:getVariable("domain_name");
domain_uuid = session:getVariable("domain_uuid");
destination_number = session:getVariable("destination_number");
caller_id_name = session:getVariable("caller_id_name");
caller_id_number = session:getVariable("caller_id_number");
outbound_caller_id_name = session:getVariable("outbound_caller_id_name");
outbound_caller_id_number = session:getVariable("outbound_caller_id_number");
call_direction = session:getVariable("call_direction");
original_destination_number = session:getVariable("destination_number");
end
--set caller id
if (effective_caller_id_name ~= nil) then
caller_id_name = effective_caller_id_name;
end
if (effective_caller_id_number ~= nil) then
caller_id_number = effective_caller_id_number;
end
--default to local if nil
if (call_direction == nil) then
call_direction = "local";
end
--set the strategy
follow_me_strategy = 'simultaneous'; --simultaneous, enterprise
--include json library
debug["sql"] = false;
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson";
end
--prepare the api object
api = freeswitch.API();
--get the destination and follow the forward
function get_forward_all(count, destination_number, domain_name)
cmd = "user_exists id ".. destination_number .." "..domain_name;
--freeswitch.consoleLog("notice", "[follow me][call forward all] " .. cmd .. "\n");
user_exists = api:executeString(cmd);
if (user_exists == "true") then
---check to see if the new destination is forwarded
cmd = "user_data ".. destination_number .."@" ..domain_name.." var forward_all_enabled";
if (api:executeString(cmd) == "true") then
--get the toll_allow var
cmd = "user_data ".. destination_number .."@" ..domain_name.." var toll_allow";
toll_allow = api:executeString(cmd);
--freeswitch.consoleLog("notice", "[follow me][call forward all] " .. destination_number .. " toll_allow is ".. toll_allow .."\n");
--get the new destination
cmd = "user_data ".. destination_number .."@" ..domain_name.." var forward_all_destination";
destination_number = api:executeString(cmd);
--freeswitch.consoleLog("notice", "[follow me][call forward all] " .. count .. " " .. cmd .. " ".. destination_number .."\n");
count = count + 1;
if (count < 5) then
count, destination_number = get_forward_all(count, destination_number, domain_name);
end
end
end
return count, destination_number, toll_allow;
end
--connect to the database
local dbh = Database.new('system');
--get the forward busy
--cmd = "user_data ".. destination_number .."@"..domain_name.." var forward_busy_enabled=";
--forward_busy_enabled = trim(api:executeString(cmd));
--cmd = "user_data ".. destination_number .."@"..domain_name.." var forward_busy_destination=";
--forward_busy_destination = trim(api:executeString(cmd));
--get the domain_uuid
if (domain_uuid == nil) then
if (domain_name ~= nil) then
local sql = "SELECT domain_uuid FROM v_domains "
.. "WHERE domain_name = :domain_name ";
local params = {domain_name = domain_name};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(rows)
domain_uuid = rows["domain_uuid"];
end);
end
end
--select data from the database
local sql = "select follow_me_uuid, toll_allow ";
sql = sql .. "from v_extensions ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and ( ";
sql = sql .. " extension = :destination_number ";
sql = sql .. " OR number_alias = :destination_number ";
sql = sql .. ") ";
local params = {domain_uuid = domain_uuid,destination_number = destination_number};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "SQL:" .. sql .. "; params: " .. json.encode(params) .. "\n");
end
status = dbh:query(sql, params, function(row)
follow_me_uuid = row["follow_me_uuid"];
extension_toll_allow = row["toll_allow"];
end);
--dbh:query(sql, params, function(row);
--get the follow me data
if (follow_me_uuid ~= nil) then
local sql = "select cid_name_prefix, cid_number_prefix, ";
sql = sql .. "follow_me_enabled, follow_me_caller_id_uuid, follow_me_ignore_busy ";
sql = sql .. "from v_follow_me ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and follow_me_uuid = :follow_me_uuid; ";
local params = {domain_uuid = domain_uuid,follow_me_uuid = follow_me_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "SQL:" .. sql .. "; params: " .. json.encode(params) .. "\n");
end
status = dbh:query(sql, params, function(row)
caller_id_name_prefix = row["cid_name_prefix"];
caller_id_number_prefix = row["cid_number_prefix"];
follow_me_enabled = row["follow_me_enabled"];
follow_me_caller_id_uuid = row["follow_me_caller_id_uuid"];
follow_me_ignore_busy = row["follow_me_ignore_busy"];
end);
--dbh:query(sql, params, function(row);
end
--get the follow me destinations
if (follow_me_uuid ~= nil) then
sql = "select d.domain_uuid, d.domain_name, f.follow_me_destination as destination_number, ";
sql = sql .. "f.follow_me_delay as destination_delay, f.follow_me_timeout as destination_timeout, ";
sql = sql .. "f.follow_me_prompt as destination_prompt ";
sql = sql .. "from v_follow_me_destinations as f, v_domains as d ";
sql = sql .. "where f.follow_me_uuid = :follow_me_uuid ";
sql = sql .. "and f.domain_uuid = d.domain_uuid ";
sql = sql .. "order by f.follow_me_order; ";
local params = {follow_me_uuid = follow_me_uuid};
destinations = {};
destination_count = 0;
x = 1;
dbh:query(sql, params, function(row)
domain_uuid = row["domain_uuid"];
domain_name = row["domain_name"];
if (row.destination_prompt == "1" or row.destination_prompt == "2") then
prompt = "true";
end
--follow the forwards
count, destination_number, toll_allow = get_forward_all(0, row.destination_number, domain_name);
--update values
row['destination_number'] = destination_number
--row['toll_allow'] = toll_allow;
--check if the user exists
cmd = "user_exists id ".. destination_number .." "..domain_name;
user_exists = api:executeString(cmd);
--cmd = "user_exists id ".. destination_number .." "..domain_name;
if (user_exists == "true") then
--add user_exists true or false to the row array
row['user_exists'] = "true";
--handle do_not_disturb
cmd = "user_data ".. destination_number .."@" ..domain_name.." var do_not_disturb";
if (api:executeString(cmd) ~= "true") then
--add the row to the destinations array
destinations[x] = row;
end
else
--set the values
external = "true";
row['user_exists'] = "false";
--add the row to the destinations array
destinations[x] = row;
end
row['domain_name'] = domain_name;
destination_count = destination_count + 1;
x = x + 1;
end);
end
--get the dialplan data and save it to a table
if (external == "true") then
dialplans = route_to_bridge.preload_dialplan(
dbh, domain_uuid, {hostname = hostname, context = context}
)
end
--prepare the array of destinations
x = 1;
for key, row in pairs(destinations) do
--set the values from the database as variables
destination_number = row.destination_number;
--determine if the user is registered if not registered then lookup
if (user_exists == "true") then
cmd = "sofia_contact */".. destination_number .."@" ..domain_name;
if (api:executeString(cmd) == "error/user_not_registered") then
freeswitch.consoleLog("NOTICE", "[follow_me] "..cmd.."\n");
cmd = "user_data ".. destination_number .."@" ..domain_name.." var forward_user_not_registered_enabled";
freeswitch.consoleLog("NOTICE", "[follow_me] "..cmd.."\n");
if (api:executeString(cmd) == "true") then
--get the new destination number
cmd = "user_data ".. destination_number .."@" ..domain_name.." var forward_user_not_registered_destination";
freeswitch.consoleLog("NOTICE", "[follow_me] "..cmd.."\n");
not_registered_destination_number = api:executeString(cmd);
freeswitch.consoleLog("NOTICE", "[follow_me] "..not_registered_destination_number.."\n");
if (not_registered_destination_number ~= nil) then
destination_number = not_registered_destination_number;
destinations[key]['destination_number'] = destination_number;
end
end
end
end
end
--process the destinations
x = 1;
for key, row in pairs(destinations) do
freeswitch.consoleLog("NOTICE", "[follow me] destination_number: "..row.destination_number.."\n");
end
--process the destinations
x = 1;
for key, row in pairs(destinations) do
--set the values from the database as variables
domain_uuid = row.domain_uuid;
destination_number = row.destination_number;
destination_delay = row.destination_delay;
destination_timeout = row.destination_timeout;
destination_prompt = row.destination_prompt;
group_confirm_key = row.group_confirm_key;
group_confirm_file = row.group_confirm_file;
toll_allow = row.toll_allow;
user_exists = row.user_exists;
--follow the forwards
count, destination_number = get_forward_all(0, destination_number, domain_name);
--check if the user exists
cmd = "user_exists id ".. destination_number .." "..domain_name;
user_exists = api:executeString(cmd);
--set ringback
--follow_me_ringback = format_ringback(follow_me_ringback);
--session:setVariable("ringback", follow_me_ringback);
--session:setVariable("transfer_ringback", follow_me_ringback);
--set the timeout if there is only one destination
if (session:ready() and destination_count == 1) then
session:execute("set", "call_timeout="..row.destination_timeout);
end
--setup the delimiter
delimiter = ",";
if (follow_me_strategy == "simultaneous") then
delimiter = ",";
end
if (follow_me_strategy == "enterprise") then
delimiter = ":_:";
end
--leg delay settings
if (follow_me_strategy == "enterprise") then
timeout_name = "originate_timeout";
delay_name = "originate_delay_start";
destination_delay = destination_delay * 500;
else
timeout_name = "leg_timeout";
delay_name = "leg_delay_start";
end
--set confirm
if (session:ready() and follow_me_strategy == "simultaneous") then
session:execute("set", "group_confirm_key=exec");
session:execute("set", "group_confirm_file=lua ".. scripts_dir:gsub('\\','/') .."/confirm.lua");
end
--determine confirm prompt
if (destination_prompt == nil) then
group_confirm = "confirm=false,";
elseif (destination_prompt == "1") then
group_confirm = "group_confirm_key=exec,group_confirm_file=lua ".. scripts_dir:gsub('\\','/') .."/confirm.lua,confirm=true";
elseif (destination_prompt == "2") then
group_confirm = "group_confirm_key=exec,group_confirm_file=lua ".. scripts_dir:gsub('\\','/') .."/confirm.lua,confirm=true";
else
group_confirm = "confirm=false";
end
--process according to user_exists, sip_uri, external number
if (user_exists == "true") then
--get the extension_uuid
cmd = "user_data ".. destination_number .."@"..domain_name.." var extension_uuid";
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..","..timeout_name.."="..destination_timeout..","..delay_name.."="..destination_delay..",dialed_extension=" .. row.destination_number .. ",extension_uuid="..extension_uuid .. "]user/" .. row.destination_number .. "@" .. domain_name;
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..","..timeout_name.."="..destination_timeout..","..delay_name.."="..destination_delay.."]" .. row.destination_number;
else
--external number
route_bridge = 'loopback/'..destination_number;
if (extension_toll_allow ~= nil) then
toll_allow = extension_toll_allow:gsub(",", ":");
end
--set the toll allow to an empty string
if (toll_allow == nil) then
toll_allow = '';
end
--get the destination caller id name and number
if (follow_me_caller_id_uuid ~= nil) then
local sql = "select destination_uuid, destination_number, destination_description, destination_caller_id_name, destination_caller_id_number ";
sql = sql .. "from v_destinations ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and destination_uuid = :destination_uuid ";
sql = sql .. "order by destination_number asc ";
local params = {domain_uuid = domain_uuid, destination_uuid = follow_me_caller_id_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "SQL:" .. sql .. "; params: " .. json.encode(params) .. "\n");
end
status = dbh:query(sql, params, function(field)
caller_id_name = field["destination_caller_id_name"];
caller_id_number = field["destination_caller_id_number"];
end);
end
--check if the user exists
if tonumber(caller_id_number) ~= nil then
cmd = "user_exists id ".. caller_id_number .." "..domain_name;
caller_is_local = api:executeString(cmd);
end
--set the outbound caller id
if (session:ready() and caller_is_local) then
if (outbound_caller_id_name ~= nil) then
caller_id_name = outbound_caller_id_name;
end
if (outbound_caller_id_number ~= nil) then
caller_id_number = outbound_caller_id_number;
end
end
--set the caller id
caller_id = '';
if (caller_id_name ~= nil) then
caller_id = "origination_caller_id_name='"..caller_id_name.."'"
end
if (caller_id_number ~= nil) then
caller_id = caller_id .. ",origination_caller_id_number="..caller_id_number;
end
--set the destination dial string
dial_string = "[ignore_early_media=true,toll_allow=".. toll_allow ..",".. caller_id ..",sip_invite_domain="..domain_name..",call_direction="..call_direction..","..group_confirm..","..timeout_name.."="..destination_timeout..","..delay_name.."="..destination_delay.."]"..route_bridge
end
--add a delimiter between destinations
if (dial_string ~= nil) then
--freeswitch.consoleLog("notice", "[follow me] dial_string: " .. dial_string .. "\n");
if (x == 1) then
if (follow_me_strategy == "enterprise") then
app_data = dial_string;
else
app_data = "{ignore_early_media=true}"..dial_string;
end
else
if (app_data == nil) then
if (follow_me_strategy == "enterprise") then
app_data = dial_string;
else
app_data = "{ignore_early_media=true}"..dial_string;
end
else
app_data = app_data .. delimiter .. dial_string;
end
end
end
--increment the value of x
x = x + 1;
end
--set ring ready
if (session:ready()) then
session:execute("ring_ready", "");
end
--send to the console
freeswitch.consoleLog("notice", "[app:follow_me] " .. destination_number .. "\n");
--session execute
if (session:ready()) then
--set the variables
session:execute("set", "hangup_after_bridge=true");
session:execute("set", "continue_on_fail=true");
--execute the bridge
if (app_data ~= nil) then
if (follow_me_strategy == "enterprise") then
app_data = app_data:gsub("%[", "{");
app_data = app_data:gsub("%]", "}");
end
freeswitch.consoleLog("NOTICE", "[follow me] app_data: "..app_data.."\n");
session:execute("bridge", app_data);
end
--timeout destination
if (app_data ~= nil) then
if session:ready() and (
session:getVariable("originate_disposition") == "ALLOTTED_TIMEOUT"
or session:getVariable("originate_disposition") == "NO_ANSWER"
or session:getVariable("originate_disposition") == "NO_USER_RESPONSE"
or session:getVariable("originate_disposition") == "USER_NOT_REGISTERED"
or session:getVariable("originate_disposition") == "NORMAL_TEMPORARY_FAILURE"
or session:getVariable("originate_disposition") == "NO_ROUTE_DESTINATION"
or session:getVariable("originate_disposition") == "USER_BUSY"
or session:getVariable("originate_disposition") == "RECOVERY_ON_TIMER_EXPIRE"
or session:getVariable("originate_disposition") == "failure"
) then
--get the forward no answer
cmd = "user_data ".. original_destination_number .."@"..domain_name.." var forward_no_answer_enabled";
forward_no_answer_enabled = trim(api:executeString(cmd));
cmd = "user_data ".. original_destination_number .."@"..domain_name.." var forward_no_answer_destination";
forward_no_answer_destination = trim(api:executeString(cmd));
cmd = "user_data ".. original_destination_number .."@"..domain_name.." var user_context";
user_context = trim(api:executeString(cmd));
--execute the time out action
if (forward_no_answer_enabled == 'true') then
session:transfer(forward_no_answer_destination, 'XML', user_context);
else
session:transfer('*99' .. original_destination_number, 'XML', user_context);
end
--check and report missed call
--missed();
end
end
end

View File

@@ -0,0 +1,150 @@
--
-- FusionPBX
-- Version: MPL 1.1
--
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
--
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
--
-- The Original Code is FusionPBX
--
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Copyright (C) 2015 - 2018
-- the Initial Developer. All Rights Reserved.
--set the debug options
debug["params"] = false;
debug["info"] = false;
debug["sql"] = false;
--general functions
require "resources.functions.config";
require "resources.functions.explode";
require "resources.functions.trim";
require "resources.functions.base64";
require "resources.functions.file_exists";
--load libraries
require 'resources.functions.send_mail'
--create the api object
api = freeswitch.API();
--check the missed calls
function missed()
if (missed_call_app ~= nil and missed_call_data ~= nil) then
if (missed_call_app == "email") then
--set the sounds path for the language, dialect and voice
default_language = env:getHeader("default_language");
default_dialect = env:getHeader("default_dialect");
default_voice = env:getHeader("default_voice");
if (not default_language) then default_language = 'en'; end
if (not default_dialect) then default_dialect = 'us'; end
if (not default_voice) then default_voice = 'callie'; end
--connect to the database
local Database = require "resources.functions.database";
local dbh = Database.new('system');
--get the templates
local sql = "SELECT * FROM v_email_templates ";
sql = sql .. "WHERE (domain_uuid = :domain_uuid or domain_uuid is null) ";
sql = sql .. "AND template_language = :template_language ";
sql = sql .. "AND template_category = 'missed' "
sql = sql .. "AND template_enabled = 'true' "
sql = sql .. "ORDER BY domain_uuid DESC "
local params = {domain_uuid = domain_uuid, template_language = default_language.."-"..default_dialect};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
subject = row["template_subject"];
body = row["template_body"];
end);
--prepare the headers
local headers = {}
headers["X-FusionPBX-Domain-UUID"] = domain_uuid;
headers["X-FusionPBX-Domain-Name"] = domain_name;
headers["X-FusionPBX-Call-UUID"] = uuid;
headers["X-FusionPBX-Email-Type"] = 'missed';
--prepare the subject
subject = subject:gsub("${caller_id_name}", caller_id_name);
subject = subject:gsub("${caller_id_number}", caller_id_number);
subject = subject:gsub("${sip_to_user}", sip_to_user);
subject = subject:gsub("${dialed_user}", dialed_user);
subject = trim(subject);
subject = '=?utf-8?B?'..base64.encode(subject)..'?=';
--prepare the body
body = body:gsub("${caller_id_name}", caller_id_name);
body = body:gsub("${caller_id_number}", caller_id_number);
body = body:gsub("${sip_to_user}", sip_to_user);
body = body:gsub("${dialed_user}", dialed_user);
body = body:gsub(" ", "&nbsp;");
body = body:gsub("%s+", "");
body = body:gsub("&nbsp;", " ");
body = body:gsub("\n", "");
body = body:gsub("\n", "");
body = body:gsub("'", "&#39;");
body = body:gsub([["]], "&#34;");
body = trim(body);
--send the emails
send_mail(headers,
missed_call_data,
{subject, body}
);
if (debug["info"]) then
freeswitch.consoleLog("notice", "[missed call] " .. caller_id_number .. "->" .. sip_to_user .. "emailed to " .. missed_call_data .. "\n");
end
end
end
end
-- show all channel variables
--serialized = env:serialize()
--freeswitch.consoleLog("INFO","[hangup]\n" .. serialized .. "\n")
-- set channel variables to lua variables
originate_disposition = env:getHeader("originate_disposition");
originate_causes = env:getHeader("originate_causes");
uuid = env:getHeader("uuid");
domain_uuid = env:getHeader("domain_uuid");
domain_name = env:getHeader("domain_name");
sip_to_user = env:getHeader("sip_to_user");
dialed_user = env:getHeader("dialed_user");
missed_call_app = env:getHeader("missed_call_app");
missed_call_data = env:getHeader("missed_call_data");
-- get the Caller ID
caller_id_name = env:getHeader("caller_id_name");
caller_id_number = env:getHeader("caller_id_number");
if (caller_id_name == nil) then
caller_id_name = env:getHeader("Caller-Caller-ID-Name");
end
if (caller_id_number == nil) then
caller_id_number = env:getHeader("Caller-Caller-ID-Number");
end
--show the logs
if (debug["info"] == true) then
freeswitch.consoleLog("INFO", "[hangup] originate_causes: " .. tostring(originate_causes) .. "\n");
freeswitch.consoleLog("INFO", "[hangup] originate_disposition: " .. tostring(originate_disposition) .. "\n");
end
--handle originate disposition
if (originate_disposition ~= nil) then
if (originate_disposition == "ORIGINATOR_CANCEL") then
missed();
end
end

View File

@@ -0,0 +1,137 @@
-- FusionPBX
-- Version: MPL 1.1
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
-- The Original Code is FusionPBX
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Portions created by the Initial Developer are Copyright (C) 2014-2019
-- the Initial Developer. All Rights Reserved.
--set defaults
expire = {}
expire["is_local"] = "3600";
--get the variables
domain_name = session:getVariable("domain_name");
destination_number = session:getVariable("destination_number");
outbound_caller_id_name = session:getVariable("outbound_caller_id_name");
outbound_caller_id_number = session:getVariable("outbound_caller_id_number");
--includes
local cache = require"resources.functions.cache"
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--prepare the api object
api = freeswitch.API();
--define the trim function
require "resources.functions.trim";
--set the cache key
key = "app:dialplan:outbound:is_local:" .. destination_number .. "@" .. domain_name;
--get the destination number
value, err = cache.get(key);
if (err == 'NOT FOUND') then
--connect to the database
local Database = require "resources.functions.database";
local dbh = Database.new('system');
--select data from the database
local sql = "SELECT destination_number, destination_context ";
sql = sql .. "FROM v_destinations ";
sql = sql .. "WHERE ( ";
sql = sql .. " destination_number = :destination_number ";
sql = sql .. " OR destination_prefix || destination_number = :destination_number ";
sql = sql .. ") ";
sql = sql .. "AND destination_type = 'inbound' ";
sql = sql .. "AND destination_enabled = 'true' ";
local params = {destination_number = destination_number};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "SQL:" .. sql .. "; params: " .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--set the local variables
destination_context = row.destination_context;
--set the cache
if (destination_number == row.destination_number) then
key = "app:dialplan:outbound:is_local:" .. destination_number .. "@" .. domain_name;
value = "destination_number=" .. row.destination_number .. "&destination_context=" .. destination_context;
ok, err = cache.set(key, value, expire["is_local"]);
else
key = "app:dialplan:outbound:is_local:" .. destination_number .. "@" .. domain_name;
value = "destination_number=" .. row.destination_number .. "&destination_context=" .. destination_context;
ok, err = cache.set(key, value, expire["is_local"]);
end
--log the result
freeswitch.consoleLog("notice", "[app:dialplan:outbound:is_local] " .. row.destination_number .. " XML " .. destination_context .. " source: database\n");
--set the outbound caller id
if (outbound_caller_id_name ~= nil) then
session:execute("set", "caller_id_name="..outbound_caller_id_name);
session:execute("set", "effective_caller_id_name="..outbound_caller_id_name);
end
if (outbound_caller_id_number ~= nil) then
session:execute("set", "caller_id_number="..outbound_caller_id_number);
session:execute("set", "effective_caller_id_number="..outbound_caller_id_number);
end
--transfer the call
session:transfer(row.destination_number, "XML", row.destination_context);
end);
else
--add the function
require "resources.functions.explode";
--define the array/table and variables
local var = {}
local key = "";
local value = "";
--parse the cache
key_pairs = explode("&", value);
for k,v in pairs(key_pairs) do
f = explode("=", v);
key = f[1];
value = f[2];
var[key] = value;
end
--set the outbound caller id
if (outbound_caller_id_name ~= nil) then
session:execute("set", "caller_id_name="..outbound_caller_id_name);
session:execute("set", "effective_caller_id_name="..outbound_caller_id_name);
end
if (outbound_caller_id_number ~= nil) then
session:execute("set", "caller_id_number="..outbound_caller_id_number);
session:execute("set", "effective_caller_id_number="..outbound_caller_id_number);
end
--send to the console
freeswitch.consoleLog("notice", "[app:dialplan:outbound:is_local] " .. value .. " source: cache\n");
--transfer the call
session:transfer(var["destination_number"], "XML", var["destination_context"]);
end

View File

@@ -0,0 +1,335 @@
--
-- Version: MPL 1.1
--
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
--
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
--
-- The Original Code is FusionPBX
--
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Copyright (C) 2018
-- Start the script
-- <!-- Subscribe to events -->
-- <hook event="CUSTOM" subclass="SMS::SEND_MESSAGE" script="app/messages/resources/events.lua"/>
--prepare the api object
api = freeswitch.API();
--define the functions
require "resources.functions.trim";
require "resources.functions.explode";
require "resources.functions.base64";
--include the database class
local Database = require "resources.functions.database";
--set debug
debug["sql"] = false;
--get the events
--serialize the data for the console
--freeswitch.consoleLog("notice","[events] " .. event:serialize("xml") .. "\n");
--freeswitch.consoleLog("notice","[evnts] " .. event:serialize("json") .. "\n");
--intialize settings
--from_user = '';
--get the event variables
uuid = event:getHeader("Core-UUID");
from_user = event:getHeader("from_user");
from_host = event:getHeader("from_host");
to_user = event:getHeader("to_user");
to_host = event:getHeader("to_host");
content_type = event:getHeader("type");
message_text = event:getBody();
--set required variables
if (from_user ~= nil and from_host ~= nil) then
message_from = from_user .. '@' .. from_host;
end
if (to_user ~= nil and to_host ~= nil) then
message_to = to_user .. '@' .. to_host;
end
message_type = 'message';
--connect to the database
dbh = Database.new('system');
--exits the script if we didn't connect properly
assert(dbh:connected());
--set debug
debug["sql"] = true;
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--check if the from user exits
if (from_user ~= nil and from_host ~= nil) then
cmd = "user_exists id ".. from_user .." "..from_host;
freeswitch.consoleLog("notice", "[messages][from] user exists " .. cmd .. "\n");
from_user_exists = api:executeString(cmd);
else
from_user_exists = 'false';
end
--check if the to user exits
if (to_user ~= nil and to_host ~= nil) then
cmd = "user_exists id ".. to_user .." "..to_host;
freeswitch.consoleLog("notice", "[messages][to] user exists " .. cmd .. "\n");
to_user_exists = api:executeString(cmd);
else
to_user_exists = 'false';
end
--add the message
if (from_user_exists == 'true') then
--set the direction
message_direction = 'send';
--get the from user_uuid
cmd = "user_data ".. from_user .."@"..from_host.." var domain_uuid";
domain_uuid = trim(api:executeString(cmd));
--get the from user_uuid
cmd = "user_data ".. from_user .."@"..from_host.." var user_uuid";
user_uuid = trim(api:executeString(cmd));
--get the from contact_uuid
cmd = "user_data ".. to_user .."@"..to_host.." var contact_uuid";
contact_uuid = trim(api:executeString(cmd));
--create a new uuid and add it to the uuid list
message_uuid = api:executeString("create_uuid");
--sql statement
sql = "INSERT INTO v_messages ";
sql = sql .."( ";
sql = sql .."domain_uuid, ";
sql = sql .."message_uuid, ";
sql = sql .."user_uuid, ";
if (contact_uuid ~= null and string.len(contact_uuid) > 0) then
sql = sql .."contact_uuid, ";
end
sql = sql .."message_direction, ";
sql = sql .."message_date, ";
sql = sql .."message_type, ";
if (message_from ~= null and string.len(message_from) > 0) then
sql = sql .."message_from, ";
end
sql = sql .."message_to, ";
sql = sql .."message_text ";
sql = sql ..") ";
sql = sql .."VALUES ( ";
sql = sql ..":domain_uuid, ";
sql = sql ..":message_uuid, ";
sql = sql ..":user_uuid, ";
if (contact_uuid ~= null and string.len(contact_uuid) > 0) then
sql = sql ..":contact_uuid, ";
end
sql = sql ..":message_direction, ";
sql = sql .."now(), ";
sql = sql ..":message_type, ";
if (message_from ~= null and string.len(message_from) > 0) then
sql = sql ..":message_from, ";
end
sql = sql ..":message_to, ";
sql = sql ..":message_text ";
sql = sql ..") ";
--set the parameters
local params= {}
params['domain_uuid'] = domain_uuid;
params['message_uuid'] = message_uuid;
params['user_uuid'] = user_uuid;
if (contact_uuid ~= null and string.len(contact_uuid) > 0) then
params['contact_uuid'] = contact_uuid;
end
params['message_direction'] = message_direction;
params['message_type'] = message_type;
if (message_from ~= null) then
params['message_from'] = message_from;
end
params['message_to'] = message_to;
params['message_text'] = message_text;
--show debug info
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[call_center] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
--run the query
dbh:query(sql, params);
end
if (to_user_exists == 'true') then
--sql statement
sql = "INSERT INTO v_messages ";
sql = sql .."( ";
sql = sql .."domain_uuid, ";
sql = sql .."message_uuid, ";
sql = sql .."user_uuid, ";
if (contact_uuid ~= null and string.len(contact_uuid) > 0) then
sql = sql .."contact_uuid, ";
end
sql = sql .."message_direction, ";
sql = sql .."message_date, ";
sql = sql .."message_type, ";
if (message_from ~= null and string.len(message_from) > 0) then
sql = sql .."message_from, ";
end
sql = sql .."message_to, ";
sql = sql .."message_text ";
sql = sql ..") ";
sql = sql .."VALUES ( ";
sql = sql ..":domain_uuid, ";
sql = sql ..":message_uuid, ";
sql = sql ..":user_uuid, ";
if (contact_uuid ~= null and string.len(contact_uuid) > 0) then
sql = sql ..":contact_uuid, ";
end
sql = sql ..":message_direction, ";
sql = sql .."now(), ";
sql = sql ..":message_type, ";
if (message_from ~= null and string.len(message_from) > 0) then
sql = sql ..":message_from, ";
end
sql = sql ..":message_to, ";
sql = sql ..":message_text ";
sql = sql ..") ";
--set the direction
message_direction = 'receive';
--get the from user_uuid
cmd = "user_data ".. to_user .."@"..to_host.." var domain_uuid";
domain_uuid = trim(api:executeString(cmd));
--get the from user_uuid
cmd = "user_data ".. to_user .."@"..to_host.." var user_uuid";
user_uuid = trim(api:executeString(cmd));
--get the from contact_uuid
cmd = "user_data ".. to_user .."@"..to_host.." var contact_uuid";
contact_uuid = trim(api:executeString(cmd));
--create a new uuid and add it to the uuid list
message_uuid = api:executeString("create_uuid");
--set the parameters
local params= {}
params['domain_uuid'] = domain_uuid;
params['message_uuid'] = message_uuid;
params['user_uuid'] = user_uuid;
if (contact_uuid ~= null and string.len(message_from) > 0) then
params['contact_uuid'] = contact_uuid;
end
params['message_direction'] = message_direction;
params['message_type'] = message_type;
params['message_from'] = message_from;
params['message_to'] = message_to;
params['message_text'] = message_text;
--show debug info
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[call_center] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
--run the query
dbh:query(sql, params);
else
--get setttings needed to send the message
require "resources.functions.settings";
settings = settings(domain_uuid);
if (settings['message'] ~= nil) then
http_method = '';
if (settings['message']['http_method'] ~= nil) then
if (settings['message']['http_method']['text'] ~= nil) then
http_method = settings['message']['http_method']['text'];
end
end
http_content_type = '';
if (settings['message']['http_content_type'] ~= nil) then
if (settings['message']['http_content_type']['text'] ~= nil) then
http_content_type = settings['message']['http_content_type']['text'];
end
end
http_destination = '';
if (settings['message']['http_destination'] ~= nil) then
if (settings['message']['http_destination']['text'] ~= nil) then
http_destination = settings['message']['http_destination']['text'];
end
end
http_auth_enabled = 'false';
if (settings['message']['http_auth_enabled'] ~= nil) then
if (settings['message']['http_auth_enabled']['boolean'] ~= nil) then
http_auth_enabled = settings['message']['http_auth_enabled']['boolean'];
end
end
http_auth_type = '';
if (settings['message']['http_auth_type'] ~= nil) then
if (settings['message']['http_auth_type']['text'] ~= nil) then
http_auth_type = settings['message']['http_auth_type']['text'];
end
end
http_auth_user = '';
if (settings['message']['http_auth_user'] ~= nil) then
if (settings['message']['http_auth_user']['text'] ~= nil) then
http_auth_user = settings['message']['http_auth_user']['text'];
end
end
http_auth_password = '';
if (settings['message']['http_auth_password'] ~= nil) then
if (settings['message']['http_auth_password']['text'] ~= nil) then
http_auth_password = settings['message']['http_auth_password']['text'];
end
end
end
--get the sip user outbound_caller_id
if (from_user ~= nil and from_host ~= nil) then
cmd = "user_data ".. from_user .."@"..from_host.." var outbound_caller_id_number";
from = trim(api:executeString(cmd));
else
from = '';
end
--replace variables for their value
http_destination = http_destination:gsub("${from}", from);
--send to the provider using curl
if (to_user ~= nil) then
cmd = [[curl ]].. http_destination ..[[ ]]
cmd = cmd .. [[-H "Content-Type: ]]..http_content_type..[[" ]];
if (http_auth_type == 'basic') then
cmd = cmd .. [[-H "Authorization: Basic ]]..base64.encode(http_auth_user..":"..http_auth_password)..[[" ]];
end
cmd = cmd .. [[-d '{"to":"]]..to_user..[[","text":"]]..message_text..[["}']]
result = api:executeString("system "..cmd);
--status = os.execute (cmd);
--debug - log the command
freeswitch.consoleLog("notice", "[message] " .. cmd.. "\n");
end
end

View File

@@ -0,0 +1,73 @@
-- FusionPBX
-- Version: MPL 1.1
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
-- The Original Code is FusionPBX
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Portions created by the Initial Developer are Copyright (C) 2018
-- the Initial Developer. All Rights Reserved.
--get the argv values
script_name = argv[0];
message_from = argv[1];
message_to = argv[2];
message_text = argv[3];
--send a message to the console
freeswitch.consoleLog("NOTICE",[[[message] from ]]..message_from);
freeswitch.consoleLog("NOTICE",[[[message] to ]] .. message_to);
freeswitch.consoleLog("NOTICE",[[[message] from ]]..message_text);
--connect to the database
--local Database = require "resources.functions.database";
--dbh = Database.new('system');
--include functions
require "resources.functions.trim";
require "resources.functions.explode";
--require "resources.functions.file_exists";
--create the api object
api = freeswitch.API();
--get the domain name for the destination
array = explode('@', message_to);
domain_name = array[2];
freeswitch.consoleLog("NOTICE",[[[message] domain_name ]]..domain_name);
--get the sip profile name
local sofia_contact = trim(api:executeString("sofia_contact */"..message_to));
local array = explode("/", sofia_contact);
local sip_profile = array[2];
--send the sms message
local event = freeswitch.Event("CUSTOM", "SMS::SEND_MESSAGE");
event:addHeader("proto", "sip");
event:addHeader("dest_proto", "sip");
event:addHeader("from", message_from);
event:addHeader("from_full", "sip:"..message_from);
event:addHeader("to", message_to);
event:addHeader("subject", "sip:"..message_to);
--event:addHeader("type", "text/html");
event:addHeader("type", "text/plain");
event:addHeader("hint", "the hint");
event:addHeader("replying", "true");
event:addHeader("sip_profile", sip_profile);
event:addBody(message_text);
--send info to the console
freeswitch.consoleLog("info", event:serialize());
--send the event
event:fire();

View File

@@ -0,0 +1 @@
Missed Call from ${caller_id_name} &lt;${caller_id_number}&gt; to ${sip_to_user} ext ${dialed_user}

View File

@@ -0,0 +1 @@
Missed Call from ${caller_id_name} <${caller_id_number}>

View File

@@ -0,0 +1,254 @@
-- Part of FusionPBX
-- Copyright (C) 2015-2018 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--set the debug options
debug["sql"] = false;
--define the explode function
require "resources.functions.explode";
require "resources.functions.trim";
--set the defaults
max_tries = 3;
digit_timeout = 5000;
max_retries = 3;
tries = 0;
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--answer
session:answer();
--sleep
session:sleep(500);
--get the domain_uuid
domain_uuid = session:getVariable("domain_uuid");
--get the variables
action = session:getVariable("action");
reboot = session:getVariable("reboot");
--set defaults
if (not reboot) then reboot = 'true'; end
--set the sounds path for the language, dialect and voice
default_language = session:getVariable("default_language");
default_dialect = session:getVariable("default_dialect");
default_voice = session:getVariable("default_voice");
if (not default_language) then default_language = 'en'; end
if (not default_dialect) then default_dialect = 'us'; end
if (not default_voice) then default_voice = 'callie'; end
--get the user id
min_digits = 2;
max_digits = 20;
user_id = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", "phrase:voicemail_enter_id:#", "", "\\d+");
--user_id = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-please_enter_extension_followed_by_pound.wav", "", "\\d+");
--get the user password
min_digits = 2;
max_digits = 20;
password = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", "phrase:voicemail_enter_pass:#", "", "\\d+");
--password = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-please_enter_pin_followed_by_pound.wav", "", "\\d+");
--get the user and domain name from the user argv user@domain
sip_from_uri = session:getVariable("sip_from_uri");
user_table = explode("@",sip_from_uri);
domain_table = explode(":",user_table[2]);
user = user_table[1];
domain = domain_table[1];
--show the phone that will be overridden
if (sip_from_uri ~= nil) then
freeswitch.consoleLog("NOTICE", "[provision] sip_from_uri: ".. sip_from_uri .. "\n");
end
if (user ~= nil) then
freeswitch.consoleLog("NOTICE", "[provision] user: ".. user .. "\n");
end
if (domain ~= nil) then
freeswitch.consoleLog("NOTICE", "[provision] domain: ".. domain .. "\n");
end
--get the device uuid for the phone that will have its configuration overridden
if (user ~= nil and domain ~= nil and domain_uuid ~= nil) then
local sql = [[SELECT device_uuid FROM v_device_lines ]];
sql = sql .. [[WHERE user_id = :user ]];
sql = sql .. [[AND server_address = :domain ]];
sql = sql .. [[AND domain_uuid = :domain_uuid ]];
local params = {user = user, domain = domain, domain_uuid = domain_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("NOTICE", "[provision] SQL: ".. sql .. "; params: " .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--get device uuid
device_uuid = row.device_uuid;
freeswitch.consoleLog("NOTICE", "[provision] device_uuid: ".. device_uuid .. "\n");
end);
end
--get the alternate device uuid using the device username and password
authorized = 'false';
if (user_id ~= nil and password ~= nil and domain_uuid ~= nil) then
local sql = [[SELECT device_uuid FROM v_devices ]];
sql = sql .. [[WHERE device_username = :user_id ]];
sql = sql .. [[AND device_password = :password ]]
sql = sql .. [[AND domain_uuid = :domain_uuid ]];
local params = {user_id = user_id, password = password, domain_uuid = domain_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("NOTICE", "[provision] SQL: ".. sql .. "; params: " .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--get the alternate device_uuid
device_uuid_alternate = row.device_uuid;
freeswitch.consoleLog("NOTICE", "[provision] alternate device_uuid: ".. device_uuid_alternate .. "\n");
--authorize the user
authorized = 'true';
end);
end
--authentication failed
if (authorized == 'false') then
result = session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/voicemail/vm-fail_auth.wav");
end
--this device already has an alternate find the correct device_uuid and then override current one
if (authorized == 'true' and action == "login" and device_uuid_alternate ~= nil and device_uuid ~= nil and domain_uuid ~= nil) then
local sql = [[SELECT * FROM v_devices ]];
sql = sql .. [[WHERE device_uuid_alternate = :device_uuid ]];
sql = sql .. [[AND domain_uuid = :domain_uuid ]];
local params = {device_uuid = device_uuid, domain_uuid = domain_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("NOTICE", "[provision] SQL: ".. sql .. "; params: " .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
if (row.device_uuid_alternate ~= nil) then
device_uuid = row.device_uuid;
end
end);
end
--remove the alternate device from another device so that it can be added to this device
if (authorized == 'true' and action == "login" and device_uuid_alternate ~= nil and domain_uuid ~= nil) then
local sql = [[SELECT * FROM v_device_lines ]];
sql = sql .. [[WHERE device_uuid = :device_uuid ]];
sql = sql .. [[AND domain_uuid = :domain_uuid ]];
local params = {device_uuid = device_uuid_alternate, domain_uuid = domain_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("NOTICE", "[provision] SQL: ".. sql .. "; params: " .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--remove the previous alternate device uuid
local sql = [[UPDATE v_devices SET device_uuid_alternate = null ]];
sql = sql .. [[WHERE device_uuid_alternate = :device_uuid_alternate ]];
sql = sql .. [[AND domain_uuid = :domain_uuid ]];
local params = {device_uuid_alternate = device_uuid_alternate, domain_uuid = domain_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("NOTICE", "[provision] SQL: ".. sql .. "; params: " .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--get the sip profile
api = freeswitch.API();
local sofia_contact = trim(api:executeString("sofia_contact */"..row.user_id.."@"..row.server_address));
array = explode("/", sofia_contact);
profile = array[2];
freeswitch.consoleLog("NOTICE", "[provision] profile: ".. profile .. "\n");
--send a sync command to the previous device
--create the event notify object
local event = freeswitch.Event('NOTIFY');
--add the headers
event:addHeader('profile', profile);
event:addHeader('user', row.user_id);
event:addHeader('host', row.server_address);
event:addHeader('content-type', 'application/simple-message-summary');
--check sync
event:addHeader('event-string', 'check-sync;reboot='..reboot);
--send the event
event:fire();
end);
end
--add the override to the device uuid (login)
if (authorized == 'true' and action == "login") then
if (device_uuid_alternate ~= nil and device_uuid ~= nil and domain_uuid ~= nil) then
--send a hangup
session:hangup();
--add the new alternate
local sql = [[UPDATE v_devices SET device_uuid_alternate = :device_uuid_alternate ]];
sql = sql .. [[WHERE device_uuid = :device_uuid ]];
sql = sql .. [[AND domain_uuid = :domain_uuid ]];
local params = {device_uuid_alternate = device_uuid_alternate,
device_uuid = device_uuid, domain_uuid = domain_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("NOTICE", "[provision] SQL: ".. sql .. "; params: " .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
end
end
--remove the override to the device uuid (logout)
if (authorized == 'true' and action == "logout") then
if (device_uuid_alternate ~= nil and device_uuid ~= nil and domain_uuid ~= nil) then
local sql = [[UPDATE v_devices SET device_uuid_alternate = null ]];
sql = sql .. [[WHERE device_uuid_alternate = :device_uuid ]];
sql = sql .. [[AND domain_uuid = :domain_uuid ]];
local params = {device_uuid = device_uuid, domain_uuid = domain_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("NOTICE", "[provision] sql: ".. sql .. "; params: " .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
end
end
--found the device send a sync command
if (authorized == 'true') then
--get the sip profile
api = freeswitch.API();
local sofia_contact = trim(api:executeString("sofia_contact */"..user.."@"..domain));
array = explode("/", sofia_contact);
profile = array[2];
freeswitch.consoleLog("NOTICE", "[provision] profile: ".. profile .. "\n");
--send a hangup
session:hangup();
--create the event notify object
local event = freeswitch.Event('NOTIFY');
--add the headers
event:addHeader('profile', profile);
event:addHeader('user', user);
event:addHeader('host', domain);
event:addHeader('content-type', 'application/simple-message-summary');
--check sync
event:addHeader('event-string', 'check-sync;reboot='..reboot);
--send the event
event:fire();
end

View File

@@ -0,0 +1,967 @@
-- Part of FusionPBX
-- Copyright (C) 2010-2019 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--
-- Contributor(s):
-- Mark J Crane <markjcrane@fusionpbx.com>
--include the log
log = require "resources.functions.log".ring_group
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--include json library
--debug["sql"] = true;
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--include functions
require "resources.functions.trim";
require "resources.functions.explode";
require "resources.functions.base64";
require "resources.functions.file_exists";
require "resources.functions.channel_utils"
require "resources.functions.format_ringback"
--- include libs
local route_to_bridge = require "resources.functions.route_to_bridge"
local play_file = require "resources.functions.play_file"
--define the session hangup
function session_hangup_hook()
--send info to the log
--freeswitch.consoleLog("notice","[ring_groups] originate_disposition: " .. session:getVariable("originate_disposition") .. "\n");
--status
status = 'answered'
--run the missed called function
if (
session:getVariable("originate_disposition") == "ALLOTTED_TIMEOUT"
or session:getVariable("originate_disposition") == "NO_ANSWER"
or session:getVariable("originate_disposition") == "NO_USER_RESPONSE"
or session:getVariable("originate_disposition") == "USER_NOT_REGISTERED"
or session:getVariable("originate_disposition") == "NORMAL_TEMPORARY_FAILURE"
or session:getVariable("originate_disposition") == "NO_ROUTE_DESTINATION"
or session:getVariable("originate_disposition") == "USER_BUSY"
or session:getVariable("originate_disposition") == "RECOVERY_ON_TIMER_EXPIRE"
or session:getVariable("originate_disposition") == "failure"
or session:getVariable("originate_disposition") == "ORIGINATOR_CANCEL"
) then
--set the status
status = 'missed'
--send missed call notification
missed();
end
--send the ring group event
event = freeswitch.Event("CUSTOM", "RING_GROUPS");
event:addHeader("domain_uuid", domain_uuid);
event:addHeader("domain_name", domain_name);
event:addHeader("ring_group_uuid", ring_group_uuid);
event:addHeader("user_uuid", user_uuid);
event:addHeader("ring_group_name", ring_group_name);
event:addHeader("ring_group_extension", ring_group_extension);
event:addHeader("status", status);
event:addHeader("call_uuid", uuid);
event:addHeader("caller_id_name", caller_id_name);
event:addHeader("caller_id_number", caller_id_number);
event:fire();
end
--define iterator function to iterate over key/value pairs in string
local function split_vars_pairs(str)
local last_pos = 1
return function()
-- end of string
if not str then return end
-- handle case when there exists comma after kv pair
local action, next_pos = string.match(str, "([^=]+=%b''),()", last_pos)
if not action then
action, next_pos = string.match(str, "([^=]+=[^'][^,]-),()", last_pos)
if not action then
action, next_pos = string.match(str, "([^=]+=),()", last_pos)
end
end
if action then
last_pos = next_pos
return action
end
-- last kv pair may not have comma after it
if last_pos < #str then
action = string.match(str, "([^=]+=%b'')$", last_pos)
if not action then
action = string.match(str, "([^=]+=[^,]-)$", last_pos)
end
str = nil -- end of iteration
end
return action
end
end
--set the hangup hook function
if (session:ready()) then
session:setHangupHook("session_hangup_hook");
end
--get the variables
if (session:ready()) then
session:setAutoHangup(false);
ring_group_uuid = session:getVariable("ring_group_uuid");
recordings_dir = session:getVariable("recordings_dir");
sounds_dir = session:getVariable("sounds_dir");
username = session:getVariable("username");
dialplan = session:getVariable("dialplan");
caller_id_name = session:getVariable("caller_id_name");
caller_id_number = session:getVariable("caller_id_number");
outbound_caller_id_name = session:getVariable("outbound_caller_id_name");
outbound_caller_id_number = session:getVariable("outbound_caller_id_number");
effective_caller_id_name = session:getVariable("effective_caller_id_name");
effective_caller_id_number = session:getVariable("effective_caller_id_number");
network_addr = session:getVariable("network_addr");
ani = session:getVariable("ani");
aniii = session:getVariable("aniii");
rdnis = session:getVariable("rdnis");
destination_number = session:getVariable("destination_number");
source = session:getVariable("source");
uuid = session:getVariable("uuid");
context = session:getVariable("context");
call_direction = session:getVariable("call_direction");
accountcode = session:getVariable("accountcode");
local_ip_v4 = session:getVariable("local_ip_v4")
end
--set caller id
if (effective_caller_id_name ~= nil) then
caller_id_name = effective_caller_id_name;
end
if (effective_caller_id_number ~= nil) then
caller_id_number = effective_caller_id_number;
end
--default to local if nil
if (call_direction == nil) then
call_direction = "local";
end
--set ring ready
if (session:ready()) then
session:execute("ring_ready", "");
end
--define additional variables
uuids = "";
external = "false";
--set the sounds path for the language, dialect and voice
if (session:ready()) then
default_language = session:getVariable("default_language");
default_dialect = session:getVariable("default_dialect");
default_voice = session:getVariable("default_voice");
if (not default_language) then default_language = 'en'; end
if (not default_dialect) then default_dialect = 'us'; end
if (not default_voice) then default_voice = 'callie'; end
end
--get record_ext
record_ext = session:getVariable("record_ext");
if (not record_ext) then
record_ext = "wav";
end
--prepare the api object
api = freeswitch.API();
--define the session hangup
--function on_hangup(s,status)
-- freeswitch.consoleLog("NOTICE","---- on_hangup: "..status.."\n");
-- error();
--end
--get current switchname
hostname = trim(api:execute("switchname", ""))
--get the ring group
ring_group_forward_enabled = '';
ring_group_forward_destination = '';
sql = "SELECT d.domain_name, r.* FROM v_ring_groups as r, v_domains as d ";
sql = sql .. "where r.ring_group_uuid = :ring_group_uuid ";
sql = sql .. "and r.domain_uuid = d.domain_uuid ";
local params = {ring_group_uuid = ring_group_uuid};
status = dbh:query(sql, params, function(row)
domain_uuid = row["domain_uuid"];
domain_name = row["domain_name"];
ring_group_name = row["ring_group_name"];
ring_group_extension = row["ring_group_extension"];
ring_group_greeting = row["ring_group_greeting"];
ring_group_forward_enabled = row["ring_group_forward_enabled"];
ring_group_forward_destination = row["ring_group_forward_destination"];
ring_group_forward_toll_allow = row["ring_group_forward_toll_allow"];
ring_group_call_timeout = row["ring_group_call_timeout"];
ring_group_caller_id_name = row["ring_group_caller_id_name"];
ring_group_caller_id_number = row["ring_group_caller_id_number"];
ring_group_cid_name_prefix = row["ring_group_cid_name_prefix"];
ring_group_cid_number_prefix = row["ring_group_cid_number_prefix"];
ring_group_follow_me_enabled = row["ring_group_follow_me_enabled"];
missed_call_app = row["ring_group_missed_call_app"];
missed_call_data = row["ring_group_missed_call_data"];
end);
--set the recording path
record_path = recordings_dir .. "/" .. domain_name .. "/archive/" .. os.date("%Y/%b/%d");
record_path = record_path:gsub("\\", "/");
--set the recording file name
if (session:ready()) then
record_name = session:getVariable("record_name");
if (not record_name) then
record_name = uuid .. "." .. record_ext;
end
end
---set the call_timeout to a higher value to prevent the early timeout of the ring group
if (session:ready()) then
if (ring_group_call_timeout and #ring_group_call_timeout == 0) then
ring_group_call_timeout = '300';
end
session:setVariable("call_timeout", ring_group_call_timeout);
end
--play the greeting
if (session:ready()) then
if (ring_group_greeting and #ring_group_greeting > 0) then
session:answer();
session:sleep(1000);
play_file(dbh, domain_name, domain_uuid, ring_group_greeting)
session:sleep(1000);
end
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 ";
sql = sql .. "and r.ring_group_uuid = u.ring_group_uuid ";
local params = {ring_group_uuid = ring_group_uuid};
status = dbh:query(sql, params, function(row)
user_uuid = row["user_uuid"];
end);
--set the caller id
if (session:ready()) then
if (ring_group_cid_name_prefix ~= nil and string.len(ring_group_cid_name_prefix) > 0) then
session:execute("export", "effective_caller_id_name="..ring_group_cid_name_prefix.."#"..caller_id_name);
end
if (ring_group_cid_number_prefix ~= nil and string.len(ring_group_cid_number_prefix) > 0) then
session:execute("export", "effective_caller_id_number="..ring_group_cid_number_prefix..caller_id_number);
end
end
--check the missed calls
function missed()
--send missed call email
if (missed_call_app ~= nil and missed_call_data ~= nil) then
if (missed_call_app == "email") then
--set the sounds path for the language, dialect and voice
default_language = session:getVariable("default_language");
default_dialect = session:getVariable("default_dialect");
default_voice = session:getVariable("default_voice");
if (not default_language) then default_language = 'en'; end
if (not default_dialect) then default_dialect = 'us'; end
if (not default_voice) then default_voice = 'callie'; end
--get the templates
local sql = "SELECT * FROM v_email_templates ";
sql = sql .. "WHERE (domain_uuid = :domain_uuid or domain_uuid is null) ";
sql = sql .. "AND template_language = :template_language ";
sql = sql .. "AND template_category = 'missed' "
sql = sql .. "AND template_enabled = 'true' "
sql = sql .. "ORDER BY domain_uuid DESC "
local params = {domain_uuid = domain_uuid, template_language = default_language.."-"..default_dialect};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
subject = row["template_subject"];
body = row["template_body"];
end);
--prepare the headers
headers = '{"X-FusionPBX-Domain-UUID":"'..domain_uuid..'",';
headers = headers..'"X-FusionPBX-Domain-Name":"'..domain_name..'",';
headers = headers..'"X-FusionPBX-Call-UUID":"'..uuid..'",';
headers = headers..'"X-FusionPBX-Email-Type":"missed"}';
--prepare the subject
subject = subject:gsub("${caller_id_name}", caller_id_name);
subject = subject:gsub("${caller_id_number}", caller_id_number);
subject = subject:gsub("${ring_group_name}", ring_group_name);
subject = subject:gsub("${ring_group_extension}", ring_group_extension);
subject = subject:gsub("${sip_to_user}", ring_group_name);
subject = subject:gsub("${dialed_user}", ring_group_extension);
subject = trim(subject);
subject = '=?utf-8?B?'..base64.encode(subject)..'?=';
--prepare the body
body = body:gsub("${caller_id_name}", caller_id_name);
body = body:gsub("${caller_id_number}", caller_id_number);
body = body:gsub("${ring_group_name}", ring_group_name);
body = body:gsub("${ring_group_extension}", ring_group_extension);
body = body:gsub("${sip_to_user}", ring_group_name);
body = body:gsub("${dialed_user}", ring_group_extension);
body = body:gsub(" ", "&nbsp;");
body = body:gsub("%s+", "");
body = body:gsub("&nbsp;", " ");
body = body:gsub("\n", "");
body = body:gsub("\n", "");
body = body:gsub("'", "&#39;");
body = body:gsub([["]], "&#34;");
body = trim(body);
--send the email
cmd = "luarun email.lua "..missed_call_data.." "..missed_call_data.." "..headers.." '"..subject.."' '"..body.."'";
if (debug["info"]) then
freeswitch.consoleLog("notice", "[missed call] cmd: " .. cmd .. "\n");
end
api = freeswitch.API();
result = api:executeString(cmd);
end
end
end
--get the destination and follow the forward
function get_forward_all(count, destination_number, domain_name)
cmd = "user_exists id ".. destination_number .." "..domain_name;
--freeswitch.consoleLog("notice", "[ring groups][call forward all] " .. cmd .. "\n");
user_exists = api:executeString(cmd);
if (user_exists == "true") then
---check to see if the new destination is forwarded - third forward
cmd = "user_data ".. destination_number .."@" ..domain_name.." var forward_all_enabled";
if (api:executeString(cmd) == "true") then
--get the toll_allow var
cmd = "user_data ".. destination_number .."@" ..leg_domain_name.." var toll_allow";
toll_allow = api:executeString(cmd);
--freeswitch.consoleLog("notice", "[ring groups][call forward all] " .. destination_number .. " toll_allow is ".. toll_allow .."\n");
--get the new destination - third foward
cmd = "user_data ".. destination_number .."@" ..domain_name.." var forward_all_destination";
destination_number = api:executeString(cmd);
--freeswitch.consoleLog("notice", "[ring groups][call forward all] " .. count .. " " .. cmd .. " ".. destination_number .."\n");
count = count + 1;
if (count < 5) then
count, destination_number = get_forward_all(count, destination_number, domain_name);
end
end
end
return count, destination_number, toll_allow;
end
--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: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
local sql = [[
SELECT
r.ring_group_strategy
FROM
v_ring_groups as r
WHERE
ring_group_uuid = :ring_group_uuid
AND r.domain_uuid = :domain_uuid
AND r.ring_group_enabled = 'true'
]];
local params = {ring_group_uuid = ring_group_uuid, domain_uuid = domain_uuid};
dbh:query(sql, params, function(row)
if (row.ring_group_strategy == "random") then
if (database["type"] == "mysql") then
sql_order = 'rand()'
else
sql_order = 'random()' --both postgresql and sqlite uses random() instead of rand()
end
else
sql_order='d.destination_delay, d.destination_number asc'
end
end);
--get the ring group destinations
sql = [[
SELECT
r.ring_group_strategy, r.ring_group_timeout_app, r.ring_group_distinctive_ring,
d.destination_number, d.destination_delay, d.destination_timeout, d.destination_prompt,
r.ring_group_caller_id_name, r.ring_group_caller_id_number,
r.ring_group_cid_name_prefix, r.ring_group_cid_number_prefix,
r.ring_group_timeout_data, r.ring_group_ringback
FROM
v_ring_groups as r, v_ring_group_destinations as d
WHERE
d.ring_group_uuid = r.ring_group_uuid
AND d.ring_group_uuid = :ring_group_uuid
AND r.domain_uuid = :domain_uuid
AND r.ring_group_enabled = 'true'
ORDER BY
]]..sql_order..[[
]];
if debug["sql"] then
freeswitch.consoleLog("notice", "[ring group] SQL:" .. sql .. "; params:" .. json.encode(params) .. "\n");
end
destinations = {};
x = 1;
dbh:query(sql, params, function(row)
if (row.destination_prompt == "1" or row.destination_prompt == "2") then
prompt = "true";
end
local array = explode("@",row.destination_number);
if (array[2] == nil) then
-- no @
leg_domain_name = domain_name;
else
leg_domain_name = array[2];
end
--follow the forwards
count, destination_number, toll_allow = get_forward_all(0, row.destination_number, leg_domain_name);
--update values
row['destination_number'] = destination_number
row['toll_allow'] = toll_allow;
--check if the user exists
cmd = "user_exists id ".. destination_number .." "..domain_name;
user_exists = api:executeString(cmd);
--cmd = "user_exists id ".. destination_number .." "..leg_domain_name;
if (user_exists == "true") then
--add user_exists true or false to the row array
row['user_exists'] = "true";
--handle do_not_disturb
cmd = "user_data ".. destination_number .."@" ..leg_domain_name.." var do_not_disturb";
if (api:executeString(cmd) ~= "true") then
--add the row to the destinations array
destinations[x] = row;
end
else
--set the values
external = "true";
row['user_exists'] = "false";
--add the row to the destinations array
destinations[x] = row;
end
row['domain_name'] = leg_domain_name;
x = x + 1;
end);
--freeswitch.consoleLog("NOTICE", "[ring_group] external "..external.."\n");
--get the dialplan data and save it to a table
if (external == "true") then
dialplans = route_to_bridge.preload_dialplan(
dbh, domain_uuid, {hostname = hostname, context = context}
)
end
---add follow me destinations
for key, row in pairs(destinations) do
if (ring_group_follow_me_enabled == "true") then
cmd = "user_data ".. row.destination_number .."@" ..row.domain_name.." var follow_me_enabled";
if (api:executeString(cmd) == "true") then
--set the default value to null
follow_me_uuid = nil;
--select data from the database
local sql = "select follow_me_uuid, toll_allow ";
sql = sql .. "from v_extensions ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and ( ";
sql = sql .. " extension = :destination_number ";
sql = sql .. " OR number_alias = :destination_number ";
sql = sql .. ") ";
local params = {domain_uuid = domain_uuid, destination_number = row.destination_number};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "SQL:" .. sql .. "; params: " .. json.encode(params) .. "\n");
end
status = dbh:query(sql, params, function(field)
follow_me_uuid = field["follow_me_uuid"];
toll_allow = field["toll_allow"];
end);
--dbh:query(sql, params, function(row);
--get the follow me destinations
if (follow_me_uuid ~= nil) then
sql = "select d.domain_uuid, d.domain_name, f.follow_me_destination as destination_number, ";
sql = sql .. "f.follow_me_delay as destination_delay, f.follow_me_timeout as destination_timeout, ";
sql = sql .. "f.follow_me_prompt as destination_prompt ";
sql = sql .. "from v_follow_me_destinations as f, v_domains as d ";
sql = sql .. "where f.follow_me_uuid = :follow_me_uuid ";
sql = sql .. "and f.domain_uuid = d.domain_uuid ";
sql = sql .. "order by f.follow_me_order; ";
local params = {follow_me_uuid = follow_me_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "SQL:" .. sql .. "; params: " .. json.encode(params) .. "\n");
end
x = 1;
dbh:query(sql, params, function(field)
--check if the user exists
cmd = "user_exists id ".. field.destination_number .." "..row.domain_name;
user_exists = api:executeString(cmd);
--prepare the key
if (x == 1) then
new_key = key;
else
new_key = #destinations + 1;
end
--Calculate the destination_timeout for follow-me destinations.
--The call should honor ring group timeouts with rg delays, follow-me timeouts and follow-me delays factored in.
--Destinations with a timeout of 0 or negative numbers should be ignored.
if (tonumber(field.destination_timeout) < (tonumber(row.destination_timeout) - tonumber(field.destination_delay))) then
new_destination_timeout = field.destination_timeout;
else
new_destination_timeout = row.destination_timeout - field.destination_delay;
end
--add to the destinations array
destinations[new_key] = {}
destinations[new_key]['ring_group_strategy'] = row.ring_group_strategy;
destinations[new_key]['ring_group_timeout_app'] = row.ring_group_timeout_app;
destinations[new_key]['ring_group_timeout_data'] = row.ring_group_timeout_data;
destinations[new_key]['ring_group_caller_id_name'] = row.ring_group_caller_id_name;
destinations[new_key]['ring_group_caller_id_number'] = row.ring_group_caller_id_number;
destinations[new_key]['ring_group_cid_name_prefix'] = row.ring_group_cid_name_prefix;
destinations[new_key]['ring_group_cid_number_prefix'] = row.ring_group_cid_number_prefix;
destinations[new_key]['ring_group_distinctive_ring'] = row.ring_group_distinctive_ring;
destinations[new_key]['ring_group_ringback'] = row.ring_group_ringback;
destinations[new_key]['domain_name'] = field.domain_name;
destinations[new_key]['destination_number'] = field.destination_number;
destinations[new_key]['destination_delay'] = field.destination_delay + row.destination_delay;
destinations[new_key]['destination_timeout'] = new_destination_timeout;
destinations[new_key]['destination_prompt'] = field.destination_prompt;
destinations[new_key]['group_confirm_key'] = row.group_confirm_key;
destinations[new_key]['group_confirm_file'] = row.group_confirm_file;
destinations[new_key]['toll_allow'] = toll_allow;
destinations[new_key]['user_exists'] = user_exists;
--increment x
x = x + 1;
end);
end
end
end
end
--prepare the array of destinations
for key, row in pairs(destinations) do
--determine if the user is registered if not registered then lookup
if (row.user_exists == "true") then
cmd = "sofia_contact */".. row.destination_number .."@" ..domain_name;
if (api:executeString(cmd) == "error/user_not_registered") then
freeswitch.consoleLog("NOTICE", "[ring_group] "..cmd.."\n");
cmd = "user_data ".. row.destination_number .."@" ..domain_name.." var forward_user_not_registered_enabled";
freeswitch.consoleLog("NOTICE", "[ring_group] "..cmd.."\n");
if (api:executeString(cmd) == "true") then
--get the new destination number
cmd = "user_data ".. row.destination_number .."@" ..domain_name.." var forward_user_not_registered_destination";
freeswitch.consoleLog("NOTICE", "[ring_group] "..cmd.."\n");
not_registered_destination_number = api:executeString(cmd);
freeswitch.consoleLog("NOTICE", "[ring_group] "..not_registered_destination_number.."\n");
if (not_registered_destination_number ~= nil) then
destination_number = not_registered_destination_number;
destinations[key]['destination_number'] = destination_number;
end
end
end
end
end
--add the array to the logs
for key, row in pairs(destinations) do
freeswitch.consoleLog("NOTICE", "[ring group] domain_name: "..row.domain_name.."\n");
freeswitch.consoleLog("NOTICE", "[ring group] destination_number: "..row.destination_number.."\n");
freeswitch.consoleLog("NOTICE", "[ring group] destination_delay: "..row.destination_delay.."\n");
freeswitch.consoleLog("NOTICE", "[ring group] destination_timeout: "..row.destination_timeout.."\n");
freeswitch.consoleLog("NOTICE", "[ring group] destination_prompt: "..row.destination_prompt.."\n");
end
--process the destinations
x = 1;
for key, row in pairs(destinations) do
if (tonumber(row.destination_timeout) > 0) then
--set the values from the database as variables
ring_group_strategy = row.ring_group_strategy;
ring_group_timeout_app = row.ring_group_timeout_app;
ring_group_timeout_data = row.ring_group_timeout_data;
ring_group_caller_id_name = row.ring_group_caller_id_name;
ring_group_caller_id_number = row.ring_group_caller_id_number;
ring_group_cid_name_prefix = row.ring_group_cid_name_prefix;
ring_group_cid_number_prefix = row.ring_group_cid_number_prefix;
ring_group_distinctive_ring = row.ring_group_distinctive_ring;
ring_group_ringback = row.ring_group_ringback;
destination_number = row.destination_number;
destination_delay = row.destination_delay;
destination_timeout = row.destination_timeout;
destination_prompt = row.destination_prompt;
group_confirm_key = row.group_confirm_key;
group_confirm_file = row.group_confirm_file;
toll_allow = row.toll_allow;
user_exists = row.user_exists;
--follow the forwards
count, destination_number = get_forward_all(0, destination_number, leg_domain_name);
--check if the user exists
cmd = "user_exists id ".. destination_number .." "..domain_name;
user_exists = api:executeString(cmd);
--set ringback
ring_group_ringback = format_ringback(ring_group_ringback);
session:setVariable("ringback", ring_group_ringback);
session:setVariable("transfer_ringback", ring_group_ringback);
--set the timeout if there is only one destination
if (#destinations == 1) then
session:execute("set", "call_timeout="..row.destination_timeout);
end
--setup the delimiter
delimiter = ",";
if (ring_group_strategy == "rollover") then
delimiter = "|";
end
if (ring_group_strategy == "sequence") then
delimiter = "|";
end
if (ring_group_strategy == "random") then
delimiter = "|";
end
if (ring_group_strategy == "simultaneous") then
delimiter = ",";
end
if (ring_group_strategy == "enterprise") then
delimiter = ":_:";
end
--leg delay settings
if (ring_group_strategy == "enterprise") then
delay_name = "originate_delay_start";
destination_delay = destination_delay * 500;
else
delay_name = "leg_delay_start";
end
--create a new uuid and add it to the uuid list
new_uuid = api:executeString("create_uuid");
if (string.len(uuids) == 0) then
uuids = new_uuid;
else
uuids = uuids ..",".. new_uuid;
end
session:execute("set", "uuids="..uuids);
--export the ringback
if (ring_group_distinctive_ring ~= nil) then
if (local_ip_v4 ~= nil) then
ring_group_distinctive_ring = ring_group_distinctive_ring:gsub("${local_ip_v4}", local_ip_v4);
end
if (domain_name ~= nil) then
ring_group_distinctive_ring = ring_group_distinctive_ring:gsub("${domain_name}", domain_name);
end
session:execute("export", "sip_h_Alert-Info="..ring_group_distinctive_ring);
end
--set confirm
if (ring_group_strategy == "simultaneous"
or ring_group_strategy == "sequence"
or ring_group_strategy == "rollover") then
session:execute("set", "group_confirm_key=exec");
session:execute("set", "group_confirm_file=lua ".. scripts_dir:gsub('\\','/') .."/confirm.lua");
end
--determine confirm prompt
if (destination_prompt == nil) then
group_confirm = "confirm=false,";
elseif (destination_prompt == "1") then
group_confirm = "group_confirm_key=exec,group_confirm_file=lua ".. scripts_dir:gsub('\\','/') .."/confirm.lua,confirm=true,";
elseif (destination_prompt == "2") then
group_confirm = "group_confirm_key=exec,group_confirm_file=lua ".. scripts_dir:gsub('\\','/') .."/confirm.lua,confirm=true,";
else
group_confirm = "confirm=false,";
end
--get user_record value and determine whether to record the session
cmd = "user_data ".. destination_number .."@"..domain_name.." var user_record";
user_record = trim(api:executeString(cmd));
--set the record_session variable
record_session = false;
if (user_record == "all") then
record_session = true;
end
if (user_record == "inbound" and call_direction == "inbound") then
record_session = true;
end
if (user_record == "outbound" and call_direction == "outbound") then
record_session = true;
end
if (user_record == "local" and call_direction == "local") then
record_session = true;
end
--record the session
if (record_session) then
record_session = ",api_on_answer='uuid_record "..uuid.." start ".. record_path .. "/" .. record_name .. "',record_path='".. record_path .."',record_name="..record_name;
else
record_session = ""
end
row.record_session = record_session
--process according to user_exists, sip_uri, external number
if (user_exists == "true") then
--get the extension_uuid
cmd = "user_data ".. destination_number .."@"..domain_name.." var extension_uuid";
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;
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
-- have to double destination_delay here due a FS bug requiring a 50% delay value for internal externsions, but not external calls.
destination_delay = destination_delay * 2;
route_bridge = 'loopback/'..destination_number;
if (extension_toll_allow ~= nil) then
toll_allow = extension_toll_allow:gsub(",", ":");
end
--set the toll allow to an empty string
if (toll_allow == nil) then
toll_allow = '';
end
--check if the user exists
if tonumber(caller_id_number) ~= nil then
cmd = "user_exists id ".. caller_id_number .." "..domain_name;
caller_is_local = api:executeString(cmd);
end
--set the caller id
caller_id = '';
--set the outbound caller id
if (caller_is_local == 'true' and outbound_caller_id_name ~= nil) then
caller_id = "origination_caller_id_name='"..outbound_caller_id_name.."'";
end
if (caller_is_local == 'true' and outbound_caller_id_number ~= nil) then
caller_id = caller_id .. ",origination_caller_id_number='"..outbound_caller_id_number.."'";
end
if (ring_group_caller_id_name ~= nil and ring_group_caller_id_name ~= '') then
caller_id = "origination_caller_id_name='"..ring_group_caller_id_name.."'";
end
if (ring_group_caller_id_number ~= nil and ring_group_caller_id_number ~= '') then
caller_id = caller_id .. ",origination_caller_id_number="..ring_group_caller_id_number..",";
end
--set the destination dial string
dial_string = "[ignore_early_media=true,toll_allow=".. toll_allow ..",".. caller_id ..",sip_invite_domain="..domain_name..",call_direction="..call_direction..","..group_confirm.."leg_timeout="..destination_timeout..","..delay_name.."="..destination_delay.."]"..route_bridge
end
--add a delimiter between destinations
if (dial_string ~= nil) then
--freeswitch.consoleLog("notice", "[ring group] dial_string: " .. dial_string .. "\n");
if (x == 1) then
if (ring_group_strategy == "enterprise") then
app_data = dial_string;
else
app_data = "{ignore_early_media=true}"..dial_string;
end
else
if (app_data == nil) then
if (ring_group_strategy == "enterprise") then
app_data = dial_string;
else
app_data = "{ignore_early_media=true}"..dial_string;
end
else
app_data = app_data .. delimiter .. dial_string;
end
end
end
--increment the value of x
x = x + 1;
end
end
--session execute
if (session:ready()) then
--set the variables
session:execute("set", "ignore_early_media=true");
session:execute("set", "hangup_after_bridge=true");
session:execute("set", "continue_on_fail=true");
-- support conf-xfer feature
-- do
-- local uuid = api:executeString("create_uuid")
-- session:execute("export", "conf_xfer_number=xfer-" .. uuid .. "-" .. domain_name)
-- end
--set bind digit action
local bind_target = 'peer'
if session:getVariable("sip_authorized") == "true" then
bind_target = 'both';
end
local bindings = {
"local,*2,exec:record_session," .. record_path .. "/" .. record_name,
-- "local,*0,exec:execute_extension,conf_xfer_from_dialplan XML conf-xfer@" .. context
}
for _, str in ipairs(bindings) do
session:execute("bind_digit_action", str .. "," .. bind_target)
end
session:execute("digit_action_set_realm", "local");
--if the user is busy rollover to the next destination
if (ring_group_strategy == "rollover") then
timeout = 0;
x = 0;
for key, row in pairs(destinations) do
--set the app data
app_data = '{ignore_early_media=true}';
--set the values from the database as variables
user_exists = row.user_exists;
destination_number = row.destination_number;
domain_name = row.domain_name;
--if the timeout was reached exit the loop and go to the timeout action
if (tonumber(ring_group_call_timeout) == timeout) then
break;
end
--send the call to the destination
if (user_exists == "true") then
dial_string = "["..group_confirm.."sip_invite_domain="..domain_name..",originate_timeout="..destination_timeout..",call_direction="..call_direction..",dialed_extension=" .. destination_number .. ",domain_name="..domain_name..",domain_uuid="..domain_uuid..row.record_session.."]user/" .. destination_number .. "@" .. domain_name;
elseif (tonumber(destination_number) == nil) then
dial_string = "["..group_confirm.."sip_invite_domain="..domain_name..",originate_timeout="..destination_timeout..",call_direction=outbound,domain_name="..domain_name..",domain_uuid="..domain_uuid.."]" .. destination_number;
else
dial_string = "["..group_confirm.."sip_invite_domain="..domain_name..",originate_timeout="..destination_timeout..",domain_name="..domain_name..",domain_uuid="..domain_uuid..",call_direction=outbound]loopback/" .. destination_number;
end
--add the delimiter
app_data = app_data .. dial_string;
freeswitch.consoleLog("NOTICE", "[ring group] app_data: "..app_data.."\n");
session:execute("bridge", app_data);
if (session:getVariable("originate_disposition") == "NO_ANSWER" ) then
timeout = timeout + destination_timeout;
end
--increment the value of x
x = x + 1;
end
end
--execute the bridge
if (app_data ~= nil) then
if (ring_group_strategy == "enterprise") then
app_data = app_data:gsub("%[", "{");
app_data = app_data:gsub("%]", "}");
end
freeswitch.consoleLog("NOTICE", "[ring group] app_data: "..app_data.."\n");
-- log.noticef("bridge begin: originate_disposition:%s answered:%s ready:%s bridged:%s", session:getVariable("originate_disposition"), session:answered() and "true" or "false", session:ready() and "true" or "false", session:bridged() and "true" or "false")
if (ring_group_strategy ~= "rollover") then
session:execute("bridge", app_data);
end
-- log.noticef("bridge done: originate_disposition:%s answered:%s ready:%s bridged:%s", session:getVariable("originate_disposition"), session:answered() and "true" or "false", session:ready() and "true" or "false", session:bridged() and "true" or "false")
end
--timeout destination
if (app_data ~= nil) then
if session:ready() and (
session:getVariable("originate_disposition") == "ALLOTTED_TIMEOUT"
or session:getVariable("originate_disposition") == "NO_ANSWER"
or session:getVariable("originate_disposition") == "NO_USER_RESPONSE"
or session:getVariable("originate_disposition") == "USER_NOT_REGISTERED"
or session:getVariable("originate_disposition") == "NORMAL_TEMPORARY_FAILURE"
or session:getVariable("originate_disposition") == "NO_ROUTE_DESTINATION"
or session:getVariable("originate_disposition") == "USER_BUSY"
or session:getVariable("originate_disposition") == "RECOVERY_ON_TIMER_EXPIRE"
or session:getVariable("originate_disposition") == "failure"
) then
--execute the time out action
if ring_group_timeout_app and #ring_group_timeout_app > 0 then
session:execute(ring_group_timeout_app, ring_group_timeout_data);
end
--check and report missed call
missed();
end
else
if (ring_group_timeout_app ~= nil) then
--execute the time out action
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";
local params = {ring_group_uuid = ring_group_uuid};
if debug["sql"] then
freeswitch.consoleLog("notice", "[ring group] SQL:" .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--execute the time out action
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
end
end
--actions
--ACTIONS = {}
--table.insert(ACTIONS, {"set", "hangup_after_bridge=true"});
--table.insert(ACTIONS, {"set", "continue_on_fail=true"});
--table.insert(ACTIONS, {"bridge", app_data});
--table.insert(ACTIONS, {ring_group_timeout_app, ring_group_timeout_data});

View File

@@ -0,0 +1,200 @@
--
-- FusionPBX
-- Version: MPL 1.1
--
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
--
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
--
-- The Original Code is FusionPBX
--
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Copyright (C) 2010-2017
-- the Initial Developer. All Rights Reserved.
--
-- Contributor(s):
-- Mark J Crane <markjcrane@fusionpbx.com>
--set default variables
local min_digits = 3;
local max_digits = 11;
local max_tries = 3;
local digit_timeout = 3000;
--include config.lua
require "resources.functions.config";
--include libraries
require "resources.functions.channel_utils";
local log = require "resources.functions.log".ring_group_call_forward
local Database = require "resources.functions.database"
local blf = require "resources.functions.blf"
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
local function empty(t)
return (not t) or (#t == 0)
end
--check if the session is ready
if not session:ready() then return end
--answer the call
session:answer();
--get the variables
local enabled = session:getVariable("enabled");
local pin_number = session:getVariable("pin_number");
local sounds_dir = session:getVariable("sounds_dir");
local domain_uuid = session:getVariable("domain_uuid");
local domain_name = session:getVariable("domain_name");
local ring_group_number = session:getVariable("ring_group_number");
local ring_group_uuid = session:getVariable("ring_group_uuid");
local request_id = session:getVariable("request_id");
local forward_destination = session:getVariable("forward_destination");
--set the sounds path for the language, dialect and voice
local default_language = session:getVariable("default_language") or 'en';
local default_dialect = session:getVariable("default_dialect") or 'us';
local default_voice = session:getVariable("default_voice") or 'callie';
--a moment to sleep
session:sleep(1000);
--connect to the database
local dbh = Database.new('system');
-- user hangup
if not session:ready() then return end
if (request_id ~= 'true') and empty(ring_group_number) and empty(ring_group_uuid) then
log.warning('can not detect ring group number. Please specify one of this var: ring_group_number, ring_group_uuid or request_id.')
return
end
--check pin code
if not empty(pin_number) then
--get the pin number
local min_digits = 3;
local max_digits = 20;
local caller_pin_number = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", "phrase:voicemail_enter_pass:#", "", "\\d+");
if empty(caller_pin_number) then return end
--check pin number
if pin_number ~= caller_pin_number then return end
--user hangup
if not session:ready() then return end
end
--get ring group number
if request_id == 'true' then
--get the ring group extension number
local min_digits = 2;
local max_digits = 20;
ring_group_number = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", "phrase:voicemail_enter_id:#", "", "\\d+");
if empty(ring_group_number) then return end
-- user hangup
if not session:ready() then return end
end
--search ring_group in database
local sql = [[SELECT ring_group_uuid as uuid, ring_group_forward_enabled as forward_enabled,
ring_group_forward_destination as forward_destination, ring_group_extension as extension
FROM v_ring_groups
WHERE domain_uuid = :domain_uuid
]]
local params = {domain_uuid = domain_uuid}
if (request_id == "true") or (empty(ring_group_uuid)) then
sql = sql .. " AND ring_group_extension=:extension"
params.extension = ring_group_number
else
sql = sql .. " AND ring_group_uuid=:ring_group_uuid"
params.ring_group_uuid = ring_group_uuid
end
if (debug["sql"]) then
log.noticef("SQL: %s; params: %s", sql, json.encode(params));
end
local ring_group = dbh:first_row(sql, params)
--if can not find ring group
if (not ring_group) or (not ring_group.uuid) then return end
-- user hangup
if not session:ready() then return end
-- get destination number form database if it not provided
if enabled == 'toggle' and empty(forward_destination) then
forward_destination = ring_group.forward_destination
end
--toggle enabled
if enabled == 'toggle' then
-- if we toggle CF and specify new destination number then just enable it
if forward_destination == ring_group.forward_destination then
enabled = (ring_group.forward_enabled == 'true') and 'false' or 'true'
else
enabled = 'true'
end
end
--get the forward destination
if enabled == 'true' and empty(forward_destination) then
-- get number
forward_destination = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-enter_destination_telephone_number.wav", "", "\\d+");
if empty(forward_destination) then return end
-- user hangup
if not session:ready() then return end
end
--update ring group call farward in database
local sql = [[UPDATE v_ring_groups
SET
ring_group_forward_enabled = :enabled,
ring_group_forward_destination = :destination
WHERE
ring_group_uuid = :uuid]]
local params = {
enabled = (enabled == 'true') and 'true' or 'false',
destination = forward_destination,
uuid = ring_group.uuid,
}
if (debug["sql"]) then
log.noticef("SQL: %s; params: %s", sql, json.encode(params));
end
dbh:query(sql, params)
--disconnect from database
dbh:release()
--notify caller
if enabled == 'true' then
--set forward_all_enabled
channel_display(session:get_uuid(), "Activated")
--say the destination number
session:say(forward_destination, default_language, "number", "iterated");
--notify the caller
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-call_forwarding_has_been_set.wav");
else
--set forward_all_enabled
channel_display(session:get_uuid(), "Cancelled")
--notify the caller
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-call_forwarding_has_been_cancelled.wav");
end
-- BLF for display CF status
blf.forward(enabled == 'true', ring_group.extension, nil,
ring_group.forward_destination, forward_destination, domain_name)

View File

@@ -0,0 +1,136 @@
--
-- FusionPBX
-- Version: MPL 1.1
--
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
--
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
--
-- The Original Code is FusionPBX
--
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Copyright (C) 2010-2013
-- the Initial Developer. All Rights Reserved.
--
-- Contributor(s):
-- Mark J Crane <markjcrane@fusionpbx.com>
--include config.lua
require "resources.functions.config";
--set variables
max_tries = "3";
digit_timeout = "5000";
--define the trim function
require "resources.functions.trim";
--define the explode function
require "resources.functions.explode";
--get the argv values
script_name = argv[0];
argv_uuid = argv[1];
prompt = argv[2];
--prepare the api
api = freeswitch.API();
--answer the call
session:answer();
--get the variables
context = session:getVariable("context");
sounds_dir = session:getVariable("sounds_dir");
destination_number = session:getVariable("destination_number");
uuid = session:getVariable("uuid");
--set the sounds path for the language, dialect and voice
default_language = session:getVariable("default_language");
default_dialect = session:getVariable("default_dialect");
default_voice = session:getVariable("default_voice");
if (not default_language) then default_language = 'en'; end
if (not default_dialect) then default_dialect = 'us'; end
if (not default_voice) then default_voice = 'callie'; end
--if an extension answer the call
-- user_exists id 1005 voip.fusionpbx.com
-- cmd = "user_exists id ".. destination_number .." "..context;
-- result = api:executeString(cmd);
-- freeswitch.consoleLog("NOTICE", "[confirm] "..cmd.." --"..result.."--\n");
-- if (result == "true") then
-- prompt = false;
-- end
--prompt for digits
if (prompt == "true") then
--get the digit
min_digits = 1;
max_digits = 1;
--check if the original call exists
cmd = "uuid_exists "..argv_uuid;
if (trim(api:executeString(cmd)) == "false") then
session:hangup("NO_ANSWER");
end
--digit = session:playAndGetDigits(2, 5, 3, 3000, "#", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-accept_reject.wav", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-that_was_an_invalid_entry.wav", "\\d+")
digit = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-accept_reject.wav", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-that_was_an_invalid_entry.wav", "\\d+");
--process the response
if (digit == "1") then
--confirmed call accepted
confirmed = true;
elseif (digit == "2") then
freeswitch.consoleLog("NOTICE", "[confirm] reject\n");
session:hangup("CALL_REJECTED"); --LOSE_RACE
else
--freeswitch.consoleLog("NOTICE", "[confirm] no answer\n");
session:hangup("NO_ANSWER");
end
else
freeswitch.consoleLog("NOTICE", "[confirm] automatically accepted\n");
confirmed = true;
end
if (confirmed) then
--cmd = "bgapi sched_transfer +1 "..uuid.." *5901";
--freeswitch.consoleLog("NOTICE", "[ring_group] uuid: "..cmd.."\n");
--result = api:executeString(cmd);
freeswitch.consoleLog("NOTICE", "[confirm] accepted\n");
--check if the original call exists
cmd = "uuid_exists "..argv_uuid;
if (trim(api:executeString(cmd)) == "false") then
session:hangup("NO_ANSWER");
end
--unschedule the timeout
cmd = "sched_del ring_group:"..argv_uuid;
freeswitch.consoleLog("NOTICE", "[confirm] cmd: "..cmd.."\n");
results = trim(api:executeString(cmd));
--get the uuids and remove the other calls
cmd = "uuid_getvar "..argv_uuid.." uuids";
freeswitch.consoleLog("NOTICE", "[confirm] cmd: "..cmd.."\n");
uuids = trim(api:executeString(cmd));
u = explode(",", uuids);
for k,v in pairs(u) do
if (uuid ~= v) then
cmd = "uuid_kill "..v;
freeswitch.consoleLog("NOTICE", "[confirm] cmd: "..cmd.."\n");
result = trim(api:executeString(cmd));
end
end
--bridge the call
cmd = "uuid_bridge "..uuid.." "..argv_uuid;
result = trim(api:executeString(cmd));
session:execute("valet_park", "confirm "..argv_uuid);
end

View File

@@ -0,0 +1,204 @@
--
-- FusionPBX
-- Version: MPL 1.1
--
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
--
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
--
-- The Original Code is FusionPBX
--
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Copyright (C) 2010-2016
-- All Rights Reserved.
--
-- Contributor(s):
-- Koldo A. Marcos <koldo.aingeru@sarenet.es>
--include config.lua
require "resources.functions.config";
--set debug
-- debug["sql"] = true;
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--set default variables
sounds_dir = "";
recordings_dir = "";
pin_number = "";
max_tries = "3";
digit_timeout = "3000";
--define uuid function
local random = math.random;
local function uuid()
local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
return string.gsub(template, '[xy]', function (c)
local v = (c == 'x') and random(0, 0xf) or random(8, 0xb);
return string.format('%x', v);
end)
end
--get session variables
if (session:ready()) then
session:answer();
--session:execute("info", "");
destination = session:getVariable("destination");
pin_number = session:getVariable("pin_number");
sounds_dir = session:getVariable("sounds_dir");
ring_group_uuid = session:getVariable("ring_group_uuid");
domain_uuid = session:getVariable("domain_uuid");
end
--get the domain uuid and set other required variables
if (session:ready()) then
--get info for the ring group
--sql = "SELECT * FROM v_ring_groups ";
--sql = sql .. "where ring_group_uuid = '"..ring_group_uuid.."' ";
--status = dbh:query(sql, function(row)
-- domain_uuid = row["domain_uuid"];
--end);
--set destination defaults
destination_timeout = 15;
destination_delay = 0;
--create the primary key uuid
ring_group_destination_uuid = uuid();
end
--if the pin number is provided then require it
if (pin_number) then
min_digits = string.len(pin_number);
max_digits = string.len(pin_number)+1;
digits = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-please_enter_pin_followed_by_pound.wav", "", "\\d+");
if (digits == pin_number) then
--pin is correct
else
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-pin_or_extension_is-invalid.wav");
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-im_sorry.wav");
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/voicemail/vm-goodbye.wav");
session:hangup("NORMAL_CLEARING");
return;
end
end
--get the destination
--if (session:ready()) then
-- if string.len(destination) == 0) then
-- destination = session:playAndGetDigits(1, 1, max_tries, digit_timeout, "#", "ivr/ivr-enter_destination_telephone_number.wav", "", "\\d+");
-- freeswitch.consoleLog("NOTICE", "[ring_group] destination: "..destination.."\n");
-- end
--end
--login or logout
if (session:ready()) then
menu_selection = session:playAndGetDigits(1, 1, max_tries, digit_timeout, "#", "ivr/ivr-enter_destination_telephone_number.wav", "", "\\d+");
freeswitch.consoleLog("NOTICE", "[ring_group] menu_selection: "..menu_selection.."\n");
if (menu_selection == "1") then
--first, check to see if the destination is already in this ring group
local sql = [[
SELECT COUNT(*) AS in_group FROM
v_ring_group_destinations
WHERE
domain_uuid = :domain_uuid
AND ring_group_uuid = :ring_group_uuid
AND destination_number = :destination
]];
local params = {domain_uuid = domain_uuid, ring_group_uuid = ring_group_uuid,
destination = destination};
if debug["sql"] then
freeswitch.consoleLog("NOTICE", "[ring_group] SQL: " .. sql .. "; params: " .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
if (row.in_group == "0") then
sql = [[
INSERT INTO
v_ring_group_destinations
( ring_group_destination_uuid,
domain_uuid,
ring_group_uuid,
destination_number,
destination_delay,
destination_timeout
)
VALUES
( :ring_group_destination_uuid,
:domain_uuid,
:ring_group_uuid,
:destination,
:destination_delay,
:destination_timeout
)]];
params = {
ring_group_destination_uuid = ring_group_destination_uuid;
domain_uuid = domain_uuid;
ring_group_uuid = ring_group_uuid;
destination = destination;
destination_delay = destination_delay;
destination_timeout = destination_timeout;
};
if debug["sql"] then
freeswitch.consoleLog("NOTICE", "[ring_group][destination] SQL: " .. sql .. "; params: " .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
freeswitch.consoleLog("NOTICE", "[ring_group][destination] LOG IN\n");
session:streamFile("ivr/ivr-you_are_now_logged_in.wav");
else
freeswitch.consoleLog("NOTICE", "[ring_group][destination] ALREADY LOGGED IN\n");
session:streamFile("ivr/ivr-you_are_now_logged_in.wav");
end
end);
end
if (menu_selection == "2") then
local sql = [[
DELETE FROM
v_ring_group_destinations
WHERE
domain_uuid =:domain_uuid
AND ring_group_uuid=:ring_group_uuid
AND destination_number=:destination
]];
local params = {domain_uuid = domain_uuid, ring_group_uuid = ring_group_uuid,
destination = destination};
if debug["sql"] then
freeswitch.consoleLog("NOTICE", "[ring_group][destination] SQL: " .. sql .. "; params: " .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
freeswitch.consoleLog("NOTICE", "[ring_group][destination] LOG OUT\n");
session:streamFile("ivr/ivr-you_are_now_logged_out.wav");
end
end
--wait for the file to be written before proceeding
if (session:ready()) then
--session:sleep(1000);
end
--hangup
if (session:ready()) then
session:hangup();
end

View File

@@ -0,0 +1,82 @@
-- uuid_hangup.lua
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--Description:
--if the uuid does not exist
--then run commands
--get the argv values
script_name = argv[0];
uuid = argv[1];
timeout = argv[2];
--define the trim function
require "resources.functions.trim";
--define the explode function
require "resources.functions.explode";
--prepare the api
api = freeswitch.API();
--get the list of uuids
cmd = "uuid_getvar "..uuid.." uuids";
--freeswitch.consoleLog("NOTICE", "[confirm] cmd: "..cmd.."\n");
uuids = trim(api:executeString(cmd));
--monitor the uuid
x = 0
while true do
--sleep a moment to prevent using unecessary resources
freeswitch.msleep(1000);
--check if the uuid exists
if (api:executeString("uuid_exists "..uuid) == "false") then
--unschedule the timeout
cmd = "sched_del ring_group:"..uuid;
--freeswitch.consoleLog("NOTICE", "[confirm] cmd: "..cmd.."\n");
results = trim(api:executeString(cmd));
--end the other uuids
u = explode(",", uuids);
for k,v in pairs(u) do
if (uuid ~= v) then
cmd = "uuid_kill "..v;
--freeswitch.consoleLog("NOTICE", "[confirm] cmd: "..cmd.."\n");
result = trim(api:executeString(cmd));
end
end
--end the loop
break;
end
--timeout
x = x + 1;
if (x > tonumber(timeout)) then
--end the loop
break;
end
end

View File

@@ -0,0 +1,165 @@
--
-- FusionPBX
-- Version: MPL 1.1
--
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
--
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
--
-- The Original Code is FusionPBX
--
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Copyright (C) 2010-2016
-- All Rights Reserved.
--
-- Contributor(s):
-- Koldo A. Marcos <koldo.aingeru@sarenet.es>
--include config.lua
require "resources.functions.config";
--connect to the database
require "resources.functions.database_handle";
dbh = database_handle('system');
--set default variables
sounds_dir = "";
recordings_dir = "";
pin_number = "";
max_tries = "3";
digit_timeout = "3000";
--define uuid function
local random = math.random;
local function uuid()
local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
return string.gsub(template, '[xy]', function (c)
local v = (c == 'x') and random(0, 0xf) or random(8, 0xb);
return string.format('%x', v);
end)
end
--get session variables
if (session:ready()) then
session:answer();
--session:execute("info", "");
destination_number = session:getVariable("destination_number");
pin_number = session:getVariable("pin_number");
sounds_dir = session:getVariable("sounds_dir");
ring_group_uuid = session:getVariable("ring_group_uuid");
domain_uuid = session:getVariable("domain_uuid");
end
--get the domain uuid and set other required variables
if (session:ready()) then
--get info for the ring group
--sql = "SELECT * FROM v_ring_groups ";
--sql = sql .. "where ring_group_uuid = '"..ring_group_uuid.."' ";
--status = dbh:query(sql, function(row)
-- domain_uuid = row["domain_uuid"];
--end);
destination_timeout = 15;
destination_delay = 0;
--create the primary key uuid
ring_group_destination_uuid = uuid();
end
--if the pin number is provided then require it
if (pin_number) then
min_digits = string.len(pin_number);
max_digits = string.len(pin_number)+1;
digits = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-please_enter_pin_followed_by_pound.wav", "", "\\d+");
if (digits == pin_number) then
--pin is correct
else
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-pin_or_extension_is-invalid.wav");
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-im_sorry.wav");
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/voicemail/vm-goodbye.wav");
session:hangup("NORMAL_CLEARING");
return;
end
end
--press 1 to login and 2 to logout
if (session:ready()) then
menu_selection = session:playAndGetDigits(1, 1, max_tries, digit_timeout, "#", "ivr/ivr-enter_destination_telephone_number.wav", "", "\\d+");
freeswitch.consoleLog("NOTICE", "[ring_group] menu_selection: "..menu_selection.."\n");
if (menu_selection == "1") then
--first, check to see if the destination is already in this ring group
sql = [[
SELECT COUNT(*) AS in_group FROM
v_ring_group_destinations
WHERE
domain_uuid = ']]..domain_uuid..[['
AND ring_group_uuid = ']]..ring_group_uuid..[['
AND destination_number = ']]..destination_number..[['
]];
--freeswitch.consoleLog("NOTICE", "[ring_group] SQL "..sql.."\n");
dbh:query(sql, function(row)
if (row.in_group == "0") then
sql = [[
INSERT INTO
v_ring_group_destinations
( ring_group_destination_uuid,
domain_uuid,
ring_group_uuid,
destination_number,
destination_delay,
destination_timeout
)
VALUES
( ']]..ring_group_destination_uuid..[[',
']]..domain_uuid..[[',
']]..ring_group_uuid..[[',
']]..destination..[[',
]]..destination_delay..[[,
]]..destination_timeout..[[
)]];
freeswitch.consoleLog("NOTICE", "[ring_group][destination] SQL "..sql.."\n");
dbh:query(sql);
freeswitch.consoleLog("NOTICE", "[ring_group][destination] LOG IN\n");
session:streamFile("ivr/ivr-you_are_now_logged_in.wav");
else
freeswitch.consoleLog("NOTICE", "[ring_group][destination] ALREADY LOGGED IN\n");
session:streamFile("ivr/ivr-you_are_now_logged_in.wav");
end
end);
end
if (menu_selection == "2") then
sql = [[
DELETE FROM
v_ring_group_destinations
WHERE
domain_uuid =']]..domain_uuid..[['
AND ring_group_uuid=']]..ring_group_uuid..[['
AND destination_number=']]..destination..[['
]];
freeswitch.consoleLog("NOTICE", "[ring_group][destination] SQL "..sql.."\n");
dbh:query(sql);
freeswitch.consoleLog("NOTICE", "[ring_group][destination] LOG OUT\n");
session:streamFile("ivr/ivr-you_are_now_logged_out.wav");
end
end
--wait for the file to be written before proceeding
if (session:ready()) then
--session:sleep(1000);
end
--hangup
if (session:ready()) then
session:hangup();
end

View File

@@ -0,0 +1,37 @@
--includes
require "resources.functions.config";
local Database = require "resources.functions.database";
local Settings = require "resources.functions.lazy_settings"
dbh = Database.new('system');
local settings = Settings.new(dbh, domain_name, domain_uuid);
--define trim
function trim (s)
return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
end
--get the argv values
cmd = argv[1];
file = argv[2];
--get the cache directory
local cache_dir = settings:get('cache', 'location', 'text')
if (cmd ~= nil) then
cmd = trim(cmd);
freeswitch.consoleLog("NOTICE","api_command: "..cmd .. " cache\n");
end
if (cmd == "flush") then
os.execute("rm " .. cache_dir .. "/*");
end
if (cmd == "delete") then
if (file ~= nil) then
file = trim(file);
freeswitch.consoleLog("NOTICE","api_command: delete ".. cache_dir .. "/" .. file .. "\n");
os.remove(cache_dir.."/"..file);
end
end

View File

@@ -0,0 +1,90 @@
--description
--monitor custom memcache event and clear memcache on remote servers
--protect xmlrpc using a firewall on the server to limit access by ip address
--dependencies
--install mod_curl freeswitch module
--uncomment mod_curl from modules.conf when compiling freeswitch
--xmlrpc
--open port xmlrpc port for other master server IP addresses
--change the password for xmlrpc in system -> settings
--conf/autoload_configs/lua.conf.xml
-- <param name="startup-script" value="app/server/resources/memcache.lua"/>
--iptables
-- /sbin/iptables -I INPUT -j ACCEPT -p tcp --dport 8080 -s x.x.x.x/32
-- ubuntu: service iptables-persistent save
--define the servers running freeswitch do not include local
--[[
#put this in local.lua
servers = {}
x = 0;
servers[x] = {}
servers[x]['method'] = "curl";
servers[x]['username'] = "freeswitch";
servers[x]['password'] = "freeswitch";
servers[x]['hostname'] = "x.x.x.x";
servers[x]['port'] = "8080";
x = x + 1;
servers[x] = {}
servers[x]['method'] = "curl";
servers[x]['username'] = "freeswitch";
servers[x]['password'] = "freeswitch";
servers[x]['hostname'] = "x.x.x.x";
servers[x]['port'] = "8080";
]]
--includes config.lua which will include local.lua if it exists
require "resources.functions.config"
--subscribe to the events
--events = freeswitch.EventConsumer("all");
events = freeswitch.EventConsumer("CUSTOM");
--define trim
function trim (s)
return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
end
--prepare the api object
api = freeswitch.API();
--get the events
for event in (function() return events:pop(1) end) do
--serialize the data for the console
--freeswitch.consoleLog("notice","event:" .. event:serialize("xml") .. "\n");
--freeswitch.consoleLog("notice","event:" .. event:serialize("json") .. "\n");
--get the uuid
local api_command = event:getHeader("API-Command");
if (api_command ~= nil) then
api_command = trim(api_command);
freeswitch.consoleLog("NOTICE","api_command: "..api_command .. "\n");
end
if (api_command == "cache") then
cache_updated = false;
local api_command_argument = event:getHeader("API-Command-Argument");
if (api_command_argument ~= nil) then
api_command_argument = trim(api_command_argument);
end
if (api_command_argument ~= nil) then
if (api_command_argument == "flush") then
cache_updated = true
end
if (string.sub(api_command_argument, 0, 6) == "delete") then
cache_updated = true
end
if (cache_updated) then
for key,row in pairs(servers) do
if (row.method == "curl") then
api_command_argument = api_command_argument:gsub(" ", "%%20");
url = [[http://]]..row.username..[[:]]..row.password..[[@]]..row.hostname..[[:]]..row.port..[[/webapi/luarun?app/servers/resources/clear_cache.lua%20]]..api_command_argument;
api:executeString("system curl " .. url );
freeswitch.consoleLog("INFO", "[notice] curl ".. url .. " \n");
end
end
end
end
end
end

View File

@@ -0,0 +1,96 @@
--description
--monitor custom memcache event and clear memcache on remote servers
--protect xmlrpc using a firewall on the server to limit access by ip address
--dependencies
--install mod_curl freeswitch module
--uncomment mod_curl from modules.conf when compiling freeswitch
--xmlrpc
--open port xmlrpc port for other master server IP addresses
--change the password for xmlrpc in system -> settings
--conf/autoload_configs/lua.conf.xml
-- <param name="startup-script" value="app/server/resources/memcache.lua"/>
--iptables
-- /sbin/iptables -I INPUT -j ACCEPT -p tcp --dport 8080 -s x.x.x.x/32
-- ubuntu: service iptables-persistent save
--define the servers running freeswitch do not include local
--[[
#put this in local.lua
servers = {}
x = 0;
servers[x] = {}
servers[x]['method'] = "curl";
servers[x]['username'] = "freeswitch";
servers[x]['password'] = "freeswitch";
servers[x]['hostname'] = "x.x.x.x";
servers[x]['port'] = "8080";
x = x + 1;
servers[x] = {}
servers[x]['method'] = "curl";
servers[x]['username'] = "freeswitch";
servers[x]['password'] = "freeswitch";
servers[x]['hostname'] = "x.x.x.x";
servers[x]['port'] = "8080";
]]
--includes config.lua which will include local.lua if it exists
require "resources.functions.config"
--subscribe to the events
--events = freeswitch.EventConsumer("all");
events = freeswitch.EventConsumer("CUSTOM");
--define trim
function trim (s)
return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
end
--prepare the api object
api = freeswitch.API();
--get the events
for event in (function() return events:pop(1) end) do
--serialize the data for the console
--freeswitch.consoleLog("notice","event:" .. event:serialize("xml") .. "\n");
--freeswitch.consoleLog("notice","event:" .. event:serialize("json") .. "\n");
--get the uuid
local api_command = event:getHeader("API-Command");
if (api_command ~= nil) then
api_command = trim(api_command);
freeswitch.consoleLog("NOTICE","api_command: "..api_command .. "\n");
end
if (api_command == "memcache") then
memcache_updated = false;
local api_command_argument = event:getHeader("API-Command-Argument");
if (api_command_argument ~= nil) then
api_command_argument = trim(api_command_argument);
end
if (api_command_argument ~= nil) then
if (api_command_argument == "flush") then
memcache_updated = true
end
if (string.sub(api_command_argument, 0, 6) == "delete") then
memcache_updated = true
end
if (memcache_updated) then
for key,row in pairs(servers) do
if (row.method == "ssh") then
api_command_argument = api_command_argument:gsub("%%20", " ");
cmd = [[ssh ]]..row.username..[[@]]..row.hostname..[[ "fs_cli -x 'memcache ]]..api_command_argument..[['"]];
freeswitch.consoleLog("INFO", "[notice] command: ".. cmd .. "\n");
os.execute(cmd);
end
if (row.method == "curl") then
api_command_argument = api_command_argument:gsub(" ", "%%20");
url = [[http://]]..row.username..[[:]]..row.password..[[@]]..row.hostname..[[:]]..row.port..[[/webapi/memcache?]]..api_command_argument;
os.execute("curl "..url);
freeswitch.consoleLog("INFO", "[notice] curl ".. url .. " \n");
end
end
end
end
end
end

View File

@@ -0,0 +1,126 @@
-- FusionPBX
-- Version: MPL 1.1
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
-- The Original Code is FusionPBX
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Portions created by the Initial Developer are Copyright (C) 2019
-- the Initial Developer. All Rights Reserved.
-- load config
require "resources.functions.config";
--set debug
-- debug["sql"] = true;
--load libraries
local log = require "resources.functions.log"["app:dialplan:outbound:speed_dial"]
local Database = require "resources.functions.database";
local cache = require "resources.functions.cache";
local json = require "resources.functions.lunajson";
--get the variables
domain_name = session:getVariable("domain_name");
domain_uuid = session:getVariable("domain_uuid");
context = session:getVariable("context");
user = session:getVariable("sip_auth_username")
or session:getVariable("username");
--get the argv values
destination = argv[2];
-- search in cache first
local key = "app:dialplan:outbound:speed_dial:" .. user .. ":" .. destination .. "@" .. domain_name
local value = cache.get(key)
-- decode value from cache
if value then
local t = json.decode(value)
if not (t and t.phone_number) then
log.warningf("can not decode value from cache: %s", value)
value = nil
else
value = t
end
end
-- search in database
if not value then
-- set source flag
source = "database"
-- connect to database
local dbh = Database.new('system');
-- search for the phone number in database using the speed dial
local sql = [[
-- find all contacts with correct user or withot users and groups at all
select t0.phone_number --, t6.extension, 'GROUP:' || t3.group_name as user_name
from v_contact_phones t0
inner join v_contacts t1 on t0.contact_uuid = t1.contact_uuid
left outer join v_contact_groups t2 on t1.contact_uuid = t2.contact_uuid
left outer join v_user_groups t3 on t2.group_uuid = t3.group_uuid
left outer join v_users t4 on t3.user_uuid = t4.user_uuid
left outer join v_extension_users t5 on t4.user_uuid = t5.user_uuid
left outer join v_extensions t6 on t5.extension_uuid = t6.extension_uuid
where t0.domain_uuid = :domain_uuid and t0.phone_speed_dial = :phone_speed_dial
and ( (1 = 0)
or (t6.domain_uuid = :domain_uuid and (t6.extension = :user or t6.number_alias = :user))
or (t2.contact_uuid is null and not exists(select 1 from v_contact_users t where t.contact_uuid = t0.contact_uuid) )
)
union
-- find all contacts with correct group or withot users and groups at all
select t0.phone_number -- , t5.extension, 'USER:' || t3.username as user_name
from v_contact_phones t0
inner join v_contacts t1 on t0.contact_uuid = t1.contact_uuid
left outer join v_contact_users t2 on t1.contact_uuid = t2.contact_uuid
left outer join v_users t3 on t2.user_uuid = t3.user_uuid
left outer join v_extension_users t4 on t3.user_uuid = t4.user_uuid
left outer join v_extensions t5 on t4.extension_uuid = t5.extension_uuid
where t0.domain_uuid = :domain_uuid and t0.phone_speed_dial = :phone_speed_dial
and ( (1 = 0)
or (t5.domain_uuid = :domain_uuid and (t5.extension = :user or t5.number_alias = :user))
or (t2.contact_user_uuid is null and not exists(select 1 from v_contact_groups t where t.contact_uuid = t0.contact_uuid))
)
]];
local params = {phone_speed_dial = destination, domain_uuid = domain_uuid, user = user};
if (debug["sql"]) then
log.noticef("SQL: %s; params: %s", sql, json.encode(params));
end
local phone_number = dbh:first_value(sql, params)
-- release database connection
dbh:release()
-- set the cache
if phone_number then
value = {phone_number = phone_number}
cache.set(key, json.encode(value), expire["speed_dial"])
end
end
-- transfer
if value then
--log the result
log.noticef("%s XML %s source: %s", destination, context, source)
--transfer the call
session:transfer(value.phone_number, "XML", context);
else
log.warningf('can not find number: %s in domain: %s', destination, domain_name)
end

View File

@@ -0,0 +1,212 @@
--
-- FusionPBX
-- Version: MPL 1.1
--
-- The contents of this file are subject to the Mozilla Public License Version
-- 1.1 (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
-- http://www.mozilla.org/MPL/
--
-- Software distributed under the License is distributed on an "AS IS" basis,
-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-- for the specific language governing rights and limitations under the
-- License.
--
-- The Original Code is FusionPBX
--
-- The Initial Developer of the Original Code is
-- Mark J Crane <markjcrane@fusionpbx.com>
-- Copyright (C) 2010-2014
-- the Initial Developer. All Rights Reserved.
--
-- Contributor(s):
-- Riccardo Granchi <riccardo.granchi@nems.it>
-- Philippe Rioual <bhouba@gmail.com>
-- Alexey Melnichuck <alexeymelnichuck@gmail.com>
--Configuration
-- Define known template names
local known_templates = {
["mobile" ] = true,
["landline" ] = true,
["international"] = true,
["tollfree" ] = true,
["sharedcharge" ] = true,
["premium" ] = true,
["unknown" ] = true,
}
--Define templates for every toll type for your country
local templates = {
IT = {
{"mobile", "[35]%d%d%d%d%d%d+" },
{"landline", "0[123456789]%d+" },
{"international", "00%d+" },
{"tollfree", "119|1[3456789]%d|19[24]%d|192[01]%d%d|800%d%d%d%d%d+|803%d%d%d+|456%d%d%d%d%d%d+|11[2345678]|15%d%d|116%d%d%d|196%d%d" },
{"sharedcharge", "84[0178]%d%d%d%d+|199%d%d%d%d%d+|178%d%d%d%d%d+|12%d%d|10%d%d%d+|1482|149%d+|4[012]%d+|70%d%d%d%d%d+" },
{"premium", "89[2459]%d%d%d+|16[456]%d%d%d+|144%d%d%d+|4[346789]%d%d+" },
{"unknown", "%d%d+" },
};
FR = {
{"mobile", "0[67]%d%d%d%d%d%d%d%d" },
{"landline", "0[1234589]%d%d%d%d%d%d%d%d" },
{"international", "00%d+" },
{"tollfree", "15|17|18|112|114|115|116%d%d%d|118%d%d%d|119|19[16]|1[06]%d%d|080%d+" },
{"sharedcharge", "081%d+|082[0156]%d+|0884%d+|089[0123789]%d+" },
{"premium", "%d%d+" },
{"unknown", "%d%d+" },
};
US = {
{"unknown", "%d+"},
};
RU = {
{"international", "810%d+|8[89]40%d+|87%d%d%d%d%d%d%d%d%d"};
{"mobile", "89%d%d%d%d%d%d%d%d%d" };
{"tollfree", "8800%d%d%d%d%d%d%d|10[1-9]" };
{"landline", "8[3-68]%d%d%d%d%d%d%d%d%d" };
{"unknown", "" };
};
ZA = {
{"international", "00%d+" };
{"mobile", "0[6-8]%d%d%d%d%d%d%d%d" };
{"landline", "0[1-5]%d%d%d%d%d%d%d%d" };
{"unknown", "" };
};
}
--Set to true to allow all calls for extensions without toll_allow
local ACCEPT_EMPTY_TOLL_ALLOW = false
--debug
debug["toll_type"] = false
require "resources.functions.explode";
--create the api object and get variables
local api = freeswitch.API()
local uuid = argv[2]
if not uuid or uuid == "" then
return
end
local function hungup()
session:hangup("OUTGOING_CALL_BARRED")
end
local function log(level, msg)
freeswitch.consoleLog(level, "[toll_allow] " .. msg .. "\n")
end
local function logf(level, ...)
return log(level, string.format(...))
end
local function trace(type, ...)
if debug[type] then log(...) end
end
local function tracef(type, ...)
if debug[type] then logf(...) end
end
local function channel_variable(uuid, name)
local result = api:executeString("uuid_getvar " .. uuid .. " " .. name)
tracef("toll_type", "NOTICE", "channel_variable %s - %s", name, result)
if result:sub(1, 4) == '-ERR' then return nil end
return result
end
local function template_match(prefix, template, called)
local parts = explode("|", template)
for index,part in ipairs(parts) do
local pattern = "^" .. prefix .. part .. "$"
if ( string.match(called, pattern) ~= nil ) then
return pattern
end
end
end
local function get_toll_type(prefix, templates, called)
for _,params in ipairs(templates) do
local label, template = params[1], params[2]
if not known_templates[label] then
logf("WARNING", "unknown template name: %s in country template array", label)
end
trace("toll_type", "NOTICE", "checking toll type " .. label .. " template: " .. template)
local pattern = template_match(prefix, template, called)
if pattern then
trace("toll_type", "NOTICE", "destination number " .. called .. " matches " .. label .. " pattern: " .. pattern)
return label
end
end
end
local function is_undef(str)
return (not str) or (#str == 0) or (str == "_undef_")
end
local called = channel_variable(uuid, "destination_number") or ""
local caller = channel_variable(uuid, "caller_id_number") or ""
local prefix = channel_variable(uuid, "outbound_prefix") or ""
local country = channel_variable(uuid, "default_country") or ""
local toll_allow = channel_variable(uuid, "toll_allow") or ""
if (debug["toll_type"]) then
logf("NOTICE", "called: %s", called)
logf("NOTICE", "prefix: %s", prefix)
logf("NOTICE", "country: %s", country)
logf("NOTICE", "tollAllow: %s", toll_allow)
end
if is_undef(toll_allow) then
if ACCEPT_EMPTY_TOLL_ALLOW then
logf("NOTICE", "unknown call authorized from %s to %s", caller, called)
return
end
logf("WARNING", "unknown call not authorized from %s to %s : OUTGOING_CALL_BARRED", caller, called)
return hungup()
end
if is_undef(prefix) then
prefix = ""
end
if is_undef(country) then
log("WARNING", "undefined country")
return
end
local templates = templates[country]
if not templates then
log("WARNING", "undefined templates")
return
end
--set toll_type
local toll_type = get_toll_type(prefix, templates, called) or "unknown"
log("NOTICE", "toll type: " .. toll_type)
local parts = explode(",", toll_allow)
for i,part in ipairs(parts) do
if not known_templates[part] then
logf("WARNING", "unknown toll_allow name: %s in extension", part)
end
tracef("toll_type", "NOTICE", "checking toll allow part " .. part)
if ( part == toll_type ) then
logf("NOTICE", "%s call authorized from %s to %s", toll_type, caller, called)
return
end
end
logf("WARNING", "%s call not authorized from %s to %s : OUTGOING_CALL_BARRED", toll_type, caller, called)
return hungup()

View File

@@ -0,0 +1,30 @@
text = text or {};
text['label-download'] = {};
text['label-download']['en-us'] = "Download";
text['label-download']['es-cl'] = "Descargar";
text['label-download']['pt-pt'] = "Baixar";
text['label-download']['ru-ru'] = "Скачать";
text['label-download']['fr-fr'] = "Télécharger";
text['label-download']['de-de'] = "Download";
text['label-download']['de-at'] = "Download";
text['label-listen'] = {};
text['label-listen']['en-us'] = "Listen";
text['label-listen']['es-cl'] = "Escuchar";
text['label-listen']['pt-pt'] = "Ouvir";
text['label-listen']['ru-ru'] = "Слушать";
text['label-listen']['fr-fr'] = "Écouter";
text['label-listen']['de-de'] = "Anhören";
text['label-listen']['de-at'] = "Anhören";
text['label-attached'] = {};
text['label-attached']['en-us'] = "Attached";
text['label-attached']['es-cl'] = "Adjunto";
text['label-attached']['pt-pt'] = "Ligado";
text['label-attached']['ru-ru'] = "Вложение";
text['label-attached']['fr-fr'] = "Attaché";
text['label-attached']['de-de'] = "im Anhang";
text['label-attached']['de-at'] = "im Anhang";
return text

View File

@@ -0,0 +1,667 @@
-- Part of FusionPBX
-- Copyright (C) 2013-2019 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--set default values
min_digits = 1;
max_digits = 8;
max_tries = 3;
max_timeouts = 3;
digit_timeout = 3000;
stream_seek = false;
--direct dial
direct_dial = {}
direct_dial["enabled"] = "false";
direct_dial["max_digits"] = 4;
--debug
--debug["info"] = true;
--debug["sql"] = true;
--get the argv values
script_name = argv[1];
voicemail_action = argv[2];
--starting values
dtmf_digits = '';
timeouts = 0;
password_tries = 0;
--connect to the database
Database = require "resources.functions.database";
dbh = Database.new('system');
--include json library (as global object)
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--set the api
api = freeswitch.API();
--if the session exists
if (session ~= nil) then
--get session variables
context = session:getVariable("context");
sounds_dir = session:getVariable("sounds_dir");
domain_name = session:getVariable("domain_name");
uuid = session:getVariable("uuid");
voicemail_id = session:getVariable("voicemail_id");
voicemail_action = session:getVariable("voicemail_action");
destination_number = session:getVariable("destination_number");
caller_id_name = session:getVariable("caller_id_name");
caller_id_number = session:getVariable("caller_id_number");
current_time_zone = session:getVariable("timezone");
effective_caller_id_number = session:getVariable("effective_caller_id_number");
voicemail_greeting_number = session:getVariable("voicemail_greeting_number");
skip_instructions = session:getVariable("skip_instructions");
skip_greeting = session:getVariable("skip_greeting");
vm_message_ext = session:getVariable("vm_message_ext");
vm_say_caller_id_number = session:getVariable("vm_say_caller_id_number");
vm_say_date_time = session:getVariable("vm_say_date_time");
vm_disk_quota = session:getVariable("vm-disk-quota");
record_silence_threshold = session:getVariable("record-silence-threshold");
voicemail_authorized = session:getVariable("voicemail_authorized");
sip_from_user = session:getVariable("sip_from_user");
sip_number_alias = session:getVariable("sip_number_alias");
--modify caller_id_number if effective_caller_id_number is set
if (effective_caller_id_number ~= nil) then
caller_id_number = effective_caller_id_number;
end
--set default values
if (string.sub(caller_id_number, 1, 1) == "/") then
caller_id_number = string.sub(caller_id_number, 2, -1);
end
if (not record_silence_threshold) then
record_silence_threshold = 300;
end
if (not vm_disk_quota) then
vm_disk_quota = session:getVariable("vm_disk_quota");
end
if (not vm_message_ext) then
vm_message_ext = 'wav';
end
if (not vm_say_caller_id_number) then
vm_say_caller_id_number = "true";
end
if (not vm_say_date_time) then
vm_say_date_time = "true";
end
--set the sounds path for the language, dialect and voice
default_language = session:getVariable("default_language");
default_dialect = session:getVariable("default_dialect");
default_voice = session:getVariable("default_voice");
if (not default_language) then default_language = 'en'; end
if (not default_dialect) then default_dialect = 'us'; end
if (not default_voice) then default_voice = 'callie'; end
--get the domain_uuid
domain_uuid = session:getVariable("domain_uuid");
if (domain_count > 1) then
if (domain_uuid == nil) then
--get the domain_uuid using the domain name required for multi-tenant
if (domain_name ~= nil) then
local sql = "SELECT domain_uuid FROM v_domains ";
sql = sql .. "WHERE domain_name = :domain_name ";
local params = {domain_name = domain_name};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(rows)
domain_uuid = rows["domain_uuid"];
end);
end
end
end
if (domain_uuid ~= nil) then
domain_uuid = string.lower(domain_uuid);
end
--if voicemail_id is non numeric then get the number-alias
if (voicemail_id ~= nil) then
if tonumber(voicemail_id) == nil then
voicemail_id = api:execute("user_data", voicemail_id .. "@" .. domain_name .. " attr number-alias");
end
end
--set the voicemail_dir
voicemail_dir = voicemail_dir.."/default/"..domain_name;
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] voicemail_dir: " .. voicemail_dir .. "\n");
end
--settings
require "resources.functions.settings";
settings = settings(domain_uuid);
if (settings['voicemail'] ~= nil) then
storage_type = '';
if (settings['voicemail']['storage_type'] ~= nil) then
if (settings['voicemail']['storage_type']['text'] ~= nil) then
storage_type = settings['voicemail']['storage_type']['text'];
end
end
storage_path = '';
if (settings['voicemail']['storage_path'] ~= nil) then
if (settings['voicemail']['storage_path']['text'] ~= nil) then
storage_path = settings['voicemail']['storage_path']['text'];
storage_path = storage_path:gsub("${domain_name}", domain_name);
storage_path = storage_path:gsub("${voicemail_id}", voicemail_id);
storage_path = storage_path:gsub("${voicemail_dir}", voicemail_dir);
end
end
message_order = '';
if (settings['voicemail']['message_order'] ~= nil) then
if (settings['voicemail']['message_order']['text'] ~= nil) then
message_order = settings['voicemail']['message_order']['text'];
end
end
remote_access = '';
if (settings['voicemail']['remote_access'] ~= nil) then
if (settings['voicemail']['remote_access']['boolean'] ~= nil) then
remote_access = settings['voicemail']['remote_access']['boolean'];
end
end
password_complexity = '';
if (settings['voicemail']['password_complexity'] ~= nil) then
if (settings['voicemail']['password_complexity']['boolean'] ~= nil) then
password_complexity = settings['voicemail']['password_complexity']['boolean'];
end
end
password_min_length = '';
if (settings['voicemail']['password_min_length'] ~= nil) then
if (settings['voicemail']['password_min_length']['numeric'] ~= nil) then
password_min_length = settings['voicemail']['password_min_length']['numeric'];
end
end
not_found_message = 'false';
if (settings['voicemail']['not_found_message'] ~= nil) then
if (settings['voicemail']['not_found_message']['boolean'] ~= nil) then
not_found_message = settings['voicemail']['not_found_message']['boolean'];
end
end
end
if (settings['voicemail']) then
if settings['voicemail']['voicemail_to_sms'] then
voicemail_to_sms = (settings['voicemail']['voicemail_to_sms']['boolean'] == 'true');
end
if settings['voicemail']['voicemail_to_sms_did'] then
voicemail_to_sms_did = settings['voicemail']['voicemail_to_sms_did']['text'];
end
voicemail_to_sms_did = voicemail_to_sms_did or '';
end
if (not temp_dir) or (#temp_dir == 0) then
if (settings['server'] ~= nil) then
if (settings['server']['temp'] ~= nil) then
if (settings['server']['temp']['dir'] ~= nil) then
temp_dir = settings['server']['temp']['dir'];
end
end
end
end
--get the voicemail settings
if (voicemail_id ~= nil) then
if (session ~= nil and session:ready()) then
--get the information from the database
local sql = [[SELECT * FROM v_voicemails
WHERE domain_uuid = :domain_uuid
AND voicemail_id = :voicemail_id
AND voicemail_enabled = 'true' ]];
local params = {domain_uuid = domain_uuid, voicemail_id = voicemail_id};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
voicemail_uuid = string.lower(row["voicemail_uuid"]);
voicemail_password = row["voicemail_password"];
greeting_id = row["greeting_id"];
voicemail_alternate_greet_id = row["voicemail_alternate_greet_id"];
voicemail_mail_to = row["voicemail_mail_to"];
voicemail_attach_file = row["voicemail_attach_file"];
voicemail_local_after_email = row["voicemail_local_after_email"];
voicemail_transcription_enabled = row["voicemail_transcription_enabled"];
voicemail_tutorial = row["voicemail_tutorial"];
end);
--set default values
if (voicemail_local_after_email == nil) then
voicemail_local_after_email = "true";
end
if (voicemail_attach_file == nil) then
voicemail_attach_file = "true";
end
--valid voicemail
if (voicemail_uuid ~= nil and string.len(voicemail_uuid) > 0) then
--answer the session
if (session ~= nil and session:ready()) then
session:answer();
session:execute("sleep", "1000");
end
--unset bind meta app
session:execute("unbind_meta_app", "");
end
end
end
end
--set the callback function
if (session ~= nil and session:ready()) then
session:setVariable("playback_terminators", "#");
session:setInputCallback("on_dtmf", "");
end
--general functions
require "resources.functions.base64";
require "resources.functions.trim";
require "resources.functions.file_exists";
require "resources.functions.explode";
require "resources.functions.format_seconds";
require "resources.functions.mkdir";
require "resources.functions.copy";
--voicemail functions
require "app.voicemail.resources.functions.on_dtmf";
require "app.voicemail.resources.functions.get_voicemail_id";
require "app.voicemail.resources.functions.check_password";
require "app.voicemail.resources.functions.change_password";
require "app.voicemail.resources.functions.macro";
require "app.voicemail.resources.functions.play_greeting";
require "app.voicemail.resources.functions.record_message";
require "app.voicemail.resources.functions.record_menu";
require "app.voicemail.resources.functions.forward_add_intro";
require "app.voicemail.resources.functions.forward_to_extension";
require "app.voicemail.resources.functions.main_menu";
require "app.voicemail.resources.functions.listen_to_recording";
require "app.voicemail.resources.functions.message_waiting";
require "app.voicemail.resources.functions.send_email";
require "app.voicemail.resources.functions.send_sms";
require "app.voicemail.resources.functions.delete_recording";
require "app.voicemail.resources.functions.message_saved";
require "app.voicemail.resources.functions.return_call";
require "app.voicemail.resources.functions.menu_messages";
require "app.voicemail.resources.functions.advanced";
require "app.voicemail.resources.functions.record_greeting";
require "app.voicemail.resources.functions.choose_greeting";
require "app.voicemail.resources.functions.record_name";
require "app.voicemail.resources.functions.message_count"
require "app.voicemail.resources.functions.mwi_notify";
require "app.voicemail.resources.functions.tutorial";
--send a message waiting event
if (voicemail_action == "mwi") then
--get the mailbox info
account = argv[3];
array = explode("@", account);
voicemail_id = array[1];
domain_name = array[2];
--send information the console
debug["info"] = "true";
--get voicemail message details
local sql = [[SELECT * FROM v_domains WHERE domain_name = :domain_name]];
local params = {domain_name = domain_name};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
domain_uuid = string.lower(row["domain_uuid"]);
end);
--get the message count and send the mwi event
message_waiting(voicemail_id, domain_uuid);
end
--check messages
if (voicemail_action == "check") then
if (session ~= nil and session:ready()) then
--check the voicemail password
if (voicemail_id) then
if (voicemail_authorized) then
if (voicemail_authorized == "true") then
if (voicemail_id == sip_from_user or voicemail_id == sip_number_alias) then
--skip the password check
else
check_password(voicemail_id, password_tries);
end
else
check_password(voicemail_id, password_tries);
end
else
check_password(voicemail_id, password_tries);
end
else
check_password(voicemail_id, password_tries);
end
--send to the main menu
timeouts = 0;
if (voicemail_tutorial == "true") then
tutorial("intro");
else
main_menu();
end
end
end
--leave a message
if (voicemail_action == "save") then
--set the variables
if (session ~= nil and session:ready()) then
session:setVariable("missed_call", "true");
session:setVariable("voicemail_answer_stamp", api:execute("strftime"));
session:setVariable("voicemail_answer_epoch", api:execute("strepoch"));
end
--check the voicemail quota
if (voicemail_uuid ~= nil and vm_disk_quota ~= nil) then
--get voicemail message seconds
local sql = [[SELECT coalesce(sum(message_length), 0) as message_sum FROM v_voicemail_messages
WHERE domain_uuid = :domain_uuid
AND voicemail_uuid = :voicemail_uuid]]
local params = {domain_uuid = domain_uuid, voicemail_uuid = voicemail_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
message_sum = row["message_sum"];
end);
if (tonumber(vm_disk_quota) <= tonumber(message_sum)) then
--play message mailbox full
session:execute("playback", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/voicemail/vm-mailbox_full.wav")
--hangup
session:hangup("NORMAL_CLEARING");
--set the voicemail_uuid to nil to prevent saving the voicemail
voicemail_uuid = nil;
end
end
--valid voicemail
if (voicemail_uuid ~= nil) then
--play the greeting
timeouts = 0;
play_greeting();
--save the message
record_message();
--process base64
if (storage_type == "base64") then
--show the storage type
freeswitch.consoleLog("notice", "[voicemail] ".. storage_type .. "\n");
--include the file io
local file = require "resources.functions.file"
-- build full path to file
local full_path = voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext
if file_exists(full_path) then
--read file content as base64 string
message_base64 = file.read_base64(full_path);
--freeswitch.consoleLog("notice", "[voicemail] ".. message_base64 .. "\n");
--delete the file
os.remove(full_path);
end
end
--get the voicemail destinations
sql = [[select * from v_voicemail_destinations
where voicemail_uuid = :voicemail_uuid]]
params = {voicemail_uuid=voicemail_uuid};
--freeswitch.consoleLog("notice", "[voicemail][destinations] SQL:" .. sql .. "; params:" .. json.encode(params) .. "\n");
destinations = {};
x = 1;
dbh:query(sql, params, function(row)
destinations[x] = row;
x = x + 1;
end);
table.insert(destinations, {domain_uuid=domain_uuid,voicemail_destination_uuid=voicemail_uuid,voicemail_uuid=voicemail_uuid,voicemail_uuid_copy=voicemail_uuid});
--show the storage type
freeswitch.consoleLog("notice", "[voicemail] ".. storage_type .. "\n");
count = 0
for k,v in pairs(destinations) do
count = count + 1
end
--loop through the voicemail destinations
y = 1;
for key,row in pairs(destinations) do
--determine uuid
if (y == count) then
voicemail_message_uuid = uuid;
else
voicemail_message_uuid = api:execute("create_uuid");
end
y = y + 1;
--save the message to the voicemail messages
if (tonumber(message_length) > 2) then
caller_id_name = string.gsub(caller_id_name,"'","''");
local sql = {}
table.insert(sql, "INSERT INTO v_voicemail_messages ");
table.insert(sql, "(");
table.insert(sql, "voicemail_message_uuid, ");
table.insert(sql, "domain_uuid, ");
table.insert(sql, "voicemail_uuid, ");
table.insert(sql, "created_epoch, ");
table.insert(sql, "caller_id_name, ");
table.insert(sql, "caller_id_number, ");
if (storage_type == "base64") then
table.insert(sql, "message_base64, ");
end
if (transcribe_enabled == "true") and (voicemail_transcription_enabled == "true") then
table.insert(sql, "message_transcription, ");
end
table.insert(sql, "message_length ");
--table.insert(sql, "message_status, ");
--table.insert(sql, "message_priority, ");
table.insert(sql, ") ");
table.insert(sql, "VALUES ");
table.insert(sql, "( ");
table.insert(sql, ":voicemail_message_uuid, ");
table.insert(sql, ":domain_uuid, ");
table.insert(sql, ":voicemail_uuid, ");
table.insert(sql, ":start_epoch, ");
table.insert(sql, ":caller_id_name, ");
table.insert(sql, ":caller_id_number, ");
if (storage_type == "base64") then
table.insert(sql, ":message_base64, ");
end
if (transcribe_enabled == "true") and (voicemail_transcription_enabled == "true") then
table.insert(sql, ":transcription, ");
end
table.insert(sql, ":message_length ");
--table.insert(sql, ":message_status, ");
--table.insert(sql, ":message_priority ");
table.insert(sql, ") ");
sql = table.concat(sql, "\n");
local params = {
voicemail_message_uuid = voicemail_message_uuid;
domain_uuid = domain_uuid;
voicemail_uuid = row.voicemail_uuid_copy;
start_epoch = start_epoch;
caller_id_name = caller_id_name;
caller_id_number = caller_id_number;
message_base64 = message_base64;
transcription = transcription;
message_length = message_length;
--message_status = message_status;
--message_priority = message_priority;
};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
if (storage_type == "base64") then
local Database = require "resources.functions.database"
local dbh = Database.new('system', 'base64');
dbh:query(sql, params);
dbh:release();
else
dbh:query(sql, params);
end
end
local params = {domain_uuid = domain_uuid, voicemail_uuid = row.voicemail_uuid_copy};
--get new message count
sql = [[SELECT count(*) as new_messages FROM v_voicemail_messages
WHERE domain_uuid = :domain_uuid
AND voicemail_uuid = :voicemail_uuid
AND (message_status is null or message_status = '') ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(result)
new_messages = result["new_messages"];
end);
--get saved message count
sql = [[SELECT count(*) as saved_messages FROM v_voicemail_messages
WHERE domain_uuid = :domain_uuid
AND voicemail_uuid = :voicemail_uuid
AND message_status = 'saved' ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(result)
saved_messages = result["saved_messages"];
end);
--get the voicemail_id
sql = [[SELECT voicemail_id FROM v_voicemails WHERE voicemail_uuid = :voicemail_uuid]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(result)
voicemail_id_copy = result["voicemail_id"];
end);
--make sure the voicemail directory exists
mkdir(voicemail_dir.."/"..voicemail_id_copy);
--copy the voicemail to each destination
if (file_exists(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext)) then
local src = voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext
local dst = voicemail_dir.."/"..voicemail_id_copy.."/msg_"..voicemail_message_uuid.."."..vm_message_ext
if src ~= dst then
copy(src, dst)
end
end
--send the message waiting event
if (tonumber(message_length) > 2) then
message_waiting(voicemail_id_copy, domain_uuid);
end
--send the email with the voicemail recording attached
if (tonumber(message_length) > 2) then
send_email(voicemail_id_copy, voicemail_message_uuid);
if (voicemail_to_sms) then
send_sms(voicemail_id_copy, voicemail_message_uuid);
end
end
end --for
else
--voicemail not enabled or does not exist
if (session ~= nil and session:ready()) then
referred_by = session:getVariable("sip_h_Referred-By");
if (referred_by) then
referred_by = referred_by:match('[%d]+');
session:transfer(referred_by, "XML", context);
else
if (not_found_message == "true") then
session:answer();
session:execute("sleep", "1000");
session:execute("playback", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/voicemail/vm-no_answer_no_vm.wav");
session:hangup();
end
end
end
end
end
--close the database connection
dbh:release();
--notes
--record the video
--records audio only
--result = session:execute("set", "enable_file_write_buffering=false");
--mkdir(voicemail_dir.."/"..voicemail_id);
--session:recordFile("/tmp/recording.fsv", 200, 200, 200);
--records audio and video
--result = session:execute("record_fsv", "file.fsv");
--freeswitch.consoleLog("notice", "[voicemail] SQL: " .. result .. "\n");
--play the video recording
--plays the video
--result = session:execute("play_fsv", "/tmp/recording.fsv");
--plays the file but without the video
--dtmf = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", "/tmp/recording.fsv", "", "\\d+");
--freeswitch.consoleLog("notice", "[voicemail] SQL: " .. result .. "\n");
--callback (works with DTMF)
--http://wiki.freeswitch.org/wiki/Mod_fsv
--mkdir(voicemail_dir.."/"..voicemail_id);
--session:recordFile(file_name, max_len_secs, silence_threshold, silence_secs)
--session:sayPhrase(macro_name [,macro_data] [,language]);
--session:sayPhrase("voicemail_menu", "1:2:3:#", default_language);
--session:streamFile("directory/dir-to_select_entry.wav"); --works with setInputCallback
--session:streamFile("tone_stream://L=1;%(1000, 0, 640)");
--session:say("12345", default_language, "number", "pronounced");
--speak
--session:set_tts_parms("flite", "kal");
--session:speak("Please say the name of the person you're trying to contact");
--callback (execute and executeString does not work with DTMF)
--session:execute(api_string);
--session:executeString("playback "..mySound);
--uuid_video_refresh
--uuid_video_refresh,<uuid>,Send video refresh.,mod_commands
--may be used to clear video buffer before using record_fsv

View File

@@ -0,0 +1,90 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--define a function for the advanced menu
function advanced ()
--clear the dtmf
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--To record a greeting press 1
if (session:ready()) then
dtmf_digits = macro(session, "to_record_greeting", 1, 100, '');
end
--To choose greeting press 2
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "choose_greeting", 1, 100, '');
end
end
--To record your name 3
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "to_record_name", 1, 100, '');
end
end
--To change your password press 6
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "change_password", 1, 100, '');
end
end
--For the main menu press 0
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "main_menu", 1, 5000, '');
end
end
--process the dtmf
if (session:ready()) then
if (dtmf_digits == "1") then
--To record a greeting press 1
timeouts = 0;
record_greeting(nil,"advanced");
elseif (dtmf_digits == "2") then
--To choose greeting press 2
timeouts = 0;
choose_greeting();
elseif (dtmf_digits == "3") then
--To record your name 3
record_name("advanced");
elseif (dtmf_digits == "6") then
--To change your password press 6
change_password(voicemail_id, "advanced");
elseif (dtmf_digits == "0") then
--For the main menu press 0
timeouts = 0;
main_menu();
else
timeouts = timeouts + 1;
if (timeouts <= max_timeouts) then
advanced();
else
macro(session, "goodbye", 1, 1000, '');
session:hangup();
end
end
end
end

View File

@@ -0,0 +1,123 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--check the voicemail password
function change_password(voicemail_id, menu)
if (session:ready()) then
--flush dtmf digits from the input buffer
session:flushDigits();
--set password valitity in case of hangup
valid_password = "false";
--please enter your password followed by pound
dtmf_digits = '';
password = macro(session, "password_new", 20, 5000, '');
if (password_complexity ~= "true") then
valid_password = "true";
end
--check password comlexity
if (password_complexity == "true") then
--check for length
if (string.len(password) < tonumber(password_min_length)) then
password_error_flag = "1";
dtmf_digits = '';
--freeswitch.consoleLog("notice", "[voicemail] Not long enough \n");
macro(session, "password_below_minimum", 20, 3000, password_min_length);
timeouts = 0;
if (menu == "tutorial") then
change_password(voicemail_id, "tutorial");
end
if (menu == "advanced") then
change_password(voicemail_id, "advanced");
end
end
--check for repeating digits
local repeating = {"000", "111", "222", "333", "444", "555", "666", "777", "888", "999"};
for i = 1, 10 do
if (string.match(password, repeating[i])) then
password_error_flag = "1";
dtmf_digits = '';
--freeswitch.consoleLog("notice", "[voicemail] You can't use repeating digits like ".. repeating[i] .." \n");
macro(session, "password_not_secure", 20, 3000);
timeouts = 0;
if (menu == "tutorial") then
change_password(voicemail_id, "tutorial");
end
if (menu == "advanced") then
change_password(voicemail_id, "advanced");
end
end
end
--check for squential digits
local sequential = {"012", "123", "345", "456", "567", "678", "789", "987"};
for i = 1, 8 do
if (string.match(password, sequential[i])) then
password_error_flag = "1";
dtmf_digits = '';
--freeswitch.consoleLog("notice", "[voicemail] You can't use sequential digits like ".. sequential[i] .." \n");
macro(session, "password_not_secure", 20, 3000);
timeouts = 0;
if (menu == "tutorial") then
change_password(voicemail_id, "tutorial");
end
if (menu == "advanced") then
change_password(voicemail_id, "advanced");
end
end
end
--password is valid
if (password_error_flag ~= "1") then
freeswitch.consoleLog("notice", "[voicemail] Password is valid! \n");
valid_password = "true";
end
end
--update the voicemail password
if (valid_password == "true") then
local sql = [[UPDATE v_voicemails
set voicemail_password = :password
WHERE domain_uuid = :domain_uuid
AND voicemail_id = :voicemail_id
AND voicemail_enabled = 'true' ]];
local params = {password = password, domain_uuid = domain_uuid,
voicemail_id = voicemail_id};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
end
--has been changed to
dtmf_digits = '';
macro(session, "password_changed", 20, 3000, password);
--advanced menu
timeouts = 0;
if (menu == "advanced") then
advanced();
end
if (menu == "tutorial") then
tutorial("record_greeting");
end
end
end

View File

@@ -0,0 +1,95 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--check the voicemail password
function check_password(voicemail_id, password_tries)
if (session:ready()) then
--flush dtmf digits from the input buffer
session:flushDigits();
--please enter your id followed by pound
if (voicemail_id) then
--do nothing
else
timeouts = 0;
voicemail_id = get_voicemail_id();
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] voicemail id: " .. voicemail_id .. "\n");
end
end
--get the voicemail settings from the database
if (voicemail_id) then
if (session:ready()) then
local sql = [[SELECT * FROM v_voicemails
WHERE domain_uuid = :domain_uuid
AND voicemail_id = :voicemail_id
AND voicemail_enabled = 'true' ]];
local params = {domain_uuid = domain_uuid, voicemail_id = voicemail_id};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
voicemail_uuid = string.lower(row["voicemail_uuid"]);
voicemail_password = row["voicemail_password"];
greeting_id = row["greeting_id"];
voicemail_mail_to = row["voicemail_mail_to"];
voicemail_attach_file = row["voicemail_attach_file"];
voicemail_local_after_email = row["voicemail_local_after_email"];
end);
end
end
--end the session if this is an invalid voicemail box
if (not voicemail_uuid) or (#voicemail_uuid == 0) then
return session:hangup();
end
--please enter your password followed by pound
min_digits = 2;
max_digits = 20;
digit_timeout = 5000;
max_tries = 3;
password = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", "phrase:voicemail_enter_pass:#", "", "\\d+");
--freeswitch.consoleLog("notice", "[voicemail] password: " .. password .. "\n");
--compare the password from the database with the password provided by the user
if (voicemail_password ~= password) then
--incorrect password
dtmf_digits = '';
macro(session, "password_not_valid", 1, 1000, '');
if (session:ready()) then
password_tries = password_tries + 1;
if (password_tries < max_tries) then
check_password(voicemail_id, password_tries);
else
macro(session, "goodbye", 1, 1000, '');
session:hangup();
end
end
end
end
end

View File

@@ -0,0 +1,167 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
local Database = require "resources.functions.database"
--define a function to choose the greeting
function choose_greeting()
--flush dtmf digits from the input buffer
session:flushDigits();
--select the greeting
if (session:ready()) then
dtmf_digits = '';
greeting_id = macro(session, "choose_greeting_choose", 1, 5000, '');
end
--check to see if the greeting file exists
if (storage_type == "base64" or storage_type == "http_cache") then
greeting_invalid = true;
local sql = [[SELECT * FROM v_voicemail_greetings
WHERE domain_uuid = :domain_uuid
AND voicemail_id = :voicemail_id
AND greeting_id = :greeting_id]];
local params = {domain_uuid = domain_uuid, voicemail_id = voicemail_id,
greeting_id = greeting_id};
dbh:query(sql, params, function(row)
--greeting found
greeting_invalid = false;
end);
if (greeting_invalid) then
greeting_id = "invalid";
end
else
if (greeting_id ~= "0") then
if (not file_exists(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav")) then
--invalid greeting_id file does not exist
greeting_id = "invalid";
end
end
end
--validate the greeting_id
if (greeting_id == "0"
or greeting_id == "1"
or greeting_id == "2"
or greeting_id == "3"
or greeting_id == "4"
or greeting_id == "5"
or greeting_id == "6"
or greeting_id == "7"
or greeting_id == "8"
or greeting_id == "9") then
--valid greeting_id update the database
if (session:ready()) then
local params = {domain_uuid = domain_uuid, voicemail_uuid = voicemail_uuid};
local sql = "UPDATE v_voicemails SET "
if (greeting_id == "0") then
sql = sql .. "greeting_id = null ";
else
sql = sql .. "greeting_id = :greeting_id ";
params.greeting_id = greeting_id;
end
sql = sql .. "WHERE domain_uuid = :domain_uuid ";
sql = sql .. "AND voicemail_uuid = :voicemail_uuid ";
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
end
--get the greeting from the database
if (storage_type == "base64") then
local dbh = Database.new('system', 'base64/read')
local sql = [[SELECT greeting_base64
FROM v_voicemail_greetings
WHERE domain_uuid = :domain_uuid
AND voicemail_id = :voicemail_id
AND greeting_id = :greeting_id]];
local params = {
domain_uuid = domain_uuid;
voicemail_id = voicemail_id;
greeting_id = greeting_id;
};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--set the voicemail message path
greeting_location = voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav"; --vm_message_ext;
--save the greeting to the file system
if (string.len(row["greeting_base64"]) > 32) then
--include the file io
local file = require "resources.functions.file"
--write decoded string to file
assert(file.write_base64(greeting_location, row["greeting_base64"]));
end
end);
dbh:release()
elseif (storage_type == "http_cache") then
greeting_location = storage_path.."/"..voicemail_id.."/greeting_"..greeting_id..".wav"; --vm_message_ext;
end
--play the greeting
if (session:ready()) then
if (file_exists(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav")) then
session:streamFile(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav");
end
end
--greeting selected
if (session:ready()) then
dtmf_digits = '';
macro(session, "greeting_selected", 1, 100, greeting_id);
end
--advanced menu
if (session:ready()) then
timeouts = 0;
advanced();
end
else
--invalid greeting_id
if (session:ready()) then
dtmf_digits = '';
greeting_id = macro(session, "choose_greeting_fail", 1, 100, '');
end
--send back to choose the greeting
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
choose_greeting();
else
timeouts = 0;
advanced();
end
end
end
end

View File

@@ -0,0 +1,65 @@
-- Part of FusionPBX
-- Copyright (C) 2013-2016 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--delete the message
function delete_recording(voicemail_id, uuid)
--message deleted
if (session ~= nil) then
if (session:ready()) then
dtmf_digits = '';
macro(session, "message_deleted", 1, 100, '');
end
end
--get the voicemail_uuid
local sql = [[SELECT * FROM v_voicemails
WHERE domain_uuid = :domain_uuid
AND voicemail_id = :voicemail_id]];
local params = {domain_uuid = domain_uuid, voicemail_id = voicemail_id};
dbh:query(sql, params, function(row)
db_voicemail_uuid = row["voicemail_uuid"];
end);
--flush dtmf digits from the input buffer
session:flushDigits();
--delete the file
os.remove(voicemail_dir.."/"..voicemail_id.."/intro_"..uuid.."."..vm_message_ext);
os.remove(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
--delete from the database
sql = [[DELETE FROM v_voicemail_messages
WHERE domain_uuid = :domain_uuid
AND voicemail_uuid = :voicemail_uuid
AND voicemail_message_uuid = :uuid]];
params = {domain_uuid = domain_uuid, voicemail_uuid = db_voicemail_uuid, uuid = uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--log to console
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail][deleted] message: " .. uuid .. "\n");
end
--clear the variable
db_voicemail_uuid = '';
end

View File

@@ -0,0 +1,120 @@
-- Part of FusionPBX
-- Copyright (C) 2016 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--define a function to forward a message to an extension
function forward_add_intro(voicemail_id, uuid)
--flush dtmf digits from the input buffer
session:flushDigits();
--request whether to add the intro
--To add an introduction to this message press 1
add_intro_id = session:playAndGetDigits(1, 1, 3, 5000, "#*", "phrase:voicemail_forward_prepend:1:2", "phrase:invalid_entry", "\\d+");
freeswitch.consoleLog("notice", "[voicemail][forward add intro] "..add_intro_id.."\n");
if (add_intro_id == '1') then
--load libraries
local Database = require "resources.functions.database";
local Settings = require "resources.functions.lazy_settings";
--connect to the database
local db = dbh or Database.new('system');
--get the settings.
local settings = Settings.new(db, domain_name, domain_uuid);
local max_len_seconds = settings:get('voicemail', 'max_len_seconds', 'boolean') or 300;
--record your message at the tone press any key or stop talking to end the recording
if (session:ready()) then
session:sayPhrase("voicemail_record_greeting", "", "en")
end
--set the file full path
message_location = voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext;
message_intro_location = voicemail_dir.."/"..voicemail_id.."/intro_"..uuid.."."..vm_message_ext;
--record the message introduction
-- syntax is session:recordFile(file_name, max_len_secs, silence_threshold, silence_secs)
silence_seconds = 5;
if (storage_path == "http_cache") then
result = session:recordFile(message_intro_location, max_len_seconds, record_silence_threshold, silence_seconds);
else
mkdir(voicemail_dir.."/"..voicemail_id);
if (vm_message_ext == "mp3") then
shout_exists = trim(api:execute("module_exists", "mod_shout"));
if (shout_exists == "true") then
freeswitch.consoleLog("notice", "using mod_shout for mp3 encoding\n");
--record in mp3 directly
result = session:recordFile(message_intro_location, max_len_seconds, record_silence_threshold, silence_seconds);
else
--create initial wav recording
result = session:recordFile(message_intro_location, max_len_seconds, record_silence_threshold, silence_seconds);
--use lame to encode, if available
if (file_exists("/usr/bin/lame")) then
freeswitch.consoleLog("notice", "using lame for mp3 encoding\n");
--convert the wav to an mp3 (lame required)
resample = "/usr/bin/lame -b 32 --resample 8 -m s "..voicemail_dir.."/"..voicemail_id.."/intro_"..uuid..".wav "..message_intro_location;
session:execute("system", resample);
--delete the wav file, if mp3 exists
if (file_exists(message_intro_location)) then
os.remove(voicemail_dir.."/"..voicemail_id.."/intro_"..uuid..".wav");
else
vm_message_ext = "wav";
end
else
freeswitch.consoleLog("notice", "neither mod_shout or lame found, defaulting to wav\n");
vm_message_ext = "wav";
end
end
else
result = session:recordFile(message_intro_location, max_len_seconds, record_silence_threshold, silence_seconds);
end
end
--save the merged file into the database as base64
if (storage_type == "base64") then
local file = require "resources.functions.file"
--get the content of the file
local file_content = assert(file.read_base64(message_intro_location));
--save the merged file as base64
local sql = [[UPDATE SET v_voicemail_messages
SET message_intro_base64 = :file_content
WHERE domain_uuid = :domain_uuid
AND voicemail_message_uuid = :uuid]];
local params = {file_content = file_content, domain_uuid = domain_uuid, uuid = uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params: " .. json.encode(params) .. "\n");
end
local dbh = Database.new('system', 'base64')
dbh:query(sql, params)
dbh:release()
end
end
end

View File

@@ -0,0 +1,171 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--define a function to forward a message to an extension
function forward_to_extension(voicemail_id, uuid)
--flush dtmf digits from the input buffer
session:flushDigits();
--save the voicemail message
message_saved(voicemail_id, uuid);
--request the forward_voicemail_id
if (session:ready()) then
dtmf_digits = '';
forward_voicemail_id = macro(session, "forward_enter_extension", 20, 5000, '');
if (session:ready()) then
if (string.len(forward_voicemail_id) == 0) then
dtmf_digits = '';
forward_voicemail_id = macro(session, "forward_enter_extension", 20, 5000, '');
end
end
end
if (session:ready()) then
if (string.len(forward_voicemail_id) == 0) then
dtmf_digits = '';
forward_voicemail_id = macro(session, "forward_enter_extension", 20, 5000, '');
end
end
--get voicemail message details
if (session:ready()) then
local sql = [[SELECT * FROM v_voicemail_messages
WHERE domain_uuid = :domain_uuid
AND voicemail_uuid = :voicemail_uuid
AND voicemail_message_uuid = :uuid]]
local params = {domain_uuid = domain_uuid, voicemail_uuid = voicemail_uuid, uuid = uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--get the values from the database
created_epoch = row["created_epoch"];
caller_id_name = row["caller_id_name"];
caller_id_number = row["caller_id_number"];
message_length = row["message_length"];
message_status = row["message_status"];
message_priority = row["message_priority"];
message_base64 = row["message_base64"];
end);
end
--get the voicemail settings
local sql = [[SELECT * FROM v_voicemails
WHERE domain_uuid = :domain_uuid
AND voicemail_id = :voicemail_id
AND voicemail_enabled = 'true' ]];
local params = {domain_uuid = domain_uuid, voicemail_id = forward_voicemail_id};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
forward_voicemail_uuid = string.lower(row["voicemail_uuid"]);
forward_voicemail_mail_to = row["voicemail_mail_to"];
forward_voicemail_attach_file = row["voicemail_attach_file"];
forward_voicemail_local_after_email = row["voicemail_local_after_email"];
end);
--get a new uuid
api = freeswitch.API();
voicemail_message_uuid = trim(api:execute("create_uuid", ""));
--save the message to the voicemail messages
local sql = {}
table.insert(sql, "INSERT INTO v_voicemail_messages ");
table.insert(sql, "(");
table.insert(sql, "voicemail_message_uuid, ");
table.insert(sql, "domain_uuid, ");
table.insert(sql, "voicemail_uuid, ");
if (storage_type == "base64") then
table.insert(sql, "message_base64, ");
end
table.insert(sql, "created_epoch, ");
table.insert(sql, "caller_id_name, ");
table.insert(sql, "caller_id_number, ");
table.insert(sql, "message_length ");
--table.insert(sql, "message_status, ");
--table.insert(sql, "message_priority, ");
table.insert(sql, ") ");
table.insert(sql, "VALUES ");
table.insert(sql, "( ");
table.insert(sql, ":voicemail_message_uuid, ");
table.insert(sql, ":domain_uuid, ");
table.insert(sql, ":forward_voicemail_uuid, ");
if (storage_type == "base64") then
table.insert(sql, ":message_base64, ");
end
table.insert(sql, ":created_epoch, ");
table.insert(sql, ":caller_id_name, ");
table.insert(sql, ":caller_id_number, ");
table.insert(sql, ":message_length ");
--table.insert(sql, ":message_status, ");
--table.insert(sql, ":message_priority ");
table.insert(sql, ") ");
sql = table.concat(sql, "\n");
local params = {
voicemail_message_uuid = voicemail_message_uuid;
domain_uuid = domain_uuid;
forward_voicemail_uuid = forward_voicemail_uuid;
message_base64 = message_base64;
created_epoch = created_epoch;
caller_id_name = caller_id_name;
caller_id_number = caller_id_number;
message_length = message_length;
-- message_status = message_status;
-- message_priority = message_priority;
};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
if (storage_type == "base64") then
local dbh = Database.new('system', 'base64')
dbh:query(sql, params);
dbh:release();
else
dbh:query(sql, params);
end
--offer to add an intro to the forwarded message
forward_add_intro(forward_voicemail_id, voicemail_message_uuid);
--get new and saved message counts
local new_messages, saved_messages = message_count_by_id(
forward_voicemail_id, domain_uuid
)
--send the message waiting event
mwi_notify(forward_voicemail_id.."@"..domain_name, new_messages, saved_messages)
--if local after email is true then copy the recording file
if (storage_type ~= "base64") then
mkdir(voicemail_dir.."/"..forward_voicemail_id);
copy(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext, voicemail_dir.."/"..forward_voicemail_id.."/msg_"..voicemail_message_uuid.."."..vm_message_ext);
end
--send the email with the voicemail recording attached
send_email(forward_voicemail_id, voicemail_message_uuid);
end

View File

@@ -0,0 +1,43 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--get the voicemail id
function get_voicemail_id()
session:flushDigits();
dtmf_digits = '';
voicemail_id = macro(session, "voicemail_id", 20, 5000, '');
if (string.len(voicemail_id) == 0) then
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
voicemail_id = get_voicemail_id();
else
macro(session, "goodbye", 1, 1000, '');
session:hangup();
end
end
end
return voicemail_id;
end

View File

@@ -0,0 +1,243 @@
-- Part of FusionPBX
-- Copyright (C) 2013-2016 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--define function to listen to the recording
function listen_to_recording (message_number, uuid, created_epoch, caller_id_name, caller_id_number)
--set default values
dtmf_digits = '';
max_digits = 1;
--flush dtmf digits from the input buffer
session:flushDigits();
--set the callback function
if (session:ready()) then
session:setVariable("playback_terminators", "#");
session:setInputCallback("on_dtmf", "");
end
--set the display
if (session:ready()) then
reply = api:executeString("uuid_display "..session:get_uuid().." "..caller_id_number);
end
--say the message number
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "message_number", 1, 100, '');
end
end
--say the number
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
session:say(message_number, default_language, "number", "pronounced");
end
end
--say the caller id number
if (session:ready() and caller_id_number ~= nil) then
if (vm_say_caller_id_number ~= nil) then
if (vm_say_caller_id_number == "true") then
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/voicemail/vm-message_from.wav");
session:say(caller_id_number, default_language, "name_spelled", "iterated");
end
end
end
--say the message date
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
if (vm_say_date_time ~= nil) then
if (vm_say_date_time == "true") then
if (current_time_zone ~= nil) then
session:execute("set", "timezone="..current_time_zone.."");
end
session:say(created_epoch, default_language, "current_date_time", "pronounced");
end
end
end
end
--get the recordings from the database
if (storage_type == "base64") then
local dbh = Database.new('system', 'base64/read')
local sql = [[SELECT * FROM v_voicemail_messages
WHERE domain_uuid = :domain_uuid
AND voicemail_message_uuid = :uuid]];
local params = {domain_uuid = domain_uuid, uuid = uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--set the voicemail message path
mkdir(voicemail_dir.."/"..voicemail_id);
message_intro_location = voicemail_dir.."/"..voicemail_id.."/intro_"..uuid.."."..vm_message_ext;
message_location = voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext;
--save the recording to the file system
if (string.len(row["message_intro_base64"]) > 32) then
local file = io.open(message_intro_location, "w");
file:write(base64.decode(row["message_intro_base64"]));
file:close();
end
if (string.len(row["message_base64"]) > 32) then
--include the file io
local file = require "resources.functions.file"
--write decoded string to file
assert(file.write_base64(message_location, row["message_base64"]));
end
end);
dbh:release()
elseif (storage_type == "http_cache") then
message_location = storage_path.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext;
end
--play the message intro
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
if (file_exists(voicemail_dir.."/"..voicemail_id.."/intro_"..uuid.."."..vm_message_ext)) then
stream_seek = true;
if (storage_type == "http_cache") then
message_intro_location = storage_path.."/"..voicemail_id.."/intro_"..uuid.."."..vm_message_ext;
session:streamFile(storage_path.."/"..voicemail_id.."/intro_"..uuid.."."..vm_message_ext);
else
if (vm_message_ext == "mp3") then
if (api:executeString("module_exists mod_vlc") == "true") then
session:streamFile("vlc://"..voicemail_dir.."/"..voicemail_id.."/intro_"..uuid.."."..vm_message_ext);
else
session:streamFile(voicemail_dir.."/"..voicemail_id.."/intro_"..uuid.."."..vm_message_ext);
end
else
session:streamFile(voicemail_dir.."/"..voicemail_id.."/intro_"..uuid.."."..vm_message_ext);
end
end
stream_seek = false;
--session:streamFile("silence_stream://1000");
end
end
end
--play the message
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
if (file_exists(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext)) then
stream_seek = true;
if (storage_type == "http_cache") then
message_location = storage_path.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext;
session:streamFile(storage_path.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
else
if (vm_message_ext == "mp3") then
if (api:executeString("module_exists mod_vlc") == "true") then
session:streamFile("vlc://"..voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
else
session:streamFile(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
end
else
session:streamFile(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
end
end
stream_seek = false;
session:streamFile("silence_stream://1000");
end
end
end
--remove the voicemail message
if (storage_type == "base64") then
os.remove(voicemail_dir.."/"..voicemail_id.."/intro_"..uuid.."."..vm_message_ext);
os.remove(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
end
--to listen to the recording press 1
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "listen_to_recording", 1, 100, '');
end
end
--to save the recording press 2
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "save_recording", 1, 100, '');
end
end
--to return the call now press 5
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "return_call", 1, 100, '');
end
end
--to delete the recording press 7
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "delete_recording", 1, 100, '');
end
end
--to forward this message press 8
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "to_forward_message", 1, 100, '');
end
end
--to forward this recording to your email press 9
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "forward_to_email", 1, 3000, '');
end
end
--wait for more digits
--if (session:ready()) then
-- if (string.len(dtmf_digits) == 0) then
-- dtmf_digits = session:getDigits(max_digits, "#", 1, 3000);
-- end
--end
--process the dtmf
if (session:ready()) then
if (dtmf_digits == "1") then
listen_to_recording(message_number, uuid, created_epoch, caller_id_name, caller_id_number);
elseif (dtmf_digits == "2") then
message_saved(voicemail_id, uuid);
macro(session, "message_saved", 1, 100, '');
elseif (dtmf_digits == "5") then
message_saved(voicemail_id, uuid);
return_call(caller_id_number);
elseif (dtmf_digits == "7") then
delete_recording(voicemail_id, uuid);
message_waiting(voicemail_id, domain_uuid);
--fix for extensions that start with 0 (Ex: 0712)
if (voicemail_id_copy ~= voicemail_id and voicemail_id_copy ~= nil) then
message_waiting(voicemail_id_copy, domain_uuid);
end
elseif (dtmf_digits == "8") then
forward_to_extension(voicemail_id, uuid);
dtmf_digits = '';
macro(session, "message_saved", 1, 100, '');
elseif (dtmf_digits == "9") then
send_email(voicemail_id, uuid);
dtmf_digits = '';
macro(session, "emailed", 1, 100, '');
elseif (dtmf_digits == "*") then
timeouts = 0;
main_menu();
elseif (dtmf_digits == "0") then
message_saved(voicemail_id, uuid);
session:transfer("0", "XML", context);
else
message_saved(voicemail_id, uuid);
macro(session, "message_saved", 1, 100, '');
end
end
end

View File

@@ -0,0 +1,364 @@
-- Part of FusionPBX
-- Copyright (C) 2013 - 2016 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--define the macro function
function macro(session, name, max_digits, max_timeout, param)
if (session:ready()) then
--create an empty table
actions = {}
--Please enter your id followed by
if (name == "voicemail_id") then
table.insert(actions, {app="streamFile",data="phrase:voicemail_enter_id:#"});
end
--Please enter your id followed by
if (name == "voicemail_password") then
table.insert(actions, {app="streamFile",data="phrase:voicemail_enter_pass:#"});
end
--the person at extension 101 is not available record your message at the tone press any key or stop talking to end the recording
if (name == "person_not_available_record_message") then
table.insert(actions, {app="streamFile",data="voicemail/vm-person.wav"});
--pronounce the voicemail_id
if (voicemail_alternate_greet_id and string.len(voicemail_alternate_greet_id) > 0) then
table.insert(actions, {app="say.number.iterated",data=voicemail_alternate_greet_id});
elseif (voicemail_greet_id and string.len(voicemail_greet_id) > 0) then
table.insert(actions, {app="say.number.iterated",data=voicemail_greet_id});
else
table.insert(actions, {app="say.number.iterated",data=voicemail_id});
end
table.insert(actions, {app="streamFile",data="voicemail/vm-not_available.wav"});
end
--record your message at the tone press any key or stop talking to end the recording
if (name == "record_message") then
table.insert(actions, {app="streamFile",data="voicemail/vm-record_message.wav"});
end
--beep
if (name == "record_beep") then
table.insert(actions, {app="tone_stream",data="L=1;%(1000, 0, 640)"});
end
--to listen to the recording press 1
if (name == "to_listen_to_recording") then
table.insert(actions, {app="streamFile",data="voicemail/vm-listen_to_recording.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/1.wav"});
end
--to save the recording press 2
if (name == "to_save_recording") then
table.insert(actions, {app="streamFile",data="voicemail/vm-save_recording.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/2.wav"});
end
--to rerecord press 3
if (name == "to_rerecord") then
table.insert(actions, {app="streamFile",data="voicemail/vm-rerecord.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/3.wav"});
end
--You have zero new messages
if (name == "new_messages") then
table.insert(actions, {app="streamFile",data="phrase:voicemail_message_count:" .. param .. ":new"})
end
--You have zero saved messages
if (name == "saved_messages") then
table.insert(actions, {app="streamFile",data="phrase:voicemail_message_count:" .. param .. ":saved"})
end
--To listen to new messages press 1
if (name == "listen_to_new_messages") then
table.insert(actions, {app="streamFile",data="voicemail/vm-listen_new.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/1.wav"});
end
--To listen to saved messages press 2
if (name == "listen_to_saved_messages") then
table.insert(actions, {app="streamFile",data="voicemail/vm-listen_saved.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/2.wav"});
end
--For advanced options press 5
if (name == "advanced") then
table.insert(actions, {app="streamFile",data="voicemail/vm-advanced.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/5.wav"});
end
--Advanced Options Menu
--To record a greeting press 1
if (name == "to_record_greeting") then
table.insert(actions, {app="streamFile",data="voicemail/vm-to_record_greeting.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/1.wav"});
end
--Choose a greeting between 1 and 9
if (name == "choose_greeting_choose") then
table.insert(actions, {app="streamFile",data="voicemail/vm-choose_greeting_choose.wav"});
end
--Greeting invalid value
if (name == "choose_greeting_fail") then
table.insert(actions, {app="streamFile",data="voicemail/vm-choose_greeting_fail.wav"});
end
--Record your greeting at the tone press any key or stop talking to end the recording
if (name == "record_greeting") then
table.insert(actions, {app="streamFile",data="voicemail/vm-record_greeting.wav"});
table.insert(actions, {app="tone_stream",data="L=1;%(1000, 0, 640)"});
end
--To choose greeting press 2
if (name == "choose_greeting") then
table.insert(actions, {app="streamFile",data="voicemail/vm-choose_greeting.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/2.wav"});
end
--Greeting 1 selected
if (name == "greeting_selected") then
table.insert(actions, {app="streamFile",data="voicemail/vm-greeting.wav"});
table.insert(actions, {app="streamFile",data="digits/"..param..".wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-selected.wav"});
end
--To record your name 3
if (name == "to_record_name") then
table.insert(actions, {app="streamFile",data="voicemail/vm-record_name2.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/3.wav"});
end
--At the tone please record your name press any key or stop talking to end the recording
if (name == "record_name") then
table.insert(actions, {app="streamFile",data="voicemail/vm-record_name1.wav"});
table.insert(actions, {app="tone_stream",data="L=1;%(2000, 0, 640)"});
end
--To change your password press 6
if (name == "change_password") then
table.insert(actions, {app="streamFile",data="voicemail/vm-change_password.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/6.wav"});
end
--For the main menu press 0
if (name == "main_menu") then
table.insert(actions, {app="streamFile",data="voicemail/vm-main_menu.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/0.wav"});
end
--To exit press *
if (name == "to_exit_press") then
table.insert(actions, {app="streamFile",data="voicemail/vm-to_exit.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/star.wav"});
end
--Additional Macros
--Please enter your new password then press the # key #
if (name == "password_new") then
table.insert(actions, {app="streamFile",data="voicemail/vm-enter_new_pin.wav"});
end
--Has been changed to
if (name == "password_changed") then
table.insert(actions, {app="streamFile",data="voicemail/vm-has_been_changed_to.wav"});
table.insert(actions, {app="say.number.iterated",data=param});
end
--Login Incorrect
--if (name == "password_not_valid") then
-- table.insert(actions, {app="streamFile",data="voicemail/vm-password_not_valid.wav"});
--end
--Login Incorrect
if (name == "password_not_valid") then
table.insert(actions, {app="streamFile",data="voicemail/vm-fail_auth.wav"});
end
--Too many failed attempts
if (name == "too_many_failed_attempts") then
table.insert(actions, {app="streamFile",data="voicemail/vm-abort.wav"});
end
--Message number
if (name == "message_number") then
table.insert(actions, {app="streamFile",data="voicemail/vm-message_number.wav"});
end
--To listen to the recording press 1
if (name == "listen_to_recording") then
table.insert(actions, {app="streamFile",data="voicemail/vm-listen_to_recording.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/1.wav"});
end
--To save the recording press 2
if (name == "save_recording") then
table.insert(actions, {app="streamFile",data="voicemail/vm-save_recording.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/2.wav"});
end
--To delete the recording press 7
if (name == "delete_recording") then
table.insert(actions, {app="streamFile",data="voicemail/vm-delete_recording.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/7.wav"});
end
--Message deleted
if (name == "message_deleted") then
table.insert(actions, {app="streamFile",data="voicemail/vm-message.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-deleted.wav"});
end
--To return the call now press 5
if (name == "return_call") then
table.insert(actions, {app="streamFile",data="voicemail/vm-return_call.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/5.wav"});
end
--To add an introduction to this message press 1
if (name == "forward_add_intro") then
table.insert(actions, {app="streamFile",data="voicemail/vm-forward_add_intro.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/1.wav"});
end
--To forward this message press 8
if (name == "to_forward_message") then
table.insert(actions, {app="streamFile",data="voicemail/vm-to_forward.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/8.wav"});
end
--Please enter the extension to forward this message to followed by #
if (name == "forward_enter_extension") then
table.insert(actions, {app="streamFile",data="voicemail/vm-forward_enter_ext.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-followed_by_pound.wav"});
end
--To forward this recording to your email press 9
if (name == "forward_to_email") then
table.insert(actions, {app="streamFile",data="voicemail/vm-forward_to_email.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/9.wav"});
end
--Emailed
if (name == "emailed") then
table.insert(actions, {app="streamFile",data="voicemail/vm-emailed.wav"});
end
--Please enter the extension to send this message to followed by #
--if (name == "send_message_to_extension") then
-- table.insert(actions, {app="streamFile",data="voicemail/vm-zzz.wav"});
--end
--Message saved
if (name == "message_saved") then
table.insert(actions, {app="streamFile",data="voicemail/vm-message.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-saved.wav"});
end
--Your recording is below the minimal acceptable length, please try again.
if (name == "too_small") then
table.insert(actions, {app="streamFile",data="voicemail/vm-too-small.wav"});
end
--Goodbye
if (name == "goodbye") then
table.insert(actions, {app="streamFile",data="voicemail/vm-goodbye.wav"});
end
--Password is not secure
if (name == "password_not_secure") then
table.insert(actions, {app="streamFile",data="voicemail/vm-password_is_not_secure.wav"});
end
--Password is below minimum length
if (name == "password_below_minimum") then
table.insert(actions, {app="streamFile",data="voicemail/vm-pin_below_minimum_length.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-minimum_pin_length_is.wav"});
table.insert(actions, {app="streamFile",data="digits/"..param..".wav"});
end
--Tutorial
--Tutorial intro
if (name == "tutorial_intro") then
table.insert(actions, {app="streamFile",data="voicemail/vm-tutorial_yes_no.wav"});
end
--Tutorial to record your name 1
if (name == "tutorial_to_record_name") then
table.insert(actions, {app="streamFile",data="voicemail/vm-tutorial_record_name.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-record_name2.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/1.wav"});
end
--Tutorial to change your password press 1
if (name == "tutorial_change_password") then
table.insert(actions, {app="streamFile",data="voicemail/vm-tutorial_change_pin.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-change_password.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/1.wav"});
end
--Tutorial to record your greeting press 1
if (name == "tutorial_record_greeting") then
table.insert(actions, {app="streamFile",data="voicemail/vm-to_record_greeting.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/1.wav"});
end
--Tutorial To skip
if (name == "tutorial_skip") then
table.insert(actions, {app="streamFile",data="ivr/ivr-to_skip.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/2.wav"});
end
--if actions table exists then process it
if (actions) then
--set default values
tries = 1;
timeout = 100;
--loop through the action and data
for key, row in pairs(actions) do
-- freeswitch.consoleLog("notice", "[voicemail] app: " .. row.app .. " data: " .. row.data .. "\n");
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
if (row.app == "streamFile") then
if string.find(row.data, ':', nil, true) then
session:streamFile(row.data);
else
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/"..row.data);
end
elseif (row.app == "playback") then
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/"..row.data);
elseif (row.app == "tone_stream") then
session:streamFile("tone_stream://"..row.data);
elseif (row.app == "silence_stream") then
session:streamFile("silence_stream://100"..row.data);
elseif (row.app == "playAndGetDigits") then
--playAndGetDigits <min> <max> <tries> <timeout> <terminators> <file> <invalid_file> <var_name> <regexp> <digit_timeout>
if (not file_exists(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/"..row.data)) then
dtmf_digits = session:playAndGetDigits(min_digits, max_digits, tries, timeout, "#", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/"..row.data, "", "\\d+", max_timeout);
else
dtmf_digits = session:playAndGetDigits(min_digits, max_digits, tries, timeout, "#", row.data, "", "\\d+", max_timeout);
end
elseif (row.app == "say.number.pronounced") then
session:say(row.data, default_language, "number", "pronounced");
elseif (row.app == "say.number.iterated") then
session:say(row.data, default_language, "number", "iterated");
end
--session:streamFile("silence_stream://100");
end --if
end --session:ready
end --for
--get the remaining digits
if (session:ready()) then
if (string.len(dtmf_digits) < max_digits) then
dtmf_digits = dtmf_digits .. session:getDigits(max_digits, "#", max_timeout);
end
end
--return dtmf the digits
return dtmf_digits;
else
--no dtmf digits to return
return '';
end
end
end

View File

@@ -0,0 +1,124 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--define function main menu
function main_menu ()
if (voicemail_uuid) then
--clear the value
dtmf_digits = '';
--flush dtmf digits from the input buffer
if (session ~= nil) then
session:flushDigits();
end
--answer the session
if (session ~= nil) then
session:answer();
session:execute("sleep", "1000");
end
--new voicemail count
if (session:ready()) then
local sql = [[SELECT count(*) as new_messages FROM v_voicemail_messages
WHERE domain_uuid = :domain_uuid
AND voicemail_uuid = :voicemail_uuid
AND (message_status is null or message_status = '') ]];
local params = {domain_uuid = domain_uuid, voicemail_uuid = voicemail_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
new_messages = row["new_messages"];
end);
dtmf_digits = macro(session, "new_messages", 1, 100, new_messages);
end
--saved voicemail count
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
sql = [[SELECT count(*) as saved_messages FROM v_voicemail_messages
WHERE domain_uuid = :domain_uuid
AND voicemail_uuid = :voicemail_uuid
AND message_status = 'saved' ]];
local params = {domain_uuid = domain_uuid, voicemail_uuid = voicemail_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
saved_messages = row["saved_messages"];
end);
dtmf_digits = macro(session, "saved_messages", 1, 100, saved_messages);
end
end
--to listen to new message
if (session:ready() and new_messages ~= '0') then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "listen_to_new_messages", 1, 100, '');
end
end
--to listen to saved message
if (session:ready() and saved_messages ~= '0') then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "listen_to_saved_messages", 1, 100, '');
end
end
--for advanced options
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "advanced", 1, 100, '');
end
end
--to exit press #
--if (session:ready()) then
-- if (string.len(dtmf_digits) == 0) then
-- dtmf_digits = macro(session, "to_exit_press", 1, 3000, '');
-- end
--end
--process the dtmf
if (session:ready()) then
if (dtmf_digits == "1") then
menu_messages("new");
elseif (dtmf_digits == "2") then
menu_messages("saved");
elseif (dtmf_digits == "5") then
timeouts = 0;
advanced();
elseif (dtmf_digits == "0") then
main_menu();
elseif (dtmf_digits == "*") then
dtmf_digits = '';
macro(session, "goodbye", 1, 100, '');
session:hangup();
else
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
main_menu();
else
macro(session, "goodbye", 1, 1000, '');
session:hangup();
end
end
end
end
end
end

View File

@@ -0,0 +1,115 @@
-- Part of FusionPBX
-- Copyright (C) 2013-2017 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--define function for messages menu
function menu_messages (message_status)
--set default values
max_timeout = 2000;
min_digits = 1;
max_digits = 1;
tries = 1;
timeout = 2000;
--clear the dtmf
dtmf_digits = '';
--flush dtmf digits from the input buffer
--session:flushDigits();
--set the message number
message_number = 0;
--message_status new,saved
if (session:ready()) then
if (voicemail_id ~= nil) then
--get the voicemail_id
--fix for extensions that start with 0 (Ex: 0712)
sql = [[SELECT voicemail_id FROM v_voicemails WHERE voicemail_uuid = :voicemail_uuid]];
local params = {voicemail_uuid = voicemail_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(result)
voicemail_id_copy = result["voicemail_id"];
end);
local sql = [[SELECT voicemail_message_uuid, created_epoch, caller_id_name, caller_id_number
FROM v_voicemail_messages
WHERE domain_uuid = :domain_uuid
AND voicemail_uuid = :voicemail_uuid ]]
if (message_status == "new") then
sql = sql .. [[AND (message_status is null or message_status = '') ]];
elseif (message_status == "saved") then
sql = sql .. [[AND message_status = 'saved' ]];
end
sql = sql .. [[ORDER BY created_epoch ]]..message_order;
local params = {domain_uuid = domain_uuid, voicemail_uuid = voicemail_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--get the values from the database
--row["voicemail_message_uuid"];
--row["created_epoch"];
--row["caller_id_name"];
--row["caller_id_number"];
--row["message_length"];
--row["message_status"];
--row["message_priority"];
--increment the message count
message_number = message_number + 1;
--listen to the message
if (session:ready()) then
if (debug["info"]) then
freeswitch.consoleLog("notice", message_number.." "..string.lower(row["voicemail_message_uuid"]).." "..row["created_epoch"]);
end
listen_to_recording(message_number, string.lower(row["voicemail_message_uuid"]), row["created_epoch"], row["caller_id_name"], row["caller_id_number"]);
end
end);
end
end
--voicemail count if zero new messages set the mwi to no
if session:ready() and voicemail_id and voicemail_uuid and #voicemail_uuid > 0 then
--get new and saved message counts
local new_messages, saved_messages = message_count_by_uuid(
voicemail_uuid, domain_uuid
)
--send the message waiting event
mwi_notify(voicemail_id.."@"..domain_name, new_messages, saved_messages)
--fix for extensions that start with 0 (Ex: 0712)
if (voicemail_id_copy ~= voicemail_id and voicemail_id_copy ~= nil) then
message_waiting(voicemail_id_copy, domain_uuid);
end
end
--set the display
if (session:ready()) then
reply = api:executeString("uuid_display "..session:get_uuid().." "..destination_number);
end
--send back to the main menu
if (session:ready()) then
timeouts = 0;
main_menu();
end
end

View File

@@ -0,0 +1,74 @@
local log = require "resources.functions.log"["voicemail-count"]
-- Tested SQL on SQLite 3, PgSQL 9.5, MySQL 5.5 and MariaDB 10
local message_count_by_uuid_sql = [[SELECT
( SELECT count(*)
FROM v_voicemail_messages
WHERE voicemail_uuid = :voicemail_uuid
AND (message_status is null or message_status = '')
) as new_messages,
( SELECT count(*)
FROM v_voicemail_messages
WHERE voicemail_uuid = :voicemail_uuid
AND message_status = 'saved'
) as saved_messages
]]
function message_count_by_uuid(voicemail_uuid)
local new_messages, saved_messages = "0", "0"
local params = {voicemail_uuid = voicemail_uuid};
if debug["sql"] then
log.noticef("SQL: %s; params: %s", message_count_by_uuid_sql, json.encode(params))
end
dbh:query(message_count_by_uuid_sql, params, function(row)
new_messages, saved_messages = row.new_messages, row.saved_messages
end)
if debug["info"] then
log.noticef("mailbox uuid: %s messages: %s/%s", voicemail_uuid, new_messages, saved_messages)
end
return new_messages, saved_messages
end
local message_count_by_id_sql = [[SELECT
( SELECT count(*)
FROM v_voicemail_messages as m inner join v_voicemails as v
on v.voicemail_uuid = m.voicemail_uuid
WHERE v.voicemail_id = :voicemail_id AND v.domain_uuid = :domain_uuid
AND (m.message_status is null or m.message_status = '')
) as new_messages,
( SELECT count(*)
FROM v_voicemail_messages as m inner join v_voicemails as v
on v.voicemail_uuid = m.voicemail_uuid
WHERE v.voicemail_id = :voicemail_id AND v.domain_uuid = :domain_uuid
AND m.message_status = 'saved'
) as saved_messages
]]
function message_count_by_id(voicemail_id, domain_uuid)
local new_messages, saved_messages = "0", "0"
local params = {voicemail_id = voicemail_id, domain_uuid = domain_uuid};
if debug["sql"] then
log.noticef("SQL: %s; params: %s", message_count_by_id_sql, json.encode(params))
end
dbh:query(message_count_by_id_sql, params, function(row)
new_messages, saved_messages = row.new_messages, row.saved_messages
end)
if debug["info"] then
log.noticef("mailbox: %s messages: %s/%s", voicemail_id, new_messages, saved_messages)
end
return new_messages, saved_messages
end

View File

@@ -0,0 +1,58 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--save the message
function message_saved(voicemail_id, uuid)
--clear the dtmf
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--get the voicemail_uuid
local sql = [[SELECT * FROM v_voicemails
WHERE domain_uuid = :domain_uuid
AND voicemail_id = :voicemail_id]];
local params = {domain_uuid = domain_uuid, voicemail_id = voicemail_id};
dbh:query(sql, params, function(row)
db_voicemail_uuid = row["voicemail_uuid"];
end);
--delete from the database
sql = [[UPDATE v_voicemail_messages SET message_status = 'saved'
WHERE domain_uuid = :domain_uuid
AND voicemail_uuid = :voicemail_uuid
AND voicemail_message_uuid = :uuid]];
params = {domain_uuid = domain_uuid, voicemail_uuid = db_voicemail_uuid, uuid = uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--log to console
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail][saved] id: " .. voicemail_id .. " message: "..uuid.."\n");
end
--check the message waiting status
message_waiting(voicemail_id, domain_uuid);
--clear the variable
db_voicemail_uuid = '';
end

View File

@@ -0,0 +1,70 @@
-- Part of FusionPBX
-- Copyright (C) 2013-2016 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--voicemail count if zero new messages set the mwi to no
function message_waiting(voicemail_id, domain_uuid)
--initialize the array and add the voicemail_id
local accounts = {}
--add the current voicemail id to the accounts array
table.insert(accounts, voicemail_id);
--get the voicemail id and all related mwi accounts
local sql = [[SELECT extension, number_alias from v_extensions
WHERE domain_uuid = :domain_uuid
AND (
mwi_account = :voicemail_id
or mwi_account = :mwi_account
or number_alias = :voicemail_id
)]];
local params = {domain_uuid = domain_uuid, voicemail_id = voicemail_id,
mwi_account = voicemail_id .. "@" .. domain_name};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
table.insert(accounts, row["extension"]);
end);
--get new and saved message counts
local new_messages, saved_messages = message_count_by_id(voicemail_id, domain_uuid);
--send the message waiting event
for _,value in ipairs(accounts) do
--add the domain to voicemail id
local account = value.."@"..domain_name;
--send the message waiting notifications
mwi_notify(account, new_messages, saved_messages);
--send information to the console
if (debug["info"]) then
if new_messages == "0" then
freeswitch.consoleLog("notice", "[voicemail] mailbox: "..account.." messages: no new messages\n");
else
freeswitch.consoleLog("notice", "[voicemail] mailbox: "..account.." messages: " .. new_messages .. " new message(s)\n");
end
end
end
end

View File

@@ -0,0 +1,29 @@
--define a function to send email
function mwi_notify(account, new_messages, saved_messages)
--includes
require "resources.functions.explode"
require "resources.functions.trim"
--create the api object
api = freeswitch.API();
local sofia_contact = trim(api:executeString("sofia_contact */"..account));
array = explode("/", sofia_contact);
sip_profile = array[2];
--set the variables
new_messages = tonumber(new_messages) or 0
saved_messages = tonumber(saved_messages) or 0
--set the event and send it
local event = freeswitch.Event("message_waiting")
event:addHeader("MWI-Messages-Waiting", (new_messages == 0) and "no" or "yes")
event:addHeader("MWI-Message-Account", "sip:" .. account)
event:addHeader("MWI-Voice-Message", string.format("%d/%d (0/0)", new_messages, saved_messages))
event:addHeader("sofia-profile", sip_profile)
return event:fire()
end
--return module value
return mwi_notify

View File

@@ -0,0 +1,60 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--define on_dtmf call back function
function on_dtmf(s, type, obj, arg)
if (type == "dtmf") then
freeswitch.console_log("info", "[voicemail] dtmf digit: " .. obj['digit'] .. ", duration: " .. obj['duration'] .. "\n");
if (obj['digit'] == "#") then
return 0;
else
dtmf_digits = dtmf_digits .. obj['digit'];
if (debug["info"]) then
freeswitch.console_log("info", "[voicemail] dtmf digits: " .. dtmf_digits .. ", length: ".. string.len(dtmf_digits) .." max_digits: " .. max_digits .. "\n");
end
if (stream_seek == true) then
if (dtmf_digits == "4") then
dtmf_digits = "";
return("seek:-5000");
end
if (dtmf_digits == "5") then
dtmf_digits = "";
return("pause");
end
if (dtmf_digits == "6") then
dtmf_digits = "";
return("seek:+5000");
end
end
if (string.len(dtmf_digits) >= max_digits) then
if (debug["info"]) then
freeswitch.console_log("info", "[voicemail] max_digits reached\n");
end
return 0;
end
end
end
return 0;
end

View File

@@ -0,0 +1,109 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
local Database = require"resources.functions.database"
--play the greeting
function play_greeting()
timeout = 100;
tries = 1;
max_timeout = 200;
--voicemail prompt
if (skip_greeting == "true") then
--skip the greeting
else
if (session:ready()) then
--set the greeting based on the voicemail_greeting_number variable
if (voicemail_greeting_number ~= nil) then
if (string.len(voicemail_greeting_number) > 0) then
greeting_id = voicemail_greeting_number;
end
end
--play the greeting
dtmf_digits = '';
if (string.len(greeting_id) > 0) then
--sleep
session:execute("playback","silence_stream://200");
--get the greeting from the database
if (storage_type == "base64") then
local dbh = Database.new('system', 'base64/read')
local sql = [[SELECT * FROM v_voicemail_greetings
WHERE domain_uuid = :domain_uuid
AND voicemail_id = :voicemail_id
AND greeting_id = :greeting_id ]];
local params = {domain_uuid = domain_uuid, voicemail_id = voicemail_id,
greeting_id = greeting_id};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
local saved
dbh:query(sql, params, function(row)
--set the voicemail message path
mkdir(voicemail_dir.."/"..voicemail_id);
greeting_location = voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav"; --vm_message_ext;
--if not found, save greeting to local file system
--saved = file_exists(greeting_location)
--if not saved then
if (string.len(row["greeting_base64"]) > 32) then
--include the file io
local file = require "resources.functions.file"
--write decoded string to file
saved = file.write_base64(greeting_location, row["greeting_base64"]);
end
--end
end);
dbh:release();
if saved then
--play the greeting
dtmf_digits = session:playAndGetDigits(min_digits, max_digits, tries, timeout, "#", voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav", "", ".*", max_timeout);
--session:execute("playback",voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav");
--delete the greeting (retain local for better responsiveness)
--os.remove(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav");
end
elseif (storage_type == "http_cache") then
dtmf_digits = session:playAndGetDigits(min_digits, max_digits, tries, timeout, "#", voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav", "", ".*", max_timeout);
--session:execute("playback",storage_path.."/"..voicemail_id.."/greeting_"..greeting_id..".wav");
else
dtmf_digits = session:playAndGetDigits(min_digits, max_digits, tries, timeout, "#", voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav", "",".*", max_timeout);
--session:execute("playback",voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav");
end
else
--default greeting
session:execute("playback","silence_stream://200");
dtmf_digits = macro(session, "person_not_available_record_message", 1, 200);
end
end
end
end

View File

@@ -0,0 +1,136 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--load libraries
local Database = require "resources.functions.database"
local Settings = require "resources.functions.lazy_settings"
--define a function to record the greeting
function record_greeting(greeting_id, menu)
--setup the database connection
local db = dbh or Database.new('system')
--get the voicemail settings
local settings = Settings.new(db, domain_name, domain_uuid)
--set the maximum greeting length
local greeting_max_length = settings:get('voicemail', 'greeting_max_length', 'numeric') or 90;
local greeting_silence_threshold = settings:get('voicemail', 'greeting_silence_threshold', 'numeric') or 200;
local greeting_silence_seconds = settings:get('voicemail', 'greeting_silence_seconds', 'numeric') or 3;
--flush dtmf digits from the input buffer
session:flushDigits();
--disable appending to the recording
session:setVariable("record_append", "false");
--choose a greeting between 1 and 9
if (greeting_id == nil) then
if (session:ready()) then
dtmf_digits = '';
greeting_id = macro(session, "choose_greeting_choose", 1, 5000, '');
freeswitch.consoleLog("notice", "[voicemail] greeting_id: " .. greeting_id .. "\n");
end
end
--validate the greeting_id
if (greeting_id == "1"
or greeting_id == "2"
or greeting_id == "3"
or greeting_id == "4"
or greeting_id == "5"
or greeting_id == "6"
or greeting_id == "7"
or greeting_id == "8"
or greeting_id == "9") then
--record your greeting at the tone press any key or stop talking to end the recording
if (session:ready()) then
dtmf_digits = '';
macro(session, "record_greeting", 1, 100, '');
end
--store the voicemail greeting
if (storage_type == "http_cache") then
freeswitch.consoleLog("notice", "[voicemail] ".. storage_type .. " ".. storage_path .."\n");
storage_path = storage_path:gsub("${domain_name}", domain_name);
session:execute("record", storage_path .."/"..recording_name);
else
--prepare to record the greeting
if (session:ready()) then
silence_seconds = 5;
mkdir(voicemail_dir.."/"..voicemail_id);
-- syntax is session:recordFile(file_name, max_len_secs, silence_threshold, silence_seconds)
result = session:recordFile(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".tmp.wav", greeting_max_length, greeting_silence_threshold, greeting_silence_seconds);
--session:execute("record", voicemail_dir.."/"..uuid.." 180 200");
end
end
--play the greeting
--if (session:ready()) then
-- if (file_exists(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav")) then
-- session:streamFile(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav");
-- end
--end
--option to play, save, and re-record the greeting
if (session:ready()) then
timeouts = 0;
record_menu("greeting", voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".tmp.wav", greeting_id, menu);
end
else
--invalid greeting_id
if (session:ready()) then
dtmf_digits = '';
macro(session, "choose_greeting_fail", 1, 100, '');
end
--send back to choose the greeting
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
record_greeting(nil, menu);
else
timeouts = 0;
if (menu == "tutorial") then
tutorial("finish")
end
if (menu == "advanced") then
advanced();
else
advanced();
end
end
end
end
--clean up any tmp greeting files
for gid = 1, 9, 1 do
if (file_exists(voicemail_dir.."/"..voicemail_id.."/greeting_"..gid..".tmp.wav")) then
os.remove(voicemail_dir.."/"..voicemail_id.."/greeting_"..gid..".tmp.wav");
end
end
end

View File

@@ -0,0 +1,247 @@
-- Part of FusionPBX
-- Copyright (C) 2013-2015 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--record message menu
function record_menu(type, tmp_file, greeting_id, menu)
if (session:ready()) then
--clear the dtmf digits variable
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--to listen to the recording press 1
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "to_listen_to_recording", 1, 100, '');
end
end
--to save the recording press 2
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "to_save_recording", 1, 100, '');
end
end
--to re-record press 3
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "to_rerecord", 1, 3000, '');
end
end
--process the dtmf
if (session:ready()) then
if (dtmf_digits == "1") then
--listen to the recording
session:streamFile(tmp_file);
--session:streamFile(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
--record menu (1=listen, 2=save, 3=re-record)
record_menu(type, tmp_file, greeting_id, menu);
elseif (dtmf_digits == "2") then
--save the message
dtmf_digits = '';
macro(session, "message_saved", 1, 100, '');
if (type == "message") then
--goodbye
macro(session, "goodbye", 1, 100, '');
--hangup the call
session:hangup();
end
if (type == "greeting") then
--remove old greeting file, and rename tmp file
local real_file = string.gsub(tmp_file, ".tmp", "");
if (file_exists(real_file)) then
os.remove(real_file);
end
if (file_exists(tmp_file)) then
os.rename(tmp_file, real_file);
end
if (storage_type == "base64") then
--delete the greeting (retain local for better responsiveness)
--os.remove(real_file);
end
--if base64, encode file
if (storage_type == "base64") then
--include the file io
local file = require "resources.functions.file"
--read file content as base64 string
greeting_base64 = assert(file.read_base64(real_file));
end
--delete the previous recording
local sql = "delete from v_voicemail_greetings ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and voicemail_id = :voicemail_id ";
sql = sql .. "and greeting_id = :greeting_id ";
local params = {domain_uuid = domain_uuid,
voicemail_id = voicemail_id, greeting_id = greeting_id};
--freeswitch.consoleLog("notice", "[SQL] DELETING: " .. greeting_id .. "\n");
dbh:query(sql, params);
--get a new uuid
voicemail_greeting_uuid = api:execute("create_uuid");
--save the message to the voicemail messages
local array = {}
table.insert(array, "INSERT INTO v_voicemail_greetings ");
table.insert(array, "(");
table.insert(array, "voicemail_greeting_uuid, ");
table.insert(array, "domain_uuid, ");
table.insert(array, "voicemail_id, ");
table.insert(array, "greeting_id, ");
if (storage_type == "base64") then
table.insert(array, "greeting_base64, ");
end
table.insert(array, "greeting_name, ");
table.insert(array, "greeting_filename ");
table.insert(array, ") ");
table.insert(array, "VALUES ");
table.insert(array, "( ");
table.insert(array, ":greeting_uuid, ");
table.insert(array, ":domain_uuid, ");
table.insert(array, ":voicemail_id, ");
table.insert(array, ":greeting_id, ");
if (storage_type == "base64") then
table.insert(array, ":greeting_base64, ");
end
table.insert(array, ":greeting_name, ");
table.insert(array, ":greeting_filename ");
table.insert(array, ") ");
sql = table.concat(array, "\n");
params = {
greeting_uuid = voicemail_greeting_uuid;
domain_uuid = domain_uuid;
voicemail_id = voicemail_id;
greeting_id = greeting_id;
greeting_base64 = greeting_base64;
greeting_name = "Greeting "..greeting_id;
greeting_filename = "greeting_"..greeting_id..".wav"
};
--freeswitch.consoleLog("notice", "[SQL] INSERTING: " .. greeting_id .. "\n");
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
if (storage_type == "base64") then
local dbh = Database.new('system', 'base64');
dbh:query(sql, params);
dbh:release();
else
dbh:query(sql, params);
end
--use the new greeting
sql = {}
table.insert(sql, "update v_voicemails ");
table.insert(sql, "set greeting_id = :greeting_id ");
table.insert(sql, "where domain_uuid = :domain_uuid ");
table.insert(sql, "and voicemail_id = :voicemail_id ");
sql = table.concat(sql, "\n");
params = {domain_uuid = domain_uuid, greeting_id = greeting_id,
voicemail_id = voicemail_id};
dbh:query(sql, params);
if (menu == "advanced") then
advanced();
end
if (menu == "tutorial") then
tutorial("finish")
end
end
if (type == "name") then
if (menu == "advanced") then
advanced();
end
if (menu == "tutorial") then
tutorial("change_password")
end
end
elseif (dtmf_digits == "3") then
--re-record the message
timeouts = 0;
dtmf_digits = '';
if (type == "message") then
record_message();
end
if (type == "greeting") then
--remove temporary greeting file, if any
if (file_exists(tmp_file)) then
os.remove(tmp_file);
end
record_greeting(greeting_id, menu);
end
if (type == "name") then
record_name(menu);
end
elseif (dtmf_digits == "*") then
if (type == "greeting") then
--remove temporary greeting file, if any
if (file_exists(tmp_file)) then
os.remove(tmp_file);
end
end
--hangup
if (session:ready()) then
dtmf_digits = '';
macro(session, "goodbye", 1, 100, '');
session:hangup();
end
else
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
record_menu(type, tmp_file, greeting_id, menu);
else
if (type == "message") then
dtmf_digits = '';
macro(session, "message_saved", 1, 100, '');
macro(session, "goodbye", 1, 1000, '');
session:hangup();
end
if (type == "greeting") then
--remove temporary greeting file, if any
if (file_exists(tmp_file)) then
os.remove(tmp_file);
end
if (menu == "advanced") then
advanced();
end
if (menu == "tutorial") then
tutorial("finish")
end
end
if (type == "name") then
if (menu == "advanced") then
advanced();
end
if (menu == "tutorial") then
tutorial("change_password")
end
end
end
end
end
end
end
end

View File

@@ -0,0 +1,553 @@
-- Part of FusionPBX
-- Copyright (C) 2013-2019 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--load libraries
local Database = require "resources.functions.database"
local Settings = require "resources.functions.lazy_settings"
local JSON = require "resources.functions.lunajson"
--define uuid function
local random = math.random;
local function gen_uuid()
local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
return string.gsub(template, '[xy]', function (c)
local v = (c == 'x') and random(0, 0xf) or random(8, 0xb);
return string.format('%x', v);
end)
end
--define escape function (prevents lua injection attacks)
local function esc(x)
return (x:gsub('%%', '%%%%')
:gsub('^%^', '%%^')
:gsub('%$$', '%%$')
:gsub('%(', '%%(')
:gsub('%)', '%%)')
:gsub('%.', '%%.')
:gsub('%[', '%%[')
:gsub('%]', '%%]')
:gsub('%*', '%%*')
:gsub('%+', '%%+')
:gsub('%-', '%%-')
:gsub('%?', '%%?'))
end
local function transcribe(file_path,settings,start_epoch)
--transcription variables
if (os.time() - start_epoch > 2) then
local transcribe_provider = settings:get('voicemail', 'transcribe_provider', 'text') or '';
transcribe_language = settings:get('voicemail', 'transcribe_language', 'text') or 'en-US';
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] transcribe_provider: " .. transcribe_provider .. "\n");
freeswitch.consoleLog("notice", "[voicemail] transcribe_language: " .. transcribe_language .. "\n");
end
if (transcribe_provider == "microsoft") then
local api_key1 = settings:get('voicemail', 'microsoft_key1', 'text') or '';
local api_key2 = settings:get('voicemail', 'microsoft_key2', 'text') or '';
if (api_key1 ~= '' and api_key2 ~= '') then
access_token_cmd = "curl -X POST \"https://api.cognitive.microsoft.com/sts/v1.0/issueToken\" -H \"Content-type: application/x-www-form-urlencoded\" -H \"Content-Length: 0\" -H \"Ocp-Apim-Subscription-Key: "..api_key1.."\""
local handle = io.popen(access_token_cmd);
local access_token_result = handle:read("*a");
handle:close();
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] CMD: " .. access_token_cmd .. "\n");
freeswitch.consoleLog("notice", "[voicemail] RESULT: " .. access_token_result .. "\n");
end
--Access token request can fail
if (access_token_result == '') then
freeswitch.consoleLog("notice", "[voicemail] ACCESS TOKEN: (null) \n");
return ''
end
transcribe_cmd = "curl -X POST \"https://speech.platform.bing.com/recognize?scenarios=smd&appid=D4D52672-91D7-4C74-8AD8-42B1D98141A5&locale=" .. transcribe_language .. "&device.os=Freeswitch&version=3.0&format=json&instanceid=" .. gen_uuid() .. "&requestid=" .. gen_uuid() .. "\" -H 'Authorization: Bearer " .. access_token_result .. "' -H 'Content-type: audio/wav; codec=\"audio/pcm\"; samplerate=8000; trustsourcerate=false' --data-binary @"..file_path
local handle = io.popen(transcribe_cmd);
local transcribe_result = handle:read("*a");
handle:close();
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] CMD: " .. transcribe_cmd .. "\n");
freeswitch.consoleLog("notice", "[voicemail] RESULT: " .. transcribe_result .. "\n");
end
--Trancribe request can fail
if (transcribe_result == '') then
freeswitch.consoleLog("notice", "[voicemail] TRANSCRIPTION: (null) \n");
return ''
else
status, transcribe_json = pcall(JSON.decode, transcribe_result);
if not status then
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] error decoding bing json\n");
end
return '';
end
end
if (debug["info"]) then
if (transcribe_json["results"][1]["name"] == nil) then
freeswitch.consoleLog("notice", "[voicemail] TRANSCRIPTION: (null) \n");
else
freeswitch.consoleLog("notice", "[voicemail] TRANSCRIPTION: " .. transcribe_json["results"][1]["name"] .. "\n");
end
if (transcribe_json["results"][1]["confidence"] == nil) then
freeswitch.consoleLog("notice", "[voicemail] CONFIDENCE: (null) \n");
else
freeswitch.consoleLog("notice", "[voicemail] CONFIDENCE: " .. transcribe_json["results"][1]["confidence"] .. "\n");
end
end
transcription = transcribe_json["results"][1]["name"];
transcription = transcription:gsub("<profanity>.*<%/profanity>","...");
confidence = transcribe_json["results"][1]["confidence"];
return transcription;
end
end
if (transcribe_provider == "azure") then
local api_key1 = settings:get('voicemail', 'azure_key1', 'text') or '';
local api_server_region = settings:get('voicemail', 'azure_server_region', 'text') or '';
if (api_server_region ~= '') then
api_server_region = api_server_region .. ".";
else
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] azure_server_region default setting must be set\n");
end
return '';
end
if (api_key1 ~= '') then
-- search in memcache first, azure documentation claims that the access token is valid for 10 minutes
local cache = require "resources.functions.cache";
local key = "app:voicemail:azure:access_token";
local access_token_result = cache.get(key)
if access_token_result then
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] Azure access_token recovered from memcached\n");
end
else
access_token_cmd = "curl -X POST \"https://"..api_server_region.."api.cognitive.microsoft.com/sts/v1.0/issueToken\" -H \"Content-type: application/x-www-form-urlencoded\" -H \"Content-Length: 0\" -H \"Ocp-Apim-Subscription-Key: "..api_key1.."\"";
local handle = io.popen(access_token_cmd);
access_token_result = handle:read("*a");
handle:close();
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] CMD: " .. access_token_cmd .. "\n");
freeswitch.consoleLog("notice", "[voicemail] ACCESS TOKEN: " .. access_token_result .. "\n");
end
--Access token request can fail
if (access_token_result == '') then
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] ACCESS TOKEN: (null) \n");
end
return ''
end
--Azure returns JSON when it has to report an error
if (string.sub(access_token_result, 1, 1) == '{') then
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] ERROR STRING: ".. access_token_result .. "\n");
end
return ''
end
cache.set(key, access_token_result, 120);
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] Azure access_token saved into memcached: " .. access_token_result .. "\n");
end
end
transcribe_cmd = "curl -X POST \"https://"..api_server_region.."stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1?language=".. transcribe_language .."&format=detailed\" -H 'Authorization: Bearer " .. access_token_result .. "' -H 'Content-type: audio/wav; codec=\"audio/pcm\"; samplerate=8000; trustsourcerate=false' --data-binary @"..file_path
local handle = io.popen(transcribe_cmd);
local transcribe_result = handle:read("*a");
handle:close();
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] CMD: " .. transcribe_cmd .. "\n");
freeswitch.consoleLog("notice", "[voicemail] RESULT: " .. transcribe_result .. "\n");
end
--Trancribe request can fail
if (transcribe_result == '') then
freeswitch.consoleLog("notice", "[voicemail] TRANSCRIPTION: (null) \n");
return ''
end
local transcribe_json = JSON.decode(transcribe_result);
if (debug["info"]) then
if (transcribe_json["NBest"][1]["Display"] == nil) then
freeswitch.consoleLog("notice", "[voicemail] TRANSCRIPTION: (null) \n");
else
freeswitch.consoleLog("notice", "[voicemail] TRANSCRIPTION: " .. transcribe_json["NBest"][1]["Display"] .. "\n");
end
if (transcribe_json["NBest"][1]["Confidence"] == nil) then
freeswitch.consoleLog("notice", "[voicemail] CONFIDENCE: (null) \n");
else
freeswitch.consoleLog("notice", "[voicemail] CONFIDENCE: " .. transcribe_json["NBest"][1]["Confidence"] .. "\n");
end
end
transcription = transcribe_json["NBest"][1]["Display"];
confidence = transcribe_json["NBest"][1]["Confidence"];
return transcription;
end
end
--Watson
if (transcribe_provider == "watson") then
local api_key = settings:get('voicemail', 'watson_key', 'text') or '';
local transcription_server = settings:get('voicemail', 'watson_url', 'text') or '';
if (api_key ~= '') then
if (vm_message_ext == "mp3") then
transcribe_cmd = [[ curl -X POST -u "apikey:]]..api_key..[[" --header "Content-type: audio/mp3" --data-binary @]]..file_path..[[ "]]..transcription_server..[[" ]]
else
transcribe_cmd = [[ curl -X POST -u "apikey:]]..api_key..[[" --header "Content-type: audio/wav" --data-binary @]]..file_path..[[ "]]..transcription_server..[[" ]]
end
local handle = io.popen(transcribe_cmd);
local transcribe_result = handle:read("*a");
handle:close();
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] CMD: " .. transcribe_cmd .. "\n");
freeswitch.consoleLog("notice", "[voicemail] RESULT: " .. transcribe_result .. "\n");
end
--Trancribe request can fail
if (transcribe_result == '') then
freeswitch.consoleLog("notice", "[voicemail] TRANSCRIPTION: (null) \n");
return ''
else
status, transcribe_json = pcall(JSON.decode, transcribe_result);
if not status then
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] error decoding watson json\n");
end
return '';
end
end
if (transcribe_json["results"] ~= nil) then
--Transcription
if (transcribe_json["results"][1]["alternatives"][1]["transcript"] ~= nil) then
transcription = '';
for key, row in pairs(transcribe_json["results"]) do
transcription = transcription .. row["alternatives"][1]["transcript"];
end
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] TRANSCRIPTION: " .. transcription .. "\n");
end
else
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] TRANSCRIPTION: (null) \n");
end
return '';
end
--Confidence
if (transcribe_json["results"][1]["alternatives"][1]["confidence"]) then
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] CONFIDENCE: " .. transcribe_json["results"][1]["alternatives"][1]["confidence"] .. "\n");
end
confidence = transcribe_json["results"][1]["alternatives"][1]["confidence"];
else
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] CONFIDENCE: (null) \n");
end
end
return transcription;
else
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] TRANSCRIPTION: json error \n");
end
return '';
end
end
end
if (transcribe_provider == "custom") then
local transcription_server = settings:get('voicemail', 'transcription_server', 'text') or '';
local api_key = settings:get('voicemail', 'api_key', 'text') or '';
local json_enabled = settings:get('voicemail', 'json_enabled', 'boolean') or "false";
if (transcription_server ~= '') then
transcribe_cmd = "curl -X POST " .. transcription_server .. " -H 'Authorization: Bearer " .. api_key .. "' -F file=@"..file_path
local handle = io.popen(transcribe_cmd);
local transcribe_result = esc(handle:read("*a"));
handle:close();
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] CMD: " .. transcribe_cmd .. "\n");
freeswitch.consoleLog("notice", "[voicemail] RESULT: " .. transcribe_result .. "\n");
end
--Trancribe request can fail
if (transcribe_result == '') then
freeswitch.consoleLog("notice", "[voicemail] TRANSCRIPTION: (null) \n");
return ''
end
if (json_enabled == "true") then
local transcribe_json = JSON.decode(transcribe_result);
if (transcribe_json["message"] == nil) then
freeswitch.consoleLog("notice", "[voicemail] TRANSCRIPTION: " .. transcribe_result .. "\n");
transcribe_result = '';
end
if (transcribe_json["error"] ~= nil) then
freeswitch.consoleLog("notice", "[voicemail] TRANSCRIPTION: " .. transcribe_result .. "\n");
transcribe_result = '';
end
transcribe_result = transcribe_json["message"];
end
return transcribe_result;
end
end
else
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] message too short for transcription.\n");
end
end
return '';
end
--save the recording
function record_message()
--set the variables
local db = dbh or Database.new('system')
local settings = Settings.new(db, domain_name, domain_uuid)
local message_max_length = settings:get('voicemail', 'message_max_length', 'numeric') or 300;
local message_silence_threshold = settings:get('voicemail', 'message_silence_threshold', 'numeric') or 200;
local message_silence_seconds = settings:get('voicemail', 'message_silence_seconds', 'numeric') or 3;
transcribe_enabled = settings:get('voicemail', 'transcribe_enabled', 'boolean') or "false";
local transcribe_provider = settings:get('voicemail', 'transcribe_provider', 'text') or '';
--debug information
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] transcribe_enabled: " .. transcribe_enabled .. "\n");
freeswitch.consoleLog("notice", "[voicemail] voicemail_transcription_enabled: " .. voicemail_transcription_enabled .. "\n");
end
--record your message at the tone press any key or stop talking to end the recording
if (skip_instructions == "true") then
--skip the instructions
else
if (dtmf_digits and string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "record_message", 1, 100);
end
end
--voicemail ivr options
if (session:ready()) then
if (dtmf_digits == nil) then
dtmf_digits = session:getDigits(max_digits, "#", 1000);
else
dtmf_digits = dtmf_digits .. session:getDigits(max_digits, "#", 1000);
end
end
if (dtmf_digits) then
if (string.len(dtmf_digits) > 0) then
if (session:ready()) then
if (direct_dial["enabled"] == "true") then
if (string.len(dtmf_digits) < max_digits) then
dtmf_digits = dtmf_digits .. session:getDigits(direct_dial["max_digits"], "#", 3000);
end
end
end
if (session:ready()) then
freeswitch.consoleLog("notice", "[voicemail] dtmf_digits: " .. string.sub(dtmf_digits, 0, 1) .. "\n");
if (dtmf_digits == "*") then
if (remote_access == "true") then
--check the voicemail password
check_password(voicemail_id, password_tries);
--send to the main menu
timeouts = 0;
main_menu();
else
--remote access is false
freeswitch.consoleLog("notice", "[voicemail] remote access is disabled.\n");
session:hangup();
end
elseif (string.sub(dtmf_digits, 0, 1) == "*") then
--do not allow dialing numbers prefixed with *
session:hangup();
else
--get the voicemail options
local sql = [[SELECT * FROM v_voicemail_options WHERE voicemail_uuid = :voicemail_uuid ORDER BY voicemail_option_order asc ]];
local params = {voicemail_uuid = voicemail_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
count = 0;
dbh:query(sql, params, function(row)
--check for matching options
if (tonumber(row.voicemail_option_digits) ~= nil) then
row.voicemail_option_digits = "^"..row.voicemail_option_digits.."$";
end
if (api:execute("regex", "m:~"..dtmf_digits.."~"..row.voicemail_option_digits) == "true") then
if (row.voicemail_option_action == "menu-exec-app") then
--get the action and data
pos = string.find(row.voicemail_option_param, " ", 0, true);
action = string.sub( row.voicemail_option_param, 0, pos-1);
data = string.sub( row.voicemail_option_param, pos+1);
--check if the option uses a regex
regex = string.find(row.voicemail_option_digits, "(", 0, true);
if (regex) then
--get the regex result
result = trim(api:execute("regex", "m:~"..digits.."~"..row.voicemail_option_digits.."~$1"));
if (debug["regex"]) then
freeswitch.consoleLog("notice", "[voicemail] regex m:~"..digits.."~"..row.voicemail_option_digits.."~$1\n");
freeswitch.consoleLog("notice", "[voicemail] result: "..result.."\n");
end
--replace the $1 and the domain name
data = data:gsub("$1", result);
data = data:gsub("${domain_name}", domain_name);
end --if regex
end --if menu-exex-app
end --if regex match
--execute
if (action) then
if (string.len(action) > 0) then
--send to the log
if (debug["action"]) then
freeswitch.consoleLog("notice", "[voicemail] action: " .. action .. " data: ".. data .. "\n");
end
--run the action
session:execute(action, data);
end
end
--clear the variables
action = "";
data = "";
--inrement the option count
count = count + 1;
end); --end results
--direct dial
if (session:ready()) then
if (direct_dial["enabled"] == "true" and count == 0) then
if (string.len(dtmf_digits) < max_digits) then
dtmf_digits = dtmf_digits .. session:getDigits(direct_dial["max_digits"], "#", 5000);
session:transfer(dtmf_digits.." XML "..context);
end
end
end
end
end
end
end
--play the beep
dtmf_digits = '';
result = macro(session, "record_beep", 1, 100);
--start epoch
start_epoch = os.time();
--save the recording
-- syntax is session:recordFile(file_name, max_len_secs, silence_threshold, silence_secs)
if (storage_path == "http_cache") then
result = session:recordFile(storage_path.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext, message_max_length, message_silence_threshold, message_silence_seconds);
else
mkdir(voicemail_dir.."/"..voicemail_id);
if (vm_message_ext == "mp3") then
shout_exists = trim(api:execute("module_exists", "mod_shout"));
if (shout_exists == "true" and transcribe_enabled == "false") or (shout_exists == "true" and transcribe_enabled == "true" and voicemail_transcription_enabled ~= "true") then
freeswitch.consoleLog("notice", "using mod_shout for mp3 encoding\n");
--record in mp3 directly, no transcription
result = session:recordFile(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid..".mp3", message_max_length, message_silence_threshold, message_silence_seconds);
elseif (shout_exists == "true" and transcribe_enabled == "true" and voicemail_transcription_enabled == "true" and transcribe_provider == "watson") then
--record in mp3 directly with mp3 transcription if watson selected
result = session:recordFile(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid..".mp3", message_max_length, message_silence_threshold, message_silence_seconds);
transcription = transcribe(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid..".mp3",settings,start_epoch);
else
--create initial wav recording
result = session:recordFile(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid..".wav", message_max_length, message_silence_threshold, message_silence_seconds);
if (transcribe_enabled == "true" and voicemail_transcription_enabled == "true") then
transcription = transcribe(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid..".wav",settings,start_epoch);
end
--use lame to encode, if available
if (file_exists("/usr/bin/lame")) then
freeswitch.consoleLog("notice", "using lame for mp3 encoding\n");
--convert the wav to an mp3 (lame required)
resample = "/usr/bin/lame -b 32 --resample 8 -m s "..voicemail_dir.."/"..voicemail_id.."/msg_"..uuid..".wav "..voicemail_dir.."/"..voicemail_id.."/msg_"..uuid..".mp3";
session:execute("system", resample);
--delete the wav file, if mp3 exists
if (file_exists(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid..".mp3")) then
os.remove(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid..".wav");
else
vm_message_ext = "wav";
end
else
freeswitch.consoleLog("notice", "neither mod_shout or lame found, defaulting to wav\n");
vm_message_ext = "wav";
end
end
else
result = session:recordFile(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext, message_max_length, message_silence_threshold, message_silence_seconds);
if (transcribe_enabled == "true" and voicemail_transcription_enabled == "true") then
transcription = transcribe(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext,settings,start_epoch);
end
end
end
--stop epoch
stop_epoch = os.time();
--calculate the message length
message_length = stop_epoch - start_epoch;
message_length_formatted = format_seconds(message_length);
--if the recording is below the minimal length then re-record the message
if (message_length > 2) then
--continue
else
if (session:ready()) then
--your recording is below the minimal acceptable length, please try again
dtmf_digits = '';
macro(session, "too_small", 1, 100);
--record your message at the tone
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
record_message();
else
timeouts = 0;
record_menu("message", voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
end
end
end
--instructions press 1 to listen to the recording, press 2 to save the recording, press 3 to re-record
if (session:ready()) then
if (skip_instructions == "true") then
--save the message
dtmf_digits = '';
macro(session, "message_saved", 1, 100, '');
macro(session, "goodbye", 1, 100, '');
--hangup the call
session:hangup();
else
timeouts = 0;
record_menu("message", voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
end
end
end

View File

@@ -0,0 +1,102 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--define a function to record the name
function record_name(menu)
if (session:ready()) then
--flush dtmf digits from the input buffer
session:flushDigits();
--play the name record
dtmf_digits = '';
macro(session, "record_name", 1, 100, '');
--prepate to record
-- syntax is session:recordFile(file_name, max_len_secs, silence_threshold, silence_secs)
max_len_seconds = 30;
silence_threshold = 30;
silence_seconds = 5;
mkdir(voicemail_dir.."/"..voicemail_id);
--record and save the file
if (storage_type == "base64") then
--set the location
voicemail_name_location = voicemail_dir.."/"..voicemail_id.."/recorded_name.wav";
--record the file to the file system
-- syntax is session:recordFile(file_name, max_len_secs, silence_threshold, silence_secs);
result = session:recordFile(voicemail_name_location, max_len_seconds, silence_threshold, silence_seconds);
--session:execute("record", voicemail_dir.."/"..uuid.." 180 200");
--show the storage type
freeswitch.consoleLog("notice", "[recordings] ".. storage_type .. "\n");
--base64 encode the file
--include the file io
local file = require "resources.functions.file"
--read file content as base64 string
voicemail_name_base64 = assert(file.read_base64(voicemail_name_location));
--update the voicemail name
local sql = "UPDATE v_voicemails ";
sql = sql .. "set voicemail_name_base64 = :voicemail_name_base64 ";
sql = sql .. "where domain_uuid = :domain_uuid ";
sql = sql .. "and voicemail_id = :voicemail_id";
local params = {voicemail_name_base64 = voicemail_name_base64,
domain_uuid = domain_uuid, voicemail_id = voicemail_id};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[recording] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
if (storage_type == "base64") then
local dbh = Database.new('system', 'base64');
dbh:query(sql, params);
dbh:release();
else
dbh:query(sql, params);
end
elseif (storage_type == "http_cache") then
freeswitch.consoleLog("notice", "[voicemail] ".. storage_type .. " ".. storage_path .."\n");
session:execute("record", storage_path .."/"..recording_name);
else
-- syntax is session:recordFile(file_name, max_len_secs, silence_threshold, silence_secs);
result = session:recordFile(voicemail_dir.."/"..voicemail_id.."/recorded_name.wav", max_len_seconds, silence_threshold, silence_seconds);
end
--play the name
--session:streamFile(voicemail_dir.."/"..voicemail_id.."/recorded_name.wav");
--option to play, save, and re-record the name
if (session:ready()) then
timeouts = 0;
record_menu("name", voicemail_dir.."/"..voicemail_id.."/recorded_name.wav",nil, menu);
if (storage_type == "base64") then
--delete the greeting
os.remove(voicemail_dir.."/"..voicemail_id.."/recorded_name.wav");
end
end
end
end

View File

@@ -0,0 +1,36 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--define a function to return the call
function return_call(destination)
if (session:ready()) then
--clear the dtmf
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--transfer the call
session:transfer(destination, "XML", context);
end
end

View File

@@ -0,0 +1,248 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--load libraries
local send_mail = require 'resources.functions.send_mail'
local Database = require "resources.functions.database"
local Settings = require "resources.functions.lazy_settings"
--define a function to send email
function send_email(id, uuid)
local db = dbh or Database.new('system')
local settings = Settings.new(db, domain_name, domain_uuid)
--get voicemail message details
local sql = [[SELECT * FROM v_voicemails
WHERE domain_uuid = :domain_uuid
AND voicemail_id = :voicemail_id]]
local params = {domain_uuid = domain_uuid, voicemail_id = id};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
db_voicemail_uuid = string.lower(row["voicemail_uuid"]);
--voicemail_password = row["voicemail_password"];
--greeting_id = row["greeting_id"];
voicemail_mail_to = row["voicemail_mail_to"];
voicemail_file = row["voicemail_file"];
voicemail_local_after_email = row["voicemail_local_after_email"];
voicemail_description = row["voicemail_description"];
end);
--set default values
if (voicemail_local_after_email == nil) then
voicemail_local_after_email = "true";
end
if (voicemail_file == nil) then
voicemail_file = "listen";
end
--require the email address to send the email
if (string.len(voicemail_mail_to) > 2) then
--include languages file
local Text = require "resources.functions.text"
local text = Text.new("app.voicemail.app_languages")
local dbh = dbh
--connect using other backend if needed
if storage_type == "base64" then
dbh = Database.new('system', 'base64/read')
end
--get voicemail message details
local sql = [[SELECT * FROM v_voicemail_messages
WHERE domain_uuid = :domain_uuid
AND voicemail_message_uuid = :uuid]]
local params = {domain_uuid = domain_uuid, uuid = uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--get the values from the database
--uuid = row["voicemail_message_uuid"];
created_epoch = row["created_epoch"];
caller_id_name = row["caller_id_name"];
caller_id_number = row["caller_id_number"];
message_length = row["message_length"];
--message_status = row["message_status"];
--message_priority = row["message_priority"];
--get the recordings from the database
if (storage_type == "base64") then
--set the voicemail message path
message_location = voicemail_dir.."/"..id.."/msg_"..uuid.."."..vm_message_ext;
--save the recording to the file system
if (string.len(row["message_base64"]) > 32) then
--include the file io
local file = require "resources.functions.file"
--write decoded string to file
file.write_base64(message_location, row["message_base64"]);
end
end
end);
--close temporary connection
if storage_type == "base64" then
dbh:release()
end
--format the message length and date
message_length_formatted = format_seconds(message_length);
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] message length: " .. message_length .. "\n");
end
local message_date = os.date("%A, %d %b %Y %I:%M %p", created_epoch);
--connect to the database
local dbh = Database.new('system');
--get the templates
local sql = "SELECT * FROM v_email_templates ";
sql = sql .. "WHERE (domain_uuid = :domain_uuid or domain_uuid is null) ";
sql = sql .. "AND template_language = :template_language ";
sql = sql .. "AND template_category = 'voicemail' "
if (transcription == nil) then
sql = sql .. "AND template_subcategory = 'default' "
else
sql = sql .. "AND template_subcategory = 'transcription' "
end
sql = sql .. "AND template_enabled = 'true' "
sql = sql .. "ORDER BY domain_uuid DESC "
local params = {domain_uuid = domain_uuid, template_language = default_language.."-"..default_dialect};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
subject = row["template_subject"];
body = row["template_body"];
end);
--get the link_address
link_address = http_protocol.."://"..domain_name..project_path;
--prepare the headers
local headers = {
["X-FusionPBX-Domain-UUID"] = domain_uuid;
["X-FusionPBX-Domain-Name"] = domain_name;
["X-FusionPBX-Call-UUID"] = uuid;
["X-FusionPBX-Email-Type"] = 'voicemail';
}
--prepare the voicemail_name_formatted
voicemail_name_formatted = id;
local display_domain_name = settings:get('voicemail', 'display_domain_name', 'boolean');
if (display_domain_name == 'true') then
voicemail_name_formatted = id.."@"..domain_name;
end
if (voicemail_description ~= nil and voicemail_description ~= "" and voicemail_description ~= id) then
voicemail_name_formatted = voicemail_name_formatted.." ("..voicemail_description..")";
end
--prepare the subject
subject = subject:gsub("${caller_id_name}", caller_id_name);
subject = subject:gsub("${caller_id_number}", caller_id_number);
subject = subject:gsub("${message_date}", message_date);
subject = subject:gsub("${message_duration}", message_length_formatted);
subject = subject:gsub("${account}", voicemail_name_formatted);
subject = subject:gsub("${voicemail_id}", id);
subject = subject:gsub("${voicemail_description}", voicemail_description);
subject = subject:gsub("${voicemail_name_formatted}", voicemail_name_formatted);
subject = subject:gsub("${domain_name}", domain_name);
subject = trim(subject);
subject = '=?utf-8?B?'..base64.encode(subject)..'?=';
--prepare the body
body = body:gsub("${caller_id_name}", caller_id_name);
body = body:gsub("${caller_id_number}", caller_id_number);
body = body:gsub("${message_date}", message_date);
if (transcription ~= nil) then
transcription = transcription:gsub("%%", "*");
body = body:gsub("${message_text}", transcription);
end
body = body:gsub("${message_duration}", message_length_formatted);
body = body:gsub("${account}", voicemail_name_formatted);
body = body:gsub("${voicemail_id}", id);
body = body:gsub("${voicemail_description}", voicemail_description);
body = body:gsub("${voicemail_name_formatted}", voicemail_name_formatted);
body = body:gsub("${domain_name}", domain_name);
body = body:gsub("${sip_to_user}", id);
body = body:gsub("${dialed_user}", id);
if (voicemail_file == "attach") then
body = body:gsub("${message}", text['label-attached']);
elseif (voicemail_file == "link") then
body = body:gsub("${message}", "<a href='"..link_address.."/app/voicemails/voicemail_messages.php?action=download&type=vm&t=bin&id="..id.."&voicemail_uuid="..db_voicemail_uuid.."&uuid="..uuid.."&src=email'>"..text['label-download'].."</a>");
else
body = body:gsub("${message}", "<a href='"..link_address.."/app/voicemails/voicemail_messages.php?action=autoplay&id="..db_voicemail_uuid.."&uuid="..uuid.."'>"..text['label-listen'].."</a>");
end
--body = body:gsub(" ", "&nbsp;");
--body = body:gsub("%s+", "");
--body = body:gsub("&nbsp;", " ");
body = trim(body);
--prepare file
file = voicemail_dir.."/"..id.."/msg_"..uuid.."."..vm_message_ext;
--send the email
send_mail(headers,
voicemail_mail_to,
{subject, body},
(voicemail_file == "attach") and file
);
end
--whether to keep the voicemail message and details local after email
if (string.len(voicemail_mail_to) > 2) then
if (voicemail_local_after_email == "false") then
--delete the voicemail message details
local sql = [[DELETE FROM v_voicemail_messages
WHERE domain_uuid = :domain_uuid
AND voicemail_uuid = :voicemail_uuid
AND voicemail_message_uuid = :uuid]]
local params = {domain_uuid = domain_uuid,
voicemail_uuid = db_voicemail_uuid, uuid = uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--delete voicemail recording file
if (file_exists(file)) then
os.remove(file);
end
--set message waiting indicator
message_waiting(id, domain_uuid);
--clear the variable
db_voicemail_uuid = '';
elseif (storage_type == "base64") then
--delete voicemail recording file
if (file_exists(file)) then
os.remove(file);
end
end
end
end

View File

@@ -0,0 +1,109 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--define a function to send sms
function send_sms(id, uuid)
debug["info"] = true;
api = freeswitch.API();
--get voicemail message details
sql = [[SELECT * FROM v_voicemails
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_id = ']] .. id ..[[']]
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
db_voicemail_uuid = string.lower(row["voicemail_uuid"]);
voicemail_sms_to = row["voicemail_sms_to"];
voicemail_file = row["voicemail_file"];
end);
--get the sms_body template
if (settings['voicemail']['voicemail_sms_body'] ~= nil) then
if (settings['voicemail']['voicemail_sms_body']['text'] ~= nil) then
sms_body = settings['voicemail']['voicemail_sms_body']['text'];
end
else
sms_body = 'You have a new voicemail from: ${caller_id_name} - ${caller_id_number} length ${message_length_formatted}';
end
--require the sms address to send to
if (string.len(voicemail_sms_to) > 2) then
--include languages file
local Text = require "resources.functions.text"
local text = Text.new("app.voicemail.app_languages")
--get voicemail message details
sql = [[SELECT * FROM v_voicemail_messages
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_message_uuid = ']] .. uuid ..[[']]
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
--get the values from the database
--uuid = row["voicemail_message_uuid"];
created_epoch = row["created_epoch"];
caller_id_name = row["caller_id_name"];
caller_id_number = row["caller_id_number"];
message_length = row["message_length"];
end);
--format the message length and date
message_length_formatted = format_seconds(message_length);
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail-sms] message length: " .. message_length .. "\n");
if (transcription ~= nil) then
freeswitch.consoleLog("notice", "[voicemail-sms] transcription: " .. transcription .. "\n");
end
freeswitch.consoleLog("notice", "[voicemail-sms] domain_name: " .. domain_name .. "\n");
end
local message_date = os.date("%A, %d %b %Y %I:%M %p", created_epoch)
sms_body = sms_body:gsub("${caller_id_name}", caller_id_name);
sms_body = sms_body:gsub("${caller_id_number}", caller_id_number);
sms_body = sms_body:gsub("${message_date}", message_date);
sms_body = sms_body:gsub("${message_duration}", message_length_formatted);
sms_body = sms_body:gsub("${account}", id);
sms_body = sms_body:gsub("${domain_name}", domain_name);
sms_body = sms_body:gsub("${sip_to_user}", id);
sms_body = sms_body:gsub("${dialed_user}", id);
if (transcription ~= nil) then
sms_body = sms_body:gsub("${message_text}", transcription);
end
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail-sms] sms_body: " .. sms_body .. "\n");
end
-- sms_body = "hello";
cmd = "luarun app.lua sms outbound " .. voicemail_sms_to .. "@" .. domain_name .. " " .. voicemail_to_sms_did .. " '" .. sms_body .. "'";
api:executeString(cmd);
end
end

View File

@@ -0,0 +1,197 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--define function main menu
function tutorial (menu)
if (voicemail_uuid) then
--intro menu
if (menu == "intro") then
--clear the value
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--play the tutorial press 1, to skip 2
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "tutorial_intro", 1, 3000, '');
end
end
--process the dtmf
if (session:ready()) then
if (dtmf_digits == "1") then
timeouts = 0;
tutorial("record_name");
elseif (dtmf_digits == "2") then
timeouts = 0;
tutorial("finish");
else
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
tutorial("intro");
else
timeouts = 0;
tutorial("finish");
end
end
end
end
end
--record name menu
if (menu == "record_name") then
--clear the value
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--play the record name press 1
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "tutorial_to_record_name", 1, 100, '');
end
end
--skip the name and go to password press 2
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "tutorial_skip", 1, 3000, '');
end
end
--process the dtmf
if (session:ready()) then
if (dtmf_digits == "1") then
timeouts = 0;
record_name("tutorial");
elseif (dtmf_digits == "2") then
timeouts = 0;
tutorial("change_password");
else
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
tutorial("record_name");
else
tutorial("change_password");
end
end
end
end
end
--change password menu
if (menu == "change_password") then
--clear the value
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--to change your password press 1
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "tutorial_change_password", 1, 100, '');
end
end
--skip the password and go to greeting press 2
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "tutorial_skip", 1, 3000, '');
end
end
--process the dtmf
if (session:ready()) then
if (dtmf_digits == "1") then
timeouts = 0;
change_password(voicemail_id, "tutorial");
elseif (dtmf_digits == "2") then
timeouts = 0;
tutorial("record_greeting");
else
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
tutorial("change_password");
else
tutorial("record_greeting");
end
end
end
end
end
--change greeting menu
if (menu == "record_greeting") then
--clear the value
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--to record a greeting press 1
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "tutorial_record_greeting", 1, 100, '');
end
end
--skip the record greeting press 2. finishes the tutorial and routes to main menu
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "tutorial_skip", 1, 3000, '');
end
end
--process the dtmf
if (session:ready()) then
if (dtmf_digits == "1") then
timeouts = 0;
record_greeting(nil, "tutorial");
elseif (dtmf_digits == "2") then
timeouts = 0;
tutorial("finish");
else
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
tutorial("record_greeting");
else
tutorial("finish");
end
end
end
end
end
if (menu == "finish") then
--clear the value
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--update play tutorial in the datebase
local sql = [[UPDATE v_voicemails
set voicemail_tutorial = 'false'
WHERE domain_uuid = :domain_uuid
AND voicemail_id = :voicemail_id
AND voicemail_enabled = 'true' ]];
local params = {domain_uuid = domain_uuid,
voicemail_id = voicemail_id};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params);
--go to main menu
main_menu();
end
end
end

View File

@@ -0,0 +1,108 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--include the lua script
require "resources.functions.config";
--define general settings
sleep = 300;
--define the run file
run_file = scripts_dir .. "/run/voicemail-mwi.tmp";
--debug
debug["sql"] = false;
debug["info"] = false;
--only run the script a single time
runonce = false;
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--used to stop the lua service
local file = assert(io.open(run_file, "w"));
file:write("remove this file to stop the script");
--define the trim function
require "resources.functions.trim";
--check if a file exists
require "resources.functions.file_exists";
--send MWI NOTIFY message
require "app.voicemail.resources.functions.mwi_notify";
--get message count for mailbox
require "app.voicemail.resources.functions.message_count";
--create the api object
api = freeswitch.API();
--run lua as a service
while true do
--exit the loop when the file does not exist
if (not file_exists(run_file)) then
freeswitch.consoleLog("NOTICE", run_file.." not found\n");
break;
end
--Send MWI events for voicemail boxes with messages
local sql = [[SELECT v.voicemail_id, v.voicemail_uuid, v.domain_uuid, d.domain_name, COUNT(*) AS message_count
FROM v_voicemail_messages as m, v_voicemails as v, v_domains as d
WHERE v.voicemail_uuid = m.voicemail_uuid
AND v.domain_uuid = d.domain_uuid
GROUP BY v.voicemail_id, v.voicemail_uuid, v.domain_uuid, d.domain_name;]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
dbh:query(sql, function(row)
--get saved and new message counts
local new_messages, saved_messages = message_count_by_uuid(
row["voicemail_uuid"], row["domain_uuid"]
)
--send the message waiting event
local account = row["voicemail_id"].."@"..row["domain_name"]
mwi_notify(account, new_messages, saved_messages)
--log to console
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] mailbox: "..account.." messages: " .. (new_messages or "0") .. "/" .. (saved_messages or "0") .. " \n");
end
end);
if (runonce) then
freeswitch.consoleLog("notice", "mwi.lua has ended\n");
break;
else
--slow the loop down
os.execute("sleep "..sleep);
end
end

View File

@@ -0,0 +1,112 @@
require "resources.functions.config"
require "resources.functions.split"
local service_name = "mwi"
local log = require "resources.functions.log"[service_name]
local BasicEventService = require "resources.functions.basic_event_service"
local Database = require "resources.functions.database"
local cache = require "resources.functions.cache"
local mwi_notify = require "app.voicemail.resources.functions.mwi_notify"
local vm_message_count do
local vm_to_uuid_sql = [[SELECT v.voicemail_uuid
FROM v_voicemails as v inner join v_domains as d on v.domain_uuid = d.domain_uuid
WHERE v.voicemail_id = :voicemail_id and d.domain_name = :domain_name]]
local vm_messages_sql = [[SELECT
( SELECT count(*)
FROM v_voicemail_messages
WHERE voicemail_uuid = %s
AND (message_status is null or message_status = '')
) as new_messages,
( SELECT count(*)
FROM v_voicemail_messages
WHERE voicemail_uuid = %s
AND message_status = 'saved'
) as saved_messages
]]
function vm_message_count(account, use_cache)
local id, domain_name = split_first(account, '@', true)
if not domain_name then return end
-- FusionPBX support only numeric voicemail id
if not tonumber(id) then
log.warningf('non numeric voicemail id: %s', id)
return
end
local dbh = Database.new('system')
if not dbh then return end
local uuid
if use_cache and cache.support() then
local uuid = cache.get('voicemail_uuid:' .. account)
if not uuid then
uuid = dbh:first_value(vm_to_uuid_sql, {
voicemail_id = id, domain_name = domain_name
})
if uuid and #uuid > 0 then
cache.set('voicemail_uuid:' .. account, uuid, 3600)
end
end
end
local row
if uuid and #uuid > 0 then
local sql = string.format(vm_messages_sql, ":voicemail_uuid", ":voicemail_uuid")
row = dbh:first_row(sql, {voicemail_uuid = uuid})
else
local uuid_sql = '(' .. vm_to_uuid_sql .. ')'
local sql = string.format(vm_messages_sql,
uuid_sql, uuid_sql
)
row = dbh:first_row(sql, {
voicemail_id = id, domain_name = domain_name
})
end
dbh:release()
if not row then return end
return row.new_messages, row.saved_messages
end
end
local service = BasicEventService.new(log, service_name)
-- MWI SUBSCRIBE
service:bind("MESSAGE_QUERY", function(self, name, event)
local account_header = event:getHeader('Message-Account')
if not account_header then
return log.warningf("MWI message without `Message-Account` header")
end
local proto, account = split_first(account_header, ':', true)
if (not account) or (proto ~= 'sip' and proto ~= 'sips') then
return log.warningf("invalid format for voicemail id: %s", account_header)
end
local new_messages, saved_messages = vm_message_count(account)
if not new_messages then
return log.warningf('can not find voicemail: %s', account)
end
log.noticef('voicemail %s has %s/%s message(s)', account, new_messages, saved_messages)
mwi_notify(account, new_messages, saved_messages)
end)
log.notice("start")
service:run()
log.notice("stop")

View File

@@ -0,0 +1,61 @@
<html>
<table width="400" border="0" cellspacing="0" cellpadding="0" align="center"
style="border: 1px solid #cbcfd5;-moz-border-radius: 4px;
-webkit-border-radius: 4px; border-radius: 4px;">
<tr>
<td valign="middle" align="center" bgcolor="#e5e9f0" style="background-color: #e5e9f0;
color: #000; font-family: Arial; font-size: 14px; padding: 7px;-moz-border-radius: 4px;
-webkit-border-radius: 4px; border-radius: 4px;">
<strong>Neue Sprachnachricht</strong>
</td>
</tr>
<tr>
<td valign="top" style="padding: 15px;">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>Nebenstelle</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${voicemail_name_formatted}
</td>
</tr>
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;" width="20%">
<strong>Anrufer</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;" width="80%">
${caller_id_number}
</td>
</tr>
<!--
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>Received</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${message_date}
</td>
</tr>
-->
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>Nachricht</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${message}
</td>
</tr>
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>L&auml;nge</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${message_duration}
</td>
</tr>
</table>
</td>
</tr>
</table>
</html>

View File

@@ -0,0 +1 @@
Sprachnachricht von ${caller_id_name} <${caller_id_number}> ${message_duration}

View File

@@ -0,0 +1,61 @@
<html>
<table width="400" border="0" cellspacing="0" cellpadding="0" align="center"
style="border: 1px solid #cbcfd5;-moz-border-radius: 4px;
-webkit-border-radius: 4px; border-radius: 4px;">
<tr>
<td valign="middle" align="center" bgcolor="#e5e9f0" style="background-color: #e5e9f0;
color: #000; font-family: Arial; font-size: 14px; padding: 7px;-moz-border-radius: 4px;
-webkit-border-radius: 4px; border-radius: 4px;">
<strong>Neue Sprachnachricht</strong>
</td>
</tr>
<tr>
<td valign="top" style="padding: 15px;">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>Nebenstelle</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${voicemail_name_formatted}
</td>
</tr>
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;" width="20%">
<strong>Anrufer</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;" width="80%">
${caller_id_number}
</td>
</tr>
<!--
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>Received</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${message_date}
</td>
</tr>
-->
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>Nachricht</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${message}
</td>
</tr>
<tr>
<td style="color: #333; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
<strong>L&auml;nge</strong>
</td>
<td style="color: #666; font-family: Arial; font-size: 12px; padding-bottom: 11px;">
${message_duration}
</td>
</tr>
</table>
</td>
</tr>
</table>
</html>

View File

@@ -0,0 +1 @@
Sprachnachricht von ${caller_id_name} <${caller_id_number}> ${message_duration}

View File

@@ -0,0 +1,6 @@
To ${voicemail_name_formatted}
Received ${message_date}
Message ${message}
Length ${message_duration}

View File

@@ -0,0 +1 @@
Voicemail from ${caller_id_name} <${caller_id_number}> ${message_duration}

View File

@@ -0,0 +1,6 @@
To ${voicemail_name_formatted}
Received ${message_date}
Message ${message}
Length ${message_duration}

View File

@@ -0,0 +1,9 @@
To ${voicemail_name_formatted}
Received ${message_date}
Message ${message}
Length ${message_duration}
Message Text
${message_text}

View File

@@ -0,0 +1 @@
Voicemail from ${caller_id_name} <${caller_id_number}> ${message_duration}

View File

@@ -0,0 +1,106 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--general functions
require "resources.functions.trim";
require "resources.functions.file_exists";
require "resources.functions.explode";
--if the params class and methods do not exist then add them to prevent errors
if (not params) then
params = {}
function params:getHeader(name)
self.name = name;
end
function params:serialize(name)
self.name = name;
end
end
--show the params in the console
if (debug["params"]) then
if (params:serialize() ~= nil) then
freeswitch.consoleLog("notice", "[xml_handler] Params:\n" .. params:serialize() .. "\n");
end
end
--show the xml request in the console
if (debug["xml_request"]) then
freeswitch.consoleLog("notice", "[xml_handler] Section: " .. XML_REQUEST["section"] .. "\n");
freeswitch.consoleLog("notice", "[xml_handler] Tag Name: " .. XML_REQUEST["tag_name"] .. "\n");
freeswitch.consoleLog("notice", "[xml_handler] Key Name: " .. XML_REQUEST["key_name"] .. "\n");
freeswitch.consoleLog("notice", "[xml_handler] Key Value: " .. XML_REQUEST["key_value"] .. "\n");
end
--get the params and set them as variables
domain_name = params:getHeader("sip_from_host");
if (domain_uuid == nil) then
domain_uuid = params:getHeader("domain_uuid");
end
domain_name = params:getHeader("domain");
if (domain_name == nil) then
domain_name = params:getHeader("domain_name");
end
if (domain_name == nil) then
domain_name = params:getHeader("variable_domain_name");
end
if (domain_name == nil) then
domain_name = params:getHeader("variable_sip_from_host");
end
purpose = params:getHeader("purpose");
profile = params:getHeader("profile");
key = params:getHeader("key");
user = params:getHeader("user");
user_context = params:getHeader("variable_user_context");
call_context = params:getHeader("Caller-Context");
destination_number = params:getHeader("Caller-Destination-Number");
caller_id_number = params:getHeader("Caller-Caller-ID-Number");
hunt_context = params:getHeader("Hunt-Context");
if (hunt_context ~= nil) then
call_context = hunt_context;
end
--prepare the api object
api = freeswitch.API();
--process the sections
if (XML_REQUEST["section"] == "configuration") then
configuration = scripts_dir.."/app/xml_handler/resources/scripts/configuration/"..XML_REQUEST["key_value"]..".lua";
if (debug["xml_request"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. configuration .. "\n");
end
if (file_exists(configuration)) then
dofile(configuration);
end
end
if (XML_REQUEST["section"] == "directory") then
dofile(scripts_dir.."/app/xml_handler/resources/scripts/directory/directory.lua");
end
if (XML_REQUEST["section"] == "dialplan") then
dofile(scripts_dir.."/app/xml_handler/resources/scripts/dialplan/dialplan.lua");
end
if (XML_REQUEST["section"] == "languages") then
dofile(scripts_dir.."/app/xml_handler/resources/scripts/languages/languages.lua");
end

View File

@@ -0,0 +1,146 @@
-- xml_handler.lua
-- Part of FusionPBX
-- Copyright (C) 2015-2018 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--[[
These ACL's are automatically created on startup.
rfc1918.auto - RFC1918 Space
nat.auto - RFC1918 Excluding your local lan.
localnet.auto - ACL for your local lan.
loopback.auto - ACL for your local lan.
]]
--get the cache
local cache = require "resources.functions.cache"
local acl_cache_key = "configuration:acl.conf"
XML_STRING, err = cache.get(acl_cache_key)
--set the cache
if not XML_STRING then
--log cache error
if (debug["cache"]) then
freeswitch.consoleLog("warning", "[xml_handler] configuration:acl.conf can not be get from the cache: " .. tostring(err) .. "\n");
end
--set a default value
if (expire["acl"] == nil) then
expire["acl"]= "3600";
end
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--exits the script if we didn't connect properly
assert(dbh:connected());
--start the xml array
local xml = {}
table.insert(xml, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
table.insert(xml, [[<document type="freeswitch/xml">]]);
table.insert(xml, [[ <section name="configuration">]]);
table.insert(xml, [[ <configuration name="acl.conf" description="Network Lists">]]);
table.insert(xml, [[ <network-lists>]]);
--run the query
sql = "select * from v_access_controls ";
sql = sql .. "order by access_control_name asc ";
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n");
end
x = 0;
dbh:query(sql, function(row)
--list open tag
table.insert(xml, [[ <list name="]]..row.access_control_name..[[" default="]]..row.access_control_default..[[">]]);
--get the nodes
sql = "select * from v_access_control_nodes ";
sql = sql .. "where access_control_uuid = :access_control_uuid";
local params = {access_control_uuid = row.access_control_uuid}
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
x = 0;
dbh:query(sql, params, function(field)
if (string.len(field.node_domain) > 0) then
table.insert(xml, [[ <node type="]] .. field.node_type .. [[" domain="]] .. field.node_domain .. [[" description="]] .. field.node_description .. [["/>]]);
else
table.insert(xml, [[ <node type="]] .. field.node_type .. [[" cidr="]] .. field.node_cidr .. [[" description="]] .. field.node_description .. [["/>]]);
end
end)
--list close tag
table.insert(xml, [[ </list>]]);
end)
--close the extension tag if it was left open
table.insert(xml, [[ </network-lists>]]);
table.insert(xml, [[ </configuration>]]);
table.insert(xml, [[ </section>]]);
table.insert(xml, [[</document>]]);
XML_STRING = table.concat(xml, "\n");
if (debug["xml_string"]) then
freeswitch.consoleLog("notice", "[xml_handler] XML_STRING: " .. XML_STRING .. "\n");
end
--close the database connection
dbh:release();
--set the cache
local ok, err = cache.set(acl_cache_key, XML_STRING, expire["acl"]);
if debug["cache"] then
if ok then
freeswitch.consoleLog("notice", "[xml_handler] " .. acl_cache_key .. " stored in the cache\n");
else
freeswitch.consoleLog("warning", "[xml_handler] " .. acl_cache_key .. " can not be stored in the cache: " .. tostring(err) .. "\n");
end
end
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. acl_cache_key .. " source: database\n");
end
else
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. acl_cache_key .. " source: cache\n");
end
end --if XML_STRING
--send the xml to the console
if (debug["xml_string"]) then
local file = assert(io.open(temp_dir .. "/acl.conf.xml", "w"));
file:write(XML_STRING);
file:close();
end

View File

@@ -0,0 +1,318 @@
-- xml_handler.lua
-- Part of FusionPBX
-- Copyright (C) 2015-2018 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--include functions
require "resources.functions.format_ringback"
--get the cache
local cache = require "resources.functions.cache"
hostname = trim(api:execute("switchname", ""));
local cc_cache_key = "configuration:callcenter.conf:" .. hostname
XML_STRING, err = cache.get(cc_cache_key)
--set the cache
if not XML_STRING then
--log cache error
if (debug["cache"]) then
freeswitch.consoleLog("warning", "[xml_handler] " .. cc_cache_key .. " can not be get from the cache: " .. tostring(err) .. "\n");
end
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--exits the script if we didn't connect properly
assert(dbh:connected());
--get the variables
dsn = freeswitch.getGlobalVariable("dsn") or ''
dsn_callcenter = freeswitch.getGlobalVariable("dsn_callcenter") or ''
--start the xml array
local xml = {}
table.insert(xml, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
table.insert(xml, [[<document type="freeswitch/xml">]]);
table.insert(xml, [[ <section name="configuration">]]);
table.insert(xml, [[ <configuration name="callcenter.conf" description="Call Center">]]);
table.insert(xml, [[ <settings>]]);
if #dsn_callcenter > 0 then
table.insert(xml, [[ <param name="odbc-dsn" value="]]..dsn_callcenter..[["/>]]);
elseif #dsn > 0 then
table.insert(xml, [[ <param name="odbc-dsn" value="]]..database["switch"]..[["/>]]);
end
-- table.insert(xml, [[ <param name="dbname" value="]]..database_dir..[[/call_center.db"/>]]);
table.insert(xml, [[ </settings>]]);
--write the queues
table.insert(xml, [[ <queues>]]);
sql = "select * from v_call_center_queues as q, v_domains as d ";
sql = sql .. "where d.domain_uuid = q.domain_uuid; ";
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n");
end
x = 0;
dbh:query(sql, function(row)
--set as variables
queue_uuid = row.call_center_queue_uuid;
domain_uuid = row.domain_uuid;
domain_name = row.domain_name;
queue_name = row.queue_name;
queue_extension = row.queue_extension;
queue_strategy = row.queue_strategy;
queue_moh_sound = row.queue_moh_sound;
queue_record_template = row.queue_record_template;
queue_time_base_score = row.queue_time_base_score;
queue_max_wait_time = row.queue_max_wait_time;
queue_max_wait_time_with_no_agent = row.queue_max_wait_time_with_no_agent;
queue_tier_rules_apply = row.queue_tier_rules_apply;
queue_tier_rule_wait_second = row.queue_tier_rule_wait_second;
queue_tier_rule_wait_multiply_level = row.queue_tier_rule_wait_multiply_level;
queue_tier_rule_no_agent_no_wait = row.queue_tier_rule_no_agent_no_wait;
queue_discard_abandoned_after = row.queue_discard_abandoned_after;
queue_abandoned_resume_allowed = row.queue_abandoned_resume_allowed;
queue_announce_sound = row.queue_announce_sound;
queue_announce_frequency = row.queue_announce_frequency;
queue_description = row.queue_description;
--replace the space with a dash
queue_name = queue_name:gsub(" ", "-");
--start the xml
table.insert(xml, [[ <queue name="]]..queue_uuid..[[" label="]]..queue_name..[[@]]..domain_name..[[">]]);
table.insert(xml, [[ <param name="strategy" value="]]..queue_strategy..[["/>]]);
--set ringback
queue_ringback = format_ringback(queue_moh_sound);
table.insert(xml, [[ <param name="moh-sound" value="]]..queue_ringback..[["/>]]);
if (queue_record_template ~= nil) then
table.insert(xml, [[ <param name="record-template" value="]]..queue_record_template..[["/>]]);
end
if (queue_time_base_score ~= nil) then
table.insert(xml, [[ <param name="time-base-score" value="]]..queue_time_base_score..[["/>]]);
end
if (queue_max_wait_time_with_no_agent ~= nil) then
table.insert(xml, [[ <param name="max-wait-time" value="]]..queue_max_wait_time..[["/>]]);
end
if (queue_max_wait_time_with_no_agent ~= nil) then
table.insert(xml, [[ <param name="max-wait-time-with-no-agent" value="]]..queue_max_wait_time_with_no_agent..[["/>]]);
end
if (queue_max_wait_time_with_no_agent_time_reached ~= nil) then
table.insert(xml, [[ <param name="max-wait-time-with-no-agent-time-reached" value="]]..queue_max_wait_time_with_no_agent_time_reached..[["/>]]);
end
if (queue_tier_rules_apply ~= nil) then
table.insert(xml, [[ <param name="tier-rules-apply" value="]]..queue_tier_rules_apply..[["/>]]);
end
if (queue_tier_rule_wait_second ~= nil) then
table.insert(xml, [[ <param name="tier-rule-wait-second" value="]]..queue_tier_rule_wait_second..[["/>]]);
end
if (queue_tier_rule_wait_multiply_level ~= nil) then
table.insert(xml, [[ <param name="tier-rule-wait-multiply-level" value="]]..queue_tier_rule_wait_multiply_level..[["/>]]);
end
if (queue_tier_rule_no_agent_no_wait ~= nil) then
table.insert(xml, [[ <param name="tier-rule-no-agent-no-wait" value="]]..queue_tier_rule_no_agent_no_wait..[["/>]]);
end
if (queue_discard_abandoned_after ~= nil) then
table.insert(xml, [[ <param name="discard-abandoned-after" value="]]..queue_discard_abandoned_after..[["/>]]);
end
if (queue_abandoned_resume_allowed ~= nil) then
table.insert(xml, [[ <param name="abandoned-resume-allowed" value="]]..queue_abandoned_resume_allowed..[["/>]]);
end
if (queue_announce_sound ~= nil) then
table.insert(xml, [[ <param name="announce-sound" value="]]..queue_announce_sound..[["/>]]);
end
if (queue_announce_frequency ~= nil) then
table.insert(xml, [[ <param name="announce-frequency" value="]]..queue_announce_frequency..[["/>]]);
end
table.insert(xml, [[ </queue>]]);
--increment the value of x
x = x + 1;
end)
table.insert(xml, [[ </queues>]]);
--get the agents
table.insert(xml, [[ <agents>]]);
sql = "select * from v_call_center_agents as a, v_domains as d ";
sql = sql .. "where d.domain_uuid = a.domain_uuid; ";
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n");
end
x = 0;
dbh:query(sql, function(row)
--get the values from the database and set as variables
agent_uuid = row.call_center_agent_uuid;
domain_uuid = row.domain_uuid;
domain_name = row.domain_name;
agent_name = row.agent_name;
agent_type = row.agent_type;
agent_call_timeout = row.agent_call_timeout;
agent_contact = row.agent_contact;
agent_status = row.agent_status;
agent_no_answer_delay_time = row.agent_no_answer_delay_time;
agent_max_no_answer = row.agent_max_no_answer;
agent_wrap_up_time = row.agent_wrap_up_time;
agent_reject_delay_time = row.agent_reject_delay_time;
agent_busy_delay_time = row.agent_busy_delay_time;
--get and then set the complete agent_contact with the call_timeout and when necessary confirm
--confirm = "group_confirm_file=custom/press_1_to_accept_this_call.wav,group_confirm_key=1";
--if you change this variable also change app/call_center/call_center_agent_edit.php
confirm = "group_confirm_file=custom/press_1_to_accept_this_call.wav,group_confirm_key=1,group_confirm_read_timeout=2000,leg_timeout="..agent_call_timeout;
if (string.find(agent_contact, '}') == nil) then
--not found
if (string.find(agent_contact, 'sofia/gateway') == nil) then
--add the call_timeout
agent_contact = "{call_timeout="..agent_call_timeout..",sip_h_caller_destination=${caller_destination}}"..agent_contact;
else
--add the call_timeout and confirm
tmp_pos = string.find(agent_contact, "}");
tmp_first = string.sub(agent_contact, 0, tmp_pos);
tmp_last = string.sub(agent_contact, tmp_pos);
agent_contact = tmp_first..',call_timeout='..agent_call_timeout..tmp_last;
agent_contact = "{"..confirm..",call_timeout="..agent_call_timeout..",sip_h_caller_destination=${caller_destination}}"..agent_contact;
end
else
--found
if (string.find(agent_contact, 'sofia/gateway') == nil) then
--not found
if (string.find(agent_contact, 'call_timeout') == nil) then
--add the call_timeout
pos = string.find(agent_contact, "}");
first = string.sub(agent_contact, 0, pos);
last = string.sub(agent_contact, tmp_pos);
agent_contact = first..[[,sip_h_caller_destination=${caller_destination},call_timeout=]]..agent_call_timeout..last;
else
--the string has the call timeout
agent_contact = agent_contact;
end
else
--found
pos = string.find(agent_contact, "}");
first = string.sub(agent_contact, 0, pos);
last = string.sub(agent_contact, pos);
if (string.find(agent_contact, 'call_timeout') == nil) then
--add the call_timeout and confirm
agent_contact = first..','..confirm..',sip_h_caller_destination=${caller_destination}call_timeout='..agent_call_timeout..last;
else
--add confirm
agent_contact = tmp_first..',sip_h_caller_destination=${caller_destination},'..confirm..tmp_last;
end
end
end
--build the xml string
table.insert(xml, [[ <agent ]]);
table.insert(xml, [[ name="]]..agent_uuid..[[" ]]);
table.insert(xml, [[ label="]]..agent_name..[[@]]..domain_name..[[" ]]);
table.insert(xml, [[ type="]]..agent_type..[[" ]]);
table.insert(xml, [[ contact="]]..agent_contact..[[" ]]);
table.insert(xml, [[ status="]]..agent_status..[[" ]]);
if (agent_no_answer_delay_time ~= nil) then
table.insert(xml, [[ no-answer-delay-time="]]..agent_no_answer_delay_time..[[" ]]);
end
if (agent_max_no_answer ~= nil) then
table.insert(xml, [[ max-no-answer="]]..agent_max_no_answer..[[" ]]);
end
if (agent_wrap_up_time ~= nil) then
table.insert(xml, [[ wrap-up-time="]]..agent_wrap_up_time..[[" ]]);
end
if (agent_reject_delay_time ~= nil) then
table.insert(xml, [[ reject-delay-time="]]..agent_reject_delay_time..[[" ]]);
end
if (agent_busy_delay_time ~= nil) then
table.insert(xml, [[ busy-delay-time="]]..agent_busy_delay_time..[[" ]]);
end
table.insert(xml, [[ />]]);
end)
table.insert(xml, [[ </agents>]]);
--get the tiers
sql = "select * from v_call_center_tiers as t, v_domains as d ";
sql = sql .. "where d.domain_uuid = t.domain_uuid; ";
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n");
end
table.insert(xml, [[ <tiers>]]);
dbh:query(sql, function(row)
--get the values from the database and set as variables
domain_uuid = row.domain_uuid;
domain_name = row.domain_name;
agent_uuid = row.call_center_agent_uuid;
queue_uuid = row.call_center_queue_uuid;
tier_level = row.tier_level;
tier_position = row.tier_position;
--build the xml
table.insert(xml, [[ <tier ]]);
table.insert(xml, [[ agent="]]..agent_uuid..[[" ]]);
table.insert(xml, [[ queue="]]..queue_uuid..[[" ]]);
table.insert(xml, [[ domain_name="]]..domain_name..[[" ]]);
--table.insert(xml, [[ agent_name="]]..agent_name..[[" ]]);
--table.insert(xml, [[ queue_name="]]..queue_name..[[" ]]);
table.insert(xml, [[ level="]]..tier_level..[[" ]]);
table.insert(xml, [[ position="]]..tier_position..[[" ]]);
table.insert(xml, [[ />]]);
end)
table.insert(xml, [[ </tiers>]]);
--close the extension tag if it was left open
table.insert(xml, [[ </configuration>]]);
table.insert(xml, [[ </section>]]);
table.insert(xml, [[</document>]]);
XML_STRING = table.concat(xml, "\n");
if (debug["xml_string"]) then
freeswitch.consoleLog("notice", "[xml_handler] XML_STRING: " .. XML_STRING .. "\n");
end
--close the database connection
dbh:release();
--freeswitch.consoleLog("notice", "[xml_handler]"..api:execute("eval ${dsn}"));
--set the cache
local ok, err = cache.set(cc_cache_key, XML_STRING, expire["callcenter"]);
if debug["cache"] then
if ok then
freeswitch.consoleLog("notice", "[xml_handler] " .. cc_cache_key .. " stored in the cache\n");
else
freeswitch.consoleLog("warning", "[xml_handler] " .. cc_cache_key .. " can not be stored in the cache: " .. tostring(err) .. "\n");
end
end
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. cc_cache_key .. " source: database\n");
end
else
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. cc_cache_key .. " source: cache\n");
end
end --if XML_STRING
--send the xml to the console
if (debug["xml_string"]) then
local file = assert(io.open(temp_dir .. "/callcenter.conf.xml", "w"));
file:write(XML_STRING);
file:close();
end

View File

@@ -0,0 +1,121 @@
-- xml_handler.lua
-- Part of FusionPBX
-- Copyright (C) 2016 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--exits the script if we didn't connect properly
assert(dbh:connected());
--set the xml array
local xml = {}
table.insert(xml, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
table.insert(xml, [[<document type="freeswitch/xml">]]);
table.insert(xml, [[ <section name="configuration">]]);
table.insert(xml, [[ <configuration name="conference.conf" description="Audio Conference">]]);
--start the conference controls
table.insert(xml, [[ <caller-controls>]]);
sql = [[SELECT * FROM v_conference_controls
WHERE control_enabled = 'true' ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference_control] SQL: " .. sql .. "\n");
end
dbh:query(sql, function(field)
conference_control_uuid = field["conference_control_uuid"];
table.insert(xml, [[ <group name="]]..field["control_name"]..[[">]]);
--get the conference control details from the database
sql = [[SELECT * FROM v_conference_control_details
WHERE conference_control_uuid = :conference_control_uuid
AND control_enabled = 'true' ]];
local params = {conference_control_uuid = conference_control_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference_control] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--conference_control_uuid = row["conference_control_uuid"];
--conference_control_detail_uuid = row["conference_control_detail_uuid"];
table.insert(xml, [[ <control digits="]]..row["control_digits"]..[[" action="]]..row["control_action"]..[[" data="]]..row["control_data"]..[["/>]]);
end);
table.insert(xml, [[ </group>]]);
end);
table.insert(xml, [[ </caller-controls>]]);
--start the conference profiles
table.insert(xml, [[ <profiles>]]);
sql = [[SELECT * FROM v_conference_profiles
WHERE profile_enabled = 'true' ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference_profiles] SQL: " .. sql .. "\n");
end
dbh:query(sql, function(field)
conference_profile_uuid = field["conference_profile_uuid"];
table.insert(xml, [[ <profile name="]]..field["profile_name"]..[[">]]);
--get the conference profile parameters from the database
sql = [[SELECT * FROM v_conference_profile_params
WHERE conference_profile_uuid = :conference_profile_uuid
AND profile_param_enabled = 'true' ]];
local params = {conference_profile_uuid = conference_profile_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference_profiles] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--conference_profile_uuid = row["conference_profile_uuid"];
--conference_profile_param_uuid = row["conference_profile_param_uuid"];
--profile_param_description = row["profile_param_description"];
table.insert(xml, [[ <param name="]]..row["profile_param_name"]..[[" value="]]..row["profile_param_value"]..[["/>]]);
end);
table.insert(xml, [[ </profile>]]);
end);
table.insert(xml, [[ </profiles>]]);
--set the xml array and then concatenate the array to a string
table.insert(xml, [[ </configuration>]]);
table.insert(xml, [[ </section>]]);
table.insert(xml, [[</document>]]);
XML_STRING = table.concat(xml, "\n");
if (debug["xml_string"]) then
freeswitch.consoleLog("notice", "[xml_handler] XML_STRING: " .. XML_STRING .. "\n");
end
--send the xml to the console
if (debug["xml_string"]) then
local file = assert(io.open(temp_dir .."/conference.conf.xml", "w"));
file:write(XML_STRING);
file:close();
end

View File

@@ -0,0 +1,303 @@
-- xml_handler.lua
-- Part of FusionPBX
-- Copyright (C) 2016-2018 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--get the ivr name
ivr_menu_uuid = params:getHeader("Menu-Name");
local log = require "resources.functions.log".ivr_menu
--get the cache
local cache = require "resources.functions.cache"
local ivr_menu_cache_key = "configuration:ivr.conf:" .. ivr_menu_uuid
XML_STRING, err = cache.get(ivr_menu_cache_key)
--set the cache
if not XML_STRING then
--log cache error
if (debug["cache"]) then
freeswitch.consoleLog("warning", "[xml_handler] " .. ivr_menu_cache_key .. " can not be get from the cache: " .. tostring(err) .. "\n");
end
--required includes
local Database = require "resources.functions.database"
local Settings = require "resources.functions.lazy_settings"
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--set the sound prefix
sound_prefix = sounds_dir.."/${default_language}/${default_dialect}/${default_voice}/";
--connect to the database
local dbh = Database.new('system');
--exits the script if we didn't connect properly
assert(dbh:connected());
--get the ivr menu from the database
local sql = [[SELECT * FROM v_ivr_menus
WHERE ivr_menu_uuid = :ivr_menu_uuid
AND ivr_menu_enabled = 'true' ]];
local params = {ivr_menu_uuid = ivr_menu_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[ivr_menu] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
domain_uuid = row["domain_uuid"];
ivr_menu_name = row["ivr_menu_name"];
ivr_menu_extension = row["ivr_menu_extension"];
ivr_menu_greet_long = row["ivr_menu_greet_long"];
ivr_menu_greet_short = row["ivr_menu_greet_short"];
ivr_menu_invalid_sound = row["ivr_menu_invalid_sound"];
ivr_menu_exit_sound = row["ivr_menu_exit_sound"];
ivr_menu_confirm_macro = row["ivr_menu_confirm_macro"];
ivr_menu_confirm_key = row["ivr_menu_confirm_key"];
ivr_menu_tts_engine = row["ivr_menu_tts_engine"];
ivr_menu_tts_voice = row["ivr_menu_tts_voice"];
ivr_menu_confirm_attempts = row["ivr_menu_confirm_attempts"];
ivr_menu_timeout = row["ivr_menu_timeout"];
--ivr_menu_exit_app = row["ivr_menu_exit_app"];
--ivr_menu_exit_data = row["ivr_menu_exit_data"];
ivr_menu_inter_digit_timeout = row["ivr_menu_inter_digit_timeout"];
ivr_menu_max_failures = row["ivr_menu_max_failures"];
ivr_menu_max_timeouts = row["ivr_menu_max_timeouts"];
ivr_menu_digit_len = row["ivr_menu_digit_len"];
ivr_menu_direct_dial = row["ivr_menu_direct_dial"];
ivr_menu_ringback = row["ivr_menu_ringback"];
ivr_menu_cid_prefix = row["ivr_menu_cid_prefix"];
ivr_menu_description = row["ivr_menu_description"];
end);
local settings = Settings.new(dbh, domain_name, domain_uuid)
local storage_type = settings:get('recordings', 'storage_type', 'text')
local storage_path = settings:get('recordings', 'storage_path', 'text')
if (storage_path ~= nil) then
storage_path = storage_path:gsub("${domain_name}", domain_name)
storage_path = storage_path:gsub("${domain_uuid}", domain_uuid)
end
--get the recordings from the database
ivr_menu_greet_long_is_base64 = false;
ivr_menu_greet_short_is_base64 = false;
ivr_menu_invalid_sound_is_base64 = false;
ivr_menu_exit_sound_is_base64 = false;
if (storage_type == "base64") then
--include the file io
local file = require "resources.functions.file"
--connect to db
local dbh = Database.new('system', 'base64/read');
--base path for recordings
local base_path = recordings_dir.."/"..domain_name
--function to get recording to local fs
local function load_record(name)
local path = base_path .. "/" .. name;
local is_base64 = false;
if not file_exists(path) then
local sql = "SELECT recording_base64 FROM v_recordings " ..
"WHERE domain_uuid = :domain_uuid " ..
"AND recording_filename = :name "
local params = {domain_uuid = domain_uuid, name = name};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[ivr_menu] SQL: "..sql.."; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
--save the recording to the file system
if #row.recording_base64 > 32 then
is_base64 = true;
file.write_base64(path, row.recording_base64);
--add the full path and file name
name = path;
end
end);
end
return name, is_base64
end
--greet long
if #ivr_menu_greet_long > 1 then
ivr_menu_greet_long, ivr_menu_greet_long_is_base64 = load_record(ivr_menu_greet_long)
end
--greet short
if #ivr_menu_greet_short > 1 then
ivr_menu_greet_short, ivr_menu_greet_short_is_base64 = load_record(ivr_menu_greet_short)
end
--invalid sound
if #ivr_menu_invalid_sound > 1 then
ivr_menu_invalid_sound, ivr_menu_invalid_sound_is_base64 = load_record(ivr_menu_invalid_sound)
end
--exit sound
if #ivr_menu_exit_sound > 1 then
ivr_menu_exit_sound, ivr_menu_exit_sound_is_base64 = load_record(ivr_menu_exit_sound)
end
dbh:release()
elseif (storage_type == "http_cache") then
--add the path to file name
ivr_menu_greet_long = storage_path.."/"..ivr_menu_greet_long;
ivr_menu_greet_short = storage_path.."/"..ivr_menu_greet_short;
ivr_menu_invalid_sound = storage_path.."/"..ivr_menu_invalid_sound;
ivr_menu_exit_sound = storage_path.."/"..ivr_menu_exit_sound;
end
--greet long
if (not ivr_menu_greet_long_is_base64 and not file_exists(ivr_menu_greet_long)) then
if (file_exists(recordings_dir.."/"..domain_name.."/"..ivr_menu_greet_long)) then
ivr_menu_greet_long = recordings_dir.."/"..domain_name.."/"..ivr_menu_greet_long;
elseif (file_exists(sounds_dir.."/en/us/callie/8000/"..ivr_menu_greet_long)) then
ivr_menu_greet_long = sounds_dir.."/${default_language}/${default_dialect}/${default_voice}/"..ivr_menu_greet_long;
end
end
--greet short
if (string.len(ivr_menu_greet_short) > 1) then
if (not ivr_menu_greet_short_is_base64 and not file_exists(ivr_menu_greet_short)) then
if (file_exists(recordings_dir.."/"..domain_name.."/"..ivr_menu_greet_short)) then
ivr_menu_greet_short = recordings_dir.."/"..domain_name.."/"..ivr_menu_greet_short;
elseif (file_exists(sounds_dir.."/en/us/callie/8000/"..ivr_menu_greet_short)) then
ivr_menu_greet_short = sounds_dir.."/${default_language}/${default_dialect}/${default_voice}/"..ivr_menu_greet_short;
end
end
else
ivr_menu_greet_short = ivr_menu_greet_long;
end
--invalid sound
if (not ivr_menu_invalid_sound_is_base64 and not file_exists(ivr_menu_invalid_sound)) then
if (file_exists(recordings_dir.."/"..domain_name.. "/"..ivr_menu_invalid_sound)) then
ivr_menu_invalid_sound = recordings_dir.."/"..domain_name.."/"..ivr_menu_invalid_sound;
elseif (file_exists(sounds_dir.."/en/us/callie/8000/"..ivr_menu_invalid_sound)) then
ivr_menu_invalid_sound = sounds_dir.."/${default_language}/${default_dialect}/${default_voice}/"..ivr_menu_invalid_sound;
end
end
--exit sound
if (not ivr_menu_exit_sound_is_base64 and not file_exists(ivr_menu_exit_sound)) then
if (file_exists(recordings_dir.."/"..ivr_menu_exit_sound)) then
if (ivr_menu_exit_sound ~= nil and ivr_menu_exit_sound ~= "") then
ivr_menu_exit_sound = recordings_dir.."/"..domain_name.."/"..ivr_menu_exit_sound;
end
elseif (file_exists(sounds_dir.."/en/us/callie/8000/"..ivr_menu_exit_sound)) then
ivr_menu_exit_sound = sounds_dir.."/${default_language}/${default_dialect}/${default_voice}/"..ivr_menu_exit_sound;
end
end
--start the xml array
local xml = {}
table.insert(xml, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
table.insert(xml, [[<document type="freeswitch/xml">]]);
table.insert(xml, [[ <section name="configuration">]]);
table.insert(xml, [[ <configuration name="ivr.conf" description="IVR Menus">]]);
table.insert(xml, [[ <menus>]]);
table.insert(xml, [[ <menu name="]]..ivr_menu_uuid..[[" description="]]..ivr_menu_name..[[" ]]);
table.insert(xml, [[ greet-long="]]..ivr_menu_greet_long..[[" ]]);
table.insert(xml, [[ greet-short="]]..ivr_menu_greet_short..[[" ]]);
table.insert(xml, [[ invalid-sound="]]..ivr_menu_invalid_sound..[[" ]]);
table.insert(xml, [[ exit-sound="]]..ivr_menu_exit_sound..[[" ]]);
table.insert(xml, [[ confirm-macro="]]..ivr_menu_confirm_macro..[[" ]]);
table.insert(xml, [[ confirm-key="]]..ivr_menu_confirm_key..[[" ]]);
table.insert(xml, [[ tts-engine="]]..ivr_menu_tts_engine..[[" ]]);
table.insert(xml, [[ tts-voice="]]..ivr_menu_tts_voice..[[" ]]);
table.insert(xml, [[ confirm-attempts="]]..ivr_menu_confirm_attempts..[[" ]]);
table.insert(xml, [[ timeout="]]..ivr_menu_timeout..[[" ]]);
table.insert(xml, [[ inter-digit-timeout="]]..ivr_menu_inter_digit_timeout..[[" ]]);
table.insert(xml, [[ max-failures="]]..ivr_menu_max_failures..[[" ]]);
table.insert(xml, [[ max-timeouts="]]..ivr_menu_max_timeouts..[[" ]]);
table.insert(xml, [[ digit-len="]]..ivr_menu_digit_len..[[" ]]);
table.insert(xml, [[ >]]);
--get the ivr menu options
local sql = [[SELECT * FROM v_ivr_menu_options WHERE ivr_menu_uuid = :ivr_menu_uuid ORDER BY ivr_menu_option_order asc ]];
local params = {ivr_menu_uuid = ivr_menu_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[ivr_menu] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(r)
ivr_menu_option_digits = r.ivr_menu_option_digits
ivr_menu_option_action = r.ivr_menu_option_action
ivr_menu_option_param = r.ivr_menu_option_param
ivr_menu_option_description = r.ivr_menu_option_description
table.insert(xml, [[ <entry action="]]..ivr_menu_option_action..[[" digits="]]..ivr_menu_option_digits..[[" param="]]..ivr_menu_option_param..[[" description="]]..ivr_menu_option_description..[["/>]]);
end);
--direct dial
if (ivr_menu_direct_dial == "true") then
table.insert(xml, [[ <entry action="menu-exec-app" digits="/^(\d{2,11})$/" param="set ${cond(${user_exists id $1 ]]..domain_name..[[} == true ? user_exists=true : user_exists=false)}" description="direct dial"/>\n]]);
table.insert(xml, [[ <entry action="menu-exec-app" digits="/^(\d{2,11})$/" param="playback ${cond(${user_exists} == true ? ]]..sound_prefix..[[ivr/ivr-call_being_transferred.wav : ]]..sound_prefix..[[ivr/ivr-that_was_an_invalid_entry.wav)}" description="direct dial"/>\n]]);
table.insert(xml, [[ <entry action="menu-exec-app" digits="/^(\d{2,11})$/" param="transfer ${cond(${user_exists} == true ? $1 XML ]]..domain_name..[[)}" description="direct dial"/>\n]]);
end
--close the extension tag if it was left open
table.insert(xml, [[ </menu>]]);
table.insert(xml, [[ </menus>]]);
table.insert(xml, [[ </configuration>]]);
table.insert(xml, [[ </section>]]);
table.insert(xml, [[</document>]]);
XML_STRING = table.concat(xml, "\n");
if (debug["xml_string"]) then
freeswitch.consoleLog("notice", "[xml_handler] XML_STRING: " .. XML_STRING .. "\n");
end
--close the database connection
dbh:release();
--freeswitch.consoleLog("notice", "[xml_handler]"..api:execute("eval ${dsn}"));
--set the cache
local ok, err = cache.set(ivr_menu_uuid, XML_STRING, expire["ivr"]);
if debug["cache"] then
if ok then
freeswitch.consoleLog("notice", "[xml_handler] " .. ivr_menu_uuid .. " stored in the cache\n");
else
freeswitch.consoleLog("warning", "[xml_handler] " .. ivr_menu_uuid .. " can not be stored in the cache: " .. tostring(err) .. "\n");
end
end
--send the xml to the console
if (debug["xml_string"]) then
local file = assert(io.open(temp_dir .. "/ivr-"..ivr_menu_uuid..".conf.xml", "w"));
file:write(XML_STRING);
file:close();
end
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. ivr_menu_cache_key .. " source: database\n");
end
else
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. ivr_menu_cache_key .. " source: cache\n");
end
end --if XML_STRING

View File

@@ -0,0 +1,150 @@
--get the cache
local cache = require "resources.functions.cache"
local local_stream_cache_key = "configuration:local_stream.conf"
XML_STRING, err = cache.get(local_stream_cache_key)
--set the cache
if not XML_STRING then
--log the cache error
if (debug["cache"]) then
freeswitch.consoleLog("warning", "[xml_handler] configuration:local_stream.conf can not be get from cache: " .. tostring(err) .. "\n");
end
--set a default value
if (expire["default"] == nil) then
expire["default"]= "3600";
end
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--exits the script if we didn't connect properly
assert(dbh:connected());
--start the xml array
local xml = {}
table.insert(xml, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
table.insert(xml, [[<document type="freeswitch/xml">]]);
table.insert(xml, [[ <section name="configuration">]]);
table.insert(xml, [[ <configuration name="local_stream.conf" description="stream files from local dir">]]);
--run the query
sql = "select d.domain_name, s.* "
sql = sql .. "from v_music_on_hold as s left outer join v_domains as d "
sql = sql .. "on d.domain_uuid = s.domain_uuid "
sql = sql .. "order by s.music_on_hold_name asc "
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n");
end
x = 0;
dbh:query(sql, function(row)
--combine the name, domain_name and the rate
name = '';
if (row.domain_uuid ~= nil and string.len(row.domain_uuid) > 0) then
name = row.domain_name..'/';
end
name = name .. row.music_on_hold_name;
if (row.music_on_hold_rate ~= nil and #row.music_on_hold_rate > 0) then
name = name .. '/' .. row.music_on_hold_rate;
end
--replace the variable with the path to the sounds directory
music_on_hold_path = row.music_on_hold_path:gsub("$${sounds_dir}", sounds_dir);
--set the rate
rate = row.music_on_hold_rate;
if rate == '' then
rate = '48000';
end
--add the full path to the chime list
chime_list = row.music_on_hold_chime_list;
if (chime_list ~= nil) then
chime_array = explode(",", chime_list);
chime_list = "";
for k,v in pairs(chime_array) do
f = explode("/", v);
if (f[1] ~= nil and f[2] ~= nil and file_exists(sounds_dir.."/en/us/callie/"..f[1].."/"..rate.."/"..f[2])) then
chime_list = chime_list .. sounds_dir.."/en/us/callie/"..v;
else
chime_list = chime_list .. v;
end
end
end
--set the default timer name to soft
if (row.music_on_hold_timer_name == nil or row.music_on_hold_timer_name == '') then
timer_name = "soft";
else
timer_name = row.music_on_hold_timer_name;
end
--build the xml ]]..row.music_on_hold_name..[["
table.insert(xml, [[ <directory name="]]..name..[[" uuid="]]..row.music_on_hold_uuid..[[" path="]]..music_on_hold_path..[[">]]);
table.insert(xml, [[ <param name="rate" value="]]..rate..[["/>]]);
table.insert(xml, [[ <param name="shuffle" value="]]..row.music_on_hold_shuffle..[["/>]]);
table.insert(xml, [[ <param name="channels" value="1"/>]]);
table.insert(xml, [[ <param name="interval" value="20"/>]]);
table.insert(xml, [[ <param name="timer-name" value="]]..timer_name..[["/>]]);
if (chime_list ~= nil) then
table.insert(xml, [[ <param name="chime-list" value="]]..chime_list..[["/>]]);
end
if (row.music_on_hold_chime_freq ~= nil) then
table.insert(xml, [[ <param name="chime-freq" value="]]..row.music_on_hold_chime_freq..[["/>]]);
end
if (row.music_on_hold_chime_max ~= nil) then
table.insert(xml, [[ <param name="chime-max" value="]]..row.music_on_hold_chime_max..[["/>]]);
end
table.insert(xml, [[ </directory>]]);
end)
--close the extension tag if it was left open
table.insert(xml, [[ </configuration>]]);
table.insert(xml, [[ </section>]]);
table.insert(xml, [[</document>]]);
XML_STRING = table.concat(xml, "\n");
if (debug["xml_string"]) then
freeswitch.consoleLog("notice", "[xml_handler] XML_STRING: " .. XML_STRING .. "\n");
end
--close the database connection
dbh:release();
--set the cache
local ok, err = cache.set(local_stream_cache_key, XML_STRING, expire["default"]);
if debug["cache"] then
if ok then
freeswitch.consoleLog("notice", "[xml_handler] " .. local_stream_cache_key .. " stored in the cache\n");
else
freeswitch.consoleLog("warning", "[xml_handler] " .. local_stream_cache_key .. " can not be stored in the cache: " .. tostring(err) .. "\n");
end
end
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. local_stream_cache_key .. " source: database\n");
end
else
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. local_stream_cache_key .. " source: cache\n");
end
end --if XML_STRING
--send the xml to the console
if (debug["xml_string"]) then
local file = assert(io.open(temp_dir .. "/local_stream.conf.xml", "w"));
file:write(XML_STRING);
file:close();
end

View File

@@ -0,0 +1,321 @@
-- xml_handler.lua
-- Part of FusionPBX
-- Copyright (C) 2013 - 2018 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--get the cache
local cache = require "resources.functions.cache"
local hostname = trim(api:execute("switchname", ""))
local sofia_cache_key = "configuration:sofia.conf:" .. hostname
XML_STRING, err = cache.get(sofia_cache_key)
--set the cache
if not XML_STRING then
--log cache error
if (debug["cache"]) then
freeswitch.consoleLog("warning", "[xml_handler] " .. sofia_cache_key .. " can not be get from the cache: " .. tostring(err) .. "\n");
end
--set a default value
if (expire["sofia"] == nil) then
expire["sofia"]= "3600";
end
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--exits the script if we didn't connect properly
assert(dbh:connected());
--get the domain_uuid
if (domain_uuid == nil) then
--get the domain_uuid
if (domain_name ~= nil) then
sql = "SELECT domain_uuid FROM v_domains ";
sql = sql .. "WHERE domain_name = :domain_name";
local params = {domain_name = domain_name};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
dbh:query(sql, params, function(row)
domain_uuid = row.domain_uuid;
end);
end
end
--get the variables
vars = trim(api:execute("global_getvar", ""));
--start the xml array
local xml = {}
table.insert(xml, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
table.insert(xml, [[<document type="freeswitch/xml">]]);
table.insert(xml, [[ <section name="configuration">]]);
table.insert(xml, [[ <configuration name="sofia.conf" description="sofia Endpoint">]]);
table.insert(xml, [[ <global_settings>]]);
table.insert(xml, [[ <param name="log-level" value="0"/>]]);
--table.insert(xml, [[ <param name="auto-restart" value="false"/>]]);
table.insert(xml, [[ <param name="debug-presence" value="0"/>]]);
--table.insert(xml, [[ <param name="capture-server" value="udp:homer.domain.com:5060"/>]]);
table.insert(xml, [[ </global_settings>]]);
table.insert(xml, [[ <profiles>]]);
--set defaults
previous_sip_profile_name = "";
profile_tag_status = "closed";
--run the query
sql = "select p.sip_profile_uuid, p.sip_profile_name, p.sip_profile_description, s.sip_profile_setting_name, s.sip_profile_setting_value ";
sql = sql .. "from v_sip_profiles as p, v_sip_profile_settings as s ";
sql = sql .. "where s.sip_profile_setting_enabled = 'true' ";
sql = sql .. "and p.sip_profile_enabled = 'true' ";
sql = sql .. "and (p.sip_profile_hostname = :hostname or p.sip_profile_hostname is null or p.sip_profile_hostname = '') ";
sql = sql .. "and p.sip_profile_uuid = s.sip_profile_uuid ";
sql = sql .. "order by p.sip_profile_name asc ";
local params = {hostname = hostname};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "; params: " .. json.encode(params) .. "\n");
end
x = 0;
dbh:query(sql, params, function(row)
--set as variables
sip_profile_uuid = row.sip_profile_uuid;
sip_profile_name = row.sip_profile_name;
--sip_profile_description = row.sip_profile_description;
sip_profile_setting_name = row.sip_profile_setting_name;
sip_profile_setting_value = row.sip_profile_setting_value;
--open xml tag
if (sip_profile_name ~= previous_sip_profile_name) then
if (x > 1) then
table.insert(xml, [[ </settings>]]);
table.insert(xml, [[ </profile>]]);
end
table.insert(xml, [[ <profile name="]]..sip_profile_name..[[">]]);
table.insert(xml, [[ <aliases>]]);
table.insert(xml, [[ </aliases>]]);
table.insert(xml, [[ <gateways>]]);
--table.insert(xml, [[ <X-PRE-PROCESS cmd="include" data="]]..sip_profile_name..[[/*.xml"/>]]);
--get the gateways
sql = "select * from v_gateways as g, v_domains as d ";
sql = sql .. "where g.profile = :profile ";
sql = sql .. "and g.enabled = 'true' ";
sql = sql .. "and (g.domain_uuid = d.domain_uuid or g.domain_uuid is null) ";
sql = sql .. "and (g.hostname = :hostname or g.hostname is null or g.hostname = '') ";
local params = {profile = sip_profile_name, hostname = hostname};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "; params:" .. json.encode(params) .. "\n");
end
x = 0;
dbh:query(sql, params, function(field)
table.insert(xml, [[ <gateway name="]] .. string.lower(field.gateway_uuid) .. [[">]]);
if (string.len(field.username) > 0) then
table.insert(xml, [[ <param name="username" value="]] .. field.username .. [["/>]]);
end
if (string.len(field.distinct_to) > 0) then
table.insert(xml, [[ <param name="distinct-to" value="]] .. field.distinct_to .. [["/>]]);
end
if (string.len(field.auth_username) > 0) then
table.insert(xml, [[ <param name="auth-username" value="]] .. field.auth_username .. [["/>]]);
end
if (string.len(field.password) > 0) then
table.insert(xml, [[ <param name="password" value="]] .. field.password .. [["/>]]);
end
if (string.len(field.realm) > 0) then
table.insert(xml, [[ <param name="realm" value="]] .. field.realm .. [["/>]]);
end
if (string.len(field.from_user) > 0) then
table.insert(xml, [[ <param name="from-user" value="]] .. field.from_user .. [["/>]]);
end
if (string.len(field.from_domain) > 0) then
table.insert(xml, [[ <param name="from-domain" value="]] .. field.from_domain .. [["/>]]);
end
if (string.len(field.proxy) > 0) then
table.insert(xml, [[ <param name="proxy" value="]] .. field.proxy .. [["/>]]);
end
if (string.len(field.register_proxy) > 0) then
table.insert(xml, [[ <param name="register-proxy" value="]] .. field.register_proxy .. [["/>]]);
end
if (string.len(field.outbound_proxy) > 0) then
table.insert(xml, [[ <param name="outbound-proxy" value="]] .. field.outbound_proxy .. [["/>]]);
end
if (string.len(field.expire_seconds) > 0) then
table.insert(xml, [[ <param name="expire-seconds" value="]] .. field.expire_seconds .. [["/>]]);
end
if (string.len(field.register) > 0) then
table.insert(xml, [[ <param name="register" value="]] .. field.register .. [["/>]]);
end
if (field.register_transport) then
if (field.register_transport == "udp") then
table.insert(xml, [[ <param name="register-transport" value="udp"/>]]);
elseif (field.register_transport == "tcp") then
table.insert(xml, [[ <param name="register-transport" value="tcp"/>]]);
elseif (field.register_transport == "tls") then
table.insert(xml, [[ <param name="register-transport" value="tls"/>]]);
table.insert(xml, [[ <param name="contact-params" value="transport=tls"/>]]);
else
table.insert(xml, [[ <param name="register-transport" value="udp"/>]]);
end
end
if (string.len(field.retry_seconds) > 0) then
table.insert(xml, [[ <param name="retry-seconds" value="]] .. field.retry_seconds .. [["/>]]);
end
if (string.len(field.extension) > 0) then
table.insert(xml, [[ <param name="extension" value="]] .. field.extension .. [["/>]]);
end
if (string.len(field.ping) > 0) then
table.insert(xml, [[ <param name="ping" value="]] .. field.ping .. [["/>]]);
end
if (string.len(field.context) > 0) then
table.insert(xml, [[ <param name="context" value="]] .. field.context .. [["/>]]);
end
if (string.len(field.caller_id_in_from) > 0) then
table.insert(xml, [[ <param name="caller-id-in-from" value="]] .. field.caller_id_in_from .. [["/>]]);
end
if (string.len(field.supress_cng) > 0) then
table.insert(xml, [[ <param name="supress-cng" value="]] .. field.supress_cng .. [["/>]]);
end
if (string.len(field.extension_in_contact) > 0) then
table.insert(xml, [[ <param name="extension-in-contact" value="]] .. field.extension_in_contact .. [["/>]]);
end
table.insert(xml, [[ <variables>]]);
if (string.len(field.sip_cid_type) > 0) then
table.insert(xml, [[ <variable name="sip_cid_type" value="]] .. field.sip_cid_type .. [["/>]]);
end
table.insert(xml, [[ </variables>]]);
table.insert(xml, [[ </gateway>]]);
end)
table.insert(xml, [[ </gateways>]]);
table.insert(xml, [[ <domains>]]);
--add sip profile domain: name, alias, and parse
table.insert(xml, [[ <!-- indicator to parse the directory for domains with parse="true" to get gateways-->]]);
table.insert(xml, [[ <!--<domain name="$${domain}" parse="true"/>-->]]);
table.insert(xml, [[ <!-- indicator to parse the directory for domains with parse="true" to get gateways and alias every domain to this profile -->]]);
table.insert(xml, [[ <!--<domain name="all" alias="true" parse="true"/>-->]]);
sql = "SELECT sip_profile_domain_name, sip_profile_domain_alias, sip_profile_domain_parse FROM v_sip_profile_domains ";
sql = sql .. "WHERE sip_profile_uuid = :sip_profile_uuid";
local params = {sip_profile_uuid = sip_profile_uuid};
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "; sip_profile_uuid:" .. sip_profile_uuid .. "\n");
end
dbh:query(sql, params, function(row)
name = row.sip_profile_domain_name;
alias = row.sip_profile_domain_alias;
parse = row.sip_profile_domain_parse;
if (name == nil or name == '') then name = 'false'; end
if (alias == nil or alias == '') then alias = 'false'; end
if (parse == nil or parse == '') then parse = 'false'; end
table.insert(xml, [[ <domain name="]] .. name .. [[" alias="]] .. alias .. [[" parse="]] .. parse .. [["/>]]);
end);
table.insert(xml, [[ </domains>]]);
table.insert(xml, [[ <settings>]]);
profile_tag_status = "open";
end
--loop through the var array
for line in (vars.."\n"):gmatch"(.-)\n" do
if (line) then
pos = string.find(line, "=", 0, true);
--name = string.sub( line, 0, pos-1);
--value = string.sub( line, pos+1);
sip_profile_setting_value = sip_profile_setting_value:gsub("%$%${"..string.sub( line, 0, pos-1).."}", string.sub( line, pos+1));
end
end
--remove $ and replace with ""
--if (sip_profile_setting_value) then
-- sip_profile_setting_value = sip_profile_setting_value:gsub("%$", "");
--end
--set the parameters
if (sip_profile_setting_name) then
table.insert(xml, [[ <param name="]]..sip_profile_setting_name..[[" value="]]..sip_profile_setting_value..[["/>]]);
end
--set the previous value
previous_sip_profile_name = sip_profile_name;
--increment the value of x
x = x + 1;
end)
--close the extension tag if it was left open
if (profile_tag_status == "open") then
table.insert(xml, [[ </settings>]]);
table.insert(xml, [[ </profile>]]);
profile_tag_status = "close";
end
table.insert(xml, [[ </profiles>]]);
table.insert(xml, [[ </configuration>]]);
table.insert(xml, [[ </section>]]);
table.insert(xml, [[</document>]]);
XML_STRING = table.concat(xml, "\n");
if (debug["xml_string"]) then
freeswitch.consoleLog("notice", "[xml_handler] XML_STRING: " .. XML_STRING .. "\n");
end
--close the database connection
dbh:release();
--set the cache
local ok, err = cache.set(sofia_cache_key, XML_STRING, expire["sofia"])
if debug["cache"] then
if ok then
freeswitch.consoleLog("notice", "[xml_handler] " .. sofia_cache_key .. " stored in the cache\n");
else
freeswitch.consoleLog("warning", "[xml_handler] " .. sofia_cache_key .. " can not be stored in the cache: " .. tostring(err) .. "\n");
end
end
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. sofia_cache_key .. " source: database\n");
end
else
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. sofia_cache_key .. " source: cache\n");
end
end --if XML_STRING
--send the xml to the console
if (debug["xml_string"]) then
local file = assert(io.open(temp_dir .. "/sofia.conf.xml", "w"));
file:write(XML_STRING);
file:close();
end

View File

@@ -0,0 +1,141 @@
-- xml_handler.lua
-- Part of FusionPBX
-- Copyright (C) 2018 Mark J Crane <markjcrane@fusionpbx.com>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--get the cache
local cache = require "resources.functions.cache"
local translate_cache_key = "configuration:translate.conf"
XML_STRING, err = cache.get(translate_cache_key)
--set the cache
if not XML_STRING then
--log cache error
if (debug["cache"]) then
freeswitch.consoleLog("warning", "[xml_handler] " .. translate_cache_key .. " can not be get from the cache: " .. tostring(err) .. "\n");
end
--log cache error
if (debug["cache"]) then
freeswitch.consoleLog("warning", "[xml_handler] configuration:translate.conf can not be get from the cache: " .. tostring(err) .. "\n");
end
--set a default value
if (expire["translate"] == nil) then
expire["translate"]= "3600";
end
--connect to the database
local Database = require "resources.functions.database";
dbh = Database.new('system');
--include json library
local json
if (debug["sql"]) then
json = require "resources.functions.lunajson"
end
--exits the script if we didn't connect properly
assert(dbh:connected());
--start the xml array
local xml = {}
table.insert(xml, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
table.insert(xml, [[<document type="freeswitch/xml">]]);
table.insert(xml, [[ <section name="configuration">]]);
table.insert(xml, [[ <configuration name="translate.conf" description="Number Translation Rules" autogenerated="true">]]);
table.insert(xml, [[ <profiles>]]);
--run the query
sql = "select * from v_number_translations ";
sql = sql .. "order by number_translation_name asc ";
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n");
end
x = 0;
dbh:query(sql, function(row)
--list open tag
table.insert(xml, [[ <profile name="]]..row.number_translation_name..[[" description="]]..row.number_translation_description..[[">]]);
--get the nodes
sql = "select * from v_number_translation_details ";
sql = sql .. "where number_translation_uuid = :number_translation_uuid ";
sql = sql .. "order by number_translation_detail_order asc ";
local params = {number_translation_uuid = row.number_translation_uuid}
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n");
end
x = 0;
dbh:query(sql, params, function(field)
if (string.len(field.number_translation_detail_regex) > 0) then
table.insert(xml, [[ <rule regex="]] .. field.number_translation_detail_regex .. [[" replace="]] .. field.number_translation_detail_replace .. [[" />]]);
end
end)
--list close tag
table.insert(xml, [[ </profile>]]);
end)
--close the extension tag if it was left open
table.insert(xml, [[ </profiles>]]);
table.insert(xml, [[ </configuration>]]);
table.insert(xml, [[ </section>]]);
table.insert(xml, [[</document>]]);
XML_STRING = table.concat(xml, "\n");
if (debug["xml_string"]) then
freeswitch.consoleLog("notice", "[xml_handler] XML_STRING: " .. XML_STRING .. "\n");
end
--close the database connection
dbh:release();
--set the cache
local ok, err = cache.set(translate_cache_key, XML_STRING, expire["translate"]);
if debug["cache"] then
if ok then
freeswitch.consoleLog("notice", "[xml_handler] " .. translate_cache_key .. " stored in the cache\n");
else
freeswitch.consoleLog("warning", "[xml_handler] " .. translate_cache_key .. " can not be stored in the cache: " .. tostring(err) .. "\n");
end
end
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. translate_cache_key .. " source: database\n");
end
else
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. translate_cache_key .. " source: cache\n");
end
end --if XML_STRING
--send the xml to the console
if (debug["xml_string"]) then
local file = assert(io.open(temp_dir .. "/translate.conf.xml", "w"));
file:write(XML_STRING);
file:close();
end

Some files were not shown because too many files have changed in this diff Show More