示例#1
0
    def __init__(self, gpu, locale="en_us"):
        self.product_ids = []
        self.cli_locale = locale.lower()
        self.locale = self.map_locales()
        self.session = requests.Session()
        self.gpu = gpu
        self.enabled = True
        try:
            self.gpu_long_name = GPU_DISPLAY_NAMES[gpu]
        except Exception as e:
            log.error("Invalid GPU name.")
            raise e

        adapter = HTTPAdapter(max_retries=Retry(
            total=10,
            backoff_factor=1,
            status_forcelist=[429, 500, 502, 503, 504],
            method_whitelist=["HEAD", "GET", "OPTIONS"],
        ))
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)
        self.notification_handler = NotificationHandler()
        self.autobuy_handler = AutoBuy()

        log.info("Getting product IDs")
        self.get_product_ids()
        while len(self.product_ids) == 0:
            log.info(
                f"We have no product IDs for {self.gpu_long_name}, retrying until we get a product ID"
            )
            self.get_product_ids()
            sleep(5)
示例#2
0
    def __init__(self, gpu, locale="en_us", test=False, interval=5):
        self.product_ids = set([])
        self.cli_locale = locale.lower()
        self.locale = self.map_locales()
        self.session = requests.Session()
        self.gpu = gpu
        self.enabled = True
        self.auto_buy_enabled = False
        self.attempt = 0
        self.started_at = datetime.now()
        self.test = test
        self.interval = interval

        self.gpu_long_name = GPU_DISPLAY_NAMES[gpu]

        # Disable auto_buy_enabled if the user does not provide a bool.
        if type(self.auto_buy_enabled) != bool:
            self.auto_buy_enabled = False

        adapter = TimeoutHTTPAdapter()
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)
        self.notification_handler = NotificationHandler()

        self.get_product_ids()
示例#3
0
    def __init__(self, debug=False):
        self.notification_handler = NotificationHandler()
        self.driver = webdriver.Chrome(executable_path=binary_path,
                                       options=options,
                                       chrome_options=chrome_options)
        self.credit_card = {}
        try:
            if path.exists(CONFIG_PATH):
                with open(CONFIG_PATH) as json_file:
                    config = json.load(json_file)
                    username = config["username"]
                    password = config["password"]
                    self.credit_card["name"] = config["credit_card"]["name"]
                    self.credit_card["number"] = config["credit_card"][
                        "number"]
                    self.credit_card["cvv"] = config["credit_card"]["cvv"]
                    self.credit_card["expiration_month"] = config[
                        "credit_card"]["expiration_month"]
                    self.credit_card["expiration_year"] = config[
                        "credit_card"]["expiration_year"]
        except Exception as e:
            log.error(
                f"This is most likely an error with your {CONFIG_PATH} file.")
            raise e

        self.login(username, password)
示例#4
0
    def __init__(self, gpu, locale="en_us", test=False, interval=5):
        self.product_ids = set([])
        self.cli_locale = locale.lower()
        self.locale = self.map_locales()
        self.session = requests.Session()
        self.gpu = gpu
        self.enabled = True
        self.auto_buy_enabled = False
        self.attempt = 0
        self.started_at = datetime.now()
        self.test = test
        self.interval = interval

        self.gpu_long_name = GPU_DISPLAY_NAMES[gpu]

        # Disable auto_buy_enabled if the user does not provide a bool.
        if type(self.auto_buy_enabled) != bool:
            self.auto_buy_enabled = False

        adapter = TimeoutHTTPAdapter(max_retries=Retry(
            total=10,
            backoff_factor=1,
            status_forcelist=[429, 500, 502, 503, 504],
            method_whitelist=["HEAD", "GET", "OPTIONS"],
        ))
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)
        self.notification_handler = NotificationHandler()

        self.get_product_ids()
        print(self.product_ids)
示例#5
0
文件: cli.py 项目: Sloop0/fairgame
 def decorator(*args, **kwargs):
     try:
         func(*args, **kwargs)
     except KeyboardInterrupt:
         pass
     except:
         NotificationHandler.send_notification(f"FairGame has crashed.")
         raise
示例#6
0
 def __init__(self, username, password, headless=False):
     self.notification_handler = NotificationHandler()
     if headless:
         enable_headless()
     self.driver = webdriver.Chrome(executable_path=binary_path, options=options)
     self.wait = WebDriverWait(self.driver, 10)
     self.username = username
     self.password = password
     self.login()
     time.sleep(3)
示例#7
0
 def __init__(self, username, password, debug=False):
     self.notification_handler = NotificationHandler()
     if not debug:
         chrome_options.add_argument("--headless")
     self.driver = webdriver.Chrome(executable_path=binary_path,
                                    options=options,
                                    chrome_options=chrome_options)
     self.wait = WebDriverWait(self.driver, 10)
     self.username = username
     self.password = password
     self.login()
     time.sleep(3)
示例#8
0
 def __init__(self, username, password, headless=False):
     self.notification_handler = NotificationHandler()
     if headless:
         enable_headless()
     self.driver = webdriver.Chrome(executable_path=binary_path, options=options)
     self.wait = WebDriverWait(self.driver, 10)
     self.username = username
     self.password = password
     self.driver.get(BASE_URL)
     if self.is_logged_in():
         log.info("Already logged in")
     else:
         self.login()
     time.sleep(3)
示例#9
0
    def __init__(self, headless=False):
        self.notification_handler = NotificationHandler()
        if headless:
            enable_headless()
        options.add_argument(f"user-data-dir=.profile-amz")
        self.driver = webdriver.Chrome(executable_path=binary_path,
                                       options=options)
        self.wait = WebDriverWait(self.driver, 10)
        if path.exists(AUTOBUY_CONFIG_PATH):
            with open(AUTOBUY_CONFIG_PATH) as json_file:
                try:
                    config = json.load(json_file)
                    self.username = config["username"]
                    self.password = config["password"]
                    self.asin_list = config["asin_list"]
                    self.amazon_website = config.get("amazon_website",
                                                     "amazon.com")
                    assert isinstance(self.asin_list, list)
                except Exception:
                    raise InvalidAutoBuyConfigException(
                        "amazon_config.json file not formatted properly.")
        else:
            log.error(
                "No config file found, see here on how to fix this: https://github.com/Hari-Nagarajan/nvidia-bot#amazon"
            )
            exit(0)

        for key in AMAZON_URLS.keys():
            AMAZON_URLS[key] = AMAZON_URLS[key].format(self.amazon_website)
        print(AMAZON_URLS)
        self.driver.get(AMAZON_URLS["BASE_URL"])
        log.info("Waiting for home page.")
        self.check_if_captcha(self.wait_for_pages, HOME_PAGE_TITLES)

        if self.is_logged_in():
            log.info("Already logged in")
        else:
            log.info("Lets log in.")
            selenium_utils.button_click_using_xpath(
                self.driver, '//*[@id="nav-link-accountList"]/div/span')
            log.info("Wait for Sign In page")
            self.check_if_captcha(self.wait_for_pages, SIGN_IN_TITLES)
            self.login()
            log.info("Waiting 15 seconds.")
            time.sleep(
                15
            )  # We can remove this once I get more info on the phone verification page.
示例#10
0
 def __init__(self, username, password, item_url, headless=False):
     self.notification_handler = NotificationHandler()
     if headless:
         enable_headless()
     h = hashlib.md5(item_url.encode()).hexdigest()
     options.add_argument(f"user-data-dir=.profile-amz-{h}")
     self.driver = webdriver.Chrome(executable_path=binary_path,
                                    options=options)
     self.wait = WebDriverWait(self.driver, 10)
     self.username = username
     self.password = password
     self.driver.get(BASE_URL)
     if self.is_logged_in():
         log.info("Already logged in")
     else:
         self.login()
         time.sleep(15)
示例#11
0
文件: cli.py 项目: Sloop0/fairgame
def test_notifications(disable_sound):
    notification_handler = NotificationHandler()
    enabled_handlers = ", ".join(notification_handler.enabled_handlers)
    message_time = datetime.now().strftime(TIME_FORMAT)
    notification_handler.send_notification(
        f"Beep boop. This is a test notification from FairGame. Sent {message_time}."
    )
    log.info(
        f"A notification was sent to the following handlers: {enabled_handlers}"
    )
    if not disable_sound:
        log.info("Testing notification sound...")
        notification_handler.play_notify_sound()
        time.sleep(2)  # Prevent audio overlap
        log.info("Testing alert sound...")
        notification_handler.play_alarm_sound()
        time.sleep(2)  # Prevent audio overlap
        log.info("Testing purchase sound...")
        notification_handler.play_purchase_sound()
    else:
        log.info("Local sounds disabled for this test.")

    # Give the notifications a chance to get out before we quit
    time.sleep(5)
示例#12
0
class Amazon:
    def __init__(self, headless=False):
        self.notification_handler = NotificationHandler()
        if headless:
            enable_headless()
        options.add_argument(f"user-data-dir=.profile-amz")
        self.driver = webdriver.Chrome(executable_path=binary_path,
                                       options=options)
        self.wait = WebDriverWait(self.driver, 10)
        if path.exists(AUTOBUY_CONFIG_PATH):
            with open(AUTOBUY_CONFIG_PATH) as json_file:
                try:
                    config = json.load(json_file)
                    self.username = config["username"]
                    self.password = config["password"]
                    self.asin_list = config["asin_list"]
                    self.amazon_website = config.get("amazon_website",
                                                     "amazon.com")
                    assert isinstance(self.asin_list, list)
                except Exception:
                    raise InvalidAutoBuyConfigException(
                        "amazon_config.json file not formatted properly.")
        else:
            raise InvalidAutoBuyConfigException(
                "Missing amazon_config.json file.")

        for key in AMAZON_URLS.keys():
            AMAZON_URLS[key] = AMAZON_URLS[key].format(self.amazon_website)
        print(AMAZON_URLS)
        self.driver.get(AMAZON_URLS["BASE_URL"])
        if self.is_logged_in():
            log.info("Already logged in")
        else:
            log.info("Lets log in.")
            selenium_utils.button_click_using_xpath(
                self.driver, '//*[@id="nav-link-accountList"]/div/span')
            selenium_utils.wait_for_any_title(self.driver, SIGN_IN_TITLES)
            self.login()
            log.info("Waiting 15 seconds.")
            time.sleep(
                15
            )  # We can remove this once I get more info on the phone verification page.

    def is_logged_in(self):
        try:
            text = wait_for_element(self.driver, "nav-link-accountList").text
            return "Hello, Sign in" not in text
        except Exception:
            return False

    def login(self):

        try:
            log.info("Email")
            self.driver.find_element_by_xpath('//*[@id="ap_email"]').send_keys(
                self.username + Keys.RETURN)
        except:
            log.info("Email not needed.")
            pass

        log.info("Password")
        self.driver.find_element_by_xpath(
            '//input[@name="rememberMe"]').click()
        self.driver.find_element_by_xpath('//*[@id="ap_password"]').send_keys(
            self.password + Keys.RETURN)

        log.info(f"Logged in as {self.username}")

    def run_item(self, delay=3, test=False):
        log.info("Checking stock for items.")
        while not self.something_in_stock():
            time.sleep(delay)
        self.notification_handler.send_notification(
            "Your items on Amazon.com were found!")
        self.checkout(test=test)

    def something_in_stock(self):
        params = {"anticache": str(secrets.token_urlsafe(32))}

        for x in range(len(self.asin_list)):
            params[f"ASIN.{x + 1}"] = self.asin_list[x]
            params[f"Quantity.{x + 1}"] = 1

        f = furl(AMAZON_URLS["CART_URL"])
        f.set(params)
        self.driver.get(f.url)
        selenium_utils.wait_for_any_title(self.driver, ADD_TO_CART_TITLES)
        if self.driver.find_elements_by_xpath('//td[@class="price item-row"]'):
            log.info("One or more items in stock!")

            return True
        else:
            return False

    def wait_for_cart_page(self):
        selenium_utils.wait_for_any_title(self.driver, SHOPING_CART_TITLES)
        log.info("On cart page.")

    def wait_for_pyo_page(self):
        selenium_utils.wait_for_any_title(self.driver,
                                          CHECKOUT_TITLES + SIGN_IN_TITLES)

        if self.driver.title in SIGN_IN_TITLES:
            log.info("Need to sign in again")
            self.login()

    def finalize_order_button(self, test, retry=0):
        button_xpaths = [
            '//*[@id="bottomSubmitOrderButtonId"]/span/input',
            '//*[@id="placeYourOrder"]/span/input',
            '//*[@id="submitOrderButtonId"]/span/input',
            '//input[@name="placeYourOrder1"]',
        ]
        button = None
        for button_xpath in button_xpaths:
            try:
                if (self.driver.find_element_by_xpath(
                        button_xpath).is_displayed()
                        and self.driver.find_element_by_xpath(
                            button_xpath).is_enabled()):
                    button = self.driver.find_element_by_xpath(button_xpath)
            except NoSuchElementException:
                log.debug(f"{button_xpath}, lets try a different one.")

        if button:
            log.info(f"Clicking Button: {button}")
            if not test:
                button.click()
            return
        else:
            if retry < 3:
                log.info("Couldn't find button. Lets retry in a sec.")
                time.sleep(5)
                self.finalize_order_button(test, retry + 1)
            else:
                log.info(
                    "Couldn't find button after 3 retries. Open a GH issue for this."
                )

    def wait_for_order_completed(self, test):
        if not test:
            selenium_utils.wait_for_any_title(self.driver,
                                              ORDER_COMPLETE_TITLES)
        else:
            log.info(
                "This is a test, so we don't need to wait for the order completed page."
            )

    def checkout(self, test):
        log.info("Clicking continue.")
        self.driver.find_element_by_xpath('//input[@value="add"]').click()

        log.info("Waiting for Cart Page")
        self.wait_for_cart_page()

        log.info("clicking checkout.")
        self.driver.find_element_by_xpath(
            '//*[@id="sc-buy-box-ptc-button"]/span/input').click()

        log.info("Waiting for Place Your Order Page")
        self.wait_for_pyo_page()

        log.info("Finishing checkout")
        self.finalize_order_button(test)

        log.info("Waiting for Order completed page.")
        self.wait_for_order_completed(test)

        log.info("Order Placed.")
示例#13
0
def testnotification():
    notification_handler = NotificationHandler()
    notification_handler.send_notification(f"Notifications Test")
示例#14
0
class Amazon:
    def __init__(self, headless=False):
        self.notification_handler = NotificationHandler()
        if headless:
            enable_headless()
        options.add_argument(f"user-data-dir=.profile-amz")
        self.driver = webdriver.Chrome(executable_path=binary_path,
                                       options=options)
        self.wait = WebDriverWait(self.driver, 10)
        if path.exists(AUTOBUY_CONFIG_PATH):
            with open(AUTOBUY_CONFIG_PATH) as json_file:
                try:
                    config = json.load(json_file)
                    self.username = config["username"]
                    self.password = config["password"]
                    self.asin_list = config["asin_list"]
                    self.amazon_website = config.get("amazon_website",
                                                     "amazon.com")
                    assert isinstance(self.asin_list, list)
                except Exception:
                    raise InvalidAutoBuyConfigException(
                        "amazon_config.json file not formatted properly.")
        else:
            log.error(
                "No config file found, see here on how to fix this: https://github.com/Hari-Nagarajan/nvidia-bot#amazon"
            )
            exit(0)

        for key in AMAZON_URLS.keys():
            AMAZON_URLS[key] = AMAZON_URLS[key].format(self.amazon_website)
        print(AMAZON_URLS)
        self.driver.get(AMAZON_URLS["BASE_URL"])
        log.info("Waiting for home page.")
        self.check_if_captcha(self.wait_for_pages, HOME_PAGE_TITLES)

        if self.is_logged_in():
            log.info("Already logged in")
        else:
            log.info("Lets log in.")
            selenium_utils.button_click_using_xpath(
                self.driver, '//*[@id="nav-link-accountList"]/div/span')
            log.info("Wait for Sign In page")
            self.check_if_captcha(self.wait_for_pages, SIGN_IN_TITLES)
            self.login()
            log.info("Waiting 15 seconds.")
            time.sleep(
                15
            )  # We can remove this once I get more info on the phone verification page.

    def is_logged_in(self):
        try:
            text = wait_for_element(self.driver, "nav-link-accountList").text
            return "Hello, Sign in" not in text
        except Exception:
            return False

    def login(self):

        try:
            log.info("Email")
            self.driver.find_element_by_xpath('//*[@id="ap_email"]').send_keys(
                self.username + Keys.RETURN)
        except:
            log.info("Email not needed.")
            pass

        log.info("Remember me checkbox")
        selenium_utils.button_click_using_xpath(self.driver,
                                                '//*[@name="rememberMe"]')

        log.info("Password")
        self.driver.find_element_by_xpath('//*[@id="ap_password"]').send_keys(
            self.password + Keys.RETURN)

        log.info(f"Logged in as {self.username}")

    def run_item(self, delay=3, test=False):
        log.info("Checking stock for items.")
        while not self.something_in_stock():
            time.sleep(delay)
        self.notification_handler.send_notification(
            "Your items on Amazon.com were found!")
        self.checkout(test=test)

    def something_in_stock(self):
        params = {"anticache": str(secrets.token_urlsafe(32))}

        for x in range(len(self.asin_list)):
            params[f"ASIN.{x + 1}"] = self.asin_list[x]
            params[f"Quantity.{x + 1}"] = 1

        f = furl(AMAZON_URLS["CART_URL"])
        f.set(params)
        self.driver.get(f.url)
        self.check_if_captcha(self.wait_for_pages, ADD_TO_CART_TITLES)
        if self.driver.find_elements_by_xpath('//td[@class="price item-row"]'):
            log.info("One or more items in stock!")

            return True
        else:
            return False

    def get_captcha_help(self):
        if not self.on_captcha_page():
            log.info("Not on captcha page.")
            return
        try:
            log.info("Stuck on a captcha... Lets try to solve it.")
            captcha = AmazonCaptcha.from_webdriver(self.driver)
            solution = captcha.solve()
            log.info(f"The solution is: {solution}")
            if solution == "Not solved":
                log.info(
                    f"Failed to solve, lets reload and get a new captcha.")
                self.driver.refresh()
                time.sleep(5)
                self.get_captcha_help()
            else:
                self.driver.find_element_by_xpath(
                    '//*[@id="captchacharacters"]').send_keys(solution +
                                                              Keys.RETURN)
        except Exception as e:
            log.debug(e)
            log.info("Error trying to solve captcha. Refresh and retry.")
            self.driver.refresh()
            time.sleep(5)

    def on_captcha_page(self):
        try:
            if self.driver.title in CAPTCHA_PAGE_TITLES:
                return True
            if self.driver.find_element_by_xpath(
                    '//form[@action="/errors/validateCaptcha"]'):
                return True
        except Exception:
            pass
        return False

    def check_if_captcha(self, func, args):
        try:
            func(args)
        except Exception as e:
            log.debug(str(e))
            if self.on_captcha_page():
                self.get_captcha_help()
                func(args, t=300)
            else:
                log.debug(self.driver.title)
                log.error(
                    f"An error happened, please submit a bug report including a screenshot of the page the "
                    f"selenium browser is on. There may be a file saved at: amazon-{func.__name__}.png"
                )
                self.driver.save_screenshot(f"amazon-{func.__name__}.png")
                time.sleep(60)
                self.driver.close()
                raise e

    def wait_for_pages(self, page_titles, t=30):
        log.debug(f"wait_for_pages({page_titles}, {t})")
        selenium_utils.wait_for_any_title(self.driver, page_titles, t)

    def wait_for_pyo_page(self):
        self.check_if_captcha(self.wait_for_pages,
                              CHECKOUT_TITLES + SIGN_IN_TITLES)

        if self.driver.title in SIGN_IN_TITLES:
            log.info("Need to sign in again")
            self.login()

    def finalize_order_button(self, test, retry=0):
        button_xpaths = [
            '//*[@id="bottomSubmitOrderButtonId"]/span/input',
            '//*[@id="placeYourOrder"]/span/input',
            '//*[@id="submitOrderButtonId"]/span/input',
            '//input[@name="placeYourOrder1"]',
        ]
        button = None
        for button_xpath in button_xpaths:
            try:
                if (self.driver.find_element_by_xpath(
                        button_xpath).is_displayed()
                        and self.driver.find_element_by_xpath(
                            button_xpath).is_enabled()):
                    button = self.driver.find_element_by_xpath(button_xpath)
            except NoSuchElementException:
                log.debug(f"{button_xpath}, lets try a different one.")

        if button:
            log.info(f"Clicking Button: {button}")
            if not test:
                button.click()
            return
        else:
            if retry < 3:
                log.info("Couldn't find button. Lets retry in a sec.")
                time.sleep(5)
                self.finalize_order_button(test, retry + 1)
            else:
                log.info(
                    "Couldn't find button after 3 retries. Open a GH issue for this."
                )

    def wait_for_order_completed(self, test):
        if not test:
            self.check_if_captcha(self.wait_for_pages, ORDER_COMPLETE_TITLES)
        else:
            log.info(
                "This is a test, so we don't need to wait for the order completed page."
            )

    def checkout(self, test):
        log.info("Clicking continue.")
        self.driver.find_element_by_xpath('//input[@value="add"]').click()

        log.info("Waiting for Cart Page")
        self.check_if_captcha(self.wait_for_pages, SHOPING_CART_TITLES)

        log.info("clicking checkout.")
        try:
            self.driver.find_element_by_xpath(
                '//*[@id="sc-buy-box-ptc-button"]/span/input').click()
        except Exception as e:
            log.debug(e)
            log.info("Failed to checkout. Returning to stock check.")
            self.run_item(test=test)

        log.info("Waiting for Place Your Order Page")
        self.wait_for_pyo_page()

        log.info("Finishing checkout")
        self.finalize_order_button(test)

        log.info("Waiting for Order completed page.")
        self.wait_for_order_completed(test)

        log.info("Order Placed.")
示例#15
0
    def __init__(self, gpu, locale="en_us", test=False):
        self.product_ids = set([])
        self.cli_locale = locale.lower()
        self.locale = self.map_locales()
        self.session = requests.Session()
        self.gpu = gpu
        self.enabled = True
        self.auto_buy_enabled = False
        self.attempt = 0
        self.started_at = datetime.now()
        self.test = test

        self.gpu_long_name = GPU_DISPLAY_NAMES[gpu]

        if path.exists(AUTOBUY_CONFIG_PATH):
            with open(AUTOBUY_CONFIG_PATH) as json_file:
                try:
                    self.config = json.load(json_file)
                except Exception as e:
                    log.error(
                        "Your `autobuy_config.json` file is not valid json.")
                    raise e
                if self.has_valid_creds():
                    self.nvidia_login = self.config["NVIDIA_LOGIN"]
                    self.nvidia_password = self.config["NVIDIA_PASSWORD"]
                    self.auto_buy_enabled = self.config["FULL_AUTOBUY"]
                    self.cvv = self.config.get("CVV")
                    self.interval = int(self.config.get("INTERVAL", 5))
                else:
                    raise InvalidAutoBuyConfigException(self.config)
        else:
            log.info("No Autobuy creds found.")

        # Disable auto_buy_enabled if the user does not provide a bool.
        if type(self.auto_buy_enabled) != bool:
            self.auto_buy_enabled = False

        adapter = TimeoutHTTPAdapter(max_retries=Retry(
            total=10,
            backoff_factor=1,
            status_forcelist=[429, 500, 502, 503, 504],
            method_whitelist=["HEAD", "GET", "OPTIONS"],
        ))
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)
        self.notification_handler = NotificationHandler()

        log.info("Opening Webdriver")
        chrome_options = webdriver.ChromeOptions()
        chrome_options.add_experimental_option("excludeSwitches",
                                               ["enable-logging"])
        self.driver = webdriver.Chrome(executable_path=binary_path,
                                       options=options,
                                       chrome_options=chrome_options)
        self.sign_in()
        selenium_utils.add_cookies_to_session_from_driver(
            self.driver, self.session)
        log.info("Adding driver cookies to session")

        log.info("Getting product IDs")
        self.token_data = self.get_nvidia_access_token()
        self.payment_option = self.get_payment_options()
        if not self.payment_option.get("id") or not self.cvv:
            log.error(
                "No payment option on account or missing CVV. Disable Autobuy")
            self.auto_buy_enabled = False
        else:
            log.debug(self.payment_option)
            self.ext_ip = self.get_ext_ip()

        if not self.auto_buy_enabled:
            log.info("Closing webdriver")
            self.driver.close()

        self.get_product_ids()
        while len(self.product_ids) == 0:
            log.info(
                f"We have no product IDs for {self.gpu_long_name}, retrying until we get a product ID"
            )
            self.get_product_ids()
            sleep(5)
示例#16
0
class Evga:
    def __init__(self, debug=False):
        self.notification_handler = NotificationHandler()
        self.driver = webdriver.Chrome(executable_path=binary_path,
                                       options=options,
                                       chrome_options=chrome_options)
        self.credit_card = {}
        try:
            if path.exists(CONFIG_PATH):
                with open(CONFIG_PATH) as json_file:
                    config = json.load(json_file)
                    username = config["username"]
                    password = config["password"]
                    self.credit_card["name"] = config["credit_card"]["name"]
                    self.credit_card["number"] = config["credit_card"][
                        "number"]
                    self.credit_card["cvv"] = config["credit_card"]["cvv"]
                    self.credit_card["expiration_month"] = config[
                        "credit_card"]["expiration_month"]
                    self.credit_card["expiration_year"] = config[
                        "credit_card"]["expiration_year"]
        except Exception as e:
            log.error(
                f"This is most likely an error with your {CONFIG_PATH} file.")
            raise e

        self.login(username, password)

    def login(self, username, password):
        """
        We're just going to attempt to load cookies, else enter the user info and let the user handle the captcha
        :param username:
        :param password:
        :return:
        """
        self.driver.execute_cdp_cmd(
            "Network.setUserAgentOverride",
            {
                "userAgent":
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.53 Safari/537.36"
            },
        )
        self.driver.execute_script(
            "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
        )

        if path.isfile("evga-cookies.pkl"):  # check for cookies file
            self.driver.get("https://www.evga.com")
            selenium_utils.wait_for_page(
                self.driver,
                "EVGA - Intelligent Innovation - Official Website", 300)
            cookies = pickle.load(open("evga-cookies.pkl", "rb"))
            for cookie in cookies:
                self.driver.add_cookie(cookie)

        self.driver.get("https://www.evga.com")
        selenium_utils.wait_for_page(
            self.driver, "EVGA - Intelligent Innovation - Official Website",
            300)
        if (len(self.driver.find_elements_by_id("svg-login")) >
                0):  # cookies did not provide logged in state
            self.driver.get(LOGIN_URL)
            selenium_utils.wait_for_page(self.driver,
                                         "EVGA - Intelligent Innovation")

            selenium_utils.field_send_keys(self.driver, "evga_login", username)
            selenium_utils.field_send_keys(self.driver, "password", password)

            log.info("Go do the captcha and log in")

            selenium_utils.wait_for_page(
                self.driver,
                "EVGA - Intelligent Innovation - Official Website", 300)
            pickle.dump(self.driver.get_cookies(),
                        open("evga-cookies.pkl", "wb"))  # save cookies

        log.info("Logged in!")

    def buy(self, delay=5, test=False, model=""):
        selector = '//a[@id="LFrame_btnAddToCart"]'
        associate_code = "&associatecode=2QME1VF65K9ZY8B"
        if test:
            model_name = "test"
            url = "https://www.evga.com/products/product.aspx?pn=08G-P4-3289-KR"
            self.driver.get(url + associate_code)
            selenium_utils.wait_for_page(
                self.driver,
                "EVGA - Products - EVGA GeForce RTX 2080 SUPER FTW3 HYDRO COPPER GAMING, 08G-P4-3289-KR, 8GB GDDR6, RGB LED, iCX2 Technology, Metal Backplate - 08G-P4-3289-KR",
            )
        else:
            models = {
                "ftw3": [
                    "https://www.evga.com/products/product.aspx?pn=10G-P5-3895-KR",
                    "EVGA - Products - EVGA GeForce RTX 3080 FTW3 GAMING, 10G-P5-3895-KR, 10GB GDDR6X, iCX3 Technology, ARGB LED, Metal Backplate - 10G-P5-3895-KR",
                ],
                "xc3": [
                    "https://www.evga.com/products/product.aspx?pn=10G-P5-3883-KR",
                    "EVGA - Products - EVGA GeForce RTX 3080 XC3 GAMING, 10G-P5-3883-KR, 10GB GDDR6X, iCX3 Cooling, ARGB LED, Metal Backplate - 10G-P5-3883-KR",
                ],
                "xc3ultra": [
                    "https://www.evga.com/products/product.aspx?pn=10G-P5-3885-KR",
                    "EVGA - Products - EVGA GeForce RTX 3080 XC3 ULTRA GAMING, 10G-P5-3885-KR, 10GB GDDR6X, iCX3 Cooling, ARGB LED, Metal Backplate - 10G-P5-3885-KR",
                ],
                "xc3black": [
                    "https://www.evga.com/products/product.aspx?pn=10G-P5-3881-KR",
                    "EVGA - Products - EVGA GeForce RTX 3080 XC3 BLACK GAMING, 10G-P5-3881-KR, 10GB GDDR6X, iCX3 Cooling, ARGB LED - 10G-P5-3881-KR",
                ],
                "ftw3ultra": [
                    "https://www.evga.com/products/product.aspx?pn=10G-P5-3897-KR",
                    "EVGA - Products - EVGA GeForce RTX 3080 FTW3 ULTRA GAMING, 10G-P5-3897-KR, 10GB GDDR6X, iCX3 Technology, ARGB LED, Metal Backplate - 10G-P5-3897-KR",
                ],
                "any": [
                    "https://www.evga.com/products/productlist.aspx?type=0&family=GeForce+30+Series+Family&chipset=RTX+3080",
                    "EVGA - Products - Graphics - GeForce 30 Series Family - RTX 3080",
                ],
            }
            if model:
                self.driver.get(models[model][0] + associate_code)
                selenium_utils.wait_for_page(self.driver, models[model][1])
            else:
                model = "any"
                selector = '//input[@class="btnBigAddCart"]'
                self.driver.get(models["any"][0] + associate_code)
                selenium_utils.wait_for_page(self.driver, models["any"][1])

        #  Check for stock
        log.info("On GPU Page")
        atc_buttons = self.driver.find_elements_by_xpath(selector)
        while not atc_buttons:
            if self.driver.current_url == models[model][0] + associate_code:
                log.debug("Refreshing page for " + model)
                self.driver.refresh()
            else:
                log.debug("Error page detected. Redirecting...")
                self.driver.get(models[model][0] + associate_code)
            atc_buttons = self.driver.find_elements_by_xpath(selector)
            sleep(delay)

        # send notification
        self.notification_handler.send_notification(
            f"EVGA BOT: " + model_name +
            " in stock! Attempting to place order...")

        #  Add to cart
        atc_buttons[0].click()

        #  Go to checkout
        selenium_utils.wait_for_page(self.driver, "EVGA - Checkout")
        selenium_utils.button_click_using_xpath(
            self.driver, '//*[@id="LFrame_CheckoutButton"]')

        # Shipping Address screen
        selenium_utils.wait_for_page(self.driver, "Shopping")

        log.info("Skip that page.")
        self.driver.get("https://secure.evga.com/Cart/Checkout_Payment.aspx")

        selenium_utils.wait_for_page(self.driver,
                                     "EVGA - Checkout - Billing Options")

        log.info("Ensure that we are paying with credit card")
        sleep(1)  # Fix this.
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable(
                (By.XPATH, './/input[@value="rdoCreditCard"]'))).click()
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable(
                (By.XPATH, '//*[@id="ctl00_LFrame_btncontinue"]'))).click()

        selenium_utils.wait_for_element(self.driver,
                                        "ctl00_LFrame_txtNameOnCard")

        log.info("Populate credit card fields")

        selenium_utils.field_send_keys(self.driver,
                                       "ctl00$LFrame$txtNameOnCard",
                                       self.credit_card["name"])
        selenium_utils.field_send_keys(self.driver,
                                       "ctl00$LFrame$txtCardNumber",
                                       self.credit_card["number"])
        selenium_utils.field_send_keys(self.driver, "ctl00$LFrame$txtCvv",
                                       self.credit_card["cvv"])
        Select(self.driver.find_element_by_id(
            "ctl00_LFrame_ddlMonth")).select_by_value(
                self.credit_card["expiration_month"])
        Select(self.driver.find_element_by_id(
            "ctl00_LFrame_ddlYear")).select_by_value(
                self.credit_card["expiration_year"])
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable((
                By.XPATH,
                "/html/body/form/div[3]/div[3]/div/div[1]/div[5]/div[3]/div/div[1]/div/div[@id='checkoutButtons']/input[2]",
            ))).click()

        log.info("Finalize Order Page")
        selenium_utils.wait_for_page(self.driver,
                                     "EVGA - Checkout - Finalize Order")

        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable(
                (By.ID, "ctl00_LFrame_cbAgree"))).click()

        selenium_utils.wait_for_element(self.driver,
                                        "ctl00_LFrame_btncontinue")

        if not test:
            WebDriverWait(self.driver, 10).until(
                EC.element_to_be_clickable(
                    (By.ID, "ctl00_LFrame_btncontinue"))).click()

        log.info("Finalized Order!")
示例#17
0
class NvidiaBuyer:
    def __init__(self, gpu, locale="en_us", test=False, interval=5):
        self.product_ids = set([])
        self.cli_locale = locale.lower()
        self.locale = self.map_locales()
        self.session = requests.Session()
        self.gpu = gpu
        self.enabled = True
        self.auto_buy_enabled = False
        self.attempt = 0
        self.started_at = datetime.now()
        self.test = test
        self.interval = interval

        self.gpu_long_name = GPU_DISPLAY_NAMES[gpu]

        # Disable auto_buy_enabled if the user does not provide a bool.
        if type(self.auto_buy_enabled) != bool:
            self.auto_buy_enabled = False

        adapter = TimeoutHTTPAdapter(max_retries=Retry(
            total=10,
            backoff_factor=1,
            status_forcelist=[429, 500, 502, 503, 504],
            method_whitelist=["HEAD", "GET", "OPTIONS"],
        ))
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)
        self.notification_handler = NotificationHandler()

        self.get_product_ids()

    def map_locales(self):
        if self.cli_locale == "de_at":
            return "de_de"
        if self.cli_locale == "fr_be":
            return "fr_fr"
        if self.cli_locale == "da_dk":
            return "en_gb"
        if self.cli_locale == "cs_cz":
            return "en_gb"
        return self.cli_locale

    def get_product_ids(self):
        if isinstance(PRODUCT_IDS[self.locale][self.gpu], list):
            self.product_ids = PRODUCT_IDS[self.locale][self.gpu]
        if isinstance(PRODUCT_IDS[self.locale][self.gpu], str):
            self.product_ids = [PRODUCT_IDS[self.locale][self.gpu]]

    def run_items(self):
        log.info(
            f"We have {len(self.product_ids)} product IDs for {self.gpu_long_name}"
        )
        log.info(f"Product IDs: {self.product_ids}")
        try:
            with ThreadPoolExecutor(
                    max_workers=len(self.product_ids)) as executor:
                product_futures = [
                    executor.submit(self.buy, product_id)
                    for product_id in self.product_ids
                ]
                concurrent.futures.wait(product_futures)
                for fut in product_futures:
                    log.debug(f"Future Result: {fut.result()}")
        except ProductIDChangedException as ex:
            log.warning("Product IDs changed.")
            self.product_ids = set([])
            self.get_product_ids()
            self.run_items()

    def buy(self, product_id):
        pass
        try:
            log.info(
                f"Stock Check {product_id} at {self.interval} second intervals."
            )
            while not self.is_in_stock(product_id):
                self.attempt = self.attempt + 1
                time_delta = str(datetime.now() -
                                 self.started_at).split(".")[0]
                with Spinner.get(
                        f"Stock Check ({self.attempt}, have been running for {time_delta})..."
                ) as s:
                    sleep(self.interval)
            if self.enabled:
                cart_success, cart_url = self.get_cart_url(product_id)
                if cart_success:
                    log.info(f"{self.gpu_long_name} added to cart.")
                    self.enabled = False
                    webbrowser.open(cart_url)
                    self.notification_handler.send_notification(
                        f" {self.gpu_long_name} with product ID: {product_id} in "
                        f"stock: {cart_url}")
                else:
                    self.buy(product_id)
        except Timeout:
            log.error("Had a timeout error.")
            self.buy(product_id)

    def is_in_stock(self, product_id):
        response = self.session.get(
            NVIDIA_STOCK_API.format(product_id=product_id, locale=self.locale),
            headers=DEFAULT_HEADERS,
        )
        log.debug(f"Stock check response code: {response.status_code}")
        if response.status_code != 200:
            log.debug(response.text)
        if "PRODUCT_INVENTORY_IN_STOCK" in response.text:
            return True
        else:
            return False

    def get_cart_url(self, product_id):
        success, token = self.get_session_token()
        if not success:
            return False, ""

        data = {"products": [{"productId": product_id, "quantity": 1}]}
        headers = DEFAULT_HEADERS.copy()
        headers["locale"] = self.locale
        headers["nvidia_shop_id"] = token
        headers["Content-Type"] = "application/json"
        response = self.session.post(url=NVIDIA_ADD_TO_CART_API,
                                     headers=headers,
                                     data=json.dumps(data))
        if response.status_code == 203:
            response_json = response.json()
            if "location" in response_json:
                return True, response_json["location"]
        else:
            log.error(response.text)
            log.error(
                f"Add to cart failed with {response.status_code}. This is likely an error with nvidia's API."
            )
        return False, ""

    def get_session_token(self):
        params = {"format": "json", "locale": self.locale}
        headers = DEFAULT_HEADERS.copy()
        headers["locale"] = self.locale

        response = self.session.get(NVIDIA_TOKEN_URL,
                                    headers=DEFAULT_HEADERS,
                                    params=params)
        if response.status_code == 200:
            response_json = response.json()
            if "session_token" not in response_json:
                log.error("Error getting session token.")
                return False, ""
            return True, response_json["session_token"]
        else:
            log.debug(f"Get Session Token: {response.status_code}")
示例#18
0
class NvidiaBuyer:
    def __init__(self, gpu, locale="en_us", test=False):
        self.product_ids = set([])
        self.cli_locale = locale.lower()
        self.locale = self.map_locales()
        self.session = requests.Session()
        self.gpu = gpu
        self.enabled = True
        self.auto_buy_enabled = False
        self.attempt = 0
        self.started_at = datetime.now()
        self.test = test

        self.gpu_long_name = GPU_DISPLAY_NAMES[gpu]

        if path.exists(AUTOBUY_CONFIG_PATH):
            with open(AUTOBUY_CONFIG_PATH) as json_file:
                try:
                    self.config = json.load(json_file)
                except Exception as e:
                    log.error(
                        "Your `autobuy_config.json` file is not valid json.")
                    raise e
                if self.has_valid_creds():
                    self.nvidia_login = self.config["NVIDIA_LOGIN"]
                    self.nvidia_password = self.config["NVIDIA_PASSWORD"]
                    self.auto_buy_enabled = self.config["FULL_AUTOBUY"]
                    self.cvv = self.config.get("CVV")
                    self.interval = int(self.config.get("INTERVAL", 5))
                else:
                    raise InvalidAutoBuyConfigException(self.config)
        else:
            log.info("No Autobuy creds found.")

        # Disable auto_buy_enabled if the user does not provide a bool.
        if type(self.auto_buy_enabled) != bool:
            self.auto_buy_enabled = False

        adapter = TimeoutHTTPAdapter(max_retries=Retry(
            total=10,
            backoff_factor=1,
            status_forcelist=[429, 500, 502, 503, 504],
            method_whitelist=["HEAD", "GET", "OPTIONS"],
        ))
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)
        self.notification_handler = NotificationHandler()

        log.info("Opening Webdriver")
        chrome_options = webdriver.ChromeOptions()
        chrome_options.add_experimental_option("excludeSwitches",
                                               ["enable-logging"])
        self.driver = webdriver.Chrome(executable_path=binary_path,
                                       options=options,
                                       chrome_options=chrome_options)
        self.sign_in()
        selenium_utils.add_cookies_to_session_from_driver(
            self.driver, self.session)
        log.info("Adding driver cookies to session")

        log.info("Getting product IDs")
        self.token_data = self.get_nvidia_access_token()
        self.payment_option = self.get_payment_options()
        if not self.payment_option.get("id") or not self.cvv:
            log.error(
                "No payment option on account or missing CVV. Disable Autobuy")
            self.auto_buy_enabled = False
        else:
            log.debug(self.payment_option)
            self.ext_ip = self.get_ext_ip()

        if not self.auto_buy_enabled:
            log.info("Closing webdriver")
            self.driver.close()

        self.get_product_ids()
        while len(self.product_ids) == 0:
            log.info(
                f"We have no product IDs for {self.gpu_long_name}, retrying until we get a product ID"
            )
            self.get_product_ids()
            sleep(5)

    @property
    def access_token(self):
        if datetime.today().timestamp() >= self.token_data.get("expires_at"):
            log.debug("Access token expired")
            self.token_data = self.get_nvidia_access_token()
        return self.token_data["access_token"]

    def has_valid_creds(self):
        if all(item in self.config.keys() for item in AUTOBUY_CONFIG_KEYS):
            return True
        else:
            return False

    def map_locales(self):
        if self.cli_locale == "de_at":
            return "de_de"
        if self.cli_locale == "fr_be":
            return "fr_fr"
        if self.cli_locale == "da_dk":
            return "en_gb"
        if self.cli_locale == "cs_cz":
            return "en_gb"
        return self.cli_locale

    def get_product_ids(self, url=DIGITAL_RIVER_PRODUCT_LIST_URL):
        log.debug(f"Calling {url}")
        payload = {
            "apiKey": DIGITAL_RIVER_API_KEY,
            "expand": "product",
            "fields": "product.id,product.displayName,product.pricing",
            "locale": self.locale,
            "format": "json",
        }
        headers = DEFAULT_HEADERS.copy()
        headers["locale"] = self.locale
        response = self.session.get(url, headers=headers, params=payload)

        log.debug(response.status_code)
        response_json = response.json()
        for product_obj in response_json["products"]["product"]:
            if product_obj["displayName"] == self.gpu_long_name:
                if self.check_if_locale_corresponds(product_obj["id"]):
                    self.product_ids.add(product_obj["id"])
        if response_json["products"].get("nextPage"):
            self.get_product_ids(
                url=response_json["products"]["nextPage"]["uri"])

    def run_items(self):
        log.info(
            f"We have {len(self.product_ids)} product IDs for {self.gpu_long_name}"
        )
        log.info(f"Product IDs: {self.product_ids}")
        try:
            with ThreadPoolExecutor(
                    max_workers=len(self.product_ids)) as executor:
                product_futures = [
                    executor.submit(self.buy, product_id)
                    for product_id in self.product_ids
                ]
                concurrent.futures.wait(product_futures)
                for fut in product_futures:
                    log.info(fut.result())
        except ProductIDChangedException as ex:
            log.warning("Product IDs changed.")
            self.product_ids = set([])
            self.get_product_ids()
            self.run_items()

    def buy(self, product_id):
        try:
            log.info(
                f"Checking stock for {product_id} at {self.interval} second intervals."
            )
            while not self.add_to_cart(product_id) and self.enabled:
                self.attempt = self.attempt + 1
                time_delta = str(datetime.now() -
                                 self.started_at).split(".")[0]
                with Spinner.get(
                        f"Still working (attempt {self.attempt}, have been running for {time_delta})..."
                ) as s:
                    sleep(self.interval)
            if self.enabled:
                self.apply_shopper_details()
                if self.auto_buy_enabled:
                    self.notification_handler.send_notification(
                        f" {self.gpu_long_name} with product ID: {product_id} available!"
                    )
                    log.info("Auto buy enabled.")
                    # self.submit_cart()
                    self.selenium_checkout()
                else:
                    log.info("Auto buy disabled.")
                    cart_url = self.open_cart_url()
                    self.notification_handler.send_notification(
                        f" {self.gpu_long_name} with product ID: {product_id} in stock: {cart_url}"
                    )
                self.enabled = False
        except Timeout:
            log.error("Had a timeout error.")
            self.buy(product_id)

    def open_cart_url(self):
        log.info("Opening cart.")
        params = {"token": self.access_token}
        url = furl(DIGITAL_RIVER_CHECKOUT_URL).set(params)
        webbrowser.open_new_tab(url.url)
        return url.url

    def selenium_checkout(self):
        log.info("Checking out.")
        autobuy_btns = autobuy_locale_btns[self.locale]
        params = {"token": self.access_token}
        url = furl(DIGITAL_RIVER_CHECKOUT_URL).set(params)
        self.driver.get(url.url)
        log.debug(
            f"Waiting for page title: {PAGE_TITLES_BY_LOCALE[self.locale]['checkout']}"
        )
        selenium_utils.wait_for_page(
            self.driver, PAGE_TITLES_BY_LOCALE[self.locale]["checkout"])

        log.info("Next.")
        log.debug(f"Clicking on button: {autobuy_btns[0]}")
        self.driver.find_element_by_xpath(
            f'//*[@value="{autobuy_btns[0]}"]').click()
        log.debug(f"Entering security code to 'cardSecurityCode'")
        security_code = selenium_utils.wait_for_element(
            self.driver, "cardSecurityCode")
        security_code.send_keys(self.cvv)
        log.info("Next.")
        log.debug(f"Clicking on button: {autobuy_btns[0]}")
        self.driver.find_element_by_xpath(
            f'//*[@value="{autobuy_btns[0]}"]').click()

        try:
            log.debug(
                f"Waiting for page title: {PAGE_TITLES_BY_LOCALE[self.locale]['verify_order']}"
            )
            selenium_utils.wait_for_page(
                self.driver,
                PAGE_TITLES_BY_LOCALE[self.locale]["verify_order"], 5)
        except TimeoutException:
            log.debug("Address validation required?")
            self.address_validation_page()

        log.debug(
            f"Waiting for page title: {PAGE_TITLES_BY_LOCALE[self.locale]['verify_order']}"
        )
        selenium_utils.wait_for_page(
            self.driver, PAGE_TITLES_BY_LOCALE[self.locale]["verify_order"], 5)

        if not self.test:
            log.info("F this captcha lmao. Submitting cart.")
            self.submit_cart()
        else:
            log.info("Test complete. No actual purchase was made.")
        # log.info("Submit.")
        # log.debug("Reached order validation page.")
        # self.driver.save_screenshot("nvidia-order-validation.png")
        # self.driver.find_element_by_xpath(f'//*[@value="{autobuy_btns[1]}"]').click()
        # selenium_utils.wait_for_page(
        #     self.driver, PAGE_TITLES_BY_LOCALE[self.locale]["order_completed"], 5
        # )
        # self.driver.save_screenshot("nvidia-order-finshed.png")
        # log.info("Done.")

    def address_validation_page(self):
        try:
            selenium_utils.wait_for_page(
                self.driver,
                PAGE_TITLES_BY_LOCALE[self.locale]["address_validation"],
                5,
            )
            log.debug("Setting suggested shipping information.")
            selenium_utils.wait_for_element(
                self.driver, "billingAddressOptionRow2").click()
            selenium_utils.button_click_using_xpath(
                self.driver, "//input[@id='selectionButton']")
        except TimeoutException:
            log.error("Address validation not required?")

    def add_to_cart(self, product_id):
        try:
            log.debug(f"Checking if item ({product_id}) in stock")
            params = {
                "apiKey": DIGITAL_RIVER_API_KEY,
                "token": self.access_token,
                "productId": product_id,
                "format": "json",
            }
            response = self.session.post(
                DIGITAL_RIVER_ADD_TO_CART_API_URL,
                headers=DEFAULT_HEADERS,
                params=params,
            )

            if response.status_code == 200:
                log.info(f"{self.gpu_long_name} ({product_id}) in stock!")
                return True
            elif response.status_code == 409:
                try:
                    response_json = response.json()
                    log.debug(f"Error: {response_json['errors']['error']}")
                    for error in response_json["errors"]["error"]:
                        if error["code"] == "invalid-product-id":
                            raise ProductIDChangedException()
                except json.decoder.JSONDecodeError as er:
                    log.warning(f"Failed to decode json: {response.text}")
            else:
                log.debug("item not in stock")
                return False
        except Exception as ex:
            log.debug(str(ex))
            log.debug("The connection has been reset.")
            return False

    def get_ext_ip(self):
        response = self.session.get("https://api.ipify.org?format=json")
        if response.status_code == 200:
            return response.json()["ip"]

    def get_payment_options(self):
        params = {
            "apiKey": DIGITAL_RIVER_API_KEY,
            "token": self.access_token,
            "format": "json",
            "expand": "all",
        }
        response = self.session.get(
            DIGITAL_RIVER_PAYMENT_METHODS_API_URL,
            headers=DEFAULT_HEADERS,
            params=params,
        )
        log.debug(response.status_code)
        log.debug(response.json())
        if response.status_code == 200:
            response_json = response.json()
            try:
                return response_json["paymentOptions"]["paymentOption"][0]
            except:
                return {}

    def apply_shopper_details(self):
        log.info("Apply shopper details")
        params = {
            "apiKey": DIGITAL_RIVER_API_KEY,
            "token": self.access_token,
            "billingAddressId": "",
            "paymentOptionId": self.payment_option.get("id", ""),
            "shippingAddressId": "",
            "expand": "all",
        }
        response = self.session.post(
            DIGITAL_RIVER_APPLY_SHOPPER_DETAILS_API_URL,
            headers=DEFAULT_HEADERS,
            params=params,
        )
        log.debug(f"Apply shopper details response: {response.status_code}")
        if response.status_code == 200:
            log.info("Success apply_shopper_details")
        else:
            log.info("Error applying shopper details")
            log.debug(json.dumps(response.json(), indent=1))

    def submit_cart(self):
        params = {
            "apiKey": DIGITAL_RIVER_API_KEY,
            "token": self.access_token,
            "format": "json",
            "expand": "all",
        }

        body = {
            "cart": {
                "ipAddress": self.ext_ip,
                "termsOfSalesAcceptance": "true"
            }
        }
        response = self.session.post(
            DIGITAL_RIVER_SUBMIT_CART_API_URL,
            headers=DEFAULT_HEADERS,
            params=params,
            json=body,
        )
        log.debug(response.status_code)
        log.debug(response.json())
        if response.status_code == 200:
            log.info("Success submit_cart")

    def check_if_locale_corresponds(self, product_id):
        special_locales = [
            "en_gb",
            "de_at",
            "de_de",
            "fr_fr",
            "fr_be",
            "da_dk",
            "cs_cz",
        ]
        if self.cli_locale in special_locales:
            url = f"{DIGITAL_RIVER_PRODUCT_LIST_URL}/{product_id}"
            log.debug(f"Calling {url}")
            payload = {
                "apiKey": DIGITAL_RIVER_API_KEY,
                "expand": "product",
                "locale": self.locale,
                "format": "json",
            }

            response = self.session.get(url,
                                        headers=DEFAULT_HEADERS,
                                        params=payload)
            log.debug(response.status_code)
            response_json = response.json()
            return self.cli_locale[3:].upper(
            ) in response_json["product"]["name"]
        return True

    def get_nvidia_access_token(self):
        log.debug("Getting session token")
        now = datetime.today()
        payload = {
            "apiKey": DIGITAL_RIVER_API_KEY,
            "format": "json",
            "locale": self.locale,
            "currency": "USD",
            "_": now,
        }
        response = self.session.get(NVIDIA_TOKEN_URL,
                                    headers=DEFAULT_HEADERS,
                                    params=payload)
        log.debug(response.status_code)
        data = response.json()
        log.debug(f"Nvidia access token: {data['access_token']}")
        data["expires_at"] = round(now.timestamp() + data["expires_in"]) - 60
        return data

    def is_signed_in(self):
        try:
            self.driver.find_element_by_id("dr_logout")
            log.info("Already signed in.")
            return True
        except NoSuchElementException:
            return False

    def sign_in(self):
        log.info("Signing in.")
        self.driver.get(
            f"https://store.nvidia.com/DRHM/store?Action=Logout&SiteID=nvidia&Locale={self.locale}&ThemeID=326200&Env=BASE&nextAction=help"
        )
        selenium_utils.wait_for_page(
            self.driver, PAGE_TITLES_BY_LOCALE[self.locale]["signed_in_help"])

        if not self.is_signed_in():
            email = selenium_utils.wait_for_element(self.driver, "loginEmail")
            pwd = selenium_utils.wait_for_element(self.driver, "loginPassword")
            try:
                email.send_keys(self.nvidia_login)
                pwd.send_keys(self.nvidia_password)
            except AttributeError as e:
                log.error("Missing 'nvidia_login' or 'nvidia_password'")
                raise e
            try:
                action = ActionChains(self.driver)
                button = self.driver.find_element_by_xpath(
                    '//*[@id="dr_siteButtons"]/input')

                action.move_to_element(button).click().perform()
                WebDriverWait(self.driver, 5).until(ec.staleness_of(button))
            except NoSuchElementException:
                log.error("Error signing in.")
示例#19
0
class NvidiaBuyer:
    def __init__(self, gpu, locale="en_us", test=False, interval=5):
        self.product_ids = set([])
        self.cli_locale = locale.lower()
        self.locale = self.map_locales()
        self.session = requests.Session()
        self.gpu = gpu
        self.enabled = True
        self.auto_buy_enabled = False
        self.attempt = 0
        self.started_at = datetime.now()
        self.test = test
        self.interval = interval

        self.gpu_long_name = GPU_DISPLAY_NAMES[gpu]

        # Disable auto_buy_enabled if the user does not provide a bool.
        if type(self.auto_buy_enabled) != bool:
            self.auto_buy_enabled = False

        adapter = TimeoutHTTPAdapter(max_retries=Retry(
            total=10,
            backoff_factor=1,
            status_forcelist=[429, 500, 502, 503, 504],
            method_whitelist=["HEAD", "GET", "OPTIONS"],
        ))
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)
        self.notification_handler = NotificationHandler()

        self.get_product_ids()
        print(self.product_ids)

    def map_locales(self):
        if self.cli_locale == "de_at":
            return "de_de"
        if self.cli_locale == "fr_be":
            return "fr_fr"
        if self.cli_locale == "da_dk":
            return "en_gb"
        if self.cli_locale == "cs_cz":
            return "en_gb"
        return self.cli_locale

    def get_product_ids(self):
        self.product_ids = [PRODUCT_IDS[self.locale][self.gpu]]

    def run_items(self):
        log.info(
            f"We have {len(self.product_ids)} product IDs for {self.gpu_long_name}"
        )
        log.info(f"Product IDs: {self.product_ids}")
        try:
            with ThreadPoolExecutor(
                    max_workers=len(self.product_ids)) as executor:
                product_futures = [
                    executor.submit(self.buy, product_id)
                    for product_id in self.product_ids
                ]
                concurrent.futures.wait(product_futures)
                for fut in product_futures:
                    log.debug(f"Future Result: {fut.result()}")
        except ProductIDChangedException as ex:
            log.warning("Product IDs changed.")
            self.product_ids = set([])
            self.get_product_ids()
            self.run_items()

    def buy(self, product_id):
        pass
        try:
            log.info(
                f"Checking stock for {product_id} at {self.interval} second intervals."
            )
            while not self.is_in_stock(product_id) and self.enabled:
                self.attempt = self.attempt + 1
                time_delta = str(datetime.now() -
                                 self.started_at).split(".")[0]
                with Spinner.get(
                        f"Still working (attempt {self.attempt}, have been running for {time_delta})..."
                ) as s:
                    sleep(self.interval)
            if self.enabled:
                log.info(f"{self.gpu_long_name} is in stock. Go buy it.")
                cart_url = self.open_cart_url(product_id)
                self.notification_handler.send_notification(
                    f" {self.gpu_long_name} with product ID: {product_id} in "
                    f"stock: {cart_url}")
                self.enabled = False
        except Timeout:
            log.error("Had a timeout error.")
            self.buy(product_id)

    def is_in_stock(self, product_id):
        response = self.session.get(
            NVIDIA_STOCK_API.format(product_id=product_id, locale=self.locale),
            headers=DEFAULT_HEADERS,
        )
        log.debug(f"Stock check response code: {response.status_code}")
        if response.status_code != 200:
            log.debug(response.text)
        if "PRODUCT_INVENTORY_IN_STOCK" in response.text:
            return True
        else:
            return False

    def open_cart_url(self, product_id):
        log.info("Opening cart.")
        params = {
            "Action": "AddItemToRequisition",
            "SiteID": "nvidia",
            "Locale": self.locale,
            "productID": product_id,
            "quantity": 1,
        }
        url = furl(NVIDIA_CART_URL).set(params)
        webbrowser.open_new_tab(url.url)
        return url.url
示例#20
0
class Evga:
    def __init__(self, headless=False):
        if headless:
            enable_headless()
        self.driver = webdriver.Chrome(executable_path=binary_path,
                                       options=options)
        self.credit_card = {}
        self.card_pn = ""
        self.card_series = ""
        self.notification_handler = NotificationHandler()

        try:
            if path.exists(CONFIG_PATH):
                with open(CONFIG_PATH) as json_file:
                    config = json.load(json_file)
                    username = config["username"]
                    password = config["password"]
                    self.card_pn = config.get("card_pn")
                    self.card_series = config["card_series"]
                    self.credit_card["name"] = config["credit_card"]["name"]
                    self.credit_card["number"] = config["credit_card"][
                        "number"]
                    self.credit_card["cvv"] = config["credit_card"]["cvv"]
                    self.credit_card["expiration_month"] = config[
                        "credit_card"]["expiration_month"]
                    self.credit_card["expiration_year"] = config[
                        "credit_card"]["expiration_year"]
        except Exception as e:
            log.error(
                f"This is most likely an error with your {CONFIG_PATH} file.")
            raise e

        self.login(username, password)

    def login(self, username, password):
        """
        We're just going to attempt to load cookies, else enter the user info and let the user handle the captcha
        :param username:
        :param password:
        :return:
        """
        self.driver.execute_cdp_cmd(
            "Network.setUserAgentOverride",
            {
                "userAgent":
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.53 Safari/537.36"
            },
        )
        self.driver.execute_script(
            "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
        )

        if path.isfile("evga-cookies.pkl"):  # check for cookies file
            self.driver.get("https://www.evga.com")
            selenium_utils.wait_for_page(
                self.driver,
                "EVGA - Intelligent Innovation - Official Website", 300)
            cookies = pickle.load(open("evga-cookies.pkl", "rb"))
            for cookie in cookies:
                self.driver.add_cookie(cookie)

        self.driver.get("https://www.evga.com")
        selenium_utils.wait_for_page(
            self.driver, "EVGA - Intelligent Innovation - Official Website",
            300)
        if (len(self.driver.find_elements_by_id("svg-login")) >
                0):  # cookies did not provide logged in state
            self.driver.get(LOGIN_URL)
            selenium_utils.wait_for_page(self.driver,
                                         "EVGA - Intelligent Innovation")

            selenium_utils.field_send_keys(self.driver, "evga_login", username)
            selenium_utils.field_send_keys(self.driver, "password", password)

            log.info("Go do the captcha and log in")

            selenium_utils.wait_for_page(
                self.driver,
                "EVGA - Intelligent Innovation - Official Website", 300)
            pickle.dump(self.driver.get_cookies(),
                        open("evga-cookies.pkl", "wb"))  # save cookies

        log.info("Logged in!")

    def buy(self, delay=5, test=False):
        if test:
            log.info("Refreshing Page Until Title Matches ...")
            selenium_utils.wait_for_title(
                self.driver,
                "EVGA - Products - Graphics - GeForce 16 Series Family - GTX 1660",
                "https://www.evga.com/products/ProductList.aspx?type=0&family=GeForce+16+Series+Family&chipset=GTX+1660",
            )
        else:
            log.info("Refreshing Page Until Title Matches ...")
            selenium_utils.wait_for_title(
                self.driver,
                "EVGA - Products - Graphics - GeForce 30 Series Family - RTX "
                + self.card_series,
                "https://www.evga.com/products/productlist.aspx?type=0&family=GeForce+30+Series+Family&chipset=RTX+"
                + self.card_series,
            )

        log.info("matched chipset=RTX+" + self.card_series + "!")

        if self.card_pn and not test:
            # check for card
            log.info("On GPU list Page")
            card_btn = self.driver.find_elements_by_xpath(
                "//a[@href='/products/product.aspx?pn=" + self.card_pn + "']")
            while not card_btn:
                log.debug("Refreshing page for GPU")
                self.driver.refresh()
                card_btn = self.driver.find_elements_by_xpath(
                    "//a[@href='/products/product.aspx?pn=" + self.card_pn +
                    "']")
                sleep(delay)

            card_btn[0].click()

        #  Check for stock
        log.info("On GPU Page")
        atc_buttons = self.driver.find_elements_by_xpath(
            '//input[@class="btnBigAddCart"]')
        while not atc_buttons:
            log.debug("Refreshing page for GPU")
            self.driver.refresh()
            atc_buttons = self.driver.find_elements_by_xpath(
                '//input[@class="btnBigAddCart"]')
            sleep(delay)

        #  Add to cart
        atc_buttons[0].click()

        # Send notification that product is available
        self.notification_handler.send_notification(
            f"📦 Card found in stock at EVGA (P/N {self.card_pn})…")

        #  Go to checkout
        selenium_utils.wait_for_page(self.driver, "EVGA - Checkout")
        selenium_utils.button_click_using_xpath(
            self.driver, '//*[@id="LFrame_CheckoutButton"]')

        # Shipping Address screen
        selenium_utils.wait_for_page(self.driver, "Shopping")

        log.info("Skip that page.")
        self.driver.get("https://secure.evga.com/Cart/Checkout_Payment.aspx")

        selenium_utils.wait_for_page(self.driver,
                                     "EVGA - Checkout - Billing Options")

        log.info("Ensure that we are paying with credit card")
        sleep(3)
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable(
                (By.XPATH, './/input[@value="rdoCreditCard"]'))).click()
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable(
                (By.XPATH, '//*[@id="ctl00_LFrame_btncontinue"]'))).click()

        selenium_utils.wait_for_element(self.driver,
                                        "ctl00_LFrame_txtNameOnCard")

        log.info("Populate credit card fields")

        selenium_utils.field_send_keys(self.driver,
                                       "ctl00$LFrame$txtNameOnCard",
                                       self.credit_card["name"])
        selenium_utils.field_send_keys(self.driver,
                                       "ctl00$LFrame$txtCardNumber",
                                       self.credit_card["number"])
        selenium_utils.field_send_keys(self.driver, "ctl00$LFrame$txtCvv",
                                       self.credit_card["cvv"])
        Select(self.driver.find_element_by_id(
            "ctl00_LFrame_ddlMonth")).select_by_value(
                self.credit_card["expiration_month"])
        Select(self.driver.find_element_by_id(
            "ctl00_LFrame_ddlYear")).select_by_value(
                self.credit_card["expiration_year"])
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable((
                By.XPATH,
                "/html/body/form/div[3]/div[3]/div/div[1]/div[5]/div[3]/div/div[1]/div/div[@id='checkoutButtons']/input[2]",
            ))).click()

        try:
            WebDriverWait(self.driver, 10).until(
                EC.element_to_be_clickable((
                    By.XPATH,
                    "/html/body/form/div[3]/div[3]/div/div[1]/div[5]/div[3]/div/div[1]/div/div[@id='checkoutButtons']/input[2]",
                ))).click()
        except:
            pass

        log.info("Finalize Order Page")
        selenium_utils.wait_for_page(self.driver,
                                     "EVGA - Checkout - Finalize Order")

        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable(
                (By.ID, "ctl00_LFrame_cbAgree"))).click()

        if not test:
            WebDriverWait(self.driver, 10).until(
                EC.element_to_be_clickable(
                    (By.ID, "ctl00_LFrame_btncontinue"))).click()

        log.info("Finalized Order!")

        # Send extra notification alerting user that we've successfully ordered.
        self.notification_handler.send_notification(
            f"🎉 Order submitted at EVGA for {self.card_pn}",
            audio_file="purchase.mp3",
        )
示例#21
0
class Amazon:
    def __init__(self, username, password, debug=False):
        self.notification_handler = NotificationHandler()
        if not debug:
            chrome_options.add_argument("--headless")
        self.driver = webdriver.Chrome(executable_path=binary_path,
                                       options=options,
                                       chrome_options=chrome_options)
        self.wait = WebDriverWait(self.driver, 10)
        self.username = username
        self.password = password
        self.login()
        time.sleep(3)

    def login(self):
        self.driver.get(LOGIN_URL)
        self.driver.find_element_by_xpath('//*[@id="ap_email"]').send_keys(
            self.username + Keys.RETURN)
        self.driver.find_element_by_xpath('//*[@id="ap_password"]').send_keys(
            self.password + Keys.RETURN)

        log.info(f"Logged in as {self.username}")

    def run_item(self, item_url, price_limit=1000, delay=3):
        log.info(f"Loading page: {item_url}")
        self.driver.get(item_url)
        try:
            product_title = self.wait.until(
                presence_of_element_located((By.ID, "productTitle")))
            log.info(f"Loaded page for {product_title.text}")
        except:
            log.error(self.driver.current_url)

        availability = self.driver.find_element_by_xpath(
            '//*[@id="availability"]').text.replace("\n", " ")

        log.info(f"Initial availability message is: {availability}")

        while not self.driver.find_elements_by_xpath(
                '//*[@id="buy-now-button"]'):
            try:
                self.driver.refresh()
                log.info("Refreshing page.")
                availability = self.wait.until(
                    presence_of_element_located(
                        (By.ID, "availability"))).text.replace("\n", " ")
                log.info(f"Current availability message is: {availability}")
                time.sleep(delay)
            except TimeoutException as _:
                log.warn("A polling request timed out. Retrying.")

        log.info("Item in stock, buy now button found!")
        price_str = self.driver.find_element_by_id("priceblock_ourprice").text
        price_int = int(round(float(price_str.strip("$"))))
        if price_int < price_limit:
            log.info(f"Attempting to buy item for {price_int}")
            self.buy_now()
        else:
            self.notification_handler.send_notification(
                f"Item was found, but price is at {price_int} so we did not buy it."
            )
            log.info(f"Price was too high {price_int}")

    def buy_now(self):
        self.driver.find_element_by_xpath('//*[@id="buy-now-button"]').click()
        log.info("Clicking 'Buy Now'.")

        try:
            place_order = WebDriverWait(self.driver, 2).until(
                presence_of_element_located(
                    (By.ID, "turbo-checkout-pyo-button")))
        except:
            log.debug("Went to check out page.")
            place_order = WebDriverWait(self.driver, 2).until(
                presence_of_element_located((By.NAME, "placeYourOrder1")))

        log.info("Clicking 'Place Your Order'.")
        place_order.click()
        self.notification_handler.send_notification(
            f"Item was purchased! Check your Amazon account.")

    def force_stop(self):
        self.driver.stop_client()
示例#22
0
    def __init__(self, sku_id):
        self.notification_handler = NotificationHandler()
        self.sku_id = sku_id
        self.session = requests.Session()
        self.auto_buy = False
        self.account = {"username": "", "password": ""}

        adapter = HTTPAdapter(max_retries=Retry(
            total=3,
            backoff_factor=1,
            status_forcelist=[429, 500, 502, 503, 504],
            method_whitelist=["HEAD", "GET", "OPTIONS", "POST"],
        ))
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)

        response = self.session.get(BEST_BUY_PDP_URL.format(sku=self.sku_id),
                                    headers=DEFAULT_HEADERS)
        log.info(f"PDP Request: {response.status_code}")
        self.product_url = response.url
        log.info(f"Product URL: {self.product_url}")

        self.session.get(self.product_url)
        log.info(f"Product URL Request: {response.status_code}")

        if self.auto_buy:
            log.info("Loading headless driver.")
            # options.add_argument('headless')  # This messes up the cookies for some reason.
            options.add_argument(
                "user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36"
            )
            chrome_options.add_argument(
                "user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36"
            )

            self.driver = webdriver.Chrome(
                executable_path=binary_path,
                options=options,
                chrome_options=chrome_options,
            )
            log.info("Loading https://www.bestbuy.com.")
            self.login()

            self.driver.get(self.product_url)
            cookies = self.driver.get_cookies()

            [
                self.session.cookies.set_cookie(
                    requests.cookies.create_cookie(
                        domain=cookie["domain"],
                        name=cookie["name"],
                        value=cookie["value"],
                    )) for cookie in cookies
            ]

            self.driver.quit()

            log.info("Calling location/v1/US/approximate")
            log.info(
                self.session.get(
                    "https://www.bestbuy.com/location/v1/US/approximate",
                    headers=DEFAULT_HEADERS,
                ).status_code)

            log.info("Calling basket/v1/basketCount")
            log.info(
                self.session.get(
                    "https://www.bestbuy.com/basket/v1/basketCount",
                    headers={
                        "x-client-id": "browse",
                        "User-Agent":
                        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36",
                        "Accept": "application/json",
                    },
                ).status_code)
示例#23
0
class Amazon:
    def __init__(self, username, password, item_url, headless=False):
        self.notification_handler = NotificationHandler()
        if headless:
            enable_headless()
        h = hashlib.md5(item_url.encode()).hexdigest()
        options.add_argument(f"user-data-dir=.profile-amz-{h}")
        self.driver = webdriver.Chrome(executable_path=binary_path,
                                       options=options)
        self.wait = WebDriverWait(self.driver, 10)
        self.username = username
        self.password = password
        self.driver.get(BASE_URL)
        if self.is_logged_in():
            log.info("Already logged in")
        else:
            self.login()
            time.sleep(15)

    def is_logged_in(self):
        try:
            text = wait_for_element(self.driver, "nav-link-accountList").text
            return "Hello, Sign in" not in text
        except Exception:
            return False

    def login(self):
        self.driver.get(LOGIN_URL)
        self.driver.find_element_by_xpath('//*[@id="ap_email"]').send_keys(
            self.username + Keys.RETURN)
        self.driver.find_element_by_xpath('//*[@id="ap_password"]').send_keys(
            self.password + Keys.RETURN)

        log.info(f"Logged in as {self.username}")

    def run_item(self, item_url, price_limit=1000, delay=3):
        log.info(f"Loading page: {item_url}")
        self.driver.get(item_url)
        item = ""
        try:
            product_title = self.wait.until(
                presence_of_element_located((By.ID, "productTitle")))
            log.info(f"Loaded page for {product_title.text}")
            item = product_title.text[:100].strip()
        except:
            log.error(self.driver.current_url)

        availability = self.driver.find_element_by_xpath(
            '//*[@id="availability"]').text.replace("\n", " ")

        log.info(f"Initial availability message is: {availability}")

        while not self.driver.find_elements_by_xpath(
                '//*[@id="buy-now-button"]'):
            try:
                self.driver.refresh()
                log.info(f"Refreshing for {item}...")
                availability = self.wait.until(
                    presence_of_element_located(
                        (By.ID, "availability"))).text.replace("\n", " ")
                log.info(f"Current availability message is: {availability}")
                time.sleep(delay)
            except TimeoutException as _:
                log.warn("A polling request timed out. Retrying.")

        log.info("Item in stock, buy now button found!")
        try:
            price_str = self.driver.find_element_by_id(
                "priceblock_ourprice").text
        except NoSuchElementException as _:
            price_str = self.driver.find_element_by_id(
                "priceblock_dealprice").text
        price_int = int(round(float(price_str.strip("$"))))
        if price_int < price_limit:
            log.info(f"Attempting to buy item for {price_int}")
            self.buy_now()
        else:
            self.notification_handler.send_notification(
                f"Item was found, but price is at {price_int} so we did not buy it."
            )
            log.info(f"Price was too high {price_int}")

    def buy_now(self):
        self.driver.find_element_by_xpath('//*[@id="buy-now-button"]').click()
        log.info("Clicking 'Buy Now'.")

        try:
            place_order = WebDriverWait(self.driver, 2).until(
                presence_of_element_located(
                    (By.ID, "turbo-checkout-pyo-button")))
        except:
            log.debug("Went to check out page.")
            place_order = WebDriverWait(self.driver, 2).until(
                presence_of_element_located((By.NAME, "placeYourOrder1")))

        log.info("Clicking 'Place Your Order'.")
        place_order.click()
        self.notification_handler.send_notification(
            f"Item was purchased! Check your Amazon account.")

    def force_stop(self):
        self.driver.stop_client()
示例#24
0
class NvidiaBuyer:
    def __init__(self, gpu, locale="en_us", test=False, interval=5):
        self.product_ids = set([])
        self.cli_locale = locale.lower()
        self.locale = self.map_locales()
        self.session = requests.Session()
        self.gpu = gpu
        self.enabled = True
        self.auto_buy_enabled = False
        self.attempt = 0
        self.started_at = datetime.now()
        self.test = test
        self.interval = interval

        self.gpu_long_name = GPU_DISPLAY_NAMES[gpu]

        self.cj = browser_cookie3.load(".nvidia.com")
        self.session.cookies = self.cj

        # Disable auto_buy_enabled if the user does not provide a bool.
        if type(self.auto_buy_enabled) != bool:
            self.auto_buy_enabled = False

        adapter = TimeoutHTTPAdapter()
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)
        self.notification_handler = NotificationHandler()

        self.get_product_ids()

    def map_locales(self):
        if self.cli_locale == "de_at":
            return "de_de"
        if self.cli_locale == "fr_be":
            return "fr_fr"
        if self.cli_locale == "da_dk":
            return "en_gb"
        if self.cli_locale == "cs_cz":
            return "en_gb"
        return self.cli_locale

    def get_product_ids(self):
        if isinstance(PRODUCT_IDS[self.cli_locale][self.gpu], list):
            self.product_ids = PRODUCT_IDS[self.cli_locale][self.gpu]
        if isinstance(PRODUCT_IDS[self.cli_locale][self.gpu], str):
            self.product_ids = [PRODUCT_IDS[self.cli_locale][self.gpu]]

    def run_items(self):
        log.info(
            f"We have {len(self.product_ids)} product IDs for {self.gpu_long_name}"
        )
        log.info(f"Product IDs: {self.product_ids}")
        try:
            with ThreadPoolExecutor(
                    max_workers=len(self.product_ids)) as executor:
                product_futures = [
                    executor.submit(self.buy, product_id)
                    for product_id in self.product_ids
                ]
                concurrent.futures.wait(product_futures)
                for fut in product_futures:
                    log.debug(f"Future Result: {fut.result()}")
        except ProductIDChangedException as ex:
            log.warning("Product IDs changed.")
            self.product_ids = set([])
            self.get_product_ids()
            self.run_items()

    def buy(self, product_id):
        try:
            log.info(
                f"Stock Check {product_id} at {self.interval} second intervals."
            )
            while not self.is_in_stock(product_id):
                self.attempt = self.attempt + 1
                time_delta = str(datetime.now() -
                                 self.started_at).split(".")[0]
                with Spinner.get(
                        f"Stock Check ({self.attempt}, have been running for {time_delta})..."
                ) as s:
                    sleep(self.interval)
            if self.enabled:
                cart_success = self.add_to_cart(product_id)
                if cart_success:
                    log.info(f"{self.gpu_long_name} added to cart.")
                    self.enabled = False
                    webbrowser.open(NVIDIA_CART_URL)
                    self.notification_handler.send_notification(
                        f" {self.gpu_long_name} with product ID: {product_id} in "
                        f"stock: {NVIDIA_CART_URL}")
                else:
                    self.notification_handler.send_notification(
                        f" ERROR: Attempted to add {self.gpu_long_name} to cart but couldn't, check manually!"
                    )
                    self.buy(product_id)
        except requests.exceptions.RequestException as e:
            log.warning(
                "Connection error while calling Nvidia API. API may be down.")
            log.info(
                f"Got an unexpected reply from the server, API may be down, nothing we can do but try again"
            )
            self.buy(product_id)

    def is_in_stock(self, product_id):
        try:
            response = self.session.get(
                NVIDIA_STOCK_API.format(
                    product_id=product_id,
                    locale=self.locale,
                    currency=CURRENCY_LOCALE_MAP.get(self.locale, "USD"),
                    cookies=self.cj,
                ),
                headers=DEFAULT_HEADERS,
            )
            log.debug(f"Stock check response code: {response.status_code}")
            if response.status_code != 200:
                log.debug(response.text)
            if "PRODUCT_INVENTORY_IN_STOCK" in response.text:
                return True
            else:
                return False
        except requests.exceptions.RequestException as e:
            log.info(
                f"Got an unexpected reply from the server, API may be down, nothing we can do but try again"
            )
            return False

    def add_to_cart(self, product_id):
        try:
            success, token = self.get_session_token()
            if not success:
                return False
            log.info(f"Session token: {token}")

            data = {"products": [{"productId": product_id, "quantity": 1}]}
            headers = DEFAULT_HEADERS.copy()
            headers["locale"] = self.locale
            headers["nvidia_shop_id"] = token
            headers["Content-Type"] = "application/json"
            response = self.session.post(
                url=NVIDIA_ADD_TO_CART_API,
                headers=headers,
                data=json.dumps(data),
                cookies=self.cj,
            )
            if response.status_code == 200:
                response_json = response.json()
                print(response_json)
                if "successfully" in response_json["message"]:
                    return True
            else:
                log.error(response.text)
                log.error(
                    f"Add to cart failed with {response.status_code}. This is likely an error with nvidia's API."
                )
            return False
        except requests.exceptions.RequestException as e:
            log.info(e)
            log.info(
                f"Got an unexpected reply from the server, API may be down, nothing we can do but try again"
            )
            return False

    def get_session_token(self):
        """
        Ok now this works, but I dont know when the cookies expire so might be unstable.
        :return:
        """

        params = {"format": "json", "locale": self.locale}
        headers = DEFAULT_HEADERS.copy()
        headers["locale"] = self.locale
        headers["cookie"] = "; ".join([
            f"{cookie.name}={cookie.value}" for cookie in self.session.cookies
        ])

        try:
            response = self.session.get(
                NVIDIA_TOKEN_URL,
                headers=headers,
                params=params,
                cookies=self.cj,
            )
            if response.status_code == 200:
                response_json = response.json()
                if "session_token" not in response_json:
                    log.error("Error getting session token.")
                    return False, ""
                return True, response_json["session_token"]
            else:
                log.debug(f"Get Session Token: {response.status_code}")
        except requests.exceptions.RequestException as e:
            log.info(
                f"Got an unexpected reply from the server, API may be down, nothing we can do but try again"
            )
            return False
示例#25
0
class NvidiaBuyer:
    def __init__(self, gpu, locale="en_us"):
        self.product_ids = []
        self.cli_locale = locale.lower()
        self.locale = self.map_locales()
        self.session = requests.Session()
        self.gpu = gpu
        self.enabled = True
        try:
            self.gpu_long_name = GPU_DISPLAY_NAMES[gpu]
        except Exception as e:
            log.error("Invalid GPU name.")
            raise e

        adapter = HTTPAdapter(max_retries=Retry(
            total=10,
            backoff_factor=1,
            status_forcelist=[429, 500, 502, 503, 504],
            method_whitelist=["HEAD", "GET", "OPTIONS"],
        ))
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)
        self.notification_handler = NotificationHandler()
        self.autobuy_handler = AutoBuy()

        log.info("Getting product IDs")
        self.get_product_ids()
        while len(self.product_ids) == 0:
            log.info(
                f"We have no product IDs for {self.gpu_long_name}, retrying until we get a product ID"
            )
            self.get_product_ids()
            sleep(5)

    def map_locales(self):
        if self.cli_locale == "de_at":
            return "de_de"
        if self.cli_locale == "fr_be":
            return "fr_fr"
        return self.cli_locale

    def get_product_ids(self, url=DIGITAL_RIVER_PRODUCT_LIST_URL):
        log.debug(f"Calling {url}")
        payload = {
            "apiKey": DIGITAL_RIVER_API_KEY,
            "expand": "product",
            "fields": "product.id,product.displayName,product.pricing",
            "locale": self.locale,
        }
        headers = DEFAULT_HEADERS.copy()
        headers["locale"] = self.locale
        response = self.session.get(url, headers=headers, params=payload)

        log.debug(response.status_code)
        response_json = response.json()
        for product_obj in response_json["products"]["product"]:
            if product_obj["displayName"] == self.gpu_long_name:
                if self.check_if_locale_corresponds(product_obj["id"]):
                    self.product_ids.append(product_obj["id"])
        if response_json["products"].get("nextPage"):
            self.get_product_ids(
                url=response_json["products"]["nextPage"]["uri"])

    def run_items(self):
        log.info(
            f"We have {len(self.product_ids)} product IDs for {self.gpu_long_name}"
        )
        log.info(f"Product IDs: {self.product_ids}")
        with ThreadPoolExecutor(max_workers=len(self.product_ids)) as executor:
            [
                executor.submit(self.buy, product_id)
                for product_id in self.product_ids
            ]

    def buy(self, product_id):
        log.info(
            f"Checking stock for {self.gpu_long_name} with product ID: {product_id}..."
        )
        cart_url = self.get_cart_url(product_id)
        while cart_url is None and self.enabled:
            log.debug(
                f"{self.gpu_long_name} with product ID: {product_id} not in stock."
            )
            sleep(5)
            cart_url = self.get_cart_url(product_id)

        log.info(
            f" {self.gpu_long_name} with product ID: {product_id} in stock: {cart_url}"
        )
        self.notification_handler.send_notification(
            f" {self.gpu_long_name} with product ID: {product_id} in stock: {cart_url}"
        )
        if self.autobuy_handler.enabled:
            log.info("Starting auto buy.")
            self.autobuy_handler.auto_buy(cart_url, self.locale)
            log.info("Auto buy complete.")
            self.enabled = False
        else:
            webbrowser.open_new(cart_url)
            log.info(f"Opened {cart_url}.")
            self.enabled = False

    def get_cart_url(self, product_id):
        access_token = self.get_nividia_access_token()

        payload = {
            "apiKey": DIGITAL_RIVER_API_KEY,
            "format": "json",
            "method": "post",
            "productId": product_id,
            "locale": self.locale,
            "quantity": 1,
            "token": access_token,
            "_": datetime.now(),
        }
        log.debug(f"Adding {self.gpu_long_name} ({product_id}) to cart")
        response = self.session.get(DIGITAL_RIVER_ADD_TO_CART_URL,
                                    headers=DEFAULT_HEADERS,
                                    params=payload)
        log.debug(response.status_code)

        if response.status_code not in CART_SUCCESS_CODES:
            return

        log.debug(self.session.cookies)
        params = {"token": access_token}
        url = furl(DIGITAL_RIVER_CHECKOUT_URL).set(params)
        return url.url

    def check_if_locale_corresponds(self, product_id):
        special_locales = ["en_gb", "de_at", "de_de", "fr_fr", "fr_be"]
        if self.cli_locale in special_locales:
            url = f"{DIGITAL_RIVER_PRODUCT_LIST_URL}/{product_id}"
            log.debug(f"Calling {url}")
            payload = {
                "apiKey": DIGITAL_RIVER_API_KEY,
                "expand": "product",
                "locale": self.locale,
                "format": "json",
            }

            response = self.session.get(url,
                                        headers=DEFAULT_HEADERS,
                                        params=payload)
            log.debug(response.status_code)
            response_json = response.json()
            return self.cli_locale[3:].upper(
            ) in response_json["product"]["name"]
        return True

    def get_nividia_access_token(self):
        log.debug("Getting session token")
        payload = {
            "apiKey": DIGITAL_RIVER_API_KEY,
            "format": "json",
            "locale": self.locale,
            "currency": "USD",
            "_": datetime.today(),
        }
        response = self.session.get(NVIDIA_TOKEN_URL,
                                    headers=DEFAULT_HEADERS,
                                    params=payload)
        log.debug(response.status_code)
        return response.json()["access_token"]
示例#26
0
class BestBuyHandler:
    def __init__(self, sku_id):
        self.notification_handler = NotificationHandler()
        self.sku_id = sku_id
        self.session = requests.Session()
        self.auto_buy = False
        self.account = {"username": "", "password": ""}

        adapter = HTTPAdapter(max_retries=Retry(
            total=3,
            backoff_factor=1,
            status_forcelist=[429, 500, 502, 503, 504],
            method_whitelist=["HEAD", "GET", "OPTIONS", "POST"],
        ))
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)

        response = self.session.get(BEST_BUY_PDP_URL.format(sku=self.sku_id),
                                    headers=DEFAULT_HEADERS)
        log.info(f"PDP Request: {response.status_code}")
        self.product_url = response.url
        log.info(f"Product URL: {self.product_url}")

        self.session.get(self.product_url)
        log.info(f"Product URL Request: {response.status_code}")

        if self.auto_buy:
            log.info("Loading headless driver.")
            # options.add_argument('headless')  # This messes up the cookies for some reason.
            options.add_argument(
                "user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36"
            )
            chrome_options.add_argument(
                "user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36"
            )

            self.driver = webdriver.Chrome(
                executable_path=binary_path,
                options=options,
                chrome_options=chrome_options,
            )
            log.info("Loading https://www.bestbuy.com.")
            self.login()

            self.driver.get(self.product_url)
            cookies = self.driver.get_cookies()

            [
                self.session.cookies.set_cookie(
                    requests.cookies.create_cookie(
                        domain=cookie["domain"],
                        name=cookie["name"],
                        value=cookie["value"],
                    )) for cookie in cookies
            ]

            self.driver.quit()

            log.info("Calling location/v1/US/approximate")
            log.info(
                self.session.get(
                    "https://www.bestbuy.com/location/v1/US/approximate",
                    headers=DEFAULT_HEADERS,
                ).status_code)

            log.info("Calling basket/v1/basketCount")
            log.info(
                self.session.get(
                    "https://www.bestbuy.com/basket/v1/basketCount",
                    headers={
                        "x-client-id": "browse",
                        "User-Agent":
                        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36",
                        "Accept": "application/json",
                    },
                ).status_code)

    def login(self):
        self.driver.get("https://www.bestbuy.com/identity/global/signin")
        self.driver.find_element_by_xpath('//*[@id="fld-e"]').send_keys(
            self.account["username"])
        self.driver.find_element_by_xpath('//*[@id="fld-p1"]').send_keys(
            self.account["password"])
        self.driver.find_element_by_xpath(
            "/html/body/div[1]/div/section/main/div[1]/div/div/div/div/form/div[3]/div/label/div/i"
        ).click()
        self.driver.find_element_by_xpath(
            "/html/body/div[1]/div/section/main/div[1]/div/div/div/div/form/div[4]/button"
        ).click()
        WebDriverWait(
            self.driver,
            10).until(lambda x: "Official Online Store" in self.driver.title)

    def run_item(self):
        while not self.in_stock():
            sleep(5)
        log.info(f"Item {self.sku_id} is in stock!")
        if self.auto_buy:
            self.auto_checkout()
        else:
            cart_url = self.add_to_cart()
            self.notification_handler.send_notification(
                f"SKU: {self.sku_id} in stock: {cart_url}")

    def in_stock(self):
        log.info("Checking stock")
        url = "https://www.bestbuy.com/api/tcfb/model.json?paths=%5B%5B%22shop%22%2C%22scds%22%2C%22v2%22%2C%22page%22%2C%22tenants%22%2C%22bbypres%22%2C%22pages%22%2C%22globalnavigationv5sv%22%2C%22header%22%5D%2C%5B%22shop%22%2C%22buttonstate%22%2C%22v5%22%2C%22item%22%2C%22skus%22%2C{}%2C%22conditions%22%2C%22NONE%22%2C%22destinationZipCode%22%2C%22%2520%22%2C%22storeId%22%2C%22%2520%22%2C%22context%22%2C%22cyp%22%2C%22addAll%22%2C%22false%22%5D%5D&method=get".format(
            self.sku_id)
        response = self.session.get(url, headers=DEFAULT_HEADERS)
        log.info(f"Stock check response code: {response.status_code}")
        try:
            response_json = response.json()
            item_json = find_values(json.dumps(response_json),
                                    "buttonStateResponseInfos")
            item_state = item_json[0][0]["buttonState"]
            log.info(f"Item state is: {item_state}")
            if item_json[0][0][
                    "skuId"] == self.sku_id and item_state == "ADD_TO_CART":
                return True
            else:
                return False
        except Exception as e:
            log.warning(
                "Error parsing json. Using string search to determine state.")
            log.info(response_json)
            log.error(e)
            if "ADD_TO_CART" in response.text:
                log.info("Item is in stock!")
                return True
            else:
                log.info("Item is out of stock")
                return False

    def add_to_cart(self):
        webbrowser.open_new(BEST_BUY_CART_URL.format(sku=self.sku_id))
        return BEST_BUY_CART_URL.format(sku=self.sku_id)

    def auto_checkout(self):
        tas_data = self.get_tas_data()
        self.auto_add_to_cart()
        self.start_checkout()
        self.submit_shipping()
        self.submit_payment(tas_data)

    def auto_add_to_cart(self):
        log.info("Attempting to auto add to cart...")

        body = {"items": [{"skuId": self.sku_id}]}
        headers = {
            "Accept": "application/json",
            "authority": "www.bestbuy.com",
            "User-Agent":
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36",
            "Content-Type": "application/json; charset=UTF-8",
            "Sec-Fetch-Site": "same-origin",
            "Sec-Fetch-Mode": "cors",
            "Sec-Fetch-Dest": "empty",
            "origin": "https://www.bestbuy.com",
            "referer": self.product_url,
            "Content-Length": str(len(json.dumps(body))),
        }
        # [
        #     log.info({'name': c.name, 'value': c.value, 'domain': c.domain, 'path': c.path})
        #     for c in self.session.cookies
        # ]
        log.info("Making request")
        response = self.session.post(BEST_BUY_ADD_TO_CART_API_URL,
                                     json=body,
                                     headers=headers,
                                     timeout=5)
        log.info(response.status_code)
        if (response.status_code == 200 and response.json()["cartCount"] > 0
                and self.sku_id in response.text):
            log.info(f"Added {self.sku_id} to cart!")
            log.info(response.json())
        else:
            log.info(response.status_code)
            log.info(response.json())

    def start_checkout(self):
        headers = {
            "accept":
            "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
            "accept-encoding":
            "gzip, deflate, br",
            "accept-language":
            "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
            "upgrade-insecure-requests":
            "1",
            "user-agent":
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36",
        }
        while True:
            log.info("Starting Checkout")
            response = self.session.post(
                "https://www.bestbuy.com/cart/d/checkout",
                headers=headers,
                timeout=5)
            if response.status_code == 200:
                response_json = response.json()
                log.info(response_json)
                self.order_id = response_json["updateData"]["order"]["id"]
                self.item_id = response_json["updateData"]["order"][
                    "lineItems"][0]["id"]
                log.info(f"Started Checkout for order id: {self.order_id}")
                log.info(response_json)
                if response_json["updateData"]["redirectUrl"]:
                    self.session.get(
                        response_json["updateData"]["redirectUrl"],
                        headers=headers)
                return
            log.info("Error Starting Checkout")
            sleep(5)

    def submit_shipping(self):
        log.info("Starting Checkout")
        headers = {
            "accept": "application/json, text/javascript, */*; q=0.01",
            "accept-encoding": "gzip, deflate, br",
            "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
            "content-type": "application/json",
            "origin": "https://www.bestbuy.com",
            "referer": "https://www.bestbuy.com/cart",
            "user-agent":
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36",
            "x-user-interface": "DotCom-Optimized",
            "x-order-id": self.order_id,
        }
        while True:
            log.info("Submitting Shipping")
            body = {"selected": "SHIPPING"}
            response = self.session.put(
                "https://www.bestbuy.com/cart/item/{item_id}/fulfillment".
                format(item_id=self.item_id),
                headers=headers,
                json=body,
            )
            response_json = response.json()
            log.info(response.status_code)
            log.info(response_json)
            if (response.status_code == 200
                    and response_json["order"]["id"] == self.order_id):
                log.info("Submitted Shipping")
                return True
            else:
                log.info("Error Submitting Shipping")

    def submit_payment(self, tas_data):
        body = {
            "items": [{
                "id": self.item_id,
                "type": "DEFAULT",
                "selectedFulfillment": {
                    "shipping": {
                        "address": {}
                    }
                },
                "giftMessageSelected": False,
            }]
        }
        headers = {
            "accept": "application/com.bestbuy.order+json",
            "accept-encoding": "gzip, deflate, br",
            "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
            "content-type": "application/json",
            "origin": "https://www.bestbuy.com",
            "referer": "https://www.bestbuy.com/checkout/r/fulfillment",
            "user-agent":
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36",
            "x-user-interface": "DotCom-Optimized",
        }
        r = self.session.patch(
            "https://www.bestbuy.com/checkout/d/orders/{}/".format(
                self.order_id),
            json=body,
            headers=headers,
        )
        [
            log.info({
                "name": c.name,
                "value": c.value,
                "domain": c.domain,
                "path": c.path
            }) for c in self.session.cookies
        ]
        log.info(r.status_code)
        log.info(r.text)

    def get_tas_data(self):
        headers = {
            "accept":
            "*/*",
            "accept-encoding":
            "gzip, deflate, br",
            "accept-language":
            "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
            "content-type":
            "application/json",
            "referer":
            "https://www.bestbuy.com/checkout/r/payment",
            "user-agent":
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36",
        }
        while True:
            try:
                log.info("Getting TAS Data")
                r = requests.get(
                    "https://www.bestbuy.com/api/csiservice/v2/key/tas",
                    headers=headers)
                log.info("Got TAS Data")
                return json.loads(r.text)
            except Exception as e:
                sleep(5)
示例#27
0
文件: cli.py 项目: JoeUk1981/Rtx-3080
        "You should try running pipenv shell and pipenv install per the install instructions"
    )
    print("Or you should only use Python 3.8.X per the instructions.")
    print("If you are attempting to run multiple bots, this is not supported.")
    print("You are on your own to figure this out.")
    exit(0)
import time

from notifications.notifications import NotificationHandler, TIME_FORMAT
from stores.amazon import Amazon
from stores.bestbuy import BestBuyHandler
from utils import selenium_utils
from utils.logger import log
from utils.version import check_version

notification_handler = NotificationHandler()

try:
    check_version()
except Exception as e:
    log.error(e)


def handler(signal, frame):
    log.info("Caught the stop, exiting.")
    exit(0)


def notify_on_crash(func):
    @wraps(func)
    def decorator(*args, **kwargs):