9.2. Writing AGI Scripts in Perl
Asterisk comes with a sample AGI script called
agi-test.agi. Let's step through
the file while we cover the core concepts of AGI programming. While
this particular script is written in Perl, please remember that
your own AGI programs may be written in almost any programming
language. Just to prove it, we're going to cover AGI programming in
a couple of other languages later in the chapter.
Let's get started! We'll look at each section of
the code in turn, and describe what it does.
#!/usr/bin/perl
This line tells the system that this particular
script is written in Perl, so it should use the Perl interpreter to
execute the script. If you've done much Linux or Unix scripting,
this line should be familiar to you. This line assumes, of course,
that your Perl binary is located in the /usr/bin/ directory. Change this to match the
location of your Perl interpreter.
use strict;
use strict tells Perl to act, well,
strict about possible programming errors, such as undeclared
variables. While not absolutely necessary, enabling this will help
you avoid common programming pitfalls.
$|=1;
This line tells Perl not to buffer its outputin
other words, that it should write any data immediately, instead of
waiting for a block of data before outputting it. You'll see this
as a recurrent theme throughout the chapter.
|
You should always use unbuffered output when writing AGI
scripts. Otherwise, your AGI may not work as expected, because
Asterisk may be waiting for the output of your program, while your
program thinks it has sent the output to Asterisk and is waiting
for a response.
|
|
# Set up some variables
my %AGI; my $tests = 0; my $fail = 0; my $pass = 0;
Here, we set up four variables. The first is a
hash called AGI, which is used to store the variables that
Asterisk passes to our script at the beginning of the AGI session.
The next three are scalar values, used to count the total number of
tests, the number of failed tests, and the number of passed tests,
respectively.
while(<STDIN>) {
chomp;
last unless length($_);
if (/^agi_(\w+)\:\s+(.*)$/) {
$AGI{$1} = $2;
}
}
As we explained earlier, Asterisk sends a group
of variables to the AGI program at startup. This loop simply takes
all of these variables and stores them in the hash named
AGI. They can be used later in the program or simply
ignored, but they should always be read from STDIN before
continuing on with the logic of the program.
print STDERR "AGI Environment Dump:\n";
foreach my $i (sort keys %AGI) {
print STDERR " -- $i = $AGI{$i}\n";
}
This loop simply writes each of the values that
we stored in the AGI hash to STDERR. This is
useful for debugging the AGI script, as STDERR is printed
to the Asterisk console.
sub checkresult {
my ($res) = @_;
my $retval;
$tests++;
chomp $res;
if ($res =~ /^200/) {
$res =~ /result=(-?\d+)/;
if (!length($1)) {
print STDERR "FAIL ($res)\n";
$fail++;
} else {
print STDERR "PASS ($1)\n";
$pass++;
}
} else {
print STDERR "FAIL (unexpected result '$res')\n";
$fail++;
}
This subroutine reads in the result of an AGI
command from Asterisk and decodes the result to determine whether
the command passes or fails.
Now that the preliminaries are out of the way,
we can get to the core logic of the AGI script.
print STDERR "1. Testing 'sendfile'...";
print "STREAM FILE beep \"\"\n";
my $result = <STDIN>;
&checkresult($result);
This first test shows how to use the STREAM
FILE command. The STREAM FILE command tells Asterisk
to play a sound file to the caller, just as the Background(
) application does. In this case, we're telling Asterisk to
play a file called beep.gsm.
You will notice that the second argument is
passed by putting in a set of double quotes, escaped by
backslashes. Without the double quotes to indicate the second
argument, this command does not work correctly.
|
You must pass all
required arguments to the AGI commands. If you want to skip
a required argument, you must send empty quotes (properly escaped
in your particular programming language), as shown above. If you
don't pass the required number of arguments, your AGI script will
not work.
You should also make sure you pass a line feed
(the \n on the end of the print statement) at the
end of the command.
|
|
After sending the STREAM FILE command,
this test reads the result from STDIN and calls the
checkresult subroutine to determine if Asterisk was able
to play the file. The STREAM FILE command takes three
arguments, two of which are required:
-
The name of the sound file to play back
-
The digits that may interrupt the playback
-
The position at which to start playing the
sound, specified in number of samples (optional)
In short, this test told Asterisk to play back
the file named beep.gsm, and then
checked the result to make sure the command was successfully
executed by Asterisk.
print STDERR "2. Testing 'sendtext'...";
print "SEND TEXT \"hello world\"\n";
my $result = <STDIN>;
&checkresult($result);
This test shows us how to call the SEND
TEXT command, which is similar to the SendText( )
application. This command will send the specified text to the
caller, if the caller's channel type supports the sending of
text.
The SEND TEXT command takes one
argument: the text to send to the channel. If the text contains
spaces (as in the example above), the argument should be
encapsulated with quotes, so that Asterisk will know that the
entire text string is a single argument to the command. Again,
notice that the quotation marks are escaped, as they must be sent
to Asterisk, not used to terminate the string in Perl.
print STDERR "3. Testing 'sendimage'...";
print "SEND IMAGE asterisk-image\n";
my $result = <STDIN>;
&checkresult($result);
This test calls the SEND IMAGE command,
which is similar to the SendImage( ) application. Its
single argument is the name of an image file to send to the caller.
As with the SEND TEXT command, this command works only if
the calling channel supports the reception of images.
print STDERR "4. Testing 'saynumber'...";
print "SAY NUMBER 192837465 \"\"\n";
my $result = <STDIN>;
&checkresult($result);
This test sends Asterisk the SAY NUMBER
command. This command behaves identically to the SayNumber(
) dialplan application. It takes two arguments:
Again, since we're not passing in any digits as
the second argument, we need to pass in an empty set of quotes.
print STDERR "5. Testing 'waitdtmf'...";
print "WAIT FOR DIGIT 1000\n";
my $result = <STDIN>;
&checkresult($result);
This test shows the WAIT FOR DIGIT
command. This command waits the specified number of milliseconds
for the caller to enter a DTMF digit. If you want the command to
wait indefinitely for a digit, use -1 as the timeout. This
application returns the decimal ASCII value of the digit that was
pressed.
print STDERR "6. Testing 'record'...";
print "RECORD FILE testagi gsm 1234 3000\n";
my $result = <STDIN>;
&checkresult($result);
This section of code shows us the RECORD
FILE command. This command is used to record the call audio,
similar to the Record( ) dialplan application. RECORD
FILE takes seven arguments, the last three of which are
optional:
-
The filename of the recorded file.
-
The format in which to record the audio.
-
The digits that may interrupt the recording.
-
The timeout (maximum recording time) in
milliseconds, or -1 for no timeout.
-
The number of samples to skip before starting
the recording (optional).
-
The word BEEP, if you'd like Asterisk
to beep before the recording starts (optional).
-
The number of seconds before Asterisk decides
that the user is done with the recording and returns, even though
the timeout hasn't been reached and no DTMF digits have been
entered (optional). This argument must be preceded by
s=.
In this particular case, we're recording a file
called testagi (in the GSM
format), with any of the DTMF digits 1 through 4 terminating the
recording, and a maximum recording time of 3,000 milliseconds.
print STDERR "6a. Testing 'record' playback...";
print "STREAM FILE testagi \"\"\n";
my $result = <STDIN>;
&checkresult($result);
The second part of this test plays back the
audio that was recorded earlier, using the STREAM FILE
command. We've already covered STREAM FILE, so this
section of code needs no further explanation.
print STDERR "================== Complete ======================\n";
print STDERR "$tests tests completed, $pass passed, $fail failed\n";
print STDERR "==================================================\n";
At the end of the AGI script, a summary of the
tests is printed to STDERR, which should end up on the
Asterisk console.
In summary, you should remember the following
when writing AGI programs in Perl:
-
Turn on strict language checking with the
use strict command.
-
Turn off output buffering by setting
$|=1.
-
Data from Asterisk is received using a
while(<STDIN>) loop.
-
Write values with the print
command.
-
Use the print STDERR command to write
debug information to the Asterisk console.
9.2.1. The Perl AGI Library
If you are interesting in building your own AGI
scripts in Perl, you may want to check out the Asterisk::AGI Perl module written by James
Golovich, which is located at http://asterisk.gnuinter.net.
The Asterisk::AGI module makes it
even easier to write AGI scripts in Perl. |