Strip3s / PhoenixBot

Phoenix Bot - A Bird Bot Resurrection
MIT License
262 stars 111 forks source link

Costco Bot #102

Open andresej21 opened 3 years ago

andresej21 commented 3 years ago

Hi could a Costco Bot be added? I'm very new to python and started trying to frankenstein some of this using existing bots, but havent been able to add the username/password to the UI to be able to test it.

from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait as wait
from webdriver_manager.chrome import ChromeDriverManager
from chromedriver_py import binary_path as driver_path
from utils import random_delay, send_webhook, create_msg
from utils.selenium_utils import change_driver
import settings, time

class Costco:
    def __init__(self, task_id, status_signal, image_signal, product, profile, proxy, monitor_delay, error_delay, max_price):
        self.task_id, self.status_signal, self.image_signal, self.product, self.profile, self.monitor_delay, self.error_delay, self.max_price = task_id, status_signal, image_signal, product, profile, float(
            monitor_delay), float(error_delay), max_price

        starting_msg = "Starting Costco"
        self.browser = self.init_driver()
        self.product_image = None

        self.SHORT_TIMEOUT = 5
        self.LONG_TIMEOUT = 20

        if settings.dont_buy:
            starting_msg = "Starting Costco in dev mode; will not actually checkout."

        self.status_signal.emit(create_msg(starting_msg, "normal"))
        self.login()
        self.monitor()
        self.add_to_cart()
        self.submit_billing()
        self.submit_order()

    def init_driver(self):
        driver_manager = ChromeDriverManager()
        driver_manager.install()
        # change_driver(self.status_signal, driver_path)
        var = driver_path
        browser = webdriver.Chrome(driver_path)

        browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": """
                  Object.defineProperty(navigator, 'webdriver', {
                   get: () => undefined
                  })
                """
        })

        return browser

    def login(self):
        self.status_signal.emit(create_msg("Logging In..", "normal"))

        self.browser.maximize_window()

        self.browser.get("https://www.costco.com")

        wait(self.browser, self.LONG_TIMEOUT).until(EC.element_to_be_clickable((By.LINK_TEXT, "Sign In / Register")))
        self.browser.find_element_by_link_text('My Account').click()

        wait(self.browser, self.LONG_TIMEOUT).until(EC.element_to_be_clickable((By.value, "Sign In"))).click()

        wait(self.browser, self.LONG_TIMEOUT).until(EC.element_to_be_clickable((By.ID, "passwordSuccessLogonMessage")))

        email = self.browser.find_element_by_id("passwordSuccessLogonMessage")
        email.send_keys(settings.costco_user)

        wait(self.browser, self.LONG_TIMEOUT).until(EC.element_to_be_clickable((By.ID, "login-form-password")))

        password = self.browser.find_element_by_id("login-form-password")
        password.send_keys(settings.costco_pass)

        time.sleep(1) # slight delay for in-between filling out login info and clicking Sign In

        wait(self.browser, self.LONG_TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="signinCheck"]/button')))
        sign_in_btn = self.browser.find_element_by_xpath('//*[@id="signinCheck"]/button')
        sign_in_btn.click()

    def monitor(self):
        wait(self.browser, self.LONG_TIMEOUT).until(lambda _: self.browser.current_url == "https://www.costco.com/AccountInformationView/")

        self.status_signal.emit(create_msg("Checking Stock..", "normal"))

        self.browser.set_window_size(900, 900)

        self.browser.get(self.product)
        wait(self.browser, self.LONG_TIMEOUT).until(lambda _: self.browser.current_url == self.product)

        in_stock = False

        while not in_stock:
            try: 
                wait(self.browser, random_delay(self.monitor_delay, settings.random_delay_start, settings.random_delay_stop)).until(EC.element_to_be_clickable((By.XPATH, '//button[@data-buttontext="Add to Cart"]')))
                add_to_cart_btn = self.browser.find_element_by_xpath('//button[@data-buttontext="Add to Cart"]')
                add_to_cart_btn.click()
                time.sleep(1)
                if not add_to_cart_btn.is_enabled():
                    self.status_signal.emit(create_msg("Waiting For Restock", "normal"))
                    self.browser.refresh()
                    continue
                in_stock = True
                self.status_signal.emit(create_msg("Added to cart", "normal"))
                self.browser.get("https://www.costco.com/CheckoutCartView")
            except:
                self.status_signal.emit(create_msg("Waiting For Restock", "normal"))
                self.browser.refresh()

    def add_to_cart(self):
        wait(self.browser, self.LONG_TIMEOUT).until(lambda _: self.browser.current_url == "https://www.costco.com/CheckoutCartView")

        self.status_signal.emit(create_msg("Checking Age Verification", "normal"))

        try:
            #seventeen_or_older_btn = self.browser.find_element_by_xpath('//*[@id="age-gate-modal"]/div/div/div[2]/div/div[2]/button')
            #seventeen_or_older_btn.click()
            time.sleep(2) # short delay for age verification modal to disappear

            # self.browser.get("https://www.gamestop.com/checkout/?stage=payment#payment")
            placeOrderBtn = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.ID, "shopCartCheckoutSubmitButton"))
                )
                placeOrderBtn.click()

        isComplete = True
        except:
            # self.browser.get("https://www.gamestop.com/checkout/?stage=payment#payment")
            placeOrderBtn = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.ID, "shopCartCheckoutSubmitButton"))
                )
                placeOrderBtn.click()

    def submit_billing(self):
        wait(self.browser, self.LONG_TIMEOUT).until(lambda _: self.browser.current_url == "https://www.costco.com/CheckoutCartDisplayView?storeId=10301&storeId=10301&catalogId=10701&catalogId=10701&langId=-1&langId=-1&krypto=I02KnWToSIKwa1OYQB6%2FxucsXvzHqCgjuMd0HyGz9C42xgGMalifOko2IATKnAbZ9wDF0q7ELVTdRx9fenKd5uj3j%2FerWgYhlLtt4SLftw7cyCTJwbopTSRVlF1THaP5fNBc6dCzj9zDmqkbqRkr%2FpBnTU5Fmw95GpJWx%2BDn14HsSh7RiQYMGGW0rNCxKErLcWqHUE64UrU6AxtJ1ExrJ1lFOWdQiDZrIHLgZIltjllBQjP77J12JsJQoxKo62DKdTJYcwXc4KkWxespZKi9K355aD8UoUUSd0%2Bb%2Fes1CWo%3D&ddkey=http%3AManageShoppingCartCmd")

        self.status_signal.emit(create_msg("Entering CVV #", "normal"))

        wait(self.browser, self.LONG_TIMEOUT).until(EC.element_to_be_clickable((By.ID, "saved-payment-security-code")))
        cvv_input = self.browser.find_element_by_id("saved-payment-security-code")
        cvv_input.send_keys(self.profile["card_cvv"])
        order_review_btn = self.browser.find_element_by_class_name("btn.btn-primary.btn-block.submit-payment")
        order_review_btn.click()

    def submit_order(self):
        wait(self.browser, self.LONG_TIMEOUT).until(lambda _: self.browser.current_url == "COSTCO.OrderSummaryCart.submitCart('https://www.costco.com/ManageShoppingCartCmd?actionType=expressCheckout');")

        self.status_signal.emit(create_msg("Submitting Order..", "normal"))

        wait(self.browser, self.LONG_TIMEOUT).until(EC.element_to_be_clickable((By.CLASS_NAME, 'btn.btn-primary.btn-block.place-order')))

        if not settings.dont_buy:
            order_review_btn = self.browser.find_element_by_class_name("btn.btn-primary.btn-block.place-order")
            order_review_btn.click()
            self.status_signal.emit(create_msg("Order Placed", "success"))
            send_webhook("OP", "Costco", self.profile["profile_name"], self.task_id, self.product_image)
        else:
            self.status_signal.emit(create_msg("Mock Order Placed", "success"))
            send_webhook("OP", "Costco", self.profile["profile_name"], self.task_id, self.product_image)
`