アスタリスクのLUAダイヤルプラン

すべてにご挨拶。 むかしむかし、アスタリスクでダイヤルプランを作成するときにluaプログラミング言語を使用するというトピックは、私にとって非常に困難でした。 実際のところ、アスタリスクを設定するときに、さまざまなGUI(FreePBXなど)を使用することを強く嫌います。



初めてすべてをセットアップするとき、私は通常のlinear extensions.confを使用しました。 時間が経つと、電話機能のニーズが高まりました。 Luaは少しずつ学びました。 そして、私は私たちの都市の1つの大企業(1つの大規模な不動産会社)で管理者として働くようになりました-その当時、約45の支店、約650-700のユーザー、都市間などがありました アスタリスクはすでにそこにありましたが、すべてがFreePBXを使用してセットアップされました。



ほとんどすぐに、指導者はアスタリスクのベルとホイッスルに関するさまざまな質問で私を圧倒し始めました。 たとえば、ある支店への着信コール、支店内のコールをランダムに配信することを望んでいました。 彼らはmp3で会話を録音したかったので、一般的なグループを作成したかったので、一般的にすべてのブランチを含めることができます。 タスクは単純に思えますが、個人的に座って、グラフィカルインターフェイスを使用してこのような問題を解決することさえ、私にとってはあまり面白くありませんでした。



もう1つの重要なポイントがありました。当時の一般的な電話の品質はひどいものでした。 声が絶え間なくゴロゴロしている、通話が途切れる、加入者が聞こえない、アスター自体が頻繁にクラッシュするなど。 ダイヤルプランのファイルを見ると、サイズは16 mbです。 テキストエディタで開きました-そして、私は何ができますか? 数百万行あります。



私はそれをやり直すことに決め、すべてをルアに投げました。 開発の開始から数日後、私はluaでダイヤルプランの最初のプロトタイプを発表することができましたが、既存の「機能」や「ささいなこと」はありませんでした。 それらを古い構成全体に置き換えてから、もう1週間、管理者が見たいと思っていた主な付加機能を鳴らしました。 また、アスター自体を11番目のバージョンに更新しました(その時点では11.3.0のようです)。 さらにその過程で、時々彼はダイヤルプランファイルをちらっと見て、彼自身がマニュアルを望んでいるか、望んでいたものを見た。 その結果、luaのダイヤルプランを使用したアスターは、前回よりもはるかに高速で安定して動作しました。



「ステーション」が機能した条件:



cpu:intel xeon e5520(間違っていない場合)

ラム:24GB

および2つのギガビットネットワークインターフェイスとreyd1を含むその他のハードウェアパラメータ

外部加入者の数:約700

トランクの数:約10(うち2つはプロバイダーで、残りはaddpack gsmゲートウェイです)。

「都市」番号の数:約200(1つのプロバイダーから150の番号、2番目から約50またはそれ以上)。



ここの都市番号は各支店に割り当てられました。 一部のブランチには、2つまたは3つの数字さえあります。 都市からのすべての呼び出しがコンテキストに飛んだため、分析を行い、呼び出しを目的のブランチに転送しました。



luaを使用して、彼はリンググループを実現し、加入者を呼び出すための2つのオプションを作成しました-ランダムとグループ内のリスト順(使用中の加入者を除く)。 lua-sqlをねじ込んで、独自の呼び出しデータベースを記録しました(cdrに追加)。 これは、このために行われました。従業員は携帯電話でクライアントに電話しますが、クライアントは話をしたくありません(忙しいなど)。 しばらくすると、彼は以前に定義した番号にコールバックし、以前に彼を呼び出した同じ従業員に連絡しなければなりません。 「モバイルへの呼び出し」イベントを別のデータベースに記録しました。 クライアントがセルからコールバックするとき、「セルからのコール」イベントで最後のコールをピックアップし、クライアントを適切な従業員に渡します。 そのような従業員が1人だけ記憶されていました。 つまり 別の顧客がこの顧客に電話した場合。 その後、それに応じて、呼び出しが彼に戻ります。



今、私はその会社では働いておらず、現在の場所です。古いPBXをアスタリスクに変更し、もちろん昔の時間を使用しています。 このトピックは私だけでなく興味深いものであることを思い出しました。 さて、このトピックに関する情報はほとんどないため、この記事をここに掲載することにしました。突然、誰かに役立つでしょう。



次に、トピックの本質であるluaでのコーディングに目を向けます。 pbx_luaモジュールの切り替えの段階については説明しません-多くの情報があります。 たとえば、現在、Centos 6.6を使用していますが、ドレーンにはすでにluaがあります。 lua-develパッケージをドッキングし、menuselectにpbx_luaモジュールを含めました。



さらに、誰かがmysql(または別のデータベース)への手動接続を使用する場合、最初にluarocksをインストールし、そこからこのアドオンをダウンロードして、lua-sqlパッケージを削除することをお勧めします。



さらにダイヤルプラン自体では、次のようなユーザーを記述してルールを設定できます。



extensions = { }; local_ext = { --  .      . h = function() --    (hangup) app.stopmixmonitor() d_status = channel["DIALSTATUS"]:get() if d_status ~= nil then app.noop("Dial over with status:"..d_status) -- ,    ,       cdr if d_status ~= "ANSWER" then channel["CDR(recordingfile)"]:set("") end app.noop("Good buy!") app.hangup() end; app.hangup() end; ["_14XXX"] = call_local; ["_21XX"] = call_local; ["_4595"] = call_all; --    ,   .         ["_*99"] = function() --       dnd (). local cid, dnd app.answer() cid = channel["CALLERID(num)"]:get() dnd = channel["DB(DND/"..cid.."/)"]:get() app.noop("DND:"..dnd) if dnd == "1" then channel["DB_DELETE(DND/"..cid.."/)"]:get() app.playback("beep") app.playback("beep") app.hangup() else channel["DB(DND/"..cid.."/)"]:set("1") app.playback("beep") app.wait(1) app.hangup() end end; include = {"mobile_out"}; };
      
      





ここ[[_XXnumber "]はテンプレートです。 つまり すべてが通常のextensions.confと同じです。

call_local-この説明によって参照される関数。 つまり たとえば、14555をダイヤルすると、call_local関数が呼び出されます。 また、この関数は、外部から電話がかかってきたときに呼び出すことができます。



 function call_local(ctx,ext) local callerid,cf,uniq,chn local n,j,i n = string.sub(ext,3) --   2   if n == "90" or n == "79" or n == "80" then --    90  ..         j = channel["CALLERID(num)"]:get() app.noop(string.format("Using ring group %s from %s",ext,j)) dial_rg(shuffle(r_group[ext],nil)) --       end --     "",         cf = channel["DB(CF/"..ext.."/"..")"]:get() app.noop("CF:"..cf) if cf ~= "" then app.noop(string.format("Call forward detected from %s to %s",ext,cf)) app.goto("mobile_out",cf,"1") end callerid = channel["CALLERID(num)"]:get() app.noop(string.format("Trying to local call %s from %s",ext,callerid)) if ext ~= "4550" and (CheckChannel(ext)) ~= NOT_INUSE then return end uniq = channel.UNIQUEID:get() chn = channel["CHANNEL"]:get() app.noop(string.format("UNIQUEID: %s",uniq)) app.noop(string.format("CHANNEL: %s",chn)) app.noop(string.format("CALLERID_name: %s",callerid)) app.noop(string.format("EXTEN: %s",ext)) app.noop(string.format("CONTEXT: %s",ctx)) record(string.format("%s-%s-%s",callerid,ext,uniq)) if ext == "4550" then local support = CallSupport(callerid) if support == "failed" then return end end if ext == "4514" or ext == "4592" then app.noop("Redirect!!!") app.dial("SIP/4591,60,tT") end app.dial(string.format("SIP/%s,60,tT",ext)) end
      
      





一部のグループとステータスにはいくつかのチェックがあります。 たとえば、4550はテクニカルサポートグループです。 それには別の機能があり、従業員の雇用の処理、「外部クライアント」への通知、ジャーナルの記録、不在着信アラートのジャバー経由のテクニカルサポートへのリセットがあります。



着信側がグループの場合、リストを混ぜてランダムな相手に電話をかけます。



グループからサブスクライバーを呼び出すためにランダムメソッドを使用するのはなぜですか? ブランチは、本質的にセールスマネージャーです。 支店の従業員を順番に呼び出すと、リストの最初の従業員の売上が常に他の従業員よりも多くなります(不正行為)。 状況はmem-primariメソッド(と思われます)と同様で、最後に回答したユーザーは無視されます。 ランダムミキシングの方法はより正直で、すべての「営業担当者」を対等な立場に置きます。 もちろん、全員に電話をかけることもできます(全員に同時に電話をかけます)が、ブランチでは、すべての電話が同時に「叫んでいる」と不満を訴え始めます。



ランダムコールの場合、キューも使用できますが、ほとんど使用しません。 理由はわかりませんが、それは起こりました。



さらに、都市から来て、説明:



 from_trunk = { h = function() app.noop("BBBBBBBLLLLAAAAHHHHHH!!!!!!!") app.stopmixmonitor() if d_status ~= nil then d_status = channel["DIALSTATUS"]:get() app.noop("Dial over with status:"..d_status) if d_status ~= "ANSWER" then channel["CDR(recordingfile)"]:set("") end exten = "" uniqid = "" app.noop("Good buy!") app.hangup() end app.noop("Some problem!!!") app.hangUP() end; ["f1"] = function(e) --  ,      ... app.goto("local_ext",e,1) end; ["_."] = foo; --  ,     foobar... include = {"local_ext"} }
      
      







次に、すべての外部受信ボックスをfooでラップします。



 function foo(ctx,ext) local chn tmptab.did = ext tmptab.rg = g_tab[ext] if tmptab.did == "99051000227736" then --        . . app.noop("Skype TEST!!!") app.dial("SIP/14553,,tT,M(bar)") end tmptab.callerid = channel["CALLERID(num)"]:get() if string.find(tmptab.callerid,"88005550678",1) then app.hungup() end -- - ... if string.find(tmptab.callerid,"79",1) then --    , -      tmptab.callerid = "8"..string.sub(tmptab.callerid,2) channel["CALLERID(all)"]:set(tmptab.callerid) end chn = channel["CHANNEL"]:get() app.noop("CHANNEL:"..chn) if string.find(chn,"SIP/gsm_",1) then --      gsm  app.noop("Found channel "..chn) --       mysql       num = sql.mobile_get(tmptab.callerid) if num then app.goto("local_ext",num,1) end end app.noop("CallerID(num):"..tmptab.callerid) app.noop("by context:"..ctx) app.noop("DID:"..tmptab.did) app.set("CDR(did)="..tmptab.did) if tmptab.did == "4595" then call_all(tmptab.did) end -- 4595       app.answer() app.wait(1) j = channel["DB(ENUM/"..tmptab.did.."/)"]:get() app.noop("tag = "..j) if j == "ngs_rec" then dial_rg(shuffle(tmptab.rg),1) else if tmptab.did ~= "3471234" then --  ,    !!! app.playback(mhold.comp_hello) if tmptab.did == "3472345" then app.goto("local_ext","4591",1) end ivr(tmptab.did) -- ,     ,     ... dial_rg(shuffle(tmptab.rg),nil) else app.noop("BLAH DETECTED") dial_rg(tmptab.rg,nil) end end app.noop("hungup?") end
      
      







この場合、携帯電話番号と外部番号を決定しました(did)。 発信者がローカルセルラー(gsmゲートウェイのSIMカード)に電話する場合、最後の発信者を取得し、このクライアントを彼に送信します。 ngs_recリストからの定義もあります。 ここでは、数字は「広告」として事前定義されています。 それは古い方法でした(それからそれをやり直しましたが、このバージョンのコードを私が取ったファイルのこのバージョンではそうではありません)。 すべての広告番号をオフィス内の特別な番号に送信し、広告に示されている番号への呼び出しがあったことをデータベースにメモします。



今のところ、これで十分なコードだと思います。 誰かが古いダイヤルプランからluaへの切り替えに興味を持つようになった場合、いくつかのことをさらに詳しく説明し続けることができると思います。 ただし、誰かがLuaでプログラムする方法をすでに知っている場合は、まったく問題はありません。



結論として、もちろん、今日、VoxImplantなどのさまざまなトリックアウトされたソリューションがたくさんあります。 多くの人は、コンソールでの作業や何かのコーディングに慣れていません。 ただし、会社の規模が大きい場合(サブスクライバー50人以上)、グラフィカルインターフェイスでボタンとチェックマークを使用して「ステーション」のロジックを構築すると、最終的に問題が発生する可能性があります。 上記の記事の冒頭で、ゴボゴボと崖について例を挙げました。 luaのダイヤルプランの重量はわずか24kb、968行です。



ほぼ700人の加入者が問題なく作業しました。



それだけです さようならみんな!



All Articles