Suatu hari nanti saya akan menulis sesuatu seperti "Bagaimana saya menjadi programmer pada usia 40" . Tapi jelas tidak hari ini, selain itu, saya tidak lagi 40 dan saya tidak menganggap diri saya seorang programmer. Dan saya ingin bercerita tentang pengalaman saya mengembangkan PBX untuk kebutuhan saya sendiri. Yate digunakan sebagai mesin VoIP , front dan backend akan di Perl.
Saya sering menemukan pertanyaan di komentar artikel: "Mengapa tidak (opsi favorit komentator mengikuti)?" Jadi, secara berurutan.
Mengapa
Mengapa tidak Asterisk, FreeSwitch, Kamailio dan lainnya. Jika ingatan saya benar, maka 12-13 tahun yang lalu, dengan Asterisk perkenalan saya dengan dunia telepon SIP dimulai, karena ambang masuk cukup rendah, Anda dapat mengunduh gambar disk yang sudah jadi, di mana Asterisk itu sendiri , moncong web, dan bahkan beberapa versi sistem penagihan yang belum sempurna. Secara alami, semua ini membangkitkan kegembiraan yang meriah, terus-menerus jatuh, dan setelah pengaturan yang berhasil, lebih baik untuk tidak menyentuhnya. Saya ingat kami bahkan mencoba menjual layanan telepon sip kepada klien kami, tetapi pada titik tertentu semua ini memerlukan lisensi dan secara ekonomi tidak menguntungkan bagi basis klien kami. Kemudian, untuk waktu yang lama, saya menggunakan Asterisk hanya sebagai PBX kantor, sampai saya bosan terus-menerus menghentikan / membekukan layanan di FreeBSD (saya menjawab sebelumnya pertanyaan "mengapa bukan Linux", -"karena gladiol" ). Percobaan dengan mesin lain tidak berakhir apa-apa, sebagai aturan, karena kurangnya web gui yang memadai atau kesulitan dalam pengaturan (di sini saya sedikit melebih-lebihkan, sebenarnya, saya sekarang memiliki dua instalasi FreeSwitch yang berfungsi yang telah berfungsi selama beberapa tahun tanpa gangguan apapun) ... Saat menjelajahi internet, saya menemukan Yate, menurut saya versi ke-2 saat itu. Hal pertama yang saya suka adalah pengaturan minimum yang diperlukan untuk mulai menelepon, mungkin di tempat lain saya tidak menemukan pengaturan yang lebih sederhana. Kedua, ada webcam sederhana, FreeSentralmencakup 90 persen dari pengaturan PBX kantor. Dan ketiga, mungkin yang paling penting - semuanya bekerja di luar kotak. Yang saya maksud ketika saya mengatakan "semuanya bekerja", tentu saja, bekerja di belakang NAT dan DTMF, terlepas dari perangkat keras / lunak di sisi klien. Mungkin hanya aku yang begitu beruntung, meskipun aku harus bekerja dengan sekumpulan besi dari long hingga cisco, yang tanpa menari dengan rebana, dengan asterisk yang sama, misalnya, tidak mentransfer dtmf. Dokumentasi yang buruk dan contoh yang rusak mungkin merupakan kelemahan utama proyek ini. Artinya, jika ada keinginan untuk melakukan sesuatu yang serius, Anda harus masuk ke sumber 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;
}
Nyatanya, saya memiliki lebih banyak pertanyaan tentang bekerja dengan Yate sekarang daripada yang saya miliki di awal. Misalnya, saya tidak tahu mengapa dtmf terbang dalam panggilan yang diteruskan, yang tidak ada dalam modul pbx asli, dll. Secara umum, tujuan dari posting ini adalah untuk memberikan komentar tentang implementasi Perl. Sangat disayangkan bahwa para pengembang meninggalkan proyek mereka, meskipun di sisi lain, sudah ada fungsionalitas di atas atap, dari WebRTC hingga Jabber, dan bukan fakta bahwa lebih banyak akan lebih baik. Orang-orang mengatur kesalahan kritis di kernel, meskipun tiket saya dengan tambalan telah menggantung selama beberapa tahun, tetapi sekali lagi, ini bukan kesalahan di kernel, tetapi dalam modul yang jarang digunakan dan lebih merupakan kasus khusus, karena dengan a struktur database yang benar, kesalahan tidak mungkin terjadi.