YateのためにPerlでPBXを書く

いつか「40歳でプログラマーになった経緯」のようなものを書きますしかし、今日は間違いなくそうではありません。さらに、私はもう40歳ではなく、自分自身をプログラマーとは見なしていません。そして、私自身のニーズに合わせてPBXを開発した私の経験についてお話ししたいと思います。YateはVoIPエンジンとして使用され、フロントエンドとバックエンドはPerlになります。





記事へのコメントでよく質問に出くわします:「なぜ(コメンテーターのお気に入りのオプションが続く)?」だから、順番に。





なぜ

Asterisk、FreeSwitch、Kamailioなどを使ってみませんか。私の記憶が正しければ、12〜13年前、SIPテレフォニーの世界についての私の知り合いが始まったのは、Asteriskでした。エントリのしきい値が非常に低かったため、Asterisk自体が含まれる既製のディスクイメージをダウンロードできました。 、Web銃口、さらには課金システムのいくつかの初歩的なバージョン。当然のことながら、これらすべては熱狂的な喜びを引き起こし、絶えず落ちました、そして成功した設定の後、それに触れない方が良いです。私たちはクライアントにSIPテレフォニーサービスを販売しようとしたことさえ覚えていますが、ある時点でこれはすべてライセンスを取得する必要があり、クライアントベースにとって単に経済的に不採算になりました。その後、長い間、私はAsteriskをオフィスのPBXとしてのみ使用していましたが、FreeBSDでサービスを絶えずドロップ/フリーズすることに飽き飽きしていました(「なぜLinuxではないのか」という質問に事前に答えます。「グラジオラスのため」)。他のエンジンでの実験は、適切なWeb GUIの欠如またはセットアップの難しさのために、原則として何も終わりませんでした(ここでは少し誇張しましたが、実際、数年間機能している2つのFreeSwitchインストールがあります干渉なし)..。ネットサーフィンをしていると、Yateに出くわしました。私の意見では2番目のバージョンです。私が最初に気に入ったのは、通話を開始するために必要な最小限の設定でした。おそらく、これほど単純な設定に出くわした場所は他にありません。第二に、シンプルなウェブカメラ、FreeSentralがありますオフィスのPBXセットアップの90%をカバーします。そして第三に、おそらく最も重要なこと-すべてが箱から出して動作します。 「すべてが機能する」と言うときは、もちろん、クライアント側のハードウェア/ソフトウェアに関係なく、NATとDTMFの背後で機能します。たぶん、とても幸運だったのは私だけでしたが、たとえば、同じアスタリスクを使用して、タンバリンで踊らずにdtmfを転送しなかった、長いものからCiscoまでの鉄片を扱う必要がありました。不十分なドキュメントと壊れた例は、おそらくプロジェクトの主な欠点です。つまり、何か深刻なことをしたいという願望がある場合は、Yateの情報源にアクセスする必要があります。





2- , - , jail 2-3 . , - php. freesentral . - , , . , , Yate . ...





. . - . , , . , Perl. Abiils, .





Yate , Perl, Vasily i. Redkin github.





. Yate , - . - clang 64- FreeBSD, - . , PBX , C++ , , , mysql psql( ). , Perl .





. yate.conf [modules]. / ( , ):





[modules]
;      SIP
ysipchan.yate=yes
; 
wavefile.yate=yes
; CDR
cdrbuild.yate=yes
;        
cdrcombine.yate=yes
;   
moh.yate=yes
; 
rmanager.yate=yes
; 
register.yate=yes
;
tonegen.yate=yes
;       (Perl, PHP, JS  )
extmodule.yate=yes
; RTP
yrtpchan.yate=yes
;
openssl.yate=yes
;,     
dumbchan.yate=yes
;    , -   ,
;    .
msgsniff.yate=yes
; ,     ,    
park.yate=yes
      
      



extmodule.conf. :





;   ,      scripts
[scripts]
pbx_route.pl=

;    ,  
[listener tcp5039]
type=tcp
addr=10.0.0.7
port=5039
      
      



, , , . - PHP, . Perl . Yate vir', .





. pbx_route.pl:





#!/usr/bin/perl -w
#
use strict;
use warnings;

#  @INC  
BEGIN {
    use FindBin '$Bin';
    our $libpath = $Bin . '/../';
    my $sql_type = 'mysql';
    unshift( @INC,
        $libpath . "Abills/$sql_type/",
        $libpath . '/lib/',
        $libpath . "Abills/modules" );
}

use Abills::SQL;

#     Yate
use Pbx::Yate;
#       
use Pbx::Pbx;

my $message = Yate->new();
my $Pbx = Pbx->new($db, $message, \%conf);

# 
trunks_init($message);

#    
$message->install('call.answered', \&call_answered_handler, 50);
$message->install('call.route', \&call_route_handler);
$message->install_watcher('call.execute', \&call_execute_handler, 50);
$message->install('chan.hangup', \&chan_hangup_handler);
$message->install('chan.disconnected', \&chan_disconnected_handler, 10);
$message->install('chan.dtmf', \&chan_dtmf_handler, 50);
$message->install('user.auth', \&user_auth_handler);
#$message->install('user.authfail', \&user_authfail);
$message->install('user.register', \&user_register_handler);
$message->install('user.unregister', \&user_unregister_handler);
$message->install('user.notify', \&user_notify_handler);
$message->install_watcher("engine.timer", \&engine_timer_handler);

#  
$message->listen();

sub trunks_init {
  my $message = shift;
  my ($attr) = @_;
  #  
  my $trunks = $Pbx->trunk_list({
    ACCOUNT      => '_SHOW',
    PROTOCOL     => '_SHOW',
    USERNAME     => '_SHOW',
    PASSWORD     => '_SHOW',
    REGISTRAR    => '_SHOW',
    LOCALADDRESS => '_SHOW',
    OUTBOUND     => '_SHOW',
    DOMAIN       => '_SHOW',
    ENABLED      => 1,
    INTERVAL     => '_SHOW',
    OPTIONS      => '_SHOW',
    COLS_NAME    => 1
  });

  if ($trunks) {
    foreach my $tr (@$trunks) {
      $message->message('user.login', undef, undef, %$tr );# 
    }
  }
}

#   
sub call_route_handler {
    my $message = shift;
    my $id = $message->param('id');
    my $called = $message->param('called');
    my $caller = $message->param('caller');
    #   
    $called =~ s/\+//g;
    #  
    my $call_type = ($Pbx->extensions_list({ NUMBER => $called, LIST2HASH => 'number,location' })) ? 'to_internal' : 'to_external';
    
    #     ,
    #       
    if ($Pbx->get_route($called)) {
      $message->params($Pbx->{params});
      $message->param('call_type', $call_type);
      $message->param('copyparams', 'maxcall,call_type,pbx_from');
      delete $Pbx->{params};
      return $Pbx->{location}
    }

    return 'noroute'
}

# 
sub user_auth_handler {
    my $message = shift;
    my $user = $message->param('username');
    if ($user) {
      my $auth = $Pbx->extensions_list({ NUMBER => $user, PASSWORD => '_SHOW', COLS_NAME => 1 });
      if ($auth) {
        return $auth->{password};
      }
    }
    return undef;
}

#    
sub user_register_handler($) {
    my $message = shift;
    $Pbx->update_location({
      LOCATION => $message->param('data'),
      CONN_ID  => $message->param('connection_id'),
      EXPIRES  => $message->param('expires'),
      NUMBER   => $message->param('number')
    }); 
    return 'true'
}

sub user_unregister_handler($) {
    my $message = shift;
    $Pbx->update_location({
      CONN_ID  => '',
      NUMBER   => $message->param('number')
    });
    return 'true'
}

#     
sub user_notify_handler($) {
    my $message = shift;
    my $account = $message->param('account');
    my $status = ($message->param('registered') ne 'false') ? 0 : 1;
    $Pbx->query2("UPDATE pbx_trunks SET status=$status WHERE account='$account';", 'do');
    return undef;
}

      
      



, , dtmf, -. , IVR. :





#   
#id -    ,
#replace -    ,  
$message->message('chan.attach', undef,'',
  replace => 'true',
  source => "wave/play/hi.wav",
  notify => $id,
  id => $id
);
    
#      
#   'eof',    wavefile.yate
#         'chan.notify'
#    ,    
my $handl;
$message->install('chan.notify', $handl = sub {
		$message->message('chan.attach', undef, '',
      replace => 'true',
      source => "wave/play/hi.wav",
      notify => $id,
      id => $id
    )
  }, 50, 'reason', 'eof');
  
#       .
#      , -
#      
#  ,      
#caller    -   
#         CDR
sub pbx_call {
  my ($attr) = @_;
  #  
  my $info = $admin->list({
  	SIP_NUMBER => '_SHOW',
    AID => $admin->{AID},
    COLS_NAME => 1
  });
  my $message = Yate->new();
  #  ID  
  my $msgid = $message->generate_id;
  #   ,     extmodule.conf
  $message->connect("10.0.0.7:5039");
  $message->message('call.execute', undef, $msgid,
    message    => 'call.execute',
    direct     => $Pbx->build_location($info->[0]->{sip_number}),
    caller     => $FORM{PHONE},# , -     
    callto     => "dumb/",#   
    callback   => $FORM{PHONE},
    cdrwrite   => 'false',
    cdrtrack   => 'false',
    target     => $info->[0]->{sip_number},
  );
  return 1;
}
  

      
      



実際、Yateでの作業については、当初よりも多くの質問があります。たとえば、ネイティブのpbxモジュールなどにない転送された通話でdtmfが飛んでいる理由がわかりません。概して、この投稿の目的は、Perlの実装についてコメントすることです。開発者がプロ​​ジェクトを放棄したのは残念ですが、一方で、WebRTCからJabberまで、すでに屋根の上に機能があり、より多くの方が優れているというのは事実ではありません。パッチのチケットが数年間ぶら下がっていますが、彼らはカーネルの重大なエラーを支配していますが、これもカーネルのエラーではなく、めったに使用されないモジュールであり、正しいデータベース構造、エラーは単に不可能です。








All Articles