Never Forget A Friend’s Birthday with Python, Flask and Twilio

September 22, 2017
Written by
Bob Belderbos
Contributor
Opinions expressed by Twilio contributors are their own

Python birthday reminders

Have you ever forgotten a friend’s birthday? It happens to the best of us. After the frustration of checking Facebook every day for birthdays, I wanted a better push notification system with better filters.

I wrote an article, Building a Simple Birthday App with Flask-SQLAlchemy, showing a way to export your Facebook birthday calendar to an .ics file and import it into a DB with Flask and Flask-SQLAlchemy.

After talking to Twilio at PyCon we thought it would be cool to extend this app by adding SMS notifications and a possibility to send birthday messages via SMS, so I signed up for a Twilio account and got coding.  

In this post we will build a simple birthday app with Python, Flask and Twilio’s Programmable SMS service API so you never miss a birthday again: 

app-printscreen

The complete code for this project on Github

Setup instructions

Start by cloning the Git repository:

$ git clone https://github.com/pybites/bday-app

Next, make a virtual environment and install the dependencies:

$ cd bday-app
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install -r requirements.txt

Create a Twilio account, get a phone number and API key (sid) and token.

Copy the settings template in place:

$ cp env-example.conf env.conf

Update the env.conf environment variables file with the correct settings:

  • flask – secret = set this to a hard to guess string (Flask docs)
  • twilio_api – sid token = obtained in step 3
  • phones – twilio = obtained in step 3
  • phones – admin = your (verified) mobile phone number, where you want to receive notification messages
  • login – user password = your login credentials for the Flask app
  • server – url = unchanged if running locally, update to base URL if deployed elsewhere

NOTE: make sure you use E.164 number formatting for phone numbers (e.g. +34666555444, +442071838750). See Twilio’s support article: Formatting International Phone Numbers.

The app uses configparser to load these settings in. 

Import your FB birthday calendar into the local SQLite database:

  • Export your birthday calendar from Facebook and save it as cal.ics in the app’s top level directory.
  • Run model.py to import the birthdays into the DB. Here I am using the -t option to strip out real names using Faker. For real use drop the -t:

$ python model.py

import_birthdays.gif

How to run it

Make sure you have your virtualenv enabled. This app has two parts: a Flask front-end and a daily cron script on the backend.

The front-end is a Flask app which you can invoke with: python app.py.
At this point go to 127.0.0.1:5000 and you can see your friend’s birthdays. As we’ll see in a bit you need to add a phone number for each friend you want to receive a notification for.  

The notifier is a daily cron script that checks for active birthdays with a phone number set. It sends SMS notifications to the admin phone you configured in your settings. This needs to run in the background so use a tool like nohup:

$ nohup ./notify.py &

How the Flask App Works

All Flask app code is in app.py. As you probably want to host this in the cloud, authentication is there from the start.

This is done by adding the login_required decorator to all private routes. When you go to the app you first have to login. I am using ngrok here to test the local app:

nfbd-login.png

Once logged in, you can see your imported friends and their birthdays. Some simple CSS emphasizes today’s birthday(s):

nfbd-current-bday.png

You can navigate friends by tabs and via the search box:

nfbd-navigate.png

The app uses Flask-SQLAlchemy to interface with the database, with a single table to store the birthdays. The model is defined in model.py which we used before to load in the data. Here is the part that defines the table structure:

class Birthday(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(120))
    bday = db.Column(db.DateTime)
    phone = db.Column(db.String(20))

Adding Phone Numbers

To avoid numerous SMS messages, the cron job only looks at friends with a phone number only. Clicking the pencil button at the right you get a form to add a phone number:

nfbd-add-phone.png

NOTE:  that to be able to send messages to your friends using the free Twilio version you need to verify their phone numbers first. You can lift this limitation by upgrading your account. You can find more info here.
Various validation rules are set up in the corresponding /friends/ route. We cannot have the same phone number twice for example.

With the phone number added you will get an SMS when it’s their birthday.

nfbd-with-phone.png

Notifications

The cron job is coded in notify.pyusing the schedule package to notify about birthdays occurring that day.

It queries the DB using Flask-SQLAlchemy getting the birthdays for the current day:

def job():
    bdays = Birthday.query.filter(and_(
            extract('day', Birthday.bday) == TODAY.day,
            extract('month', Birthday.bday) == TODAY.month,
            Birthday.phone != None)).all()  # noqa E711
    ...

Then, it sends an SMS to your configured admin phone. Twilio makes this very easy as you can see in sms.py:

def send_sms(message, media=None, to_phone=ADMIN_PHONE):
    message = CLIENT.messages.create(
        from_=FROM_PHONE,
        to=to_phone,
        body=message,
        media_url=media,
    )
    return message.sid

Running the cron job manually:

nfbd-sms-notify.png

As this will run on a remote server the current version of the app uses logging instead of stdout. As stated before you want to use nohup to run it in the background. The schedule module does the rest:

...

schedule.every().day.at('00:05').do(job)

while True:
    schedule.run_pending()
    time.sleep(1)

And here is the notification SMS for today’s birthday:

nfbd-notification-sms.png

The link is the entry point into the Flask app to send a birthday message or SMS card which we will see in the next section.

Send Birthday Messages and Cards

This is the second part of the app. There are two ways to get to the send message feature: follow the link in the SMS, or click the phone icon at the left of your friend’s name from the homepage.

You are presented with the following form:

nfbd-send-msg.png

Some validation is in place to only be able to send a message to the person that has a birthday:

nfbd-only-for-bday.png

You can now send a text message and include an image link:

nfbd-include-img-link.png

If you include an image link, the Pillow library is used to put the text on top of the image making a simple birthday card. The code for this is in the text_on_image.py module.

When you click verify you can check if you are happy with the result:

nfbd-confirm.png

If so you can send the card via MMS:

nfbd-sent.png

Gary would receive an SMS like this:

nfbd-card-sms.png

This is on an iPhone. On Android it did not display the image inline. For this reason it sends the text alongside the image.

Conclusion and Learning

Having a birthday app managed by SMS is cool. Twilio’s API makes it very easy. The Pillow image integration was the hardest part of building this app.

Splitting the code into various modules helped and will help manage complexity. Moving forward it would be good to add a regression test suite and possibly automate end-to-end testing with a tool like Selenium.

And finally, nothing beats building practical apps when it comes to honing your programming skills. Apart from the Twilio API, building this app I learned more Flask and integration of various interesting modules.  

Practice Yourself

Join our Twilio PyBites code challenge to build your own automated texting app and other awesome applications each week.

Feel free to reach out if you have any questions or comments:

Contact info

I am Bob Belderbos from PyBites, you can reach out to me by: