From b7828fe9c66fcdf7da102b91fa567bbde182f796 Mon Sep 17 00:00:00 2001 From: DakkJaniels <6080734+DakkJaniels@users.noreply.github.com> Date: Wed, 28 Apr 2021 16:50:09 -0400 Subject: [PATCH 1/3] maybe BIN implementation for selenium? --- stores/amazon.py | 126 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 88 insertions(+), 38 deletions(-) diff --git a/stores/amazon.py b/stores/amazon.py index 676aaecb..a8793e17 100644 --- a/stores/amazon.py +++ b/stores/amazon.py @@ -44,6 +44,7 @@ from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait +import utils.selenium_utils from utils import discord_presence as presence from utils.debugger import debug from utils.logger import log @@ -264,40 +265,43 @@ def run(self, delay=DEFAULT_REFRESH_DELAY, test=False): while continue_stock_check: self.unknown_title_notification_sent = False asin = self.run_asins(delay) - # found something in stock and under reserve - # initialize loop limiter variables - self.try_to_checkout = True - self.checkout_retry = 0 - self.order_retry = 0 - loop_iterations = 0 - self.great_success = False - while self.try_to_checkout: - try: - self.navigate_pages(test) - # if for some reason page transitions in the middle of checking elements, don't break the program - except sel_exceptions.StaleElementReferenceException: - pass - # if successful after running navigate pages, remove the asin_list from the list - if ( - not self.try_to_checkout - and not self.single_shot - and self.great_success - ): - self.remove_asin_list(asin) - # checkout loop limiters - elif self.checkout_retry > DEFAULT_MAX_PTC_TRIES: - self.try_to_checkout = False - self.fail_to_checkout_note() - elif self.order_retry > DEFAULT_MAX_PYO_TRIES: - self.try_to_checkout = False - self.fail_to_checkout_note() - loop_iterations += 1 - if loop_iterations > DEFAULT_MAX_CHECKOUT_LOOPS: - self.fail_to_checkout_note() - self.try_to_checkout = False - # if no items left it list, let loop end - if not self.asin_list: + self.remove_asin_list(asin) + if not self.asin_list or self.single_shot: continue_stock_check = False + # # found something in stock and under reserve + # # initialize loop limiter variables + # self.try_to_checkout = True + # self.checkout_retry = 0 + # self.order_retry = 0 + # loop_iterations = 0 + # self.great_success = False + # while self.try_to_checkout: + # try: + # self.navigate_pages(test) + # # if for some reason page transitions in the middle of checking elements, don't break the program + # except sel_exceptions.StaleElementReferenceException: + # pass + # # if successful after running navigate pages, remove the asin_list from the list + # if ( + # not self.try_to_checkout + # and not self.single_shot + # and self.great_success + # ): + # self.remove_asin_list(asin) + # # checkout loop limiters + # elif self.checkout_retry > DEFAULT_MAX_PTC_TRIES: + # self.try_to_checkout = False + # self.fail_to_checkout_note() + # elif self.order_retry > DEFAULT_MAX_PYO_TRIES: + # self.try_to_checkout = False + # self.fail_to_checkout_note() + # loop_iterations += 1 + # if loop_iterations > DEFAULT_MAX_CHECKOUT_LOOPS: + # self.fail_to_checkout_note() + # self.try_to_checkout = False + # # if no items left it list, let loop end + # if not self.asin_list: + # continue_stock_check = False runtime = time.time() - self.start_time log.info(f"FairGame bot ran for {runtime} seconds.") time.sleep(10) # add a delay to shut stuff done @@ -775,14 +779,12 @@ def check_stock(self, asin, reserve_min, reserve_max, retry=0): if offering_id_elements: log.info("Attempting Add To Cart with offer ID...") offering_id = offering_id_elements[0].get_attribute("value") - if self.attempt_atc( - offering_id, max_atc_retries=DEFAULT_MAX_ATC_TRIES - ): + if self.buy_it_now(offering_id, max_atc_retries=20): return True else: self.send_notification( - "Failed Add to Cart after {max-atc-retries}", - "failed-atc", + "Failed Buy it Now ", + "failed-BIN", self.take_screenshots, ) self.save_page_source("failed-atc") @@ -854,6 +856,54 @@ def check_stock(self, asin, reserve_min, reserve_max, retry=0): log.info(f"Offers exceed price range ({reserve_min:.2f}-{reserve_max:.2f})") return in_stock + def buy_it_now(self, offering_id, max_atc_retries=DEFAULT_MAX_ATC_TRIES): + retry = 0 + successful = False + while not successful: + buy_it_now_url = f"https://{self.amazon_website}/checkout/turbo-initiate?ref_=dp_start-bbf_1_glance_buyNow_2-1&pipelineType=turbo&weblab=RCX_CHECKOUT_TURBO_DESKTOP_NONPRIME_87784&temporaryAddToCart=1&offerListing.1={offering_id}&quantity.1=1" + with self.wait_for_page_content_change(): + self.driver.get(buy_it_now_url) + timeout = self.get_timeout(5) + while self.driver.title == "" and time.time() < timeout: + time.sleep(0.5) + if self.driver.title != "Place Your Order - AmazonSmile Checkout": + retry += 1 + if retry > max_atc_retries: + return False + continue + try: + place_order_button = self.driver.find_element_by_xpath( + "//input[@id='turbo-checkout-pyo-button' and @type='submit']" + ) + except sel_exceptions.NoSuchElementException: + log.info("No PYO button found, don't ask why") + retry += 1 + if retry > max_atc_retries: + return False + continue + if place_order_button: + try: + with self.wait_for_page_content_change(): + place_order_button.click() + except sel_exceptions.WebDriverException: + log.info("Could not click button, don't ask why") + retry += 1 + if retry > max_atc_retries: + return False + continue + timeout = self.get_timeout(5) + while self.driver.title == "" and time.time() < timeout: + time.sleep(0.5) + if self.driver.title in amazon_config["ORDER_COMPLETE_TITLES"]: + log.info("maybe this worked, check your orders") + self.save_screenshot("Order-Complete-Maybe") + successful = True + else: + log.info("maybe this didn't work, check your orders") + self.save_screenshot("Order-Maybe-Not-Complete") + successful = True + return True + def attempt_atc(self, offering_id, max_atc_retries=DEFAULT_MAX_ATC_TRIES): # Open the add.html URL in Selenium f = f"{AMAZON_URLS['ATC_URL']}?OfferListingId.1={offering_id}&Quantity.1=1" From 7ae2aff645991f66461635c198452d6b4c062f27 Mon Sep 17 00:00:00 2001 From: DakkJaniels <6080734+DakkJaniels@users.noreply.github.com> Date: Thu, 29 Apr 2021 06:55:05 -0400 Subject: [PATCH 2/3] add flag to go back to old ATC method add flag to go back to old ATC method, update version number for release --- cli/cli.py | 8 ++-- stores/amazon.py | 109 +++++++++++++++++++++++++++-------------------- utils/version.py | 2 +- 3 files changed, 68 insertions(+), 51 deletions(-) diff --git a/cli/cli.py b/cli/cli.py index 0aa041a6..09e40f25 100644 --- a/cli/cli.py +++ b/cli/cli.py @@ -187,10 +187,10 @@ def main(): help="Purge Amazon credentials and prompt for new credentials", ) @click.option( - "--alt-offers", + "--alt-checkout", is_flag=True, default=False, - help="Directly hit the offers page. Preferred, but deprecated by Amazon.", + help="Use old add to cart method. Not preferred", ) @click.option( "--captcha-wait", @@ -217,7 +217,7 @@ def amazon( shipping_bypass, clean_profile, clean_credentials, - alt_offers, + alt_checkout, captcha_wait, ): notification_handler.sound_enabled = not disable_sound @@ -250,7 +250,7 @@ def amazon( encryption_pass=p, log_stock_check=log_stock_check, shipping_bypass=shipping_bypass, - alt_offers=alt_offers, + alt_checkout=alt_checkout, wait_on_captcha_fail=captcha_wait, ) try: diff --git a/stores/amazon.py b/stores/amazon.py index a8793e17..b9737766 100644 --- a/stores/amazon.py +++ b/stores/amazon.py @@ -112,6 +112,7 @@ def __init__( shipping_bypass=False, alt_offers=False, wait_on_captcha_fail=False, + alt_checkout=False, ): self.notification_handler = notification_handler self.asin_list = [] @@ -144,6 +145,7 @@ def __init__( self.unknown_title_notification_sent = False self.alt_offers = alt_offers self.wait_on_captcha_fail = wait_on_captcha_fail + self.alt_checkout = alt_checkout presence.enabled = not disable_presence @@ -265,43 +267,46 @@ def run(self, delay=DEFAULT_REFRESH_DELAY, test=False): while continue_stock_check: self.unknown_title_notification_sent = False asin = self.run_asins(delay) - self.remove_asin_list(asin) - if not self.asin_list or self.single_shot: - continue_stock_check = False - # # found something in stock and under reserve - # # initialize loop limiter variables - # self.try_to_checkout = True - # self.checkout_retry = 0 - # self.order_retry = 0 - # loop_iterations = 0 - # self.great_success = False - # while self.try_to_checkout: - # try: - # self.navigate_pages(test) - # # if for some reason page transitions in the middle of checking elements, don't break the program - # except sel_exceptions.StaleElementReferenceException: - # pass - # # if successful after running navigate pages, remove the asin_list from the list - # if ( - # not self.try_to_checkout - # and not self.single_shot - # and self.great_success - # ): - # self.remove_asin_list(asin) - # # checkout loop limiters - # elif self.checkout_retry > DEFAULT_MAX_PTC_TRIES: - # self.try_to_checkout = False - # self.fail_to_checkout_note() - # elif self.order_retry > DEFAULT_MAX_PYO_TRIES: - # self.try_to_checkout = False - # self.fail_to_checkout_note() - # loop_iterations += 1 - # if loop_iterations > DEFAULT_MAX_CHECKOUT_LOOPS: - # self.fail_to_checkout_note() - # self.try_to_checkout = False - # # if no items left it list, let loop end - # if not self.asin_list: - # continue_stock_check = False + # New normal (buy it now) + if not self.alt_checkout: + self.remove_asin_list(asin) + if not self.asin_list or self.single_shot: + continue_stock_check = False + else: + # found something in stock and under reserve + # initialize loop limiter variables + self.try_to_checkout = True + self.checkout_retry = 0 + self.order_retry = 0 + loop_iterations = 0 + self.great_success = False + while self.try_to_checkout: + try: + self.navigate_pages(test) + # if for some reason page transitions in the middle of checking elements, don't break the program + except sel_exceptions.StaleElementReferenceException: + pass + # if successful after running navigate pages, remove the asin_list from the list + if ( + not self.try_to_checkout + and not self.single_shot + and self.great_success + ): + self.remove_asin_list(asin) + # checkout loop limiters + elif self.checkout_retry > DEFAULT_MAX_PTC_TRIES: + self.try_to_checkout = False + self.fail_to_checkout_note() + elif self.order_retry > DEFAULT_MAX_PYO_TRIES: + self.try_to_checkout = False + self.fail_to_checkout_note() + loop_iterations += 1 + if loop_iterations > DEFAULT_MAX_CHECKOUT_LOOPS: + self.fail_to_checkout_note() + self.try_to_checkout = False + # if no items left it list, let loop end + if not self.asin_list: + continue_stock_check = False runtime = time.time() - self.start_time log.info(f"FairGame bot ran for {runtime} seconds.") time.sleep(10) # add a delay to shut stuff done @@ -779,16 +784,28 @@ def check_stock(self, asin, reserve_min, reserve_max, retry=0): if offering_id_elements: log.info("Attempting Add To Cart with offer ID...") offering_id = offering_id_elements[0].get_attribute("value") - if self.buy_it_now(offering_id, max_atc_retries=20): - return True + if not self.alt_checkout: + if self.buy_it_now(offering_id, max_atc_retries=20): + return True + else: + self.send_notification( + "Failed Buy it Now ", + "failed-BIN", + self.take_screenshots, + ) + self.save_page_source("failed-atc") + return False else: - self.send_notification( - "Failed Buy it Now ", - "failed-BIN", - self.take_screenshots, - ) - self.save_page_source("failed-atc") - return False + if self.attempt_atc(offering_id): + return True + else: + self.send_notification( + "Failed ATC ", + "failed-ATC", + self.take_screenshots, + ) + self.save_page_source("failed-atc") + return False else: log.error( "Unable to find offering ID to add to cart. Using legacy mode." diff --git a/utils/version.py b/utils/version.py index 3dcc7096..de4564da 100644 --- a/utils/version.py +++ b/utils/version.py @@ -29,7 +29,7 @@ # See https://www.python.org/dev/peps/pep-0440/ for specification # See https://www.python.org/dev/peps/pep-0440/#examples-of-compliant-version-schemes for examples -__VERSION = "0.6.5" +__VERSION = "0.6.6" version = Version(__VERSION) From 10a58f386819e1d6111786e16cec137e229e013f Mon Sep 17 00:00:00 2001 From: DakkJaniels <6080734+DakkJaniels@users.noreply.github.com> Date: Thu, 29 Apr 2021 09:06:34 -0400 Subject: [PATCH 3/3] Update amazon.py fix formatting --- stores/amazon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stores/amazon.py b/stores/amazon.py index b9737766..b32770b1 100644 --- a/stores/amazon.py +++ b/stores/amazon.py @@ -2004,7 +2004,7 @@ def from_str(cls, label): def get_item_condition(form_action) -> AmazonItemCondition: - """ Attempts to determine the Item Condition from the Add To Cart form action """ + """Attempts to determine the Item Condition from the Add To Cart form action""" if "_new_" in form_action: # log.debug(f"Item condition is new") return AmazonItemCondition.New