How to Build a Picture Scavenger Hunt Using Twilio MMS, Twilio and Sinatra
Time to read: 9 minutes
When I get together with my siblings (one younger sister, and two little brothers- 12 and 9) I tend to come up with crazy ideas. A couple years ago in Northern California I was visiting my little brothers and had the grand idea to create a scavenger hunt for them when they woke up in the morning. Of course, this blew their mind and we had a great time. So, when I ended up visiting for an extended period a couple months ago, I decided it was time to reprise the scavenger hunt– MMS Style.
How will the game work?
There are lots of ways to set up a scavenger hunt. You could take pictures of locations, and then ask people to send pictures back at those locations. But then you have to figure out how to verify the location, which would be tricky. In this case, the way I verified that the player found the location was to plant a keyword somewhere in the vicinity of the location.
Most of the keywords I wrote were 80’s bands since I wanted keywords that the boys were unlikely to know or guess :) If you are building this for a younger audience, definitely be sure to make the keywords easier.
Once the player finds the keyword, they text it back to the number, which checks it and then sends the next clue. Here is a diagram of the entire interaction:
If you’d like to interact with the demo app you can text 619.555.5555. Seems pretty simple right? Now let’s build the dang thing.
Setting Up
It might make sense to download all of the sample code and follow along in your IDE of choice. If you’d like to checkout the code you can get it here: Github ➭
Before we can start building an interactive scavenger hunt, we have a little bit of work to do. We need to sign up for a Twilio account for starters, and then obtain an MMS-enabled phone number to use with our application. Let’s get that taken care of so we can move on to the code.
Sign up for a Twilio account
If you don’t already have one, sign up for a Twilio account now. During the signup process, you’ll be prompted to search for and buy a number. You can go ahead and just take the default number for now.
After you take the default number, you might want to play around with it and send yourself a few text messages, maybe receive a phone call. Once you’re ready to get to the MMS action, click the button to go to your account.
Buy an MMS-enabled phone number
On your account dashboard, you’ll see your account SID and auth token near the top of the screen. These are like your username and password for the Twilio API – you’ll need these values later on to make authenticated requests to the Twilio API.
But for now, we need to either buy or use an existing phone number that can send MMS messages. Click on “Numbers” in the top nav. In your list of phone numbers, there are icons indicating the capabilities of each number. If you already have a number with MMS enabled, then you’re all set!
If you still need a number with MMS, click the “Buy Number” button. In the resulting dialogue, search for a number that has MMS capabilities.
In the resulting list, choose the option to buy one of these MMS-enabled phone numbers. Great! Now that we have an MMS-capable number, we’re ready to write code that sends and receives MMS messages. We’ll start sending our first pictures by using a high level helper library that makes it easier to work with the Twilio API.
Set up our project
Before we begin, we assume you have Ruby and Ruby Gems installed. If you’re on a Mac, these should be installed already – if you’re on Windows, you might consider this installer. If you’re on Linux, you probably know what you’re doing ;) – but here’s a solid guide to getting started through apt-get on Ubuntu.
In a terminal window, create a new folder called “scavenger-demo”. Change into this directory – we’ll put all our application code here. Next, let’s install some Ruby gems (in the github directory I use bundler and a gemfile)
Next, let’s create the files and directory structures we will need:
For this application, we will be using the lightweight Ruby web framework Sinatra. We’ll use the Twilio Ruby gem to make interacting with the Twilio APIs a bit easier – we’ll see how as the tutorial goes on.
Now that our project is all set up, let’s write some logic to manage the scavenger hunt.
Server-side game mechanics
I chose to use Sinatra as the scavenger hunt Server. Sinatra allows us to write a simple one-page Ruby file that runs the whole shebang. In this case our server only needs to do two things:
- Send and Receive texts and parse the Body
- Maintain a database of Players to track clues completed
First let’s setup our Sinatra file with all of the requirements. Github ➭
Here we include our requirements and initialize a Twilio client that will handle all of our Message sending and receiving.
Next we need to create a database to store some players in, and in this case we’re going to use Data Mapper. I like using Data Mapper (DM) for small projects like this since I don’t need to write any migrations and I can define the model once in ruby and datamapper will create the necessary layout in the datastore. It also works with a bunch of different datastores, in this case we’ll be using Postgres.
The only thing we actually need from the player to start is a phone number, so we define it as required. This will prevent us from creating a model without a phone number. Also notice that status is an enum, this allows us to quickly access this property by name even though it’s stored as an int in the DB. The Player.status needs to be 0 or ‘new’ when we begin.
Lastly we call two magical methods on datamapper, finalize and auto_upgrade!. Finalize checks the models for validity and initializes the necessary properties, while auto_upgrade! runs any migrations needed to update the datastore while not removing any existing entries. Great, so we have somewhere to store the players, now let’s build the interface for the players.
Sending picture clues and checking their validity
First we need to define a set of clues that the user will receive as the hunt progresses. Let’s store these inside a Ruby Hash for easy access throughout the code.
The riddle will accompany the image so that you can give your hunters a little more context for what they might be looking for… in my case some of the clues were pretty abstract so it was important to include this. You could drop the riddle and just send pictures however.
Init the player and kick-off the hunt!
When I kicked off my scavenger hunt I got all the kids in a room and told them to type the number into their messaging app but not send anything. Then on my mark they all texted “let’s hunt!” to the number I gave them. When they texted what actually ended up happening is my Twilio number made a request to my master route which then kicked off the game.
The first thing it did was create our players in the database and then prepare to listen for incoming texts.
At the beginning of our master route ‘scavenger/’ we’ll throw the phone_number into a global var so that other methods can access it. Next the router checks to see if this user exists by the phone_number, and if she doesn’t exist it creates a new one.
Now that we have a player we can send them their first clue.
Send a picture clue
First we need to select a clue. When we created the user we also stored an array of clue keys (‘clue1’, ‘clue2’, etc) under their profile that shows us which clues are remaining to be sent. Now all our program needs to do is choose one and send it.
Aside from selecting a random clue, this snippet also stores the current clue that the player is hunting and calls sendPicture():
Notice we’re using our handy global objects @client and @phone_number. By using the Twilio Ruby library we can send an MMS by using the same messages.create() method we always used to send SMS, we just include a media_url parameter and it’s a picture message (as long as the phone number is MMS enabled).
Okay, to recap our user has been created in the database, along with some state about where in the game she is and fired off the first picture clue. As far as we know the hunt is on and we should probably setup something to listen for those incoming texts.
Finding clues!
If you remember, the way this particular scavenger hunt works is by sending the user a picture clue and then waiting for them to send a keyword back. This keyword is hidden somewhere around where the picture was taken. This could be on a conference table, written on the back of playing cards, or tattooed on the belly of a miniature pig. However it’s delivered, we need a way to verify that the keyword sent is correct.
Next, inside of our master router ‘/scavenger/?’ we need to add these lines:
You can view the master route in it’s entirety here: Github ➭.
Okay we are mostly done with the heavy lifting code wise, now we just need to connect it all to Twilio and we’re ready to hunt.
Connecting to Twilio
Now that we have our master route, we need to tell Twilio to send all of our messaging traffic to that endpoint. Once you login to Twilio go to your numbers portal. Once you’re there you can click on the number you provisioned for the scavenger hunt and point it to your server. It should look like this:
Great you’re all ready! Now all you have to do is arguably the hardest part– build the actual scavenger hunt!
Building the Arena
If you’re me and you’re building this for some exceptionally clever young men, you should probably take the weirdest pictures possible and hide them in the hardest spots imaginable.
But it might be a better approach to take pictures of obvious landmarks, with clear instructions. That way your participants are guaranteed to enjoy themselves.
Happy Hunting Compadres
Now you are competently armed with the tools to write an interactive scavenger hunt using Twilio and Sinatra. Feel free to tweak the experience to your liking, in fact you can checkout a more “game-ified” version on a branch of the repo I shared earlier, including injuries, fastest hunter and a console dashboard. Some other improvements would be:
- 2-factor auth
- Picture submission in place of keywords
- A live dashboard of all the teams/players progress
- Virtual “pitfalls” that players can use on one another
I’ve always loved scavenger hunts, and I would love to see a whole new generation of hunts arise with all of the technology we developers have at our fingertips. If you have ideas about the next great MMS hunt, or you just want to wax poetic about the illustrious brilliance and inevitable demise of Steve Irwin, shoot me an email or find me on twitter.
Until next time, happy hunting!
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.