The Palner Group, Inc.

Kamailio, Asterisk, VoIP, and IT Consulting

Using AGI to get Caller ID Name CNAM

Everyone has them — and here’s Team Forrest’s version of a Caller ID to Name (CNAM, CIDNAME, etc.) lookup using AnyWho, Google, and 411.com. The first file is the calleridname.pl:

UPDATE April 4, 2009 — Frank (user comment) let us know that AnyWho had changed their website. As a result the code has been updated. Thanks Frank!

UPDATE November 18, 2009 — Robert (user comment) let us know of another change. As a result the code has been updated. Thanks Robert!

#!/usr/bin/perl -w
use strict;
use LWP::UserAgent;
$|=1;

my ($cidnum,$cidname,$npa,$nxx,$station,$name);

#----------------------------------------------------------------
# get asterisk initial info
#----------------------------------------------------------------

while(<STDIN>) {
	chomp;
	last unless length($_);
}

#----------------------------------------------------------------
# check if we have a caller id
#----------------------------------------------------------------

if ($ARGV[0]) {
		$cidnum = $ARGV[0];
	} else {
		print qq(VERBOSE "ERROR: no callerid provided" 2\n);
		exit(0);
}

#----------------------------------------------------------------
# check caller id and split into npa, nxx, and station
#----------------------------------------------------------------

if(substr($cidnum,0,1) eq '1'){
	$cidnum=substr($cidnum,1);
}

if(substr($cidnum,0,2) eq '+1'){
	$cidnum=substr($cidnum,2);
}

if ($cidnum =~ /^(\d{3})(\d{3})(\d{4})$/) {
		$npa = $1;
		$nxx = $2;
		$station = $3;
	} elsif ($cidnum =~/\<(\d{3})(\d{3})(\d{4})\>/) {
		$npa = $1;
		$nxx = $2;
		$station = $3;
	} else {
		print qq(VERBOSE "ERROR: unable to parse caller id" 2\n);
		exit(0);
}

print qq(VERBOSE "STATUS: CID is $npa-$nxx-$station" 2\n);

#----------------------------------------------------------------
# check npa, nxx, and station for cid name
# 1 = check. 0 = skip.
#----------------------------------------------------------------

my $AnyWho = '1' ;
my $Google = '1' ;
my $www411 = '1' ;

if ($AnyWho > '0') {
		print qq(VERBOSE "STATUS: checking AnyWho for name lookup" 2\n);
		if ($name = &anywho_lookup ($npa, $nxx, $station)) {
				$cidname = $name;
				print qq(SET VARIABLE CALLERID\(name\) "$cidname"\n);
				print qq(VERBOSE "STATUS: AnyWho said name was $cidname " 2\n);
				exit(0);
			} else {
				print qq(VERBOSE "STATUS: unable to find name with AnyWho" 2\n);
		}
	} else {
		print qq(VERBOSE "STATUS: AnyWho lookup disabled" 2\n);
}

if ($Google > '0') {
		print qq(VERBOSE "STATUS: checking Google for name lookup" 2\n);
		if ($name = &google_lookup ($npa, $nxx, $station)) {
				$cidname = $name;
				print qq(SET VARIABLE CALLERID\(name\) "$cidname"\n);
				print qq(VERBOSE "STATUS: Google said name was $cidname " 2\n);
				exit(0);
			} else {
				print qq(VERBOSE "STATUS: unable to find name with Google" 2\n);
		}
	} else {
		print qq(VERBOSE "STATUS: Google lookup disabled" 2\n);
}

if ($www411 > '0') {
		print qq(VERBOSE "STATUS: checking www411 for name lookup" 2\n);
		if ($name = &www411_lookup ($npa, $nxx, $station)) {
				$cidname = $name;
				print qq(SET VARIABLE CALLERID\(name\) "$cidname"\n);
				print qq(VERBOSE "STATUS: www411 said name was $cidname " 2\n);
				exit(0);
			} else {
				print qq(VERBOSE "STATUS: unable to find name with www411" 2\n);
		}
	} else {
		print qq(VERBOSE "STATUS: www411 lookup disabled" 2\n);
}

#----------------------------------------------------------------
# return results and exit
#----------------------------------------------------------------

print qq(SET VARIABLE CALLERID\(name\) "$cidnum"\n);
print qq(VERBOSE "STATUS: Unknown name for $cidnum " 2\n);
exit(0);

#----------------------------------------------------------------
# parse anywho
# http://whitepages.anywho.com/results.php?qnpa=$npa&qnpanxx=$npa$nxx&qnxx=$nxx&qp=$nxx$station&qstation=$station
# Find More Information for First Last</a>
#----------------------------------------------------------------

sub anywho_lookup {
	my ($npa, $nxx, $station) = @_;
	my $ua = LWP::UserAgent->new( timeout => 45);
	my $URL = 'http://whitepages.anywho.com/results.php';
	$URL .= qq(?qnpa=$npa&qnpanxx=$npa$nxx&qnxx=$nxx&qp=$nxx$station&qstation=$station);
	$ua->agent('AsteriskAGIQuery/1');
	my $req = new HTTP::Request GET => $URL;
	my $res = $ua->request($req);
	if ($res->is_success()) {
		if ($res->content =~ /Find More Information for (.*)<\/a>/) {
			my $clidname = $1;
			return $clidname;
		}
	}
	return "";
}

#----------------------------------------------------------------
# parse google
# http://www.google.com/search?rls=en&q=phonebook:$npa$nxx$station
# <td>First Name<td>(<b>$npa
#----------------------------------------------------------------

sub google_lookup {
	my ($npa, $nxx, $station) = @_;
	my $ua = LWP::UserAgent->new( timeout => 45);
	my $URL = qq(http://www.google.com/search?rls=en&q=phonebook:$npa$nxx$station&ie=UTF-8&oe=UTF-8);
	$ua->agent('AsteriskAGIQuery/1');
	my $req = new HTTP::Request GET => $URL;
	my $res = $ua->request($req);
	if ($res->is_success()) {
		if ($res->content =~ /<td>(.+)<td>\(<b>$npa/) {
			my $clidname = $1;
			return $clidname;
		}
	}
	return "";
}

#----------------------------------------------------------------
# parse 411
# http://www.411.com/search/Reverse_Phone?phone=$npa$nxx$station
# View map, driving directions, and more">Name</a>
#----------------------------------------------------------------

sub www411_lookup {
	my ($npa, $nxx, $station) = @_;
	my $ua = LWP::UserAgent->new( timeout => 45);
	my $URL = qq(http://www.411.com/search/Reverse_Phone?phone=$npa$nxx$station);
	$ua->agent('AsteriskAGIQuery/1');
	my $req = new HTTP::Request GET => $URL;
	my $res = $ua->request($req);
	if ($res->is_success()) {
		if ($res->content =~ /View map, driving directions, and more\">(.*)<\/a>/) {
			my $clidname = $1;
			if ($clidname eq "Listing Detail") {
				if ($res->content =~ /Type: <strong>(.*)<\/strong>/) {
					$clidname = $1;
					if ($res->content =~ /Location: <strong>(.*)<\/strong>/) {
						$clidname = $clidname . " $1";
					}
				}
			}
			return $clidname;
		}
	}
	return "";
}

This perl script will work well as an AGI script — checking AnyWho, Google, and then 411 for a caller’s name or location. If all else fails, the callerid name is set as the callerid number.

The perl script was designed to only use the Internet with minimal installation; so it will work without a database, Perl Asterisk module, or locally hosted NPA / NXX (phone number to region) file.

Team Forrest recommends using a subroutine context to get the callerid when needed; calling the script with either a GoSub or GosubIf command, such as:

exten => s,n,Gosub(cidname-lookup,s,1)
exten => s,n,dial(${PHONE},30,t)
...

...
[cidname-lookup]
exten => s,1,NoOp(looking up callerid name)
exten => s,n,GotoIf($["foo${CALLERID(NAME)}" = "foo" ]?getname)
exten => s,n,GotoIf($["${CALLERID(NAME)}" = "${CALLERID(NUM)}" ]?getname)
exten => s,n,NoOp(caller id name exists as ${CALLERID(NAME)})
exten => s,n,Return
exten => s,n(getname),AGI(calleridname.pl,${CALLERID(NUM)})
exten => s,n,NoOp(Caller ID Name is now ${CALLERID(NAME)})
exten => s,n,Return

Enjoy the file (download here) and remember, Team Forrest is here to assist you will all of your Asterisk, VoIP, or technical needs.

12 Comments

  1. the google phonebook and anywho never work. Google returns a 403 forbidden every time because using scripts to access its database are against its terms of service. 411.com returns a city and state only on anything it has a listing for, and I haven’t seen anywho return any results for me yet either. I know beggars can’t be choosers, but does anyone actually have this script working?

  2. Mulderlr,

    This works for us consistently. Google will block it if you are hammering it, but I certainly assume that’s not the case here. Anywho also works for us. Why not give us a shout (use the contact page) and let’s see if we can’t help you with this.

    Team Forrest

  3. The Anywho query will not work anymore as their query structure has changed.

  4. Frank, you’re 100% correct. Anywho has changed… this is the hack:

    sub anywho_lookup {
    	my ($npa, $nxx, $station) = @_;
    	my $ua = LWP::UserAgent->new( timeout => 45);
    	my $URL = 'http://whitepages.anywho.com/results.php?ReportType=33';
    	$URL .= '&qnpa=' . $npa . '&qp=' . $nxx . $station;
    	$ua->agent('AsteriskAGIQuery/1');
    	my $req = new HTTP::Request GET => $URL;
    	my $res = $ua->request($req);
    	if ($res->is_success()) {
    		if ($res->content =~ /Find More Information for (.*)< \/a>/) {
    			my $listing = $1;
    			return $listing;
    		}
    	}
    	return "";
    }

    And I will update the post… Thanks!

  5. Changed again This retrieves the info but I haven’t parsed it out yet.
    sub anywho_lookup {
    my ($npa, $nxx, $station) = @_;
    my $ua = LWP::UserAgent->new( timeout => 45);
    my $URL = ‘http://whitepages.anywho.com/results.php?ReportType=33&refer=2938&adword=ANYWHO.COM&qi=0&qk=10’;
    #$URL .= ‘&qnpa=’ . $npa . ‘&qp=’ . $nxx . $station;
    $URL .=
    ‘&qnpa=’. $npa .
    ‘&qnpanxx=’. $npa . $nxx .
    ‘&qnpanxx7=’. $npa . $nxx . substr($station,1,1) .
    ‘&qnxx=’ . $nxx .
    ‘&qp=’. $nxx . $station .
    ‘&qstation=’.$station .
    ‘&PHPSESSID=’ . ‘626da7c88dfce19b235ce7088a96a336’;
    $ua->agent(‘AsteriskAGIQuery/1’);
    my $req = new HTTP::Request GET => $URL;
    my $res = $ua->request($req);
    if ($res->is_success()) {
    print $res->content;
    if ($res->content =~ /Find More Information for (.*)/) {
    my $listing = $1;
    return $listing;
    }
    }
    return “”;
    }

    Have you used the whitepages api? Net::Whitepages
    I’ve tried to get it working
    my $White_Pages = Net::WhitePages->new( TOKEN => ‘12345’, api_key => ‘mykey’ );
    my $res = $White_Pages->reverse_phone( phone => “7990989890” );
    print $res;

    What is this TOKEN for? Also where am I supposed to use my api_key?

    What about WWW::WhoCallsMe
    I put in my listed number and it never finds it.

    use WWW::WhoCallsMe;
    my $who = WWW::WhoCallsMe->new;

    my $number = ‘9870709989’;
    my $calledme = $who->fetch($number);
    if ($calledme->{listed})
    {
    my $name = $calledme->{name};
    print “The number $number is listed. “;
    print “It seems that $name was calling.” if $name;
    print “I don’t know who was calling, though.” unless $name;
    print “\n”;
    }
    else
    {
    print “This number is not listed.\n”;
    }

  6. Even using the updated code, lookups are still failing for anywho and google, with a fallback of www411 working only about 75% of the time (giving city/state rather than name).

    Any new updates? I can use the URLs from the script in a browser and get a good lookup, but apparently the script won’t parse the info and return it to asterisk.

  7. Thanks Robert!

    I’ve updated the code and fixed the others.

    –fred

  8. I don’t know about anybody else but this does not work period. At all.

    The script is just fine. I had zero problems running the script.

    It’s all the websites. I ran those directly against some test numbers:

    http://www.google.com/search?rls=en&q=phonebook:$thenumber&ie=UTF-8&oe=UTF-8

    That never works. Tried it with 20+ phone numbers and did not work. Tried with a 1, + etc, different formats.

    http://phonenumbers.addresses.com/results.php?ReportType=33&qfilter%5Bpro%5D=on&qi=0&qk=10&qnpa=$npa&qp=$qp

    Does not work either. Tested with the same 20+ phone numbers and it did not work.

    http://whocalled.us/do?action=getScore&name=$WhoCalled_name&pass=$WhoCalled_pass&phoneNumber=$thenumber

    This only seems to work for telemarketers and debt collectors. I can see it being useful as a phone spam blocker, but not a CNAM lookup.

    http://www.whitepages.com/search/ReversePhone?full_phone=$thenumber

    1/20 success rate and it was a 10 year old business.

    http://yellowpages.addresses.com/yellow-pages/phone:$thenumber/listings.html

    2/20 and it was both old businesses again.

    http://whitepages.anywho.com/results.php?&qnpa=$npa&qp=$telephone

    Nothing. Again.

    I guess my big question here is if this even worth the processing cycles? It seems as if there is no way to get even a 10% hit rate on the free lookup services available.

  9. Ed,

    Google is good for primarily residential numbers. If we take an example of a Chicago area McDonald’s:

    http://www.google.com/search?rls=en&q=phonebook:3128670455
    (fail)

    http://www.411.com/search/Reverse_Phone?phone=3128670455
    (comes back with McDonald’s and more)

    http://whitepages.anywho.com/results.php?qnpa=312&qnpanxx=312867&qnxx=867&qp=8670455&qstation=0455
    (fail)

    Lately, 411 is my favorite. If it fails on a identity it will at least tell you if it’s a land line, cellular, etc and the city / state.

  10. How long has this blog been around? I have been searching for this kind of information for the past week and a half.

  11. how would this script work if i recieve a prefixed numbers from my telco

    example 6045277777
    is prefixed as inbound from telco to our asterisk ast 21036045277777

    how would you modify the script to ignor the first 4 digits

  12. Hi wes,

    A couple ways you could handle this… either send it to the script with just the first 10 digits (with asterisk stripping the last 4), or with the perl script stripping it. We can help you deploy this… just contact us for more help.

Comments are closed.