Ecrire un PBX en Perl pour Yate

Un jour, j'écrirai quelque chose comme "Comment je suis devenu programmeur à 40 ans" . Mais certainement pas aujourd'hui, d'ailleurs je n'ai plus 40 ans et je ne me considère pas comme un programmeur. Et je voudrais vous parler de mon expérience dans le développement de PBX pour mes propres besoins. Yate est utilisé comme moteur VoIP , le front et le backend seront en Perl.





Je rencontre souvent des questions dans les commentaires des articles: "Pourquoi pas (les options préférées des commentateurs suivent)?" Donc, dans l'ordre.





Pourquoi

Pourquoi pas Asterisk, FreeSwitch, Kamailio et autres. Si ma mémoire est bonne, alors il y a 12-13 ans, c'est avec Asterisk que ma connaissance du monde de la téléphonie SIP a commencé, puisque le seuil d'entrée était assez bas, vous pouviez télécharger une image disque prête à l'emploi, où Asterisk lui-même , un museau Web et même des versions rudimentaires des systèmes de facturation. Naturellement, tout cela suscitait un plaisir exubérant, tombait constamment, et après un réglage réussi, il valait mieux ne pas y toucher. Je me souviens que nous avons même essayé de vendre le service de téléphonie SIP à nos clients, mais à un moment donné, tout cela a nécessité l'obtention de licences et cela est devenu simplement économiquement non rentable pour notre clientèle. Plus tard, pendant longtemps, j'ai utilisé Asterisk uniquement comme un PBX de bureau, jusqu'à ce que je me lasse de constamment abandonner / geler le service sur FreeBSD (je réponds à l'avance à la question "pourquoi pas Linux", -"parce que glaïeul" ). En règle générale, les expériences avec d'autres moteurs ne se sont soldées par rien en raison du manque d'interface graphique adéquate ou de la difficulté de configuration (ici, j'ai un peu exagéré, en fait, j'ai maintenant deux installations FreeSwitch qui fonctionnent depuis plusieurs années. sans aucune interférence) ... En surfant sur le net, je suis tombé sur Yate, à mon avis la 2ème version alors. La première chose que j'ai aimée était le minimum de paramètres requis pour commencer à appeler, peut-être nulle part ailleurs je n'ai rencontré un paramètre plus simple. Deuxièmement, il existe une webcam simple, FreeSentralcouvrant 90 pour cent d'une configuration PBX de bureau. Et troisièmement, peut-être la chose la plus importante - tout fonctionne hors de la boîte. Ce que je veux dire quand je dis "tout fonctionne", c'est bien sûr travailler derrière NAT et DTMF, quel que soit le matériel / logiciel côté client. C'est peut-être juste moi qui ai eu la chance, même si j'ai dû travailler avec un tas de morceaux de fer de long à cisco, qui, sans danser avec un tambourin, avec le même astérisque, par exemple, ne transféraient pas de dtmf. Une documentation médiocre et des exemples cassés sont peut-être le principal inconvénient du projet. Autrement dit, s'il y a un désir de faire quelque chose de sérieux, vous devrez aller dans les sources 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;
}
  

      
      



En fait, j'ai plus de questions sur le travail avec Yate maintenant qu'au début. Par exemple, je ne peux tout simplement pas comprendre pourquoi dtmf vole dans les appels transférés, ce qui n'est pas dans le module pbx natif, etc. En gros, le but de cet article est de commenter l'implémentation de Perl. C'est dommage que les développeurs aient abandonné leur projet, même si d'un autre côté, il y a déjà des fonctionnalités au-dessus du toit, de WebRTC à Jabber, et ce n'est pas un fait que plus sera mieux. Les gars régissent les erreurs critiques dans le noyau, bien que mon ticket avec le patch soit suspendu depuis plusieurs années, mais encore une fois, ce n'est pas une erreur dans le noyau, mais dans un module rarement utilisé et c'est plutôt un cas particulier, car avec un structure de base de données correcte, une erreur est tout simplement impossible.








All Articles