Tim Petlock
2004-Feb-18 10:11 UTC
[Asterisk-Users] agi scripting in perl - dealiing with unexpected disconnects gracefully / spurious DTMF
Ok - before posting I (think I) read every post on the list that had the phrase "calling card" in it. This is probably more of a perl-related question. I've slightly modified Brian West's calling card script and I'm stumped for how to deal with users hanging up on it - it doesn't recover gracefully. When I first tested the script out I was doing it from various payphones near my home. The phones were a little unpredictable as far as the length of DTMF tones generated and whether the keys generated DTMF tones at all so there were a number of call attempts made as I traveled around the area. I noticed that it took longer and longer for * to answer the calls as the day went on and it eventually stopped responding at all. If everything is entered correctly and perfectly the script works and the call goes through. However, I've found that if I enter only a partial "calling card number" and then hang up the script will continue to run with the perl process that it lives inside of taking up lots of processor time and for each failed call you get one of those disconnected processor-hungry processes. In the first while statement I changed the OR to an AND because the script was never satisfying (length($pin) != 8 || $try < 3) consistently. I can see how it could if the user perfectly entered eight digits each time but in the real world that's not going to happen 100%. Could be that a DTMF recognition issue happens due to background noise, user will get distracted and enter the wrong length, or someone will go on a fishing expedition for valid pins. Once I changed the || to an && it would gracefully finish after three variable-length inputs - and if you hang up on it the perl process finishes too: with the unfortunate side effect of taking * with it. When the perl process goes away so does asterisk. Any ideas for keeping asterisk running after the perl script dies off? It'd be kinda nice to have the perl script go away but have asterisk stay running. The complete perl script is at the bottom of this message in the hope that my next question will more likely be noticed. :) I'm also getting spurious DTMF when calling people. I very occasionally will hear a spurious DTMF tone coming from the other end, must its much more frequent from the point of view of the person I'm talking to - they hear a lot more. What should I look for in my configuration? I'm using a cisco ata-186 configured as follows: [cisco] type=friend username=cisco secret=***** nat=yes ; This phone may be natted host=dynamic canreinvite=no ; Cisco poops on reinvite sometimes qualify=200 ; Qualify peer is no more than 200ms away defaultip=***** context=homesip dtmfmode=rfc2833 callerid="Test" <4444> mailbox=1000 The audiomode parameter on my ATA-186 is set to 0x11241124 which I'm pretty sure matches the dtmfmode parameter in my sip.conf. I'm using nufone as the link to the PSTN. The way I see it the links look like this: (ata) ---sip g.711 ulaw---> (*) ---iax2 gsm---> (nufone) ---???---> (PSTN) I'm going to try to make calls by calling the line connected to my FXO interface to rule out the first hop. Is there anything I can do in my iax.conf file? Here's the modified perl script I'm playing with... #!/usr/bin/perl # # PrePaid CallingCard IVR Application for Asterisk PBX # Copyright 2003, Brian K. West <brian@bkw.org> 2003-08-20 # # All prompts are kludged together.. I would like it to read # "Please enter your calling card number" # "I'm sorry thats an invalid card" # "You have Twenty minutes remaing on this card" # "Please enter the number you wish to call" # # We could be evil and set the initial AbsoluteTimeout when the call is answered # So slower users are cut off thus not wasting time :P Like 60 seconds. # # Also to be on the more evil side you can start the AbsoluteTimeout before we ask for the number # So we would be on the users time at that point and they would have some sort of incentive # to enter a number. As you see I have it set to give them 3 trys for an X number of digits. # # TODO: # Not really sure.... This was more to prove I could do it. # Fix the error checking... its far from graceful now. # Write monthly cron to clean db and remove old pins. # Do a happy dance? # # mad propz to citats for Asterisk::AGI perl modules. # use Asterisk::AGI; use DBI; # Config options %MYSQL = ( hostname => "localhost", username => "****", password => "*******", database => "*********" ); $dbh DBI->connect("dbi:mysql:$MYSQL{database}:$MYSQL{hostname}","$MYSQL{usern ame}","$MYSQL{password}") || die("Couldn't connect to database!\n"); # # We should throw an error down the channel and take care of it gracefully # $AGI = new Asterisk::AGI; my %input = $AGI->ReadParse(); my $target = 0; my $try = 0; # Watch out for loops they will make you dizzy. while(length($pin) != 8 && $try < 3) { $pin = $AGI->get_data("plscrdnum", "10000", "8"); # debug ---------- $AGI->exec('SayDigits',$try); # debug ---------- $units = check_pin($pin); if($units eq undef) { $try++; # we could do an invalid pin warning here... :P } else { # reset try because we want to make sure we try to get a valid # X digit number from the user.. its set at 7 now.. but you can # change that to say 11. # $try = 0; $timeout = $units * 60; # use the pin as the account code so we can track this $AGI->exec('SetAccount',$pin); $AGI->stream_file('balance'); $AGI->exec('SayDigits',$units); $AGI->stream_file('minutes'); $AGI->exec('Wait','1'); while(length($target) != 11) { if($try >= 3) { $AGI->stream_file('vm-goodbye'); $AGI->hangup(); exit(0); } $target = $AGI->get_data("plsdial1", "10000", "12"); $try++; } $AGI->exec('AbsoluteTimeout',$timeout); # maybe a prompt saying "connecing your call" here. # this could be a config option or even dynamic depending on info associated # with the pin. Many Many options here. $provider = 'IAX2/******@NuFone/'; $dialstring = $provider . $target; $AGI->exec('Dial',$dialstring); $AGI->hangup(); exit(0); } $try++; } # user screwed up so lets just say goodbye and let them try again later. $AGI->stream_file('vm-goodbye'); $AGI->hangup(); exit(0); sub check_pin($pin) { my $query = "SELECT units FROM pins WHERE pin='$pin' LIMIT 1"; my $sth = $dbh->prepare($query); $sth->execute || die("Couldn't exec sth2!"); # need to be graceful here also my $units = $sth->fetchrow_hashref; # # Now we subtract usage from this pin. # Lets ditch all calls under 6 seconds.. you can change this. # Also when you clean the database you need to be sure that you remove the pin from # pins database. Check TODO list above. # my $query = "SELECT SUM(CEILING(billsec/60)) AS used FROM cdr WHERE accountcode='$pin' and billsec > 6;"; my $sth = $dbh->prepare($query); $sth->execute || die("Couldn't exec sth2!"); # need to be graceful here also my $used = $sth->fetchrow_hashref; $units->{units} = $units->{units} - $used->{used}; $sth->finish; if($units->{units} > 0) { return $units->{units}; } else { return undef; } } Thanks! Tim Petlock
Steven Critchfield
2004-Feb-18 16:00 UTC
[Asterisk-Users] agi scripting in perl - dealiing with unexpected disconnects gracefully / spurious DTMF
On Wed, 2004-02-18 at 11:11, Tim Petlock wrote:> If everything is entered correctly and perfectly the script works and > the call goes through. However, I've found that if I enter only a > partial "calling card number" and then hang up the script will continue > to run with the perl process that it lives inside of taking up lots of > processor time and for each failed call you get one of those > disconnected processor-hungry processes.When a user disconnects and asterisk hangs up the line, it closes it's side of the pipes it uses to talk to AGI. If you don't handle a null read from the pipe as a hangup, then it writes commands to asterisk and then gets a null read back and loops hard and fast. Maybe if I get some more time later I'll read the rest of that lengthy post and see if there are more answers for you. -- Steven Critchfield <critch@basesys.com>