Getting Tweets

circuitpython_478px-Oauth_logo.svg.png
OAuth logo by Chris Messina CC BY_SA 3.0

Authenticating

Once you have your Twitter keys, you can use them when the program starts to get a Twitter OAuth 2.0 bearer token. This involves combining the two keys, doing a base64 encoding, making a call to the Twitter API, and extracting the bearer token from the response. It's somewhat involved, and a great candidate to wrap in a function even though we just need to use it once.

Who knows, it could be the start of a Twitter API wrapper library.

Download: file
def get_bearer_token():
    """Get the bearer authentication token from twitter."""
    raw_key = secrets['twitter_api_key'] + ':' + secrets['twitter_secret_key']
    encoded_key = binascii.b2a_base64(bytes(raw_key, 'utf8'))
    string_key = bytes.decode(encoded_key)
    headers= {'Authorization': 'Basic ' + string_key,
              'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'}
    response = requests.post('https://api.twitter.com/oauth2/token',
                             headers=headers,
                             data='grant_type=client_credentials')
    response_dict = json.loads(response.content)
    if response_dict['token_type'] != 'bearer':
        halt_and_catch_fire('Wrong token type from twitter: %s', response_dict['token_type'])
    return response_dict['access_token']

Notice the call to the function halt_and_catch_fire. That function takes arguments that get passed, combines them and prints the result, then stops (by endlessly looping). The name is an homage to the AMC show of the same name about the early days of the microcomputer industry. While not completely accurate (trust me, I was there) it's entertaining nonetheless. In turn, it's a reference to (from Wikipedia):

In computer engineering, Halt and Catch Fire, known by the assembly mnemonic HCF, is an idiom referring to a computer machine code instruction that causes the computer's central processing unit (CPU) to cease meaningful operation, typically requiring a restart of the computer.

It seemed appropriate.

Download: file
def halt_and_catch_fire(message, *args):
    """Log a critical error and stall the system."""
    print(message % args)
    while True:
        pass

Setting up the PyPortal Object

Before anything can really happen, we need an instance of PyPortal. This sets the URL, the json path used to extract the tweet text from the json returned from that URL, and the text area characteristics.

Download: file
cwd = ("/"+__file__).rsplit('/', 1)[0]
url = 'https://api.twitter.com/1.1/statuses/user_timeline.json?count=1&screen_name=' + username


# Initialize the pyportal object and let us know what data to fetch and where
# to display it
pyportal = PyPortal(url=url,
                    json_path=(0, 'text'),
                    status_neopixel=board.NEOPIXEL,
                    default_bg=cwd + '/twitter_background.bmp',
                    text_font=cwd+'/fonts/Helvetica-Bold-16.bdf',
                    text_position=(20, 60),
                    text_color=0xFFFFFF,
                    text_wrap=35,
                    caption_text='@' + username,
                    caption_font=cwd+'/fonts/Helvetica-Bold-16.bdf',
                    caption_position=(5, 210),
                    caption_color=0x808080)

That also initializes the wifi stack, so now we can get our authentication token and construct the authentication header needed for the tweet fetch.

Download: file
bearer_token = get_bearer_token()
pyportal.set_headers({'Authorization': 'Bearer ' + bearer_token})

Fetching Tweets

Now that the bearer token has been fetched and used to set the auth header, the API can be called to fetch some tweets.

The main loop is below.  Every hour it fetches the most recent tweet. Above we set up the PyPortal instance with everything it needs to fetch data from the Twitter API, extract the text of the tweet, and display it. All that's needed is to tell it to do that, using the fetch() method.

Download: file
while True:
    pyportal.fetch()
    time.sleep(3600)       # check every hour

The Code

"""
Twitter API for PyPortal.

Adafruit invests time and resources providing this open source code.
Please support Adafruit and open source hardware by purchasing
products from Adafruit!

Written by Dave Astels for Adafruit Industries
Copyright (c) 2019 Adafruit Industries
Licensed under the MIT license.

All text above must be included in any redistribution.
"""

#pylint:disable=invalid-name
import time
import binascii
import json
import board
from adafruit_pyportal import PyPortal
import adafruit_esp32spi.adafruit_esp32spi_requests as requests

username = 'codewisdom'

try:
    from secrets import secrets
except ImportError:
    print("""WiFi settings are kept in secrets.py, please add them there!
the secrets dictionary must contain 'ssid' and 'password' at a minimum""")
    raise

def halt_and_catch_fire(message, *args):
    """Log a critical error and stall the system."""
    print(message % args)
    while True:
        pass

def get_bearer_token():
    """Get the bearer authentication token from twitter."""
    raw_key = secrets['twitter_api_key'] + ':' + secrets['twitter_secret_key']
    encoded_key = binascii.b2a_base64(bytes(raw_key, 'utf8'))
    string_key = bytes.decode(encoded_key)
    headers = {'Authorization': 'Basic ' + string_key,
               'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'}
    response = requests.post('https://api.twitter.com/oauth2/token',
                             headers=headers,
                             data='grant_type=client_credentials')
    response_dict = json.loads(response.content)
    if response_dict['token_type'] != 'bearer':
        halt_and_catch_fire('Wrong token type from twitter: %s', response_dict['token_type'])
    return response_dict['access_token']

# determine the current working directory
# needed so we know where to find files
cwd = ("/"+__file__).rsplit('/', 1)[0]
url = 'https://api.twitter.com/1.1/statuses/user_timeline.json?count=1&screen_name=' + username


# Initialize the pyportal object and let us know what data to fetch and where
# to display it
pyportal = PyPortal(url=url,
                    json_path=(0, 'text'),
                    status_neopixel=board.NEOPIXEL,
                    default_bg=cwd + '/twitter_background.bmp',
                    text_font=cwd+'/fonts/Helvetica-Bold-16.bdf',
                    text_position=(20, 60),
                    text_color=0xFFFFFF,
                    text_wrap=35,
                    caption_text='@' + username,
                    caption_font=cwd+'/fonts/Helvetica-Bold-16.bdf',
                    caption_position=(5, 210),
                    caption_color=0x808080)

bearer_token = get_bearer_token()

pyportal.set_headers({'Authorization': 'Bearer ' + bearer_token})

while True:
    pyportal.fetch()
    time.sleep(3600)       # check every hour
This guide was first published on Jul 16, 2019. It was last updated on Jul 16, 2019. This page (Getting Tweets) was last updated on Sep 17, 2019.