Skip to content

Instantly share code, notes, and snippets.

@adrienshen
Last active March 15, 2024 04:00
Show Gist options
  • Save adrienshen/d281e160fec36a3f53b7ff080d052e88 to your computer and use it in GitHub Desktop.
Save adrienshen/d281e160fec36a3f53b7ff080d052e88 to your computer and use it in GitHub Desktop.
Beating the Austin DPS Appointment Booking System
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
import pprint
pp = pprint.PrettyPrinter(indent=4)
import requests
import json
from datetime import datetime
DEV_FORCE_BOOKING = False
DATE_DELAY = 0 # 0 would be the first available date,, but it could also be the same day as today
TIME_SLOT = -1 # last slot one in the day
BASE_URL = 'https://publicapi.txdpsscheduler.com/api/'
# change these details to your personal information to book
DOB = "09/08/1990"
FIRST_NAME = 'Fred'
LAST_NAME = 'Johnson'
EMAIL = 'fredjohnson@gmail.com'
LAST_4 = '2233'
SERVICE_TYPE = 71
DATE_PATTERN = '%m/%d/%Y'
# these are the needed headers to communicate with the scheduler apis
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36',
'Referer': 'https://public.txdpsscheduler.com/',
'Origin': 'https://public.txdpsscheduler.com',
'Host': 'publicapi.txdpsscheduler.com',
'Accept': 'application/json, text/plain, */*',
'Accept-Encoding': 'gzip, deflate, br',
'Content-Type': 'application/json',
'Cookie': 'ARRAffinity=bbe667b6ac7363554ad0781245985818906290e4fd0c62146e63586c6747a1f4; ARRAffinitySameSite=bbe667b6ac7363554ad0781245985818906290e4fd0c62146e63586c6747a1f4'
}
def get_soonest_date_from_locations(results):
da_soonest = None
for location in results:
print('{} with Id={}, Soonest={}, Distance={}'.format(location.get('Name'), location.get('Id'), location.get('NextAvailableDate'), location.get('Distance')))
compare_date = datetime.strptime(location.get('NextAvailableDate'), DATE_PATTERN)
if da_soonest and (da_soonest.get('next_date') < compare_date):
continue
if da_soonest and (da_soonest.get('next_date') == compare_date) and \
da_soonest.get('distance') <= location.get('Distance'):
# print('date is equal and distance is less or equal')
continue
da_soonest = {
'id': location.get('Id'),
'next_date': datetime.strptime(location.get('NextAvailableDate'), DATE_PATTERN),
'distance': location.get('Distance')
}
return da_soonest
def get_response_id():
# make sure we are eligible:
ELIGIBILITY_URL = BASE_URL + 'Eligibility'
PAYLOAD = json.dumps({
"CardNumber": "",
"DateOfBirth": DOB,
"FirstName": FIRST_NAME,
"LastFourDigitsSsn": LAST_4,
"LastName": LAST_NAME
})
response = requests.request('POST', ELIGIBILITY_URL, headers=HEADERS, data=PAYLOAD)
results = response.json()
return results[0].get('ResponseId')
def get_bookings():
PAYLOAD = json.dumps({
"DateOfBirth": DOB,
"FirstName": FIRST_NAME,
"LastFourDigitsSsn": LAST_4,
"LastName": LAST_NAME
})
BOOKING_URL = BASE_URL + 'Booking'
response = requests.request('POST', BOOKING_URL, headers=HEADERS, data=PAYLOAD)
return response.json()
def make_booking_request(last_slot_of_earliest_day, response_id, da_soonest):
MAKE_BOOKING_URL = BASE_URL + 'RescheduleBooking'
MAKE_BOOKING_BODY = json.dumps({
'BookingDateTime': last_slot_of_earliest_day.get('StartDateTime'),
'BookingDuration': last_slot_of_earliest_day.get('Duration'),
'CardNumber': "",
'CellPhone': "",
'DateOfBirth': DOB,
'Email': EMAIL,
'FirstName': FIRST_NAME,
'HomePhone': "",
'Last4Ssn': LAST_4,
'LastName': LAST_NAME,
# where is this from?
'ResponseId': response_id,
'SendSms': False,
'ServiceTypeId': SERVICE_TYPE,
'SiteId': da_soonest.get('id'),
'SpanishLanguage': "N"
})
print(MAKE_BOOKING_BODY)
response = requests.request('POST', MAKE_BOOKING_URL, headers=HEADERS, data=MAKE_BOOKING_BODY)
return response.json()
# return None
def get_available_locations():
PAYLOAD = json.dumps({
"CityName": "",
"PreferredDay": 0,
"TypeId": 71,
"ZipCode": "78723"
})
URL = BASE_URL + 'AvailableLocation'
response = requests.request('POST', URL, headers=HEADERS, data=PAYLOAD)
return response.json()
def hold_slot_request(slot_id):
HOLD_SLOT_URL = BASE_URL + 'HoldSlot'
PAYLOAD = json.dumps({
"DateOfBirth": DOB,
"FirstName": FIRST_NAME,
"Last4Ssn": LAST_4,
"LastName": LAST_NAME,
"SlotId": slot_id
})
print('\nHOLD_SLOT payload: ', PAYLOAD, '\n')
response = requests.request('POST', HOLD_SLOT_URL, headers=HEADERS, data=PAYLOAD)
return response.json()
def __main__():
results = get_available_locations()
da_soonest = get_soonest_date_from_locations(results)
print('>>> Soonest Date: ', da_soonest, '\n\n')
print('>>> Check Current Booking Against Next Availability to See if We Improved Our Time:')
response_id = get_response_id()
print('ResponseId: ', response_id)
results = get_bookings()
# the order is most recent bookings to older bookings
print('>>> Print current best booking time: {}'.format(results[0].get('BookingDateTime')))
print('>>> Confirmation: ', results[0].get('ConfirmationNumber'))
print(results, '\n\n')
current_booking_date = datetime.strptime(results[0]['BookingDateTime'], '%Y-%m-%dT%H:%M:%S')
print('>>> Compare current booking with new availability (Is the New Date Earlier Than Current Booking?):')
print('>>>> ', da_soonest.get('next_date') < current_booking_date)
# print("(da_soonest.get('next_date') < current_booking_date)", (da_soonest.get('next_date') > current_booking_date))
if DEV_FORCE_BOOKING or (da_soonest.get('next_date') < current_booking_date):
print('>>> We improved our time slot, rescedule for the better position')
print('>>> Get available timeslots for soonest location:')
TIME_SLOTS_URL = BASE_URL + 'AvailableLocationDates'
PAYLOAD = json.dumps({
'LocationId': da_soonest.get('id'),
'PreferredDay': 0,
'SameDay': False,
'TypeId': SERVICE_TYPE,
})
response = requests.request('POST', TIME_SLOTS_URL, headers=HEADERS, data=PAYLOAD)
results = response.json()
last_slot_of_earliest_day = results.get('LocationAvailabilityDates')[DATE_DELAY].get('AvailableTimeSlots')[TIME_SLOT]
pp.pprint(last_slot_of_earliest_day)
results = hold_slot_request(last_slot_of_earliest_day.get('SlotId'))
print('>>> Hold Slot Successful: ', results.get('SlotHeldSuccessfully'))
print('>>> Make the Booking After 2 Seconds Hold:')
time.sleep(2)
response = make_booking_request(last_slot_of_earliest_day, response_id, da_soonest)
print('>>> Final Booking Response:')
booking = response.get('Booking')
if booking.get('ConfirmationNumber'):
print('\n\nNew Booking on: {}, Confirmation # is {}, Site Address is {}'.format(booking.get('BookingDateTime'), booking.get('ConfirmationNumber'), booking.get('SiteAddress')))
print('>>> You Should Be Getting a Email for This Booking From DPS')
else:
print('>>> New booking was not successful, response details: ', response)
else:
print('\n\nNo date to optimize, check again in 5 minutes...\n\n')
for i in range(60 * 24): # should run for 1 day
print('Script has been running for: {} minutes'.format(i))
__main__()
time.sleep(60)
# How to Use:
# set this script on a a cronjob running every X minutes
# python3 makebooking.py
@poweryura
Copy link

good job, you saved half of my day

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment