Provider AGI script

From Kolmisoft Wiki
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

This topic is ADVANCED and shows how to MODIFY MOR call flow. Please do not use this functionality if you are not sure what you are doing!

Asterisk AGI knowledge is necessary to use this feature.

What is it for?

This feature allows to execute custom script which tells MOR to either allow/reject call to specific Provider or add suffix to destination number when dialing through specific Provider. This is useful if you require some additional number/Provider validation that is not implemented in MOR.

How does it work?

If this feature is enabled then MOR Core executes custom AGI script. Script is executed just before dial command of each Provider (after all other validations are done by Core). MOR Core passes these variables to script:

  • MOR_AGI_DESTINATION - localized destination number.
  • MOR_AGI_PROVIDER_ID - Provider id which is being validated.

AGI script should validate number/Provider and set these variables back to Core (via AGI):

  • MOR_AGI_STATUS - this variables tells core to allow/reject call. If value is 1, then call is allowed. If value is 0, then call is rejected. This variable is required!
  • MOR_AGI_RESPONSE - variable is used to pass description of why call was rejected or allowed to go to Provider. For example, this variable could contain message "Number is blocked". Variable is optional.
  • MOR_AGI_RUN_TIME - if your script calculates run time, then you can set elapsed time to this variable. Variable is optional.
  • MOR_AGI_TIMEOUT - in case your script has timeout prevention and you want to reject call on timeout, you may set this variable value to 1 and then core will reject call when AGI script timeouts. Variable is optional.
  • MOR_AGI_DESTINATION_SUFFIX - if this variable is set, then value will be added to destination number. For example if original destination number is 370123456 and MOR_AGI_DESTINATION_SUFFIX is set to ;cic=123, then MOR will send 370123456;cic=123 as destination number to Provider. Variable is optional.

As you can see, call flow can be controlled by using variables MOR_AGI_STATUS and MOR_AGI_TIMEOUT. Also note that variable MOR_AGI_STATUS is required and call will be rejected if this variable is missing (or value is not 1).
If AGI script returns MOR_AGI_STATUS = 0 and this way rejects call, then other Providers will not be checked. Call will be terminated completely. Following hangupcause codes are assosiated with this feature:

  • 266 Provider skipped because AGI script returned timeout error
  • 267 Provider skipped because AGI script rejected this provider (AGI script status 0)

How to enable this feature

For this feature to work, you need to enable 'Execute AGI script' in selected Provider settings. In addition, you have to set path to AGI script in mor.conf configuration file (variable provider_agi_script_path).

HLR number lookup example script

Asterisk AGI can be used in many programming languages. In this example we will use PHP AGI library (http://phpagi.sourceforge.net, look for phpagi-2.20.tgz or newer).
Let's build AGI script that checks number availability in HLR database.

Note that Kolmisoft does not provide support for custom AGI scripts and these scripts should be developed by MOR administrator! Also, HLR services are paid and Kolmisoft does not provide HLR service accounts.

HLR number lookup PHP example script:

#!/usr/bin/php -q
<?php

// These values should be set by admin
define("HLR_PASSWORD", "YOUR_PASSWORD");
define("HLR_API_KEY", "YOUR_API");
define("TIMEOUT_SECONDS", "4");

// These lines are used to calculate script run time
declare(ticks = 1);
$start = microtime(true);

if (function_exists('pcntl_signal')) {
    pcntl_signal(SIGHUP, SIG_IGN);
}

error_reporting(E_ALL ^ (E_NOTICE | E_WARNING));

// Include Asterisk PHP AGI library (http://phpagi.sourceforge.net)
include("/var/lib/asterisk/agi-bin/HLR/lib/phpagi/phpagi.php");

// Function to loopuk number in HLR database
function lookupCarrier(&$response, &$status, $msisdn, &$timeout_state) {

    // Set URL
    $url = "https://www.hlrlookup.com/api/hlr/?apikey=" . HLR_API_KEY . "&password=" . HLR_PASSWORD . "&msisdn=$msisdn";
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, TIMEOUT_SECONDS);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, TIMEOUT_SECONDS);

    // Send request and get response
    $response = curl_exec($ch);

    // Check if response is received
    if (strlen($response)) {
        // If we got response, then connection did not timeout
        $timeout_state = 0;

        // Parse response
        $decoded_response = json_decode($response);
        // Get value of 'error_text' variable
        if (isset($decoded_response->{'error_text'})) {
            $status = $decoded_response->{'error_text'};
        }
    }

    // Limit response length
    $response = substr($response, 0, 1000);
}

// initialize AGI
$agi = new AGI();
// Get variable MOR_AGI_DESTINATION (this variable is set by MOR Core)
$msisdn = $agi->get_variable("MOR_AGI_DESTINATION");
$msisdn = $msisdn['data'];

$response = '';
$status = '';

// Set default status
$agi->set_variable("MOR_AGI_STATUS", "0");
// Set default state
$timeout_state = 1;

// Get status
lookupcarrier($response, $status, $msisdn, $timeout_state);

// Map response status to AGI return status
switch ($status) {
    // Set MOR_AGI_STATUS to 1 if error_text contains 'Absent Subscriber' or 'Live' messages
    // Call will be accepted on these messages and rejected on any other
    case "Absent Subscriber": $agi->set_variable("MOR_AGI_STATUS", "1"); break;
    case "Live": $agi->set_variable("MOR_AGI_STATUS", "1"); break;
}

// Calculate elapsed time
$time_elapsed_secs = microtime(true) - $start;
// Set variables back to MOR Core (MOR_AGI_STATUS is already set)
$agi->set_variable("MOR_AGI_RUN_TIME", $time_elapsed_secs);
$agi->set_variable("MOR_AGI_RESPONSE", $response);
$agi->set_variable("MOR_AGI_TIMEOUT", $timeout_state);

return 0;

?>

Let's create separate directory for HLR lookup functionality:

mkdir -p /var/lib/asterisk/agi-bin/HLR

This directory will include our PHP AGI script and library:

[root@dev]# ls -l /var/lib/asterisk/agi-bin/HLR/
total 8
-rwxrwxrwx 1 root root 2100 Apr 15 16:10 hlr_carrier_lookup.php
drwxrwxrwx 3 root root 4096 Mar 12 22:24 lib

Set executable permissions to our AGI script:

chmod 777 -R /var/lib/asterisk/agi-bin/HLR/hlr_carrier_lookup.php

Now that we have placed our script in /var/lib/asterisk/agi-bin/HLR/ and set proper permission, we need to enable Provider AGI script in /etc/asterisk/mor.conf configuration file. To do so, just set correct path to our AGI script in variable provider_agi_script_path:

provider_agi_script_path = /var/lib/asterisk/agi-bin/HLR/hlr_carrier_lookup.php

Now reload MOR core:

asterisk -rx "mor reload"

Finally, enable 'Execute AGI script' in selected Provider settings.

Example how to add suffix to destination number

It is possible to add suffix to destination number when dialing through specific Provider. In this example PHP script we retrieve suffix by MOR_AGI_DESTINATION/MOR_AGI_PROVIDER_ID from some external database. Retrieved suffix is set to MOR_AGI_DESTINATION_SUFFIX and MOR core will use this variable to modify outgoing number.

#!/usr/bin/php -q
<?php

// These lines are used to calculate script run time
declare(ticks = 1);
$start = microtime(true);

if (function_exists('pcntl_signal')) {
    pcntl_signal(SIGHUP, SIG_IGN);
}

error_reporting(E_ALL ^ (E_NOTICE | E_WARNING));

// Include Asterisk PHP AGI library (http://phpagi.sourceforge.net)
include("/var/lib/asterisk/agi-bin/HLR/lib/phpagi.php");

// initialize AGI
$agi = new AGI();

// Get variable MOR_AGI_DESTINATION (this variable is set by MOR Core)
$destination_number = $agi->get_variable("MOR_AGI_DESTINATION");
$destination_number = $destination_number['data'];

// Get variable MOR_AGI_PROVIDER_ID (this variable is set by MOR Core)
$provider_id = $agi->get_variable("MOR_AGI_PROVIDER_ID");
$provider_id = $provider_id['data'];


// CODE GOES HERE
// CODE GOES HERE
// CODE GOES HERE

// set to 1, otherwise provider will be skipped
$agi->set_variable("MOR_AGI_STATUS", "1");

// SELECT CIC BY $destination_number or $provider_id FROM EXTERNAL DB AND SET IT TO MOR_AGI_DESTINATION_SUFFIX
$agi->set_variable("MOR_AGI_DESTINATION_SUFFIX", ";cic=123");

// CODE GOES HERE
// CODE GOES HERE
// CODE GOES HERE


// Calculate elapsed time
$time_elapsed_secs = microtime(true) - $start;

// Set other variables
$agi->set_variable("MOR_AGI_RUN_TIME", $time_elapsed_secs);
$agi->set_variable("MOR_AGI_TIMEOUT", "0");

return 0;

?>

If user dials 370123456 and retrieved suffix is ;cic=123, then outgoing number will be 370123456;cic=123

How to debug

If you suspect that script is not working properly, you can check Call Log to get more information about that call attempt.
Firstly, we need to find those calls. There are two hangup cause codes related to Provider AGI script:

  • 266 Provider skipped because AGI script returned timeout error
  • 267 Provider skipped because AGI script rejected this provider (AGI script status 0)

In Last Calls page we can find such calls:
Provider agi debug last calls.png

Press blue Call info button and then at the bottom of the page click 'Retrieve Log file' button. Wait till Log file is found and then you should be able to see additional information about custom Provider AGI script:

Provider agi debug call log.png

From this output we can see that AGI script status is 0, that means script rejected call. AGI Script response output shows more detailed information and we can see that 'error_text' contains message 'Dead'. Our example AGI script rejects call based on 'error_text' variable so we know that this particular number was rejected correctly.
In addition, we can see that script took about 2 seconds to execute and it did not timeout.