Skip to main content

CallerID AGI for Asterisk: trifecta 2012.0 includes opencnam, anywho and fonefinder


I have taken the Nerd Vittles calleridname.agi script and updated it to work with currently available free online caller ID services. My changes are in public domain, what's left of original code obviously retains original copyright and whatever license terms went with it.

I have never checked if the asteridex/npanxx part of the code has any chance of working, so tread carefully.

I have implemented lookups for OpenCnam, AnyWho (YellowPages took over them) and FoneFinder. The original code was useless for the latter two, as the pages got redesigned.

This was a quick hack, I need to redo it in Python -- properly this time. I obviously have no clue about Perl and don't care enough about this code to make it any prettier, sorry.


#!/usr/bin/perl -w 

use Asterisk::AGI;
use LWP::UserAgent;
use DBI;

$AGI = new Asterisk::AGI;

$Timeout       = 2;
$OpenCnam      = '1';
$AnyWho        = '0';
$FoneFinder    = '1';
$Asteridex     = '0';
$TermOnSuccess = 1;

my %input = $AGI->ReadParse();

my $callerid     = $input{'calleridnum'};
my $calleridfull = $input{'callerid'};

if ( $callerid eq '' ) {
   $callerid = $input{'callerid'};
}

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

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

$calleridfull =~ s/[\,\"\']+/ /g;

$AGI->verbose( "CALLERID IS: $calleridfull\n", 1 );

if ( $callerid =~ /^(\d{3})(\d{3})(\d{4})$/ ) {
   $npa     = $1;
   $nxx     = $2;
   $station = $3;
   $AGI->verbose( "Checking $npa $nxx $station...\n", 1 );
}
elsif ( $callerid =~ /\<(\d{3})(\d{3})(\d{4})\>/ ) {
   $npa     = $1;
   $nxx     = $2;
   $station = $3;
   $AGI->verbose( "Checking $npa $nxx $station...\n", 1 );
}
else {
   $AGI->verbose(
"Unable to parse phone number for NPA/NXX/station. Phone number is: $callerid\n",
      1
   );
   exit(0);
}

# handle timeouts
alarm($Timeout);
$SIG{ALRM} = sub {
   $AGI->set_callerid("\"... <$npa$nxx$station>\"");
   $AGI->verbose( "Lookup timed out\n", 1 );
   exit(0);
};

#$npa='641';
#$nxx='892';
#$station='8019';

&lookup( \&opencnam_lookup,   $npa, $nxx, $station, "OpenCnam",   $OpenCnam );
&lookup( \&anywho_lookup,     $npa, $nxx, $station, "AnyWho",     $AnyWho );
&lookup( \&fonefinder_lookup, $npa, $nxx, $station, "FoneFinder", $FoneFinder );
&lookup( \&asteridex_lookup,  $npa, $nxx, $station, "AsteriDex",  $AsteriDex );
exit(0);

sub lookup {
   my ( $fun, $npa, $nxx, $station, $description, $enabled ) = @_;
   if ( !$enabled ) {
      $AGI->verbose( "$description lookup disabled.\n", 2 );
      return "";
   }
   $AGI->verbose( "Ready for $description lookup...\n", 2 );
   if ( $name = $fun->( $npa, $nxx, $station ) ) {
      $newcallerid = "\"$name <$npa$nxx$station>\"";
      $AGI->set_callerid($newcallerid);
      $AGI->verbose( "$description match. New CallerIDName = $name\n", 1 );
      if ( $TermOnSuccess > 0 ) { exit(0); }
   }
   else {
      $AGI->verbose( "Unable to find an $description match.\n", 1 );
   }
}

sub opencnam_lookup {
   my ( $npa, $nxx, $station ) = @_;
   my $ua = LWP::UserAgent->new( timeout => 45 );
   my $URL =
       'https://api.opencnam.com/v1/phone/'
     . $npa
     . $nxx
     . $station
     . '?format=text';
   my $req = new HTTP::Request GET => $URL;
   my $res = $ua->request($req);
   if ( $res->is_success() ) {
      my $cid = $res->content;
      if ( length $cid > 0 ) {

         #print $cid . "\n";
         return $cid;
      }
   }
   return "";
}

sub fonefinder_lookup {
   my ( $npa, $nxx ) = @_;
   my $ua = LWP::UserAgent->new( timeout => 45 );
   my $URL =
       'http://www.fonefinder.net/findome.php?npa='
     . $npa . '&nxx='
     . $nxx
     . '&thoublock=&usaquerytype=Search+by+Number&cityname=';
   $ua->agent(
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)'
   );
   my $req = new HTTP::Request GET => $URL;
   my $res = $ua->request($req);
   if ( $res->is_success() ) {
      if ( $res->content =~ /areacode=(.+)H[1-9]>/ ) {
         my $listing = $1;

         #print "$listing\n\n";
         if ( $listing =~ /<A HREF='findcity.php\?cityname=([^&]+)&.+>/ ) {
            my $city = $1;

            #print "$city\n";
            if ( $listing =~
               /npamap[^>]+>([^<]+)<.+<A HREF=[^>]+>([^<]+)<\/A><TD>([^<]+)<TD>/
              )
            {
               my $state    = $1;
               my $provider = $2;
               my $type     = $3;

               #print "$state // $provider // $type\n";
               return $city . ' ' . $state . ' ' . $provider;
            }
         }
      }
   }
   return "";
}

sub anywho_lookup {
   my ( $npa, $nxx, $station ) = @_;
   my $ua = LWP::UserAgent->new( timeout => 45 );

   # lookup a person
   my $URL =
     'http://anywhoyp.yellowpages.com/reversephonelookup?fap_terms%5Bphone%5D=';
   $URL .= $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 =~ />([^<]+)<\/a>[^<]+<span/ ) {
         my $name = $1;

         #print $name . "\n";
         return $name;

      }
   }

   # lookup a business
   $URL = 'http://anywhoyp.yellowpages.com/phone/?phone_search_terms=';
   $URL .= $npa . $nxx . $station;
   $req = new HTTP::Request GET => $URL;
   $res = $ua->request($req);
   if ( $res->is_success() ) {
      if ( $res->content =~
         /<h3 class="business-name[^<]+<a href=[^>]+>([^<]+)</ )
      {
         my $name = $1;

         #print $name . "\n";
         return $name;
      }
   }

   return "";
}

sub asteridex_lookup {
   my ( $npa, $nxx, $station ) = @_;
   my $dbh = DBI->connect( "dbi:mysql:asteridex", "root", "passw0rd" )
     or die("Connect failed");
   my $sth = $dbh->prepare("select * from user1 where out = '$npa$nxx$station'")
     or die("Prepare failed.");
   $sth->execute;
   if ( $sth->rows == 0 ) {
      return "";
   }
   else {
      my $resptr   = $sth->fetchrow_hashref();
      my $clidname = $resptr->{"name"};
      return $clidname;
   }

   $dbh->disconnect;
   return "";
}

sub npanxx_lookup {
   my ( $npa, $nxx ) = @_;
   my $dbh = DBI->connect( "dbi:mysql:phone", "root", "passw0rd" )
     or die("Connect failed");
   my $lookup = $npa . "-" . $nxx;

   #print $lookup ;
   my $sth = $dbh->prepare("select * from npanxx where npanxx = '$lookup'")
     or die("Prepare failed.");
   $sth->execute;

   #print $sth->rows ;
   if ( $sth->rows == 0 ) {
      return "";
   }
   else {
      my $resptr     = $sth->fetchrow_hashref();
      my $ratecenter = $resptr->{"ratecenter"};
      my $state      = $resptr->{"state"};
      my $company    = $resptr->{"company"};
      $company = substr( $company, 0, -1 );
      $company = substr( $company, 1, 9 );
      my $clidname =
        substr( $ratecenter, 0, 3 ) . "-" . $state . "-" . $company;
      return $clidname;
   }

   $dbh->disconnect;
   return "";
}

Comments

Popular posts from this blog

Both INCLUDEPATH and DEPENDPATH are usually needed in qmake .pro project files.

qmake, the build tool provided with the Qt toolkit, converts project files written in its own mini-language to platform-specific Makefiles.

This process includes adding necessary dependencies to the Makefile, so that changes in source files trigger rebuilding of the outputs that depend on said sources.

If your project is spread across directories, you'll likely add an INCLUDEPATH line to your .pro file so that the #include directives look sane -- say #include "library/foo.h" instead of #include "../library/foo.h". This can be done by adding INCLUDEPATH += "../" to the .pro project file.

This, by itself, doesn't cause the files in include directories to be treated as dependencies. This is a sane default, since you likely don't want to rebuild your whole project if a system library changes -- assuming, of course, that the library is meant to stay binary compatible between releases!

Thus, if any of your source files references a file from somewhe…

Asterisk 1.8 with SELinux on RHEL 6 / CentOS 6

Asterisk 1.8 is the current long term support (LTS) version of Asterisk. You can find it in the atrpms repository. Using atrpms requires a bit of ingenuity, since you must enable yum priorities. Here's how I've set up my yum priorities and excludes to play well with RHEL (lower priority is higher):

# /etc/yum/pluginconf.d/rhnplugin.conf
priority = 9

# files in /etc/yum/repos.d - current samba and subversion override those of RHEL
sernet-samba - 5
wandisco - 6
rhnplugin - 9
centos-base - 20, includepkgs=xfs* fftw-* glpk-* dell-firmware-repository - 30 dell-omsa-repository - 30 rpmforge-repo - 50, exclude=hdf5*
epel - 60, exclude=dahdi* atrpms - 70
I'm using a bunch of non-redhat packages, including Asterisk, recent subversion, octave, xfs tools, and Dell server management tools.

For Asterisk proper, I'm running an AGI caller id script, and a fax receiving script. The fax script uses cups to print. Those scripts require exceptions to the targeted SELinux policy. Note that the polic…

Details of Migrating Zimbra 6 to Zimbra 8

I have a small Zimbra system that needed to be migrated from 6.0 to 8.0. Generally:
You must update to 6.0.16 on the source system. The account export functionality is broken in versions prior to 6.0.15 and will fail on some accounts.Start with a fresh 8.0 install. On a small system (dozens of users), that seems like the simplest option.Keep the 6.0 system running. Use the migration wizard in 8.0's admin console (Tools & Migration -> Account Migration) to move over account records only.
Set "type of mail server" to Zimbra Collaboration Suite. Set "would you like to import mail" to No.Import from another Zimbra LDAP directory.Set the LDAP Search Base to "dc=foo,dc=com" where foo.com would be your domain. This saves work on manually moving accounts between systems.
To move account contents and filter settings between servers, you can do the following on the source server:
su - zimbra
ACCT=user@foo.com      # account to move
DEST=root@destination  # ss…