#!/usr/bin/perl -w # # Script notifies user on incoming call at selected link. # CallerID and CallerIDName is remembered and user can redial them. # # Written by: Petr Kristan # # This script is distributed under GPL licence - http://www.gnu.org/licenses/gpl.txt # If you make modifications, please consider sending them to me. use warnings; use strict; use locale; use Gtk2; use Gtk2::TrayIcon; use MIME::Base64; use Getopt::Long; use POSIX qw(strftime); #version with asynchronous events use Asterisk::Manager 0.02; #Globals my %Images; my $icon; my $asterisk; my $popup; my @History; my $CallerIDEntry; my %config = ( user => "manager", host => 'asterisk', secret => 'password', listenlink => '', msgsuppfilter => '^$', diallink => 'SIP/user', context => 'default', msgtimeout => 5000, windowmsgtimeout => 5000 ); sub InitAsterisk { $asterisk = new Asterisk::Manager; #$asterisk->debug(1); $asterisk->host($config{host}); $asterisk->port($config{port}) if $config{port}; $asterisk->user($config{user}); $asterisk->secret($config{secret}); $asterisk->connect || die "Could not connect to Asterisk on " . $asterisk->host . "!\n". $asterisk->error; $asterisk->setcallback("Dial", \&CallbackAsteriskDial); &Log("Asterisk connected"); } sub CallbackAsteriskDial { my (%par) = @_; my $msg = $par{CallerIDName}." <".$par{CallerID}.">"; &Log("Dial: ".$msg." -> ".$par{Destination}); return if not $par{Destination} =~ m#$config{listenlink}#; &Log("Listen link '".$config{listenlink}."' pass"); return if $msg =~ m#$config{msgsuppfilter}#; &Log("Message suppress filter '".$config{msgsuppfilter}."' pass"); my %hist = (CallerID => $par{CallerID}, CallerIDName => $par{CallerIDName}, Time => time); push(@History, \%hist); &ShowNotify($msg); } sub ShowNotify { my ($msg) = @_; #Works only on newer gtk and hates <> my $s = $msg; $s =~ s/[<>]//g; $icon->send_message($config{msgtimeout}/1000, $s) if $config{msgtimeout} >= 0; #Alternative way if ($config{windowmsgtimeout} >= 0) { my $dlg = Gtk2::Window->new('toplevel'); $dlg->set_position('center'); $dlg->set_default_size(400, 40); my $label = Gtk2::Label->new($msg); $dlg->add($label); $dlg->set_type_hint('splashscreen'); $dlg->show_all(); Glib::Timeout->add($config{windowmsgtimeout}, \&CloseNotify, $dlg); } } sub CloseNotify { my ($dlg) = @_; $dlg->hide_all; return 0; } sub DoneAsterisk { $asterisk->disconnect; &Log("Asterisk disconnected"); } sub InitIcon { $icon= Gtk2::TrayIcon->new("anotify"); my $icon_image = Gtk2::Image->new_from_pixbuf( $Images{"n"} ); my $eventbox = Gtk2::EventBox->new; $eventbox->add( $icon_image ); $eventbox->signal_connect('button_press_event', \&IconClick ); $icon->add($eventbox); my $tooltip = Gtk2::Tooltips->new; $tooltip->set_tip($icon, "Asterisk call notifier (".$config{listenlink}.") and dialer (".$config{diallink}.")."); $icon->show_all; } sub TimerHook { my $err; while ($asterisk->eventpending) { $err = $asterisk->error; if ($err) { &Log($err); Gtk2->main_quit; return 0; } $asterisk->handleevent; } return 1; } sub IconClick { #left mouse button if ( $_[ 1 ]->button == 1 ) { &ShowCalls; } #right mouse button elsif ( $_[ 1 ]->button == 3 ) { &PopupMenu; } return 1; } sub ShowCalls { if ($popup) { $popup->hide_all; $popup = undef; return; } $popup = Gtk2::Window->new( 'toplevel' ); $popup->set_position('mouse'); $popup->set_title("Call history"); my $vbox = Gtk2::VBox->new( 0, 6 ); $popup->add($vbox); $vbox->set_border_width(2); #Model my $model = Gtk2::TreeStore->new(qw/Glib::String Glib::String Glib::String/); foreach my $hist (reverse @History) { my $iter = $model->append (undef); $model->set($iter, 0, $hist->{CallerID}, 1, $hist->{CallerIDName}, 2, strftime("%X", localtime($hist->{Time})) ); } my $view = Gtk2::TreeView->new($model); $view->signal_connect ("cursor-changed" => \&RowSelected, $model); $view->signal_connect ("row-activated" => \&RowDblClick, $model); #Column 0 my $renderer = Gtk2::CellRendererText->new; $renderer->set (xalign => 0.0); my $col_offset = $view->insert_column_with_attributes (0, "Caller ID", $renderer, text => 0); #Column 1 $renderer = Gtk2::CellRendererText->new; $renderer->set (xalign => 0.0); $col_offset = $view->insert_column_with_attributes (1, "Caller name", $renderer, text => 1); #Column 2 $renderer = Gtk2::CellRendererText->new; $renderer->set (xalign => 0.0); $col_offset = $view->insert_column_with_attributes (2, "Time", $renderer, text => 2); $vbox->pack_start($view, 1, 1, 0 ); #lower box my $hbox= Gtk2::HBox->new( 0, 6 ); $vbox->pack_end($hbox,0,0,2); $CallerIDEntry = Gtk2::Entry->new; $hbox->pack_start($CallerIDEntry, 0, 0, 0 ); my $dialbtn = Gtk2::Button->new_from_stock('Dial'); $dialbtn->set_image(Gtk2::Image->new_from_pixbuf( $Images{"n"})); $hbox->pack_start( $dialbtn, 0, 0, 0 ); $dialbtn->signal_connect( clicked => \&DialNumber ); my $closebtn = Gtk2::Button->new_from_stock('gtk-close'); $hbox->pack_start( $closebtn, 0, 0, 0 ); $closebtn->signal_connect( clicked => sub{ $popup->hide_all; $popup = undef; } ); $popup->show_all; } sub RowSelected { my ($view) =@_; my $sel = $view->get_selection; my ($model, $iter) = $sel->get_selected; return if ! $model; my ($cid, $cin) = $model->get($iter); #&Log("Selected $cid $cin"); $CallerIDEntry->set_text($cid); } sub RowDblClick { #RowSelected se vola predtim samo &DialNumber; } sub DialNumber { my $num = $CallerIDEntry->get_text; $num =~ s/[^0-9]//g; $asterisk->sendcommand( Action => 'Originate', Channel => $config{diallink}, Context => $config{context}, Priority => 1, Exten => $num, Callerid => "Dialing ".$num ); } sub PopupMenu { my $menu = Gtk2::Menu->new(); #Quit my $menu_quit = Gtk2::ImageMenuItem->new_with_label( "Quit" ); $menu_quit->signal_connect(activate => sub {Gtk2->main_quit; return 0;}); $menu_quit->set_image(Gtk2::Image->new_from_stock( 'gtk-quit', 'menu' )); $menu->add($menu_quit); $menu->show_all; $menu->popup( undef, undef, undef, undef, 0, 0 ); return 1; } sub LoadImages { my %png_images; #PNG format $png_images{'n'} = decode_base64( 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAADAFBMVEUAAABtbW12dnZ5eXp8 fHwZmOKCgoKIiImD1Puh5P+04v/Hx83Kys7Ozs/Ly9DOztPX19vb29/e///m5unp6ev29vf3 9/j///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZ dxFjAAAAAXRSTlMAQObYZgAAAAFiS0dE/6UH8sUAAAAJcEhZcwAAAEgAAABIAEbJaz4AAABt SURBVBjTjY7RCsMgDEVjiMvWIGy1ev//TxsXB46+9FwkeLxgiK48VP/uCQDLDyJlF9L6REhl COQAIZqLfd9ezxyNXqXPRojjLV1nvuJjLoIhGFa4+Ss80TCTtjQSSmE/NvBvSVCrLpu6iXGb E4fMB8m3Qv26AAAAAElFTkSuQmCC'); #loader foreach my $key ( keys %png_images ) { $Images{ $key } = do { my $loader = Gtk2::Gdk::PixbufLoader->new(); $loader->write( $png_images{ $key } ); $loader->close(); $loader->get_pixbuf(); }; } } sub Log { my ($msg) = @_; print($msg."\n"); } sub main { Gtk2->init; &LoadImages; &InitIcon; &InitAsterisk; Glib::Timeout->add(100, \&TimerHook); Gtk2->main; &DoneAsterisk; } GetOptions(\%config, "help", "user=s", "host=s", "port=i", "secret=s", "listenlink:s", "diallink:s", "context:s", "msgtimeout:i", "windowmsgtimeout:i", "msgsuppfilter:s"); if ($config{help}) { print("Asterisk call notifier and dialer Params: --user=asterisk_login ([user] from manager.conf) --secret=password (secret from manager.conf) --host=pbx_hostname --port=pbx_port (optional port from manager.conf, usualy 5038) --listenlink=monitored_link_prefix (REGEXP prefix in event Dial. For example: SIP/20) --msgsuppfilter=message_suppress_filter (REGEXP for filtering final message. For example: <2[0-9]>) --diallink=link for originate call (SIP/20) --msgtimeout=notify_message_timeout [ms] (-1 disable) --windowmsgtimeout=window_notify_message_timeout [ms] (-1 disable) --context=dialing_context (dialplan context for dial button) "); exit; } &main;