Thursday, 3 September 2015

Asterisk voicemail for Avaya over H.323

If you want to replace your old intuity / audix by something more powerful it’s time to move to Asterisk and use its voicemail solution. In case you only have H.323 protocol activated on your Avaya  then no choice to follow steps below, which will be much more easier with SIP.

I'm using this voicemail for several years now and doesn't have any issue, I'm handling thousand calls with it on different Asterisk servers from different Avaya.

Install pre-requirement prior getting voicemail working


cpan install
cpan install YAML
cpan install threads
cpan install threads::shared
cpan install DBI
cpan install Error
cpan install POSIX
cpan install Log::Dispatch
cpan install Log::Dispatch::File
cpan install File::Spec
cpan install Date::Format
cpan install Net::SMTP
cpan install Asterisk::AGI
cpan install
DBD::SQLite


Create Sqlite database
cd /var/lib/asterisk/agi-bin/
sqlite3 auderix.db

Now you get a shell , you should create table
CREATE TABLE auderix (callednum TEXT,callernum TEXT unique,callername TEXT);
.exit

Create init.d service

cd /etc/init.d/
vi auderix
and copy and paste content below

#!/bin/sh
# Source function library.
prog="Auderix "

start() {
        echo "Starting $prog "
        # start daemon
        /usr/bin/perl /usr/local/bin/auderix-analyzer &> /var/log/auderix.log &
}

stop() {
        echo "Stopping $prog"
        PID=`cat /var/run/auderix-tshark.pid`
        kill -9 $PID
}
reload(){
        stop
        start
}

restart(){
        stop
        start
}

case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  restart)
        restart
        ;;
  *)
        echo $"Usage: $0 {start|stop|restart}"
esac



once done do command below:

chmod a+x auderix
update-rc.d auderix defaults

vi /usr/local/bin/auderix-analyzer
and copy and paste text below:

#!/usr/bin/perl
use threads;
use threads::shared;
use DBI;
use Error qw(:try);
use POSIX qw(setsid);
use Log::Dispatch;
use Log::Dispatch::File;
use File::Spec;
use Date::Format;


use constant LOG_DIR    => '/var/log/auderix';
use constant LOG_FILE   => 'auderix-analyzer-access.log';
use constant EXCPT_FILE   => 'auderix-analyzer-exception.log';
use constant PIDDIR     => LOG_DIR;

my $log = new Log::Dispatch(
      callbacks => sub { my %h=@_; return Date::Format::time2str('%B %e %T', time)." ".$h{message}."\n"; });
$log->add( Log::Dispatch::File->new( name      => 'file1',
                                     min_level => 'warning',
                                     mode      => 'append',
                                     filename  => File::Spec->catfile(LOG_DIR, LOG_FILE),
                                   )
);
my $exception = new Log::Dispatch(
      callbacks => sub { my %h=@_; return Date::Format::time2str('%B %e %T', time)." ".$h{message}."\n"; });

$exception->add( Log::Dispatch::File->new( name      => 'file1',
                                     min_level => 'warning',
                                     mode      => 'append',
                                     filename  => File::Spec->catfile(LOG_DIR, EXCPT_FILE),
                                   )
);
$log->warning("Starting Processing:  ".time());
#
#open tshark in TSHARK file
#
my $tshark_pid = open (TSHARK,"tshark -l -T fields -e qsig.privateNumberDigits -e q931.calling_party_number.digits -e qsig.na.namePresentationAllowedSimple -Y qsig.privateNumberDigits -E occurrence=l -E separator=#|");

my $pidFile = '/var/run/auderix-tshark.pid';
open PIDFILE, ">$pidFile" or die "can't open $pidFile: $!\n";
print PIDFILE $tshark_pid;
close PIDFILE;

while () {
        my ($called,$caller,$name) = split("#",$_);
        # do whatever I want with the variables here ...
        my $thr = threads->new( \&processit,$called,$caller,$name)->detach();
}

sub processit {
        my ($lcalled,$lcaller,$lname) = @_; #local client
                $lname =~ s/'/''/g;
               
                $log->warning("Repondeur! Called : $lcalled, Caller : $lcaller, Name :$lname");
                $log->warning("INSERT OR REPLACE INTO auderix VALUES ('$lcalled', '$lcaller','''$lname''')");
        my $db = DBI->connect("dbi:SQLite:/var/lib/asterisk/agi-bin/auderix.db", "", "",{RaiseError => 1, AutoCommit => 1});
        $db->do("INSERT OR REPLACE INTO auderix VALUES ('$lcalled', '$lcaller','''$lname''')") or $exception->warning("ERROR a l'insertion de INSERT OR REPLACE INTO auderix VALUES ('$lcalled', '$lcaller','$lname')");;
}

mkdir /var/log/auderix
touch /var/log/auderix/auderix-analyzer-access.log
Now we start the service:
/etc/init.d/auderix start

vi /var/lib/asterisk/agi-bin/Auderixrequester.pl
chmod a+x Auderixrequester.pl
copy and paste inside content below:

#!/usr/bin/perl
use DBI;
use Error qw(:try);
use POSIX qw(setsid);
use Log::Dispatch;
use Log::Dispatch::File;
use File::Spec;
use Date::Format;
use Asterisk::AGI;

#my $caller = $ARGV[0];
$AGI = new Asterisk::AGI;
my %input = $AGI->ReadParse();
my $caller = $AGI->get_variable("CALLERID(num)");
my $db = DBI->connect("dbi:SQLite:/var/lib/asterisk/agi-bin/auderix.db", "", "",
{RaiseError => 1, AutoCommit => 1});
my $all = $db->selectall_arrayref("SELECT * FROM auderix where callernum='$caller'");
foreach my $row (@$all) {
        my ($callednum,$callernum,$calledname) = @$row;
        if (length($callernum) == 9 )
        {
                $callernum="0".$callernum;
        }
        elsif (length($callernum) > 9 )
        {
                $callernum="+".$callernum;
        }
        #$AGI->set_variable("callernum", $callernum);
        #$AGI->set_variable("callednum", $callednum);
        print "SET VARIABLE callernum \"$callernum\"\n";
        print "SET VARIABLE callednum \"$callednum\"\n";
        $db->do("DELETE FROM auderix where callednum='$callednum'");
}

Check that process auderix is running by using command below:
ps aux| grep tsh
you should see
tshark -l -T fields -e qsig.privateNumberDigits -e q931.calling_party_number.digits -e qsig.na.namePresentationAllowedSimple -Y qsig.privateNumberDigits -E occurrence l -E separator #

Into /etc/asterisk/ooh323.conf

[general]

 ;The port asterisk should listen for incoming H323 connections.
;Default - 1720
port=1720

;The dotted IP address asterisk should listen on for incoming H323
;connections
;Default - tries to find out local ip address on it's own
bindaddr=0.0.0.0   

;This parameter indicates whether channel driver should register with
;gatekeeper as a gateway or an endpoint.
;Default - no
;gateway=no

; See https://wiki.asterisk.org/wiki/display/AST/IP+Quality+of+Service for a description of these parameters.
tos_audio=ef           ; Sets TOS for RTP audio packets.
cos_audio=5            ; Sets 802.1p priority for RTP audio packets.

;Whether asterisk should use fast-start and tunneling for H323 connections.
;Default - yes
faststart=yes
;h245tunneling=yes

;Whether media wait for connect
;Default - No
;mediawaitforconnect=yes

;H323-ID to be used for asterisk server
;Default - Asterisk PBX
h323id=Anonyme
;e164=none

;CallerID to use for calls
;Default - Same as h323id
;callerid=asterisk

;Whether this asterisk server will use gatekeeper.
;Default - DISABLE
;gatekeeper = DISCOVER
;gatekeeper = a.b.c.d
gatekeeper = DISABLE

;Location for H323 log file
;Default - /var/log/asterisk/h323_log
;tracelevel=20
;logfile=/var/log/asterisk/h323_log

disallow=all
allow=alaw
canreinvite=no
dtmfmode=rfc2833
context=from-h323


[Avaya]
language = fr
country = fr
type=friend
host=10.147.9.64
port=1720
disallow=all
allow=alaw
canreinvite=no
;h245tunneling=yes
;dtmfmode=rfc2833
;dtmfmode=h245signal

As you can see incoming calls coming from ooh323 will goes to extensions.conf into from-h323 context, into this one we’ll call ParisOfficeVoicemail

[from-h323]
include => ParisOfficeVoicemail

Into /etc/asterisk/extensions.conf create a context that will be dialed by from-h323 context in my case:

[ParisOfficeVoicemail]

exten => 46750,1,Progress()
exten => 46750,n,Wait(1) ;//Important because Asterisk is faster than Tshark so it let time to perl script using tshark to write into database
exten => 46750,n,AGI(Auderixrequester.pl)
exten => 46750,n,NoOp(Callernum: ${callernum})
exten => 46750,n,NoOp(Callednum: ${callednum})
exten => 46750,n,Set(CHANNEL(language)=fr)
exten => 46750,n,Set(CALLERID(num)=${callernum})
exten => 46750,n,GotoIf($["${callednum}" = ""]?2000)
exten => 46750,n,GotoIf($["${CALLERID(num)}" = ""]?1000)
exten => 46750,n,VoiceMail(${callednum}@ParisOfficeVoiceMail,su)
exten => 46750,n,Hangup()
exten => 46750,1000,Set(CALLERID(name)="numéro masqué")
exten => 46750,n,VoiceMail(${callednum}@ParisOfficeVoiceMail,su)
exten => 46750,2000,PlayBack(vm-goodbye)
exten => 46750,n,system(/bin/echo "There is an issue with TSHARK please restart it" | /usr/bin/mail -s "TSHARK ISSUE" cyril.constantin@gmail.com)
exten => 46750,n,system(/etc/init.d/auderix restart)
exten => 46750,n,Hangup()



If you are using realtime for storing your voicemail config then create an entry into this mysql table where context would be “ParisOfficeVoicemail”

Mailbox : Your Avaya IP Phone number so in my case 40053
Fullname: Cyril CONSTANTIN
Attach:yes
Attachfmt:wav49
Deletevoicemail:yes
Sendvoicemail:no


Now on Avaya you should have created a route to go to Asterisk, in our case we have setup 46XXX






So it will use IP trunk 62, for more details on how to create IP trunk through H.323 to Asterisk check on my blog there is all details.





We create a hunt group that would be assigned to a coverage path, which will be attributed to IP Phone station.







We need to configure our voicemail config file which will generate an email with subject and voicemail in attachment vi /etc/asterisk/voicemail.conf:


[general]
format=wav49|gsm|wav
serveremail=asterisk@pt0asterisk01.lenumero.local
attach=no
skipms=3000
maxsilence=10
silencethreshold=128
maxlogins=3
charset=UTF-8
fromstring=Serveur Vocal
emailsubject = Vous avez un nouveau message d'une durée de ${VM_DUR} de la part du ${VM_CALLERID}
emailbody = ${VM_NAME}, \n\nVous avez reçu un nouveau message d'une durée de ${VM_DUR} \nde la part de ${VM_CALLERID}.\n\n\n\n\n\t\t\t\t\t\t\t\t\t\t Votre serveur vocal\n

attachfmt=wav49
endvoicemail=no
delete=no

I’ll let you configure your mail server properly.


Make a test call from another Avaya IP Phone to the Avaya phone using coverage path 100 you call should go to Asterisk and into the console you should see  something like below (use asterisk –r)


[Sep  3 10:44:28]     -- Executing [46750@from-h323:1] Wait("OOH323/AvayaSpecificDTMF-2270", "1") in new stack
[Sep  3 10:44:29]     -- Executing [46750@from-h323:2] AGI("OOH323/AvayaSpecificDTMF-2270", "Auderixrequester.pl") in new stack
[Sep  3 10:44:29]     -- Launched AGI Script /var/lib/asterisk/agi-bin/Auderixrequester.pl
[Sep  3 10:44:29] ERROR[18992][C-00000905]: utils.c:1393 ast_carefulwrite: write() returned error: Broken pipe
[Sep  3 10:44:29]     -- AGI Script Auderixrequester.pl completed, returning 0
[Sep  3 10:44:29]     -- Executing [46750@from-h323:3] NoOp("OOH323/AvayaSpecificDTMF-2270", "Callernum: 44241") in new stack
[Sep  3 10:44:29]     -- Executing [46750@from-h323:4] NoOp("OOH323/AvayaSpecificDTMF-2270", "Callednum: 40053") in new stack
[Sep  3 10:44:29]     -- Executing [46750@from-h323:5] Set("OOH323/AvayaSpecificDTMF-2270", "CHANNEL(language)=fr") in new stack
[Sep  3 10:44:29]     -- Executing [46750@from-h323:6] Set("OOH323/AvayaSpecificDTMF-2270", "CALLERID(num)=44241") in new stack
[Sep  3 10:44:29]     -- Executing [46750@from-h323:7] GotoIf("OOH323/AvayaSpecificDTMF-2270", "0?2000") in new stack
[Sep  3 10:44:29]     -- Executing [46750@from-h323:8] GotoIf("OOH323/AvayaSpecificDTMF-2270", "0?1000") in new stack
[Sep  3 10:44:29]     -- Executing [46750@from-h323:9] VoiceMail("OOH323/AvayaSpecificDTMF-2270", "40053@ParisOfficeVoiceMail,su") in new stack
[Sep  3 10:44:30] WARNING[18992][C-00000905]: app_voicemail.c:6350 leave_voicemail: No entry in voicemail config file for '40053'
[Sep  3 10:44:30]     -- Executing [46750@from-h323:10] Hangup("OOH323/AvayaSpecificDTMF-2270", "") in new stack
[Sep  3 10:44:30]   == Spawn extension (from-h323, 46750, 10) exited non-zero on 'OOH323/AvayaSpecificDTMF-2270'

pt0asterisk01*CLI>



In all case you will have maybe to change a little bit some piece regarding your needs regarding your country digit lengths because it was based on French digits length and international format so it require a little bit of knowledge to edit PERL or bash script if needed. Also this article has been done with using Debian 8 and tshark 1.12.1, I know that with older release of tshark it was need into auderix-analyzer to use a different command like below:

my $tshark_pid = open (TSHARK,"tshark -l -T fields -e qsig.privateNumberDigits -e q931.calling_party_number.digits -e qsig.na.namePresentationAllowedSimple -R qsig.privateNumberDigits -E separator=#|");


In all case prepare your routing on Avaya, set H323 trunk between Avaya and Asterisk then on linux shell  use tshark command manually to see what you get from Avaya normally you should get something under this format:
40053#44241#Cyril, CONSTANT

Called extension#Calling extension#Called Name

So you could move forward on setting all scripts above.

Enjoy it and don't hesitate to ask me question and I'll try to do my best to help you.

Best Regards