Mod cidlookup

From FreeSWITCH Wiki
Jump to: navigation, search


Warning

Superseded by https://confluence.freeswitch.org/display/FREESWITCH/mod_cidlookup

 


Contents

mod_cidlookup (Caller ID Lookup API)

mod_cidlookup allows one to:

  • lookup number -> name mapping in a local database
  • lookup number->name mapping from a URL
  • cache the results of the URL lookup in memcache (if the cache module is installed)
  • if all else fails, lookup city/state information

Providers that provide number -> name lookups can be found here.

Other CallerID / CNAM Resources

Some CNAM providers offer you a way to perform the CNAM DIP via an e164 method, or a SIP Subscribe method.

For information on how to perform the CNAM dip via e164, or enum see this page on Enum / e164 VoIP CNAM Dips.

There is no obvious way to perform the CNAM DIP via the SIP Subscribe method, although that does not mean that it isn't possible.

WARNING!

I have discovered that on occasion retrieval of information from voipcnam.com takes some time to occur. If you perform the CID Lookup before you perform the <action application="answer"/> in your dialplan, this can cause the call to drop and/or behave in other unexpected ways.

In my dialplan, I prefer to perform the CID Lookup before I answer the call so I can filter not only on numbers, but also on the name. I discovered that if you do <action application="pre_answer"/> before performing the CID Lookup, the problem of the carrier giving up on placing the call amongst other problems, that the problems go away.

WARNING!!

The voipcnam.com site is no longer in service.

Requirements

mod_cidlookup will work with any of the modules missing (url, local database, memcache) -- though that part of the functionality will be disabled if one of them is missing.

memcache support requires the mod_memcache module to be loaded.

Installing

To use mod_cidlookup:

Tell FreeSWITCH to compile in this module by editing modules.conf in /usr/src/freeswitch/trunk and uncomment:

#applications/mod_cidlookup

Now go recompile FreeSWITCH...

make ; make install

Tell FreeSWITCH to actually use the cidlookup module when running by adding the module to modules.conf.xml in /usr/local/freeswitch/conf/autoload_configs:

<load module="mod_memcache"/>
<load module="mod_cidlookup"/>

Finally, edit the default config in the autoload_configs directory to hold your cidlookup configuration.

Now load up FreeSWITCH!

CLI

From the commandline issue:

cidlookup status

which would use the options from your config file:

+OK
 url: https://api.opencnam.com/v2/phone/${caller_id_number}?format=pbx
 cache: false
 cache-expire: 86400
 odbc-dsn: phone
 sql:  SELECT name||' ('||type||')' AS name    FROM phonebook p JOIN numbers n ON p.id = n.phonebook_id   WHERE n.number='${caller_id_number}'    LIMIT 1
 ODBC Compiled: true
cidlookup 16502530000

might give:

GOOGLE INC

If you have DEBUG logging turned on, you'll see it trying each step and whether the data is stored in memcache or not (if you're using the cache module).

This verifies that the module is running and it can load it's configuration information.

Dialplan Application

The cidlookup Application sets the caller_id_name if the lookup succeeds. It sets the original caller id name to the variable original_caller_id_name The easiest way to use it is to put the following block towards the top of your dialplan/public.xml:


    <extension name="cid_number_cleanup" continue="true">
      <condition field="caller_id_number" expression="^(?:\+)(\d+)$">
        <action application="set" data="effective_caller_id_number=$1" inline="true"/>
      </condition>
    </extension>

    <extension name="cid_name_cleanup" continue="true">
      <condition field="caller_id_name" expression="^(?:\+)(\d+)$">
        <action application="set" data="effective_caller_id_name=$1" inline="true"/>
      </condition>
    </extension>

    <extension name="cid_lookup-country_code_1" continue="true">
      <condition field="${module_exists(mod_cidlookup)}" expression="true"/>
      <condition field="caller_id_name" expression="^(?:\+)(\d+)$|^$"/>
      <condition field="caller_id_number" expression="^(?:\+1|1)?([2-9]\d\d[2-9]\d{6})$">
        <action application="cidlookup" data="$1"/>
      </condition>
    </extension>

Easier Dialplan

The documentation isn't quite clear that what we're really trying to set here is effective_caller_id_name (vs caller_id_name). For an inbound route it would look something like this:

<extension name="Demo Inbound" >
   <condition field="context" expression="public"/>
   <condition field="destination_number" expression="1234567890">
       <action application="set" data="call_direction=inbound"/>
       <action application="answer"/>
       <action application="sleep" data="1000"/>
       <action application="set" data="caller_id_name=${cidlookup(${caller_id_number})}"/>
       <action application="set" data="effective_caller_id_name=${caller_id_name}"/>
       <action application="ivr" data="Your_IVR"/>
   </condition>
</extension>

WARNING: This will cause a lookup if the call comes from "Anonymous" too, but an sql lookup of "blank" (it strips alpha characters it seems) which will return unrelated results. Is there some more elegant way to do the anti-action?


The cidlookup API can also be called from the dialplan using the ${api(args)} method. For example:

<action application="set" data="cid_name=${cidlookup(${caller_id_number})}"/>

A failed lookup will result in a value of "-ERR" -- check that prior to setting effective_caller_id_name.

API

The API can be called via ESL or used directly from fs_cli.

cidlookup status|number [skipurl] [skipcitystate]

Configuration

Parameters:

url
 URL of the service that takes a number and returns as it's only data the name.
 There are several providers that do this or you can implement this yourself.
 Sample config is using the service from OpenCNAM.

whitepages-apikey

 Whitepages.com offers a free reverse lookup API for low volume use.  If you
 intend to run high volume, they offer a commercial service.  Refer to 
 [developer.whitepages.com] for an API key.
cache
 true|false: use memcache to cache the number->name lookup
cache-expire
 seconds to keep the lookup in the cache
odbc-dsn
 database:user:password
 ODBC Database connection string
sql
 The SQL to execute.  The only parameter supported is ${caller_id_number} which is 
 translated to the number passed to cidlookup.  The query should return a single
 value which is the name.
citystate-sql
 The SQL to execute when falling back to city/state query.  The only parameter supplied is
 ${caller_id_number} which is the number passed to cidlookup.  The query should return a 
 single value which is the name.  This is ONLY used for NANPA numbers.  They must be 11 
 digits and start with 1.  

Sample configuration file:

<configuration name="cidlookup.conf" description="cidlookup Configuration">
  <settings>
    <param name="url" value="https://api.opencnam.com/v2/phone/${caller_id_number}?format=pbx&account_sid=ACCOUNTSID&auth_token=AUTHTOKEN"/>
    <param name="cache" value="false"/>

    <param name="odbc-dsn" value="phone:phone:phone"/>
    <param name="sql" value="
SELECT name||' ('||type||')' AS name
  FROM phonebook p JOIN numbers n ON p.id = n.phonebook_id
  WHERE n.number='${caller_id_number}'
  LIMIT 1
    "/>
    <param name="citystate-sql" value="
    SELECT ratecenter||' '||state as name
      FROM npa_nxx_company_ocn
      WHERE npa = ${caller_id_number:1:3} AND nxx = ${caller_id_number:4:3}
      LIMIT 1
      "/>
  </settings>
</configuration>

Trivial sample configuration file:

<configuration name="cidlookup.conf" description="cidlookup Configuration">
  <settings>
    <param name="url" value="https://api.opencnam.com/v2/phone/+${caller_id_number}"/>
    <param name="cache" value="false"/>
  </settings>
</configuration>

Sample schema for PostgreSQL:

phone=> \d phonebook
                           Table "fs.phonebook"
 Column |  Type   |                       Modifiers
--------+---------+--------------------------------------------------------
 id     | integer | not null default nextval('phonebook_id_seq'::regclass)
 name   | text    | not null
 notes  | text    | not null default ''::text
Indexes:
    "phonebook_pkey" PRIMARY KEY, btree (id)

phone=> \d p_numbers
                              Table "fs.p_numbers"
    Column    |  Type   |                       Modifiers                       
--------------+---------+--------------------------------------------------------
 id           | integer | not null default nextval('p_numbers_id_seq'::regclass)
 phonebook_id | integer |
 number       | text    | not null default ''::text
 type         | text    | not null default 'h'::text
Indexes:
    "p_numbers_pkey" PRIMARY KEY, btree (id)
    "i_numbers" btree (number)
Foreign-key constraints:
    "numbers_phonebook_id_fkey" FOREIGN KEY (phonebook_id) REFERENCES phonebook(id) ON DELETE CASCADE

Table creation query for the above schema:

 create table phonebook(id serial PRIMARY KEY, name text '', notes text default 'h'); create table p_numbers(id serial PRIMARY KEY, phonebook_id integer, number text default '', type text default 'h'); create unique index number on p_numbers (number); alter table p_numbers add constraint numbers_phonebook_id_fkey FOREIGN KEY (phonebook_id) REFERENCES phonebook(id) ON DELETE CASCADE;

Caching

If memcache is enabled, the cache will be checked first before doing other lookups. However, only the results of successful URL lookups are cached. Lookups from the "City, State" fallback are not cached.


Falling back to "City State" in the absence of a name

Sometimes it would be nice to give the general location of the number based on telco records. In that case, a bit of setup needs to be done.

The table needs to be loaded into the same database as your cidlookup directory database. The CSV file is available at: npa nxx file

I loaded this data into PostgreSQL with the following ddl:

CREATE TABLE npa_nxx_company_ocn (
    npa smallint NOT NULL,
    nxx smallint NOT NULL,
    company_type text,
    ocn text,
    company_name text,
    lata integer,
    ratecenter text,
    state text
);
CREATE UNIQUE INDEX npanxx_idx ON npa_nxx_company_ocn USING btree (npa, nxx);

You can then load the data using psql with:

phone=> \copy npa_nxx_company_ocn from 'npa-nxx-companytype-ocn.csv' with csv
phone=> select count(*) from npa_nxx_company_ocn ;
 count
--------
 163900
(1 row)

The default config will work with the above data structure. The MySQL equivalent should be pretty simple.

Setting Outbound Name

Some phones (polycom and snom) support setting the callee's name once the call is complete. This allows one to see the name of the person being called as well as the number. Using cidlookup the name could come out of your corporate directory and if that doesn't work will be queried against a cnam lookup service. Assuming you've already normalized your number to e164 (without the leading +), just add the following to your dialplan prior to the bridge:

        <action application="export" data="callee_id_name=${cidlookup($1)}" />

Getting Caller ID with OpenCNAM

OpenCNAM (https://www.opencnam.com/) is a large CNAM provider that works well with Freeswitch. OpenCNAM provides two tiers of CNAM lookups that you can use:

  • A Hobbyist Tier, which allows you to do up to 60 cached CNAM queries per hour, completely free.
  • A Professional Tier, which allows you to do unlimited real-time CNAM queries, for a small fee ($0.004 per successful lookup).

To get started with OpenCNAM's Hobbyist Tier, you don't need to do anything. OpenCNAM's Hobbyist Tier doesn't require any registration, accounts, etc., and is enabled by default in the cidlookup.conf.xml configuration file.

If you need to do more than 60 requests per hour, or prefer to have more accurate Caller ID information (with real-time CNAM queries), you can create an OpenCNAM account on their website: https://www.opencnam.com/, deposit funds into your account, then update your cidlookup.conf.xml configuration file appropriately. Once you've created an OpenCNAM account, you'll notice that on your dashboard page (https://www.opencnam.com/dashboard) you have two account tokens at the top of your page: an Account SID and Auth Token. To make Freeswitch use OpenCNAM's Professional Tier, and perform only real-time CNAM queries, modify your cidlookup.conf.xml file's URL attribute like so:

<configuration name="cidlookup.conf" description="cidlookup Configuration">
 <settings>
  <param name="url" value="https://api.opencnam.com/v2/phone/${caller_id_number}?format=pbx&account_sid=YOUR_ACCOUNT_SID&auth_token=YOUR_AUTH_TOKEN"/>
  <param name="cache" value="false"/>
 </settings>
</configuration>


Using PHP to do CID lookups

If you run also a webserver and have PHP on it you can query web directories for the caller id. In the following example I use http://tel.search.ch - a provider to lookup swiss phone numbers. They offer an api with XML output from which the name then will be parsed. For the api you have to get the key. In the case of http://tel.search.ch you can get the key from here: http://admin.tel.search.ch/api/getkey

Prerequisite is the php5-curl library that must be installed on your server. Adjust this as required.

<?php

/*********************************************************
CONFIG
*********************************************************/

$apiKey = '******';



/*******************************************************
BELOW BE DRAGONS
*******************************************************/

$cid = $_GET['cid'];
$check=strlen($cid);

// Do a few checks for the input... e.g. only lookup swiss numbers
if($check == 10) {
        // 10 digits as required
} elseif($check == 11) {
        $sub1 = substr($cid, 0 ,2);
        $sub2 = substr($cid, 2);
        // check if swiss country code is added
        if($sub1 == '41') {
                $cid = '0' . $sub2;
        }
} elseif ($check == 9) {
        // check if leading 0 is missing
        $cid = '0' . $cid;
}

// Run query
$q = 'http://tel.search.ch/api/?key=' . $apiKey . '&pos=1&maxnum=1&was=' . $cid;


$ch=curl_init();
curl_setopt($ch,        CURLOPT_URL,            $q);
curl_setopt($ch,        CURLOPT_HEADER,         0);
curl_setopt ($ch,       CURLOPT_RETURNTRANSFER, 1);

$result = curl_exec($ch);
curl_close($ch);

// As http://tel.search.ch provides an xml result, you have to filter out the number
$result = explode('<tel:name>', $result);
$result = explode('</tel:name>', $result[1]);
$result = $result[0];

if($result == '') {
        // If there is no result, then assign the cid as result. If you do more checks, then you may want to comment the following line out
         $result = $cid;
} else {
         $result = $result;
}

$result = utf8_encode($result);

echo $result;

?>

Save that script somewhere on your server and enhance the freeswitch/conf/autoload_configs/cidlookup.conf.xml with this:

         <param name="url" value="http://localhost/cid/index.php?cid=${caller_id_number}"/>

The above example assumes that you named the PHP script index.php and that it resides in the /cid/ folder from the server.

Doing CID lookups in the background

A CID lookup can introduce a delay of several seconds, depending on which service you use. If you answer with an IVR, the CID lookup can happen in the background, while the IVR is playing the greeting.

To do this, first create the following LUA script. I call it cid_bg.lua

--[[
  Lookup the CID name, and set the effective_caller_id_name variable.
  This script will run in the background, so we need to set the variable via the UUID.
]]
api = freeswitch.API();
uuid = argv[1];
if not uuid or uuid == "" then return end;
number = api:executeString("uuid_getvar " .. uuid .. " caller_id_number");
name = api:executeString("cidlookup " .. number);
api:executeString("uuid_setvar " .. uuid .. " effective_caller_id_name " .. name);

Next, call the script from the "public" dialplan as follows:

  <extension name="incoming main">
    <condition field="destination_number" expression="^(12025551212|18035551212)$" require-nested="false">
      <condition field="${cond(${caller_id_name} == ${caller_id_number} ?true:false)" expression="^true$">
        <action application="set" data="api_result=${luarun cid_bg.lua ${uuid}}"/>
      </condition>
      <action application="answer"/>
      <action application="sleep" data="1000"/>
      <action application="ivr" data="main_ivr"/>
      <action application="transfer" data="operator_vmail XML default"/>
    </condition>
  </extension>