Skip to content

Instantly share code, notes, and snippets.

@dat-vikash
Last active August 5, 2025 09:35
Show Gist options
  • Save dat-vikash/2280dd53f37a6406fb73b719dc6f8cb2 to your computer and use it in GitHub Desktop.
Save dat-vikash/2280dd53f37a6406fb73b719dc6f8cb2 to your computer and use it in GitHub Desktop.
script to help golf hacker
"""
Helps make dad life easier by reserving times before pre-k.
Features:
- allow for filtering by time, day of week, number of player and course
- handles reservations and ensures no overbooking
- keeps track of default and state
Requires python3 and selenium:
+ https://sites.google.com/chromium.org/driver/
+ https://selenium-python.readthedocs.io/
"""
try:
from urllib.request import Request, urlopen # Python 3
except ImportError:
from urllib2 import Request, urlopen # Python 2
import json
import datetime
import time
import os
from selenium import webdriver
import pytz
COURSEIDS_MAP = {
"B": {"name":"Francis A. Byrne Golf Course", "id":"54f14d8a0c8ad60378b03e95"},
"H": {"name":"Hendricks Field Golf Course", "id":"54f14d8a0c8ad60378b03e98"},
"W": {"name":"Weequahic Park Golf Course", "id":"54f14d8b0c8ad60378b03e9c"}
}
local_tz = pytz.timezone('US/Eastern')
AUTO_RESERVATION=False
def login(driver):
"""
login to site
"""
driver.get(f"https://essex-group.book.teeitup.golf/login")
time.sleep(5)
username = driver.find_element("id","txtUsername")
password = driver.find_element("id","txtPassword")
# FILL in your county log in here
username.send_keys("")
password.send_keys("")
# Login and return driver with auth context
driver.find_element("xpath","//button[@data-testid='login-button']").click()
return driver
def reserve(tees):
"""
does the actual reservation
"""
reservations = load_reservations()
reservations_weekly_max = tees[0]['rounds_per_week']
reservations_daily_max = tees[0]['rounds_per_day']
# todo: get existing reservations
# existing_reservations = 0
#
# reservations_weekly_max -= existing_reservations
# reservations_daily_max -= existing_reservations
drivers = []
for tee in tees:
local_time = tee['teetime'].astimezone(local_tz)
course_name = list(
filter(None,
[COURSEIDS_MAP[c]['name'] if COURSEIDS_MAP[c]['id'] == tee['course'] else '' for c in COURSEIDS_MAP]
)
)[0]
if int(reservations['daily'].get(tee['date'], reservations_daily_max)) > 0 \
and int(reservations['weekly'].get(local_time.strftime("%V"), reservations_weekly_max)) > 0 : # weekly check
print(f"Reserving Tee time for Date: {tee['date'] } Time: {local_time.strftime('%H:%M %p')} Course: {course_name} Available: {tee['playersAvail']} Fee: ${tee['fee']/100} Booking: {tee['num_player_booking']}")
# start driver and get auth context
driver = webdriver.Chrome()
driver = login(driver)
time.sleep(5)
drivers.append(driver)
# time to book it
driver.get(f"https://essex-group.book.teeitup.golf/?course={tee['course']}&date={tee['date']}")
t = tee['teetime'].strftime('%-H:%M %p')
time.sleep(5)
# find the tee time
button = driver.find_element(f"xpath",f"//p[@data-testid='teetimes-tile-time'][contains(., '{t}')]").find_element("xpath","./../..").find_element("xpath","//button[@data-testid='teetimes_book_now_button']")
button.click()
time.sleep(3)
# checkout
driver.find_element("xpath",f"//button[@data-testid='button-value-{tee['num_player_booking']}']").click()
driver.find_element("xpath","//span[contains(.,'Proceed to Checkout')]").click()
time.sleep(5)
# no turning back
driver.find_element("name","chb-nm").click()
# todo: actually checkout
# driver.find_element("xpath","//button[@data-testid='make-your-reservation-btn']").click()
# update reservations
reservations['daily'].update({tee['date']: reservations['daily'].get(tee['date'],reservations_daily_max) - 1 })
reservations['weekly'].update({local_time.strftime("%V"): reservations['weekly'].get(local_time.strftime("%V"),reservations_weekly_max) - 1 })
else:
print(f"Daily/Weekly Max Filled for Reserving Tee time for Date: {tee['date'] } Time: {local_time.strftime('%H:%M %p')} Course: {course_name} Available: {tee['playersAvail']} Fee: ${tee['fee']/100} Booking: {tee['num_player_booking']}")
time.sleep(5)
for d in drivers:
d.quit()
write_reservations(reservations)
def banner():
banner = """
'\ . . |>18>>
\ . ' . |
O>> . 'o |
\ . |
/\ . |
/ / .' |
jgs^^^^^^^`^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
"""
print(banner)
def ranger(teetimes):
for t in teetimes:
print("Date: " + t['date'] + " Time: " + t['teetime'].strftime('%H:%M'))
count = 0
while count < 300:
if count % 30 :
os.system('say -v "Victoria" "TEE TIMES FOUND, HURRY UP"')
time.sleep(1)
count += 1
def load_reservations():
data = []
if os.path.exists('reservations.json'):
f = open('reservations.json')
data = json.load(f)
f.close()
else:
data = {'weekly':{}, 'daily':{}}
return data
def write_reservations(payload):
if os.path.exists('reservations.json'):
pass
else:
print("reservations File created.")
with open('reservations.json', "w") as outfile:
outfile.write(json.dumps(payload))
return
def load_defaults():
data = None
# Opening JSON file
if os.path.exists('config.json'):
f = open('config.json')
data = json.load(f)
f.close()
return data
def write_defaults(payload):
if os.path.exists('config.json'):
pass
else:
print("Default File created.")
with open('config.json', "w") as outfile:
outfile.write(json.dumps(payload))
return
def get_filters():
# load defaults
defaults = load_defaults()
if defaults is None:
print(""""
##### Inital Setup: Fill out scorecard ####
""")
# get round details
starting_tee_time = int(input("Enter the earliest hour you can tee off in 24 hour time (ex: 7 for 7 am, 13 for 1 pm): "))
ending_tee_time = int(input("Enter the latest hour you can tee off in 24 hour time (ex: 7 for 7 am, 13 for 1 pm): "))
num_players = int(input("How many players to reserve?: "))
auto_reserve = input("Should I auto reserve for you (y for yes, n for no)?: ")
# print out course
print("\n### Course's Available ###")
[print(key + " - " + COURSEIDS_MAP[key]['name']) for key in COURSEIDS_MAP.keys()]
print("A - ALL")
courses = input(f"Which course(s) to search?: ") or "A"
dow = input("""Which day(s) of the week:
1 = Monday
2 = Tuesday
3 = Wednesday
4 = Thursday
5 = Friday
6 = Saturday
7 = Sunday
A = ALL
Ex: 1,2 for mon/tues or 1 for mon only or A for all): """) or "A"
rounds_per_week = int(input("How many rounds to reserve per week? : ") or 1)
rounds_per_day = int(input("How many rounds to reserve per day? : ") or 1)
if dow == "A" or dow == "a":
dow = None
else:
dow = dow.split(",")
if courses == 'A':
courses = ",".join(list([f"{COURSEIDS_MAP[key]['id']}" for key in COURSEIDS_MAP.keys()]))
else:
courses = COURSEIDS_MAP[courses]['id']
else:
# use defaults
print("Loading defaults")
starting_tee_time = defaults['starting_tee_time']
ending_tee_time = defaults['ending_tee_time']
courses = defaults['courses']
dow = defaults['dow']
num_players = defaults['num_players']
rounds_per_week = defaults['rounds_per_week']
rounds_per_day = defaults['rounds_per_day']
auto_reserve = defaults['auto_reserve']
if auto_reserve == 'y':
AUTO_RESERVATION = True
# Adjust for utc
starting_tee_time += 4
ending_tee_time += 4
# get date range for next couple days
base = datetime.datetime.today()
numdays=10
date_list = [base + datetime.timedelta(days=x) for x in range(numdays)]
# figure out tee times
all_times = {}
for date in date_list:
#check filters
if dow is None or str(date.isoweekday()) in dow:
# get tee times
date_str = date.strftime('%Y-%m-%d')
print("Working on tee times for day: " + date_str)
req = Request(f"https://phx-api-be-east-1b.kenna.io/tee-times?date={date_str}&courseIds={courses}")
req.add_header('x-be-alias', 'essex-group')
content = urlopen(req).read()
info = json.loads(content)
tee_times = []
for result in info:
tee_times += result['teetimes']
newlist = sorted(tee_times, key=lambda d: datetime.datetime.strptime(d['teetime'], '%Y-%m-%dT%H:%M:%S.%fZ'))
all_times[date_str]=newlist
time.sleep(1)
# filter for acceptable times
final_tees = []
for dayslot in all_times:
for timeslot in all_times[dayslot]:
hour = datetime.datetime.strptime(timeslot['teetime'], '%Y-%m-%dT%H:%M:%S.%fZ').hour
playerBookedCount = timeslot['bookedPlayers']
playerAvailCount = timeslot['maxPlayers']
if (hour >= starting_tee_time) \
and (hour < ending_tee_time) \
and playerAvailCount >= num_players:
final_tees.append({"date":dayslot,
"teetime": datetime.datetime.strptime(timeslot['teetime'], '%Y-%m-%dT%H:%M:%S.%fZ') - datetime.timedelta(hours=4),
"playersBooked": playerBookedCount,
"playersAvail": playerAvailCount,
"course":timeslot['courseId'],
"fee": timeslot['rates'][0]['greenFeeWalking'],
"num_player_booking":num_players,
"rounds_per_day":rounds_per_day,
"rounds_per_week":rounds_per_week})
write_defaults({'starting_tee_time':starting_tee_time-4, 'ending_tee_time':ending_tee_time-4, 'courses':courses, 'dow':dow, 'num_players':num_players,"rounds_per_day":rounds_per_day,
"rounds_per_week":rounds_per_week, 'auto_reserve':auto_reserve})
return final_tees
def runner():
banner()
final_tees = get_filters()
if len(final_tees) > 0:
print("Times Available")
for idx,t in enumerate(final_tees):
local_time = t['teetime'].astimezone(local_tz)
course_name = list(
filter(None,
[COURSEIDS_MAP[c]['name'] if COURSEIDS_MAP[c]['id'] == t['course'] else '' for c in COURSEIDS_MAP]
)
)[0]
print(f"[{idx}] Date: {t['date'] } Time: {local_time.strftime('%H:%M %p')} Course: {course_name} Available: {t['playersAvail']} Fee: ${t['fee']/100}")
do_reserve = 'n'
if AUTO_RESERVATION is False:
do_reserve = input("Proceed with reservation? , y for yes, n for no: ", "n")
if AUTO_RESERVATION or do_reserve == "y":
reserve(final_tees)
if __name__ == "__main__":
runner()
@dat-vikash
Copy link
Author

dat-vikash commented Aug 2, 2022

python3 caddie_bot.py

      '\                   .  .                        |>18>>
        \              .         ' .                   |
       O>>         .                 'o                |
        \       .                                      |
        /\    .                                        |
       / /  .'                                         |
 jgs^^^^^^^`^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


    ##### Fill out scorecard ####

Enter the earliest hour you can tee off in 24 hour time (ex: 7 for 7 am, 13 for 1 pm): 6
Enter the latest hour you can tee off in 24 hour time (ex: 7 for 7 am, 13 for 1 pm): 7
How many players to reserve?: 1

### Course's Available ###
B - Francis A. Byrne Golf Course
H - Hendricks Field Golf Course
W - Weequahic Park Golf Course
A - ALL
Which course(s) to search?: B
Which day(s) of the week:
      1 = Monday
      2 = Tuesday
      3 = Wednesday
      4 = Thursday
      5 = Friday
      6 = Saturday
      7 = Sunday
      A = ALL
    Ex: 1,2 for mon/tues or 1 for mon only or A for all): 4
Working on tee times for day: 2022-08-04
Working on tee times for day: 2022-08-11
Default File created.
Times Available
[0] Date: 2022-08-04 Time: 06:24 AM Course: Francis A. Byrne Golf Course Available: 3  Fee: $54.0
Proceed with reservation? , y for yes, n for no: y
Reserving Tee time for Date: 2022-08-04 Time: 06:24 AM Course: Francis A. Byrne Golf Course Available: 3  Fee: $54.0  Booking: 1

@apwilliamson
Copy link

How do you pull the course ids?

COURSEIDS_MAP = {
"B": {"name":"Francis A. Byrne Golf Course", "id":"54f14d8a0c8ad60378b03e95"},
"H": {"name":"Hendricks Field Golf Course", "id":"54f14d8a0c8ad60378b03e98"},
"W": {"name":"Weequahic Park Golf Course", "id":"54f14d8b0c8ad60378b03e9c"}

@solutionsh724
Copy link

solutionsh724 commented Jul 30, 2025

I’ll be honest—my house needed work. The foundation had shifted, there was water damage in the basement, and the kitchen hadn’t been updated in decades. I thought I’d have to spend tens of thousands just to list it.
But then I heard about Favor Home Solutions from someone who’d sold in similar condition. They explained I could sell without touching a thing. I was skeptical at first, but they walked me through the process and showed me how it could work.
And it did. I sold my home as-is, no repairs, no contractors, and no out-of-pocket expenses. I never thought that was even possible. If your house has seen better days and you’re not up for fixing it all, I’d suggest looking into what Favor Home Solutions can do.
Visit Them: https://favorhomesolutions.com/tennessee-washington-county-johnson-city-sell-house-for-cash//

@landscpeingse
Copy link

Edging adds structure and definition to gardens and lawns, making your landscaping look polished and well-maintained. Traditional edging materials include brick, stone, and metal, but there are plenty of creative alternatives. Wooden logs or reclaimed railroad ties add rustic charm, while recycled glass or colorful tiles bring a unique artistic touch. Living edging — such as low-growing plants like lavender, thyme, or boxwood — provides a soft, natural border. Curved edging lines can make gardens feel more organic, while straight lines create a clean, formal look. Edging also prevents grass from invading flower beds and makes mowing easier. By choosing a style that complements your home and garden design, you can transform your landscape with this simple yet effective upgrade.
Searching for “landscaping near me”? We provide professional, reliable landscaping services designed to make your outdoor space both beautiful and functional. Our team specializes in everything from lawn care and garden design to patios, walkways, and seasonal cleanups, all tailored to your property’s unique needs.

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