Add. Lua part of fax_queue.
To poll active task you can run `luarun fax_queue_poll_once.lua`
This commit is contained in:
@@ -8,7 +8,13 @@
|
||||
|
||||
require "resources.functions.trim";
|
||||
|
||||
local api = api or freeswitch.API();
|
||||
local api = api
|
||||
if (not api) and freeswitch then api = freeswitch.API() else
|
||||
api = {}
|
||||
function api:execute()
|
||||
return '-ERR UNSUPPORTTED'
|
||||
end
|
||||
end
|
||||
|
||||
local function send_event(action, key)
|
||||
local event = freeswitch.Event("MEMCACHE", action);
|
||||
|
||||
@@ -1,115 +1,190 @@
|
||||
require 'resources.functions.config'
|
||||
require 'resources.functions.file_exists'
|
||||
require 'resources.functions.database_handle'
|
||||
|
||||
local unpack = unpack or table.unpack
|
||||
-----------------------------------------------------------
|
||||
local OdbcDatabase = {} if not freeswitch then
|
||||
OdbcDatabase.__index = OdbcDatabase
|
||||
|
||||
local Database = {} do
|
||||
local odbc = require "odbc.dba"
|
||||
|
||||
Database.__index = Database
|
||||
function OdbcDatabase.new(name)
|
||||
local self = setmetatable({}, OdbcDatabase)
|
||||
|
||||
function Database.new(name)
|
||||
local dbh = assert(name)
|
||||
if type(name) == 'string' then
|
||||
if name == 'switch' and file_exists(database_dir.."/core.db") then
|
||||
dbh = freeswitch.Dbh("sqlite://"..database_dir.."/core.db")
|
||||
else
|
||||
dbh = database_handle(name)
|
||||
end
|
||||
end
|
||||
assert(dbh:connected())
|
||||
local connection_string = assert(database[name])
|
||||
|
||||
local self = setmetatable({
|
||||
_dbh = dbh;
|
||||
}, Database)
|
||||
local typ, dsn, user, password = connection_string:match("^(.-)://(.-):(.-):(.-)$")
|
||||
assert(typ == 'odbc', "unsupported connection string:" .. connection_string)
|
||||
|
||||
return self
|
||||
self._dbh = odbc.Connect(dsn, user, password)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function Database:query(sql, fn)
|
||||
if (fn == nil) then
|
||||
return self._dbh:query(sql)
|
||||
else
|
||||
return self._dbh:query(sql, fn)
|
||||
end
|
||||
function OdbcDatabase:query(sql, fn)
|
||||
self._rows_affected = nil
|
||||
if fn then
|
||||
return self._dbh:neach(sql, function(row)
|
||||
local o = {}
|
||||
for k, v in pairs(row) do
|
||||
if v == odbc.NULL then
|
||||
o[k] = nil
|
||||
else
|
||||
o[k] = tostring(v)
|
||||
end
|
||||
end
|
||||
return fn(o)
|
||||
end)
|
||||
end
|
||||
local ok, err = self._dbh:exec(sql)
|
||||
if not ok then return nil, err end
|
||||
self._rows_affected = ok
|
||||
return self._rows_affected
|
||||
end
|
||||
|
||||
function OdbcDatabase:affected_rows()
|
||||
return self._rows_affected;
|
||||
end
|
||||
|
||||
function OdbcDatabase:release()
|
||||
if self._dbh then
|
||||
self._dbh:destroy()
|
||||
self._dbh = nil
|
||||
end
|
||||
end
|
||||
|
||||
function OdbcDatabase:connected()
|
||||
return self._dbh and self._dbh:connected()
|
||||
end
|
||||
|
||||
end
|
||||
-----------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------
|
||||
local FsDatabase = {} if freeswitch then
|
||||
|
||||
require "resources.functions.file_exists"
|
||||
require "resources.functions.database_handle"
|
||||
|
||||
FsDatabase.__index = FsDatabase
|
||||
|
||||
function FsDatabase.new(name)
|
||||
local dbh = assert(name)
|
||||
if type(name) == 'string' then
|
||||
if name == 'switch' and file_exists(database_dir.."/core.db") then
|
||||
dbh = freeswitch.Dbh("sqlite://"..database_dir.."/core.db")
|
||||
else
|
||||
dbh = database_handle(name)
|
||||
end
|
||||
end
|
||||
assert(dbh:connected())
|
||||
|
||||
local self = setmetatable({
|
||||
_dbh = dbh;
|
||||
}, FsDatabase)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function FsDatabase:query(sql, fn)
|
||||
if fn then
|
||||
return self._dbh:query(sql, fn)
|
||||
end
|
||||
return self._dbh:query(sql)
|
||||
end
|
||||
|
||||
function FsDatabase:affected_rows()
|
||||
if self._dbh then
|
||||
return self._dbh:affected_rows()
|
||||
end
|
||||
end
|
||||
|
||||
function FsDatabase:release()
|
||||
if self._dbh then
|
||||
self._dbh:release()
|
||||
self._dbh = nil
|
||||
end
|
||||
end
|
||||
|
||||
function FsDatabase:connected()
|
||||
return self._dbh and self._dbh:connected()
|
||||
end
|
||||
|
||||
end
|
||||
-----------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------
|
||||
local Database = {} do
|
||||
Database.__index = Database
|
||||
Database.__base = freeswitch and FsDatabase or OdbcDatabase
|
||||
Database = setmetatable(Database, Database.__base)
|
||||
|
||||
function Database.new(...)
|
||||
local self = Database.__base.new(...)
|
||||
setmetatable(self, Database)
|
||||
return self
|
||||
end
|
||||
|
||||
function Database:first_row(sql)
|
||||
local result
|
||||
local ok, err = self:query(sql, function(row)
|
||||
result = row
|
||||
return 1
|
||||
end)
|
||||
if not ok then return nil, err end
|
||||
return result
|
||||
local result
|
||||
local ok, err = self:query(sql, function(row)
|
||||
result = row
|
||||
return 1
|
||||
end)
|
||||
if not ok then return nil, err end
|
||||
return result
|
||||
end
|
||||
|
||||
function Database:first_value(sql)
|
||||
local result, err = self:first_row(sql)
|
||||
if not result then return nil, err end
|
||||
local k, v = next(result)
|
||||
return v
|
||||
local result, err = self:first_row(sql)
|
||||
if not result then return nil, err end
|
||||
local k, v = next(result)
|
||||
return v
|
||||
end
|
||||
|
||||
function Database:first(sql, ...)
|
||||
local result, err = self:first_row(sql)
|
||||
if not result then return nil, err end
|
||||
local t, n = {}, select('#', ...)
|
||||
for i = 1, n do
|
||||
t[i] = result[(select(i, ...))]
|
||||
end
|
||||
return unpack(t, 1, n)
|
||||
local result, err = self:first_row(sql)
|
||||
if not result then return nil, err end
|
||||
local t, n = {}, select('#', ...)
|
||||
for i = 1, n do
|
||||
t[i] = result[(select(i, ...))]
|
||||
end
|
||||
return unpack(t, 1, n)
|
||||
end
|
||||
|
||||
function Database:fetch_all(sql)
|
||||
local result = {}
|
||||
local ok, err = self:query(sql, function(row)
|
||||
result[#result + 1] = row
|
||||
end)
|
||||
if not ok then return nil, err end
|
||||
return result
|
||||
local result = {}
|
||||
local ok, err = self:query(sql, function(row)
|
||||
result[#result + 1] = row
|
||||
end)
|
||||
if (not ok) and err then return nil, err end
|
||||
return result
|
||||
end
|
||||
|
||||
function Database:release(sql)
|
||||
if self._dbh then
|
||||
self._dbh:release()
|
||||
self._dbh = nil
|
||||
end
|
||||
end
|
||||
function Database.__self_test__(...)
|
||||
local db = Database.new(...)
|
||||
assert(db:connected())
|
||||
|
||||
function Database:connected(sql)
|
||||
return self._dbh and self._dbh:connected()
|
||||
end
|
||||
assert("1" == db:first_value("select 1 as v union all select 2 as v"))
|
||||
|
||||
function Database.__self_test__(name)
|
||||
local db = Database.new(name or 'system')
|
||||
assert(db:connected())
|
||||
local t = assert(db:first_row("select '1' as v union all select '2' as v"))
|
||||
assert(t.v == "1")
|
||||
|
||||
assert("1" == db:first_value("select 1 as v union all select 2 as v"))
|
||||
t = assert(db:fetch_all("select '1' as v union all select '2' as v"))
|
||||
assert(#t == 2)
|
||||
assert(t[1].v == "1")
|
||||
assert(t[2].v == "2")
|
||||
|
||||
local t = assert(db:first_row("select 1 as v union all select 2 as v"))
|
||||
assert(t.v == "1")
|
||||
local a, b = assert(db:first("select '1' as b, '2' as a", 'a', 'b'))
|
||||
assert(a == "2")
|
||||
assert(b == "1")
|
||||
|
||||
t = assert(db:fetch_all("select 1 as v union all select 2 as v"))
|
||||
assert(#t == 2)
|
||||
assert(t[1].v == "1")
|
||||
assert(t[2].v == "2")
|
||||
-- assert(nil == db:first_value("some non sql query"))
|
||||
|
||||
local a, b = assert(db:first("select 1 as b, 2 as a", 'a', 'b'))
|
||||
assert(a == "2")
|
||||
assert(b == "1")
|
||||
|
||||
-- assert(nil == db:first_value("some non sql query"))
|
||||
|
||||
db:release()
|
||||
assert(not db:connected())
|
||||
print(" * databse - OK!")
|
||||
db:release()
|
||||
assert(not db:connected())
|
||||
print(" * databse - OK!")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- if debug.self_test then
|
||||
-- Database.__self_test__()
|
||||
-- end
|
||||
-----------------------------------------------------------
|
||||
|
||||
return Database
|
||||
@@ -0,0 +1,159 @@
|
||||
local function class(base)
|
||||
local t = base and setmetatable({}, base) or {}
|
||||
t.__index = t
|
||||
t.__class = t
|
||||
t.__base = base
|
||||
|
||||
function t.new(...)
|
||||
local o = setmetatable({}, t)
|
||||
if o.__init then
|
||||
if t == ... then -- we call as Class:new()
|
||||
return o:__init(select(2, ...))
|
||||
else -- we call as Class.new()
|
||||
return o:__init(...)
|
||||
end
|
||||
end
|
||||
return o
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
local EventSocket = class() do
|
||||
|
||||
if not freeswitch then
|
||||
|
||||
local socket = require "socket"
|
||||
local ESLParser = require "lluv.esl".ESLParser
|
||||
local split_status = require "lluv.esl.utils".split_status
|
||||
local Database = require "resources.functions.database"
|
||||
|
||||
local EOL = '\n'
|
||||
|
||||
local host, port, auth
|
||||
|
||||
function EventSocket:__init()
|
||||
if not host then
|
||||
local db = Database.new('system')
|
||||
local settings, err = db:first_row("select event_socket_ip_address, event_socket_port, event_socket_password from v_settings")
|
||||
if not settings then return nil, err end
|
||||
host, port, auth = settings.event_socket_ip_address, settings.event_socket_port, settings.event_socket_password
|
||||
end
|
||||
|
||||
return self:_connect(host, port, auth)
|
||||
end
|
||||
|
||||
function EventSocket:_connect(host, port, password)
|
||||
local err
|
||||
self._cnn, err = socket.connect(host, port)
|
||||
if not self._cnn then return nil, err end
|
||||
|
||||
self._cnn:settimeout(1)
|
||||
|
||||
self._parser = ESLParser.new()
|
||||
local auth
|
||||
while true do
|
||||
local event
|
||||
event, err = self:_recv_event()
|
||||
if not event then break end
|
||||
|
||||
local ct = event:getHeader('Content-Type')
|
||||
if ct == 'auth/request' then
|
||||
self._cnn:send('auth ' .. password .. EOL .. EOL)
|
||||
elseif ct == 'command/reply' then
|
||||
local reply = event:getHeader('Reply-Text')
|
||||
if reply then
|
||||
local ok, status, msg = split_status(reply)
|
||||
if ok then auth = true else err = msg end
|
||||
else
|
||||
err = 'invalid response'
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not auth then
|
||||
self._cnn:close()
|
||||
self._cnn = nil
|
||||
return nil, err
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function EventSocket:_recv_event()
|
||||
local event, err = self._parser:next_event()
|
||||
|
||||
while event == true do
|
||||
local str, rst
|
||||
str, err, rst = self._cnn:receive("*l")
|
||||
if str then self._parser:append(str):append(EOL) end
|
||||
if rst then self._parser:append(rst) end
|
||||
if err and err ~= 'timeout' then
|
||||
break
|
||||
end
|
||||
event = self._parser:next_event()
|
||||
end
|
||||
|
||||
if (not event) or (event == true) then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
return event
|
||||
end
|
||||
|
||||
function EventSocket:_request(cmd)
|
||||
if not self._cnn then return nil, 'closed' end
|
||||
|
||||
for str in (cmd .. '\n'):gmatch("(.-)\n") do
|
||||
self._cnn:send(str .. EOL)
|
||||
end
|
||||
self._cnn:send(EOL)
|
||||
|
||||
return self:_recv_event()
|
||||
end
|
||||
|
||||
function EventSocket:api(cmd)
|
||||
local event, err = self:_request('api ' .. cmd)
|
||||
if not event then return nil, err end
|
||||
local body = event:getBody()
|
||||
if body then return body end
|
||||
return event:getReply()
|
||||
end
|
||||
|
||||
function EventSocket:close()
|
||||
if self._cnn then
|
||||
self._cnn:close()
|
||||
self._cnn = nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if freeswitch then
|
||||
|
||||
local api
|
||||
|
||||
function EventSocket:__init()
|
||||
self._api = api or freeswitch.API()
|
||||
api = self._api
|
||||
return self
|
||||
end
|
||||
|
||||
function EventSocket:api(cmd)
|
||||
local result = self._api:executeString(cmd)
|
||||
if result and result:sub(1, 4) == '-ERR' then
|
||||
return nil, result:sub(5)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function EventSocket:close()
|
||||
self._api = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return EventSocket
|
||||
@@ -0,0 +1,172 @@
|
||||
-- -- Global settings
|
||||
-- local settings = Settings.new('system')
|
||||
-- print(settings:get('switch', 'base', 'dir'))
|
||||
--
|
||||
-- Domain settings (to `fax_retry.lua`)
|
||||
-- local Settings = require "resources.functions.settings"
|
||||
-- local settings = Settings.new(dbh, domain_name, domain_uuid)
|
||||
-- storage_type = settings:get('fax', 'storage_type', 'text') or ''
|
||||
-- storage_path = settings:get('fax', 'storage_path', 'text') or ''
|
||||
-- storage_path = storage_path
|
||||
-- :gsub("${domain_name}", domain_name)
|
||||
-- :gsub("${voicemail_id}", voicemail_id)
|
||||
-- :gsub("${voicemail_dir}", voicemail_dir)
|
||||
|
||||
local Database = require "resources.functions.database"
|
||||
local cache = require "resources.functions.cache"
|
||||
require "resources.functions.split"
|
||||
|
||||
-----------------------------------------------------------
|
||||
local Settings = {} do
|
||||
Settings.__index = Settings
|
||||
|
||||
local NONE = '15783958-912c-4893-8866-4ccd1ca73c6e'
|
||||
|
||||
local function append(t, v)
|
||||
t[#t+1] = v
|
||||
return t
|
||||
end
|
||||
|
||||
local function append_setting(array, category, subcategory, name, value)
|
||||
--add the category array
|
||||
if not array[category] then
|
||||
array[category] = {}
|
||||
end
|
||||
|
||||
--add the subcategory array
|
||||
if not array[category][subcategory] then
|
||||
array[category][subcategory] = {}
|
||||
end
|
||||
|
||||
--set the name and value
|
||||
if (name == "array") then
|
||||
if not array[category][subcategory][name] then
|
||||
array[category][subcategory][name] = {}
|
||||
end
|
||||
append(array[category][subcategory][name], value);
|
||||
elseif value ~= nil then
|
||||
array[category][subcategory][name] = value;
|
||||
end
|
||||
end
|
||||
|
||||
function Settings.new(db, domain_name, domain_uuid)
|
||||
local self = setmetatable({}, Settings)
|
||||
self._array = {}
|
||||
self._db = db
|
||||
self._domain_name = domain_name
|
||||
self._domain_uuid = domain_uuid
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function Settings:_cache_key(category, subcategory, name)
|
||||
return 'setting:' .. (self._domain_name or '') .. ':' .. category .. ':' .. subcategory .. ':' .. name
|
||||
end
|
||||
|
||||
function Settings:set(category, subcategory, name, value)
|
||||
append_setting(self._array, category, subcategory, name, value)
|
||||
return self
|
||||
end
|
||||
|
||||
function Settings:get(category, subcategory, name)
|
||||
local a = self._array
|
||||
local v = a[category] and a[category][subcategory] and a[category][subcategory][name]
|
||||
if v == NONE then return nil end
|
||||
if v ~= nil then return v end
|
||||
|
||||
local key = self:_cache_key(category, subcategory, name)
|
||||
|
||||
v = cache.get(key)
|
||||
if v then
|
||||
if v ~= NONE and name == 'array' then
|
||||
v = split(v, '/+/', true)
|
||||
end
|
||||
self:set(category, subcategory, name, v)
|
||||
if v == NONE then return nil end
|
||||
return v
|
||||
end
|
||||
|
||||
return self:_load(category, subcategory, name)
|
||||
end
|
||||
|
||||
function Settings:_load(category, subcategory, name)
|
||||
local domain_uuid = self._domain_uuid
|
||||
local db = self._db
|
||||
if type(self._db) == 'string' then
|
||||
db = Database.new(self._db)
|
||||
end
|
||||
|
||||
local found = false
|
||||
--get the domain settings
|
||||
if domain_uuid then
|
||||
sql = "SELECT domain_setting_uuid,domain_setting_category,domain_setting_subcategory,domain_setting_name,domain_setting_value "
|
||||
sql = sql .. "FROM v_domain_settings ";
|
||||
sql = sql .. "WHERE domain_uuid = '" .. domain_uuid .. "'";
|
||||
sql = sql .. "AND domain_setting_enabled = 'true' ";
|
||||
sql = sql .. "AND domain_setting_category = '" .. category .."'";
|
||||
sql = sql .. "AND domain_setting_subcategory = '" .. subcategory .. "'";
|
||||
sql = sql .. "AND domain_setting_name = '" .. name .. "'";
|
||||
sql = sql .. "AND domain_setting_value is not null ";
|
||||
sql = sql .. "ORDER BY domain_setting_category, domain_setting_subcategory ASC ";
|
||||
|
||||
db:query(sql, function(row)
|
||||
found = true;
|
||||
self:set(
|
||||
row.domain_setting_category,
|
||||
row.domain_setting_subcategory,
|
||||
row.domain_setting_name,
|
||||
row.domain_setting_value
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
if not found then
|
||||
local sql = "SELECT default_setting_uuid,default_setting_category,default_setting_subcategory,default_setting_name,default_setting_value "
|
||||
sql = sql .. "FROM v_default_settings ";
|
||||
sql = sql .. "WHERE default_setting_enabled = 'true' ";
|
||||
sql = sql .. "AND default_setting_category = '" .. category .."'";
|
||||
sql = sql .. "AND default_setting_subcategory = '" .. subcategory .. "'";
|
||||
sql = sql .. "AND default_setting_name = '" .. name .. "'";
|
||||
sql = sql .. "AND default_setting_value is not null ";
|
||||
sql = sql .. "ORDER BY default_setting_category, default_setting_subcategory ASC";
|
||||
|
||||
db:query(sql, function(row)
|
||||
found = true;
|
||||
self:set(
|
||||
row.default_setting_category,
|
||||
row.default_setting_subcategory,
|
||||
row.default_setting_name,
|
||||
row.default_setting_value
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
if not found then
|
||||
self:set(category, subcategory, name, NONE)
|
||||
end
|
||||
|
||||
local a = self._array
|
||||
local v = a[category] and a[category][subcategory] and a[category][subcategory][name]
|
||||
|
||||
if cache.support() then
|
||||
local key = self:_cache_key(category, subcategory, name)
|
||||
local value = v
|
||||
if v ~= NONE and name == 'array' then
|
||||
value = table.concat(v, '/+/')
|
||||
end
|
||||
local exp = expire and expire["settings"] or 3600
|
||||
cache.set(key, value, exp)
|
||||
end
|
||||
|
||||
if type(self._db) == 'string' then
|
||||
db:release()
|
||||
end
|
||||
|
||||
if v == NONE then return nil end
|
||||
return v
|
||||
end
|
||||
|
||||
end
|
||||
-----------------------------------------------------------
|
||||
|
||||
return Settings
|
||||
@@ -3,9 +3,14 @@
|
||||
-- log.noticef("%s %s", "hello", "world")
|
||||
-- -- log if debug.SQL or debug.xml_handler.SQL then
|
||||
-- log.tracef("SQL", "SQL is %s", sql)
|
||||
|
||||
local function log(name, level, msg)
|
||||
freeswitch.consoleLog(level, "[" .. name .. "] " .. msg .. "\n")
|
||||
local log if freeswitch then
|
||||
log = function (name, level, msg)
|
||||
freeswitch.consoleLog(level, "[" .. name .. "] " .. msg .. "\n")
|
||||
end
|
||||
else
|
||||
log = function (name, level, msg)
|
||||
print(os.date("%Y-%m-%d %X") .. '[' .. level:upper() .. '] [' .. name .. '] ' .. msg)
|
||||
end
|
||||
end
|
||||
|
||||
local function logf(name, level, ...)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
if freeswitch then
|
||||
|
||||
function sleep(ms)
|
||||
freeswitch.msleep(ms)
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
local socket = require "socket"
|
||||
|
||||
function sleep(ms)
|
||||
socket.sleep(ms/1000)
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user