From 03e62d9e9170ec7f84269b27b7cdeea86053b436 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Sat, 11 Jun 2016 16:33:16 +0300 Subject: [PATCH] Add. Support sound prompts on call flow. (#1645) This file uses new way to load recordings from DB. --- app/call_flows/app_config.php | 8 + app/call_flows/app_languages.php | 40 ++++ app/call_flows/call_flow_edit.php | 166 +++++++++++++++- resources/install/scripts/call_flow.lua | 179 ++++++++++++++---- .../scripts/resources/functions/file.lua | 57 ++++++ 5 files changed, 411 insertions(+), 39 deletions(-) create mode 100644 resources/install/scripts/resources/functions/file.lua diff --git a/app/call_flows/app_config.php b/app/call_flows/app_config.php index aeaa615769..475f61b1c0 100644 --- a/app/call_flows/app_config.php +++ b/app/call_flows/app_config.php @@ -132,4 +132,12 @@ $apps[$x]['db'][$y]['fields'][$z]['type'] = "text"; $apps[$x]['db'][$y]['fields'][$z]['description']['en'] = "Enter the description."; $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = "call_flow_sound_on"; + $apps[$x]['db'][$y]['fields'][$z]['type'] = "text"; + $apps[$x]['db'][$y]['fields'][$z]['description']['en'] = "Select the sound when on."; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = "call_flow_sound_off"; + $apps[$x]['db'][$y]['fields'][$z]['type'] = "text"; + $apps[$x]['db'][$y]['fields'][$z]['description']['en'] = "Select the sound when off."; + $z++; ?> diff --git a/app/call_flows/app_languages.php b/app/call_flows/app_languages.php index f7e220fd14..6ed6cf66ff 100644 --- a/app/call_flows/app_languages.php +++ b/app/call_flows/app_languages.php @@ -120,6 +120,26 @@ $text['label-alternate_destination']['sv-se'] = "Alternativ Destination"; $text['label-alternate_destination']['uk'] = ""; $text['label-alternate_destination']['de-at'] = "Alternatives Ziel"; +$text['label-sound_on']['en-us'] = "Sound"; +$text['label-sound_on']['es-cl'] = ""; +$text['label-sound_on']['pt-pt'] = ""; +$text['label-sound_on']['fr-fr'] = ""; +$text['label-sound_on']['pt-br'] = ""; +$text['label-sound_on']['pl'] = ""; +$text['label-sound_on']['sv-se'] = ""; +$text['label-sound_on']['uk'] = ""; +$text['label-sound_on']['de-at'] = ""; + +$text['label-sound_off']['en-us'] = "Alternate Sound"; +$text['label-sound_off']['es-cl'] = ""; +$text['label-sound_off']['pt-pt'] = ""; +$text['label-sound_off']['fr-fr'] = ""; +$text['label-sound_off']['pt-br'] = ""; +$text['label-sound_off']['pl'] = ""; +$text['label-sound_off']['sv-se'] = ""; +$text['label-sound_off']['uk'] = ""; +$text['label-sound_off']['de-at'] = ""; + $text['header-call_flows']['en-us'] = "Call Flows"; $text['header-call_flows']['es-cl'] = "flujo de Llamada"; $text['header-call_flows']['pt-pt'] = "Fluxo de Chamadas"; @@ -260,4 +280,24 @@ $text['description-alternate_destination']['sv-se'] = "Välj den alternativa des $text['description-alternate_destination']['uk'] = ""; $text['description-alternate_destination']['de-at'] = "Wählen Sie ein alternatives Ziel."; +$text['description-sound_on']['en-us'] = "Select the sound when on."; +$text['description-sound_on']['es-cl'] = ""; +$text['description-sound_on']['pt-pt'] = ""; +$text['description-sound_on']['fr-fr'] = ""; +$text['description-sound_on']['pt-br'] = ""; +$text['description-sound_on']['pl'] = ""; +$text['description-sound_on']['sv-se'] = ""; +$text['description-sound_on']['uk'] = ""; +$text['description-sound_on']['de-at'] = ""; + +$text['description-sound_off']['en-us'] = "Select the sound when off."; +$text['description-sound_off']['es-cl'] = ""; +$text['description-sound_off']['pt-pt'] = ""; +$text['description-sound_off']['fr-fr'] = ""; +$text['description-sound_off']['pt-br'] = ""; +$text['description-sound_off']['pl'] = ""; +$text['description-sound_off']['sv-se'] = ""; +$text['description-sound_off']['uk'] = ""; +$text['description-sound_off']['de-at'] = ""; + ?> \ No newline at end of file diff --git a/app/call_flows/call_flow_edit.php b/app/call_flows/call_flow_edit.php index 1380c69912..1560528bb7 100644 --- a/app/call_flows/call_flow_edit.php +++ b/app/call_flows/call_flow_edit.php @@ -61,6 +61,8 @@ else { $call_flow_anti_label = check_str($_POST["call_flow_anti_label"]); $call_flow_alternate_destination = check_str($_POST["call_flow_alternate_destination"]); $call_flow_description = check_str($_POST["call_flow_description"]); + $call_flow_sound_on = check_str($_POST["call_flow_sound_on"]); + $call_flow_sound_off = check_str($_POST["call_flow_sound_off"]); $dialplan_uuid = check_str($_POST["dialplan_uuid"]); //seperate the action and the param @@ -139,7 +141,9 @@ if (count($_POST) > 0 && strlen($_POST["persistformvar"]) == 0) { $sql .= "call_flow_anti_label, "; $sql .= "call_flow_anti_app, "; $sql .= "call_flow_anti_data, "; - $sql .= "call_flow_description "; + $sql .= "call_flow_description, "; + $sql .= "call_flow_sound_on, "; + $sql .= "call_flow_sound_off "; $sql .= ")"; $sql .= "values "; $sql .= "("; @@ -158,7 +162,9 @@ if (count($_POST) > 0 && strlen($_POST["persistformvar"]) == 0) { $sql .= "'$call_flow_anti_label', "; $sql .= "'$call_flow_anti_app', "; $sql .= "'$call_flow_anti_data', "; - $sql .= "'$call_flow_description' "; + $sql .= "'$call_flow_description', "; + $sql .= "'$call_flow_sound_on', "; + $sql .= "'$call_flow_sound_off' "; $sql .= ")"; $db->exec(check_sql($sql)); unset($sql); @@ -184,7 +190,9 @@ if (count($_POST) > 0 && strlen($_POST["persistformvar"]) == 0) { $sql .= "call_flow_anti_label = '$call_flow_anti_label', "; $sql .= "call_flow_anti_app = '$call_flow_anti_app', "; $sql .= "call_flow_anti_data = '$call_flow_anti_data', "; - $sql .= "call_flow_description = '$call_flow_description' "; + $sql .= "call_flow_description = '$call_flow_description', "; + $sql .= "call_flow_sound_on = '$call_flow_sound_on', "; + $sql .= "call_flow_sound_off = '$call_flow_sound_off' "; $sql .= "where domain_uuid = '$domain_uuid' "; $sql .= "and call_flow_uuid = '$call_flow_uuid'"; $db->exec(check_sql($sql)); @@ -381,6 +389,8 @@ if (count($_POST) > 0 && strlen($_POST["persistformvar"]) == 0) { $call_flow_anti_app = $row["call_flow_anti_app"]; $call_flow_anti_data = $row["call_flow_anti_data"]; $call_flow_description = $row["call_flow_description"]; + $call_flow_sound_on = $row["call_flow_sound_on"]; + $call_flow_sound_off = $row["call_flow_sound_off"]; $dialplan_uuid = $row["dialplan_uuid"]; //if superadmin show both the app and data @@ -416,6 +426,152 @@ if (count($_POST) > 0 && strlen($_POST["persistformvar"]) == 0) { $document['title'] = $text['title-call_flow-add']; } +//get the recordings + $sql = "select recording_name, recording_filename from v_recordings "; + $sql .= "where domain_uuid = '".$_SESSION["domain_uuid"]."' "; + $sql .= "order by recording_name asc "; + $prep_statement = $db->prepare(check_sql($sql)); + $prep_statement->execute(); + $recordings = $prep_statement->fetchAll(PDO::FETCH_ASSOC); + + if (if_group("superadmin")) { + echo "\n"; + echo "\n"; + } + + function sound_select_list($var, $name, $description_name, $load_sound=false) { + global $text, $recordings, $db; + + echo "\n"; + echo "\n"; + echo " ".$text['label-' . $description_name]."\n"; + echo "\n"; + echo "\n"; + + echo "\n"; + echo "
\n"; + echo $text['description-' . $description_name]."\n"; + echo "\n"; + echo "\n"; + } + //show the content echo "
\n"; echo "\n"; @@ -545,6 +701,8 @@ if (count($_POST) > 0 && strlen($_POST["persistformvar"]) == 0) { echo "\n"; echo "\n"; + sound_select_list($call_flow_sound_on, 'call_flow_sound_on', 'sound_on', true); + echo "\n"; echo "\n"; echo "\n"; + sound_select_list($call_flow_sound_off, 'call_flow_sound_off', 'sound_off', true); + echo "\n"; echo "
\n"; echo " ".$text['label-destination']."\n"; @@ -574,6 +732,8 @@ if (count($_POST) > 0 && strlen($_POST["persistformvar"]) == 0) { echo "
\n"; echo " ".$text['label-alternate_destination']."\n"; diff --git a/resources/install/scripts/call_flow.lua b/resources/install/scripts/call_flow.lua index b8cd5f90a9..873ecf31fe 100644 --- a/resources/install/scripts/call_flow.lua +++ b/resources/install/scripts/call_flow.lua @@ -31,35 +31,128 @@ --include config.lua require "resources.functions.config"; ---connect to the database - require "resources.functions.database_handle"; - dbh = database_handle('system'); - - local log = require "resources.functions.log".call_flow - local presence_in = require "resources.functions.presence_in" + local Database = require "resources.functions.database" + local Settings = require "resources.functions.lazy_settings" + local file = require "resources.functions.file" + local log = require "resources.functions.log".call_flow + +--connect to the database + local dbh = Database.new('system'); + +--! @todo move to library +local function basename(file_name) + return (string.match(file_name, "([^/]+)$")) +end + +--! @todo move to library +local function isabspath(file_name) + return string.sub(file_name, 1, 1) == '/' or string.sub(file_name, 2, 1) == ':' +end + +--! @todo move to library +local function find_file(dbh, domain_name, domain_uuid, file_name) + -- if we specify e.g. full path + if isabspath(file_name) and file.exists(file_name) then + log.debugf('found file `%s` in file system', file_name) + return file_name + end + + local file_name_only = basename(file_name) + + local is_base64, found + + if file_name_only == file_name then -- this can be recordings + local full_path = recordings_dir .. "/" .. domain_name .. "/" .. file_name + if file.exists(full_path) then + log.debugf('resolve `%s` as recording `%s`', file_name, full_path) + file_name, found = full_path, true + else -- recordings may be in database + local settings = Settings.new(dbh, domain_name, domain_uuid) + local storage_type = settings:get('recordings', 'storage_type', 'text') or '' + if storage_type == 'base64' then + local sql = "SELECT recording_base64 FROM v_recordings " + .. "WHERE domain_uuid = '" .. domain_uuid .."'" + .. "AND recording_filename = '".. file_name.. "' " + if (debug["sql"]) then + log.notice("SQL: " .. sql) + end + + local dbh = Database.new('system', 'base64/read') + local recording_base64 = dbh:first_value(sql); + dbh:release(); + + if recording_base64 and #recording_base64 > 32 then + log.debugf('resolve `%s` as recording `%s`(base64)', file_name, full_path) + file_name, found, is_base64 = full_path, true, true + file.write_base64(file_name, recording_base64) + end + end + end + end + + if not found then + local sounds_dir + if session then + -- Implemented based on stream.lua. But seems it never works. + -- because if we have file like `digits/1.wav` but full_path is `sounds_dir/digits/XXXX/1.wav` + sounds_dir = session:getVariable("sounds_dir") + 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' + + sounds_dir = sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice + else + --! @todo implement for not sessoin call. + end + + if sounds_dir then + found = file.exists(sounds_dir.. "/" ..file_name_only) + if found then + log.debugf('resolve `%s` as sound `%s`', file_name, found) + file_name, found = found, true + end + end + end + + if not found then + return + end + + return file_name, is_base64 +end + +--! @todo move to library +local function play_file(dbh, domain_name, domain_uuid, file_name) + local full_path, is_base64 = find_file(dbh, domain_name, domain_uuid, file_name) + if not full_path then + log.warningf('Can not find audio file: %s. Try using it in raw mode.', file_name) + full_path = file_name + else + log.noticef('Found `%s` as `%s`%s', file_name, full_path, is_base64 and '(BASE64)' or '') + end + + session:execute("playback", full_path); +end if (session:ready()) then --get the variables - domain_name = session:getVariable("domain_name"); - call_flow_uuid = session:getVariable("call_flow_uuid"); - sounds_dir = session:getVariable("sounds_dir"); - feature_code = session:getVariable("feature_code"); + local domain_name = session:getVariable("domain_name"); + local domain_uuid = session:getVariable("domain_uuid"); + local call_flow_uuid = session:getVariable("call_flow_uuid"); + local sounds_dir = session:getVariable("sounds_dir"); + local feature_code = session:getVariable("feature_code"); --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 + 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'; --get the extension list - sql = "SELECT * FROM v_call_flows where call_flow_uuid = '"..call_flow_uuid.."'" + local sql = "SELECT * FROM v_call_flows where call_flow_uuid = '"..call_flow_uuid.."'" -- .. "and call_flow_enabled = 'true'" --log.notice("SQL: %s", sql); - x = 0; dbh:query(sql, function(row) call_flow_name = row.call_flow_name; call_flow_extension = row.call_flow_extension; @@ -69,11 +162,13 @@ if (session:ready()) then pin_number = row.call_flow_pin_number; call_flow_label = row.call_flow_label; call_flow_anti_label = row.call_flow_anti_label; + call_flow_sound_on = row.call_flow_sound_on or ''; + call_flow_sound_off = row.call_flow_sound_off or ''; if #call_flow_status == 0 then call_flow_status = "true"; end - if (call_flow_status == "true") then + if call_flow_status == "true" then app = row.call_flow_app; data = row.call_flow_data else @@ -84,14 +179,12 @@ if (session:ready()) then if (feature_code == "true") then --if the pin number is provided then require it - if (string.len(pin_number) > 0) then - min_digits = string.len(pin_number); - max_digits = string.len(pin_number)+1; + if #pin_number > 0 then + local min_digits = #pin_number; + local max_digits = #pin_number+1; session:answer(); - digits = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", "phrase:voicemail_enter_pass:#", "", "\\d+"); - if (digits == pin_number) then - --pin is correct - else + local digits = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", "phrase:voicemail_enter_pass:#", "", "\\d+"); + if digits ~= pin_number then session:streamFile("phrase:voicemail_fail_auth:#"); session:hangup("NORMAL_CLEARING"); return; @@ -99,7 +192,7 @@ if (session:ready()) then end --feature code - toggle the status - toggle = (call_flow_status == "true") and "false" or "true" + local toggle = (call_flow_status == "true") and "false" or "true" -- turn the lamp presence_in.turn_lamp( toggle == "false", @@ -107,22 +200,36 @@ if (session:ready()) then call_flow_uuid ); + --active label local active_flow_label = (toggle == "true") and call_flow_label or call_flow_anti_label - --answer and play a tone - session:answer(); - if #active_flow_label > 0 then - api = freeswitch.API(); - reply = api:executeString("uuid_display "..session:get_uuid().." "..active_flow_label); - end - session:execute("sleep", "2000"); - session:execute("playback", "tone_stream://%(200,0,500,600,700)"); + + --play info message + local audio_file = (toggle == "false") and call_flow_sound_on or call_flow_sound_off --show in the console - log.noticef("label=%s,status=%s,uuid=%s", active_flow_label, toggle, call_flow_uuid); + log.noticef("label=%s,status=%s,uuid=%s,audio=%s", active_flow_label, toggle, call_flow_uuid, audio_file) --store in database dbh:query("UPDATE v_call_flows SET call_flow_status = '"..toggle.."' WHERE call_flow_uuid = '"..call_flow_uuid.."'"); + --answer + session:answer(); + + --display label on Phone (if support) + if #active_flow_label > 0 then + local api = freeswitch.API(); + local reply = api:executeString("uuid_display "..session:get_uuid().." "..active_flow_label); + end + + if #audio_file > 0 then + session:sleep(1000); + play_file(dbh, domain_name, domain_uuid, audio_file) + session:sleep(1000); + else + session:sleep(2000); + audio_file = "tone_stream://%(200,0,500,600,700)" + end + --hangup the call session:hangup(); else diff --git a/resources/install/scripts/resources/functions/file.lua b/resources/install/scripts/resources/functions/file.lua new file mode 100644 index 0000000000..7e4eb24924 --- /dev/null +++ b/resources/install/scripts/resources/functions/file.lua @@ -0,0 +1,57 @@ +-- load base64 if exists + pcall(require, "resources.functions.base64") + +-- load logger for file library + local log = require "resources.functions.log".file + +local base64 = base64 + +local function write_file(fname, data, mode) + local file, err = io.open(fname, mode or "wb") + if not file then + log.err("Can not open file to write:" .. tostring(err)) + return nil, err + end + file:write(data) + file:close() + return true +end + +-- decode and write to file +local function write_base64(fname, encoded, mode) + return write_file(fname, base64.decode(encoded), mode) +end + +local function read_file(fname, mode) + local file, err = io.open(fname, mode or "rb") + if not file then + log.err("Can not open file to read:" .. tostring(err)) + return nil, err + end + local data = file:read("*all") + file:close() + return data +end + +-- read file and encode +local function read_base64(fname, mode) + local data, err = read_file(fname, mode) + if not data then return nil, err end + return base64.encode(data) +end + +local function file_exists(name) + local f = io.open(name, "r") + if not f then return end + f:close() + return name +end + +return { + read = read_file; + read_base64 = read_base64; + write = write_file; + write_base64 = write_base64; + exists = file_exists; + remove = os.remove; +} \ No newline at end of file