Using perl to retrieve Caller ID data from the web or a database

From Etel

Jump to: navigation, search

Contents

Using perl to retrieve Caller ID data from the web or a database

Problem

You have caller ID and want to determine the name of the caller, so your phone can display it.

Solution

We can use the Asterisk Gateway Interface to run programs from the dialplan. For perl, there is a set of modules for dealing with Asterisk, including Asterisk::AGI, a module implementing the AGI protocol. These modules are available from http://asterisk.gnuinter.net/.

Calling an AGI program from the dialplan is straight-forward: All we have to do is insert the following command into the dialplan.

AGI(find-caller-id.pl)

This will execute the script find-caller-id.pl, located in the server's agi-bin directory, e.g. /var/lib/asterisk/agi-bin.

Assuming you're using the old-style extensions.conf file, and your extension for incoming calls is 500, the relevant part of your dialplan could look like this:

exten => 500,1,AGI(find-caller-id.pl)
exten => 500,2,Dial(SIP/office-phone) 

If you're using the new-style extensions.ael file, the block for incoming calls could look similar to this:

500 => {
    AGI(find-caller-id.pl);
    Dial(SIP/office-phone);
}

Now that we've established how to call our lookup script, all that remains is the lookup script itself. The skeleton of a script that works with the caller ID of an incoming call could look like this:

#!/usr/bin/perl -w
use strict;
use Asterisk::AGI;

# initialize the AGI module and read incoming arguments
my $AGI = new Asterisk::AGI;
my %input = $AGI->ReadParse();

# extract the caller ID
my $callerid = $input{'callerid'};

# if the caller ID is in "name" <number> form, extract the number 
$callerid = $1 if $callerid =~ /<(\d*)>/;

# remove all other non-digits
$callerid =~ s/[^\d]//g;

# depending on where you live, the following part is going to be
# different. We're assuming local calls have no prefix, national
# calls have a single zero prefix, and worldwide calls have a
# two zeros prefix. Additionally, we're assuming that numbers
# in our databse start with the country code.

if($callerid =~ /^00/) {
        # if this is a worldwide call, just remove the prefix
        $callerid =~ s/^00//;
} elsif($callerid =~ /^0/) {
        # if it's a national call, remove the prefix...
        $callerid =~ s/^0//;
        # ..and add our own country code
        $callerid = '49' . $callerid;
} else {
        # if it's a local call, add our own country and local codes
        $callerid = '49721' . $callerid;
}
 
# look the normalized number up
my $caller = do_lookup($callerid);

if($caller) {
        # finally, pass the name along with the normalized number
        # back to Asterisk, if lookup was successful
        $AGI->set_callerid('"' . $caller . '" <' . $callerid . '>');
}
 
# we don't have to do anything if we don't change the caller ID
exit 0;

Additionally, we will have to write the subroutine do_lookup to do the lookup. The following is a skeleton for do_lookup with no actual lookup, but everything else in place:

sub do_lookup {
        my $callerid = shift;
        my $caller = undef;
        # insert code to do actual lookup here
        return $caller;
}

Discussion

Normalizing Telephone Numbers

The main script above requires some adjustment. The incoming caller's number has to be normalized, i.e. all numbers of incoming calls should have the same format before lookup. We're ensuring this by transforming phone numbers into worldwide numbers starting with a country code, followed by a local code.

if($callerid =~ /^00/) {
        # if this is a worldwide call, just remove the prefix
        $callerid =~ s/^00//;
} elsif($callerid =~ /^0/) {
        # if it's a national call, remove the prefix...
        $callerid =~ s/^0//;
        # ..and add our own country code
        $callerid = '49' . $callerid;
} else {
        # if it's a local call, add our own country and local codes
        $callerid = '49721' . $callerid;
}

This code assumes you're from a city in the southern part of Germany, which is probably not very useful for most people. If you're in the USA, you might change it as follows: note, Danny does not actually know this is correct :-)

if($callerid =~ /^011/) {
        # if this is a worldwide call, just remove the prefix
        $callerid =~ s/^011//;
} elsif($callerid =~ /^1/) {
        # if it's a national call, remove the prefix...
        $callerid =~ s/^1//;
        # ..and add our own country code
        $callerid = '1' . $callerid;
} else {
        # if it's a local call, add our own country and local codes
        $callerid = '1' . 'your local area code' . $callerid;
}

Implementing the actual lookup

So far, we only have a skeleton script that can do everything except looking up the actual caller name. If you're like the author of this recipe, you probably have a telephone book database. The following snippet illustrates how to do a lookup in a MySQL database. To use it, just append it to the main script above, after the last line with the exit statement.

use DBI;
use DBD::mysql;
sub do_lookup {
        my $callerid = shift;
        my $caller = undef;
        # connect to the database
        my $dbh = DBI->connect('DBI:mysql:phonebook', 'user', 'password');
        if($dbh) {
                # find the first record with the same phone number
                my $sth = $dbh->prepare('SELECT CONCAT(firstname," ",lastname) FROM phonebook WHERE number=? LIMIT 1');
                $sth->execute($callerid);
                my @result = $sth->fetchrow_array();
                $sth->finish;
                $dbh->disconnect;
                if(scalar(@result) > 0) {
                        # if we got a result from the database, remember it
                        $caller = $result[0];
                }
        }
        return $caller;
}

This snippet also needs modifications depending on your database setup. In order of appearance in the script, make the following modifications:

  • replace phonebook in DBI:mysql:phonebook by the name of your database
  • replace user and password by a user name allowed to connect to the database, and his password
  • replace CONCAT(firstname," ",lastname) by the appropriate field or an expression containing the appropriate fields in your phone book table
  • replace phonebook in the SELECT clause by the name of the phone book table
  • replace number in the SELECT clause by the appropriate field name

Implementing lookup with a different data source

To implement your own data source for lookup, you could start with the skeleton do_lookup:

sub do_lookup {
        my $callerid = shift;
        my $caller = undef;
        # insert code to do actual lookup here
        return $caller;
}

All you have to add is code that sets $caller to the name of the caller with the number in $callerid. For example, you could query a web site or you could just do a flat file lookup if you only want lookup for a limited set of numbers, e.g. your family and close friends.

See Also:

Metadata

  • Author: Danny 01:22, 29 January 2007 (PST)
Personal tools