Phone IVR Application Migration from Plivo XML to FlexML
If you have read the Migrating from Plivo to CarrierX Quick Start, you could see that Plivo XML and FlexML are not too much different. Both offer a special syntax to provide instructions, and all you need is to change the representation of these instructions in your code.
But when it comes to a real-case migration from Plivo to CarrierX, users might meet some difficulties.
Let’s see such a migration in details and learn how to solve the issues that arise so that your migrated application worked with CarrierX flawlessly.
Getting Application Source Code
We take the Phone IVR application from Plivo as an example. This sample application answers the call and offers the calling party to either listen to the prerecorded message in different languages or to listen to a song. You can download the application source code at GitHub.
The application contains a single phone_ivr.py
file which we are going to modify.
All the routes used to send requests and responses for our application are in this file.
Modifying Application Routes
Let’s take a closer look at each of the routes in the file.
The application code file contains three routes:
- ivr route
- firstbranch route
- secondbranch route
We will go step by step through each of the routes, check what each of them contains, and learn how to modify these routes to migrate their code to FlexML.
1. ivr Route
The first route we are going to change is ivr. The application uses it to greet its users, play the initial instructions, and wait for the calling party input.
We modify the ivr route like this:
-
The ivr route uses the
ResponseElement()
class to build the Plivo XML response to the call. In FlexML we do not need it, so we can remove this line. -
We remove the absolute URL path from the
getinput_action_url
, as FlexML allows using the relative path. This will be more useful if we decide to test our application locally. -
We also remove the
response.add()
function which builds the response. -
Finally, we
return
the FlexML formatted string instead of the Plivo XMLResponse()
. In it, we replace Plivo XMLGetInput
element with the corresponding CarrierX FlexMLGather
verb (dropping the unsupporteddtmf
attribute), theSpeak
element with theSay
verb.
Plivo XML Python Code
@app.route('/ivr/', methods=['GET','POST'])
def ivr():
response = plivoxml.ResponseElement()
getinput_action_url = "http://www.foo.com/firstbranch/"
response.add(plivoxml.GetInputElement().
set_action(getinput_action_url).
set_method('POST').
set_input_type('dtmf').
set_digit_end_timeout(5).
set_redirect(True).add(
plivoxml.SpeakElement(ivr_message1)))
response.add(plivoxml.SpeakElement(noinput_message))
return Response(response.to_string(), mimetype='application/xml')
Corresponding FlexML Python Syntax
@app.route('/ivr/', methods=['GET','POST'])
def ivr():
getinput_action_url = "/firstbranch/"
return f'''
<Response>
<Gather action="{getinput_action_url}" method="POST" timeout="5">
<Say>{ivr_message1}</Say>
</Gather>
<Say>{noinput_message}</Say>
</Response>'''
2. firstbranch Route
The firstbranch route checks what digits the calling party enters:
- if they enter
1
, the route redirects to the secondbranch route to listen to new instructions; - if they enter
2
, the route playbacks an audio file; - if they enter any other digit, the route responds with an error message.
Thus, we change this route the following way:
-
The firstbranch route uses the
ResponseElement()
class to build the Plivo XML response to the call. In FlexML we do not need it, so we can remove this line. -
The next code portion to change is the way the application gets the data from the call. In CarrierX, it is pure JSON, which you can receive and parse using the common Flask
request
module. Refer to the code below to see how we replace the definition of thedigit
variable. -
We remove the absolute URL path from the
getinput_action_url
, as FlexML allows using the relative path. This will be more useful if we decide to test our application locally. -
We replace the
response.add()
functions in theif
condition with a newresponse
variable. It will be a string with the FlexML syntax. -
We perform the same operation for the
response.add()
function in theelif
condition. -
And the
response.add()
function in theelse
condition. -
Finally, we simply return the resulting
response
to the route.
Plivo XML Python Code
@app.route('/firstbranch/', methods=['GET','POST'])
def firstbranch():
response = plivoxml.ResponseElement()
digit = request.values.get('Digits')
if digit == "1":
getinput_action_url = "http://www.foo.com/secondbranch/"
response.add(plivoxml.GetInputElement().
set_action(getinput_action_url).
set_method('POST').
set_input_type('dtmf').
set_digit_end_timeout(5).
set_redirect(True).add(
plivoxml.SpeakElement(ivr_message2)))
response.add(plivoxml.SpeakElement(noinput_message))
elif digit == "2":
response.add_play(plivo_song)
else:
response.add_speak(wronginput_message)
return Response(response.to_string(), mimetype='application/xml')
Corresponding FlexML Python Syntax
@app.route('/firstbranch/', methods=['GET','POST'])
def firstbranch():
data = request.get_json()
digit = data.get('Digits','')
if digit == "1":
getinput_action_url = "/secondbranch/"
response = f'''
<Response>
<Gather action="{getinput_action_url}" method="POST" timeout="5">
<Say>{ivr_message2}</Say>
</Gather>
<Say>{noinput_message}</Say>
</Response>'''
elif digit == "2":
response = f'''
<Response>
<Play>{plivo_song}</Play>
</Response>'''
else:
response = f'''
<Response>
<Say>{wronginput_message}</Say>
</Response>'''
return response
3. secondbranch Route
The secondbranch route checks what digits the calling party enters:
- if they enter
1
to3
, the route responds with the message in the selected language; - if they enter any other digit, the route responds with an error message.
Thus, we change this route the following way:
-
The secondbranch route uses the ResponseElement() class to build the Plivo XML response to the call. In FlexML we do not need it, so we can remove this line.
-
The next code portion to change is the way the application gets the data from the call. In CarrierX, it is pure JSON, which you can receive and parse using the common Flask
request
module. Refer to the code below to see how we replace the definition of thedigit
variable. -
We replace the
params
variable with thelanguage
variable and theresponse.add()
function with a newresponse
variable in theif
condition. It will be a string with the FlexML syntax. -
We do the same for the French language in the first
elif
condition. -
And the same for the Russian language in the second
elif
condition. -
We also replace the
response.add()
function with theresponse
variable in theelse
condition. -
Finally, we simply return the resulting
response
to the route.
Plivo XML Python Code
@app.route('/secondbranch/', methods=['GET','POST'])
def secondbranch():
response = plivoxml.ResponseElement()
digit = request.values.get('Digits')
if digit == "1":
text = u"This message is being read out in English"
params = {
'language': "en-GB",
}
response.add_speak(text,**params)
elif digit == "2":
text = u"Ce message est lu en français"
params = {
'language': "fr-FR",
}
response.add_speak(text,**params)
elif digit == "3":
text = u"Это сообщение было прочитано на русском"
params = {
'language': "ru-RU",
}
response.add_speak(text,**params)
else:
response.add_speak(wronginput_message)
return Response(response.to_string(), mimetype='application/xml')
Corresponding FlexML Python Syntax
@app.route('/secondbranch/', methods=['GET','POST'])
def secondbranch():
data = request.get_json()
digit = data.get('Digits','')
if digit == "1":
text = u"This message is being read out in English"
language = "en-GB"
response = f'''
<Response>
<Say language="{language}">{text}</Say>
</Response>'''
elif digit == "2":
text = u"Ce message est lu en français"
language = "fr-FR"
response = f'''
<Response>
<Say language="{language}">{text}</Say>
</Response>'''
elif digit == "3":
text = u"Это сообщение было прочитано на русском"
language = "ru-RU"
response = f'''
<Response>
<Say language="{language}">{text}</Say>
</Response>'''
else:
response = f'''
<Response>
<Say>{wronginput_message}</Say>
</Response>'''
return response
Now that we modified all the routes, we can safely remove the Plivo library import declaration from the beginning of the phone_ivr.py
file:
from plivo import plivoxml
from flask import Flask, request
plivo_song = "https://s3.amazonaws.com/plivocloud/music.mp3"
ivr_message1 = "Welcome to the CarrierX IVR Demo App. Press 1 to listen to a pre recorded text in different languages. \
Press 2 to listen to a song."
ivr_message2 = "Press 1 for English. Press 2 for French. Press 3 for Russian"
noinput_message = "Sorry, I didn't catch that. Please hangup and try again \
later."
wronginput_message = "Sorry, it's wrong input."
app = Flask(__name__)
@app.route('/', methods=['GET','POST'])
def ivr():
getinput_action_url = "/firstbranch/"
return f'''
<Response>
<Gather action="{getinput_action_url}" method="POST" timeout="5">
<Say>{ivr_message1}</Say>
</Gather>
<Say>{noinput_message}</Say>
</Response>'''
@app.route('/firstbranch/', methods=['GET','POST'])
def firstbranch():
data = request.get_json()
digit = data.get('Digits','')
if digit == "1":
getinput_action_url = "/secondbranch/"
response = f'''
<Response>
<Gather action="{getinput_action_url}" method="POST" timeout="5">
<Say>{ivr_message2}</Say>
</Gather>
<Say>{noinput_message}</Say>
</Response>'''
elif digit == "2":
response = f'''
<Response>
<Play>{plivo_song}</Play>
</Response>'''
else:
response = f'''
<Response>
<Say>{wronginput_message}</Say>
</Response>'''
return response
@app.route('/secondbranch/', methods=['GET','POST'])
def secondbranch():
data = request.get_json()
digit = data.get('Digits','')
if digit == "1":
text = u"This message is being read out in English"
language = "en-GB"
response = f'''
<Response>
<Say language="{language}">{text}</Say>
</Response>'''
elif digit == "2":
text = u"Ce message est lu en français"
language = "fr-FR"
response = f'''
<Response>
<Say language="{language}">{text}</Say>
</Response>'''
elif digit == "3":
text = u"Это сообщение было прочитано на русском"
language = "ru-RU"
response = f'''
<Response>
<Say language="{language}">{text}</Say>
</Response>'''
else:
response = f'''
<Response>
<Say>{wronginput_message}</Say>
</Response>'''
return response
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
Finishing Migration
Now let’s test our application! You can run your local Flask server through terminal using the following command that will serve the created application to the incoming requests.
FLASK_APP=phone_ivr.py flask run
Now your application is running, but it is not visible to the outside world. We need to expose the localhost route publicly over the Internet so that your endpoint could access and load the FlexML instructions. To do this, we can use the free ngrok tool. If you choose to use ngrok, follow the instructions on their website to download it. Alternatively, you may expose the URL any other way you choose.
Once your Flask server is running, your terminal will list the port it is running on. Expose this port by running the following command through terminal. Make sure to replace 5000
(which is the Flask default port) with the port that your Flask server is running on.
./ngrok http 5000
When ngrok is run successfully, it will open a new terminal tab that will show you the http
and https
URLs that make the application publicly available over the Internet.
Add the https
ngrok link (in our sample it is https://d4d66ed41d49.ngrok.io/ivr
, as we chose ivr
to be our default route) to either a FlexML endpoint or a DID associated with a FlexML endpoint. Refer to the FlexML Endpoint quick start guide to learn how.
After that, call the associated DID to check how the application works.
Further Reading
You have successfully migrated the Phone IVR application from Plivo XML to FlexML!
Refer to the following pages to learn more about FlexML verbs and how to use them, and about ways to set up a FlexML endpoint:
Use our Migrating from Plivo XML to CarrierX Quick Start to learn more about other difficulties you can meet while migrating from Plivo XML to CarrierX and the ways to solve these issues.
Read other instructions on real-case migrations from Plivo XML to CarrierX here:
- Voicemail Application Migration
- Call Transfer Application Migration
- Guessing Game Application Migration
Refer to our other quick start guides for instructions on how to work with CarrierX: