Build a collaborative playlist over WhatsApp with Rails, Twilio, and Spotify
Tags: ruby, rails, twilio, spotify
Published: 2018-08-28 23:21:10

---
title: Build a collaborative playlist over WhatsApp with Rails, Twilio, and Spotify
description: Combine the Twilio API for WhatsApp with the Spotify API using Ruby on Rails to create a collaborative playlist.
published: true
tags: ruby, rails, twilio, spotify
canonical_url: https://www.twilio.com/blog/collaborative-playlist-whatsapp-rails-twilio-spotify
cover_image: https://thepracticaldev.s3.amazonaws.com/i/yalaxdvwgp85qoualzjy.jpg
---

Sharing music over playlists is a great way to discover new and old music. Spotify has collaborative playlists, but I don't like how they let your friends re-order and delete songs from the list. We can fix this by building our own collaborative playlist that only allows additions using the [Spotify Web API](https://developer.spotify.com/documentation/web-api/). With the [Twilio API for WhatsApp](https://www.twilio.com/whatsapp) we can let our friends send in a song whenever they are struck by inspiration.

In this post we are going to build a WhatsApp bot that can do all of the above using Ruby on Rails.

## Getting started

To build this application we will need a few things:

- [Ruby version 2.5.1 installed](https://www.ruby-lang.org/en/downloads/)
- Rails and bundler, which can be installed with `gem install rails bundler`
- A Twilio account ([sign up for a free Twilio account here](https://www.twilio.com/try-twilio))
- The [WhatsApp sandbox](https://www.twilio.com/console/sms/whatsapp/sandbox) set up in our Twilio account
- [A Spotify developer account](https://developer.spotify.com/)
- [ngrok](https://ngrok.com), so we can [tunnel through to our locally developed application to use webhooks](https://www.twilio.com/blog/2015/09/6-awesome-reasons-to-use-ngrok-when-testing-webhooks.html)

### Spotify API Credentials

To work with the Spotify API we will need to generate some API credentials. From the [Spotify developer dashboard](https://developer.spotify.com/dashboard/applications) create a new application and follow the 3 step process.

1. First, fill in some details about the app. Name it, I called mine WhatsPlaying, provide a short description and check what app type it is, I chose "website"
2. Next we are asked if this is a commercial integration; choose "no"
3. Finally, check the agreements

Once these steps are complete we will see the application dashboard. We will need the Client ID and Client Secret from this page.

There's one more thing we need to do here. Click on "edit settings", enter the redirect URL `http://localhost:3000/auth/spotify/callback` and click "add".

Now that credentials are sorted, we can start to build our app.

## Preparing the project

I've started the project off, so [clone or download the repo from GitHub](https://github.com/philnash/whats_playing) and checkout the `getting-started` branch.

```bash
git clone -b getting-started https://github.com/philnash/whats_playing.git
```

Change into the directory and install the project's dependencies.

```bash
cd whats_playing
bundle install
```

Copy the `config/env.yml.example` file to `config/env.yml` (we're using envyable to [manage environment variables for the application](https://www.twilio.com/blog/2015/02/managing-development-environment-variables-across-multiple-ruby-applications.html)). Open `config/env.yml` and fill in the Spotify Client ID and Client Secret we just generated. We'll fill in the rest soon.

We're now going to generate a Spotify access token for your account on Spotify as well as a new playlist, called "WhatsPlaying", that we are going to use as our collaborative playlist. Run the Rails app with:

```bash
bundle exec rails server
```

Open the app at [http://localhost:3000](http://localhost:3000). There will be a link to "Sign in with Spotify". Click the link and follow the OAuth process. All of this is handled by the [RSpotify gem](https://github.com/guilhermesad/rspotify) and [omniauth](https://github.com/omniauth/omniauth), you can check out the config required to make this work in `config/initializers/rspotify.rb`.

![](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/-cLIvjXZ1zUy1dnYRd6rWFkXIhh0AO33g1YJCEHZAb_Q6x.width-500.png)

Once you have been directed back to the application you will see your Spotify user ID, access token, refresh token, and playlist ID on the page in front of you. Copy these into `config/env.yml`, restart the application, and open [http://localhost:3000](http://localhost:3000) again. You will find the playlist that the script just created empty and embedded in the page. Now it's time to build the integration between WhatsApp, Twilio, and Spotify.

## Getting to grip with webhooks

When you send a message to a WhatsApp number that you control with Twilio, Twilio will send an HTTP request, or webhook, to an application URL we provide. We will use this to respond to incoming messages and add songs to our playlist. Let's set up our webhook endpoint first.

To do so we will install the [twilio-ruby gem](https://github.com/twilio/twilio-ruby/). This will make writing [TwiML](https://www.twilio.com/docs/sms/twiml), the XML that Twilio understands and takes direction from, easier. Open up the `Gemfile` and under the line that includes RSpotify, add twilio-ruby:

```ruby
gem 'rspotify', '~> 2.1.1'
gem 'twilio-ruby', '~> 5.12.3'
```

Install the gem by running `bundle install` in the project directory again.

We now need a new controller to handle the incoming webhooks. Use the Rails generator to create a new one:

```bash
bundle exec rails generate controller twilio/messages
```

Open up the newly generated `app/controllers/twilio/messages_controller.rb` and start by removing the default [Rails CSRF protection](https://guides.rubyonrails.org/security.html#csrf-countermeasures). Webhooks are, by definition, a cross site request and we should secure them by [validating that the request came from Twilio](https://www.twilio.com/docs/usage/security). We can do so later using the [Twilio request validation Rack middleware provided by the twilio-ruby gem](https://www.twilio.com/blog/2014/09/securing-your-ruby-webhooks-with-rack-middleware.html).

```ruby
class Twilio::MessagesController < ApplicationController
  skip_before_action :verify_authenticity_token
end
```

Next we need an action to handle the incoming webhook requests. In the context of Rails, I consider webhooks to be `create` actions, so make a new `create` method.

```ruby
class Twilio::MessagesController < ApplicationController
  skip_before_action :verify_authenticity_token
  def create
  end
end
```

To make sure we're on track, let's make this respond with a simple message first. We'll use the TwiML generation capability of the twilio-ruby gem to respond to any incoming message with the customary "Hello World!".

```ruby
class Twilio::MessagesController < ApplicationController
  skip_before_action :verify_authenticity_token
  def create
    response = Twilio::TwiML::MessagingResponse.new
    response.message(body: "Hello World!")
    render xml: response.to_xml
  end
end
```

This action needs a route too. Open `config/routes.rb` and add the following:

```ruby
Rails.application.routes.draw do
  namespace :twilio do
    post 'messages', to: 'messages#create'
  end
  # other routes
end
```

This is all we need to get a response, so let's hook up the Twilio WhatsApp sandbox to this application.

Start your app:

```bash
bundle exec rails server
```

Start up an ngrok tunnel so that the Twilio webhook can reach your app with a public URL. In a new terminal window on the command line run:

```bash
ngrok http 3000
```

Take the URL ngrok generates, add the `/twilio/messages` path and enter it as the URL when "A MESSAGE COMES IN"  for the [WhatsApp channel](https://www.twilio.com/console/sms/whatsapp/sandbox).

![](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/mOcd_C7CBBLk1HcW4JB11qrpaKD6I0_o2vJlWe4cZDtoYi.width-500.png)

Send a message to the WhatsApp sandbox number and you will get a response back saying "Hello World!". Now it's time to hook up the Spotify API.

## Connecting with the Spotify API

We're going to build a conversational interface to interact with the Spotify API. We'll assume that when a user sends their first message it is a song title they want to add to the playlist. We'll use the Spotify API to search for that song and send the user back the first result to see if that's what they wanted. If they confirm with a positive response then we'll add the track to the playlist. If they say no we'll ask them to search again. If they send anything else back we can assume that is a new search.

We're using the excellent [RSpotify](https://github.com/guilhermesad/rspotify) library for this application, but let's create a class that is more suited to our use. Create a new directory in the `app` directory called `services` and a file in there called `spotify.rb`. Open `spotify.rb` and create a new class:

```ruby
class Spotify
end
```

In the initializer we will use the credentials we created earlier to create an authenticated user.

```ruby
class Spotify
  def initialize(user_id, user_token, user_refresh_token)
    @user = RSpotify::User.new({
      'id' => user_id,
      'credentials' => {
        'token' => user_token,
        'refresh_token' => user_refresh_token
      }
    })
  end
end
```

Searching for a track is performed by passing a query to the `RSpotify::Track.search` method and we only want to return the first result.

```ruby
class Spotify
  def initialize(user_id, user_token, user_refresh_token)
  end

  def track_search(query)
    RSpotify::Track.search(query).first
  end
end
```

We can add tracks to a playlist by getting the track ID from the result of `track_search`, searching for the playlist by ID, and then adding the track to the playlist.

```ruby
class Spotify
  def initialize(user_id, user_token, user_refresh_token)
  end

  def track_search(query)
  end

  def add_to_playlist(playlist_id, track_id)
    playlist = RSpotify::Playlist.find(@user.id, playlist_id)
    playlist.add_tracks!([track_id])
  end
end
```

Let's use this class in our controller now, open `app/controllers/twilio/messages_controller.rb` again. At the start of the `create` method, get the body of the message, which we'll use as the search term and instantiate a new Spotify object using the credentials in the environment.

### Searching for songs

We'll start by testing the search functionality and returning the name and artist of the first track we find. Spotify returns the artists as a list as there could be more than one responsible for a track. We can turn that into a sentence with a bit of ActiveSupport trickery. [WhatsApp also supports some basic message formatting](https://faq.whatsapp.com/en/android/26000002/); if we surround our track and artist names with underscores, they will appear in italics.

```ruby
class Twilio::MessagesController < ApplicationController
  skip_before_action :verify_authenticity_token
  def create
    body = params['Body']
    spotify = Spotify.new(ENV['SPOTIFY_USER_ID'], ENV['SPOTIFY_TOKEN'], ENV['SPOTIFY_REFRESH_TOKEN'])

    track = spotify.track_search(body)
    message = "Did you want to add _#{track.name}_ by _#{track.artists.map(&:name).to_sentence}_?"

    response = Twilio::TwiML::MessagingResponse.new
    response.message(body: message)
    render xml: response.to_xml
  end
end
```

Restart the application and send a message, searching for a track you want to hear. You should get a message back listing the track you wanted. If you get a different song, try adding the artist or album to narrow down the search.

![](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/bA3Sdd8dpQp03o8M8PfVIFv5Hm-L19JSBaeuItZpblztY0.width-500.png)

We do need to to handle the case if we don't find any songs though.

```ruby
  def create
    body = params['Body']
    spotify = Spotify.new(ENV['SPOTIFY_USER_ID'], ENV['SPOTIFY_TOKEN'], ENV['SPOTIFY_REFRESH_TOKEN'])

    track = spotify.track_search(query)
    if track
      message = "Did you want to add _#{track.name}_ by _#{track.artists.map(&:name).to_sentence}_?"
    else
      message = "I couldn't find any songs by searching for '#{body}'. Try something else."
    end

    response = Twilio::TwiML::MessagingResponse.new
    response.message(body: message)
    render xml: response.to_xml
  end
```

### Adding to the playlist

We can handle search requests. Now we need to be able to add the track if the user responds with a positive answer.

Like SMS conversations on Twilio, we can [use the session to retain data between messages](https://support.twilio.com/hc/en-us/articles/223136287-How-do-Twilio-cookies-work-). For this application we can store the track URI, which identifies it to Spotify, in the session. Then, if we have a track in the session we can look for a "yes" in the response. If we get "yes", we add the track to the playlist and clear the session.

```ruby
  def create
    body = params['Body']
    spotify = Spotify.new(ENV['SPOTIFY_USER_ID'], ENV['SPOTIFY_TOKEN'], ENV['SPOTIFY_REFRESH_TOKEN'])

    if session[:track]
      answer = body.split(' ').first.downcase.strip
      if ['yes', 'yeah', 'yep', 'yup', '👍'].include? answer
        message = "OK, adding that track now."
        spotify.add_to_playlist(ENV['SPOTIFY_PLAYLIST_ID'], session[:track])
        session[:track] = nil
      end
    end

    if !message
      track = spotify.track_search(body)
      if track
        session[:track] = track.uri
        message = "Did you want to add _#{track.name}_ by _#{track.artists.map(&:name).to_sentence}_?"
      else
        message = "I couldn't find any songs by searching for '#{body}'. Try something else."
      end
    end
    response = Twilio::TwiML::MessagingResponse.new
    response.message(body: message)
    render xml: response.to_xml
  end
```

I added some other options for saying "yes" including the emoji thumbs up 👍. [Who doesn't love emoji?](https://www.twilio.com/blog/2018/08/emoji-translations-twilio-api-whatsapp-node-js.html)

You can test this path through the application by sending a song to the WhatsApp number and following up with "yes". Then check that the song appears in the playlist.

### Other responses

To complete our Spotify WhatsApp bot we need to handle the other potential responses. If the user sends back a negative response, then we want to ask what track they want to add instead. Any other response and we'll assume it's a new search.

```ruby
  def create
    body = params['Body']
    spotify = Spotify.new(ENV['SPOTIFY_USER_ID'], ENV['SPOTIFY_TOKEN'], ENV['SPOTIFY_REFRESH_TOKEN'])

    if session[:track]
      answer = body.split(' ').first.downcase.strip
      if ['yes', 'yeah', 'yep', 'yup', '👍'].include? answer
        message = "OK, adding that track now."
        spotify.add_to_playlist(ENV['SPOTIFY_PLAYLIST_ID'], session[:track])
        session[:track] = nil
      elsif ['no', 'nah', 'nope', '👎'].include? answer
        session[:track] = nil
        message = "What do you want to add?"
      end
    end

    if !message
      track = spotify.track_search(body)
      if track
        session[:track] = track.uri
        message = "Did you want to add _#{track.name}_ by _#{track.artists.map(&:name).to_sentence}_?"
      else
        message = "I couldn't find any songs by searching for '#{body}'. Try something else."
      end
    end
    response = Twilio::TwiML::MessagingResponse.new
    response.message(body: message)
    render xml: response.to_xml
  end
```

Restart the app one more time and start searching and confirming or denying tracks. You can see the songs you add appear on your playlist and in the embedded playlist on the homepage of your app too.

## A bot and a brand new playlist

With the Spotify API and the Twilio API for WhatsApp we've just built a collaborative playlist. The interaction is quite simple right now, but you could build on this to include voting on songs to move them up in the playlist or ways to better share the playlist with more friends.

If you want to see all the code for this application, check out [the repo on GitHub](https://github.com/philnash/whats_playing).

I thought it would be fun to see what you would add to the playlist, so I've deployed my version of this app. You can add songs to it by connecting to my WhatsApp sandbox by messaging " **join beaver-wrasse**" to this Twilio WhatsApp number: +14155238886. Then start sending me all your favourite songs! Check out [the playlist online here](https://twilio-whatsapp-spotify.herokuapp.com/)!

Got any ideas for collaborative applications you can build with WhatsApp and Twilio? Let me know in the comments, [on Twitter](https://twitter.com/philnash) or by email at philnash@twilio.com.