How hacking helped me get married

Marriage

The term “hacking” conjures many images and thoughts in the mind.  Dark rooms, black hoodies, electronic music.  For me, hacking just means understanding the underlying systems and getting it to do what you want (perhaps in an unorthodox manner).

In that sense, the Fonz is a hacker

Arthur Fonzarelli hacking a jukebox

Like most people who had planned to get married in 2020, my plan needed to change.  My partner and I wanted to stay safe during the pandemic — Thankfully, the great city of New York offered a way to remotely apply for a marriage license — Hooray!

5 steps… seems easy enough.

Alas, not everything is so easy.  Sure, make an account, get verified, pay a license fee — all good.  However, we hit a snag upon actually scheduling a meeting with the City Clerk.  It seems that it was as hard to snag an appointment with them as it is to get concert tickets.

Checked for a few months into the future and couldn’t find anything

So after a few days of logging in, navigating through the web app and checking the calendar for every available date in the next few months, I got tired of it and said “let’s automate this.”  Well, in order to automate, we need to understand what’s happening under the hood.

Thankfully, it has never been easier for anyone to peek into what your browser is doing on the internet.  Popping open the developer tools, you can take a look at each HTTP request your browser makes.

Whenever your visit a website, your browser makes HTTP requests for information.  For example, typing in https://blog.mattmintzer.dev into your URL bar tells your browser to make an HTTP GET request for the resource at blog.mattmintzer.dev.  That resource has further instructions and information for your browser to render my blog.

Where does this calendar get its data?

Pouring over the tremendous amounts of requests the browser made (modern web apps sure make a lot of requests), I found a URL (circled in red) that looked particularly relevant.

Let’s dissect this URL a bit.  Let’s look at the query parameters (data in the form of key, value pairs that gets sent directly through the URL).  They come after the question mark. (everything after /range?)  Looks like the calendar range info gets set in these parameters (range_start and range_end).  I played around and wanted to see if I could ask for data from the current date to the end of the calendar year so I modified the range parameter to the end of the year (2020-12-31) and just put that URL in my browser and see what happens.

JSON data response was a lot of this

Awesome!  JSON Data!  I also wanted to check that I could still get this data without being authenticated in the app so I just threw open an incognito tab, made the same request and confirmed we still got data, score!

JSON data is another kind of key-value pair data structure.  Quick look at it makes it kind of clear.  The response has a key of “days” which has a value of a list of what I’ll just call “day objects.”  Each of these day objects has 4 keys.  The one that piques my interest is the “status” key.

My brain is turning rapidly as I think of how to automate my problem away.  Let’s just write a script that will send a request to the calendly link. Grab that “days” list, loop through the days searching for a day that has a status that is not “unavailable.”  Once we have a match, I want to be notified so how about it sends me an email.

#!/usr/bin/env python3

import json
import requests
import smtplib

Libraries are how you solve your problems with someone else’s solutions

We need a few tools to make this hack work:

  • python – Easy to read and write and useful libraries
  • json – python library for parsing json data
  • requests – python library for making HTTP requests
  • smtplib – python library for sending email.

Next up, we need to actually check that calendar:

def check_calendar():
    url = 'https://calendly.com/api/booking/event_types/BFEN3HYGXBSSUYOC/calendar/range?timezone=America%2FNew_York&diagnostics=false&range_start=2020-09-09&range_end=2020-12-29&single_use_link_uuid=&embed_domain=projectcupid.cityofnewyork.us&embed_type=Inline'
    r = requests.get(url)
    json_data = json.loads(r.text)
    days = json_data['days']
    
    found_date = False 
    for date in days:
        if date['status'] != 'unavailable':
            found_date = True
            send_email(date['date'], date['spots'])
            break
            
    if not found_date:
        send_email('nothing available', 'checkback tomorrow')
  1. I set the url I found from the web app as a string variable
  2. Use requests to send an HTTP GET request to the resource, and save the response to a variable called r
  3. I use the json library to parse the text into a python dictionary
  4. I’m specifically interested in the “days” key so I loop through each day checking for the availability
  5. If I find availability, I send myself an email with the dates
  6. If I don’t find one after checking every date, I’ll send myself an email to confirm my script is working and nothing is available.

Here’s the send_email function I wrote.  Nothing too special.

def send_email(date, spots):
    sender = '' # ADD GMAIL ACCOUNT HERE
    sender_password = '' # ADD GMAIL PASSWORD HERE
    to = ''#ADD EMAIL HERE
    body = f'get married on {date} {spots}'
    try:
        server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
        server.ehlo()
        server.login(sender, sender_password)
        server.sendmail(sender, to, body)
    except:
        print('something went wrong')

The smtplib library allows me to login to a gmail account and send email.  For ease of use, I set up a gmail account specifically for development.  This is because you have to adjust gmail account’s security settings to allow access to the python code. (nevermind the fact that I didn’t want to spam actual email with test emails as I make sure this works).  I’m using smtplib’s SMTP_SSL module to to ensure I use SSL for encrypted communication.

Once I was confident the script would work as expected, the next question was upon me.  How/when would I use it?  Sure, all I have to do now is make it executable and run it, but that means I still have to manually do it (upwards of a few keystrokes).  We can do better.  I’ve got a raspberry pi I use only as a pihole. Simple enough to get the script on the pi and set up a cronjob to run every morning at 9:45am and 11:45am.  My reasoning is that I wasn’t sure when the calendar gets updated but assumed it would happen some time in the morning and I don’t mind receiving two emails a day

Getting the script onto the raspberry pi
setting the cronjob to run at 9:45 and 11:45am Monday – Friday

And now… we wait.  And wait I did.  It actually took 3 weeks of emails before I got a hit.  Once I received the confirmation email, I scheduled the date.

It has been a few weeks since and we have been able to get married. Knowledge of some basic scripting and web helped me get married in the middle of a pandemic =)

Want to take apart the code I used yourself? Here’s a link to my github repo

Horse not included

Leave a Reply

Your email address will not be published. Required fields are marked *