Build a location-aware WhatsApp weather bot with Ruby, Sinatra and Twilio
Tags: twilio, whatsapp, ruby, sinatra
Published: 2020-03-20 12:34:20

---
title: Build a location-aware WhatsApp weather bot with Ruby, Sinatra and Twilio
published: true
description: WhatsApp 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.
tags: twilio, whatsapp, ruby, sinatra
cover_image: https://twilio-cms-prod.s3.amazonaws.com/images/vH4Jj-ZCocwxofBifY9riDxHkcE6YKpYspnWULEyAsTbOm.width-808.png
---

We've seen [how to write a text chatbot using the Twilio API for WhatsApp using Ruby](https://www.twilio.com/blog/whatsapp-chatbot-ruby-sinatra-twilio), but [WhatsApp also supports sending and receiving location data via their API](https://www.twilio.com/blog/new-rich-features-support-deeper-customer-engagement-twilio-api-whatsapp). 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](https://www.ruby-lang.org/en/downloads/) and [Bundler](https://bundler.io/) installed
*   [ngrok](https://ngrok.com/) so we can [expose our local endpoints so that we can receive incoming webhooks](https://www.twilio.com/blog/2015/09/6-awesome-reasons-to-use-ngrok-when-testing-webhooks.html)
*   A [WhatsApp account](https://www.whatsapp.com/)
*   A [free Dark Sky API account](https://darksky.net/dev/register) 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](https://twil.io/philnash))

## Configure your WhatsApp sandbox

If you want to launch a bot on WhatsApp you need to [get approval from WhatsApp](https://www.twilio.com/whatsapp/request-access), but Twilio allows you to [build and test your WhatsApp bots using our sandbox](https://www.twilio.com/console/sms/whatsapp/learn). 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](https://www.twilio.com/console/sms/whatsapp/learn) and follow [the instructions in my previous post](https://www.twilio.com/blog/whatsapp-chatbot-ruby-sinatra-twilio). When you've received a message back over WhatsApp you're ready to continue.

![The Twilio Sandbox for WhatsApp first screen. It tells you to send a message to the WhatsApp number +1 415 523 8886 with a code that starts with "join".](https://twilio-cms-prod.s3.amazonaws.com/images/2A68cuYsZ8PLQNrRU3baSMuMGOUYdDvNkGum4C8ygiC7MG.width-500.png)

## Setting up the Ruby application

At the end of the last post, we had a [good base application](https://github.com/philnash/ruby-whatsapp-bots) which we can start with this time. Get this setup by cloning it from GitHub and changing into the directory:

```bash
git clone https://github.com/philnash/ruby-whatsapp-bots.git
cd ruby-whatsapp-bots
```

Install the dependencies:

```bash
bundle install
```

Copy `config/env.yml.example` to `config/env.yml` and fill in your Twilio Auth Token (available in your [Twilio console](https://www.twilio.com/console)).

Copy the `base` directory to one called `location_bot`.

```bash
cp -R base location_bot
```

Open `location_bot/config.ru` and change the last line to:

```ruby
run LocationBot
```

Open `location_bot/bot.rb`. Change the class name to `LocationBot` and check out the current contents:

```ruby
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](https://www.twilio.com/docs/sms/twiml). It also protects the endpoint from outsiders by [vallidating the signature](https://www.twilio.com/docs/usage/webhooks/webhooks-security#validating-signatures-from-twilio). Run the application with:

```bash
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 ](https://www.twilio.com/blog/whatsapp-chatbot-ruby-sinatra-twilio)[Body](https://www.twilio.com/blog/whatsapp-chatbot-ruby-sinatra-twilio) 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`:

```ruby
  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:

```bash
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](https://ngrok.com/download) and when you're ready, run:

```bash
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](https://www.twilio.com/console/sms/whatsapp/sandbox) and enter that URL plus the path `/bot` into the field labelled _When a message comes in_.

![The Twilio Sandbox for WhatsApp config page. You should add your ngrok URL into the field labelled "When a message comes in".](https://twilio-cms-prod.s3.amazonaws.com/images/5wb2tdFbLYe41sujXXCf5YFzjwZvocj8wyTEWqavoidPWt.width-500.png)

Send the Sandbox number a location message, by hitting the _plus_ button and then choosing the location to share.

![In the WhatsApp application, press the plus button next to the text input and then choose location from the menu.](https://twilio-cms-prod.s3.amazonaws.com/images/0nftooayvbI09n6mfRfLp7N2wQJ4AhG0VTLXiFf1rCuvgU.width-500.png)

In the terminal you will see the location information printed out.

![A terminal window showing the logs from the request. I sent a message from the Twilio office and it shows the latitude, longitude, label and address.](https://twilio-cms-prod.s3.amazonaws.com/images/HnFiuKOBKIv42ytE2SGZdZ1ky2CYS3UqsdeYuiOdTEq_9C.width-500.png)

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](https://darksky.net/dev). It's a simple API and you can [sign up for a free API key here](https://darksky.net/dev/register). Once you have your API key, open `config/env.yml` and add the Dark Sky API key under your Twilio auth token.

```yaml
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](https://darksky.net/dev/docs#forecast-request) looks like this:

```
https://api.darksky.net/forecast/{API_KEY}/{LATITUDE},{LONGITUDE}?{OPTIONS}
```

In [the last blog post](https://www.twilio.com/blog/whatsapp-chatbot-ruby-sinatra-twilio) we used the [http.rb library](https://github.com/httprb/http) 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:

```ruby
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:

```ruby
  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](https://darksky.net/dev/docs#request-parameters). In this case we are requesting [SI](https://en.wikipedia.org/wiki/International_System_of_Units) [(metric) units](https://en.wikipedia.org/wiki/International_System_of_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](https://docs.ruby-lang.org/en/2.6.0/URI.html#method-c-encode_www_form). 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.

```ruby
  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:

![I sent a location message from the Twilio Melbourne office and received the following reply: "It is currently clear with a temperature of 25°C. Forecast: partly cloudy throughout the day."](https://twilio-cms-prod.s3.amazonaws.com/images/AHalYe9S7H9m0Nzoq3mYDJproNho9DSJU-RD43v65YP0-r.width-500.jpg)

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](https://github.com/philnash/ruby-whatsapp-bots). You can [find the location bot under the location directory](https://github.com/philnash/ruby-whatsapp-bots/tree/master/location).

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](https://twitter.com/philnash).