Create your own Slack bots -- and web APIs -- in R

Our step-by-step tutorial shows you how

1 2 Page 2
Page 2 of 2

Step 3: Host the R API remotely

There are several ways to host the API, as there are for any web-based application: Add it to an existing internal server, upload it to your organization's external server, use a cloud service with or without a virtual machine, or use a traditional web host. I can't cover them all, but the basics are pretty similar (unless you're using a web host where you don't have root access to the machine; in that case, additional tweaks may be needed). Note that if you want to deploy this as a Docker application, there is a Docker image for the R plumber package.

One of the easiest ways to host this externally is with Digital Ocean. Set-up is simple, and you can use the smallest "droplet" at $5/month.

Software engineer and R consultant Dean Attali has excellent step-by-step instructions on how to set up R at Digital Ocean. He outlines more than you'll need for this project, since his goal is to get up and running with the R Shiny Web framework (not needed for this project).

I suggest you follow Attali's steps 1 through 6:

  • Sign up with DigitalOcean
  • Create a new droplet
  • Log into your new server
  • Set up some security and authentication basics
  • Access your droplet from a browser
  • Install R and necessary packages -- don't forget to install plumber and quantmod, but you don't need devtools or shinyjs, which appear in Attali's instructions, unless you want them

You may want to go on to step 7 and install your own cloud version of RStudio Server, if you prefer that to working in a typical R command-line environment. You definitely won't need a Shiny server for this Slack project.

Once you've got your Digital Ocean server, create an app directory for your app's two R files and upload them to the server. At last, you can test your API.

Run the myserver.R file with Rscript myserver.R from your droplet's command line while in your app directory. You should see a message "Starting server to listen on port 8000" (or whatever port you chose).

The URL for your API will look something like http://xxx.xxx.xxx.xxx:8000/stockquote?thesymbol=STOCKSYMBOL (with xxx.xxx.xxx.xxx being the IP address of your droplet). To test the API, open a browser and head to http://your.droplet.ip.address:port/APIpath?thesymbol=MSFT and you should get something like ["Price for MSFT is $64.49 as of 2017-02-17 12:34:00"] in return. That APIpath is what you set up with the #* @get in the first line of your R function file. It is not the name of your app directory on the server.

Now that you know the API is working, stop that manual server with Ctrl-C and head to Trestle's instructions on how to web-host a plumber application. You should have already installed R by following Attali's How to get your very own RStudio Server and Shiny Server with DigitalOcean post, so the remaining steps are:

  • Install the Node.js JavaScript framework
  • Install npm (the Node.js package manager)
  • Install pm2 (a process manager for Node.js)
  • Create a third R script file that pm2 can run
  • Register the plumber server with pm2
  • Set up pm2 so it will run your server on boot-up

Fortunately, most of these tasks require just one or two lines of code, and they're all explained in how to web-host a plumber application. If you have trouble installing pm2 on your Ubuntu droplet, try installing node-legacy with sudo apt install nodejs-legacy and then installing pm2 with sudo npm install -g pm .

If you're wondering why you need the Node.js JavaScript framework to run an R server, the answer is: To have access to Node's pm2 process manager. As Allen explains in the plumber documentation, basic R was designed for one user at a time, but your API might need to handle multiple sessions at once. Process managers such as Node's pm2 or Ruby's Phusion Passenger can allow R (and other platforms) to do this.

That third script file, which I'll call run-myserver.R, should look something like:


#!/usr/bin/env Rscript

library(plumber)
myplumobject <- plumb('myserver.R')
myplumobject$run(port=8000)

'myserver.R' in line 2 needs to be whatever you named your plumber server file. The port in line 3 can be any port that's available to your application. (If you're using a shared hosting service such as Webfaction, your application may only have one port available; make sure you know what that is.)

Upload that file to the droplet's app directory and make sure it's executable -- in other words, that it has the right Unix permissions. You can do this by running the command chmod 755 run-myserver.R while in the app directory.

If you followed Allen's instructions to register your run-myserver.R file with pm2 using pm2 start --interpreter="Rscript" /path/to/run-myserver.R and pm2 save, the server should keep running. Note that you can't run the myserver.R file manually with Rscript myserver.R anymore if pm2 is running the server, since only one service can run on the port at a time.

Voila! You've turned some R code into a publicly available API. There are just a few more tweaks to get it working as a Slack bot -- plus a way to make sure only your Slack channel uses it.

Step 4: Connect the API with Slack

To set up a "custom slash command" in Slack, head to the custom integrations portion of your channel, which you should be able to find directly at https://YOURTEAMNAME.slack.com/apps/manage/custom-integrations, or via the dropdown menu next to your channel name at the top left of the app screen -- click on Customize Slack > Menu > Configure Apps > Custom Integrations. Whichever way you get there, click on Slash Commands. You should see a green bar that says Add Configuration.

Slack custom integration options Screenshot of Slack menu

Where in Slack's custom integration menu you will find the place to add a custom slash command.

Click that, and at last you'll get to the screen to add a slash command. Choose a word for people to use to execute your command: /stockprice or /stockquote, perhaps, for a stock-price app. Start with a slash and then use only lower-case characters. Click the Add Slash Command button.

Adding a Slack custom slash command Screen shot of Slack menu

Slack page for adding a custom slash command.

Enter the root URL for your R API, such as http://xxx.xxx.xxx.xxx:8000/stockquote, and choose GET as the Method. Slack will generate a unique token for this slash command. That token isn't required, but you can use it to add to your R function to make sure that only authorized requests from Slack can use the API -- something you might want to do for an app using internal, sensitive data.

To restrict access, wrap your basic function with an if-else statement such as:


#* @get /stockquote
newGetQuote <- function(thesymbol, token){
  if(token == "YOURSLACKTOKEN"){
  myresults <- quantmod::getQuote(thesymbol)
  if(is.na(myresults[1,1])){
    mytext <- paste0("A price is not available for ", thesymbol)
  } else {
    mytext <- paste0("Price for ", thesymbol, " is $", myresults[1,2], " as of ", myresults[1,1])
  }
  } else {
  mytext <- "Service not available"
}
  return(mytext)
}

You can test this with a URL such as http://xxx.xxx.xxx.xxx:8000/stockquote?thesymbol=GOOG&token=YOURSLACKTOKEN. Important: If you upload a new version of any of the R files onto your droplet, you need to stop and restart your plumber server in order for any changes to take effect. pm2 updatePM2 is an easy way to update the process if you're not worried about interfering with other processes on the system.

Slack gives you the option of customizing the name of the bot that prints out responses -- I called mine stock-bot -- or you can use the bot default.

The last piece of the integration is making sure that the text your Slack users enter after your new /stockprice slash command is sent from Slack to your API. Slack explains the GET or POST variables that it uses with slash commands with an example:


token=3qORvogpJ0JPW2f1t6QXigPO
team_id=T0001
team_domain=example
channel_id=C2147483705
channel_name=test
user_id=U2147483697
user_name=Steve
command=/weather
text=94070
response_url=https://hooks.slack.com/commands/1234/5678

According to the example, text is the variable that stores what the user types in after the slash command. That means we'll need to change the newGetQuote function, because it will be getting a variable called text and not one called thesymbol. I'd also suggest adding a line to trim white space from before and after the user's input, with R's trimws() function, such as:


#* @get /stockquote
newGetQuote <- function(text, token){
  thesymbol <- trimws(text)
  if(token == "YOURSLACKTOKEN"){
  myresults <- quantmod::getQuote(thesymbol)
  if(is.na(myresults[1,1])){
    mytext <- paste0("A price is not available for ", thesymbol)
  } else {
    mytext <- paste0("Price for ", thesymbol, " is $", myresults[1,2], " as of ", myresults[1,1])
  }
  } else {
  mytext <- "Service not available"
}
  return(mytext)
}

Upload the revised stockfunction.R into your droplet's app directory, restart the server with pm2 updatePM2 and then test the new version in your browser with a URL that looks something like http://xxx.xxx.xxx.xxx:8000/stockquote?text=GOOG&token=YOURSLACKTOKEN.

If that's working OK, fill out any additional optional fields for your slash command in Slack -- uploading a photo if you'd like, or adding some help text -- and then save it.

Your Slack bot should now be operational, so anyone in your team should able to type /stockprice AAPL (or whatever you named your slash command in Slack if not stockprice) and receive the latest stock price for Apple.

You probably noticed that the response came back in brackets and quotation marks, like ["Price for AAPL is $135.485 as of 2017-02-17 03:04:00"]. That's because the default response from the plumber package is json. If this bugs you and you'd rather your results come back as plain text, you need to add a serializer to define another format for plumber's response.

There isn't much documentation for this functionality yet, but adding


#* @serializer contentType list(type="text/plain")

at the very top of my function file, before the stock function's #* @get /stockquote initial line, sends back plain text without the [""] JSON wrapper.

These steps should get you started on creating a lot of other Slack bots -- and Web APIs -- with R code. After all, once you've got your plumber environment already set up, adding additional APIs and slash commands is easier, as long as you've got other available ports on your server.

If you want some ideas on other data you can pull into an API and Slack slash command this way, check out my searchable table: These R packages import sports, weather, stock data and more.

Have comments on this R how-to? You can find Sharon on Twitter @sharon000 and by email at smachlis@computerworld.com.

Copyright © 2017 IDG Communications, Inc.

1 2 Page 2
Page 2 of 2
Download: EMM vendor comparison chart 2019
  
Shop Tech Products at Amazon