Build your own Pokédex with Django, MMS and PokéAPI

November 03, 2014
Written by
Paul Hallett
Contributor
Opinions expressed by Twilio contributors are their own

pokedelg

Remember the Pokédex from Pokémon?

It was the hand-held, all-knowing super machine that was invaluable to any Pokémon trainer trying to Catch ‘Em All in the late 90’s and early 00’s. With it’s encyclopedic knowledge of Pokémon, the Pokédex was the tool you whipped out of your pocket whenever you wanted to learn about a new Pokémon you had discovered in the games, the television series, or the card game.

But to the disappointment of millions of children, the Pokédex wasn’t real, or at least, a Pokédex that had the same functionality as the one in the television series. There were quite a few knock-off products that provided a mediocre experience and the craving for the cool gadget was never really satisfied. Even now, at 25, I still dream of a Pokédex as good as the one in the television series. Luckily, with ten extra years of technological advancement and hard work by the world’s top scientists and programmers, I think we’ve discovered a way to get close to that dream of owning a Pokédex.

Nearly all the information about the Pokémon video games was recently added to a web API (disclaimer: I built it) called PokéAPI. This API contains all the data from the Pokémon computer games series, including sprites (images from the game) and descriptions of each Pocket Monster. With a few APIs calls and some software manipulation, you can replicate the content that a Pokédex might give to you when you discover a Pokémon in the field.

But how to get this data into a portable, hand-held device? Well, it turns out that we all carry a portable hand-held “does everything” device these days. Building an iOS or Android application might be a good way to share this content but that would require a data connection. Getting people to install an app is hard and it’s also really time consuming for the developer. In Pokémon: the Pokédex works almost anywhere, in a glacier, in the desert, even in the ocean! It must be running on the some form of PSTN network and using SMS and MMS as it’s medium of communication, right?

As we’re trying to get as close as possible to an authentic Pokédex, let’s use Twilio MMS and the PokéAPI to build a service that we can query when we discover a new Pokémon, no matter where we are, even if we don’t have cellular data!

Try it now!

Don’t want to build your own Pokédex? Give the one I built earlier a whirl by sending the name of a Pokémon to (740) 630-0051 (US) or +442030952965 (UK is SMS only):

 

There really is no better Pokémon to demonstrate this with

How to build your own Pokédex

In order to create our own MMS Pokédex we’ll need to:

  • Make HTTP requests to PokéAPI.co.
  • Collect all the relevant resources from PokéAPI (Pokémon, description and sprite).
  • Create a Django web application and add our PokéAPI query code to it.
  • Add our PokéAPI description to a TwiML document for Twilio.
  • Deploy our Django application to Heroku so it is visible on the web.
  • Test it out.
  • Be the very best, like no one ever was
What will you need?

In order to complete this tutorial you will need:

and with that you’re good to go!

Making HTTP Requests to PokéAPI.co

Getting yourself familiar with PokéAPI will also mean you’ll be learning how to talk to and navigate a REST API, a common way of presenting resources and data through an API on the web. We’ll be using Python for this tutorial, but the API can be queried in any language, even with cURL, try this command in the terminal:

 

$ curl http://pokeapi.co/api/v1/pokemon/

 

The PokéAPI will return JSON formatted representations of the data stored on the application’s servers. JSON is a common data format which can easily be translated into a programming language’s native object representation.

In Python, we’ll start by using the Requests library to make an HTTP Request to PokéAPI for data on a Pokémon (let’s pick Charizard because he’s awesome) and load that JSON data into a native Python object representation using Python’s JSON library.

First, install the requests library using pip:

 

$ pip install requests

 

Create a new directory called pokemon (or something descriptive), then open up a new file inside that directory and write the following code. Save the file as query.py:

 

import requests
import json

url = 'http://pokeapi.co/api/v1/pokemon/charizard/'
response = requests.get(url)

if response.status_code == 200:
    data = json.loads(response.text)
    print data['name']
else:
   print 'An error occurred querying the API'

Here we are importing requests and Python’s core JSON library. Then we’re creating a variable called url containing a url to the PokéAPI. Below that, on line 5, we make the HTTP Request using requests’ get() function and store the results in a variable called response.

The conditional statement below this checks that we’ve received an HTTP 200 OK response (the standard response for HTTP when we’ve made a successful query). If we get back an okay response we transform the JSON string into a native Python object and print out the name attribute. If we did not get a HTTP 200 OK response (check your internet connection!) then we just print out an error.

Let’s run that script with Python in our terminal and see the result:

 

$ python query.py
Charizard

With that small script, we’ve learned how to query the PokéAPI REST API and format the response natively in Python. Nice! Give yourself a big victory roar!

Collecting relevant resources for our Pokédex

Victory roar made you feel awesome? Good. Let’s collect the resources relevant to Charizard that we want to use for our Pokédex: the description and the sprite resources.

We’ll modify our script to grab this data and print it out:

 


import requests
import json

BASE_URL = 'http://pokeapi.co'

def query_pokeapi(resource_url):
    url = '{0}{1}'.format(BASE_URL, resource_url)
    response = requests.get(url)

    if response.status_code == 200:
        return json.loads(response.text)
    return None

charizard = query_pokeapi('/api/v1/pokemon/charizard/')

sprite_uri = charizard['sprites'][0]['resource_uri']
description_uri = charizard['descriptions'][0]['resource_uri']

sprite = query_pokeapi(sprite_uri)
description = query_pokeapi(description_uri)

print charizard['name']
print description['description']
print BASE_URL + sprite['image']

Quite a bit has changed to get us to where we want to be, so let’s go through this and understand what is happening. We’ve taken the url for PokéAPI and stored it in a global variable called BASE_URL, because we want to use it throughout the code.

Below this on line 6 we have created a function called query_pokeapi that takes a single parameter resource_url. This function does most of the work from the previous script by querying PokéAPI and returning a Python representation of the JSON data it receives. We’ll be using this code block a lot, so making it into a function means we can reuse it.

On line 14 we’re making a query to PokéAPI to get data on the Pokémon Charizard. On lines 16 and 17 we’re looking at the Charizard data and getting back the resource_uri attribute for linked Description and Sprite resources. This is using another important concept of REST APIs called HATEOAS, the jist of it being to provide hypermedia links (such as URLs) to related resources. We’re then using those hypermedia links to query PokéAPI for the relevant Sprite and Description resources.

Finally, we’re printing out the information that we want our Pokédex to display:

 

$ python query.py
Charizard
Spits fire that is hot enough to melt boulders. Known to cause forest fire unintentionally.
http://pokeapi.co/media/img/6.png

If we paste the sprite image link into our browser we’ll see a sprite of Charizard from the computer games:

 

 

This print out isn’t quite as spectacular as a real Pokédex, so let’s figure out a way to get this on our mobile phones via Twilio and MMS.

Creating a Django web application and composing a TwiML response

In order to get our code online and pokémons into our phones, we’ll need to install a few things and configure a basic Django web application. When we have the web application running, we can send an SMS to a Twilio phone number. When Twilio receives this SMS, it will make an HTTP Request to our Django application. The Django app will execute the code we’ve written and  send back a response to Twilio, which then processes the instructions and sends the user an MMS (or SMS if you’re in the UK).

To get started building our application, we want to use git to clone a basic Django project (that I’ve created to save some time) and checkout the start branch:

 

$ git clone git@github.com:phalt/poketext.git
$ cd poketext
$ git checkout start

Once you have cloned the project and checked out the start branch, we need to create a Python virtual environment and install all the relevant software for this project to run. Type the following into the terminal:

$ virtualenv venv
New python executable in venv/bin/python
Installing setuptools, pip...done.

For Twilio to work with Django, we need to add our TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN tokens to our environment. Open the venv/bin/activate file with a text editor, navigate to the bottom and add these two lines:

export TWILIO_ACCOUNT_SID=ACXXXXXX
export TWILIO_AUTH_TOKEN=ATXXXXXX

Be sure to replace the values with your own tokens. Save the file and activate the virtual environment:

$ source venv/bin/activate
(venv)$

Then use pip to install all the requirements:

(venv)$ pip install -r requirements.txt
Downloading/unpacking Django==1.7.1 (from -r requirements.txt (line 1)

...

Successfully installed Django SQLAlchemy dj-database-url dj-static django-phonenumber-field django-postgrespool django-toolbelt django-twilio gunicorn httplib2 phonenumbers psycopg2 requests six static3 twilio whitenoise

Cleaning up...
(venv) $

With everything installed, make sure the server is working:

(venv)$ python manage.py runserver

If you see the following output then we’re good to go:

October 28, 2014 - 20:08:26
Django version 1.7.1, using settings 'poketext.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Type CONTROL+C to stop the server.

Let’s write some code! Open the file in the poketext directory called views.py in your prefered text editor. This is where we’ll build the views that our web application serves. It should currently be completely blank. If you see some code here then you’ve probably forgotten to checkout the start branch:

(venv)$ git checkout start

With the file empty, let’s copy in the content from the previous query.py file and write some new Pythonic logic around it to work with Django. All changes are highlighted below:


from django_twilio.views import twilio_view
from twilio.twiml import Response
import requests
import json

BASE_URL = 'http://pokeapi.co'

def query_pokeapi(resource_uri):
    url = '{0}{1}'.format(BASE_URL, resource_uri)
    response = requests.get(url)

    if response.status_code == 200:
        return json.loads(response.text)
    return None

@twilio_view
def incoming_message(request):
    twiml = Response()

    body = request.POST.get('Body', '')
    body = body.lower()

    pokemon_url = '/api/v1/pokemon/{0}/'.format(body)
    pokemon = query_pokeapi(pokemon_url)

    if pokemon:
        sprite_uri = pokemon['sprites'][0]['resource_uri']
        description_uri = pokemon['descriptions'][0]['resource_uri']

        sprite = query_pokeapi(sprite_uri)
        description = query_pokeapi(description_uri)

        message = '{0}, {1}'.format(pokemon['name'], description['description'])
        image = '{0}{1}'.format(BASE_URL, sprite['image'])

        frm = request.POST.get('From', '')
        if '+44' in frm:
            twiml.message('{0} {1}'.format(message, image))
            return twiml
        twiml.message(message).media(image)
        return twiml

    twiml.message("Something went wrong! Try 'Pikachu' or 'Rotom'")
    return twiml

 

Let’s go through the highlights and get an understanding of what is happening.

On line 1 and 2 we are importing some Twilio-specific python tools, the twilio_view is a decorator that helps to format responses from Django back to Twilio. The Response class will help us to generate our TwiML response programmatically in Python. Below this you’ll notice the same query_pokeapi function that we had earlier, it still serves the same purpose here.

The function incoming_message which starts on line 17 is the Django view that will receive requests from Twilio. The first parameter it takes, request, is a Python representation of the incoming HTTP Request. You’ll notice just above, on line 16, that we’ve written @twilio_view, this is some “syntactic sugar” in Python that tells the decorator to be run when this view is called.

Starting on line 18, we create a new Response object called twiml, which we’ll use later on. Lines 20 and 21 get the Body of the inbound SMS message we’ll be expecting from Twilio and makes sure that lower casing is applied to it. When Twilio sends an HTTP Request about an inbound SMS message, we’ll get a bunch of POST parameters with it. You can see a full list of the items that are sent to you in the documentation on twilio.com. Just below, a URL is composed from the Body variable and used to query the PokéAPI for a Pokémon.

Through lines 26 to 31 we’re saying “If a Pokémon response is given, get the description and sprite resources for that Pokémon”, much like in the second script we wrote.

Line 33 and 34 format the correct message and image that we’ll use for our response and line 38 builds that response. The penultimate step is to see what number we received the message from, if ‘+44’ is in the number, we know it is a UK phone number and we want to send back just an standard SMS, otherwise we send back an MMS.

Finally we return the twiml variable. At this point, Django-twilio will format it correctly (by adding the correct content type and representing it as XML) and send it back to Twilio.

If we did not get a pokémon response on line 26, we create a different set of TwiML on line 43 and 44 with an error message.

Setting up URL routing and making a test request

Before we can test that this works, we want to link the view function incoming_message to a URL in Django’s URL config. This can be done in the poketext/urls.py file:

 


from django.conf.urls import patterns, include, url
from django.contrib import admin

import views

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'poketext.views.home', name='home'),
    # url(r'^blog/', include('blog.urls')),
    url(r'^admin/', include(admin.site.urls)),
    url(r'^incoming/message$', views.incoming_message),
)

Import the views file on line 4 and create a new URL in the tuple on line 11 like the highlights above. Save the file and then run the server again:

(venv)$ python manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
October 28, 2014 - 13:37:00
Django version 1.7.1, using settings 'poketext.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

In a new terminal window, send a cURL request to the server to make sure we get back the XML response we’re expecting:

(venv) $ curl -F Body=Charizard localhost:8000/incoming/message
<?xml version="1.0" encoding="UTF-8"?><Response><Message><Body>Charizard, Spits fire that is hot enough to melt boulders. Known to cause forest fires unintentionally.</Body><Media>http://pokeapi.co/media/img/6.png</Media></Message></Response>

The output on the terminal is a bit messy, here is a properly formatted XML output:

<?xml version="1.0" encoding="UTF-8"?>
  <Response>
    <Message>
      <Body>
        Charizard, Spits fire that is hot enough to melt boulders. Known to cause forest fires unintentionally.
      </Body>
      <Media>
        http://pokeapi.co/media/img/6.png
      </Media>
    </Message>
  </Response>

Woo! Our Django application is taking our HTTP Request, querying PokéAPI and then giving us back an XML response that Twilio will understand! Time for another celebration:

It appears Cubone, Snubbull and Raichu are not impressed. Why’s that? Probably because we haven’t got this deployed and integrated with Twilio yet. Let’s make them happy and get the last piece of our Pokédex puzzle solved.

Deploying our application to Heroku

In order to get our application running on the web, we’re going to need to commit all the changes we’ve made using git:

 

(venv)$ git add poketext/urls.py
(venv)$ git add poketext/views.py
(venv)$ git commit -m 'Added views and url config'
[start d62b334] Added views and url config
2 files changed, 44 insertions(+)

With these changes committed (with an appropriate commit message) we can deploy the code.

We’re going to be using Heroku, a cloud application platform, to host and run the app. If you don’t want to learn how to use Heroku and just want to deploy a copy of the application, click the button below and skip to the next step:

If you want to learn how to use Heroku, they have a fantastic getting started guide that I recommend reading before you continue. In particular, you want to make sure you have the Heroku toolbelt installed and ready to use.

With the toolbelt installed and with you logged into Heroku, let’s go ahead and create a new Heroku app:

 

(venv)$ heroku create
Creating aqueous-coast-1744... done, stack is cedar
https://aqueous-coast-1744.herokuapp.com/ | git@heroku.com:aqueous-coast-1744.git
Git remote heroku added

Don’t forget that your heroku URL and app name will be different from the one shown here. To deploy the code to the new Heroku app use the following command:

(venv)$ git push heroku master
Initializing repository, done.
...

-----> Python app detected
-----> Installing runtime (python-2.7.8)
-----> Installing dependencies with pip
      Downloading/unpacking Django==1.7.1 (from -r requirements.txt (line 1))

...

The project I created, that we cloned with git near the start of this guide, already has all the settings files Heroku expects, so Heroku will detect that it is a Python application and begin installing the correct requirements. If all goes to plan, you should see the following terminal output:

Compressing... done, 43.0MB
-----> Launching... done, v1
    https://aqueous-coast-1744.herokuapp.com/ deployed to Heroku
To git@heroku.com:aqueous-coast-1744.git
* [new branch]      master -> master

Before we look at the live version of the app, we need to add our Twilio Account Sid and Twilio Auth Token to Heroku using the following commands:

(venv)$ heroku config:set TWILIO_ACCOUNT_SID='ACXXXXXX'
Setting config vars and restarting aqueous-coast-1744... done, v1
TWILIO_ACCOUNT_SID: ACXXXXXX
(venv)$ heroku config:set TWILIO_AUTH_TOKEN='ATXXXXXX'
Setting config vars and restarting aqueous-coast-1744... done, v1
TWILIO_AUTH_TOKEN: ATXXXXXX

Be sure to replace the values with your actual credentials.

With the Twilio account credentials set, we should be able to send a cURL request to the Heroku application:

(venv)$ curl -F Body=Charizard https://YOUR-APP-NAME.herokuapp.com/incoming/message
<?xml version="1.0" encoding="UTF-8"?><Response><Message><Body>Charizard, Spits fire that is hot enough to melt boulders. Known to cause forest fires unintentionally.</Body><Media>http://pokeapi.co/media/img/6.png</Media></Message></Response>

 

Linking our application to a Twilio phone number

Now that we have a live, publicly addressable version of our application running, let’s get it linked to a Twilio phone number. With a free Twilio trial account, we will have one Twilio phone number in the numbers dashboard, we want to make sure it has MMS capabilities, which is denoted by this little image:

If you don’t have an MMS-enabled number, get a new phone number here.

Within the MMS enabled phone number settings, we want to change the Messaging URL to point to our heroku URL and then click save.

The very last step is to send an SMS message to the Twilio phone number to check it is working with some different Pokémon:

 

Ash Ketchum would cheer in joy right now if he was here.

Be the very best, like no one ever was

You should now be the proud owner of a real life, sophisticated Pokédex. During any future Pokémon battle, or when you’re trying to show your nephew how cool you still are, the MMS Pokédex is an invaluable tool to have.

I hope you’ve enjoyed learning how to do all the things we’ve covered here:

  • Interacting with a REST API using Python
  • Building a Twilio TwiML response to send an MMS
  • Deploying a Django application to Heroku

Each of these skills will be useful in your future Pokémon-related software journeys, I promise. See you at the Indigo Plateau!