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:
The complete code for this project on Github.
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
$ cp env-example.conf env.conf
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.icsin 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
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:
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.
Once logged in, you can see your imported friends and their birthdays. Some simple CSS emphasizes today’s birthday(s):
You can navigate friends by tabs and via the search box:
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:
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.
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
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:
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:
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:
Some validation is in place to only be able to send a message to the person that has a birthday:
You can now send a text message and include an image link:
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:
If so you can send the card via MMS:
Gary would receive an SMS like this:
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.
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:
I am Bob Belderbos from PyBites, you can reach out to me by: