summaryrefslogtreecommitdiff
path: root/misc/freeswitch
diff options
context:
space:
mode:
Diffstat (limited to 'misc/freeswitch')
-rw-r--r--misc/freeswitch/conf/freeswitch.xml136
-rw-r--r--misc/freeswitch/scripts/common/configuration_table.lua59
-rw-r--r--misc/freeswitch/scripts/dialplan/dialplan.lua16
-rw-r--r--misc/freeswitch/scripts/dialplan/functions.lua25
-rw-r--r--misc/freeswitch/scripts/dialplan/ivr.lua67
-rw-r--r--misc/freeswitch/scripts/dialplan/voicemail.lua378
-rw-r--r--misc/freeswitch/scripts/dialplan_default.lua7
7 files changed, 568 insertions, 120 deletions
diff --git a/misc/freeswitch/conf/freeswitch.xml b/misc/freeswitch/conf/freeswitch.xml
index 5792e96..7283211 100644
--- a/misc/freeswitch/conf/freeswitch.xml
+++ b/misc/freeswitch/conf/freeswitch.xml
@@ -74,6 +74,70 @@
</match>
</input>
</macro>
+ <macro name="voicemail_message_play">
+ <input pattern="^(\d+):(\d+):(.+)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-message_number.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-received.wav"/>
+ <action function="say" data="$2" method="pronounced" type="short_date_time"/>
+ <action function="play-file" data="$3"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_no_messages">
+ <input pattern="^(.*)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-no_more_messages.wav"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_message_menu_new">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-play_previous_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-delete_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-save_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-play_next_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_message_menu_saved">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-play_previous_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-delete_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-play_next_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
<macro name="voicemail_menu">
<input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
<match>
@@ -250,6 +314,14 @@
<input pattern="^(.*)$">
<match>
<action function="play-file" data="voicemail/vm-record_message.wav"/>
+ <action function="play-file" data="tone_stream://%(1000,0,500)"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_message_too_short">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-too-small.wav"/>
</match>
</input>
</macro>
@@ -600,6 +672,70 @@
</match>
</input>
</macro>
+ <macro name="voicemail_message_play">
+ <input pattern="^(\d+):(\d+):(.+)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-message_number.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-received.wav"/>
+ <action function="say" data="$2" method="pronounced" type="short_date_time"/>
+ <action function="play-file" data="$3"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_no_messages">
+ <input pattern="^(.*)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-no_more_messages.wav"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_message_menu_new">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-play_previous_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-delete_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-save_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-play_next_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_message_menu_saved">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-play_previous_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-delete_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-play_next_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
<macro name="voicemail_menu">
<input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
<match>
diff --git a/misc/freeswitch/scripts/common/configuration_table.lua b/misc/freeswitch/scripts/common/configuration_table.lua
index 85bc014..1b8d8b7 100644
--- a/misc/freeswitch/scripts/common/configuration_table.lua
+++ b/misc/freeswitch/scripts/common/configuration_table.lua
@@ -4,12 +4,43 @@
module(...,package.seeall)
+
+function cast(variable_type, value, default)
+ require 'common.str';
+
+ if variable_type == 'boolean' then
+ return common.str.to_b(value);
+ elseif variable_type == 'integer' then
+ if default and not tonumber(value) then
+ return default;
+ end
+ return common.str.to_i(value);
+ elseif variable_type == 'float' then
+ if default and not tonumber(value) then
+ return default;
+ end
+ return common.str.to_n(value);
+ elseif variable_type == 'string' then
+ if default and not value then
+ return default;
+ end
+ return common.str.to_s(value);
+ elseif variable_type == 'array' then
+ if default and not value then
+ return default;
+ end
+ return common.str.to_a(value, ',');
+ end
+end
+
-- retrieve configuration from database
-function get(database, entity, section)
+function get(database, entity, section, defaults)
if not database or not entity then
return {};
end
+ defaults = defaults or {};
+
require 'common.str'
local sql_query = 'SELECT * FROM `gs_parameters` WHERE `entity` = "' .. entity .. '"';
@@ -17,7 +48,7 @@ function get(database, entity, section)
sql_query = sql_query .. ' AND `section` = "' .. section .. '"';
end
- local root = {}
+ local root = defaults[1] or {}
local parameter_class = '';
database:query(sql_query, function(parameters)
@@ -26,21 +57,27 @@ function get(database, entity, section)
local p_name = common.str.strip(parameters.name);
if not root[p_section] then
- root[p_section] = {};
+ root[p_section] = defaults[p_section] or {};
end
- if p_class_type == 'boolean' then
- root[p_section][p_name] = common.str.to_b(parameters.value);
- elseif p_class_type == 'integer' then
- root[p_section][p_name] = common.str.to_i(parameters.value);
- else
- root[p_section][p_name] = tostring(parameters.value);
- end
+ root[p_section][p_name] = cast(p_class_type, parameters.value);
end)
if section then
- return root[section];
+ return root[section] or defaults[section];
end
return root;
end
+
+
+function settings(database, table_name, key, value, defaults)
+ local sql_query = 'SELECT * FROM ' .. database:escape(table_name, '`') .. ' WHERE ' .. database:escape(key, '`') .. ' = ' .. database:escape(value, '"');
+
+ local settings_entries = defaults or {};
+ database:query(sql_query, function(record)
+ settings_entries[record.name] = cast(record.class_type:lower(), record.value, settings_entries[record.name]);
+ end);
+
+ return settings_entries;
+end
diff --git a/misc/freeswitch/scripts/dialplan/dialplan.lua b/misc/freeswitch/scripts/dialplan/dialplan.lua
index 5335328..7ed835b 100644
--- a/misc/freeswitch/scripts/dialplan/dialplan.lua
+++ b/misc/freeswitch/scripts/dialplan/dialplan.lua
@@ -590,19 +590,21 @@ end
function Dialplan.voicemail(self, destination)
- require 'dialplan.voicemail'
+ require 'dialplan.voicemail';
local voicemail_account = nil;
-
- local sip_account_id
- if not common.str.blank(destination.number) and false then
- voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_number(destination.number);
+ if common.str.to_i(destination.id) > 0 then
+ voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database, domain = self.domain }:find_by_id(destination.id);
+ elseif not common.str.blank(destination.number) then
+ voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database, domain = self.domain }:find_by_number(destination.number);
elseif self.caller.auth_account and self.caller.auth_account.class == 'sipaccount' then
- voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_sip_account_id(self.caller.auth_account.id);
+ voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database, domain = self.domain }:find_by_sip_account_id(self.caller.auth_account.id);
+ elseif self.caller.forwarding_number then
+ voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database, domain = self.domain }:find_by_number(self.caller.forwarding_number);
end
if not voicemail_account then
- self.log:error('VOICEMAIL - no mailbox');
+ self.log:error('VOICEMAIL - mailbox not found, ');
return { continue = false, code = 404, phrase = 'Mailbox not found' }
end
diff --git a/misc/freeswitch/scripts/dialplan/functions.lua b/misc/freeswitch/scripts/dialplan/functions.lua
index a47d9d3..6399c5e 100644
--- a/misc/freeswitch/scripts/dialplan/functions.lua
+++ b/misc/freeswitch/scripts/dialplan/functions.lua
@@ -756,31 +756,28 @@ function Functions.voicemail_message_leave(self, caller, phone_number)
end
-function Functions.voicemail_check(self, caller, phone_number)
+function Functions.voicemail_check(self, caller, number)
+ require 'dialplan.voicemail';
local voicemail_account = nil;
local voicemail_authorized = false;
-
- require 'dialplan.voicemail'
-
- if phone_number then
- voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_number(phone_number);
- else
- if caller.auth_account_type == 'SipAccount' then
- voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_sip_account_id(caller.auth_account.id);
- voicemail_authorized = true;
- end
+
+ if number then
+ voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_number(number);
+ elseif caller.auth_account and tostring(caller.auth_account.class):lower() == 'sipaccount' then
+ voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_sip_account_id(caller.auth_account.id);
+ voicemail_authorized = true;
end
if not voicemail_account then
+ self.log:notice('FUNCTION_VOICEMAIL_CHECK - mailbox not found');
return { continue = false, code = 404, phrase = 'Mailbox not found', no_cdr = true }
end
- voicemail_account:menu(caller, voicemail_authorized);
-
- return { continue = false, code = 200, phrase = 'OK', no_cdr = true }
+ return voicemail_account:menu_main(caller, voicemail_authorized);
end
+
function Functions.acd_membership_toggle(self, caller, agent_id, phone_number)
-- Find caller's SipAccount
local caller_sip_account = self:ensure_caller_sip_account(caller);
diff --git a/misc/freeswitch/scripts/dialplan/ivr.lua b/misc/freeswitch/scripts/dialplan/ivr.lua
index f8b8a2d..2a16e3b 100644
--- a/misc/freeswitch/scripts/dialplan/ivr.lua
+++ b/misc/freeswitch/scripts/dialplan/ivr.lua
@@ -21,29 +21,51 @@ function Ivr.new(self, arg)
end
-function Ivr.ivr_phrase(self, phrase, keys, timeout, ivr_repeat)
+function Ivr.ivr_break(self)
+ return self.exit or not self.caller:ready();
+end
+
+
+function Ivr.ivr_phrase(self, phrase, keys, timeout, ivr_repeat, phrase_data)
ivr_repeat = ivr_repeat or 3;
timeout = timeout or 30;
self.digit = '';
self.exit = false;
self.break_keys = {};
+ local query_keys = {};
+
for index=1, #keys do
- self.break_keys[keys[index]] = true;
+ if type(keys[index]) == 'table' then
+ if tostring(keys[index].key) ~= '' then
+ table.insert(query_keys, keys[index].key);
+ end
+ self.break_keys[keys[index].key] = keys[index];
+ else
+ if tostring(keys[index]) ~= '' then
+ table.insert(query_keys, keys[index]);
+ end
+ self.break_keys[keys[index]] = true;
+ end
end
global_callback:callback('dtmf', 'ivr_ivr_phrase', self.ivr_phrase_dtmf, self);
-
+ local continue = true;
for index=0, ivr_repeat do
- self.caller.session:sayPhrase(phrase, table.concat(keys, ':'));
- self.caller:sleep(timeout * 1000);
- if self.exit then
+ continue = self:ivr_break() or self.caller.session:sayPhrase(phrase, phrase_data or table.concat(query_keys, ':'));
+ continue = self:ivr_break() or self.caller:sleep(timeout * 1000);
+
+ if self:ivr_break() then
break;
end
end
global_callback:callback_unset('dtmf', 'ivr_ivr_phrase');
+ if type(self.break_keys[self.digit]) == 'table' then
+ return self.digit, self.break_keys[self.digit];
+ end
+
return self.digit;
end
@@ -66,8 +88,8 @@ function Ivr.read_phrase(self, phrase, phrase_data, max_keys, min_keys, timeout,
timeout = timeout or 30;
global_callback:callback('dtmf', 'ivr_read_phrase', self.read_phrase_dtmf, self);
- self.caller.session:sayPhrase(phrase, phrase_data or enter_key or '');
- self.caller:sleep(timeout * 1000);
+ local continue = self:ivr_break() or self.caller.session:sayPhrase(phrase, phrase_data or enter_key or '');
+ continue = self:ivr_break() or self.caller:sleep(timeout * 1000);
global_callback:callback_unset('dtmf', 'ivr_read_phrase');
return self.digits;
@@ -88,7 +110,7 @@ function Ivr.read_phrase_dtmf(self, dtmf)
end
-function Ivr.check_pin(self, phrase, pin, pin_timeout, pin_repeat, key_enter)
+function Ivr.check_pin(self, phrase_enter, phrase_incorrect, pin, pin_timeout, pin_repeat, key_enter)
if not pin then
return nil;
end
@@ -101,19 +123,42 @@ function Ivr.check_pin(self, phrase, pin, pin_timeout, pin_repeat, key_enter)
for i = 1, pin_repeat do
if digits == pin then
self.caller:send_display('PIN: OK');
- break
+ break;
elseif digits ~= "" then
self.caller:send_display('PIN: wrong');
+ self.caller.session:sayPhrase(phrase_incorrect, '');
+ elseif self:ivr_break() then
+ break;
end
self.caller:send_display('Enter PIN');
- digits = ivr:read_phrase(phrase, nil, 0, pin:len() + 1, pin_timeout, key_enter);
+ digits = ivr:read_phrase(phrase_enter, nil, 0, pin:len() + 1, pin_timeout, key_enter);
end
if digits ~= pin then
self.caller:send_display('PIN: wrong');
+ self.caller.session:sayPhrase(phrase_incorrect, '');
return false
end
self.caller:send_display('PIN: OK');
return true;
end
+
+
+function Ivr.record(self, file_name, phrase_record, phrase_too_short, record_length_max, record_length_min, record_repeat, silence_level, silence_lenght_abort)
+ local duration = nil;
+ for index=1, record_repeat do
+ if (duration and duration >= record_length_min) or not self.caller:ready() then
+ break;
+ elseif duration then
+ self.caller:send_display('Recording too short');
+ self.caller.session:sayPhrase(phrase_too_short);
+ end
+ self.caller.session:sayPhrase(phrase_record);
+ self.caller:send_display('Recording...');
+ local result = self.caller.session:recordFile(file_name, record_length_max, silence_level, silence_lenght_abort);
+ duration = self.caller:to_i('record_seconds');
+ end
+
+ return duration or 0;
+end
diff --git a/misc/freeswitch/scripts/dialplan/voicemail.lua b/misc/freeswitch/scripts/dialplan/voicemail.lua
index caeeb48..4fd5612 100644
--- a/misc/freeswitch/scripts/dialplan/voicemail.lua
+++ b/misc/freeswitch/scripts/dialplan/voicemail.lua
@@ -1,17 +1,39 @@
-- Gemeinschaft 5 module: voicemail class
--- (c) AMOOMA GmbH 2012-2013
+-- (c) AMOOMA GmbH 2013
--
module(...,package.seeall)
Voicemail = {}
-MESSAGE_LENGTH_MIN = 3;
-MESSAGE_LENGTH_MAX = 120;
-SILENCE_LENGTH_ABORT = 5;
-SILENCE_LEVEL = 500;
-BEEP = 'tone_stream://%(1000,0,500)';
-RECORD_FILE_PREFIX = '/var/spool/freeswitch/voicemail_';
+DEFAULT_SETTINGS = {
+ pin_length_max = 10,
+ pin_length_min = 2,
+ pin_timeout = 20,
+ key_new_messages = '1',
+ key_saved_messages = '2',
+ key_config_menu = '0',
+ key_terminator = '#',
+ key_previous = '4',
+ key_next = '6',
+ key_delete = '7',
+ key_save = '2',
+ key_main_menu = '#',
+ record_length_max = 300,
+ record_length_min = 4,
+ records_max = 100,
+ silence_lenght_abort = 3,
+ silence_level = 500,
+ beep = 'tone_stream://%(1000,0,500)',
+ record_file_prefix = 'voicemail_',
+ record_file_suffix = '.wav',
+ record_file_path = '/var/spool/freeswitch/',
+ record_repeat = 3,
+ notify = true,
+ attachment = true,
+ mark_read = true,
+ purge = false,
+}
-- create voicemail object
function Voicemail.new(self, arg)
@@ -23,93 +45,313 @@ function Voicemail.new(self, arg)
self.log = arg.log;
self.database = arg.database;
self.record = arg.record;
+ self.domain = arg.domain;
return object
end
--- find voicemail account by sip account id
-function Voicemail.find_by_sip_account_id(self, id)
- local sql_query = 'SELECT `a`.`id`, `a`.`uuid`, `a`.`auth_name`, `a`.`caller_name`, `b`.`name_path`, `b`.`greeting_path`, `a`.`voicemail_pin`, `b`.`password`, `c`.`host` AS `domain` \
- FROM `sip_accounts` `a` LEFT JOIN `voicemail_prefs` `b` ON `a`.`auth_name` = `b`.`username` \
- JOIN `sip_domains` `c` ON `a`.`sip_domain_id` = `c`.`id` \
- WHERE `a`.`id` = ' .. tonumber(id);
+function Voicemail.find_by_sql(self, sql_query)
local voicemail_account = nil;
self.database:query(sql_query, function(entry)
voicemail_account = Voicemail:new(self);
voicemail_account.record = entry;
voicemail_account.id = tonumber(entry.id);
voicemail_account.uuid = entry.uuid;
- end)
+ voicemail_account.name = entry.name;
+ voicemail_account.settings = self:settings_get(entry.id);
+ end);
return voicemail_account;
end
--- Find Voicemail account by name
-function Voicemail.find_by_name(self, account_name)
- id = tonumber(id) or 0;
- local sql_query = string.format('SELECT * FROM `voicemail_prefs` WHERE `username`= "%s" LIMIT 1', account_name)
- local record = nil
- self.database:query(sql_query, function(voicemail_entry)
- record = voicemail_entry
- end)
+function Voicemail.find_by_name(self, name)
+ local sql_query = 'SELECT * FROM `voicemail_accounts` \
+ WHERE `name` = ' .. self.database:escape(name, '"') .. ' LIMIT 1';
+
+ return self:find_by_sql(sql_query);
+end
+
+
+function Voicemail.find_by_id(self, id)
+ local sql_query = 'SELECT * FROM `voicemail_accounts` \
+ WHERE `id` = ' .. self.database:escape(id, '"') .. ' LIMIT 1';
+
+ return self:find_by_sql(sql_query);
+end
+
+
+function Voicemail.find_by_sip_account_id(self, id)
+ local sql_query = 'SELECT `a`.* FROM `voicemail_accounts` `a` \
+ JOIN `sip_accounts` `b` ON `a`.`id` = `b`.`voicemail_account_id` \
+ WHERE `b`.`id` = ' .. self.database:escape(id, '"') .. ' LIMIT 1';
+
+ return self:find_by_sql(sql_query);
+end
+
+
+function Voicemail.find_by_number(self, number)
+ local sql_query = 'SELECT `a`.* FROM `voicemail_accounts` `a` \
+ JOIN `sip_accounts` `b` ON `a`.`id` = `b`.`voicemail_account_id` \
+ JOIN `phone_numbers` `c` ON `b`.`id` = `c`.`phone_numberable_id` \
+ WHERE `c`.`number` = ' .. self.database:escape(number, '"') .. ' \
+ AND `c`.`phone_numberable_type` = "SipAccount" LIMIT 1';
+
+ return self:find_by_sql(sql_query);
+end
+
+
+function Voicemail.settings_get(self, id)
+ require 'common.configuration_table';
+ local parameters = common.configuration_table.get(self.database, 'voicemail', 'settings', { settings = DEFAULT_SETTINGS });
- if voicemail_account then
- voicemail_account.account_name = account_name;
- if record then
- voicemail_account.name_path = record.name_path;
- voicemail_account.greeting_path = record.greeting_path;
- voicemail_account.password = record.password;
+ return common.configuration_table.settings(self.database, 'voicemail_settings', 'voicemail_account_id', id or self.id, parameters)
+end
+
+
+function Voicemail.messages_get(self, status)
+ local sql_query = 'SELECT * FROM `voicemail_msgs` WHERE `username` = ' .. self.database:escape(self.name, '"');
+
+ status = status or 'all';
+
+ if status == 'read' then
+ sql_query = sql_query .. ' AND `read_epoch` > 0';
+ elseif status == 'unread' then
+ sql_query = sql_query .. ' AND (`read_epoch` = 0 OR `read_epoch` IS NULL)';
+ elseif status == 'new' then
+ sql_query = sql_query .. ' AND `in_folder` != "save" AND `flags` != "save"';
+ elseif status == 'saved' then
+ sql_query = sql_query .. ' AND (`in_folder` = "save" OR `flags` = "save")';
+ end
+
+ return self.database:query_return_all(sql_query);
+end
+
+
+function Voicemail.check_pin(self, pin)
+ self.caller:answer();
+ self.caller:sleep(1000);
+
+ require 'dialplan.ivr';
+ local ivr = dialplan.ivr.Ivr:new{ caller = self.caller, log = self.log };
+
+ local digits = '';
+ for i = 1, 3 do
+ if digits == pin then
+ self.caller:send_display('PIN: OK');
+ break
+ elseif digits ~= "" then
+ self.caller:send_display('PIN: wrong');
end
+ self.caller:send_display('Enter PIN');
+ digits = ivr:read_phrase('voicemail_enter_pass', nil, self.settings.pin_length_min, self.settings.pin_length_max, self.settings.pin_timeout, self.settings.terminator_key);
+ end
+
+ if digits ~= pin then
+ return false
+ end
+
+ return true;
+end
+
+
+function Voicemail.menu_main(self, caller, authorized)
+ self.caller = caller;
+
+ require 'dialplan.ivr';
+ self.ivr = dialplan.ivr.Ivr:new{ caller = self.caller, log = self.log };
+
+ if not authorized then
+ if common.str.blank(self.pin) then
+ self.log:notice('VOICEMAIL_MAIN_MENU - unaunthorized, no PIN, ', self.class, '=', self.id, '/', self.uuid, '|', self.name);
+ return { continue = false, code = 500, phrase = 'Unauthorized', no_cdr = true }
+ end
+
+ self.caller:answer();
+ self.caller:sleep(1000);
+
+ if not self.ivr:check_pin('voicemail_enter_pass', 'voicemail_fail_auth', self.pin) then
+ self.log:notice('VOICEMAIL_MAIN_MENU - wrong PIN, ', self.class, '=', self.id, '/', self.uuid, '|', self.name);
+ caller.session:sayPhrase('voicemail_goodbye');
+ return { continue = false, code = 500, phrase = 'Unauthorized', no_cdr = true }
+ end
+ end
+
+ local messages_new = self:messages_get('unread');
+ local messages_saved = self:messages_get('read');
+
+ if not caller:answered() then
+ self.caller:answer();
+ self.caller:sleep(1000);
end
- return voicemail_account
+ if self.settings.voicemail_hello then
+ caller.session:sayPhrase('voicemail_hello');
+ end
+
+ if self.settings.voicemail_message_count then
+ caller.session:sayPhrase('voicemail_message_count', #messages_new .. ':new');
+ end
+
+ while true do
+ self.log:info('VOICEMAIL_MAIN_MENU - ', self.class, '=', self.id, '/', self.uuid, '|', self.name, ', messages: ', #messages_new, ':', #messages_saved);
+ self.caller:send_display(#messages_new .. ' new messages');
+
+ local main_menu = {
+ { key = self.settings.key_new_messages, method = self.menu_messages, parameters = { self, 'new', messages_new } },
+ { key = self.settings.key_saved_messages, method = self.menu_messages, parameters = { self, 'saved', messages_saved } },
+ { key = self.settings.key_config_menu, method = self.menu_options, parameters = { self } },
+ { key = self.settings.key_terminator, exit = true },
+ { key = '', exit = true },
+ };
+
+ local digits, key = self.ivr:ivr_phrase('voicemail_menu', main_menu);
+ self.log:debug('VOICEMAIL_MAIN_MENU - digit: ', digits);
+ if key.exit then
+ break;
+ end
+
+ key.method(unpack(key.parameters));
+
+ messages_new = self:messages_get('unread');
+ messages_saved = self:messages_get('read');
+ end
+ self.caller:send_display('Goodbye');
+ caller.session:sayPhrase('voicemail_goodbye');
end
--- Find Voicemail account by number
-function Voicemail.find_by_number(self, phone_number)
- local sip_account = nil;
- require "common.phone_number"
- local destination_number_object = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database }:find_by_number(phone_number);
- if destination_number_object and destination_number_object.record.phone_numberable_type:lower() == "sipaccount" then
- return self:find_by_sip_account_id(destination_number_object.record.phone_numberable_id);
+function Voicemail.menu_messages(self, folder, messages)
+ self.log:info('VOICEMAIL_MESSAGES_MENU - ', folder,' messages: ', #messages);
+
+ local digits = nil;
+ local key = nil;
+
+ local message_menu = {
+ { key = self.settings.key_previous, action = 'previous' },
+ { key = self.settings.key_delete, action = 'delete' },
+ { key = self.settings.key_save, action = 'save' },
+ { key = self.settings.key_next, action = 'next' },
+ { key = self.settings.key_main_menu, exit = true },
+ };
+
+ if folder == 'saved' then
+ message_menu = {
+ { key = self.settings.key_previous, action = 'previous' },
+ { key = self.settings.key_delete, action = 'delete' },
+ { key = self.settings.key_next, action = 'next' },
+ { key = self.settings.key_main_menu, exit = true },
+ };
+ end
+
+ if #messages == 0 then
+ digits, key = self.ivr:ivr_phrase('voicemail_no_messages', message_menu, 0, 0);
+ return;
end
- return false;
+ local index = 1;
+ while index <= #messages do
+ local message = messages[index];
+ self.caller:send_display(index .. ': ' .. message.cid_name .. ' ' .. message.cid_number);
+ digits, key = self.ivr:ivr_phrase('voicemail_message_play', message_menu, 0, 0,
+ index .. ':' .. message.created_epoch .. ':' .. message.file_path
+ );
+ if digits == '' then
+ if common.str.to_i(message.read_epoch) == 0 then
+ self:message_mark_read(message);
+ end
+ digits, key = self.ivr:ivr_phrase('voicemail_message_menu_' .. folder, message_menu, 15, 0);
+ end
+
+ if not key or key.exit then
+ break;
+ end
+
+ if key.action == 'previous' then
+ if index > 1 then
+ index = index - 1;
+ end
+ else
+ index = index + 1;
+ end
+
+ if index > #messages then
+ digits, key = self.ivr:ivr_phrase('voicemail_no_messages', message_menu, 0, 0);
+ return;
+ end
+
+ if key.action == 'delete' and self:message_delete(message) then
+ self.caller:send_display('Message deleted');
+ digits = self.caller.session:sayPhrase('voicemail_ack', 'deleted');
+ elseif key.action == 'save' and self:message_save(message) then
+ self.caller:send_display('Message saved');
+ digits = self.caller.session:sayPhrase('voicemail_ack', 'saved');
+ end
+ end
end
-function Voicemail.leave(self, caller, phone_number)
- require 'common.str'
+function Voicemail.menu_options(self)
+ self.log:info('VOICEMAIL_OPTIONS_MENU');
+ self.caller:send_display('Voicemail options');
+end
+
+
+function Voicemail.message_delete(self, message)
+ self.log:debug('VOICEMAIL_MESSAGE_DELETE - message: ', message.uuid);
+ require 'common.fapi';
+ return common.fapi.FApi:new{ log = self.log }:execute('vm_delete', message.username .. '@' .. message.domain .. ' ' .. message.uuid);
+end
+
+function Voicemail.message_mark_read(self, message)
+ self.log:debug('VOICEMAIL_MESSAGE_MARK_READ - message: ', message.uuid);
+ require 'common.fapi';
+ return common.fapi.FApi:new{ log = self.log }:execute('vm_read', message.username .. '@' .. message.domain .. ' read ' .. message.uuid);
+end
- self.log:info('VOICEMAIL_LEAVE - account=', self.record.id, '/', self.record.uuid, ', auth_name: ', self.record.auth_name, ', caller_name: ', self.record.caller_name);
- caller:set_callee_id(phone_number, self.record.caller_name);
+function Voicemail.message_save(self, message)
+ self.log:debug('VOICEMAIL_MESSAGE_SAVE - message: ', message.uuid);
+ require 'common.fapi';
+ return common.fapi.FApi:new{ log = self.log }:execute('vm_fsdb_msg_save', 'default ' .. message.domain .. ' ' .. message.username .. ' ' .. message.uuid);
+end
+
+
+function Voicemail.leave(self, caller, greeting)
+ local forwarding_number = caller.forwarding_number;
+ self.log:info('VOICEMAIL_LEAVE - voicemail_account=', self.record.id, '/', self.record.uuid, '|', self.record.name, ', forwarding_number: ', forwarding_number);
+
+ caller:set_callee_id(forwarding_number, common.array.try(caller, 'auth_account.caller_name') or common.array.try(caller, 'auth_account.name'));
caller:answer();
- caller:send_display(common.str.to_s(self.record.caller_name), common.str.to_s(phone_number));
+ caller:send_display(common.array.try(caller, 'auth_account.caller_name') or common.array.try(caller, 'auth_account.name'));
caller:sleep(1000);
- if not common.str.blank(self.record.greeting_path) then
- caller.session:sayPhrase('voicemail_play_greeting', 'greeting:' .. tostring(self.record.greeting_path));
- elseif not common.str.blank(self.record.name_path) then
- caller.session:sayPhrase('voicemail_play_greeting', 'name:' .. tostring(self.record.name_path));
- elseif not common.str.blank(phone_number) then
- caller.session:sayPhrase('voicemail_play_greeting', (tostring(phone_number):gsub('[%D]', '')));
+ if not common.str.blank(forwarding_number) then
+ caller.session:sayPhrase('voicemail_play_greeting', (tostring(forwarding_number):gsub('[%D]', '')));
end
- local record_file_name = RECORD_FILE_PREFIX .. caller.uuid .. '.wav';
- caller.session:streamFile(BEEP);
+ local record_file_name = self.settings.record_file_path ..self.settings.record_file_prefix .. caller.uuid .. self.settings.record_file_suffix;
self.log:info('VOICEMAIL_LEAVE - recording to file: ', tostring(record_file_name));
- local result = caller.session:recordFile(record_file_name, MESSAGE_LENGTH_MAX, SILENCE_LEVEL, SILENCE_LENGTH_ABORT);
- local duration = caller:to_i('record_seconds');
+
+ require 'dialplan.ivr';
+ local ivr = dialplan.ivr.Ivr:new{ caller = caller, log = self.log };
+
+ local duration = ivr:record(
+ record_file_name,
+ 'voicemail_record_message',
+ 'voicemail_message_too_short',
+ self.settings.record_length_max,
+ self.settings.record_length_min,
+ self.settings.record_repeat,
+ self.settings.silence_level,
+ self.settings.silence_lenght_abort);
- if duration >= MESSAGE_LENGTH_MIN then
+ if duration >= self.settings.record_length_min then
self.log:info('VOICEMAIL_LEAVE - saving recorded message to voicemail, duration: ', duration);
require 'common.fapi'
common.fapi.FApi:new{ log = self.log, uuid = caller.uuid }:execute('vm_inject',
- self.record.auth_name ..
- '@' .. self.record.domain .. " '" ..
+ self.record.name ..
+ '@' .. self.domain .. " '" ..
record_file_name .. "' '" ..
caller.caller_id_number .. "' '" ..
caller.caller_id_name .. "' '" ..
@@ -121,32 +363,14 @@ function Voicemail.leave(self, caller, phone_number)
caller:set_variable('voicemail_message_len');
end
os.remove(record_file_name);
- return true;
+ caller:send_display('Goodbye');
+ caller.session:sayPhrase('voicemail_goodbye');
end
function Voicemail.trigger_notification(self, caller)
- local command = 'http_request.lua ' .. caller.uuid .. ' http://127.0.0.1/trigger/voicemail?sip_account_id=' .. tostring(self.id);
+ local command = 'http_request.lua ' .. caller.uuid .. ' http://127.0.0.1/trigger/voicemail?voicemail_account_id=' .. tostring(self.id);
- require 'common.fapi'
+ require 'common.fapi';
return common.fapi.FApi:new():execute('luarun', command);
end
-
-
-function Voicemail.menu(self, caller, authorized)
- self.log:info('VOICEMAIL_MENU - account: ', self.record.auth_name);
-
- if authorized then
- caller:set_variable('voicemail_authorized', true);
- end
-
- caller:set_callee_id(phone_number, self.record.caller_name);
- caller:answer();
- caller:send_display(common.str.to_s(self.record.caller_name), common.str.to_s(phone_number));
-
- caller:sleep(1000);
- caller:set_variable('skip_greeting', true);
- caller:set_variable('skip_instructions', true);
-
- caller:execute('voicemail', 'check default ' .. self.record.domain .. ' ' .. self.record.auth_name);
-end
diff --git a/misc/freeswitch/scripts/dialplan_default.lua b/misc/freeswitch/scripts/dialplan_default.lua
index 2b651c5..ba51ed3 100644
--- a/misc/freeswitch/scripts/dialplan_default.lua
+++ b/misc/freeswitch/scripts/dialplan_default.lua
@@ -29,6 +29,9 @@ function input_call_back_callee(s, object_type, object_data, arg)
end
end
+function global_callback_handler(...)
+ return global_callback:run({...});
+end
-- initialize logging
require 'common.log'
@@ -90,6 +93,10 @@ else
end
end
+require 'dialplan.callback';
+global_callback = dialplan.callback.Callback:new{ session = session, log = log };
+
+
if start_caller.from_node then
log:debug('AUTHENTICATION_REQUIRED_NODE - node_id: ', start_caller.node_id, ', domain: ', start_dialplan.domain);
start_dialplan:hangup(407, start_dialplan.domain);