アスタリスク-fi、それは悪いマナーです
この素晴らしいリソースの読者の皆様、こんにちは。伝統的に、私は長年のhabrリーダーですが、今だけ投稿することにしました。実際、何があなたに書くように促しましたか?正直なところ、私は自分自身を知りません。FreeSWITCH / Yate / 3CX /などのパフォーマンスとAsteriskとの比較に関する記事、またはAsteriskのアーキテクチャの実際の問題、あるいは何かユニークなことをしたいという願望のいずれか。
そして驚くべきことに、最初のケースでは、原則として、彼らは柔らかくて暖かい、いわばFreeSWITCH / Yate / etcとFreePBXを比較します。はい、FreePBXです。これはタイプミスではありません。また、すべての比較で、デフォルト構成にアスタリスクが1つあることが多いのは興味深いことです。ご存知のとおり、この構成には、使用可能なすべてのモジュール、ダイヤルプラン曲線(FreePBXのような貢献)、およびその他の多数のバイアスがロードされています。一般的なアスタリスクの痛みについては-はい、客観的に彼らの馬車と小さなカート。
これをどうするか?ステレオタイプを打ち破り、出生時のトラウマを修復します。これが私たちがすることです。
ヘビと生け垣を渡る
初心者の多くは、アスタリスクでダイヤルプランを記述するための構文を見ることに不快感を覚えます。また、デフォルトの形式でダイヤルプランを作成する必要があるため、別のテレフォニーサーバーの選択を真剣に正当化する人もいます。同様に、複数行のXMLをシャブリングすることは、快適さの高さです。はい、LUA / AELを使用することは可能です。これは良いことです。しかし、個人的には、これを不利な点として、特にpbx_luaに関して分類します。
, — . , . , , , , , .., .. , , . – , .
, Asterisk' pbx_lua, Yate , FreeSWITCH , "overhead" . , , , . :
- Asterisk, . ARI , , 12- . , - 1.8/1.6, 1.4, .
- Lua — , . , , .
- Lunapark — github', voip-.
Lunapark . , AMI- FastAGI, . , ARI AGI AMI .
: ? Asterisk REST Interface, ! . , ARI : , , , "" , WebSockets , , , XML/JSON — . , , , . — . — - , .
? FastAGI-, , pbx_lua . Asterisk’ , FastAGI- AMI-. , FastAGI-, , , NewChannel. ARI, , stasis' ARI .
Lunapark , . "shared data". , . — , , - .
?
— ? , , . , . .
:
[test]
exten => _XXX/102,1,Hangup()
exten => _XXX,1,Dial(SIP/${EXTEN})
, 102. , , extended CallerID . , , CallerIDName , , regexp, . , , , :
[test]
exten => _XXX/102,1,Hangup()
; CallerIDName
exten => _XXX,1,ExecIf($[ "${CALLERID(name)}" == "Vasya" ]?Hangup())
;
exten => _XXX,n,ExecIf($[ "${CHANNEL(state)}" != "Ring" ]?Hangup())
;
exten => _XXX,n,ExecIf($[ "${CUT(CUT(CHANNEL,-,1),/,2)}" == "333" ]?Hangup())
exten => _XXX,n,Dial(SIP/${EXTEN})
, , Hangup', extensions.conf Goto, GoSub, Macro , , Local.
— .
:
${Exten}:match('%d%d%d')
and
(
${CallerIDNum}:match('201') or
${CallerIDName}:match('Vasya') or
${State}:lower() ~= 'ring' or
${Channel}:match('^[^/]+/([^%-]+)') == '333'
) => Hangup();
${Exten}:match('%d%d%d') => Dial {callee = ('SIP/%s'):format(${Exten})};
, , . , regexp' , , , .
, .
Lunapark pbx_lua. . ${...} , ('...'). .
, :
-- Exten = 123
-- Sate = Ring
-- CallerIDNum = 100
-- CallerIDName = Test
-- Channel = SIP/100-00000012c
if ('123'):match('%d%d%d') and
(
('100'):match('201') or
('Test'):match('Vasya') or
('Ring'):lower() ~= 'ring' or
('SIP/100-00000012c'):match('^[^/]+/([^%-]+)') == '333'
) then
Hangup()
end
if ('123'):match('%d%d%d') then
Dial {callee = ('SIP/%s'):format(('123'))}
end
fmt syntax :
local fmt = function(str, tab)
return (str:gsub('(%${[^}{]+})', function(w)
local mark = w:sub(3, -2)
return (mark:gsub('(.+)',function(v)
local out = tab[v] or v
return ("('%s')"):format(out)
end))
end))
end
local syntax = function(str)
return (str:gsub('([^;]+)=>([^;]+)',function(p,r)
return ([[
if %s then
%s
end
]]):format(p,r)
end))
end
, . — , . routes.
local routes = function(...)
local conf, content = ...
local f, err = io.open(conf, "r")
if io.type(f) ~= 'file' then
log.warn(err) -- LOG Lunapark'
return ""
else
content = f:read('*all')
end
f:close() return content
end
: Lunapark . — Lunapark handler'. , FastAGI- AMI .
, AMI — , AMI-, AMI . , extensions.conf.
[default]
exten => _[hit],1,NoOp()
exten => _.,n,Wait(5)
exten => _.,1,AGI(agi://127.0.0.1/${EXTEN}${IF($[ "X${PRMS}" != "X" ]?"?${PRMS}")})
Wait(5) FastAGI-, , Redirect default ${EXTEN}.
, Lunapark', FastAGI-.
-- rules
local rules = routes('routes.conf')
-- ,
-- HUP/QUIT
ami.removeEvents('*')
--
ami.addEvents {
['newchannel'] = function(e)
-- , users
if (e['Context'] and e['Context']:match('users')) and e['Exten'] then
-- , , FastAGI
local step
-- FatsAGI
local count = 0
--
local code, err = loadstring(syntax(fmt(rules,e)))
-- ,
if type(code) == 'function' then
-- FastAGI
setfenv(code,setmetatable({indexes = {}},{__index = function(t,k)
--
return coroutine.wrap(
function(...)
local prms = {} -- FastAGI
local owner = t --
local event = e -- event
local thread = coroutine.running() -- ID
-- URI
for p,v in pairs({...}) do
if type(v) == 'table' then
for key, val in pairs(v) do
table.insert(prms,("%s=%s"):format(key,val))
end
else
table.insert(prms,("%s=%s"):format(p,v))
end
end
-- FastAGI
if step then
--
local last = ("%s"):format(step)
-- UserEvent .
-- indexes( )
--
table.insert(owner['indexes'],ami.addEvent('UserEvent',function(evt)
-- AGIStatus
-- ,
if (evt['Channel'] and evt['Channel'] == event['Channel'])
and
(evt['UserEvent'] and evt['UserEvent']:match('AGIStatus'))
and
(evt['Script'] and evt['Script'] == last)
then
--
--
--
if owner['indexes'][count] == thread then
if coroutine.status(thread) ~= 'dead' then
coroutine.resume(thread)
end
end
end
end,thread))
-- FastAGI
step = k
--
coroutine.yield()
else -- FastAGI
local index -- Hangup
-- FastAGI
step = k
-- Hangup
--
index = ami.addEvent('Hangup',function(evt)
if evt['Channel'] and evt['Channel'] == event['Channel'] then
-- Hangup
ami.removeEvent('Hangup',index)
--
for _,v in pairs(owner['indexes']) do
ami.removeEvent('UserEvent',v)
end
--
owner = nil
end
end,thread)
end
-- AMI
ami.setvar{
Value = table.concat(prms,'&'),
Channel = event['Channel'],
Variable = 'PRMS'
}
-- AGI- default
ami.redirect{
Exten = k,
Priority = 1,
Channel = event['Channel'],
Context = 'default'
}
--
count = count + 1
end)
end}))()
else
-- -
log.warn(err)
end
end
end
}
, , , . , . , . , , , ..
, . — , redirect . , , FastAGI-. Lunapark UserEvent FastAGI- — . default , , PRMS.
, redirect' handler, AGI . Hangup() Dial(). .
function Hangup(...)
local app, channel = ... -- pbx_lua
app.verbose(('The Channel %s does not match by routing rules'):format(channel.get('CHANNEL')))
app.hangup()
end
function Dial(...)
local app, channel = ...
local leg = app.agi.params['callee'] or ''
app.verbose(('Trying to make a call from %s to %s'):format(
channel.get('CALLERID(num)'),
leg:match('^[^/]+/([^%-]+)'))
)
app.dial(leg)
end
, —
, . ?
- , ;
- VoIP-. Queue, , asterisk';
- , VoIP-, asterisk' Mediahub, VoIP- ;
- 非常にシンプルで拡張可能で非常に柔軟なスクリプト言語を使用してVoIPアプリケーションを作成する機能。
- VoIPアプリケーションからの外部システムとの統合の可能性を拡大しました。
誰もがそうですが、私はまだすべてが好きです。
local fmt = function(str, tab)
return (str:gsub('(%${[^}{]+})', function(w)
local mark = w:sub(3, -2)
return (mark:gsub('(.+)',function(v)
local out = tab[v] or v
return ("('%s')"):format(out)
end))
end))
end
local syntax = function(str)
return (str:gsub('([^;]+)=>([^;]+)',function(p,r)
return ([[
if %s then
%s
end
]]):format(p,r)
end))
end
local routes = function(...)
local conf, content = ...
local f, err = io.open(conf, "r")
if io.type(f) ~= 'file' then
log.warn(err) -- LOG Lunapark'
return ""
else
content = f:read('*all')
end
f:close() return content
end
-- rules
local rules = routes('routes.conf')
-- ,
-- HUP/QUIT
ami.removeEvents('*')
--
ami.addEvents {
['newchannel'] = function(e)
-- , users
if (e['Context'] and e['Context']:match('users')) and e['Exten'] then
local step -- , , FastAGI
local count = 0 -- FatsAGI
--
local code, err = loadstring(syntax(fmt(rules,e)))
-- ,
if type(code) == 'function' then
-- FastAGI
setfenv(code,setmetatable({indexes = {}},{__index = function(t,k)
--
return coroutine.wrap(
function(...)
local prms = {} -- FastAGI
local owner = t --
local event = e -- event
local thread = coroutine.running() -- ID
-- URI
for p,v in pairs({...}) do
if type(v) == 'table' then
for key, val in pairs(v) do
table.insert(prms,("%s=%s"):format(key,val))
end
else
table.insert(prms,("%s=%s"):format(p,v))
end
end
-- FastAGI
if step then
--
local last = ("%s"):format(step)
-- UserEvent .
-- indexes( )
--
table.insert(owner['indexes'],ami.addEvent('UserEvent',function(evt)
-- AGIStatus
-- ,
if (evt['Channel'] and evt['Channel'] == event['Channel'])
and
(evt['UserEvent'] and evt['UserEvent']:match('AGIStatus'))
and
(evt['Script'] and evt['Script'] == last)
then
--
--
--
if owner['indexes'][count] == thread then
if coroutine.status(thread) ~= 'dead' then
coroutine.resume(thread)
end
end
end
end,thread))
-- FastAGI
step = k
--
coroutine.yield()
else -- FastAGI
local index -- Hangup
-- FastAGI
step = k
-- Hangup
--
index = ami.addEvent('Hangup',function(evt)
if evt['Channel'] and evt['Channel'] == event['Channel'] then
-- Hangup
ami.removeEvent('Hangup',index)
--
for _,v in pairs(owner['indexes']) do
ami.removeEvent('UserEvent',v)
end
--
owner = nil
end
end,thread)
end
-- AMI
ami.setvar{
Value = table.concat(prms,'&'),
Channel = event['Channel'],
Variable = 'PRMS'
}
-- AGI- default
ami.redirect{
Exten = k,
Priority = 1,
Channel = event['Channel'],
Context = 'default'
}
--
count = count + 1
end)
end}))()
else
-- -
log.warn(err)
end
end
end
}
function Hangup(...)
local app, channel = ... -- pbx_lua
app.verbose(('The Channel %s does not match by routing rules'):format(channel.get('CHANNEL')))
app.hangup()
end
function Dial(...)
local app, channel = ...
local leg = app.agi.params['callee'] or ''
app.verbose(('Trying to make a call from %s to %s'):format(
channel.get('CALLERID(num)'),
leg:match('^[^/]+/([^%-]+)'))
)
app.dial(leg)
end