|
| 1 | +import argparse |
| 2 | +import json |
| 3 | +# from resy_bot.logging import logging |
| 4 | +import os |
| 5 | +import sys |
| 6 | +from resy_bot.models import ResyConfig, TimedReservationRequest |
| 7 | +from resy_bot.manager import ResyManager |
| 8 | +import requests |
| 9 | +from user_agent import generate_user_agent |
| 10 | +from datetime import datetime |
| 11 | +import random |
| 12 | +import time |
| 13 | +from requests import Session, HTTPError |
| 14 | +from resy_bot.errors import NoSlotsError, ExhaustedRetriesError |
| 15 | +from datetime import datetime, timedelta |
| 16 | +from prettytable import PrettyTable |
| 17 | +import logging |
| 18 | +current = os.path.dirname(os.path.realpath(__file__)) |
| 19 | +parent = os.path.dirname(current) |
| 20 | +sys.path.append(parent) |
| 21 | +from settings import CLOSE_MESSAGE, CONTINUE_MESSAGE, TRY_MESSAGE, MIN_IDLE_TIME, MAX_IDLE_TIME |
| 22 | + |
| 23 | +logger = logging.getLogger('simple_example') |
| 24 | +logger.setLevel(logging.DEBUG) |
| 25 | +# create file handler that logs debug and higher level messages |
| 26 | +fh = logging.FileHandler('logs/command1.log') |
| 27 | +fh.setLevel(logging.DEBUG) |
| 28 | +# create console handler with a higher log level |
| 29 | +ch = logging.StreamHandler() |
| 30 | +ch.setLevel(logging.ERROR) |
| 31 | +# create formatter and add it to the handlers |
| 32 | +formatter = logging.Formatter( |
| 33 | + '%(asctime)s - %(name)s - %(levelname)s - %(message)s') |
| 34 | +ch.setFormatter(formatter) |
| 35 | +fh.setFormatter(formatter) |
| 36 | +# add the handlers to logger |
| 37 | +logger.addHandler(ch) |
| 38 | +logger.addHandler(fh) |
| 39 | + |
| 40 | +def random_delay(min_seconds, max_seconds): |
| 41 | + time.sleep(random.uniform(min_seconds, max_seconds)) |
| 42 | + |
| 43 | +def convert24(time): |
| 44 | + t = datetime.strptime(time, '%I:%M %p') |
| 45 | + return t.strftime('%H:%M') |
| 46 | + |
| 47 | +def convert24wsecond(time): |
| 48 | + t = datetime.strptime(time, '%I:%M:%S %p') |
| 49 | + return t.strftime('%H:%M:%S') |
| 50 | + |
| 51 | +def wait_for_drop_time(resy_config: dict, reservation_config: dict) -> str: |
| 52 | + logger.info("waiting for drop time!") |
| 53 | + config_data = resy_config |
| 54 | + reservation_data = reservation_config |
| 55 | + config = ResyConfig(**config_data) |
| 56 | + manager = ResyManager.build(config) |
| 57 | + timed_request = TimedReservationRequest(**reservation_data) |
| 58 | + return manager.make_reservation_at_opening_time(timed_request) |
| 59 | + |
| 60 | +def run_now(resy_config: dict, reservation_config: dict) -> str: |
| 61 | + config_data = resy_config |
| 62 | + reservation_data = reservation_config |
| 63 | + config = ResyConfig(**config_data) |
| 64 | + manager = ResyManager.build(config) |
| 65 | + timed_request = TimedReservationRequest(**reservation_data) |
| 66 | + # breakpoint() |
| 67 | + return manager.make_reservation_with_retries(timed_request.reservation_request) |
| 68 | + |
| 69 | +def main(): |
| 70 | + parser = argparse.ArgumentParser(description="Resy Bot v4") |
| 71 | + parser.add_argument('-u', '--url', type=str,help="Base URL") |
| 72 | + parser.add_argument('-d', '--date', type=str,help="Date wanted") |
| 73 | + parser.add_argument('-t', '--time', type=str,help="Time wanted") |
| 74 | + parser.add_argument('-s', '--seats', type=str,help="Seats count") |
| 75 | + parser.add_argument('-r', '--reservation', type=str,help="Reservation type") |
| 76 | + parser.add_argument('-cp', '--chprofile', type=str,help="Chrome Profile Name") |
| 77 | + parser.add_argument('-rd', '--rdate', type=str,help="Run Date") |
| 78 | + parser.add_argument('-rt', '--rtime', type=str,help="Run Time") |
| 79 | + parser.add_argument('-rh', '--rhours', type=str,help="Range Hours") |
| 80 | + parser.add_argument('-rn', '--runnow', type=str,help="Run Now") |
| 81 | + parser.add_argument('-ns', '--nonstop', type=str,help="Non Stop Checking") |
| 82 | + parser.add_argument('-dr', '--duration', type=str,help="Duration time") |
| 83 | + parser.add_argument('-up', '--proxy', type=str,help="Use Proxy") |
| 84 | + parser.add_argument('-re', '--retry', type=str,help="Retry Count") |
| 85 | + parser.add_argument('-mn', '--minidle', type=str,help="Min Idle Time") |
| 86 | + parser.add_argument('-mx', '--maxidle', type=str,help="Max Idle Time") |
| 87 | + |
| 88 | + args = parser.parse_args() |
| 89 | + if not args.url or not args.date or not args.time or not args.seats or not args.reservation or not args.chprofile or not args.rdate or not args.rtime or not args.rhours or not args.runnow or not args.nonstop or not args.duration or not args.duration or not args.proxy or not args.retry or not args.minidle or not args.maxidle: |
| 90 | + print(" ".join(['Please add complete parameters, ex: python resybotv4b -u [url] -d [dd-mm-yyyy] -t [h:m am/pm] -s [seats_count] -p [period] -r [reservation_type] -cp [chrome_profile] -rd [rdate] -rt [rtime] -rh [rhours] -rn [runnow] -ns [nonstop] -dr [duration] -up [proxy] -re [retry] -mn [minidle] -mx [maxidle]', CLOSE_MESSAGE])) |
| 91 | + sys.exit() |
| 92 | + # breakpoint() |
| 93 | + |
| 94 | + file = open("profilelist.json", "r") |
| 95 | + profilelist = json.load(file) |
| 96 | + for profile in profilelist: |
| 97 | + if profile['email'] == args.chprofile: |
| 98 | + break |
| 99 | + myTable = PrettyTable(["KEY","VALUE"]) |
| 100 | + myTable.align ="l" |
| 101 | + myTable.add_row(["Restaurant", args.url.split("/")[-1]]) |
| 102 | + myTable.add_row(["Date Wanted", args.date]) |
| 103 | + myTable.add_row(["Time Wanted", args.time]) |
| 104 | + myTable.add_row(["Seats", args.seats]) |
| 105 | + myTable.add_row(["Reservation Type", args.reservation]) |
| 106 | + myTable.add_row(["Account", profile['email']]) |
| 107 | + myTable.add_row(["Bot Run Date", args.rdate]) |
| 108 | + myTable.add_row(["Bot Run Time",args.rtime]) |
| 109 | + myTable.add_row(["Range Hours",args.rhours]) |
| 110 | + myTable.add_row(["Run Immediately", args.runnow]) |
| 111 | + myTable.add_row(["Non Stop Checking", args.nonstop]) |
| 112 | + myTable.add_row(["Bot Duration", f"{args.duration} Minute"]) |
| 113 | + myTable.add_row(["Proxy", args.proxy]) |
| 114 | + myTable.add_row(["Retry Count", args.retry]) |
| 115 | + myTable.add_row(["Min Idle Time", args.minidle]) |
| 116 | + myTable.add_row(["Max Idle Time", args.maxidle]) |
| 117 | + # myTable.add_row(["URL", args.url]) |
| 118 | + print(myTable) |
| 119 | + # breakpoint() |
| 120 | + headers = { |
| 121 | + "Authorization": 'ResyAPI api_key="{}"'.format(profile['api_key']), |
| 122 | + "X-Resy-Auth-Token": profile['token'], |
| 123 | + "X-Resy-Universal-Auth": profile['token'], |
| 124 | + "Origin": "https://resy.com", |
| 125 | + "X-origin": "https://resy.com", |
| 126 | + "Referrer": "https://resy.com/", |
| 127 | + "Accept": "application/json, text/plain, */*", |
| 128 | + "User-Agent": generate_user_agent(), |
| 129 | + 'Cache-Control': "no-cache", |
| 130 | + } |
| 131 | + params = { |
| 132 | + 'url_slug': str(args.url).split("/")[-1], |
| 133 | + 'location': str(args.url).split("/")[-3], |
| 134 | + } |
| 135 | + try: |
| 136 | + session = Session() |
| 137 | + response = session.get('https://api.resy.com/3/venue', params=params, headers=headers) |
| 138 | + venue_id = response.json()['id']['resy'] |
| 139 | + https_proxy = '' |
| 140 | + http_proxy = '' |
| 141 | + if args.proxy != '<Not Set>': |
| 142 | + file = open("proxylist.json", "r") |
| 143 | + listvalue = json.load(file) |
| 144 | + proxy = [prof for prof in listvalue if prof['profilename']==args.proxy] |
| 145 | + http_proxy = proxy[0]['http_proxy'] |
| 146 | + https_proxy = proxy[0]['https_proxy'] |
| 147 | + resy_config = {"api_key": profile['api_key'], "token": profile["token"], "payment_method_id":profile["payment_method_id"], "email":profile["email"], "password":profile["password"], "http_proxy":http_proxy, "https_proxy": https_proxy, "retry_count": int(args.retry)} |
| 148 | + |
| 149 | + if args.reservation == '<Not Set>': |
| 150 | + reservation_type = None |
| 151 | + else: |
| 152 | + reservation_type = args.reservation |
| 153 | + # breakpoint() |
| 154 | + reservation_config = { |
| 155 | + "reservation_request": { |
| 156 | + "party_size": args.seats, |
| 157 | + "venue_id": venue_id, |
| 158 | + "window_hours": args.rhours, |
| 159 | + "prefer_early": False, |
| 160 | + "ideal_date": args.date, |
| 161 | + # "days_in_advance": 14, |
| 162 | + "ideal_hour": int(convert24(args.time).split(":")[0]), |
| 163 | + "ideal_minute": int(convert24(args.time).split(":")[1]), |
| 164 | + "preferred_type": reservation_type |
| 165 | + }, |
| 166 | + "expected_drop_hour": int(convert24wsecond(args.rtime).split(":")[0]), |
| 167 | + "expected_drop_minute": int(convert24wsecond(args.rtime).split(":")[1]), |
| 168 | + "expected_drop_second": int(convert24wsecond(args.rtime).split(":")[2]), |
| 169 | + "expected_drop_year":str(args.rdate).split("-")[0], |
| 170 | + "expected_drop_month":str(args.rdate).split("-")[1], |
| 171 | + "expected_drop_day":str(args.rdate).split("-")[2], |
| 172 | + } |
| 173 | + except KeyError as e: |
| 174 | + print("KeyError", e) |
| 175 | + print("Error Accurred " + CLOSE_MESSAGE) |
| 176 | + sys.exit() |
| 177 | + except Exception as e: |
| 178 | + print("Exception", e) |
| 179 | + print("Error Accurred " + CLOSE_MESSAGE) |
| 180 | + sys.exit() |
| 181 | + |
| 182 | + if args.nonstop == 'No': |
| 183 | + try: |
| 184 | + if args.runnow == "No": |
| 185 | + wait_for_drop_time(resy_config=resy_config, reservation_config=reservation_config) |
| 186 | + else: |
| 187 | + run_now(resy_config=resy_config, reservation_config=reservation_config) |
| 188 | + print("Reservation Success..." + CLOSE_MESSAGE) |
| 189 | + except (HTTPError, ExhaustedRetriesError, NoSlotsError) as e: |
| 190 | + print("Reservation Failed: " + str(e) + CLOSE_MESSAGE) |
| 191 | + except IndexError as e: |
| 192 | + print("Reservation Error: " + str(e) + CLOSE_MESSAGE) |
| 193 | + except Exception as e: |
| 194 | + print("Application Error: " + str(e) + CLOSE_MESSAGE) |
| 195 | + |
| 196 | + else: |
| 197 | + if args.runnow == "No": |
| 198 | + stoptime = datetime.strptime(f"{args.rdate} {args.rtime}", '%Y-%m-%d %I:%M:%S %p') + timedelta(minutes = int(args.duration)) |
| 199 | + else: |
| 200 | + stoptime = datetime.now() + timedelta(minutes = int(args.duration)) |
| 201 | + |
| 202 | + if datetime.strptime(f"{args.rdate} {args.rtime}", '%Y-%m-%d %I:%M:%S %p') < datetime.now(): |
| 203 | + stoptime = datetime.now() + timedelta(minutes = int(args.duration)) |
| 204 | + while True: |
| 205 | + # sleeptime = random.uniform(10, 30) |
| 206 | + if int(args.duration) != 0 and datetime.now() >= stoptime: |
| 207 | + print(f"Duration time reached -> {args.duration} minutes") |
| 208 | + break |
| 209 | + sleeptime = random.uniform(int(args.minidle), int(args.maxidle)) |
| 210 | + try: |
| 211 | + if args.runnow == "No": |
| 212 | + wait_for_drop_time(resy_config=resy_config, reservation_config=reservation_config) |
| 213 | + else: |
| 214 | + run_now(resy_config=resy_config, reservation_config=reservation_config) |
| 215 | + print("Reservation Success..." + CLOSE_MESSAGE) |
| 216 | + break |
| 217 | + except (HTTPError, ExhaustedRetriesError, NoSlotsError) as e: |
| 218 | + print("Reservation Failed: " + str(e) + TRY_MESSAGE) |
| 219 | + print("idle time", int(sleeptime), "seconds") |
| 220 | + time.sleep(sleeptime) |
| 221 | + continue |
| 222 | + except IndexError as e: |
| 223 | + print("Reservation Error: " + str(e) + TRY_MESSAGE) |
| 224 | + print("idle time", int(sleeptime), "seconds") |
| 225 | + time.sleep(sleeptime) |
| 226 | + continue |
| 227 | + except Exception as e: |
| 228 | + print("Application Error: " + str(e) + TRY_MESSAGE) |
| 229 | + print("idle time", int(sleeptime), "seconds") |
| 230 | + time.sleep(sleeptime) |
| 231 | + continue |
| 232 | + |
| 233 | +if __name__ == "__main__": |
| 234 | + main() |
0 commit comments