diff --git a/agc_2-X/trunk/UPGRADE b/agc_2-X/trunk/UPGRADE index 8ed5562d..d9fe55c8 100644 --- a/agc_2-X/trunk/UPGRADE +++ b/agc_2-X/trunk/UPGRADE @@ -633,6 +633,9 @@ OTHER CHANGES: It will define the Hold music played to a customer when the phone user puts them on hold through the phone. +177. Added compatibility with TILTX STIR/SHAKEN Call Shaper pre-carrier call + filtering service. Read the TILTX_SHAKEN_API.txt doc for more info. + diff --git a/agc_2-X/trunk/agi/agi-TILTX_SHAKEN.agi b/agc_2-X/trunk/agi/agi-TILTX_SHAKEN.agi new file mode 100644 index 00000000..6d98bb0c --- /dev/null +++ b/agc_2-X/trunk/agi/agi-TILTX_SHAKEN.agi @@ -0,0 +1,646 @@ +#!/usr/bin/perl +# +# agi-TILTX_SHAKEN.agi version 2.14 +# +# Runs before a call goes to a carrier for optional DNC/Disconnect check as well +# as CID check and STIR/SHAKEN SIP call Identity tagging through TILTX service +# +# You need to put lines similar to those below in your VICIdial carrier dialplan entry: +# +# exten => _91NXXNXXXXXX,n,AGI(/var/lib/asterisk/agi-bin/agi-TILTX_SHAKEN.agi,${EXTEN:-10}-----${CALLERID(num)}-----YES-----) +# +# CLI FLAG OPTIONS: +# 1. Phone number being called +# 2. CallerID number being sent with call +# 3. (YES/NO) whether to speak error messages or not, default 'NO' +# 4. Settings Container ID to use for TILTX API settings, default 'TILTX_SHAKEN_API_KEY' +# +# Copyright (C) 2021 Matt Florell LICENSE: AGPLv2 +# +# changes: +# 210605-0936 - first build +# + +$script = 'agi-TILTX_SHAKEN.agi'; + +$A = 1; # set to 1 for AMD output messages mode +$AMD_LOG = 3; # set to 1 for logfile and 2 forDB log, 3 for both +$wav='.wav'; +$US='_'; + +$secX = time(); #Start time +($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); +$year = ($year + 1900); +$mon++; +if ($mon < 10) {$mon = "0$mon";} +if ($mday < 10) {$mday = "0$mday";} +if ($hour < 10) {$hour = "0$hour";} +if ($min < 10) {$min = "0$min";} +if ($sec < 10) {$sec = "0$sec";} + +$now_date_epoch = time(); +$now_date = "$year-$mon-$mday $hour:$min:$sec"; +$file_day = "$year$mon$mday"; +$SQL_day = "$year-$mon-$mday"; + +# default path to astguiclient configuration file: +$PATHconf = '/etc/astguiclient.conf'; + +open(conf, "$PATHconf") || die "can't open $PATHconf: $!\n"; +@conf = ; +close(conf); +$i=0; +foreach(@conf) + { + $line = $conf[$i]; + $line =~ s/ |>|\n|\r|\t|\#.*|;.*//gi; + if ( ($line =~ /^PATHhome/) && ($CLIhome < 1) ) + {$PATHhome = $line; $PATHhome =~ s/.*=//gi;} + if ( ($line =~ /^PATHlogs/) && ($CLIlogs < 1) ) + {$PATHlogs = $line; $PATHlogs =~ s/.*=//gi;} + if ( ($line =~ /^PATHagi/) && ($CLIagi < 1) ) + {$PATHagi = $line; $PATHagi =~ s/.*=//gi;} + if ( ($line =~ /^PATHweb/) && ($CLIweb < 1) ) + {$PATHweb = $line; $PATHweb =~ s/.*=//gi;} + if ( ($line =~ /^PATHsounds/) && ($CLIsounds < 1) ) + {$PATHsounds = $line; $PATHsounds =~ s/.*=//gi;} + if ( ($line =~ /^PATHmonitor/) && ($CLImonitor < 1) ) + {$PATHmonitor = $line; $PATHmonitor =~ s/.*=//gi;} + if ( ($line =~ /^VARserver_ip/) && ($CLIserver_ip < 1) ) + {$VARserver_ip = $line; $VARserver_ip =~ s/.*=//gi;} + if ( ($line =~ /^VARDB_server/) && ($CLIDB_server < 1) ) + {$VARDB_server = $line; $VARDB_server =~ s/.*=//gi;} + if ( ($line =~ /^VARDB_database/) && ($CLIDB_database < 1) ) + {$VARDB_database = $line; $VARDB_database =~ s/.*=//gi;} + if ( ($line =~ /^VARDB_user/) && ($CLIDB_user < 1) ) + {$VARDB_user = $line; $VARDB_user =~ s/.*=//gi;} + if ( ($line =~ /^VARDB_pass/) && ($CLIDB_pass < 1) ) + {$VARDB_pass = $line; $VARDB_pass =~ s/.*=//gi;} + if ( ($line =~ /^VARDB_port/) && ($CLIDB_port < 1) ) + {$VARDB_port = $line; $VARDB_port =~ s/.*=//gi;} + $i++; + } + +if (!$VARDB_port) {$VARDB_port='3306';} +if (!$AGILOGfile) {$AGILOGfile = "$PATHlogs/agiout.$year-$mon-$mday";} +if (!$CEPLOGfile) {$CEPLOGfile = "$PATHlogs/Cepstral.$year-$mon-$mday";} + +use DBI; +use Time::Local; +use Asterisk::AGI; +$AGI = new Asterisk::AGI; + + +$dbhA = DBI->connect("DBI:mysql:$VARDB_database:$VARDB_server:$VARDB_port", "$VARDB_user", "$VARDB_pass") + or die "Couldn't connect to database: " . DBI->errstr; + +############################################# +##### Gather system_settings ##### +$stmtA = "SELECT sip_event_logging,call_quota_lead_ranking FROM system_settings;"; +$sthA = $dbhA->prepare($stmtA) or die "preparing: ",$dbhA->errstr; +$sthA->execute or die "executing: $stmtA ", $dbhA->errstr; +$sthArows=$sthA->rows; +if ($sthArows > 0) + { + @aryA = $sthA->fetchrow_array; + $SSsip_event_logging = $aryA[0]; + $SScall_quota_lead_ranking = $aryA[1]; + } +$sthA->finish(); +########################################### + +### Grab Server values from the database +$stmtA = "SELECT agi_output,asterisk_version,ext_context FROM servers where server_ip = '$VARserver_ip';"; +$sthA = $dbhA->prepare($stmtA) or die "preparing: ",$dbhA->errstr; +$sthA->execute or die "executing: $stmtA ", $dbhA->errstr; +$sthArows=$sthA->rows; +if ($sthArows > 0) + { + $AGILOG = '0'; + @aryA = $sthA->fetchrow_array; + $DBagi_output = $aryA[0]; + $asterisk_version = $aryA[1]; + $DBext_context = $aryA[2]; + if ($DBext_context) {$ext_context = $DBext_context;} + if ($DBagi_output =~ /STDERR/) {$AGILOG = '1';} + if ($DBagi_output =~ /FILE/) {$AGILOG = '2';} + if ($DBagi_output =~ /BOTH/) {$AGILOG = '3';} + } +$sthA->finish(); + +### begin parsing run-time options ### +if (length($ARGV[0])>1) + { + if ($AGILOG) + {$agi_string = "Perl Environment Dump:"; &agi_output;} + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + if ($AGILOG) {$agi_string = "$i|$ARGV[$i]"; &agi_output;} + $i++; + } + + ### list of command-line array arguments: + @ARGV_vars = split(/-----/, $ARGV[0]); + $phone_number = $ARGV_vars[0]; + $CIDnumber = $ARGV_vars[1]; + $audio_errors = $ARGV_vars[2]; + $TILTX_container = $ARGV_vars[3]; + } +if ( ($audio_errors != 'YES') && ($audio_errors != 'NO') ) + {$audio_errors='NO';} +if (length($TILTX_container) < 1) + {$TILTX_container='TILTX_SHAKEN_API_KEY';} + +if ($AGILOG) {$agi_string = "CLI Variables: |$phone_number|$CIDnumber|$audio_errors|$TILTX_container|"; &agi_output;} + +### gather Asterisk channel variables +$|=1; +while() + { + chomp; + last unless length($_); + if ($AGILOG) + { + if (/^agi_(\w+)\:\s+(.*)$/) + { + $AGI{$1} = $2; + } + } + + if (/^agi_uniqueid\:\s+(.*)$/) {$unique_id = $1; $uniqueid = $unique_id;} + if (/^agi_priority\:\s+(.*)$/) {$priority = $1;} + if (/^agi_channel\:\s+(.*)$/) {$channel = $1;} + if (/^agi_extension\:\s+(.*)$/) {$extension = $1;} + if (/^agi_type\:\s+(.*)$/) {$type = $1;} + if (/^agi_callerid\:\s+(.*)$/) {$callerid = $1; $calleridnum = $callerid;} + if (/^agi_calleridname\:\s+(.*)$/) {$calleridname = $1;} + } + + +if ( (length($callerid)>20) && ($callerid =~ /\"\S\S\S\S\S\S\S\S\S\S\S\S\S\S\S\S\S\S/) ) + { + $callerid =~ s/^\"//gi; + $callerid =~ s/\".*$//gi; +# ### set the callerid to the ACQS value(calleridname) +# print "SET CALLERID $callerid\n"; +# checkresult($result); +# print STDERR "callerID changed: $callerid\n"; + } +if ( ( +(length($calleridname)>5) && ( (!$callerid) or ($callerid =~ /unknown|private|00000000/i) or ($callerid =~ /5551212/) ) +) or ( (length($calleridname)>17) && ($calleridname =~ /\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d/) ) ) + { + $callerid = $calleridname; +# ### set the callerid to the ACQS value(calleridname) +# print "SET CALLERID $callerid\n"; +# checkresult($result); +# print STDERR "callerID changed: $callerid\n"; + } + +if ($AGILOG) {$agi_string = "AGI Environment Dump:"; &agi_output;} + +foreach $i (sort keys %AGI) + { + if ($AGILOG) {$agi_string = " -- $i = $AGI{$i}"; &agi_output;} + } + +if ($AGILOG) {$agi_string = "AGI Variables: |$unique_id|$channel|$extension|$type|$callerid|"; &agi_output;} + + +$VDADcampaign=''; +$VDADphone=''; +$VDADphone_code=''; + +$callerid =~ s/\"//gi; +$callerid =~ s/ .*//gi; +$CIDlead_id = $callerid; +$CIDlead_id = substr($CIDlead_id, 10, 10); +$CIDlead_id = ($CIDlead_id + 0); +if ( ($CLIlead_id > 0) && ($CIDlead_id < 1) ) {$CIDlead_id = $CLIlead_id;} +$VD_lead_id = $CIDlead_id; + +if ($AGILOG) {$agi_string = "+++++ VD TILTX SHAKEN START : |$CIDlead_id|$now_date|"; &agi_output;} + + +##### BEGIN GATHER TILTX AHAKEN API KEY data from settings container record ##### +$TILTX_api_key=''; +$TILTX_api_url='https://api.shaper.tiltx.com/Calls/shaper'; + +$stmtA = "SELECT container_entry FROM vicidial_settings_containers where container_id='$TILTX_container' limit 1;"; +$sthA = $dbhA->prepare($stmtA) or die "preparing: ",$dbhA->errstr; +$sthA->execute or die "executing: $stmtA ", $dbhA->errstr; +$sthArowsTILTX=$sthA->rows; +if ($AGILOG) {$agi_string = "$sthArowsTILTX|$stmtA|"; &agi_output;} +if ($sthArowsTILTX > 0) + { + @aryA = $sthA->fetchrow_array; + $TILTXcontainer_entry = $aryA[0]; + $sthA->finish(); + } +if (length($TILTXcontainer_entry) > 3) + { + @TILTXcontainer_lines = split(/\n/,$TILTXcontainer_entry); + $c=0; + foreach(@TILTXcontainer_lines) + { + $TILTXcontainer_lines[$c] =~ s/;.*|\r|\t| //gi; + if (length($TILTXcontainer_lines[$c]) > 3) + { + if ($TILTXcontainer_lines[$c] =~ /^URL=>/) + { + $TILTX_api_url = $TILTXcontainer_lines[$c]; + $TILTX_api_url =~ s/^URL=>//gi; + if ($AGILOG) {$agi_string = "+++++ TILTX SHAKEN API URL OVERRIDDEN! |$TILTX_api_url|"; &agi_output;} + } + else + { + $TILTX_api_key = $TILTXcontainer_lines[$c]; + } + } + $c++; + } + } +##### END GATHER TILTX AHAKEN API KEY data from settings container record ##### + +if (length($TILTX_api_key) < 4) + { + if ($AGILOG) {$agi_string = "+++++ TILTX SHAKEN API KEY NOT SET! exiting... |$TILTX_api_key|"; &agi_output;} + + if ($audio_errors =~ /YES/i) + { + $AGI->stream_file('sip-silence'); + $AGI->stream_file('sip-silence'); + $AGI->stream_file('an-error-has-occurred'); + $AGI->stream_file('error-number'); + $AGI->stream_file('1'); + $AGI->stream_file('silence'); + $AGI->stream_file('key'); + $AGI->stream_file('is-not-set'); + $AGI->stream_file('silence'); + $AGI->stream_file('vm-goodbye'); + $AGI->stream_file('silence'); + } + + $dbhA->disconnect(); + + $AGI->hangup($channel); + + exit; + } + + + + +##### BEGIN Gather and process TILTX Results ##### +$wgetbin = ''; +if ( -e ('/bin/wget')) {$wgetbin = '/bin/wget';} +else + { + if ( -e ('/usr/bin/wget')) {$wgetbin = '/usr/bin/wget';} + else + { + if ( -e ('/usr/local/bin/wget')) {$wgetbin = '/usr/local/bin/wget';} + else + { + if ($AGILOG) {$agi_string = "+++++ TILTX SHAKEN CANNOT FIND WGET! exiting... |$wgetbin|"; &agi_output;} + + if ($audio_errors =~ /YES/i) + { + $AGI->stream_file('sip-silence'); + $AGI->stream_file('sip-silence'); + $AGI->stream_file('an-error-has-occurred'); + $AGI->stream_file('error-number'); + $AGI->stream_file('2'); + $AGI->stream_file('silence'); + $AGI->stream_file('wget'); + $AGI->stream_file('was-not-found'); + $AGI->stream_file('silence'); + $AGI->stream_file('vm-goodbye'); + $AGI->stream_file('silence'); + } + + $dbhA->disconnect(); + + $AGI->hangup($channel); + + exit; + } + } + } + +# append number to dial and CID number to URL +$TILTX_api_url .= '?ToNumber='.$phone_number.'\&FromNumber='.$CIDnumber; + +### insert a new url log entry +$SQL_log = "$TILTX_api_url"; +$SQL_log =~ s/;|\||\\//gi; +$stmtA = "INSERT INTO vicidial_url_log SET uniqueid='$uniqueid',url_date='$now_date',url_type='TILTX_SHAKEN',url='$SQL_log',url_response='';"; +$affected_rows = $dbhA->do($stmtA); +$stmtB = "SELECT LAST_INSERT_ID() LIMIT 1;"; +$sthA = $dbhA->prepare($stmtB) or die "preparing: ",$dbhA->errstr; +$sthA->execute or die "executing: $stmtA ", $dbhA->errstr; +$sthArows=$sthA->rows; +if ($sthArows > 0) + { + @aryA = $sthA->fetchrow_array; + $url_id = $aryA[0]; + } +$sthA->finish(); + +$url = $TILTX_api_url; +$url =~ s/'/\\'/gi; +$url =~ s/"/\\"/gi; + +my $secW = time(); + +# disconnect from the database to free up the DB connection +$dbhA->disconnect(); + +# Test wget command +# /usr/bin/wget --no-check-certificate --output-document=/tmp/TILTXtmpD_1 --output-file=/tmp/TILTXtmpF_2 --header="x-api-key: XXXXXXXXXX" --header="Content-Type: application/json" https://api.shaper.tiltx.com/Calls/shaper?ToNumber=6155551212\&FromNumber=8635551212 + +# request the TILTX web URL +`$wgetbin --no-check-certificate --output-document=/tmp/TILTXtmpD$US$phone_number$US$secX --output-file=/tmp/TILTXtmpF$US$phone_number$US$secX --header="x-api-key: $TILTX_api_key" --header="Content-Type: application/json" $url `; + +# reconnect to the database to log response and response time +$dbhA = DBI->connect("DBI:mysql:$VARDB_database:$VARDB_server:$VARDB_port", "$VARDB_user", "$VARDB_pass") + or die "Couldn't connect to database: " . DBI->errstr; + +my $secY = time(); +my $response_sec = ($secY - $secW); + +# initialize TILTX SHAKEN response variables +$IsDNC = 0; +$IsDisconnected = 0; +$TILTXID=''; +$Identity=''; +$CAID=''; +$CallerIDToUse=$CIDnumber; + +open(Wdoc, "/tmp/TILTXtmpD$US$phone_number$US$secX") || die "can't open /tmp/TILTXtmpD$US$phone_number$US$secX: $!\n"; +@Wdoc = ; +close(Wdoc); +$i=0; +$Wdocline_cat=''; +foreach(@Wdoc) + { + if ($Wdoc[$i] =~ /"IsDNC":"true"/i) {$IsDNC = 1;} + if ($Wdoc[$i] =~ /"IsDisconnected":"true"/i) {$IsDisconnected = 1;} + if ($Wdoc[$i] =~ /"TILTXID":"[a-zA-Z0-9-_]+/i) + { + $TILTXID = $Wdoc[$i]; + $TILTXID =~ s/.*"TILTXID":"//; + $TILTXID =~ s/".*//; + } + if ($Wdoc[$i] =~ /"Identity":".*"/i) + { + $Identity = $Wdoc[$i]; + $Identity =~ s/.*"Identity":"//; + $Identity =~ s/".*//; + } + if ($Wdoc[$i] =~ /"CAID":".*"/i) + { + $CAID = $Wdoc[$i]; + $CAID =~ s/.*"CAID":"//; + $CAID =~ s/".*//; + } + if ($Wdoc[$i] =~ /"CallerIDToUse":".*"/i) + { + $CallerIDToUse = $Wdoc[$i]; + $CallerIDToUse =~ s/.*"CallerIDToUse":"//; + $CallerIDToUse =~ s/".*//; + } + # log all response data as a single string together + $Wdocline = $Wdoc[$i]; + $Wdocline =~ s/\n|\r/!/gi; + $Wdocline =~ s/\t|\'|\`//gi; + $Wdocline_cat .= "$Wdocline"; + $i++; + } +if (length($Wdocline_cat)<1) + {$Wdocline_cat='';} + +open(Wfile, "/tmp/TILTXtmpF$US$phone_number$US$secX") || die "can't open /tmp/TILTXtmpF$US$phone_number$US$secX: $!\n"; +@Wfile = ; +close(Wfile); +$i=0; +$Wfileline_cat=''; +foreach(@Wfile) + { + $Wfileline = $Wfile[$i]; + $Wfileline =~ s/\n|\r/!/gi; + $Wfileline =~ s/\t|\'|\`//gi; + $Wfileline_cat .= "$Wfileline"; + $i++; + } +if (length($Wfileline_cat)<1) + {$Wfileline_cat='
';} + + +### update url log entry +$stmtA = "UPDATE vicidial_url_log SET url_response='$Wdocline_cat|$Wfileline_cat',response_sec='$response_sec' where url_log_id='$url_id';"; +$affected_rows = $dbhA->do($stmtA); +if ($DB) {print "$affected_rows|$stmtA\n";} + +$stmtA = "INSERT INTO vicidial_tiltx_shaken_log SET db_time=NOW(),server_ip='$VARserver_ip',url_log_id='$url_id',caller_code='$calleridname',phone_number='$phone_number',CIDnumber='$CIDnumber',CallerIDToUse='$CallerIDToUse',IsDNC='$IsDNC',IsDisconnected='$IsDisconnected',TILTXID='$TILTXID',Identity='$Identity',CAID='$CAID';"; + if ($AGILOG) {$agi_string = "|$stmtA|"; &agi_output;} +$affected_rows = $dbhA->do($stmtA); +if ($AGILOG) {$agi_string = "TILTX SHAKEN logging result to DB: $affected_rows $calleridname $CIDnumber $phone_number $VD_lead_id"; &agi_output;} + +### Check for DNC, if true then exit +if ($IsDNC > 0) + { + if ($AGILOG) {$agi_string = "+++++ TILTX SHAKEN DNC NUMBER! exiting... |$IsDNC|"; &agi_output;} + #$AGI->answer(); + + $stmtA = "INSERT IGNORE INTO vicidial_carrier_log SET uniqueid='$uniqueid',call_date=NOW(),server_ip='$VARserver_ip',lead_id='$CIDlead_id',hangup_cause='34',dialstatus='DNC',channel='$channel',dial_time='0',answered_time='0',sip_hangup_cause='503',sip_hangup_reason='TILTX National DNC',caller_code='$calleridname';"; + if ($AGILOG) {$agi_string = "|$stmtA|"; &agi_output;} + $affected_rows = $dbhA->do($stmtA); + if ($AGILOG) {$agi_string = "TILTX SHAKEN adding carrier log entry: $affected_rows $calleridname $CIDnumber $phone_number $CIDlead_id"; &agi_output;} + + if ($audio_errors =~ /YES/i) + { + $AGI->stream_file('sip-silence'); + $AGI->stream_file('sip-silence'); + $AGI->stream_file('do-not-call'); + $AGI->stream_file('number'); + $AGI->stream_file('silence'); + $AGI->stream_file('vm-goodbye'); + $AGI->stream_file('silence'); + } + + $AGI->exec('Set',"DIALSTATUS=DNC"); + $AGI->exec('Set',"HANGUPCAUSE=34"); + + $dbhA->disconnect(); + + $AGI->hangup($channel); + + exit; + } + +# Check for Disconnected number, if true then exit +if ($IsDisconnected > 0) + { + if ($AGILOG) {$agi_string = "+++++ TILTX SHAKEN DISCONNECTED NUMBER! exiting... |$IsDisconnected|"; &agi_output;} + #$AGI->answer(); + + $stmtA = "INSERT IGNORE INTO vicidial_carrier_log SET uniqueid='$uniqueid',call_date=NOW(),server_ip='$VARserver_ip',lead_id='$CIDlead_id',hangup_cause='21',dialstatus='DISCONNECT',channel='$channel',dial_time='0',answered_time='0',sip_hangup_cause='404',sip_hangup_reason='TILTX Disconnect',caller_code='$calleridname';"; + if ($AGILOG) {$agi_string = "|$stmtA|"; &agi_output;} + $affected_rows = $dbhA->do($stmtA); + if ($AGILOG) {$agi_string = "TILTX SHAKEN adding carrier log entry: $affected_rows $calleridname $CIDnumber $phone_number $CIDlead_id"; &agi_output;} + + if ($audio_errors =~ /YES/i) + { + $AGI->stream_file('sip-silence'); + $AGI->stream_file('sip-silence'); + $AGI->stream_file('disconnected'); + $AGI->stream_file('number'); + $AGI->stream_file('silence'); + $AGI->stream_file('vm-goodbye'); + $AGI->stream_file('silence'); + } + + $AGI->exec('Set',"DIALSTATUS=DISCONNECT"); + $AGI->exec('Set',"HANGUPCAUSE=21"); + + $dbhA->disconnect(); + + $AGI->hangup($channel); + + exit; + } + +# Check for change in CIDnumber, if true, then modify CIDnum +if ($CallerIDToUse ne $CIDnumber) + { + if ($AGILOG) {$agi_string = "+++++ TILTX SHAKEN CIDNUMBER CHANGE! |$CIDnumber -> $CallerIDToUse|"; &agi_output;} + + $AGI->exec('Set',"CALLERID(num)=" . $CallerIDToUse); + } + +# Check for TILTXID, if populated, set as a SIP Header +if (length($TILTXID) > 3) + { + if ($AGILOG) {$agi_string = "+++++ TILTX SHAKEN TILTXID variable set! |$TILTXID|"; &agi_output;} + + $AGI->exec('SIPAddHeader', "X-TILTX-ID:" . $TILTXID); + } + +# Check for Identity, if populated, set as a SIP Header +if (length($Identity) > 3) + { + if ($AGILOG) {$agi_string = "+++++ TILTX SHAKEN Identity variable set! |$Identity|"; &agi_output;} + + $AGI->exec('SIPAddHeader', "Identity:" . $Identity); + } + +# Check for CAID, if populated, set as a SIP Header +if ( (length($CAID) > 3) && ($CAID ne '00000000-0000-0000-0000-000000000000') ) + { + if ($AGILOG) {$agi_string = "+++++ TILTX SHAKEN CAID variable set! |$CAID|"; &agi_output;} + + $AGI->exec('SIPAddHeader', "CAID:" . $CAID); + } + +my $secZ = time(); +my $script_time = ($secZ - $secX); +if ($DB) {print "DONE execute time: $script_time seconds\n";} + +##### END Gather and process TILTX Results ##### +exit 0; + + + + + + +########## SUBROUTINES ########## +sub checkresult + { + my ($res) = @_; + my $retval; + $tests++; + chomp $res; + if ($res =~ /^200/) + { + $res =~ /result=(-?\d+)/; + if (!length($1)) + { + # print STDERR "FAIL ($res)\n"; + $fail++; + } + else + { + # print STDERR "PASS ($1)\n"; + $pass++; + } + } + else + { + # print STDERR "FAIL (unexpected result '$res')\n"; + $fail++; + } + } + + +sub agi_output + { + if ($AGILOG >=2) + { + ### open the log file for writing ### + open(Lout, ">>$AGILOGfile") + || die "Can't open $AGILOGfile: $!\n"; + print Lout "$now_date|$script|$agi_string\n"; + close(Lout); + } + ### send to STDERR writing ### + if ( ($AGILOG == '1') || ($AGILOG == '3') ) + {print STDERR "$now_date|$script|$agi_string\n";} + $agi_string=''; + } + +# subroutine to parse the asterisk version +# and return a hash with the various part +sub parse_asterisk_version + { + # grab the arguments + my $ast_ver_str = $_[0]; + + # get everything after the - and put it in $ast_ver_postfix + my @hyphen_parts = split( /-/ , $ast_ver_str ); + + my $ast_ver_postfix = $hyphen_parts[1]; + + # now split everything before the - up by the . + my @dot_parts = split( /\./ , $hyphen_parts[0] ); + + my %ast_ver_hash; + + if ( $dot_parts[0] <= 1 ) + { + %ast_ver_hash = ( + "major" => $dot_parts[0], + "minor" => $dot_parts[1], + "build" => $dot_parts[2], + "revision" => $dot_parts[3], + "postfix" => $ast_ver_postfix + ); + } + + # digium dropped the 1 from asterisk 10 but we still need it + if ( $dot_parts[0] > 1 ) + { + %ast_ver_hash = ( + "major" => 1, + "minor" => $dot_parts[0], + "build" => $dot_parts[1], + "revision" => $dot_parts[2], + "postfix" => $ast_ver_postfix + ); + } + + return ( %ast_ver_hash ); + } diff --git a/agc_2-X/trunk/bin/ADMIN_keepalive_ALL.pl b/agc_2-X/trunk/bin/ADMIN_keepalive_ALL.pl index 18822f95..a33880bc 100644 --- a/agc_2-X/trunk/bin/ADMIN_keepalive_ALL.pl +++ b/agc_2-X/trunk/bin/ADMIN_keepalive_ALL.pl @@ -148,9 +148,10 @@ # 210311-2229 - Added purging of old records from vicidial_two_factor_auth # 210325-2145 - Fix for -adfill-delay= CLI flag, Issue #1266 # 210429-1644 - Added mohsuggest config for SIP and IAX phones +# 210605-1407 - Added purging of vicidial_tiltx_shaken_log log entries # -$build = '210429-1644'; +$build = '210605-1407'; $DB=0; # Debug flag $teodDB=0; # flag to log Timeclock End of Day processes to log file @@ -1938,6 +1939,24 @@ if ($timeclock_end_of_day_NOW > 0) ##### END vicidial_shared_log end of day process removing records older than 7 days ##### + ##### BEGIN vicidial_tiltx_shaken_log end of day process removing records older than 7 days ##### + $stmtA = "DELETE from vicidial_tiltx_shaken_log where db_time < \"$SDSQLdate\";"; + if($DBX){print STDERR "\n|$stmtA|\n";} + $affected_rows = $dbhA->do($stmtA); + if($DB){print STDERR "\n|$affected_rows vicidial_tiltx_shaken_log records older than 7 days purged|\n";} + if ($teodDB) {$event_string = "vicidial_tiltx_shaken_log records older than 7 days purged: |$stmtA|$affected_rows|"; &teod_logger;} + + $stmtA = "optimize table vicidial_tiltx_shaken_log;"; + if($DBX){print STDERR "\n|$stmtA|\n";} + $sthA = $dbhA->prepare($stmtA) or die "preparing: ",$dbhA->errstr; + $sthA->execute or die "executing: $stmtA ", $dbhA->errstr; + $sthArows=$sthA->rows; + @aryA = $sthA->fetchrow_array; + if ($DB) {print "|",$aryA[0],"|",$aryA[1],"|",$aryA[2],"|",$aryA[3],"|","\n";} + $sthA->finish(); + ##### END vicidial_tiltx_shaken_log end of day process removing records older than 7 days ##### + + ##### BEGIN vicidial_two_factor_auth end of day process removing expired and older records ##### $stmtA = "DELETE from vicidial_two_factor_auth where (auth_exp_date < NOW()) or ( (auth_code_exp_date < NOW()) and (auth_stage != '1') );"; if($DBX){print STDERR "\n|$stmtA|\n";} diff --git a/agc_2-X/trunk/bin/AST_VDauto_dial.pl b/agc_2-X/trunk/bin/AST_VDauto_dial.pl index 306ec480..daf61c16 100644 --- a/agc_2-X/trunk/bin/AST_VDauto_dial.pl +++ b/agc_2-X/trunk/bin/AST_VDauto_dial.pl @@ -138,9 +138,10 @@ # 201218-2224 - Added code for SHARED agent campaigns # 210207-0952 - Added more logging, debug code and fixes for SHARED agent campaigns # 210423-2241 - Fix for campaign ID underscore issue #1265 +# 210606-1006 - Added TILTX features for pre-carrier call filtering # -$build='210423-2241'; +$build='210606-1006'; ### begin parsing run-time options ### if (length($ARGV[0])>1) { @@ -2332,8 +2333,11 @@ while($one_day_interval > 0) if ($CLstatus =~ /BUSY/) {$CLnew_status = 'B';} else { - if ($CLstatus =~ /DISCONNECT/) {$CLnew_status = 'ADC';} - else {$CLnew_status = 'NA';} + $new_status_set=0; + if ($CLstatus =~ /DISCONNECT/) {$CLnew_status = 'ADC'; $new_status_set=1;} + if ($CLstatus =~ /ADCCAR/) {$CLnew_status = 'ADCCAR'; $new_status_set=1;} + if ($CLstatus =~ /DNCCAR/) {$CLnew_status = 'DNCCAR'; $new_status_set=1;} + if ($new_status_set < 1) {$CLnew_status = 'NA';} } if ($CLstatus =~ /LIVE/) {$CLnew_status = 'DROP';} else diff --git a/agc_2-X/trunk/bin/FastAGI_log.pl b/agc_2-X/trunk/bin/FastAGI_log.pl index dd38ff03..e409b6d1 100644 --- a/agc_2-X/trunk/bin/FastAGI_log.pl +++ b/agc_2-X/trunk/bin/FastAGI_log.pl @@ -87,6 +87,7 @@ # 191001-1509 - Small fix for monitoring issue # 200318-1054 - Added code for OpenSIPs CallerIDname # 210314-1015 - Added enhanced_disconnect_logging=2 option +# 210606-1007 - Added TILTX features for pre-carrier call filtering # # defaults for PreFork @@ -1030,7 +1031,7 @@ sub process_request ### END - CPD Look for result for B/DC calls ############################################################## } - if ( ($PRI =~ /^PRI$/) && ($callerid =~ /\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d/) && ( ( ($dialstatus =~ /BUSY/) || ( ($dialstatus =~ /CHANUNAVAIL/) && ($hangup_cause =~ /^1$|^28$/) ) || ( ($enhanced_disconnect_logging > 0) && ( ( ($dialstatus =~ /CONGESTION/) && ($hangup_cause =~ /^1$|^19$|^21$|^34$|^38$|^102$/) ) || ( ($dialstatus =~ /CHANUNAVAIL/) && ($hangup_cause =~ /^18$/) ) ) ) || ($CPDfound > 0) && ($callerid !~ /^S\d\d\d\d\d\d\d\d\d\d\d\d/) ) ) ) + if ( ($PRI =~ /^PRI$/) && ($callerid =~ /\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d/) && ( ( ($dialstatus =~ /BUSY/) || ( ($dialstatus =~ /CHANUNAVAIL/) && ($hangup_cause =~ /^1$|^28$/) ) || ( ($enhanced_disconnect_logging > 0) && ( ( ($dialstatus =~ /CONGESTION/) && ($hangup_cause =~ /^1$|^19$|^21$|^34$|^38$|^102$/) ) || ( ($dialstatus =~ /CHANUNAVAIL/) && ($hangup_cause =~ /^18$/) ) || ($dialstatus =~ /DNC|DISCONNECT/) ) ) || ($CPDfound > 0) && ($callerid !~ /^S\d\d\d\d\d\d\d\d\d\d\d\d/) ) ) ) { if ($CPDfound < 1) { @@ -1043,6 +1044,8 @@ sub process_request if ($dialstatus =~ /CHANUNAVAIL/ && $hangup_cause =~/^18/) {$VDL_status = 'ADCT'; $VDAC_status = 'DISCONNECT';} if ($dialstatus =~ /CONGESTION/ && $hangup_cause =~ /^1$/) {$VDL_status = 'ADC'; $VDAC_status = 'DISCONNECT';} if (($dialstatus =~ /CONGESTION/ && $hangup_cause =~ /^21$|^34$|^38$|^102$/) || ($dialstatus =~ /BUSY/ && $hangup_cause =~ /^19$/)) {$VDL_status = 'ADCT'; $VDAC_status = 'DISCONNECT';} + if ($dialstatus =~ /DISCONNECT/) {$VDL_status = 'ADCCAR'; $VDAC_status = 'ADCCAR';} # pre-carrier disconnect filter + if ($dialstatus =~ /DNC/) {$VDL_status = 'DNCCAR'; $VDAC_status = 'DNCCAR';} # pre-carrier DNC filter } } else @@ -1053,6 +1056,8 @@ sub process_request { if ($dialstatus =~ /CONGESTION/ && $hangup_cause =~ /^1$/) {$VDL_status = 'ADC'; $VDAC_status = 'DISCONNECT';} if ($dialstatus =~ /CONGESTION/ && $hangup_cause =~ /^19$|^21$|^34$|^38$/) {$VDL_status = 'ADCT'; $VDAC_status = 'DISCONNECT';} + if ($dialstatus =~ /DISCONNECT/) {$VDL_status = 'ADCCAR'; $VDAC_status = 'ADCCAR';} # pre-carrier disconnect filter + if ($dialstatus =~ /DNC/) {$VDL_status = 'DNCCAR'; $VDAC_status = 'DNCCAR';} # pre-carrier DNC filter } } } diff --git a/agc_2-X/trunk/docs/TILTX_SHAKEN_API.txt b/agc_2-X/trunk/docs/TILTX_SHAKEN_API.txt new file mode 100644 index 00000000..a7345dd4 --- /dev/null +++ b/agc_2-X/trunk/docs/TILTX_SHAKEN_API.txt @@ -0,0 +1,79 @@ +TILTX STIR/SHAKEN API (Call Shaper) INTEGRATION Updated: 2021-06-05 + +This document covers the TILTX SHAKEN (Call Shaper) API service features and how they integrate into VICIdial. + +NOTE: you will need to use the VICIdial svn/trunk revision 3449 or higher on all servers in your cluster to use this feature. + + + +What is the TILTX SHAKEN (Call Shaper) API service? + +This service requires an AGI script to be run before each call is placed through your dialer(agi-TILTX_SHAKEN.agi). It will tag the call with SIP Headers with the "X-TILTX-ID", "Identity" and "CAID" values that TILTX returns so your carrier can route the call properly. There are also optional service features that allow for a National Do-Not-Call(DNC) list check and a Disconnected-number check, as well as CallerID Number validation/replacement "NumberRisk" service. + +Reference TILTX URLs: +https://tiltx.zendesk.com/hc/en-us/articles/360060683571-TILTX-Call-Shaper-Module +https://tiltx.zendesk.com/hc/en-us/articles/4402421951885 + + + +How to set up TILTX Call Shaper on your VICIdial system: + +REQUIREMENT 1: To use this feature, you will have to get a valid API Key from TILTX and create a new VICIdial Settings Container entry with the Container ID of "TILTX_SHAKEN_API_KEY", and place only the TILTX API key into the Container Entry. + +REQUIREMENT 2: You will need to have the VICIdial svn/trunk revision 3449 or higher installed on all servers in your VICIdial cluster. + +REQUIREMENT 3: You will need to put a similar dialplan entry into your Carrier Dialplan BEFORE the line where the call is placed out through your carrier: + exten => _91NXXNXXXXXX,n,AGI(/var/lib/asterisk/agi-bin/agi-TILTX_SHAKEN.agi,${EXTEN:-10}-----${CALLERID(num)}-----YES-----) + + NOTE: Here are the configurable CLI flags for the above AGI script: + 1. Phone number being called (USE DIALPLAN VARIABLE like '${EXTEN:-10}') + 2. CallerID number being sent with call (USE DIALPLAN VARIABLE like '${CALLERID(num)}') + 3. (YES/NO) whether to speak error messages or not, default 'NO' + 4. Settings Container ID(override) to use for TILTX API settings, default 'TILTX_SHAKEN_API_KEY' if empty + + +New VICIdial statuses used: (if you are these subscribed to TILTX services) +ADCCAR - Disconnect Carrier-Defined +DNCCAR - DNC Carrier-Defined + + + + +Advanced Options: + +You can override the hard-coded TILTX Call Shaper API "https://api.shaper.tiltx.com/Calls/shaper" by adding a second line to the Settings Container that looks like the following: + +URL=>https://api.shaper.tiltx.com/Calls/shaper + + + +Advanced Debug: + +To see that the SIP Headers are being set properly, add the following line to your dialplan(extensions.conf after the 8368 "Playback" line), then reload your Asterisk dialplan and place a test call through the campaign detail screen: + +exten => 8368,n,Verbose("X-CIDNAME:${SIP_HEADER(X-CIDNAME)}, X-TILTX-ID:${SIP_HEADER(X-TILTX-ID)}, Identity:${SIP_HEADER(Identity)}, CAID:${SIP_HEADER(CAID)}") + + + + + + +-------------- FOR DOCUMENTATION PURPOSES ONLY, DO NOT EXECUTE!!! -------------- + +Database changes: + +CREATE TABLE vicidial_tiltx_shaken_log ( +db_time DATETIME NOT NULL, +server_ip VARCHAR(15) NOT NULL, +url_log_id INT(9) UNSIGNED NOT NULL, +caller_code VARCHAR(20), +phone_number VARCHAR(19) default '', +CIDnumber VARCHAR(19) default '', +CallerIDToUse VARCHAR(19) default '', +IsDNC TINYINT(1) default '0', +IsDisconnected TINYINT(1) default '0', +TILTXID VARCHAR(50), +Identity TEXT, +CAID VARCHAR(50), +index (db_time) +) ENGINE=MyISAM; diff --git a/agc_2-X/trunk/docs/VICIDIAL_statuses.txt b/agc_2-X/trunk/docs/VICIDIAL_statuses.txt index 87d83a3c..0611f0b1 100644 --- a/agc_2-X/trunk/docs/VICIDIAL_statuses.txt +++ b/agc_2-X/trunk/docs/VICIDIAL_statuses.txt @@ -17,10 +17,12 @@ AB - Busy Auto - Carrier-received Busy signal DC - Disconnected Number - Agent-Defined Disconnected number ADC - Disconnected Number Auto - Carrier-received Disconnected number ADCT - Congested Number Auto - Carrier-received Congestion(19,21,34,38) +ADCCAR - Disconnect Carrier-Defined, used by TILTX service DEC - Declined Sale - Agent-defined status DNC - DO NOT CALL - If defined, lead will also go in the VICIDIAL DNC list DNCL - DO NOT CALL Hopper Match - status of a lead that matches the phone number of a lead that has been placed in the VICIDIAL DNC list DNCC - DO NOT CALL Hopper Match Campaign- status of a lead that matches the phone number of a lead that has been placed in the VICIDIAL Campaign-specific DNC list for this Campaign +DNCCAR - DO NOT CALL Carrier-Defined, used by TILTX service SALE - Sale Made - Agent-defined status N - No Answer - Agent-defined status NI - Not Interested - Agent-defined status diff --git a/agc_2-X/trunk/extras/MySQL_AST_CREATE_tables.sql b/agc_2-X/trunk/extras/MySQL_AST_CREATE_tables.sql index a6156fe9..a4bc0ab0 100644 --- a/agc_2-X/trunk/extras/MySQL_AST_CREATE_tables.sql +++ b/agc_2-X/trunk/extras/MySQL_AST_CREATE_tables.sql @@ -4590,6 +4590,22 @@ KEY `channel` (`channel`) ) ENGINE=MyISAM AUTO_INCREMENT=630320 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +CREATE TABLE vicidial_tiltx_shaken_log ( +db_time DATETIME NOT NULL, +server_ip VARCHAR(15) NOT NULL, +url_log_id INT(9) UNSIGNED NOT NULL, +caller_code VARCHAR(20), +phone_number VARCHAR(19) default '', +CIDnumber VARCHAR(19) default '', +CallerIDToUse VARCHAR(19) default '', +IsDNC TINYINT(1) default '0', +IsDisconnected TINYINT(1) default '0', +TILTXID VARCHAR(50), +Identity TEXT, +CAID VARCHAR(50), +index (db_time) +) ENGINE=MyISAM; + ALTER TABLE vicidial_email_list MODIFY message text character set utf8; @@ -4931,4 +4947,4 @@ INSERT INTO vicidial_settings_containers(container_id,container_notes,container_ UPDATE system_settings set vdc_agent_api_active='1'; -UPDATE system_settings SET db_schema_version='1633',db_schema_update_date=NOW(),reload_timestamp=NOW(); +UPDATE system_settings SET db_schema_version='1634',db_schema_update_date=NOW(),reload_timestamp=NOW(); diff --git a/agc_2-X/trunk/extras/upgrade_2.14.sql b/agc_2-X/trunk/extras/upgrade_2.14.sql index 4401817b..62364e01 100644 --- a/agc_2-X/trunk/extras/upgrade_2.14.sql +++ b/agc_2-X/trunk/extras/upgrade_2.14.sql @@ -1683,3 +1683,21 @@ UPDATE system_settings SET db_schema_version='1632',db_schema_update_date=NOW() ALTER TABLE phones ADD mohsuggest VARCHAR(100) default ''; UPDATE system_settings SET db_schema_version='1633',db_schema_update_date=NOW() where db_schema_version < 1633; + +CREATE TABLE vicidial_tiltx_shaken_log ( +db_time DATETIME NOT NULL, +server_ip VARCHAR(15) NOT NULL, +url_log_id INT(9) UNSIGNED NOT NULL, +caller_code VARCHAR(20), +phone_number VARCHAR(19) default '', +CIDnumber VARCHAR(19) default '', +CallerIDToUse VARCHAR(19) default '', +IsDNC TINYINT(1) default '0', +IsDisconnected TINYINT(1) default '0', +TILTXID VARCHAR(50), +Identity TEXT, +CAID VARCHAR(50), +index (db_time) +) ENGINE=MyISAM; + +UPDATE system_settings SET db_schema_version='1634',db_schema_update_date=NOW() where db_schema_version < 1634; diff --git a/agc_2-X/trunk/sounds/do-not-call.wav b/agc_2-X/trunk/sounds/do-not-call.wav new file mode 100644 index 00000000..240bfd91 Binary files /dev/null and b/agc_2-X/trunk/sounds/do-not-call.wav differ diff --git a/agc_2-X/trunk/sounds/key.wav b/agc_2-X/trunk/sounds/key.wav new file mode 100644 index 00000000..16f3a892 Binary files /dev/null and b/agc_2-X/trunk/sounds/key.wav differ diff --git a/agc_2-X/trunk/sounds/was-not-found.wav b/agc_2-X/trunk/sounds/was-not-found.wav new file mode 100644 index 00000000..74b41281 Binary files /dev/null and b/agc_2-X/trunk/sounds/was-not-found.wav differ diff --git a/agc_2-X/trunk/sounds/wget.wav b/agc_2-X/trunk/sounds/wget.wav new file mode 100644 index 00000000..98de23ae Binary files /dev/null and b/agc_2-X/trunk/sounds/wget.wav differ diff --git a/agc_2-X/trunk/www/agc/vdc_db_query.php b/agc_2-X/trunk/www/agc/vdc_db_query.php index 03c8e7a6..09db3e49 100644 --- a/agc_2-X/trunk/www/agc/vdc_db_query.php +++ b/agc_2-X/trunk/www/agc/vdc_db_query.php @@ -506,10 +506,11 @@ # 210330-2255 - Fixed issues with dashes as custom fields values throwing off delimiters # 210417-1109 - Added calls_waiting_vl_ options # 210421-2111 - Added more screen labels +# 210606-0957 - Added TILTX features for pre-carrier call filtering # -$version = '2.14-399'; -$build = '210421-2111'; +$version = '2.14-400'; +$build = '210606-0957'; $php_script = 'vdc_db_query.php'; $mel=1; # Mysql Error Log enabled = 1 $mysql_log_count=850; @@ -7102,7 +7103,7 @@ if ($ACTION == 'manDiaLlookCaLL') $end_epoch = $row[2]; ### Check carrier log for error - $stmt="SELECT dialstatus,hangup_cause,sip_hangup_cause,sip_hangup_reason FROM vicidial_carrier_log where uniqueid='$uniqueid' and server_ip='$server_ip' and channel='$channel' and dialstatus IN('BUSY','CHANUNAVAIL','CONGESTION') LIMIT 1;"; + $stmt="SELECT dialstatus,hangup_cause,sip_hangup_cause,sip_hangup_reason FROM vicidial_carrier_log where uniqueid='$uniqueid' and server_ip='$server_ip' and channel='$channel' and dialstatus IN('BUSY','CHANUNAVAIL','CONGESTION','DNC','DISCONNECT') LIMIT 1;"; $rslt=mysql_to_mysqli($stmt, $link); if ($mel > 0) {mysql_error_logging($NOW_TIME,$link,$mel,$stmt,'00292',$user,$server_ip,$session_name,$one_mysql_log);} if ($DB) {echo "$stmt\n";} diff --git a/agc_2-X/trunk/www/agc/vicidial.php b/agc_2-X/trunk/www/agc/vicidial.php index 35de1bd2..7c0719e1 100644 --- a/agc_2-X/trunk/www/agc/vicidial.php +++ b/agc_2-X/trunk/www/agc/vicidial.php @@ -666,10 +666,11 @@ # 210330-2254 - Fixed issues with dashes as custom fields values throwing off delimiters # 210417-1109 - Added calls_waiting_vl_ campaign options # 210421-2110 - Added more screen labels +# 210606-0955 - Cleanup of debugbottomspan, testing of TILTX features # -$version = '2.14-634c'; -$build = '210421-2110'; +$version = '2.14-635c'; +$build = '210606-0955'; $mel=1; # Mysql Error Log enabled = 1 $mysql_log_count=95; $one_mysql_log=0; @@ -9930,7 +9931,7 @@ function set_length(SLnumber,SLlength_goal,SLdirection) } } agent_events('agent_alert', "SIP Action: " + MDactionOption + ' ' + MDactionDispo, aec); aec++; - document.getElementById("debugbottomspan").innerHTML = "
|" + manDiaLlook_query + "|
\n" + debug_response + "
\n" + MDactionOption; + // document.getElementById("debugbottomspan").innerHTML = "
|" + manDiaLlook_query + "|
\n" + debug_response + "
\n" + MDactionOption; } if (MDalert == 'CALL UNANSWERED') { @@ -18625,7 +18626,7 @@ function phone_number_format(formatphone) { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { // alert(xmlhttp.responseText); - document.getElementById("debugbottomspan").innerHTML = "CUSTOMER 3WAY HANGUP " + xmlhttp.responseText; + // document.getElementById("debugbottomspan").innerHTML = "CUSTOMER 3WAY HANGUP " + xmlhttp.responseText; } } delete xmlhttp;