As Shiny Server gets more and more production ready, developers are adding new features that typically aren't easy to build in—examples include automated phone notifications through Twilio. Twilio allows your application to send phone calls and text messages to users of your application when a triggering event occurs. For example, we use it to call our clients of potential flaws, taints, and contamination's in their products in real time.

This tutorial will teach you to integrate Twilio with a basic Shiny application which places a sample call to your users. This tutorial will leave you with a working application which you can build into your own needs. This tutorial will require the following:

  • R
  • Shiny
  • some knowledge of servers including FTP and APIs

This tutorial focuses on Shiny Server, but most of the code could easily be converted to other R based web frameworks or batch scripts. The code for the Shiny Version is located on GitHub.

Our current situation provides is we have a button and field in shiny through which our users can enter their phone number and hit the button to test. Here's how we did it in R. The first thing we needed was a simple input and button in our Ui.R file which was the following code:

 1 div(class="message_test",
 2     div(class="center",
 3         textInput("phone_number", label="", value="")),
 4     div(class="center",
 5         HTML("<div class='center'>
 6             <button id='send_test_call' class='action-button btn btn-default '>
 7             Send test Call</button>
 8             </div>")
 9         )
10     )

Twilio works by issuing a post request through either code (such as PHP) or a REST API, which is what this tutorial will use. The phone number and account information is sent through the API. Attached to the API call is a URL that Twilio will go to in order to grab the XML or TwiML. This is the data specifying what the text-to-speech says, as well as other configurations. Check out the TwiML information here. Our hardest decision for us was where to store the XML file as it needed to be programatically made and uploaded each time the user tests our system. At first the choice was going to be Amazon S3, but lack of tools and compatibility with R got in the way. We are working on building an open source library to utilize AWS resources directly from R—check back on this blog for its release. Until then, an FTP server works fine. Building a simple FTP server is out of scope of this article, but is quite easy. We suggest this additional tutorial here.

Now that the frontend is built, let's build the backend. In our server.R file, we have an observe function which will check for the button being clicked input$send_test_call and that the phone number isn't null.

observe({
    if(!is.null(input$send_test_call)) {
        if(!is.null(isolate(input$phone_number)) &&
             isolate(input$phone_number) != "") {
             time <- now()

### We make the message
            message <- paste("Greetings, it is currently, ",time," . This message can be made to say anything.")

### Now we build the XML File make sure you have require(XML)
            xml_message <- newXMLNode("Response")
            newXMLNode("Say", message, attrs=c(voice='alice',
                       language='en-gb', loop=3), parent=xml_message)

### Save the file temporarily.
### We call it based of the date and username
            filename <- paste0(Sys.Date(), "_", gsub(" ", "_", username(), ".xml"))
            f <- tempfile()

            saveXML(xml_message, file=f,
                    prefix='<?xml version="1.0" encoding="UTF-8" ?>')

### function to upload it to somewhere accessible.
    ftpupload(f,paste0("ftp://<your-location>",filename))

### Twilio RCurl Rest Connection
            POST('https://api.twilio.com/2010-04-01/Accounts/
            <Your-Twilio-account-id>/Calls.json',
            body = list(
            Url=paste0("https://<where-your-xml-file-is-directory>",filename),
            From="+1<Your-Twilio-account-phone-number>", To=paste0("+1",isolate(input$phone_number))),
            config=authenticate("<Your-Twilio-account-id>",
            "<Your-Twilio-account-auth-token>", type="basic"))
        }
    }
})

Basically, you wait until the button is pressed and you make sure that a phone number has been entered. It's a bit exhausting, but you can adapt the message to be whatever based on data you want. Then you next create xml with the message. Upload that to a publicly openable location and finally use the Twilio API to access that point. If doing this in pure R without Shiny, the only things you need is the following:

require(XML)
require(RCurl)
require(httr)

time <- now()

### Phone number
call_from <- "+17248675309"

### We make the message
message <- paste("Greetings, it is currently, ",time," . This message can be made to say anything.")

### Now we build the XML File make sure you have require(XML)
xml_message <- newXMLNode("Response")
newXMLNode("Say", message, attrs=c(voice='alice',
           language='en-gb', loop=3), parent=xml_message)

### Save the file temporarily.
### We call it based of the date and username
filename <- paste0(Sys.Date(), "_", gsub(" ", "_", username(), ".xml"))
f <- tempfile()

saveXML(xml_message, file=f,
prefix='<?xml version="1.0" encoding="UTF-8" ?>')

### function to upload it to somewhere accessible.
ftpupload(f,paste0("ftp://<your-location>",filename))

### Twilio RCurl Rest Connection
POST('https://api.twilio.com/2010-04-01/Accounts/
<Your-Twilio-account-id>/Calls.json',
body = list(
Url=paste0("https://<where-your-xml-file-is-directory>",filename),
From=call_from, To=paste0("+1",isolate(input$phone_number))),
config=authenticate("<Your-Twilio-account-id>",
"<Your-Twilio-account-auth-token>", type="basic"))

And that is it. That's how we got Twilio to work in R and in Shiny. We are going to expand a lot on this simple version to alert our users in a custom manner. Adapt this to your needs. Just pull the code off the GitHub repo (GitHub) and run the code with your info added in.


Evan Farrell

Application Engineer

Evan studied Information Sciences and Technology at Penn State, and continues to learn everything he can about technology. Before joining AFS, Evan started learning everything to do with beer and beer production while homebrewing. When not coding, you will find him climbing 14ers, hiking, and doing anything else in the outdoors.

comments powered by Disqus