From 6228b167a037d4bff5c5ff70a3eb234130332a90 Mon Sep 17 00:00:00 2001 From: mattf Date: Sun, 6 Jun 2021 14:08:45 +0000 Subject: [PATCH] Added TILTX features for pre-carrier call filtering git-svn-id: svn://192.168.202.10@3449 3d104415-ff17-0410-8863-d5cf3c621b8a --- UPGRADE | 3 + agi/agi-TILTX_SHAKEN.agi | 646 +++++++++++++++++++++++++++++ bin/ADMIN_keepalive_ALL.pl | 21 +- bin/AST_VDauto_dial.pl | 10 +- bin/FastAGI_log.pl | 7 +- docs/TILTX_SHAKEN_API.txt | 79 ++++ docs/VICIDIAL_statuses.txt | 2 + extras/MySQL_AST_CREATE_tables.sql | 18 +- extras/upgrade_2.14.sql | 18 + sounds/do-not-call.wav | Bin 0 -> 24170 bytes sounds/key.wav | Bin 0 -> 15674 bytes sounds/was-not-found.wav | Bin 0 -> 28594 bytes sounds/wget.wav | Bin 0 -> 13882 bytes www/agc/vdc_db_query.php | 7 +- www/agc/vicidial.php | 9 +- 15 files changed, 807 insertions(+), 13 deletions(-) create mode 100644 agi/agi-TILTX_SHAKEN.agi create mode 100644 docs/TILTX_SHAKEN_API.txt create mode 100644 sounds/do-not-call.wav create mode 100644 sounds/key.wav create mode 100644 sounds/was-not-found.wav create mode 100644 sounds/wget.wav diff --git a/UPGRADE b/UPGRADE index 8ed5562d..d9fe55c8 100644 --- a/UPGRADE +++ b/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/agi/agi-TILTX_SHAKEN.agi b/agi/agi-TILTX_SHAKEN.agi new file mode 100644 index 00000000..6d98bb0c --- /dev/null +++ b/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/bin/ADMIN_keepalive_ALL.pl b/bin/ADMIN_keepalive_ALL.pl index 18822f95..a33880bc 100644 --- a/bin/ADMIN_keepalive_ALL.pl +++ b/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/bin/AST_VDauto_dial.pl b/bin/AST_VDauto_dial.pl index 306ec480..daf61c16 100644 --- a/bin/AST_VDauto_dial.pl +++ b/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/bin/FastAGI_log.pl b/bin/FastAGI_log.pl index dd38ff03..e409b6d1 100644 --- a/bin/FastAGI_log.pl +++ b/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/docs/TILTX_SHAKEN_API.txt b/docs/TILTX_SHAKEN_API.txt new file mode 100644 index 00000000..a7345dd4 --- /dev/null +++ b/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/docs/VICIDIAL_statuses.txt b/docs/VICIDIAL_statuses.txt index 87d83a3c..0611f0b1 100644 --- a/docs/VICIDIAL_statuses.txt +++ b/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/extras/MySQL_AST_CREATE_tables.sql b/extras/MySQL_AST_CREATE_tables.sql index a6156fe9..a4bc0ab0 100644 --- a/extras/MySQL_AST_CREATE_tables.sql +++ b/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/extras/upgrade_2.14.sql b/extras/upgrade_2.14.sql index 4401817b..62364e01 100644 --- a/extras/upgrade_2.14.sql +++ b/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/sounds/do-not-call.wav b/sounds/do-not-call.wav new file mode 100644 index 0000000000000000000000000000000000000000..240bfd91efe6681f5eb4f8a7875fa0ebf8a21100 GIT binary patch literal 24170 zcmeHv1(Xz5x9%y|*6C3kf)m`GAPG(gkl^kbAh-pG;DHbV1d?FEf=iGDmmmRx%`nn4 zJuW>n?cG&%-Z@i$FLLky=fCT|x9)mxt(U4*()*-tXI%bc&;QPm&f8ye_^+@0_1S;N@n7@xS2pmksD;mk zfB%~5+bjQ_wr_j=*L(lbQvU1qf4BG>0UZI|a96Gfug87c;w=Yn&;8%u`)jVknCPhdv;O`kcn^>AuXOak zJ067k^8g7#kOcuq0SPRE4d60(3Fbiqs0iAEPH1}%bO-N))}RHb1FC{@zycUFqypWr z5W1ijs!)R=v?(wM8T7_t1OkWvrBOQpSiw7}hZRJFIAB2Ikbninuo#I$0S`=vc9;y~ z;R~1m-@xbaHB5sJv}K|3^Pmq=)KE(j(c#g%5Qk{9Xj~4_vm#y@bQZ!KL?Z{acAzx_ zrj#7HO8RoBHG_JwsGV5So<_VyAihi>3Y0=TMV1`NfG%k#qhlMoiuvadWfG0)g8{?? z_Hh+)j28~cAtQHi(wI>hxbBg9E>?`rT2#j7Z%ax&^Q3m zmPk-m=4w;<{xu{ISZ8Z(a^6S z_4lIRAX+^LQ*2%M*o#nbqkSE9j4+AlL-ec57yO5mN(nUrp`mYB3oP1KO2)z%;RSQ9 zB5L@of^^6s{sgqL=o(eRH;H)V5vRB%mV+um3ZrX6?J#!O9*iXjVVr0*BK8H`9_yS$ zZSc5=t&)+j?tDlFEFG3Nh+5%3SW8%gc#lK-G#ZB~k;{kV_oJ4+68W*EU_`LrVY|fb zu~a^EjHSY(`_UL!o*?R_p#30fi8;U=u_a^RV=NE0ORNp7Cv52ik`r4xrh=^okHD13 ziP6K_g(y~FE8$8|!8WWfr-@SJQ1VOiKAA&$NH1w8&yaJ-)}%nbAx;s?h|$C_Vj$6* zXiro@CUOrfLb2oloC9mX0__)Vh1OqdtyRD@XjQny!%QS+H2yrraTa(<&)zKtLmv%FshR z1*M2%WF^u}JOOcp8MM@Hs2{5J)n!U$B}u)b?9!UCv-twyp>elqh^ehPdx1#0wvH9%^$`w4% zf3Gmsz0&Is)C2YD1XhwenkJg(T7Iy6XnkOo3li+PA52fOA=XrOa08H8UgA z7dh@{UC!-Vu-r8+*opX(c`44e4v!ob^=_$crR_1#V=`l3+xnOl(-7Ii?d3Fj4++T> zwWVq&js_oi8ipErCg-j!Xq-PjEi=uV*&!`G)t5ayk1JT?ZmeXGH8{VVY1=CkU6MeRNgw*Z`x?W!ws{3ZL1tzwsc-x*Vvh{l_Q&4N|`T7-MKAH8)^-~5RJ)3 zM58$C3bQVyCZvqYKAKmx;B)V4bsDu;ZeaPsW{-QP z)V-L;aa&_rTW^{l8t?NIn@gnvfv^!V#B=xu3^K*^J#{sBNKEn$C~V`}ocAi#<(Qb! z*#0OrGJ8?(l0u_@oB9JAZ|q?y6MdxghPVf@=2Fk2Y}V@LUb4uSVeTM5$_HPAL!d8Q zPF`hyB1UNy$sy{b;#Ng!;ke8dseRKgrS-ADoBk-fRY7~tvfxj|r-p1x*@&1@Wy;3H zMa5l-J!zY2J}*}herE3zSGDe7C%6c!Y1_aVb^u)qRwI2xnlGk!XK`^JpMEiobvRS! zrFqi#>!z5<*rlL*`F zS(iE~eRfV-0plM`Ze&}?b!~T|e~n!k=Zl#iV~(n3cFXOhB6cOSiy?@1WPPDLcZ8fo zH74pRO?)ezU2>AM?xvMVUYhc5s_J+z=d1k5-r?j7w!btYVr}$|xYu#P=Yx2y)K(aD`tnAH2}(b>(My@OAP-fV3H zYik?Zq14{k#MpnH7DTNqo3*SPWQZcv9TB@ALmkh95DI8oW;tM2?h=V11E$MzJf zy?yG2bS7tZK~vu~c$cmxow00)st`9SE-!X#Y}=?p>sixNkrEGb$3P}ELm0tKByEIM zqZR0>lRqn`dG=7pFnbMq!?bysVs5>{D}EkqVHQgLEC-{$jZKOzjy)OkFmivyIpcBh zQ)xQ0l(@uGyoX#z#=vgMs6e2wQm!%An0Yw$Y3kb4z8UE`@8|dQv{h|nZ~m2mj;J1e zGx~>^nAq)+y(1L!Cdnps=T8C~yH%_}Eg_x62(_8g&~q^VxBTU~Ptx}}My7L_CvzU; z4Jy7JG{8gD8eyri*t*)bEmA^uLs(l|jvDR==ftB-M^auA#x>=}x6?JDS5 zv@!3)?C)~8oc?(!&itYb?>?nJxK1tSU&}_*Ny`*#C+lF#75P26moSGq$DAX+P%qMu zny0M>6!B2)p}Li{K=VMmV1jRnr=fR%x36cQcbk7=;7TxFIisG_QeZi-2Q(lS6R(JL zq7QkRYQqbkTECIoc&!c z9^Rx~uR@TFB$mxv2trV*pmYBY<};eLwD} zxN32k(U}pwjVHOWfD1G&=JTFr)JVIV@=J366e;y*$KqJAq`H8v9Kw6PFSjh+b&xV=@ZuNHP@Rj?Z76wZ)NT-<$F%rDtlPqhVIX zyhnwDJSzgL)M~^+dKD*$pUQ&iu!XjrkL(hCASNfKV(h5cP;|hy$$VWbOJ^vg_p!5H zj*!tjZGnB5eOGF{qbT#I+;*E^A%evPdE-C-QmZ zX~fM!BPn*JCu!D;Y;8bN+1#+gP&tc%(ZmmOn?m|)GbG%~+1>@vSJB{3HT#ju<`5VGdgNWYet z@nqJMf|sgeNYM??c(pElmAVcpD>3SMG;`91ekI+uPK}C-JRV6!eq)N4opJ|Lb8{v0 zIm>uqJkwGd!Cg>m7hFnBP3rmLgFmXj{={+I{Z8nbIsrWo2H~}!H&78|Q+ezxsfy)I z8GS7_TRDx_}Xh5U|aQta#P!)ex$V}hA}%(r{^u%$ly6 zeltHaCK$gJa@11DZ|)$qgA#UCQx50*{}-LgjifSIkjrioum&PPRIK>y*=Z# z@5whxqrmv!b>%kj5!2{2K1W_^ddHX|*E4)zm?uvV^JC{l~EmFwkC(TU~A1!O=PEW zWrT|IQX_4wEJsR@L@0X2;Zjq>WMj7BfYe6p!oCl$XG1o9#97McP z*MXZ9K}5PrrGJ~)BDr?PitH)*TV3}Zo~vNkZ<=eoi{uGOK|`K#ovEk!6SH9MWLQOygkO=5*v_=U_ig5hq%%pc z)1GCH%&lDXW#GDYQGFTs%3ChbQ~R9QN7kTPqZ?BN^8nK{d4V)a9%XbG?;2~GFPP_A z>za1aLHLTC#HCQ(J$*9jByCTbp8g;+n3Gx9F1S@Yp=9`y-TMQpp_7&98~V0pdarm?K?pmCbHwsl*?A=yfKh)kvpzXx`4HcSJ_ds0_q%*v{kZ}!|!4yx?} ztY?$wa0q~D#0laz>V=SIm|^tD5%Mm>@5cMa{>DS5HI`Qqmn^L~6Zx86C=BA#eRB5L z6nFAS$JF$g9IdEyXeT@$jQ99l+kCs#W$+Q$NLsm$avehgdKP-uaNX!K{Ay@t>TmhX z*2p$jdXIj_OcLh_p3tFOzkN_j8OI5SF>|`JPcQ^7D`Py?;wPT3l*!s|?I$>oF&IV~ z>&mfWUAeTmjQKO;Kw~NMAzS^ZcjZO&POhuGLu?RwlT#_pW7izV(uQZOC`<_U0uzEi zy05!hd3S~?gKcBRZy210ZkDswi4muw zQ_SmxPYkzBR}C@XjPr8Vs?_Ewb&}89^9r5>9%!Z2r~c0Z*S&|_#q_%b&POsbY+D4QBSF<XDyBOL4<3BaJO$+StzUL$rRLh(bDb&x^QM^AfhDQrQ{Kb4oL68NHY( zq}#JE$%)ENWuC^tJIIep392E%G{HJ9a$fYFSby{)i`QIinIVlJEBO-(`{%ZFtMqDSB5vK_b zj$+%>N6GWVSTIDprA!a~5jf`0@|E@t_O14{@;&v5{x<$6{)T}v!HuB->In^iXGDL> zz)WPTbI14}h0($nLQSD9|B8En#!~4$R3EY(@diHCnrSoCX-f0ZjzD#PZQnSr<{9aU z@I3c;ynX!71LH#j)w%FC@r)YF6ti}21b>nr%Xi=%+&i4WZDtQJ3hkgCk?lzrv6o0g zZ`HqRd(;mUd+=7k>3`4v(6`37*QffX`XkZ1#F3Cu-KPm?&aNz(MxLd1&@bo*bObYw zdB&7t|6r~%F)YiDX8O|&sVrg^=nm^>J=JZ>lhBgT&`^s|Y)B2Z57|Rql+B7+9iX;V zyQq8B;o3;p3RENth+oJ(R6dG(3lZI#Ol1brKhv+Nm*fp%KX5=6cGEoSE%m56S#7UY zQ)M+@$yX|(zo|-Hb+CF*t)U&$K8Kaj+{$gl;ZD*;_NE3?6{y}+9Q6bF9yytK1va7Q zkusnFOtJYNasGYQ+=7&EbzHSiB$<^dWaxpoH z)QDX~C1Nue3~GR~pcmMM<_{q#i{=f!1zo`?I3J!=uPQx38(1-93nqv9z!-I^c7@(d z3dE<{=gN8N0R51tL{0>6U@O8xHU*0Igg8!-sEEORu^uAD^V_{AsRae)Fj3c3(zgN2&97)z!8~fM)iB*7vc=@E%7CBm>5p%0U5A2 ze620f%4*NhobMX-j@nT>r3JJqa33s-GDjTBn+!m6Mhn0&&=)iSv%y|)3cN#nM*K{q z5O;`&!~wK#CE9^?Xg2wC?R^xZiqWibb8U;3rd5T_;COf)&6t-#86FzZDvOY(dt3 z0l!8$wr41R#-lbj;bWBjGJ~mL8@K{^q7g9-aWa+ooET5kAQHi+AQqg3gJB(*r~Ry* z&~9m|TB4S(m4p4@=Wql34xU5nNq7`(Phkug2{wS^h$9Q)WF*mzXh@VIMB)aR3%Z~= z{Kv?WM!>qTEUXP{!3Y=&o5NnPJM0R(p{*a%M1ME~?m{`14qzU*20X~FIuf0T_lU-X zk??^hU?tLPb&wD5!sW0B>;o6V?PwbZr=qPdY>Cir3M;`T@FO?`UHKi_(DV6f@BqE- zCm`uQCTbB*WD8Dk19U^#gR&@l@&n2P97P!1MA)8(hY-pG5vBHs!{%@#918~_Ie$S} zjPjrZn2%7}3$BA?G>;e$u7Z7F2&jhgc{p1GfE#5-Sd=;O!z4s$EBq2^U^E;6=OJF_ zBMokZ7ZKlYP%fqc7z5^jjmWAFfc0QF=nR^o{N!_#K}FR3q~UvebIhxlpP76KFRPUYC$3EEra-RqMS@R zypQ_*hQ@e-#sFwE2g+9Fp_~hYFnAZq)~O`N+7Q`hd1R#_l-nynnb9=FZE4U1v_orC zPyyv#a*@ScfmabHj}fgq@D}=ubEj@7BX6&P@TdbSqx`0T5FtPi<%Y6hD#{%aXdDBQ ztRC_q92I0Vin-)E&bZ~mL?qP>B;g&zV>W8ESrfvS<77^zByIi)uiZED39vE^_ueBTx6N!1#I9^PvC zQ;H4L7U^Tgs`UWp*|E$_RSeEn<3LT66x^b8g1gAWl!JJxv{f2&9gKim7N`ouNBG6v@HCUiDpu2Gu*=p2z@)f<7RzOR|s&;4F z8WM$$baUzn*%q!2jP{iY><_U1&pn@d+l9V^W@;w2jyOa5*zVjRxFhgD8A4117f_PF zGS!^v#6rRb+LGPLbzrT!RN18FDCuerY2in(?*apQ7JJXDiPXFa2PBF z?~+6QiH)P@p!;Pe=tud;(Qu8@Q_Y4SqdTmV7)0IS z-s9%N#L%b21bP$kFgVjqcz+ID3-%6u5_+WUg59a_>E*;Yb+b) zoTg*x)o`3jkcatY{91Y*F%VRxej{Eh-vp-x-h(aamRebFa?uv=3!)FVoACNR^TY+a z5s6g3x+-u{{fPZTS}WA0(qR+wI=|eoUy5R5$=(D_%z))#Jz@g+A@QO5slT`HqW7fl zWq=LL2(09jH9+(NcsYhHdoI8sqdk2QLqWRQE!C!+ObpGp3Dh+3w{pwlCLWn z(fiOrLlfg;d97GURD>SFS@t^7HZ(ri55}P<#~FdFqH#sf+)2Lco?nV@6{vZu3VtlC z>$|T$B`;Ga(cPgmy-?OH8Kwo|2=v_8+>mJb!*W`F#0y+q_95Mv_)fc|_Eeprp#OVM zqARnQF1()mYxbkutLSOn?coEY<|Z6eJ7&0W&3M{eY-le$;FlSiMU1tLF>MrbnQZ0? zJBeOI9)EV?SddVm4Z99bAHz8aP+e0IWFg}DdJu2UGKRo_~&ai=*GfQ z*=cBEs3d(UB^V76-`QGOPn#+jdl-641@w>F`|5eQ2o8qPuf8Z;yCH@p(7@gMSn zRLK}+S!Rha>_qPmgXB_{=>~^3H77n{Lc)hx>jKr7T;`x!&V9R}ZsAlfr@5*6tetgm zt;K-M8s9UnG<+tmndvrBll${glzbyOL@uq_f{a!N1A1(^bwt zQF}~H;nMh#VnBMwP}j86@Hv-AuBL7Y?M#E@Q`+9#_DTJdJUOy5l#dseF|lf#x4i2c zcTwOK+(Osk`XU{^kmeejnd`_MnM=e5x{>s&;V7$mk7mqI?wG#N^$o!Yk^CI;Kxm7KAbTUGFTYTeXhc|zz1<|h9wGaB>= zt@ds7q~QK`rilopfBZ!=oDhZ5VlrYuQJ zRptgh^PTrj^dD3vlM(zO>41Dg@<|r?d%hxin=Ise8v08Iz}CXXj@hY4bDITQF`w{v zXbCUupBS#lAee^vo|T3`9oyQEe%01%K3BZ9ebI)F`>2W zLBY=4hjF3G{wZGQso}q=d`QmZW{Rbx9N~yiQC!Derdu#Kh4n_k@S6OtxNdr`y?joZ ze>dY12lIP~MIps^&719A>`xE%0bQBJ!fwebF+weV3mZ+hqC4_l!%rqZ-#l0@r>?z} zV^3iM6vb-NdZv!nH!#ZA+_&G?Jy1biO3vhyB~q>=^knC=jo4018@9gK$n?^WIXWj@aZGUcr)$d&;Q+BR(AD$Cz13SK@Vl~$sLKu({6ZXmlvzw4 zX19yoB|&(`4HI`7n;9F>9f}v)FC?sUZ1Ila8X8XuQ{i)OiffW*g8ymo1JwuCFm;8} z;uY>kdOh`onJ5i63UV8M0v8ld8Xh9VPG@E$j8C%X_a-t8(+#~T)wiLzru(oz6l$dI z)SN_VluJ3x+t?fQIQF!dVzQe;!a#Ze{X)24n9JYtr=&+G%uIGWcadcb3F2v#$tZS> z_T~n@2we!ZP(LS*vg?FKLL@hXP2$JP%S=O!xm+D`5&4M_WiBwRgL`x8C2Wr0mQ`Ds zFV2(O(*=Qs?j7!3=-%%PzELU?+gL%25LNDDb}5%3It*uxSOc!i>6UQsbw+xB-yymt-;s(|X8Yp3ZT#H>T<}>aA0A_BizB7yLJIc_ zM~F}5u?9_w5e75$=+Rs&Lk)8s@lbGY=C|?V<5y(N^nFHe* z^Qj@+FG8X)j~~WWqwdTo*O142xEl%d|lzWm@W4-%#(f)UUJ9SG_HoU(9l#&*Btqu zCDnW7O>B|ZJJgAG&@&+wxZ+viZHe-Rs|Xu)o%)Sl#P&j8Q&p16%GC@H4PP4iN~ib- zTnx8^{fz4;TowxGO#vb2^qauTs!4L5Jva`{i7P~9Wq`lAzk0|DNpdUo9o>SR%J&p& zN;RcIv75xmUFDk6ettQ3ko%3j#_VU>p-Kl3s`;7MruY@InU5G|;^r9q->0Boc zeE~*yAYT(QwVFJvd{TJMF236G>P*^v_iC!2v{!sgpVU0T-N7m7ePle@gqg;T<2CLq znq3nRcefuYe@R+*|2MhM6KA~`6yG`PS)eP|K<*;6 zqBfyc)PZB$j>D%Ubx5x27rDxUW9bi{ZDB7{3N&iV;B4ZU z*4%x-vHK#Rh z!?)t6>=o)`su5%6BA7$qy828L$pb{&K)>v=uTMPL`EpywqYZwTJVHA6q3X*YCRX> znEvv}AHKxn&iBZp#`4w)#+H0z%1oR>-_Fbcvx)U|FX0_Sf5Ql2D#gKFT0SugW%4iZ zcbT8bFNxt~1Li2VlJ%1J(3go<;T*M^$C0@?;nj=siPpTcYC|c*yvQ_JT*CZHhKNbT zDDV(&Bl@yCq&J50(nb2b_E^~sZ_!Wq9>O8E47HhfMf#c9LPvpSV!%;#fp%Ln1Wyz! zNg4lQ`HR!`gRX(h=jIU+MW#`rpIJ*aBmV&N;T|}M{D6BT9h9H)i^(PGW2GS|qHFQ> zc%HpYO`{}cFn5g~$o3)ns27xzS_DiDG%2W^^81S&FUzLY@*H8GTO77CmX7iSZan=N zSsOIe&Zsufk(nTNm*)y!Q;oINN;#NH-Qr&G{kadAc{IU9aB=9nrR78^t%JH;{UP+g zy*_(-Qta!v#PQkR1QNLVX1`^qX@_)zFJ#9tCaNoVfxd04Nu6OCz6Lvv`~)(vD{+=? z!+pv*m`Z5o_blCrtIE$|yr8f8qcTOEtDNw*&TC_L##c%_mg(`1X6(i$){wcp@v$^s zOy-X;b%`>UH8+$=B%5}jv<@HVvIqJmJR7c3UEch>i>YJZ(22JkeTr6S_xX9o&&)rV zzcz0&k1_=$7gHVH2#oN}@(&EP)JSj-d`UE?ZZe4yp{DN~rf(lO(G^E&IU2*vuyH?xl;7>EF#lLBnI0ro zGxjpqwLCVrGrf^Bh5ju5_V=XPPfbn+U6ev?+g{vfqh);cHj?oo$P7kQJh2z|3KHhNOz1G5=D(dL3!_1%EW zJ*r49{JH2S?-@0b7Nl;buPwW+3#|9de!~HA3Hyw=rw$Cw4;BW>`LkU=<~PsOQV*r} z$k>>7*wrGK0?RW~#bc&wwo6enqXtEMWt=KprN0E_m72bb#ZiT03%j~E1cwmExpi`? ziLu_b4zm7c-e9=F&!TQ?Jwsyx8&H0QaK{%!W|Qg79ThUK=CyP+4}7e-$y3}-d4y%9 zt!v~b5wlG9#SUyavQV8EcS%i-?=$1>Z+wZ__(qt<=al_-C^Tre=ZfmMwR zO%6T{Jn?OF$px0|(HTVM%$xzvq3&XTN7Y9(;#SK>%hiaKh;5c)0~G49-O1bPZT~oT zY;hS^72kHH14(mR#DRtyrYdI1JkEGn%H?)bg>by;4J8Kc{`Wn73%lk%$h2nGMPI5+ zFYe}pp}XKA^G57tYG8eA9cuZ?SX)}leNTM|4+STAp{tjh^OX*rf>r28+~;C~Jki+6 z=#}kaKDURiNqnF^SL!RvLw5r$yjzNz<{!=3nsYF(c41k!;&X=D0GY`USmPmcOG}<< zzG0@=mU}?$g@#ab-*C?jkHLR4lma(VPucClUFieEO7xlkMByddiT;Wx1^cLtl#9V? z{#EY9g>CXva(~U6<&1RQ_s$76(;AaQ*>GZrSPp#=&{=5C71Q0xM96B#l%1h5!5RLqJyVJU&eG1f!llJqJ+J-c zmC4Xf_G4EI$x@oUMQ$$j6JojP^k72NqC+hMOkhUvk+J~tWF-1}p$b14y~8K*qqy}< zEVTjb*2b%+Pk9tNl2NZ0gjaK87>cR8AIiBY3k?tFwyFMcLfwDv6h}G0f<{NG{-<_Yx zHD)vE�rs8aPP35vmh9A1bY0)NEiF5l?ofCo?pg#?(a5#5U?N@jW;VlTdE%h>{x2 z_jmKH@;>&ue7gg)L;ciF@FU_Aie;*@yV#-ZK4u@ilIlUGg7$Eqx>%{B>`{)RqK;ai zBbp7IOj+p>bPxIl6-f;x=Mx)IEkiH(5>ftE5ktoUioa~2C{QId2YrLn2j+t1J zFTGX-@8k10mw>ByaHhtGayqzbNUtHm^(lD6)l0Z01y}WiP^%)8J19c!aa9YhHNiDF zILCuNT|xN*6WYU7GkR?ku6V(fLwY5VUL%BSad1D(AFc+{(5L~l;tDEU`Jwqw)xRwc58{wKOTvLXt!*G?DUT=e|y*wxrgRAC>(H^F+%Y!k;cwy9V z4UvvJuB=kf*e;Y^#PwqVbd^Kd8CywpTLhvnps}!Q#V9|MS5j*hZig#-aE3@iZLpp& zez=AUW2aXs;a|OKO}~OQ5yk{pE#Ygr^tc~xqgNu~{1N65x7B6Cs3F$Sf`8FL$x*m+ z3flli8`rOeb*odu2>Z~g*FouZcNi&L`KH^Kt`FQA^MLiDS7Tv}aV-;;QP%=Qv@lM% z>aGCU7`8}^FGg6$RIh`>TG91_X=9CGE5mjXUiB(J9^o8;aK@E|7-xX$2MZC#xrlxd z(lxGf!&qYb##rdJV;F5LC8n$Ug%{Dr)qS`^6Wdp~tr2DGOsJP$$A)fzNQXE_r`r&2 zAGQY_$G7&6uV9Svf+_0bW8cIZ_DYO0wqRUehb^Z7{q>+TxMB=v_i&XP_B32=hN9FH3dTa$0H%%O38tu5QDTq6*x_h_V;$xK#|OM(The{{&-%l9!W`;6 zVxJ2i;h3YxSUp-{o5xhKo$J2gN0?#%(6QES2#*{dU8jug0VAxp#!(7eyp99*53D;} z_lPOuNap&->Mqd-t{v3dhTCEL(S09d9Yz96ihT*^xN*eBc7*j!B75*6pTIe0KN=f* zg?b04emX0{aT-Tc3 zM@J9qS?3qahx=h0#WG^;={2etTYTTb{Ng%Ie6P~uo*tR7H|m~%`3&E8uzZ+f{fr(9 zvGs?21Zx9Z6JEpC7PdDW$uN%LV_kz`OJEQOmr=7B;gNBB?DIOZm~(6q`kfwA$5`kK<`mlz z9tm5U9$&*&iES9`NRQmuOLa;0{ur?wSr|iX`(cc9p7lEtZlm9m-r|DW>imW+3ELCK0rRgv)8OYB z9SPzeSm{qvx?bLD5L?Y#i`G3)_eA}PNspd5+J`CYmKNqoe}=#q>-^&Lm?HkGYY_7q z)*MD!=UwMmx3q9{)Afy8==O;9tXnAd0vuVfCF-7}YdGvnSSpM_c-5`ttzTn)!XAp- zV1&bVp~nhrkGMV>dnu-^ub2-#*6Vi<{V$dk*E;Ju)H%WJ!q$WJuHWPE-30TiQw_`a z)-Eu*ILhF=i2fuLzBgiy!=8(I!C1aM_ZA_Hkv=X)S&xVMxj$nN_9eX^9zPrlbvZFo zVVXMTIPPO=y6*H}tS79Uu-{-r@q$tM>qYOYbEZ?%d*PAsxNmJv_w_LMc>F)h79Jzq zTSp{pjhL74{XlPt+u`vrjkh|-TGwM)8zi}}*6QnwrqbU?5$ntk?^f&>9~dMCH!s>mIGs~ z;~(~ZJ=TRIW*D`w_vliFW4!)cp+_GaN%S~^-wpKf^>+x2VHi`4#9NLr*I~+7Bl_J# zmo9w2!x1I?2J~l)!%;d2;+z)#S-dX5cUD=LHNlLQ`Wr)KRN2Fjw{wb z)(^%-mq6FKj%3*9{*0QgXt`SYkcGWsZC{KK>`5_r{h z)2;JIeehd(=N_GUcIf<3FZ^FMbebbZ&zU&@KbZWz{0|`z*4_UQ`Tyt1jvPLHCUW(^ Ym%kDC8-c$O_#1)05%?Q{{|^!PUx~-AZ2$lO literal 0 HcmV?d00001 diff --git a/sounds/key.wav b/sounds/key.wav new file mode 100644 index 0000000000000000000000000000000000000000..16f3a8923c2b4925990486fcbc152c22459448c4 GIT binary patch literal 15674 zcmeHtca&7c)^F8`9cQM~1cscEoU;T0L85>p5y^t&q+kL?NdjI#f}$WOIfG&#qlh3% zj>7;lsl#;7^h~D{>+R~fvj*>{>)n68Ki+zFt&{3h?eN>7PM>}H_vqHGNqd9_cN)}n z{FK?1?Fb~CmPWSAqdfp{^(fRn13HjdjGtq_y7L) zSNHzh_vfqsq*QvB9!sVA&oL#}RE{TQ|Lakp-oJJG>-%NB|NY3aIe$JYk-b!d{~O=Z zag+uw93v)&gj0ZuXJWR)CBITW^jLOIDbgSHcDm8ulSewYbTmDu z980w<<4$|Z8XZUJ&|_KepU=wTsD#R&$Cu34L6TCrXhh0Hf);}&69uj*bq*R2jYI6e z8|^8%D&bKo^PlCW(){O-%20+*$$5z+^t==mdR{WB^jJoxR7c9WRKwD~QjVq9C32O{ zEkP?zI=(CIFXjLD*H7LlJOAsm5{aL@SHicXr|g`LE0y=JP`z^Y?oH6@{|C zva`RUOzD=&U3&cMaev=iHm`)&pE;Iz&EL;1xl@Wp*|`7NQ+n^OY)W}P*5^YbHlSlmeTCAXF+(Yk_$(ZMil9^| zKRTC(48_E0&|VE19ZRJu;aWnKiE}HFiHYYlI#Yl~=W{TcKCP6DeG-Qf*{MV&{OE|1 ztSQIkp*%aH-lz|1hU_Q}whVHU+$9f4Cb>?EA*1F)cFl+EDxxS!L?S98NyvmOz&VHf zL9URi%GcvOk$jD8n~lFzvl;#0`UHbMim*4hnei9ASoLHR@dT;Hb4`U}i^>_KJ+ zdyPwCr{I%#5jsvbkZ(v6vRnIHGi#Ssr@lr%t~*dyWJ4z00=K1hbZb53Hvoq+d|Z=r3WL=4I}XIV6wSL+aptdWhLiR;xbTUYVgS)^E#g z^r@trc2Dg}=IPUMYi0+&#lFMl@mtxWxCI`FQrM1U6se5Y>ea~%?RRaJwoCn5Ypl)D zyAxg4NOg7`bB;N|EkZ7aXZw>+v4%6WkF+KF$LdJ+XZ5nWiGVh`h6`ZnBK zUqS=c%!^+wd<6O@zLJETjr% z?q{|ceoU&8h%#LHQQjPrqQ6D|2!9ayK6*WJFVZ&>w!ms%SAs%ZcD`^#%YGl z*2_tk?6qA*&f3nGoxj-*+u_~Ra2(mPU3vaUOSiaMcOCjYNx-FCMBPCg_Dk$>zi(K+aYtT zlxjwYghmH8=J)nY%A1pSC2xUyfp@vzQ7|Wjr72ntx5c9gD|D&#O(CxrW}>2li)E z8m4tgZ=XIRbxF!#SIFu!k2Y3k?=k&#QQ8&l6D-I-;2W7M<+O2som1Ca$-l#I4$q2K zQS;bj@k8qz`@dX^(jKP$RPM7>oN~Z*%zD^z!YH#0_pN?G9u&PA9N-_}-H_KUyHDQE z-16?)f%E>`#oeXZN;|H-VTR?Jqq%EcYC^g#t!b)~T*YzLl3*EX;2s^-9(Y-eJ=iI0K4f6K|dq=0lE}*I0K*NZ{leSb>D8-*@O0Ae; zOfe6JYf{4Fr2ZB!|EX~-%2!vYYlB-0JNWD6itbCf7qUlqj{3U#SBB0- zuP6<;6Z|T3KU+6j`Q!sBO!D;P2`Q;ICZW3Rq}a_65zndpN!wVkU_Iy#HYTdpSxIK`jB&BBTI-Iu==;Hi zL6@hV`@VZ!_E7f@?=-KaxKVhP^aZn`DH{_L<{g;%w6wiL1p2 z;w;0r`a)!oTNaNfUXagw=Xyt+&rl zddKm=_@?DUYdh```0ysZ6V8q_2nCCheU*J}yl>^|o;2UnzB5JNhjzwPbPl%{Wy@^K zM%xBQE!$=1ueOtk7RLbN2s4wglI>&qkw33>WNt?XhF%Z7=iBAueJgV-c{}<%Ua{z@ z(CX;xWGRyPndYC(my>?A%eF5ZH|?J#UQSw;Fivb^scF2;663G<19AYhjC72M#ozfB z`k(dg&Xav7Jj9n#zXuyiskgadwqf1huR+v=_Eb$I&}%nMcur>kT2SGYsQ*UThop1q@Et?PGtUB_#- z7S_Ru%?;y)%cgeBSNvc;Q~e%YlNy8ugopbd`HFqEyf&T!&u~wrf-XT9xaqL&l))E%g2D9+LOi zQ{VGOe$8Ot;(X~7eE{>Ev6jV`$U0j(H@UjGx;YZ;Z4xJ$Q_RVR*~}b4W6vQ$SWjB& z`}OnTc42?8uWxbwo1PoF$Gqh|2Yu$ErbS;$V@W++#dOG$n^4}h-;v|YaILY8v+Yh8 zW14Cd4TspJ%u4a8aEjILPcRI3oAJt)S)b-k z%zNI~sOWa+sG7-)7TP83Pg>_#meMrYp7L&Tw*6iE(!>PI81rJo62T@c;m0yxGp)Hn z=rv|1nI*p`+lu=Y+{^zuH!tUh?1othx#Qhky&DTZ3ip#cF$)A_sboLqn39s5b~U9{ z>JC?T`{1ONmSx6C;uvl_dmaCRlNg@tVBFYE49o&GRbCRQRJg)_z`Y_TH`|ykWw&xW zdJKSg+b&bG@J9Ogox#DER|tHQUGp&OFF)o8QmwX4c{e5}BL0H9RA! zcnkRi^-=U_`|!@dOZofVJ#%X1?#sEDTi$ol|5nkxSQo7tmteYOt>NgA+%LtGdMjnK zE7fUFida%jjfIumR%R_aK^BpQq&^yh7$W1(P_{l4otDqXx9l@<%PEk6XE>gB{o(4H z+QId;W3BCu`LJ;g{|Ee!;Ix<3E!qd_+vFHLTQ%Yf*`w@au?oA1S;Y)j@}-}`4GOje zx_Q^+edqbaUF`K2><*5IuF|fd76!)pRARRCZC9@Io#glJ`HA4yAr>)4|48z+MKiO~ri;Li%N_Y9YP-$4`N*$Yi^WK*B-MS zf{2w8CR-(AGky?jCkvEsApUn4H;guW+pXuCb*8cqE6LzE1{;jwz!vR zO0FpvVqZl^N3KOC$L>lKGzL#*7l^sWpG~h=_n0I@FVhzw{RaGuyfT(iG|F$wADDM9 z_jUJZ_nY~XgO@{nlyuybpKY$5*wY?!U2{!JKIXb(+mU$OG=V?DwAHG~!(ut1I^n8e zXSjaMB_C6JVjs(hZJ_e7@JirS?+cy_-a5W%fyG6q z!l}x3GMQa!bXigo1;-%!Oh?#0EV0mr-`!+v)C*bjaQ9Z#EIMy_GkT&d^#ElRw>w+pXpib?dsc}zqjy0@z!WhErOa0 z$UN5izAa#{;5g#&B)yjKgZa9!l}$x^l-K17v2Vf;Bj>`CBck-0Bxnc8Mtp^DCpwHn z&EJ?*Q-}Dvs^d8scY#S4{1! zw~~(9lI=g(QWNi4`WRp2%QJ$WsLYWXME8O|^CGQdo22f_T>TwXo4w7yA!>$6M$zOq z1&pf=jfG>}DXuE~PoF`$C`V%Ligy*<%>UAp;l1jsALvp%Ec&a`7-w+H4dX1^tcw!| zCcb5DZ7~~~@>Aen-*SB&{O7Zgbkq&M!2HBaVj445@Q?6bV;X6OvPdKJD!u}Ftsggv zw+J=G)#50jF8@5vRENhF6>SV0^zHUk_cqB72l^GO(XMI)&Eh@>ui?$d&B$D6tYe7q zv$zm5mD$4dVzaoG`~cywxL(K-X7c}HLa2x4QWi+3qHn~$l(s8F_2xK(J9O)zc;@&&wxcvh;=AM(se3qrPlselwph)E62H zp9%(UEL#IzmX}5|gGc-^-yW|yzkb1iqHtuOQWM@J9Ao!$*M)#kSCqxdLUUmg&vMgv z8~?1ZA0(J$+HTr!cx0?AyvN?apQ_80s**YSQ*1}|Xw;utnS^UKXba z=lE^>YUU68jowsV8`XoT@F#z#{LDc2f{%)Bg+GbbRDOad$D8OEtTQrqk=x537q0W` zMO|!fIAYpkdf7bRGQ^y1>TH@N&f|T|c|D*$CwGeZWBp>e(i`epZ6E5vJYcSIYq>_@(utykf_+7j#)EB?!yv%+)S4&qeMk^I>3eE|b{I3PZ1iA;Wg^otn$^*0&`s;Wi zGlIRrKj1fr$XICDZ2HjTHtjbLHs_mnnJbzbo4n#$z8iB{ucgkEI>y}5da0zF1iWoY9PYvZ}GgDDR{aJOpyi590Ql$*} zx*E|skX`67!pvUA#jfB~_HEA2_u{Ja9`-nM8oTuKs#$IuX&4?~R9M(5IK6OQ&{>=u z`V>~8iONiU9?E7~^2>y)h7XOSOkK@=Opi?#Qyb$6qs`FPuuyExALjnR9+E_^s+^{% z57pKBulfoy2{lBwk&as7J@_!*h^OM+cocqsd*D6rj^`_dll`%K;rWqP;aZ^{;Zw!G zhh7dJ2p^Aikf&%Lp;k-|#|m!>Q6VTqgvnxe!)e2K<8#IXhWn5kTkzM}8_ZUwA^i9I zk(tZfXWBDg;A0rx03jnbA}2bhKLu~)#wur&H43X-R>sS176hvPG^XUy$EbHmid*UfZI+sOZuWDFiV!U2&+NX)kMU zsrkxr87aRhOSPxaSY{B1`7>Msm%!FxYBI~2@0lN2i7n5q;P$Z(*)Q2%Yzr=(bMX#% zlhsbVApFeV;kq*2(C7M6HKIJN)C27XsxB=_Yo_W-MBXY3@;lNM=`+ZR?UZJUuC!6s z%31PcxwrB(yr4gdbvDR-$M@ssaXq;9>@)B-#mNc0%6-bsw~BZ3 zUHD17%5G*h<2A%g7`?u>TYE+=r}kC{C}Y$*s$c1*E|K@jg;H0^sr(|pt^TNa)Yke9 z^^Q_ieIQp;=firm6YpXkvT0mbb{Z5l1I%aaC#=q@?ANT3JIY>R)47)1Ev_QJm(S&o z^JjPy{}J~Qn~B$;SM^=$$I5j16Zy1k1UYU#bcy@~Z`tdhnz%bU zhn6D)x`qa$ws<+lcsAaL9-|(3A0ETD=HB8*2q{8&p+BF=eFpw>ioM1hz{}B0`d?Jr zs}0ev!5ifP>S3jm^1j?s9w0ZC8D)=RQpajJ@IJdInMfW%ZQ&QPo^*w`y1$Wg=q#?! ze9p9EIA#TIjThtfxIcs0SJ*r39JVu?%DS0hY$vu2yltP$v}C@)sdxr@m)z4k>pyB! zG@=euTfsZjoeEYjDp!@))yb-+*4GT$RgKrb)fF-iEyZT$9(FM`@JjRy`jD8RPBRLH z(XUV`7>U1P|B!Mw#BhMX(1W7(%+-=EE5?lR4pruYI>SC*4TWVSw9zW{G(XG3+O zm6ihku$pLR)LUu;?V;Km-n%c2VGdKYRj%gGQ@pB&Ts=r^_A zS{2QwK2rVArfCng7j#W;M5>Sk;?~3PPWm=^5uHUF(NZ)3Dm@Buphai^&cxH14NPAs zYxTz2=sWZ=BB&oegp2VP_*EQ2i_uE-F1mp1utsEo)xRfg$Z7pceYoCHZ=)ycUG?Ym zDf$Me*%a#*;v^<=SHG-Z(G8?0d6(pn_GlLR65juR4Enu=7Qp#8kRva^eiPb&rla@J zx9AqCjT_+#xEQTKFG2N-)~RlhG}IkttOfp`qV;GQ>Vi~wbDl!H`VV?QPlPwyQ^;sC zoHQVh^;`O0*c!ss_fQkN2KB33xjmJ zo%=&2tSWM#n`9?hN~V#o;Fv=yLJofxHAZdGAgH&E1I;@@%`8gpll|bE$G}5lq!OwG zm4z&*N_|51kZ*zSBXSY;Uz3BtJ&!boYTXNHJZgvPqXcAyDq4G>G!czNeNh$oUdaO% zya?X#Gx;6rcK4u?CxP@gU^_uhle6Ic*+3;oR8j;rNGsIn+M!-(2KoR^LsRM7f1t`i zHIV};Sg0r#!Q9hiA84?h>;M{V$y}kY8E@N|nG4 zMv%h{Ix%oW;O7C5+D&p{W;XG`RS>XplTiE!MFG&7R%l~D*B|$rdte2Bd|U+X`eD`s zk_Gk8`w%fVV4nrnjX@2x3fQ9q#Cs2DU7&S9Eug0wV9KCk7@q-%U7#5Vw)=odh(L7+ zYK;_g_^SpqqTpvyu$csPS`KPQMyRzqP&%p&x^w~W?Fp?r>IOd47^*c6u%G}Q6aq}| zL(2pnkD&QLg9v;Pqk|S=ys}#XY(tUIK{}ii&m(qFnK~tS1Lqv5z zt)RCUqUAPNBLk$b3^uF;dO3iv8}L64$j}(82>Kd9w+!ex1o27zOou#b1@2bxny1jy z;KwhZ7s02Wf%8@%yAGQE4r8;R{R}f7LtI`3J3Ry}9gv6Gf%lC7ZR)|+5qvTQcsszF z8r0N3CtrdtyP(oeSHu$_2}R!o<3>Ubp9qpEV-W5{ zK%!jG!vmff0B!HaGvGbYj%G?5NLUYKq&n0AE;YahD?qOk?41wu^1;8d;p>Yc@V+dV z2p5{Q8dc6@aDF%ri!suTi`<;cH{R_x;9p<{h_hOJy zoY1O(R4rj^2pN;^YeP>O%;sQ|z)Eh|3V^Q$63H-623_1h*AH?&0=yoB#(%)x3wH$I z+YqR<2CMc4Eqg#jwufuFp142@`lX;hKAx^*k6>07Xy6A82*^XJ`+*nbNo{ctS_aVe z!hHddZ~}t$ApbUsYuOBr4FH{F@G%MOP1l7-K>H5ha|g7hnTX0pqcb;-lLVfV07#Yt zt5*S9RpZ%=N@fAsXml3?9WRW|gM9(W5`@efgZ?PU7K3w|of4rrAZM13XOwPHUM9m&8Xy5Y6oyli(9@T^rr(o`n0QpoDtAB7c{3|6KNn^m1`cf*)ssLD42L#gKm=2sB;2Q>rIUVGqYt2JI>oz=nUW0ZA;^rFQ z`2b|1E1wB;EC+Z!1^(C)e7qxAw+*x=&`0GlLbMiwMN8uPD%jvO_{5ceM_&=>F2wU_ zWuQc=c0YgEWwlJ_!{7$~kca?#1Jt%5fRCyC7Kx=u`kY5wN8T(5MKJ z-vlD07Eo*eK2j01D-Uuq;1glMgFXSe;phPrVjvyOra|bV{*?`}lM_drW}h%v9s!;y zK%**9sUDBuWS~XAd81xIy+H>Q=#yjuU`O*d3mbhp2?1R{Q1t>*)DD?IopSJkhX{ah z3P@NJWNikysy=92AFk5DN9Y%BGH~|;#CJTFLbKJ>Xu1;prcTgf-YB8;R~qIl5npb?Dc0D~yRNf0FW02VZFsE`93@%6(A ze4N1LDbTqxcnbCS2;fu<*6@IJXtdE4j6QkkBmqXI#F4Q9&h)vI#QnP%ID}ve!(IXU z3S=e#IDsm^qj)d4Xow+X%-%!lW6D`3WeUP&OI3UP-}0Qk~e;e)2Z`b71hXi)uW zrbz=-Du4&kF9}uPM4z^Ou&4j%D3XsLKLtR)5YTmkoHT~(g8$KMR1q|z@yG&Int!R6 zQD0?1PAV&1dFYX%uEY9egguo?1Sx4&i2-FgClJRa3~LpT#W zp?*Q1aA-c^0V6tE0e+Mt{ohasXLN0*s|mF|jR5*IY6nVmtt`nucA!JGP6SGnLLrQ# z642H70qNCaz(D#Btw*13eY^A+&`*GK3>!kD$IYERjESHAv-@8`pakdt71IAt5FImW e=4=R-e}4ZU@DBq2An*?Y{~+)W0{OrUg literal 0 HcmV?d00001 diff --git a/sounds/was-not-found.wav b/sounds/was-not-found.wav new file mode 100644 index 0000000000000000000000000000000000000000..74b4128110eb7c624a7c2e9b0a74882a6165fb03 GIT binary patch literal 28594 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 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/www/agc/vicidial.php b/www/agc/vicidial.php index 35de1bd2..7c0719e1 100644 --- a/www/agc/vicidial.php +++ b/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;