#!/usr/bin/perl # # agi-CALLCARD_dial_in.agi version 2.6 # # This script is designed to allow customers the ability to dial in to a DID and # enter in a PIN number that would look up their account and send on to a # specified in-group if pin is valid # # user is prompted for their PIN number only (no pound '#' required) # then the user is forwarded to the active vicidial agent session # # can be added as an AGI to a Call Menu, or as below: # Call Menu Entry: "agi-CALLCARD_dial_in.agi,en---3---callcard_pin_enter---callcard_pin_invalid---default" # ;agent dial-in: #exten => 2346,1,Answer ; Answer the line #exten => 2346,2,AGI(agi-CALLCARD_dial_in.agi,en---3---callcard_pin_enter---callcard_pin_invalid---default) #exten => 2346,3,Hangup # # ; Below are the parameters needed for the script to be run properly # ; 1. language # ; - en - English # ; - es - Spanish # ; - fr - French, not fully supported # ; 2. maximum attempts before hangup: # ; - 3 # ; 3. audio file enter pin number prompt: # ; - callcard_pin_enter # ; 4. audio file invalid pin number prompt: # ; - callcard_pin_invalid # ; 5. DID pattern to use for in-group forwarding settings: # ; - default # ; - USE-ORIGINATING-DID - this option will look up the DID the customer dialed in on and use those settings # # other audio files used: # - pin-number-accepted # - vm-youhave # - callcard_minutes_left # # Copyright (C) 2013 Matt Florell LICENSE: AGPLv2 # # CHANGELOG # 100311-1135 - First build # 130108-1812 - Changes for Asterisk 1.8 compatibility # &get_time_now; $start_epoch = $now_date_epoch; $script = 'agi-CALLCARD_dial_in.agi'; $now_date_epoch = time(); $now_date = "$year-$mon-$mday $hour:$min:$sec"; ($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";} $US='_'; # 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 =~ /^PATHlogs/) && ($CLIlogs < 1) ) {$PATHlogs = $line; $PATHlogs =~ 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/ivrout.$year-$mon-$mday";} use DBI; 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; # defaults: $language = 'en'; $maximum_attempts = '3'; $pin_enter_audio = 'callcard_pin_enter'; $pin_invalid_audio = 'callcard_pin_invalid'; $did_ingroup_settings = 'default'; ### 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]); if (length($ARGV_vars[0]) > 0) {$language = $ARGV_vars[0];} if (length($ARGV_vars[1]) > 0) {$maximum_attempts = $ARGV_vars[1];} if (length($ARGV_vars[2]) > 0) {$pin_enter_audio = $ARGV_vars[2];} if (length($ARGV_vars[3]) > 0) {$pin_invalid_audio = $ARGV_vars[3];} if (length($ARGV_vars[4]) > 0) {$did_ingroup_settings = $ARGV_vars[4];} } if ($language =~ /es/) { $audio_suffix='_es'; $AGI->exec("EXEC Set(CHANNEL(language)=$language)"); # $AGI->exec("SetLanguage($language)"); $AGI->stream_file('sip-silence'); $AGI->stream_file('sip-silence'); } if ($language =~ /fr/) { $audio_suffix='_fr'; $AGI->exec("EXEC Set(CHANNEL(language)=$language)"); # $AGI->exec("SetLanguage($language)"); $AGI->stream_file('sip-silence'); $AGI->stream_file('sip-silence'); } if ($language !~ /es|fr/) {$audio_suffix='';} $|=1; while() { chomp; last unless length($_); if ($V) { if (/^agi_(\w+)\:\s+(.*)$/) { $AGI{$1} = $2; } } if (/^agi_uniqueid\:\s+(.*)$/) {$unique_id = $1; $uniqueid=$unique_id} if (/^agi_extension\:\s+(.*)$/) {$extension = $1;} if (/^agi_channel\:\s+(.*)$/) {$channel = $1;} if (/^agi_callerid\:\s+(.*)$/) {$callerid = $1;} if (/^agi_calleridname\:\s+(.*)$/) {$calleridname = $1;} } if ( ($callerid =~ /\".*\"/) && ( (!$calleridname) or ($calleridname =~ /unknown/) ) ) { $calleridname = $callerid; $calleridname =~ s/\<\d\d\d\d\d\d\d\d\d\d\>//gi; $calleridname =~ s/\"|\" //gi; } $callerid =~ s/\D|\'//gi; $calleridname =~ s/unknown|\'//gi; if ( (!$callerid) or ($callerid =~ /unknown/) ) {$callerid = $calleridname;} $phone_number=$callerid; ### Grab Server values from the database $stmtA = "SELECT ext_context,agi_output 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; $rec_count=0; while ($sthArows > $rec_count) { @aryA = $sthA->fetchrow_array; $DBext_context = "$aryA[0]"; $DBagi_output = "$aryA[1]"; 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';} $rec_count++; } $sthA->finish(); $AGI->stream_file('sip-silence'); $AGI->stream_file('sip-silence'); $AGI->stream_file('sip-silence'); $entry_chances=0; $VALID_login=0; $ACTIVE_login=0; $VDADremDIALstr=''; while ( ($entry_chances < $maximum_attempts) && ($VALID_login < 1) ) { $entry_chances++; &user_password_gather; ###### Check userID and password against vicidial_users table $stmtA = "SELECT count(*) FROM callcard_accounts where pin='$PIN_number' and status IN('ACTIVE','USED') and balance_minutes > 0;"; $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; $VALID_login = $aryA[0]; } $sthA->finish(); if ($AGILOG) {$agi_string = "validate PIN: |$VALID_login|$stmtA|"; &agi_output;} if ($VALID_login > 0) { $AGI->stream_file("pin-number-accepted$audio_suffix"); } else { $AGI->stream_file("$pin_invalid_audio$audio_suffix"); } } if ($VALID_login < 1) { if ($AGILOG) {$agi_string = "DIAL-IN END: Invalid Pin |$VALID_login|$PIN_number|"; &agi_output;} $AGI->stream_file("vm-goodbye$audio_suffix"); $AGI->hangup(); $dbhA->disconnect(); exit; } else { $secX = time(); $PDtarget = ($secX - 30); ($Psec,$Pmin,$Phour,$Pmday,$Pmon,$Pyear,$Pwday,$Pyday,$Pisdst) = localtime($PDtarget); $Pyear = ($Pyear + 1900); $Pmon++; if ($Pmon < 10) {$Pmon = "0$Pmon";} if ($Pmday < 10) {$Pmday = "0$Pmday";} if ($Phour < 10) {$Phour = "0$Phour";} if ($Pmin < 10) {$Pmin = "0$Pmin";} if ($Psec < 10) {$Psec = "0$Psec";} $PDtsSQLdate = "$Pyear$Pmon$Pmday$Phour$Pmin$Psec"; ###### Check for the current minute balance to play back to the customer in the callcard_accounts table $stmtA = "SELECT card_id,balance_minutes,inbound_group_id FROM callcard_accounts where pin='$PIN_number';"; $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; $card_id = $aryA[0]; $balance_minutes = $aryA[1]; $inbound_group_id = $aryA[2]; } $sthA->finish(); ###### Check for the current minute balance to play back to the customer in the callcard_accounts table $stmtA = "SELECT vdl.did_id,did_pattern FROM vicidial_did_log vdl, vicidial_inbound_dids vid where uniqueid='$uniqueid' and vdl.did_id=vid.did_id order by call_date desc limit 1;"; $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; $original_did_pattern = $aryA[1]; } $sthA->finish(); if ($did_ingroup_settings =~ /USE-ORIGINATING-DID/) {$did_id_search = $original_did_pattern;} else {$did_id_search = $did_ingroup_settings;} $did_id=0; ###### find the in-group settings to use when forwarding this call $stmtA = "SELECT did_id FROM vicidial_inbound_dids where did_pattern='$did_id_search';"; $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; $did_id = $aryA[0]; } $sthA->finish(); ##### play customer's balance to them before transferring $AGI->stream_file("vm-youhave$audio_suffix"); $AGI->say_number("$balance_minutes"); $AGI->stream_file("callcard_minutes_left$audio_suffix"); ##### update callcard_accounts_detail record for this account $stmtA = "UPDATE callcard_accounts_detail set used_time='$now_date' where card_id='$card_id';"; $affected_rows = $dbhA->do($stmtA); if ($AGILOG) {$agi_string = "callcard_accounts_detail update: |$affected_rows|$now_date|$card_id|"; &agi_output;} ##### insert callcard_log record for this call $stmtA = "INSERT INTO callcard_log set uniqueid='$uniqueid',card_id='$card_id',call_time='$now_date',phone_number='$phone_number',inbound_did='$original_did_pattern',balance_minutes_start='$balance_minutes';"; $affected_rows = $dbhA->do($stmtA); if ($AGILOG) {$agi_string = "callcard_log INSERT: |$affected_rows|$stmtA|"; &agi_output;} ### format the remote server dialstring to get the call to the overflow agent meetme room $forward_extension = "99909*$did_id*$inbound_group_id"; if ($AGILOG) {$agi_string = "Customer $PIN_number sent to $forward_extension"; &agi_output;} print "SET CONTEXT $ext_context\n"; $result = ; checkresult($result); print "SET EXTENSION $forward_extension\n"; $result = ; checkresult($result); print "SET PRIORITY 1\n"; $result = ; checkresult($result); } $dbhA->disconnect(); exit; ################################################################################ ##### SUBROUTINES ############################################################## ################################################################################ ##### BEGIN collect the pin number ###################################### sub user_password_gather { ################################################################################ # please enter the pin number followed by the pound key $interrupt_digit=''; $interrupt_digit = $AGI->stream_file("$pin_enter_audio$audio_suffix",'0123456789'); print STDERR "interrupt_digit |$interrupt_digit|\n"; $digits_being_entered=1; $PIN_number=''; if ($interrupt_digit > 0) { # if ($interrupt_digit == 35) {$interrupt_digit='#';} # if ($interrupt_digit == 42) {$interrupt_digit='*';} if ($interrupt_digit == 48) {$interrupt_digit=0;} if ($interrupt_digit == 49) {$interrupt_digit=1;} if ($interrupt_digit == 50) {$interrupt_digit=2;} if ($interrupt_digit == 51) {$interrupt_digit=3;} if ($interrupt_digit == 52) {$interrupt_digit=4;} if ($interrupt_digit == 53) {$interrupt_digit=5;} if ($interrupt_digit == 54) {$interrupt_digit=6;} if ($interrupt_digit == 55) {$interrupt_digit=7;} if ($interrupt_digit == 56) {$interrupt_digit=8;} if ($interrupt_digit == 57) {$interrupt_digit=9;} $PIN_number=$interrupt_digit; } $digit_loop_counter=0; while ( ($digits_being_entered > 0) && ($digit_loop_counter < 8) ) { $digit = chr($AGI->wait_for_digit('10000')); # wait 10 seconds for input, until the pound key is pressed or 8 digits if ($digit =~ /\d/) { $PIN_number = "$PIN_number$digit"; print STDERR "digit |$digit| PIN_number |$PIN_number|\n"; # $AGI->say_digits("$digit"); undef $digit; } else { $digits_being_entered=0; } $digit_loop_counter++; } $PIN_number =~ s/\D//gi; if ($PIN_number) {print STDERR "digit collection done|$digit| PIN_number |$PIN_number|\n";} } ##### END collect the pin number ############################################### sub get_time_now #get the current date and time and epoch for logging call lengths and datetimes { ($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"; $filedate = "$year$mon$mday$hour$min$sec"; } 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=''; } sub leading_zero($) { $_ = $_[0]; s/^(\d)$/0$1/; s/^(\d\d)$/0$1/; return $_; } # End of the leading_zero() routine.