Voicemail 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 Voicemail application from Plivo as an example. This sample application answers the call and invites the calling party to record a voicemail message which they can listen to later. You can download the application source code at GitHub.
The application contains a single voicemail.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:
- record route
- save_record_url route
- transcription 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. record Route
The first route we are going to change is record. The application uses it to greet its users, play the initial instructions, and wait for the calling party input.
We modify the record route like this:
-
The record 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
action
attribute, as FlexML allows using the relative path. This will be more useful if we decide to test our application locally. We leave the absolute URL for thetranscription_url
attribute, but rename it totranscribeCallback
which is used with FlexML. We also remove theresponse.add()
function which builds the response. -
Finally, we
return
the FlexML formatted string instead of the Plivo XMLResponse()
. In this string, we add theplayBeep="true"
andtranscribe="true"
attributes as they are set tofalse
by default in FlexML. We also replace Plivo XMLSpeak
element with the corresponding CarrierX FlexMLSay
verb.
Plivo XML Python Code
@app.route("/record/", methods=["POST", "GET"])
def record():
response = plivoxml.ResponseElement()
response.add(
plivoxml.SpeakElement(
"Please leave a message after the beep."
)
)
response.add(
plivoxml.RecordElement(
action="http://foo.com/save_record_url/",
method="GET",
max_length=30,
transcription_type="auto",
transcription_url="http://foo.com/transcription/",
transcription_method="GET",
)
)
return Response(response.to_string(), mimetype="application/xml")
Corresponding FlexML Python Syntax
@app.route("/record/", methods=["POST", "GET"])
def record():
return '''
<Response>
<Say>Please leave a message after the beep.</Say>
<Record action="/save_record_url/" maxLength="30" playBeep="true" method="GET" transcribe="true" transcribeCallback="http://foo.com/transcription/"></Record>
</Response>'''
2. save_record_url Route
The save_record_url route checks what method is used to access the route and prints the URL of the recorded message to the console:
- if the route is opened using the
GET
method, the application receives the recording URL from the request arguments both in Plivo XML and in CarrierX FlexML; - if the route is opened using the
POST
method, in FlexML the application receives the recording URL from the request JSON body.
Thus, we change this route the following way:
-
In CarrierX FlexML, the callback returns the recording URL as the
RecordingUrl
attribute. We change the attribute name returned by theGET
request. -
The next code portion to change is the way the application gets the data from the callback which uses the
POST
method. In CarrierX, it is pure JSON, which you can receive and parse using the common Flaskrequest
module. We replace the way the application extracts the recording URL for thePOST
request and change the attribute name withRecordingUrl
. -
Finally, we pronounce the
OK
message to the calling party.
Plivo XML Python Code
@app.route("/save_record_url/", methods=["GET"])
def save_record_url():
if request.method == "GET":
print(f"Record URL : {request.args.get('RecordUrl')}")
elif request.method == "POST":
print(f"Record URL :{request.form.get('RecordUrl')}")
return Response("OK", mimetype="text/xml")
Corresponding FlexML Python Syntax
@app.route("/save_record_url/", methods=["GET"])
def save_record_url():
if request.method == "GET":
print(f"Record URL : {request.args.get('RecordingUrl')}")
elif request.method == "POST":
print(f"Record URL :{request.get_json().get('RecordingUrl','')}")
return '''
<Response>
<Say>OK</Say>
</Response>'''
3. transcription Route
The transcription route checks what method is used to access the route and prints the transcription of the recorded message to the console:
- if the route is opened using the
GET
method, the application receives the transcription text from the request arguments both in Plivo XML and in CarrierX FlexML; - if the route is opened using the
POST
method, in FlexML the application receives the transcription text from the request JSON body.
Thus, we change this route the following way:
-
In CarrierX FlexML, the callback returns the transcription text as the
TranscriptionText
attribute. We change the attribute name returned by theGET
request. -
The next code portion to change is the way the application gets the data from the callback which uses the
POST
method. In CarrierX, it is pure JSON, which you can receive and parse using the common Flaskrequest
module. We replace the way the application extracts the transcription text for thePOST
request and change the attribute name withTranscriptionText
. -
Finally, we pronounce the
OK
message to the calling party.
Plivo XML Python Code
@app.route("/transcription/", methods=["GET", "POST"])
def transcription():
if request.method == "GET":
print("Transcription is : %s " % (request.args.get("transcription")))
elif request.method == "POST":
print("Transcription is : %s " % (request.form.get("transcription")))
return Response("OK", mimetype="text/xml")
Corresponding FlexML Python Syntax
@app.route("/transcription/", methods=["GET", "POST"])
def transcription():
if request.method == "GET":
print("Transcription is : %s " % (request.args.get('TranscriptionText')))
elif request.method == "POST":
print("Transcription is : %s " % (request.get_json().get('TranscriptionText','')))
return '''
<Response>
<Say>OK</Say>
</Response>'''
Now that we modified all the routes, we can safely remove the Plivo library import declaration from the beginning of the voicemail.py
file:
from plivo import plivoxml
from flask import Flask, request
app = Flask(__name__)
@app.route("/record/", methods=["POST", "GET"])
def record():
return '''
<Response>
<Say>Please leave a message after the beep.</Say>
<Record action="/save_record_url/" maxLength="30" playBeep="true" method="GET" transcribe="true" transcribeCallback="/transcription/"></Record>
</Response>'''
@app.route("/save_record_url/", methods=["GET"])
def save_record_url():
if request.method == "GET":
print(f"Record URL : {request.args.get('RecordingUrl')}")
elif request.method == "POST":
print(f"Record URL :{request.get_json().get('RecordingUrl','')}")
return '''
<Response>
<Say>OK</Say>
</Response>'''
@app.route("/transcription/", methods=["GET", "POST"])
def transcription():
if request.method == "GET":
print("Transcription is : %s " % (request.args.get('TranscriptionText')))
elif request.method == "POST":
print("Transcription is : %s " % (request.get_json().get('TranscriptionText','')))
return '''
<Response>
<Say>OK</Say>
</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=voicemail.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/record
, as we chose record
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 Voicemail 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:
- Phone IVR 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: