アスタリスクはフォーミュラ1の車であり、通常のバスではありません

アスタリスク-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



All Articles