We've seen how to write a text chatbot using the Twilio API for WhatsApp using Ruby, but WhatsApp also supports sending and receiving location data via their API. In this post we are going to see how to build a WhatsApp bot that can receive and respond to location messages.
We'll build a weather bot so that you can send your location to the bot to get your local weather forecast.
What you'll need
To code along with this post and build your own location-aware WhatsApp bot you will need:
- Ruby and Bundler installed
- ngrok so we can expose our local endpoints so that we can receive incoming webhooks
- A WhatsApp account
- A free Dark Sky API account to make weather forecast requests
- A Twilio account (if you don't have one, sign up for a new Twilio account here and receive $10 credit when you upgrade)
Configure your WhatsApp sandbox
If you want to launch a bot on WhatsApp you need to get approval from WhatsApp, but Twilio allows you to build and test your WhatsApp bots using our sandbox. Let's start by configuring the sandbox to use with your WhatsApp account.
If you haven't set up your WhatsApp sandbox, head to the Twilio console WhatsApp Sandbox and follow the instructions in my previous post. When you've received a message back over WhatsApp you're ready to continue.
Setting up the Ruby application
At the end of the last post, we had a good base application which we can start with this time. Get this setup by cloning it from GitHub and changing into the directory:
git clone https://github.com/philnash/ruby-whatsapp-bots.git
cd ruby-whatsapp-bots
Install the dependencies:
bundle install
Copy config/env.yml.example
to config/env.yml
and fill in your Twilio Auth Token (available in your Twilio console).
Copy the base
directory to one called location_bot
.
cp -R base location_bot
Open location_bot/config.ru
and change the last line to:
run LocationBot
Open location_bot/bot.rb
. Change the class name to LocationBot
and check out the current contents:
require "sinatra/base"
class LocationBot < Sinatra::Base
use Rack::TwilioWebhookAuthentication, ENV['TWILIO_AUTH_TOKEN'], '/bot'
post '/bot' do
response = Twilio::TwiML::MessagingResponse.new
response.message body: "This is the base bot. Edit me to make your own bot."
content_type "text/xml"
response.to_xml
end
end
This sets up an endpoint at /bot
to receive incoming webhooks from Twilio and respond with a message in a TwiML response. It also protects the endpoint from outsiders by vallidating the signature. Run the application with:
bundle exec rackup location_bot/config.ru
and once your application server boots up then everything is ready to go.
Receiving location messages in WhatsApp
Whenever a user sends a message to your WhatsApp number Twilio will turn that into a webhook to the endpoint you set up in the console. The webhook is an HTTP request with all the details about the incoming message. Previously we worked with the message Body but this time we want to deal with location messages.
WhatsApp allows a user to send text or location, but not both at the same time. When a user sends a location the incoming webhook will include Latitude
and Longitude
parameters. You might also receive Label
and Address
parameters if the user chooses a named location to share. We can access all of these bits of data in Sinatra via the params
hash.
We can take a look at this data by logging it out. Add the following to location_bot/bot.rb
:
post '/bot' do
puts "Latitude: #{params["Latitude"]}"
puts "Longitude: #{params["Longitude"]}"
puts "Label: #{params["Label"]}"
puts "Address: #{params["Address"]}"
response = Twilio::TwiML::MessagingResponse.new
response.message body: "This is the base bot. Edit me to make your own bot."
content_type "text/xml"
response.to_xml
end
Restart the application or run it with:
bundle exec rackup location_bot/config.ru
To test this we'll need to open up a tunnel to our server running on our machine. I recommend using ngrok for this. If you don't have it installed follow the instructions on ngrok.com and when you're ready, run:
ngrok http 9292
This will open a tunnel pointed to localhost port 9292 which is where Sinatra is hosted by default using rackup
. You will find a public ngrok URL that now points at your local application. Open up the WhatsApp Sandbox in your Twilio console and enter that URL plus the path /bot
into the field labelled When a message comes in.
Send the Sandbox number a location message, by hitting the plus button and then choosing the location to share.
In the terminal you will see the location information printed out.
Let's take this location data and use it to return a localised weather report. For this blog post, we'll use the Dark Sky API. It's a simple API and you can sign up for a free API key here. Once you have your API key, open config/env.yml
and add the Dark Sky API key under your Twilio auth token.
TWILIO_AUTH_TOKEN: YOUR_TWILIO_AUTH_TOKEN
DARK_SKY_API_KEY: YOUR_DARK_SKY_API_KEY
Let's write a small class that can make HTTP requests to the API to find the weather for the latitude and longitude that we are getting from the WhatsApp location message.
Calling the Dark Sky API
The Dark Sky API URL format looks like this:
https://api.darksky.net/forecast/{API_KEY}/{LATITUDE},{LONGITUDE}?{OPTIONS}
In the last blog post we used the http.rb library to make requests to various dog and cat APIs. We can use that again to make requests to the Dark Sky API. Let's build a small class to do this. At the bottom of location_bot/bot.rb
add the following:
class DarkSky
def initialize(api_key)
@api_key = api_key
end
BASE_URL = "https://api.darksky.net/forecast/"
def forecast(lat, long)
end
end
This is a good starting point that gives us a class we can initialize with our API key. It has stored the base URL for the API and we have a forecast method that will take the latitude and longitude. Complete the forecast method with the following:
def forecast(lat, long)
url_options = URI.encode_www_form({ :exclude => "minutely,daily,alerts,flags", :units => "si" })
url = "#{BASE_URL}#{@api_key}/#{lat},#{long}?#{url_options}"
response = HTTP.get(url)
result = JSON.parse(response.to_s)
end
The first line of the method includes a few request parameters. In this case we are requesting SI (metric) units for the responses and we are excluding some of the detail of the response. We then build up the URL encoding the query parameters using the standard library's URI#encode_www_form
method. We pass the URL to the HTTP.get
method to get a response and then parse that response body as JSON, returning the result.
We can now use this class in our bot.
Sending a weather forecast based on WhatsApp location
Return to the code that responds to the incoming WhatsApp message. Now we want to check if the incoming message is a location message by checking for the Latitude
and Longitude
parameters. If it is we'll make the call to the Dark Sky API using the class we just wrote but if it isn't we'll return a message asking for the user's location.
Start by removing the debugging puts
calls and then build up the conditional.
post '/bot' do
response = Twilio::TwiML::MessagingResponse.new
if params["Latitude"] && params["Longitude"]
dark_sky = DarkSky.new(ENV['DARK_SKY_API_KEY'])
forecast = dark_sky.forecast(params["Latitude"], params["Longitude"])
forecast_message = "It is currently #{forecast["currently"]["summary"].downcase} with a temperature of #{forecast["currently"]["temperature"].to_s.split(".").first}ยฐC.\nForecast: #{forecast["hourly"]["summary"].downcase}"
response.message body: forecast_message
else
response.message body: "To get a weather forecast, send your location from WhatsApp."
end
content_type "text/xml"
response.to_xml
end
Above we use the forecast returned by the Dark Sky API to build up a message made up of the current weather summary and temperature followed by the hourly forecast summary. This looks like:
Success! We received a WhatsApp location message and turned that into a weather forecast for that location.
Power up your bots with location
Location can be hugely useful with bots and this is a feature that the Twilio API for WhatsApp has over other channels like SMS. If you want to see the entire code for this bot and others, check out the GitHub repo here. You can find the location bot under the location directory.
There are loads of other things you could do with a location-aware bot. You could find nearby stores, send directions, or provide local insights for your users. I'd love to hear any ideas you have for location based bots too. Let me know in the comments or on Twitter at @philnash.