Link to home
Start Free TrialLog in
Avatar of rdelrosario
rdelrosario

asked on

Parsing a text file in Asterisk to used in an inbound IVR Campaign

1.  When an inbound call is answered by my IVR campaign, I'd like to parse a text file to see if a 7 digit number exists before letting them into the survey.  Can you direct me to some samples that will parse an entire file (plain ascii text file) and look for the occurrence of a 7 digit number that the caller enters at the beginning of a survey IVR.

As an example.  "please enter your 7 digit customer number followed by the # button".  Store that number in a variable then parse a file and if it matches the record continue with the IVR.

2.  I need to make a recording in the IVR and then rename that recording to the 7 digit number plus the question number.   Say in Question 4 we ask them to tell us about their recent service experience at a dealership.   I'd like to make a recording and call it the Q4-"7 digit number".

Avatar of palner
palner

1. Create a perl script (like the code attached) and call it something like searchfile.pl
2. Add it to the agi-bin in var/lib/asterisk
3. Capture the 7 digits with a read or background, something like...

exten => s,n,Read(searchphone,SOUNDFILE,7,,1);variable searchphone will be 7 digits

4. Send that response to an AGI call

exten => s,n,AGI(searchfile.pl,${searchphone})

5. Check the output....

exten => s,n,GotoIf($[${MATH(${SEARCHCOUNT}>=1,i)}=TRUE]?found)


#!/usr/bin/perl -w
use strict;
$|=1;
 
my %AGI; my $tests = 0; my $fail = 0; my $pass = 0; my $result = "";
my $string = ""; my $count = 0;
 
$string = $ARGV[0];
      
while(<STDIN>) {
	chomp;
	last unless length($_);
	if (/^agi_(\w+)\:\s+(.*)$/) {
		$AGI{$1} = $2;
	}
}
 
#Replace the searchfile.txt with the file name
open FILE, "<searchfile.txt";
my @line = <FILE>;
for (@lines) {
    if ($_ =~ /$string/) {
        $count = $count + 1;
    }
}
 
print qq(SET VARIABLE SEARCHCOUNT "$count"\n);

Open in new window

To record a 7 digit number.... (using the same read command)....

exten => s,n,Record(/tmp/Q4-${searchphone}:wav,3,20)
Avatar of rdelrosario

ASKER

I'm reviewing your information now and will likely have a quick follow-up question.  In the mean time, when you say add it to the agi-bin in asterisk.. are you saying all I have to do is copy the program to that directory.   Must I CHMOD or install any other component or run some other related command?  We are running trixbox and am not sure if any base software is needed to call the perl scripts.  Can you let me know what I'll need to make sure/test that I have sufficient files capable of running these perl scripts?

Thanks
the user you have running asterisk will need read/execute access to the script; so yes chmod. You will also need perl installed; which is most likely already installed.
I created the file below and put it in the /var/lib/asterisk folder as you specified and called it searchinfo.pl and chmod as necessary.   The below dialplan is a sample with 1 question followed by the 7 digit account request to search for.  The following is the exact cut and paste of my fille:

; IVR RoadSide Test Survey
exten => 2222,1,Answer()
exten => 2222,n,set(TRYAGAIN=0)
exten => 2222,n(Q1START),NoOp(Begin Q1)
exten => 2222,n,Wait(.5)I
exten => 2222,n,SayAlpha(Q1)
exten => 2222,n,Read(Q1,,1,,,3)

exten => 2222,n,GotoIf($[${Q1} = 1]?Q2START)
exten => 2222,n,GotoIf($[${Q1} = 2]?Q2START)
exten => 2222,n,GotoIf($[${Q1} = 3]?Q2START)
exten => 2222,n,GotoIf($[${Q1} = 4]?Q2START)
exten => 2222,n,GotoIf($[${Q1} = 5]?Q2START)

exten => 2222,n,NoOp(User entered an invalid entry digits 1-5 allowed, restart Q2 otherwise hangup after 3 tries)
exten => 2222,n,Set(TRYAGAIN=$[${TRYAGAIN} + 1])
exten => 2222,n,GotoIf($[${TRYAGAIN} = 3]?ENDIT)
exten => 2222,n,Playback(vm-sorry)
exten => 2222,n,Goto(Q1START)

exten => 2222,n(Q2START),NoOp(Begin Q2)
exten => 2222,n,Read(searchphone,your-account,7,,1)
exten => 2222,n,AGI(searchfile.pl,${searchphone})
exten => 2222,n,GotoIf($[${MATH(${SEARCHCOUNT}>=1,i)}=TRUE]?found)
exten => 2222,n,PlayBack(no-info-about-number)
exten => 2222,n,Goto(Q2START)  ; ask to re-enter 7 digit account
exten => 2222,n(found),SayAlpha(FOUND)

exten => 2222,n(ENDIT),Playback(goodbye)
exten => 2222,n,Hangup()

*****************************  THE BELOW IS THE CLI OUTPUT OF THE ABOVE DIALPLAN *********************
I entered 1 for question 1 (Q1) then digits 1234567 for the searchphone variable.  the name of the .txt file is roadside.txt and is in the /etc/asterisk folder as roadside.txt.  Is the syntax right, am I missing a bracket or parenthesis?  Anyway.. here is the output of the CLI


    -- Executing Answer("SIP/9981-b7b26b10", "") in new stack
    -- Executing Set("SIP/9981-b7b26b10", "TRYAGAIN=0") in new stack
    -- Executing NoOp("SIP/9981-b7b26b10", "Begin Q1") in new stack
    -- Executing Wait("SIP/9981-b7b26b10", ".5") in new stack
    -- Executing SayAlpha("SIP/9981-b7b26b10", "Q1") in new stack
    -- Playing 'letters/q' (language 'en')
    -- Playing 'digits/1' (language 'en')
    -- Executing Read("SIP/9981-b7b26b10", "Q1||1|||3") in new stack
    -- Accepting a maximum of 1 digits.
    -- User entered '1'
    -- Executing GotoIf("SIP/9981-b7b26b10", "1?Q2START") in new stack
    -- Goto (from-internal,2222,17)
    -- Executing NoOp("SIP/9981-b7b26b10", "Begin Q2") in new stack
    -- Executing Read("SIP/9981-b7b26b10", "searchphone|your-account|7||1") in new stack
    -- Accepting a maximum of 7 digits.
    -- Playing 'your-account' (language 'en')
    -- User entered '1234567'
    -- Executing AGI("SIP/9981-b7b26b10", "searchfile.pl|1234567") in new stack
    -- Launched AGI Script /var/lib/asterisk/agi-bin/searchfile.pl
    -- AGI Script searchfile.pl completed, returning 0
    -- Executing GotoIf("SIP/9981-b7b26b10", "0?found") in new stack
    -- Executing Playback("SIP/9981-b7b26b10", "no-info-about-number") in new stack
    -- Playing 'no-info-about-number' (language 'en')
    -- Executing Goto("SIP/9981-b7b26b10", "Q2START") in new stack
    -- Goto (from-internal,2222,17)
    -- Executing NoOp("SIP/9981-b7b26b10", "Begin Q2") in new stack
    -- Executing Read("SIP/9981-b7b26b10", "searchphone|your-account|7||1") in new stack
    -- Accepting a maximum of 7 digits.
    -- Playing 'your-account' (language 'en')
    -- User disconnected
  == Spawn extension (from-internal, 2222, 18) exited non-zero on 'SIP/9981-b7b26b10'
    -- Executing Macro("SIP/9981-b7b26b10", "hangupcall") in new stack
    -- Executing ResetCDR("SIP/9981-b7b26b10", "w") in new stack
    -- Executing NoCDR("SIP/9981-b7b26b10", "") in new stack
    -- Executing GotoIf("SIP/9981-b7b26b10", "1?skiprg") in new stack
    -- Goto (macro-hangupcall,s,6)
    -- Executing GotoIf("SIP/9981-b7b26b10", "1?theend") in new stack
    -- Goto (macro-hangupcall,s,9)
    -- Executing Wait("SIP/9981-b7b26b10", "5") in new stack
  == Spawn extension (macro-hangupcall, s, 9) exited non-zero on 'SIP/9981-b7b26b10' in macro 'hangupcall'
  == Spawn extension (macro-hangupcall, s, 9) exited non-zero on 'SIP/9981-b7b26b10'
TrixPrimary2*CLI>


******************************************

The text file has the following in it.  Not sure how your perl text search program parses files and such, but the file looks like this.  It is attached as well for review:
[root@TrixPrimary2 asterisk]# vi roadside.txt
1234567
11111
22622
55555
14111
11111
~
~
~
~
~
~
"roadside.txt" 6L, 38C
*********************************

Any help regarding the code or what I might be missing would be great.                                              

I'm sure you know this, but my first sentence in the last post stated I put the dialplan in the /var/lib/asterisk folder.. I meant that I put the searchfile.pl in there.  The file I mentioned was the dialplan. :)
Did you you change the perl script to match your new file name?

Also, put in the full location if it's better and make sure there's good permissions for it...

Remember this part:

#Replace the searchfile.txt with the file name

by default it's going to look in the agi-bin for the text file. If you have it in let's say tmp, change the file to /tmp/filename.txt (or whatever is appropriate).

Also, you can test the perl script itself first....

something like: perl /var/lib/asterisk/agi-bin/searchfile.pl 1234567

That will show you if there's any errors your system is having with the script.
Sorry, here is the exact searchfile.pl cut and pasted with the path of my .txt file.   Am I missing anything syntax wise.

#!/usr/bin/perl -w
use strict;
$|=1;

my %AGI; my $tests = 0; my $fail = 0; my $pass = 0; my $result = "";
my $string = ""; my $count = 0;

$string = $ARGV[0];

while(<STDIN>) {
        chomp;
        last unless length($_);
        if (/^agi_(\w+)\:\s+(.*)$/) {
                $AGI{$1} = $2;
        }
}

#Replace the searchfile.txt with the file name
open FILE, "</etc/asterisk/roadside.txt";
my @line = <FILE>;
for (@lines) {
    if ($_ =~ /$string/) {
        $count = $count + 1;
    }
}

print qq(SET VARIABLE SEARCHCOUNT "$count"\n);

                                                 
Here is the output from manually running the program

[root@TrixPrimary2 sounds]# perl /var/lib/asterisk/searchfile.pl 1234567
Global symbol "@lines" requires explicit package name at /var/lib/asterisk/searchfile.pl line 21.
Execution of /var/lib/asterisk/searchfile.pl aborted due to compilation errors.


**  the file path is hardcoded in the searchfile.pl file so I don't believe I need to be in any specific directory unless I got the syntax messed up.
change:

my @line = <FILE>;


to:

my @lines = <FILE>;
OK, that fixed the problem and SEARCHCOUNT is returning a 1 when the digits are found, but in the dial plan syntax, it still isn't matching.  Can you look at the syntax in there again to confirm I have the right brackets or paranthesis in the right places.  Do I need to do something to pipe the SEARCHCOUNT variable back into the asterisk environment.

Here is the perl run at unix:
[root@TrixPrimary2 asterisk]# perl /var/lib/asterisk/searchfile.pl 1234567

SET VARIABLE SEARCHCOUNT "1"    

Here is the snip of the code in my dialplan.

exten => 2222,n,GotoIf($[${MATH(${SEARCHCOUNT}>=1,i)}=TRUE]?found)
exten => 2222,n,PlayBack(no-info-about-number)
exten => 2222,n,Goto(Q2START)  ; ask to re-enter 7 digit account
exten => 2222,n(found),SayAlpha(FOUND)


**  DO I have to initialize or do something with the SEARCHCOUNT variable for it to be passed back to asterisk?
The CLI states:
AGI Script searchfile.pl completed, returning 0
is this suppose to be returning the SEARCHCOUNT of 1?  If so I dont' understand the MATH routine.  Aren't we just checking for a SEARCHCOUNT value of 1?  I think I'm real close, but am missing something very minor I'm assuming.

Thanks
Please show what happens in the CLI after a match (completely including the gotoif statement)


If you only look for 1 and there's more than one match, you will fail. This is why you look for greater than or equal to 1.
If this helps any to the last post I had....  when running the perl command line... for some reason I have to hit a carriage return (enter key) for the SET VARIABLE SEARCHCOUNT "1" shows on screen.  Could it be that the AGI call runs but actually doesn't finish... Do I need something to make a hard carriage return execute without manually hitting the key?

thanks
please post the out put from the cli like in ID:22712836.
I'm sorry, I'm not familiar with the ID term you use.  The CLI output is posted above, but here it is again regarding the AGI execute:  My verbose is already at 25.  Do I need to set it higher to get the ID you speak of?

Executing Read("SIP/9981-b7b26b10", "searchphone|your-account|7||1") in new stack
    -- Accepting a maximum of 7 digits.
    -- Playing 'your-account' (language 'en')
    -- User entered '1234567'
    -- Executing AGI("SIP/9981-b7b26b10", "searchfile.pl|1234567") in new stack
    -- Launched AGI Script /var/lib/asterisk/agi-bin/searchfile.pl
    -- AGI Script searchfile.pl completed, returning 0
    -- Executing GotoIf("SIP/9981-b7b26b10", "0?found") in new stack
    -- Executing Playback("SIP/9981-b7b26b10", "no-info-about-number") in new stack
    -- Playing 'no-info-about-number' (language 'en')

The id is the id of your post here.

Are you sure that the user account used for trixbox has access/permission to the text file?
Hmm.  Thats a good question.  I'm not sure if the asterisk user account is used for execution of a dialplan.   None the less, I'll chmod it to work with ugoa rwx.

I know I already asked, but can you confirm the syntax to the GotoIF Math execution line in your dialplan.  I used it exactly.  

Also, is there a debug line I can put in asterisk to see where its failing.   The AGI perl script seems to work fine other than the carriage return thingy I mentioned (again is that an issue).   I tried to "echo" the output of the SEARCHCOUNT variable in asterisk, but it didn't have a value.  Does this mean that the AGI didn't pass the value back to asterisk?   Anything I can add or try to find out what/where the problem is... again I think we're so close.   In the mean time, I'll make sure the rights are setup.
If the file cannot be opened, the script will fail and nothing will be added. If your test outside the dialplan shows SEARCHCOUNT then it will be there when the script runs scuccessfully. Yes, the Math is correct for the reasons stated above:

"If you only look for 1 and there's more than one match, you will fail. This is why you look for greater than or equal to 1."

You are checking if Searchcount is >= 1.
The rights are correct.  Just to see if SEARCHCOUNT VALUE was being passed back to asterisk I added the following:

exten => 2222,n,GotoIf($[${SEARCHCOUNT} >= 1]?foundit)

It didn't go to the foundit section, but yet when running the perl script outside of asterisk it returns a value of 1.  So the perl script runs fine, but its not getting into asterisk.   what else do you think it could be?
exten => 2222,n,GotoIf($[${SEARCHCOUNT} >= 1]?foundit)

The about statement will fail... the math is needed.

run an agi debug during the session and post the results please.

Also, add something like this after the script:

2222,n,NoOp(Search Count ${SEARCHCOUNT})

Please be detailed here... why not post the exact script you have, the exact text file, the dialplan, etc so we can check for typo's. If the AGI is failing inside asterisk and working outside, 9x out of 10 it's the permissions. Also, I cannot emphasize enough the math is needed and works.
I found out the problem....
Asterisk CLI output is very deceptive.   I know you mentioned to put the full file path name in the perl script, but I thought about putting that in the dial plan too.   That did the trick so my dialplan is now:

exten => 2222,n,AGI(/var/lib/asterisk/searchfile.pl,${searchphone})

just for kicks, I even spelled the path wrong or the file name wrong in the AGI call and the CLI output was always successful returning 0.  

So anyway, it looks to be working well with your help...  thanks.   I'll award the points, but I do have one question about the current search script.

I'll grant the points promptly, but I do have a followup question.  Do you have sample code or working code that will allow me to:  I'll grant another 500 points for the below:

1.  search a specific column in the file for a pattern entered by the Read command.
2.  Use the search found/matched in item 1) above to read a particular value in that record layout.  Specifically,  lets say we are looking for account # in column 1-7 of the file I've been working with and I want to playback a balance amount in column 75-85 of that record.

thanks
ASKER CERTIFIED SOLUTION
Avatar of palner
palner

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I have some other proprietary programs that use flat file structures in place already which we've spent a lot of time on.  We could use mysql, but would rather stick to the native flat file for now.  With that said, I'll award the points now.  Can you give me the equivelent search samples for mysql to the above 7 digit AGI code we got working above along with searching specific columns from found records.  that would great to review.