I gave this talk to the
Pittsburgh Perl Mongers on 2004-03-10. (There were also some cool
WPLUG folks in the house.) The Perl Mongers are a really fun group to talk to. After this talk, they asked lots of interesting questions. It makes me think that we might be seeing some cool Perl-based PBX projects soon. ;-)
You can download my slides from the talk in PDF form:
pgh-pm-talk-asterisk.pdf.
When looking at the slides, however, you'll have to use your imagination because the demonstrations and audience participation were an important part of this talk.
I set up a simple network comprising three VoIP phones and a Linux server running
Asterisk. I put the phones on different tables so that I could "call" audience members during the talk and have them call each other. Then I gave a quick introduction to Asterisk, during which we placed calls to each other and tried out transfers, parking, and conferences.
After this, the Perl part of the talk began, during which I provided four demonstrations:
- How to give Asterisk text-to-speech capability
- How to set up a simple Dial-the-Weather service
- How to create a web form that initiates a call from one phone to another
- How to create a web form that sets up a multi-phone conference call
For each demonstration I first showed the code that made it work (see below) and then I invited the audience to try it out by picking up a phone and dialing the appropriate extension.
Source code for the demonstrations
Here is the source code for the four demonstrations. Each snippet of code will probably make more sense if you look at it when you get to the portion of the talk where it is introduced.
All of these examples use James Golovich's
asterisk-perl distribution.
How to give Asterisk text-to-speech capability
(Yes, I know that Asterisk has support for text-to-speech via Festival, and that it's built in. However, Asterisk wants a special installation of Festival, and I would rather use the stock Red Hat / Fedora version. Hence this script.)
This is an AGI program and must be executable and placed in your
/usr/lib/asterisk/agi-bin directory.
say-text.pl#!/usr/bin/perl -w
#
# Speaks the text in $ARGV[0].
# Caches text-to-speech conversions as 8-kHz .WAV files.use Asterisk::AGI;
use File::Basename;
use Digest::MD5 qw(md5_hex);use strict;# set up communications w/ Asteriskmy $agi = new Asterisk::AGI;
$agi->ReadParse();# figure out the WAV file to use based on a hash of the text to saymy ($text_to_say) = @ARGV;
my $hash = md5_hex($text_to_say);
my $sounddir = "/var/lib/asterisk/sounds";
my $wavefile = "$sounddir/say-text-$hash.wav";# unless we have already cached this text-to-speed conversion,
# create the WAV file using Festival's text2wav (expensive)unless (-f $wavefile) { require File::Temp;
my (undef, $tmpfile) = File::Temp::tempfile(DIR => $sounddir); my $pid = open my $pipe, "|-";
die "can't fork: $!" unless (defined $pid); if (!$pid) { # child
open STDOUT, ">$tmpfile" or die "can't redir to $tmpfile: $!";
exec qw( text2wave -F 8000 - ); # text->speech conv; 8-kHz WAV output
die "exec in child failed: $!";
}
else { # parent
print $pipe $text_to_say;
close $pipe;
waitpid $pid, 0; # wait until text->speech conv. is done
rename $tmpfile, $wavefile
or die "can't rename $tmpfile to $wavefile: $!";
}
}# stream the WAV file down the phone line$agi->stream_file(basename($wavefile, ".wav"));
How to set up a simple Dial-the-Weather service
This is an AGI program and must be executable and placed in your
/usr/lib/asterisk/agi-bin directory.
get-weather.pl#!/usr/bin/perl -w
#
# Gets the weather in glorious Pittsburgh, PA, and speaks it proudly!use Geo::Weather;
use strict;my $w = (new Geo::Weather)->get_weather("Pittsburgh", "PA");# just in case our net connection is down during the talk ...unless (ref $w) {
$w = { cond => "Storming packets", temp => "75" };
}# exec to say-text.pl to speak the weather for usmy $msg = "It is currently $w->{temp} degrees and $w->{cond}.";
exec "/var/lib/asterisk/agi-bin/say-text.pl", $msg;# should never get heredie "couldn't exec: $!";
How to create a web form that initiates a call from one phone to another
This is a CGI program that must be executable and placed in your
/var/www/cgi-bin directory (or wherever CGIs go on your system). Further, this program must be able to write to the
/var/spool/asterisk/outgoing directory.
webdial.pl#!/usr/bin/perl -wuse CGI qw( :standard );
use Asterisk::Outgoing;
use strict;# get params from formmy ($from, $to) = (param("from"), param("to"));# if we got good params, place the callif (validQ($from) && validQ($to) && $from != $to) {
place_call("SIP/$from", "SIP/$to");
print(header, start_html("Call placed"), "Your call has been placed.",
p, start_form, submit("Place another call"), end_form, end_html);
}# if we didn't get valid params, ask for themelse {
my $title = 'Place a call';
print(header, start_html($title), h1($title),
start_form,
"Your extension:", br,
textfield("from"), p,
"Destination extension:", br,
textfield("to"), p,
submit("Place call"),
end_form, end_html);
}# determine if an extension is validsub validQ {
my @valid_extensions = (5001, 5002, 5003); # should init from file
my ($ext) = @_;
return $ext && grep { $ext == $_ } @valid_extensions;
}sub place_call {
my ($from, $to) = @_;
my %config = ( 'MaxRetries' => 0, 'RetryTime' => 60, 'WaitTime' => 60,
Channel => $from, Application => "Dial", Data => $to );
umask 000;
my $out = new Asterisk::Outgoing;
while (my ($name, $val) = each %config) {
$out->setvariable($name, $val);
}
$out->create_outgoing;
}
How to create a web form that sets up a multi-phone conference call
This is a CGI program that must be executable and placed in your
/var/www/cgi-bin directory (or wherever CGIs go on your system). Further, this program must be able to write to the
/var/spool/asterisk/outgoing directory.
conference.pl#!/usr/bin/perl -wuse CGI qw( :standard );
use Asterisk::Outgoing;
use strict;# get params from formmy @participants = param("participants");# if we got good params, place the callif (@participants) {
add_to_conference("SIP/$_") foreach @participants;
print(header, start_html("Conference started"),
"Your conference has been initiated.",
p, start_form, submit("Start another conference"),
end_form, end_html);
}# if we didn't get valid params, ask for themelse {
my $title = 'Set up a conference';
print(header, start_html($title), h1($title),
start_form,
"What extensions should participate in the conference?", p
checkbox_group(-name=>"participants",
-values=>[qw(5001 5002 5003)]),p
submit("Start conference"),
end_form, end_html);
}sub add_to_conference {
my ($ext) = @_;
my %config = ( 'MaxRetries' => 0, 'RetryTime' => 60, 'WaitTime' => 60,
Channel => $ext, Application => "MeetMe", Data => "8000" );
umask 000;
my $out = new Asterisk::Outgoing;
while (my ($name, $val) = each %config) {
$out->setvariable($name, $val);
}
$out->create_outgoing;
sleep 2; # to prevent phone slamming
}