How to Build a Meme Generator Using Twilio MMS, Imgflip and Sinatra

October 02, 2014
Written by

4edbbd471861334a62000000

When I think of the most hilarious text message conversations I have ever had with my 26-year-old little brother they almost all involve memes. When I’m stuck with a decision between two desirable things, I’m very likely to receive a “why not both?” meme. Working on a seemingly impossible task? There’s a quick “one does not simply…” coming my way. The launch of Twilio MMS made me think about the process involved in sending those memes.

What would make it even easier for my brother to meme-roll me? I didn’t want him to have to switch context out of sending text messages just to generate a meme. The lowest friction way to accomplish the task would be to translate a sentence sent in via SMS into a meme picture returned to his phone. So, this is exactly what I built for the Twilio MMS launch. I want to show you how I built it so you can get started on your own variations.

If you can’t wait because you’re super stoked about generating memes using a text message, I don’t blame you. You can give the meme generator a try by sending the word “list” to one of the following numbers (depending on if you are in the US or Canada):

United States: (215) 240-7664

Canada: (587) 410-6363

Now it’s time to…

AllTheMemes

What you will need

How it works

Meme Workflow

The app will include a list of meme formats that it will match incoming text messages against. For instance, an incoming text starting with the words “Brace yourselves” will match the “Brace yourselves” meme. The user can also text the word “list” to be presented with a list of valid meme formats. Any text message that is not “list” and does not match a meme format will return a text message indicating that the meme was not recognized and to use the “list” keyword to find valid formats.

To make all of this happen we’ll use a Sinatra server with the twilio-ruby helper library to handle incoming texts. If a meme is matched we’ll make a REST request to the Imgflip API to create a meme and use the resulting image URL to send an MMS back to the user.

If you want to see this in action before digging into the code, you can deploy your own instance of this project to Heroku with the handy Heroku Button below. (Note: Make sure to set your Imgflip API credentials in the environment variables section.)

Deploy

The full project code is available if you want to follow along: Github

Setting up the project

To get started on this project you are going to need to have Ruby and Ruby Gems installed. If you are on a Mac, this should already be the case. For Windows users, I would recommend checking out Ruby Installer. For the Linux users that might need a refresher on package management, here’s a guide for using apt-get in Ubuntu.

Now that we have that prerequisite squared away, open up a terminal window and create a new folder called “memegen”. This directory is where we’ll be putting all of our application code so change into this directory. To make this project work we’re going to need to install some Ruby Gems:

sudo] gem install sinatra twilio-ruby unirest

Next, we’ll create the file that will hold our application:

touch app.rb

 

We will build the meme generator using Sinatra which is a lightweight Ruby web framework. We’ll also use the Twilio Ruby gem to interact with the Twilio APIs just to make things a little easier to work with. I’m also using the Unirest gem to make REST calls to the Imgflip API but you are free to make those REST calls another way if you have a preference (e.g. Rest Client).

Our project structure is set up, let’s generate some memes!

Building the meme generator

Our meme generator has very simple needs for backend logic so I have chosen to go with Sinatra for the server. It is very lightweight and allows us to focus on what matters most – generating all the memes!

Let’s set up the Sinatra server with the twilio-ruby and unirest dependencies:

require 'sinatra'
require 'twilio-ruby'
require 'unirest'

post ‘/memegen’ do
  # Server code will go here
end

The endpoint we set up at /memegen will be called by Twilio when an incoming text message comes in at our Twilio number. Let’s get started by handling the scenario in which the user texts in the word “list”. In this case we will return a text message that tells the user which memes are supported. Here is the list of meme formats we will support:

Add the following code inside the /memegen endpoint:

message = params[:Body]
message = message.downcase.strip

if message.eql? "list"
  twiml = Twilio::TwiML::Response.new do |r|
    r.Message "Supported memes: ___ all the ___, what if i told you ___, brace yourselves ____, ___ but that's none of my business, ____ all the ____, ___ ain't nobody got time for that, ___ we're dealing with a badass over here, ___ aaaand it's gone"
  end
  return twiml.text
end

 

We first store the body of the incoming message in a variable and then compare the message to see if it contains the word “list”. Note that we’re removing any whitespace from the beginning or end of the string and also setting it to lowercase since capitalization will not be important in the end (memes are all caps). If the message just contains “list” we use the Twilio Ruby helper library to return a TwiML Response to send an SMS back to the user containing the supported meme formats.

Next, let’s write a method that will handle matching against our meme formats:

def match_memes(message)
  case message.downcase
  when /^(one does not simply)(.*)/
    return { id: 61579, top: $1, bottom: $2.strip! }
  when /^(what if i told you)(.*)/
    return { id: 100947, top: $1, bottom: $2.strip! }
  when /^(brace yourselves)(.*)/
    return { id: 61546, top: $1, bottom: $2.strip! }
  when /^(.*)(but that(?:')?s none of my business)/
    return { id: 16464531, top: $1.strip!, bottom: $2 }
  when /^(.*)((all the)(.*))/
    return { id: 61533, top: $1.strip!, bottom: $2 }
  when /^(.*)(ain(?:')?t nobody got time for that)/
    return { id: 442575, top: $1.strip!, bottom: $2 }
  when /^(.*)(we(?:')?re dealing with a badass over here)/
    return { id: 11074754, top: $1.strip!, bottom: $2 }
  when /^(.*?)((a)+nd it(?:')?s gone)\z/
    return { id: 766986, top: $1.strip!, bottom: $2 }
  else
    return nil
  end
end

 

We’re using regular expressions to match against our meme templates. Note that I have allowed for optional punctuation since this can be inconsistent in text messages (read: I’m allowing for laziness). For matched memes we are returning a hash that contains the ID for the meme on Imgflip (here’s a list of popular meme IDs) and the top and bottom text for the meme. If the message doesn’t match a meme we return nil so that we can check for this later and return an error response.

Now we can use our match_memes method on the incoming message. If it doesn’t match a meme we’ll send the user a response letting them know how to get a list of valid meme templates. If it does match a meme we’ll make a REST call to the Imgflip API and store the image URL returned in the response.

# Attempt to match a meme
meme_match = match_memes(message)

if meme_match.nil?
  # If message doesn’t match a meme, return a prompt to send “list”...
  twiml = Twilio::TwiML::Response.new do |r|
    r.Message "Sorry, I don't know that meme! Send 'list' to see a list of supported memes."
  end
  return twiml.text
else
  # We have a match! Make a REST call to Imgflip to create the meme.
  response = Unirest.post "https://api.imgflip.com/caption_image",
       parameters:
       {
          "username" => "** Your Imgflip username **", 
          "password" => "** Your Imgflip password **", 
          "template_id" => meme_match[:id], 
          "text0" => meme_match[:top], 
          "text1" => meme_match[:bottom]
      }

  # Grab the meme image URL out of the response.
  image_url = response.body['data']['url']
end

The only thing left to do at this point is send the meme back to the user’s phone:

twiml = Twilio::TwiML::Response.new do |r|
  r.Message do |m|
    m.Media "#{image_url}"
    m.Body "Here's your meme! Powered by Twilio MMS."
  end
end

return twiml.text

That’s it, now you can generate all the memes with a simple text message! You should deploy your server code somewhere publicly accessible so that Twilio will be able to contact your server. I recommend Heroku for this and this guide will show you how to deploy your Sinatra app to Heroku. You can view the full project on Github.

Hooking up our app to Twilio

There’s one more thing we need to do before we have a functioning meme generator. We need to connect our app to Twilio so that incoming texts will be routed to our app logic. Log in to Twilio and head over to the numbers portal. Click on the number you wish to use for the meme generator and configure the Messaging URL to point at your newly deployed Sinatra server:

Number config

Now the meme generator is good to go. Send in a valid meme sentence to test it out!

Next steps

Now that we have a functioning meme generator, we can build upon this to add even more features. Here are just some ideas for what could be added:

  • Support all the memes
  • Support custom memes
  • Instead of sending memes to yourself, why don’t you create a Meme-o-gram and send memes to your friends? In fact, once you’ve built it feel free to send them to (248) 686-3637 to share them with me!

Building a meme generator was a lot of fun and it shows off one creative way to use Twilio MMS. Given how easy it is to use this thing I’m sure my brother will be unleashing MMS powered memes at me any second now.

I’m really excited to see what you build with MMS so please don’t hesitate to share your results with me. I’m easy to find on Twitter @brentschooley or you can email me at brent@twilio.com.