The premise is that our company buys and stores various numbers that they lease out later. However, if those numbers do not have activity in a certain amount of time, they can become deactivated.
In the past, the company had individuals who would (every few months) get a list of current numbers in the risk zone for deactivation (hundreds of numbers) and dial each one to make sure it still went to our menu.
So the Provisioning Team reached out to me to ask me if this could be automated....
They're requirements were that:
a) they have lists of phone numbers (DNIS) that need to be called. this list could be a CSV
b) they would like to upload it to a tool which would dial each number validating if it was active or not
c) they would be notified of the results somehow.
I put together this tool and here's how I did it...
Basically I built an auto dialer with Grails and SIPCLI. I could have used SIPP, but since I foresaw a future need to do text to speech, I opted for sipcli. The downside to sipcli is that it is a windows tool... so you're tied to the Windows Env in this case. But you could easily repurpose the methodology here to work with Linux using SIPP instead.
Using Grails, I created an application with:
grails create-app dialer
then I cd'd into the dialer directory and created the main controller:
grails create-controller csvImport
Once done I opened up my IDE and imported the Grails project (I use Grails Tools Suite.)
View
In the View I did this to capture the user's CSV file:<div id="status" role="complementary">
<h1>Upload your CSV file of phone numbers</h1>
<g:form controller="CsvImport" method="post" action="save"
enctype ="multipart/form-data">
E-mail Results to a @mydomain.com address:<br>
<g:textField type="field" name="emailAdd" value="brian@mydomain.com" style='width: 500px;' required=""/><p/>
<input type="file" name="myfile" required/>
<g:actionSubmit value="Start Process" action="save"/>
</g:form>
</div>
The controller looks like this:
def save() {
def emailTo = params.emailAdd
if (emailTo =~ /@mydomain.com/) {
def csv = request.getFile('myfile').inputStream.text
def data = parseCsv(csv)
runAsync{
for(line in data) {
println "Trying... $line.Phone"
def dialNum = "sipcli/sipcli.exe $line.Phone -d **.**.*.*** [masked IP of our proxy] -o 4 -t \"This is a test. this is a test. this is a test. this is a test\"-l 3".execute()
def outFile = new File("grails-app/test.txt")
if (dialNum.text =~ /success/){
outFile << ("PASS on number $line.Phone\r\n")
} else {
System.getProperty("line.separator")
outFile.append("FAIL on number $line.Phone\r\n")
}
}
sendMail {
multipart true
to "${emailTo}"
from "SOMEONE@ADDRESS.COM"
subject "Provisioning Report"
body 'Please find the attached Provisioning Report...'
attachBytes 'grails-app/test.txt','text/csv', new File('grails-app/test.txt').readBytes()
}
} else {
render "Error: Email does not conform to @mydomain.com" }
}
}
To explain how it works...
The view is pretty easy to understand, it just takes a file and passes it to the controller.
Controller
The controller isn't doing any special validation, since this is a internal tool. It takes the CSV and expects to have a column header called "Phone." Phone will have a list of DNIS (or numbers) that need to be called.CSV Parsing
To parse the CSV I import:@Grab('com.xlson.groovycsv:groovycsv:1.0')
import static com.xlson.groovycsv.CsvParser.parseCsv
Then use this call def csv = request.getFile('myfile').inputStream.text to grab the input and finally pass it to the parser:
def data = parseCsv(csv)
Running Asynchronously
I don't want the user to wait 30min for 500 phone numbers to be dialed... so instead, I send them immediately to a page... this is handled with runAsync... this is a plugin called executor. The Grails Executor plugin will run a closure asynchronously so you can do other stuff while the longer method runs.Inside the runAsync closure is this for loop and if statement:
for(line in data) {
println "Trying... $line.Phone"
def dialNum = "sipcli/sipcli.exe $line.Phone -d **.**.*.*** [masked IP of our proxy] -o 4 -t \"This is a test. this is a test. this is a test. this is a test\"-l 3".execute()
def outFile = new File("grails-app/test.txt")
if (dialNum.text =~ /success/){
outFile << ("PASS on number $line.Phone\r\n")
} else {
System.getProperty("line.separator")
outFile.append("FAIL on number $line.Phone\r\n")
}
Basically it's doing this:
For each line in the csv file, it prints out the phone number it's trying, and then I've defined an action to run the sipcli sip client.... to call that same number. SipCli is a awesome command line sip tool for windows that can be used to find sip problems and issues. It's very light and easy to use. In this case I have it set to a 4 second timeout and i'm telling it to read the text, "This is a test..." when it makes the phone connection.Assertions
The assertion of whether or not the phone call is valid is via the regex i'm doing in the if statement... If dialNum.text has success then we output to a file "Pass on number [DNIS]"However, if the number fails to connect, we append to the same file "FAIL on number [DNIS]"
sendMail {
multipart true
to "${emailTo}"
from "SOMEONE@ADDRESS.COM"
subject "Provisioning Report"
body 'Please find the attached Provisioning Report...'
attachBytes 'grails-app/test.txt','text/csv', new File('grails-app/test.txt').readBytes()
}
That's it.
No comments:
Post a Comment