PBX in Perl für Yate schreiben

Eines Tages werde ich so etwas wie "Wie ich mit 40 Programmierer wurde" schreiben . Aber definitiv nicht heute, außerdem bin ich nicht mehr 40 und betrachte mich nicht als Programmierer. Und ich möchte Ihnen von meinen Erfahrungen bei der Entwicklung von PBX für meine eigenen Bedürfnisse erzählen. Yate wird als VoIP-Engine verwendet , Front- und Backend befinden sich in Perl.





In den Kommentaren zu Artikeln stoße ich häufig auf Fragen: "Warum nicht (die bevorzugten Optionen der Kommentatoren folgen)?" Also in Ordnung.





Warum

Warum nicht Asterisk, FreeSwitch, Kamailio und andere? Wenn mein Gedächtnis mir recht tut, dann begann vor 12-13 Jahren mit Asterisk meine Bekanntschaft mit der Welt der SIP-Telefonie, da die Eintrittsschwelle ziemlich niedrig war, konnten Sie ein fertiges Disk-Image herunterladen, auf dem Asterisk selbst , eine Web-Schnauze und sogar einige rudimentäre Versionen von Abrechnungssystemen. Natürlich erregte all dies überschwängliche Freude, fiel ständig, und nach einer erfolgreichen Einstellung war es besser, es nicht zu berühren. Ich erinnere mich, dass wir sogar versucht haben, den sip-Telefoniedienst an unsere Kunden zu verkaufen, aber irgendwann erforderte dies den Erwerb von Lizenzen und es wurde für unseren Kundenstamm einfach wirtschaftlich unrentabel. Später, lange Zeit, habe ich Asterisk nur als Büro-PBX verwendet, bis ich es satt hatte, den Dienst auf FreeBSD ständig zu löschen / einzufrieren (ich beantworte im Voraus die Frage "Warum nicht Linux", -"weil Gladiolen" ). Experimente mit anderen Engines endeten in der Regel aufgrund des Mangels an adäquater Web-GUI oder der Schwierigkeit beim Einrichten mit nichts (hier habe ich ein wenig übertrieben, tatsächlich habe ich jetzt zwei funktionierende FreeSwitch-Installationen, die seit mehreren Jahren funktionieren ohne Störung) ... Beim Surfen im Internet bin ich auf Yate gestoßen, meiner Meinung nach die 2. Version dann. Das erste, was mir gefallen hat, war das Minimum an Einstellungen, die erforderlich sind, um mit dem Anrufen zu beginnen. Vielleicht bin ich nirgendwo anders auf eine einfachere Einstellung gestoßen. Zweitens gibt es eine einfache Webcam, FreeSentralAbdeckung von 90 Prozent einer Büro-PBX-Einrichtung. Und drittens vielleicht das Wichtigste - alles funktioniert sofort. Was ich meine, wenn ich sage "alles funktioniert", ist natürlich die Arbeit hinter NAT und DTMF, unabhängig von der Hardware / Software auf der Clientseite. Vielleicht hatte nur ich so viel Glück, obwohl ich mit einem Bündel Eisenstücken von Long bis Cisco arbeiten musste, die, ohne mit einem Tamburin zu tanzen, mit demselben Sternchen zum Beispiel kein dtmf übertragen haben. Schlechte Dokumentation und kaputte Beispiele sind möglicherweise der Hauptnachteil des Projekts. Das heißt, wenn der Wunsch besteht, etwas Ernstes zu tun, müssen Sie in die Yate-Quellen gehen.





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;
}
  

      
      



Tatsächlich habe ich jetzt mehr Fragen zur Arbeit mit Yate als zu Beginn. Zum Beispiel kann ich einfach nicht herausfinden, warum dtmf in weitergeleiteten Anrufen fliegt, die nicht im nativen pbx-Modul usw. enthalten sind. Im Großen und Ganzen dient dieser Beitrag dazu, die Perl-Implementierung zu kommentieren. Schade, dass die Entwickler ihr Projekt aufgegeben haben, obwohl es auf der anderen Seite bereits Funktionen über dem Dach gibt, von WebRTC bis Jabber, und es ist keine Tatsache, dass mehr besser sein wird. Die Jungs regieren kritische Fehler im Kernel, obwohl mein Ticket mit dem Patch seit einigen Jahren baumelt, aber auch dies ist kein Fehler im Kernel, sondern in einem selten verwendeten Modul und eher ein Sonderfall, da mit einem korrekte Datenbankstruktur, ein Fehler ist einfach unmöglich.








All Articles