Asterisk-LCR

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

- 0.08
  - Added search_rates method to generic storage module
  - Tweaked the 'bin' script so that 'locale' is optional in the Dialer section
  - Added countries list in /contrib directory
  - Added ITSP-centric features such as rates generation & management

- 0.07 (2006-01-24)
  - Done lots of refactoring on the ./bin scripts
  - Wrote an abstract persistence storage database layer
  - Wrote Asterisk::LCR::Importer::DiskStatic (slow)
  - Wrote Asterisk::LCR::Importer::DiskBlock (faster but non granular)
  - Added RichMedium importer
  - Updated documentation

MANIFEST  view on Meta::CPAN

lib/Asterisk/LCR/Route.pm
lib/Asterisk/LCR/Storage.pm
lib/Asterisk/LCR/Storage/DiskBlob.pm
lib/Asterisk/LCR/Storage/DiskStatic.pm
lib/Asterisk/LCR/Storage/MyRoutes.pm
LICENSE
Makefile.PL
MANIFEST			This list of files
META.yml
README
t/001_locale.t

bin/asterisk-lcr-dialplan  view on Meta::CPAN


$SIG{__WARN__} = sub { $_[0] !~ /Can't locate AGI\/Fake\.pm/ and warn @_ };

@ARGV || die "Usage: $0 <config_file>";
Config::Mini::parse_file (shift);

$Asterisk::LCR::Storage::MyRoutes::FILE = shift || "__NO_FILE__";

our $STORE  = Config::Mini::instantiate ("storage") || die "no storage configured";
our $DIALER = Config::Mini::instantiate ("dialer")  || die "no dialer configured";
our $LOCALE = $DIALER->locale();


# ----------------------------------------------------------------------------
print STDERR "Reading all possible prefixes from all_rates.csv\n";
our %Prefixes = ();

open FP, "<all_rates.csv" or die "Cannot open-read all_rates.csv";
my $head = <FP>;
while (<FP>)
{

bin/asterisk-lcr-myroutes  view on Meta::CPAN

Config::Mini::parse_file (shift @ARGV);

@ARGV || die "Usage: $0 <config_file> <country_prefixes> <rules_file>";
my $country_pfx = shift (@ARGV);

@ARGV || die "Usage: $0 <config_file> <country_prefixes> <rules_file>";
my $rategen = Routegen->new (file => shift @ARGV);

our $STORE    = Config::Mini::instantiate ("storage")  || die "no storage configured"; 
our $DIAL     = Config::Mini::instantiate ("dialer")   || die "no dialer configured"; 
our $LOCALE   = $DIAL->locale();

my %pfx2label = ();
my %pfx2rate  = ();

print STDERR "Importing supported prefixes\n";
open FP, "<$country_pfx" or die "Cannot read-open $country_pfx!";
while (<FP>)
{
    chomp();
    my ($key, $val) = split /\s*,\s*/, $_, 2;

lib/Asterisk/LCR.pm  view on Meta::CPAN

  # example.
  [comparer]
  package  = Asterisk::LCR::Comparer::XERAND
  currency = eur
  
  # Asterisk::LCR supports pluggable dialing strategies. Currently there is
  # 'MinCost' which tries the absolutely cheapest route, and 'MinTime' which
  # tries the $n cheapest providers simultaneously.
  [dialer]
  package  = Asterisk::LCR::Dialer::MinCost
  locale   = fr 
  
  # Finally, you need to define which providers rates you want to import. 
  [import:voipjet]
  package  = Asterisk::LCR::Importer::VoIPJet
  dial     = us IAX2/login@voipjet/REPLACEME
  
  [import:nufone]
  package  = Asterisk::LCR::Importer::NuFone
  dial     = us IAX2/login@NuFone/REPLACEME

lib/Asterisk/LCR.pm  view on Meta::CPAN

You can choose between two strategies:


=head3 dialer - Asterisk::LCR::Dialer::MinCost

This strategy minimizes cost by trying from cheapest to most expensive provider
for any given route, in the limit of 3 providers.

  [dialer]
  package  = Asterisk::LCR::Dialer::MinCost
  locale   = fr
  limit    = 3

=head3 dialer - Asterisk::LCR::Dialer::MinTime

This strategy minimizes PDD (Post-Dialing-Delay) by trying dialing out the 3
cheapest providers at the same time.

  [dialer]
  package  = Asterisk::LCR::Dialer::MinCost
  locale   = fr
  limit    = 3


=head2 import modules

ATTENTION: ALL import sections must be named [import:<something>] and ALL of
them must have a unique name.

These modules are used to import / download rates from various providers. The
following modules are available.

lib/Asterisk/LCR/Dialer.pm  view on Meta::CPAN

use base qw /Asterisk::LCR::Object/;
use Asterisk::LCR::Locale;
use Config::Mini;
use warnings;
use strict;


our $STORE = undef;


=head2 $self->locale();

Returns the 'locale' attribute.

=cut
sub locale
{
    my $self = shift;
    my $loc  = $self->{locale};
    return $loc ? Asterisk::LCR::Locale->new ($loc) : undef;
}


=head2 $self->set_locale ($locale);

Sets the 'locale' attribute to $locale.

=cut
sub set_locale
{
    my $self = shift;
    $self->{locale} = shift;
}


=head2 $self->limit();

Returns the optional 'limit' attribute.

Returns 100000 if it's not defined (quasi-infinity)

=cut

lib/Asterisk/LCR/Dialer.pm  view on Meta::CPAN

sub set_opts
{
    my $self = shift;
    $self->{opts} = shift;
}


=head2 $self->process ($number);

Turns $number into a canonical, global number if $number
if $self->locale() returns an Asterisk::LCR::Locale object.

Then calls $self->_process ($number). This method must
be defined in subclasses.

=cut
sub process
{
    my $self = shift;
    my $num  = shift;
    my $loc  = $self->locale();
    
    $loc and do { $num = $loc->local_to_global ($num) };
    $self->_process ($num);
}


sub _process
{
    die "Asterisk::LCR::Dialer::process() is a virtual method";
}

lib/Asterisk/LCR/Dialer.pm  view on Meta::CPAN

within Dial()

Extract a string dial template from the asterisk config file.

For example, say $rate->{provider} = 'voipjet'. This method will look for
an asterisk variable called 'ASTERISK_LCR_TMPL_VOIPJET'. The grammar to be used
for this syntax is:

  VARIABLE: [LOCALE] DIAL_STRING

  LOCALE: (scalar string, i.e. 'us' or 'fr' or even /var/mylocale.txt)

  DIAL_STRING: <whateverwhatever>REPLACEME<whateverwhatever>

Where REPLACEME will be replaced by the phone number to dial.

If LOCALE is defined and an Asterisk::LCR::Locale object can be created,
then $num is assumed to be given as a canonical, international
number.

For example, if LOCALE is 'fr' and the number is 33575874745, then

lib/Asterisk/LCR/Dialer.pm  view on Meta::CPAN

sub dial_string
{
    my $self = shift;
    my $num  = shift || return;
    my $rate = shift || return;
    
    my $provider   = $rate->provider();
    my $provider_o = Config::Mini::instantiate ("import:$provider");
    my $value      = $provider_o->{dial};
    
    my ($locale, $dialtmpl) = ($value =~ /\s/) ? ( split /\s+/, $value, 2 ) : (undef, $value);
    for ($locale)
    {
    	$locale || next;
    	$locale = Asterisk::LCR::Locale->new ($locale) || next;
    	$num = $locale->global_to_local ($num);
    }
    
    $dialtmpl || return;
    $dialtmpl =~ s/REPLACEME/$num/;
    return ($dialtmpl);
}


=head2 $self->rates ($number);

lib/Asterisk/LCR/Dialer/MinCost.pm  view on Meta::CPAN

use strict;


sub _process
{
    my $self   = shift;
    my $prefix = shift || return;
    my @rates  = $self->rates ($prefix);
    @rates || return [];
        
    my $local_prefix = $self->locale() ? $self->locale()->global_to_local ($prefix) : $prefix;
    my $exten_remove = length ($local_prefix);
    
    $prefix = "$prefix\${EXTEN:$exten_remove}";
    my $res ||= [];
    foreach my $rate (@rates)
    {
        my $str = $self->dial_string ($prefix, $rate) || next;
        push @{$res}, $str;
    }
    

lib/Asterisk/LCR/Importer.pm  view on Meta::CPAN


  package MyOwnImporter;
  use base qw /Asterisk::LCR::Importer/;
  use warnings;
  use strict;
  
  sub new
  {
    my $class = shift;
    my $self  = $class->SUPER::new (@_);
    $self->{prefix_locale}            = 'us'
    $self->{prefix_position}          = '0'
    $self->{label_position}           = '1'
    $self->{rate_position}            = '4'
    $self->{first_increment_position} = '2'
    $self->{increment_position}       = '3'
    $self->{connection_fee}           = '0'
    $self->{currency}                 = 'USD'
    $self->{uri}                      = 'http://www.plainvoip.com/ratedump.php'
    $self->{separator}                = '(?:,|(?<=\d)\/(?=\d))'
    return $self;

lib/Asterisk/LCR/Importer.pm  view on Meta::CPAN

=head2 $self->prefix ($rec);

Extracts and returns the prefix from $rec.

=cut
sub prefix
{
    my $self = shift;
    my $rec  = shift;
    my $pos  = $self->prefix_pos();
    my $loc  = $self->prefix_locale();
    
    my $res  = $rec->[$pos];
    if ($loc)
    {
        $res = $loc->local_to_global ($res);
        $res = $loc->normalize ($res);
    }
    
    return $res;
}

lib/Asterisk/LCR/Importer.pm  view on Meta::CPAN

=head2 $self->prefix_pos();

Returns the position of the field which contains the prefix in the CSV data.
By default, returns 0.
If $self->{prefix_position} is defined, returns it instead.

=cut
sub prefix_pos { my $self = shift; return defined $self->{prefix_position} ? $self->{prefix_position} : 0 }


=head2 $self->prefix_locale();

Returns the locale which should be used for normalizing / translating the prefix.

Returns undef unless $self->{prefix_locale} is defined.

See L<Asterisk::LCR::Locale> for more details.

=cut
sub prefix_locale
{
    my $self = shift;
    $self->{prefix_locale} || return;
    $self->{prefix_locale_obj} ||= Asterisk::LCR::Locale->new ( $self->{prefix_locale} );
    return $self->{prefix_locale_obj};
}


=head2 $self->label ($rec);

Extracts and returns the label from $rec.

=cut
sub label
{

lib/Asterisk/LCR/Importer.pm  view on Meta::CPAN

=cut
sub rates
{
    my $self   = shift;
    $self->{rates} and return $self->{rates};
    
    my $data   = $self->get_data();
    my $filter = $self->filter();
    my $comma  = $self->separator();

    my $locale = Config::Mini::get ("dialer", "locale");
    my $loc    = $locale ? Asterisk::LCR::Locale->new ($locale) : undef;
    my $res    = {};

    for (@{$data})
    {
        /$filter/ or do {
            print "IGNORED: $_ (doesn't match /$filter/)\n";
            next;
        };
        my $rec = [ split /\s*$comma\s*/, $_ ];
        my $pfx = $self->prefix ($rec);

lib/Asterisk/LCR/Importer/CanonicalCSV.pm  view on Meta::CPAN



##
# $self->rates();
# ---------------
# Returns a { <international_code> => <rate> } hash reference
##
sub rates
{
    my $self = shift;
    my $locale = Asterisk::LCR::Locale->new ("fix_intl");
    $self->{rates} ||= do {
       my $data = $self->get_data();
       my $res  = {};
       for (@{$data})
       {
           my ($prefix, $label, $provider, $currency, $rate, $connection_fee, $first_increment, $increment) = split /\s*,\s*/, $_;
           $prefix = $locale->local_to_global ($prefix);   
           $res->{$prefix} = Asterisk::LCR::Route->new (
  	    connection_fee  => $connection_fee,
	    first_increment => $first_increment,
	    increment       => $increment,
	    currency        => $currency,
	    rate            => $rate,
	    provider	    => $provider,
	    label	    => $label,
            prefix          => $prefix,
        );

lib/Asterisk/LCR/Importer/MutualPhone.pm  view on Meta::CPAN

package Asterisk::LCR::Importer::PlainVoip;
use base qw /Asterisk::LCR::Importer/;
use warnings;
use strict;


sub new
{
    my $class = shift;
    my $self  = $class->SUPER::new (@_);
    $self->{prefix_locale}   = 'us';
    $self->{prefix_position} = '0';
    $self->{label_position}  = '1';
    $self->{rate_position}   = '2';
    $self->{first_increment} = '1';
    $self->{increment}       = '1';
    $self->{connection_fee}  = '0';
    $self->{currency}        = 'USD';
    $self->{uri}             = 'http://mutualphone.com/mutualphone-a-z.csv';
    return $self;
}

lib/Asterisk/LCR/Importer/NuFone.pm  view on Meta::CPAN

use base qw /Asterisk::LCR::Importer/;
use warnings;
use strict;


sub new
{
    my $class = shift;
    my $self  = $class->SUPER::new (@_);
    $self->{prefix_position}          ||= 1;
    $self->{prefix_locale}            ||= 'us';
    $self->{label_position}           ||= 0;
    $self->{rate_position}            ||= 2;
    $self->{currency}                 ||= 'USD';
    $self->{connection_fee}           ||= 0;
    $self->{first_increment}          ||= 15;
    $self->{increment}                ||= 15;
    $self->{uri}                      ||= 'https://www.nufone.net/rates.csv';
    $self->{filter}                   ||= '^.*,\d+,';
    return $self;
}

lib/Asterisk/LCR/Importer/PlainVoip.pm  view on Meta::CPAN

package Asterisk::LCR::Importer::PlainVoip;
use base qw /Asterisk::LCR::Importer/;
use warnings;
use strict;


sub new
{
    my $class = shift;
    my $self  = $class->SUPER::new (@_);
    $self->{prefix_locale}            = 'us';
    $self->{prefix_position}          = '0';
    $self->{label_position}           = '1';
    $self->{rate_position}            = '4';
    $self->{first_increment_position} = '2';
    $self->{increment_position}       = '3';
    $self->{connection_fee}           = '0';
    $self->{currency}                 = 'USD';
    $self->{uri}                      = 'http://www.plainvoip.com/ratedump.php';
    $self->{separator}                = '(?:,|(?<=\d)\/(?=\d))';
    return $self;

lib/Asterisk/LCR/Importer/RichMedium.pm  view on Meta::CPAN

use warnings;
use strict;


sub new
{
    my $class = shift;
    my $self  = $class->SUPER::new (@_);
    $self->{uri}                      ||= "http://www.richmedium.com/wholesale-termination.csv";
    $self->{prefix_position}          ||= 0;
    $self->{prefix_locale}            ||= 'us';
    $self->{label_position}           ||= 4;
    $self->{rate_position}            ||= 1;
    $self->{first_increment_position} ||= 2;
    $self->{increment_position}       ||= 3;
    $self->{connection_fee}           ||= 0;
    $self->{currency}                 ||= 'USD';
    return $self;
}


lib/Asterisk/LCR/Importer/VoIPJet.pm  view on Meta::CPAN

use base qw /Asterisk::LCR::Importer/;
use warnings;
use strict;


sub new
{
    my $class = shift;
    my $self  = $class->SUPER::new (@_);
    $self->{prefix_position}          ||= 1;
    $self->{prefix_locale}            ||= 'us';
    $self->{label_position}           ||= 0;
    $self->{rate_position}            ||= 2;
    $self->{first_increment_position} ||= 3;
    $self->{increment}                ||= 6;
    $self->{connection_fee}           ||= 0;
    $self->{currency}                 ||= 'USD';
    $self->{uri}                      ||= 'http://voipjet.com/ratescsv.php';
    $self->{filter}                   ||= '^.*(?<!dialing),\d+,';
    return $self;
}

lib/Asterisk/LCR/Locale.pm  view on Meta::CPAN

    };
    
    return $self->{local_to_global_cache}->{$num};
}


sub validate
{
    my $self = shift;
    my $id = $self->id() or do {
        die "asterisk/lcr/locale/id/undefined";
    	return 0;
    };
    
    $self->path() or do {
        die "asterisk/lcr/locale/id/no_path : $id";
    	return 0;
    };
    
    return 1;
}


sub id
{
    my $self = shift;

t/001_locale.t  view on Meta::CPAN

#!/usr/bin/perl
use Test::More 'no_plan';
use lib qw (../lib lib);
use warnings;
use strict;
use Asterisk::LCR::Locale;

my $locale = Asterisk::LCR::Locale->new ('fr');
is ($locale->global_to_local ("33262"), "0262");
is ($locale->local_to_global ("0262"), "262262");

# 262,Reunion,phonext,EUR,0.099,0,30,1
# 33262,Reunion,phonext,EUR,0.349,0,30,1
# 33262692,Reunion - Mobile,phonext,EUR,0.349,0,30,1
# 33692,Reunion - Mobile,phonext,EUR,0.349,0,30,1
# 262692,Reunion - Mobile,phonext,EUR,0.199,0,30,1
# 262,reunion island proper,voipjet,USD,0.0620,0,30,6
# 262692,reunion island cellular,voipjet,USD,0.2682,0,30,6
# 262,Reunion,ykoz,EUR,0.05,0,1,1
# 262692,Reunion Mobile,ykoz,EUR,0.20,0,1,1

is ($locale->local_to_global ($locale->global_to_local ("262")) => "262262");
is ($locale->local_to_global ($locale->global_to_local ("33262")) => "262262");
is ($locale->local_to_global ($locale->global_to_local ("33692")) => "262692");
is ($locale->local_to_global ($locale->global_to_local ("262692")) => "262692");
is ($locale->global_to_local ("33262692") => "0692");
is ($locale->local_to_global ($locale->global_to_local ("33262692")) => "262692");



( run in 2.155 seconds using v1.01-cache-2.11-cpan-5a3173703d6 )