Skip to content

Instantly share code, notes, and snippets.

@zshanhui
Last active March 15, 2024 04:00
Show Gist options
  • Select an option

  • Save zshanhui/d281e160fec36a3f53b7ff080d052e88 to your computer and use it in GitHub Desktop.

Select an option

Save zshanhui/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
@zshanhui

zshanhui commented Feb 5, 2021

Copy link
Copy Markdown
Author

@poweryura

Copy link
Copy Markdown

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