def handle_throttle(driver, timeout_mins=10): alert('Throttled', 'Sosumi') # Dump source until we're sure we have correct locator for continue button dump_source(driver) try: click_when_enabled(driver, wait_for_element(driver, config.Locators.THROTTLE_CONTINUE), timeout=60) except Exception as e: log.error(e) t = datetime.now() while config.Patterns.THROTTLE_URL in remove_qs(driver.current_url): if int((datetime.now() - t).total_seconds()) > timeout_mins * 60: raise UnhandledRedirect( 'Throttled and timed out waiting for user input') sleep(1)
def wait_for_auth(driver, timeout_mins=10): t = datetime.now() alerted = [] if is_logged_in(driver): log.debug('Already logged in') return log.info('Waiting for user login...') while not is_logged_in(driver): elapsed = int((datetime.now() - t).total_seconds() / 60) if is_logged_in(driver): break elif elapsed > timeout_mins: raise RuntimeError( 'Timed out waiting for login (>= {}min)'.format(timeout_mins)) elif elapsed not in alerted: alerted.append(elapsed) alert('Log in to proceed') sleep(1) log.info('Logged in')
def handle_oos(driver, ignore_oos, timeout_mins=10): try: save_removed_items(driver) except Exception: log.error('Could not save removed items') if ignore_oos: log.warning('Attempting to proceed through OOS alert') click_when_enabled( driver, wait_for_element(driver, config.Locators.OOS_CONTINUE)) else: t = datetime.now() alert( "An item is out of stock. Press continue if you'd like to proceed", 'Sosumi') while config.Patterns.OOS_URL in remove_qs(driver.current_url): if int((datetime.now() - t).total_seconds()) > timeout_mins * 60: raise ItemOutOfStock( 'Encountered OOS alert and timed out waiting for user ' 'input\n Use `ignore-oos` to bypass these alerts') sleep(1)
def main_loop(driver, args): slot_prefs = get_prefs_from_conf() site_config = config.SiteConfig(args.service) wait_for_auth(driver) if args.save_cart: try: save_cart(driver, site_config) except Exception: log.error('Failed to save cart items') slot_route = build_route(site_config, 'SLOT_SELECT', args) slot_route.navigate(driver) slots = get_slots(driver, slot_prefs, slot_route) if slots: annoy() alert('Delivery slots available. What do you need me for?', 'Sosumi') else: executor = ThreadPoolExecutor() while not slots: log.info('No slots found :( waiting...') jitter(config.INTERVAL) driver.refresh() slots = get_slots(driver, slot_prefs, slot_route) if slots: alert('Delivery slots found') message_body = generate_message(slots, args.service, args.checkout) executor.submit(send_sms, message_body) executor.submit(send_telegram, message_body) if not args.checkout: break checked_out = False log.info('Attempting to select slot and checkout') while not checked_out: try: log.info('Selecting slot: ' + slots[0].full_name) slots[0].select() build_route(site_config, 'CHECKOUT', args).navigate(driver) checked_out = True alert('Checkout complete', 'Hero') except RouteRedirect: log.warning('Checkout failed: Redirected to slot select') slots = get_slots(driver, slot_prefs, slot_route) if not slots: break try: executor.shutdown() except Exception as e: log.error(e)
def main_loop(self): wait_for_auth(self) if self.args.save_cart: try: self.save_cart() except Exception: log.error('Failed to save cart items') self.navigate_route('SLOT_SELECT', retry=True) slots = self.get_slots() if slots: annoy() alert('Delivery slots available. What do you need me for?', 'Sosumi') else: self.executor = ThreadPoolExecutor() while not slots: log.info('No slots found :( waiting...') jitter(INTERVAL) self.driver.refresh() slots = self.get_slots() if slots: alert('Delivery slots found') message_body = self.generate_message(slots) self.executor.submit(send_sms, message_body) self.executor.submit(send_telegram, message_body) if not self.args.checkout: break checked_out = False log.info('Attempting to select slot and checkout') while not checked_out: try: log.info('Selecting slot: ' + slots[0].full_name) slots[0].select() self.navigate_route('CHECKOUT') checked_out = True alert('Checkout complete', 'Hero') except RouteRedirect: log.warning( 'Checkout failed: Redirected to slot select') slots = self.get_slots() if not slots: break if self.executor: self.executor.shutdown()
logging.basicConfig( format='[%(asctime)s] {%(funcName)s} %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=logging.INFO if not args.debug else logging.DEBUG) if not args.no_import: # Import appends ./env/lib/.../chromedriver to $PATH import chromedriver_binary log.info('Invoking Selenium Chrome webdriver') opts = Options() opts.add_argument("user-data-dir=" + config.USER_DATA_DIR) driver = webdriver.Chrome(options=opts) try: main_loop(driver, args) except WebDriverException: alert('Encountered an error', 'Basso') if args.debug: dump_source(driver) raise try: # allow time to check out manually min = 15 log.info('Sleeping for {} minutes (press Ctrl+C to close)'.format(min)) sleep(min * 60) except KeyboardInterrupt: log.warning('Slumber disturbed') log.info('Closing webdriver') driver.close()