From c3050138514e0d982c5c46029e83dd6b870d7be6 Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 14 Sep 2021 09:37:31 -0700 Subject: [PATCH 1/7] Fixed bestbuy remember me checkbox' --- sites/bestbuy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sites/bestbuy.py b/sites/bestbuy.py index 0b0d4bc..af150a2 100644 --- a/sites/bestbuy.py +++ b/sites/bestbuy.py @@ -183,7 +183,7 @@ def login(self): time.sleep(5) # set remember me to true, probably don't need this TBH WebDriverWait(self.browser, 10).until( - EC.presence_of_element_located((By.ID, "ca-remember-me")) + EC.presence_of_element_located((By.ID, "cia-remember-me")) ).click() # Fill email field From 535635b7a0ba512d593039a30c3da519bf2268e6 Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 20 Sep 2021 20:28:52 -0700 Subject: [PATCH 2/7] Update requirements. Added Twilio and Audio Alerts. Added alerts and page logs to bestbuy and target. --- app.py | 25 ++++--- data/settings_default.json | 14 +++- pages/settingspage.py | 144 ++++++++++++++++++++++++++++++++++--- requirements.in | 33 ++++++--- requirements.txt | 78 ++++++++++++++++---- settings.py | 35 +++++++++ sites/bestbuy.py | 10 ++- sites/target.py | 14 +++- utils/__init__.py | 54 +++++++++++++- 9 files changed, 361 insertions(+), 46 deletions(-) diff --git a/app.py b/app.py index d9e3e1b..a71094c 100644 --- a/app.py +++ b/app.py @@ -1,14 +1,18 @@ -from theming.styles import globalStyles -from PyQt5 import QtCore, QtGui, QtWidgets -from pages.homepage import HomePage, TaskTab -from pages.createdialog import CreateDialog -from pages.profilespage import ProfilesPage -from pages.proxiespage import ProxiesPage -from pages.settingspage import SettingsPage -from pages.pollbrowser import PollBrowserDialog -import sys, os, settings -from theming.styles import globalStyles +import sys, os, settings +try: + from theming.styles import globalStyles + from PyQt5 import QtCore, QtGui, QtWidgets + from pages.homepage import HomePage, TaskTab + from pages.createdialog import CreateDialog + from pages.profilespage import ProfilesPage + from pages.proxiespage import ProxiesPage + from pages.settingspage import SettingsPage + from pages.pollbrowser import PollBrowserDialog + from theming.styles import globalStyles +except ModuleNotFoundError: + print("\nMissing necessary modules to run, please install using:\n\npip install -r requirements.txt\n") + sys.exit()[0] def no_abort(a, b, c): sys.__excepthook__(a, b, c) @@ -172,3 +176,4 @@ def create_task(self): ui = MainWindow() ui.setWindowIcon(QtGui.QIcon("images/birdbot.png")) os._exit(ui_app.exec_()) + diff --git a/data/settings_default.json b/data/settings_default.json index 2eb5be2..e14b3ba 100644 --- a/data/settings_default.json +++ b/data/settings_default.json @@ -16,5 +16,15 @@ "target_pass": "", "gamestop_user": "", "gamestop_pass": "", - "geckodriver":"" -} + "geckodriver": "", + "twilio_auth_token": "", + "twilio_sid": "", + "toNumber": "", + "fromNumber": "", + "text_on_error": false, + "text_on_success": true, + "text_on_stock": true, + "audio_on_error": false, + "audio_on_success": false, + "audio_on_stock": false +} \ No newline at end of file diff --git a/pages/settingspage.py b/pages/settingspage.py index 81980aa..9e1f797 100644 --- a/pages/settingspage.py +++ b/pages/settingspage.py @@ -5,7 +5,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets from theming.styles import globalStyles -from utils import return_data, write_data, Encryption, data_exists, BirdLogger, validate_data +from utils import return_data, write_data, Encryption, data_exists, BirdLogger, validate_data, create_twilio_client def no_abort(a, b, c): @@ -126,7 +126,26 @@ def setup_ui(self, settingspage): self.gamestop_user_edit = self.create_edit(self.settings_card, QtCore.QRect(300, 390, 235, 20), self.small_font, "Gamestop.com Username (Email)") self.gamestop_pass_edit = self.create_edit(self.settings_card, QtCore.QRect(300, 415, 235, 20), - self.small_font, "Gamestop.com Password") + self.small_font, "Gamestop.com Password") + + # Twilio stuff + self.twilio_auth_token_edit = self.create_edit(self.settings_card, QtCore.QRect(500, 50, 235, 20), + self.small_font, "Twilio Auth Token") + self.twilio_sid_edit = self.create_edit(self.settings_card, QtCore.QRect(500, 75, 235, 20), + self.small_font, "Twilio SID") + self.toNumber_edit = self.create_edit(self.settings_card, QtCore.QRect(500, 100, 235, 20), + self.small_font, "To Number") + self.fromNumber_edit = self.create_edit(self.settings_card, QtCore.QRect(500, 125, 235, 20), + self.small_font, "From Number") + self.text_on_error_checkbox = self.create_checkbox(QtCore.QRect(500, 150, 400, 20), "Send Text on Error") + self.text_on_success_checkbox = self.create_checkbox(QtCore.QRect(500, 175, 400, 20), "Send Text on Success") + self.text_on_stock_checkbox = self.create_checkbox(QtCore.QRect(500, 200, 400, 20), "Send Text on Stock Alert") + + # Audio stuff + self.audio_on_error_checkbox = self.create_checkbox(QtCore.QRect(500, 225, 400, 20), "Audio Alert on Error") + self.audio_on_success_checkbox = self.create_checkbox(QtCore.QRect(500, 250, 400, 20), "Audio Alert on Success") + self.audio_on_stock_checkbox = self.create_checkbox(QtCore.QRect(500, 275, 400, 20), "Audio Alert on Stock") + self.set_data() QtCore.QMetaObject.connectSlotsByName(settingspage) @@ -141,10 +160,9 @@ def set_data(self): write_data("./data/settings.json", settings_default) settings = return_data("./data/settings.json") - if not validate_data(settings, settings_default): - logger.error("Set-Settings-Data", "Parsed settings data is malformed! " - "This will most likely cause a fatal exception. " - "Try removing existing settings.json") + settings = validate_data(settings, settings_default) + # add missing settings to user specific setting file just to be sure + write_data("./data/settings.json", settings) self.webhook_edit.setText(settings["webhook"]) if settings["webhookonbrowser"]: @@ -208,6 +226,48 @@ def set_data(self): except: self.gamestop_pass_edit.setText("") + # Twilio Settings + try: + self.twilio_auth_token_edit.setText( + (Encryption().decrypt(settings["twilio_auth_token"].encode("utf-8"))).decode("utf-8")) + except: + self.twilio_auth_token_edit.setText("") + + try: + self.twilio_sid_edit.setText( + (Encryption().decrypt(settings["twilio_sid"].encode("utf-8"))).decode("utf-8")) + except: + self.twilio_sid_edit.setText("") + + try: + self.toNumber_edit.setText( + (Encryption().decrypt(settings["toNumber"].encode("utf-8"))).decode("utf-8")) + except: + self.toNumber_edit.setText("") + + try: + self.fromNumber_edit.setText( + (Encryption().decrypt(settings["fromNumber"].encode("utf-8"))).decode("utf-8")) + except: + self.fromNumber_edit.setText("") + + if settings['text_on_error']: + self.text_on_error_checkbox.setChecked(settings["text_on_error"]) + + if settings['text_on_success']: + self.text_on_success_checkbox.setChecked(settings["text_on_success"]) + + if settings['text_on_stock']: + self.text_on_stock_checkbox.setChecked(settings["text_on_stock"]) + + # Audio Settings + if settings['audio_on_error']: + self.audio_on_error_checkbox.setChecked(settings["audio_on_error"]) + if settings['audio_on_success']: + self.audio_on_success_checkbox.setChecked(settings["audio_on_success"]) + if settings['audio_on_stock']: + self.audio_on_stock_checkbox.setChecked(settings["audio_on_stock"]) + self.update_settings(settings) def save_settings(self): @@ -229,7 +289,17 @@ def save_settings(self): "target_pass": Encryption().encrypt(self.target_pass_edit.text()).decode("utf-8"), "gamestop_user": self.gamestop_user_edit.text(), "gamestop_pass": Encryption().encrypt(self.gamestop_pass_edit.text()).decode("utf-8"), - "geckodriver" : self.geckodriver_path + "geckodriver" : self.geckodriver_path, + "twilio_auth_token" : Encryption().encrypt(self.twilio_auth_token_edit.text()).decode('utf-8'), + "twilio_sid" : Encryption().encrypt(self.twilio_sid_edit.text()).decode('utf-8'), + "toNumber" : Encryption().encrypt(self.toNumber_edit.text()).decode('utf-8'), + "fromNumber" : Encryption().encrypt(self.fromNumber_edit.text()).decode('utf-8'), + "text_on_error":self.text_on_error_checkbox.isChecked(), + "text_on_success":self.text_on_success_checkbox.isChecked(), + "text_on_stock":self.text_on_stock_checkbox.isChecked(), + "audio_on_error":self.audio_on_error_checkbox.isChecked(), + "audio_on_success":self.audio_on_success_checkbox.isChecked(), + "audio_on_stock":self.audio_on_stock_checkbox.isChecked() } write_data("./data/settings.json", settings) @@ -237,8 +307,49 @@ def save_settings(self): QtWidgets.QMessageBox.information(self, "Phoenix Bot", "Saved Settings") def update_settings(self, settings_data): - global webhook, webhook_on_browser, webhook_on_order, webhook_on_failed, browser_on_failed, run_headless, bb_ac_beta, dont_buy, random_delay_start, random_delay_stop, target_user, target_pass, gamestop_user, gamestop_pass, geckodriver - settings.webhook, settings.webhook_on_browser, settings.webhook_on_order, settings.webhook_on_failed, settings.browser_on_failed, settings.run_headless, settings.bb_ac_beta, settings.buy_one, settings.dont_buy = settings_data["webhook"], settings_data["webhookonbrowser"], settings_data["webhookonorder"], settings_data["webhookonfailed"], settings_data["browseronfailed"], settings_data["runheadless"], settings_data["bb_ac_beta"], settings_data['onlybuyone'], settings_data['dont_buy'] + global \ + webhook, \ + webhook_on_browser, \ + webhook_on_order, \ + webhook_on_failed, \ + browser_on_failed, \ + run_headless, \ + bb_ac_beta, \ + buy_one, \ + dont_buy, \ + random_delay_start, \ + random_delay_stop, \ + target_user, \ + target_pass, \ + gamestop_user, \ + gamestop_pass, \ + geckodriver, \ + twilio_auth_token, \ + twilio_sid, \ + toNumber, \ + fromNumber, \ + text_on_error, \ + text_on_success, \ + text_on_stock, \ + audio_on_error, \ + audio_on_success, \ + audio_on_stock + + settings.webhook = settings_data["webhook"] + settings.webhook_on_browser = settings_data["webhookonbrowser"] + settings.webhook_on_order = settings_data["webhookonorder"] + settings.webhook_on_failed = settings_data["webhookonfailed"] + settings.browser_on_failed = settings_data["browseronfailed"] + settings.run_headless = settings_data["runheadless"] + settings.bb_ac_beta = settings_data["bb_ac_beta"] + settings.buy_one = settings_data["onlybuyone"] + settings.dont_buy = settings_data["dont_buy"] + settings.text_on_error = settings_data["text_on_error"] + settings.text_on_success = settings_data["text_on_success"] + settings.text_on_stock = settings_data["text_on_stock"] + settings.audio_on_error = settings_data["audio_on_error"] + settings.audio_on_success = settings_data["audio_on_success"] + settings.audio_on_stock = settings_data["audio_on_stock"] if settings_data.get("random_delay_start", "") != "": settings.random_delay_start = settings_data["random_delay_start"] @@ -260,3 +371,18 @@ def update_settings(self, settings_data): "utf-8") if settings_data.get("geckodriver","") != "": settings.geckodriver_path = settings_data["geckodriver"] + + # Twilio + if settings_data.get("twilio_auth_token","") != "": + settings.twilio_auth_token = (Encryption().decrypt(settings_data["twilio_auth_token"].encode("utf-8"))).decode("utf-8") + + if settings_data.get("twilio_sid","") != "": + settings.twilio_sid = (Encryption().decrypt(settings_data["twilio_sid"].encode("utf-8"))).decode("utf-8") + + if settings_data.get("toNumber","") != "": + settings.toNumber = (Encryption().decrypt(settings_data["toNumber"].encode("utf-8"))).decode("utf-8") + + if settings_data.get("fromNumber","") != "": + settings.fromNumber = (Encryption().decrypt(settings_data["fromNumber"].encode("utf-8"))).decode("utf-8") + + settings.twclient = create_twilio_client() \ No newline at end of file diff --git a/requirements.in b/requirements.in index c327f6c..790964c 100644 --- a/requirements.in +++ b/requirements.in @@ -1,11 +1,28 @@ -pyqt5==5.15.4 -js2py==0.70 -lxml==4.6.3 -darkdetect==0.2.0 -colorama==0.4.4 -selenium==3.141.0 +beepy==1.0.7 +certifi==2020.11.8 +chardet==3.0.4 chromedriver-py==92.0.4515.43 -pycryptodome -webdriver_manager==3.3.0 +click==7.1.2 +colorama==0.4.4 +configparser==5.0.1 +crayons==0.4.0 +darkdetect==0.2.0 +idna==2.10 +Js2Py==0.70 +lxml==4.6.3 pip-tools==5.5.0 +pycryptodome==3.9.9 +pyjsparser==2.7.1 +PyJWT==1.7.1 +PyQt5==5.15.4 +PyQt5-Qt5==5.15.2 +PyQt5-sip==12.8.1 +pytz==2020.4 requests==2.25.1 +selenium==3.141.0 +simpleaudio==1.0.4 +six==1.15.0 +twilio==6.63.2 +tzlocal==2.1 +urllib3==1.26.5 +webdriver-manager==3.3.0 diff --git a/requirements.txt b/requirements.txt index 7d832f6..cca569a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,14 +4,22 @@ # # pip-compile --generate-hashes --no-index --output-file=requirements.txt requirements.in # +beepy==1.0.7 \ + --hash=sha256:0e6616ad678fcf334e57c00633676ffef0ce1232127855fb2fc62c91e67ddb06 \ + --hash=sha256:817348ff3cc098aca8d1de7bc15292b762fde20fcc0a020fccea7936940d5b5f + # via -r requirements.in certifi==2020.11.8 \ --hash=sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd \ --hash=sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4 - # via requests + # via + # -r requirements.in + # requests chardet==3.0.4 \ --hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae \ --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691 - # via requests + # via + # -r requirements.in + # requests chromedriver-py==92.0.4515.43 \ --hash=sha256:0121d9a299d1eb9f858934f1f7253a18eb1bbd8282f1b0a2f44df4188a916470 \ --hash=sha256:8ee55bc0675ee9720b40dadbca9d1e0cef4fa15b0185a79a77f65a35d3462c65 @@ -19,7 +27,9 @@ chromedriver-py==92.0.4515.43 \ click==7.1.2 \ --hash=sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a \ --hash=sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc - # via pip-tools + # via + # -r requirements.in + # pip-tools colorama==0.4.4 \ --hash=sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b \ --hash=sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2 @@ -29,11 +39,15 @@ colorama==0.4.4 \ configparser==5.0.1 \ --hash=sha256:005c3b102c96f4be9b8f40dafbd4997db003d07d1caa19f37808be8031475f2a \ --hash=sha256:08e8a59ef1817ac4ed810bb8e17d049566dd6e024e7566f6285c756db2bb4ff8 - # via webdriver-manager + # via + # -r requirements.in + # webdriver-manager crayons==0.4.0 \ --hash=sha256:bd33b7547800f2cfbd26b38431f9e64b487a7de74a947b0fafc89b45a601813f \ --hash=sha256:e73ad105c78935d71fe454dd4b85c5c437ba199294e7ffd3341842bc683654b1 - # via webdriver-manager + # via + # -r requirements.in + # webdriver-manager darkdetect==0.2.0 \ --hash=sha256:3b0e90b6e4e2d8e10bf45a87de110a7a23faa9ca04e41721eee31a271a152ecf \ --hash=sha256:dd9f04d39a9f6b45456af579cc688ee4a3e9ebadd6db9022712549244584d837 @@ -41,7 +55,9 @@ darkdetect==0.2.0 \ idna==2.10 \ --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \ --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0 - # via requests + # via + # -r requirements.in + # requests js2py==0.70 \ --hash=sha256:7568b33f6bd15ee8ab1f3655928ed05481a6236ad2acca4598703ae501571661 \ --hash=sha256:f78e76c88942585973129a0a10e7054b5ea1602d761d4c0ccfa0593bc819f0bb @@ -128,13 +144,23 @@ pycryptodome==3.9.9 \ pyjsparser==2.7.1 \ --hash=sha256:2b12842df98d83f65934e0772fa4a5d8b123b3bc79f1af1789172ac70265dd21 \ --hash=sha256:be60da6b778cc5a5296a69d8e7d614f1f870faf94e1b1b6ac591f2ad5d729579 - # via js2py + # via + # -r requirements.in + # js2py +pyjwt==1.7.1 \ + --hash=sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e \ + --hash=sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96 + # via + # -r requirements.in + # twilio pyqt5-qt5==5.15.2 \ --hash=sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a \ --hash=sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962 \ --hash=sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154 \ --hash=sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327 - # via pyqt5 + # via + # -r requirements.in + # pyqt5 pyqt5-sip==12.8.1 \ --hash=sha256:0304ca9114b9817a270f67f421355075b78ff9fc25ac58ffd72c2601109d2194 \ --hash=sha256:0cd969be528c27bbd4755bd323dff4a79a8fdda28215364e6ce3e069cb56c2a9 \ @@ -157,7 +183,9 @@ pyqt5-sip==12.8.1 \ --hash=sha256:da9c9f1e65b9d09e73bd75befc82961b6b61b5a3b9d0a7c832168e1415f163c6 \ --hash=sha256:ed897c58acf4a3cdca61469daa31fe6e44c33c6c06a37c3f21fab31780b3b86a \ --hash=sha256:f168f0a7f32b81bfeffdf003c36f25d81c97dee5eb67072a5183e761fe250f13 - # via pyqt5 + # via + # -r requirements.in + # pyqt5 pyqt5==5.15.4 \ --hash=sha256:213bebd51821ed89b4d5b35bb10dbe67564228b3568f463a351a08e8b1677025 \ --hash=sha256:2a69597e0dd11caabe75fae133feca66387819fc9bc050f547e5551bce97e5be \ @@ -168,32 +196,56 @@ pyqt5==5.15.4 \ pytz==2020.4 \ --hash=sha256:3e6b7dd2d1e0a59084bcee14a17af60c5c562cdc16d828e8eba2e683d3a7e268 \ --hash=sha256:5c55e189b682d420be27c6995ba6edce0c0a77dd67bfbe2ae6607134d5851ffd - # via tzlocal + # via + # -r requirements.in + # twilio + # tzlocal requests==2.25.1 \ --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \ --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e # via # -r requirements.in + # twilio # webdriver-manager selenium==3.141.0 \ --hash=sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c \ --hash=sha256:deaf32b60ad91a4611b98d8002757f29e6f2c2d5fcaf202e1c9ad06d6772300d # via -r requirements.in +simpleaudio==1.0.4 \ + --hash=sha256:05b63da515f5fc7c6f40e4d9673d22239c5e03e2bda200fc09fd21c185d73713 \ + --hash=sha256:67348e3d3ccbae73bd126beed7f1e242976889620dbc6974c36800cd286430fc \ + --hash=sha256:691c88649243544db717e7edf6a9831df112104e1aefb5f6038a5d071e8cf41d \ + --hash=sha256:86f1b0985629852afe67259ac6c24905ca731cb202a6e96b818865c56ced0c27 \ + --hash=sha256:f1a4fe3358429b2ea3181fd782e4c4fff5c123ca86ec7fc29e01ee9acd8a227a \ + --hash=sha256:f346a4eac9cdbb1b3f3d0995095b7e86c12219964c022f4d920c22f6ca05fb4c \ + --hash=sha256:f68820297ad51577e3a77369e7e9b23989d30d5ae923bf34c92cf983c04ade04 + # via + # -r requirements.in + # beepy six==1.15.0 \ --hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259 \ --hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced - # via js2py + # via + # -r requirements.in + # js2py + # twilio +twilio==6.63.2 \ + --hash=sha256:cd1456b5deb82166d5f41d5e5eecd7938e656a626571332b9d9ae8062303fabd + # via -r requirements.in tzlocal==2.1 \ --hash=sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44 \ --hash=sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4 - # via js2py + # via + # -r requirements.in + # js2py urllib3==1.26.5 \ --hash=sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c \ --hash=sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098 # via + # -r requirements.in # requests # selenium -webdriver_manager==3.3.0 \ +webdriver-manager==3.3.0 \ --hash=sha256:22ae0ace4da117472302c21ead8ec05eb5c1312eeeba97547395d79f83afb241 \ --hash=sha256:6b54b18f75379f97f9e49f504a8e79010d47af1da8e7b5d7d7a103ca535b1e46 # via -r requirements.in diff --git a/settings.py b/settings.py index 571cafe..113f8bd 100644 --- a/settings.py +++ b/settings.py @@ -53,4 +53,39 @@ global geckodriver_path geckodriver_path = "" + +## Twilio Settings +global twclient +twclient = None + +global text_on_error +text_on_error = False + +global text_on_success +text_on_success = False + +global text_on_stock +text_on_stock = False + +global twilio_auth_token +twilio_auth_token = "" + +global twilio_sid +twilio_sid = "" + +global toNumber +toNumber = "" + +global fromNumber +fromNumber = "" + +## Audio Settings +global audio_on_error +audio_on_error = False + +global audio_on_success +audio_on_success = False + +global audio_on_stock +audio_on_stock = False ################################################# diff --git a/sites/bestbuy.py b/sites/bestbuy.py index af150a2..6f0b149 100644 --- a/sites/bestbuy.py +++ b/sites/bestbuy.py @@ -14,7 +14,7 @@ from utils.json_utils import find_values from utils.selenium_utils import enable_headless # not sure this actually works since we call options() below -from utils import create_msg, log_webpage +from utils import create_msg, log_webpage, alert try: from Crypto.PublicKey import RSA @@ -216,6 +216,7 @@ def login(self): except Exception as e: self.status_signal.emit(create_msg("Bestbuy login error, see console for details","error")) print(f"Dumped webpage to file: {log_webpage('errors','bby_login',self.browser.page_source)}") + alert("error",f"Bestbuy Login Error for sku: {self.sku_id}") if not settings.run_headless: try: @@ -274,6 +275,7 @@ def in_stock(self): "ADD_TO_CART", "PRE_ORDER" ]: + alert("stock",f"SKU {self.sku_id} for bestbuy is in stock") return True else: return False @@ -283,6 +285,7 @@ def in_stock(self): self.status_signal.emit(create_msg(f"{e}", "error")) if "ADD_TO_CART" in response.text: #TODO: Make this case insensitive self.status_signal.emit(create_msg("Item is in stock!", "normal")) + alert("stock",f"SKU {self.sku_id} for bestbuy is in stock") return True else: self.status_signal.emit(create_msg("Item is out of stock", "normal")) @@ -335,6 +338,7 @@ def add_to_cart(self, retry=False): break except(NoSuchElementException, TimeoutException) as error: print(f'Queue System Refresh Error: ${error}',flush=True) + log_webpage("errors","queue_refresh_error",self.browser.page_source) self.browser.get('https://www.bestbuy.com/cart') @@ -411,9 +415,13 @@ def start_checkout(self): self.did_submit = True else: self.status_signal.emit(create_msg("Order Placed", "success")) + alert("success",f"Successfully purchased 1x sku: {self.sku_id}") + log_webpage('success','bby_login',self.browser.page_source) self.did_submit = True except (NoSuchElementException, TimeoutException, ElementNotInteractableException): print("Could Not Complete Checkout.",flush=True) + alert("error",f"Error during checkout for sku: {self.sku_id}") + log_webpage('errors','bby_checkout',self.browser.page_source) pass except: self.status_signal.emit(create_msg('Retrying submit order until success', 'normal')) \ No newline at end of file diff --git a/sites/target.py b/sites/target.py index 11f3736..c8a1295 100644 --- a/sites/target.py +++ b/sites/target.py @@ -7,8 +7,9 @@ from webdriver_manager.chrome import ChromeDriverManager from chromedriver_py import binary_path as driver_path from selenium.webdriver.firefox.options import Options +from selenium.common.exceptions import TimeoutException -from utils import random_delay, send_webhook, create_msg +from utils import random_delay, send_webhook, create_msg, alert from utils.selenium_utils import change_driver import settings, time, random @@ -187,6 +188,15 @@ def monitor(self): # self.browser.quit() self.status_signal.emit(create_msg("Invalid URL","stopnow")) else: + + try: + TitleFetch = wait(self.browser, 5).until( + EC.presence_of_element_located((By.XPATH,"//h1[@data-test='product-title']")) + ) + if TitleFetch: + self.title = TitleFetch.text + except TimeoutException: + pass while not self.img_found: try: if not self.img_found: @@ -201,6 +211,7 @@ def monitor(self): while not self.in_stock: self.in_stock = self.check_stock() if self.in_stock: + alert("stock",f"Target {self.title} in stock, sku:{self.sku_id}\n\n{self.product}") continue else: self.status_signal.emit(create_msg("Waiting on Restock", "normal")) @@ -242,6 +253,7 @@ def submit_order(self): self.status_signal.emit(create_msg("Mock Order Placed", "success")) else: self.status_signal.emit(create_msg("Order Placed", "success")) + alert("success",f"Target succesful order for {self.sku_id}") send_webhook("OP", "Target", self.profile["profile_name"], self.task_id, self.product_image) self.did_submit = True except: diff --git a/utils/__init__.py b/utils/__init__.py index 8d2dd12..19c98b3 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -7,6 +7,7 @@ import string import os import pathlib +from beepy import beep from Crypto import Random from Crypto.Cipher import AES @@ -15,8 +16,24 @@ import settings from webhook import DiscordEmbed, DiscordWebhook +from twilio.rest import Client + normal_color = Fore.CYAN +def create_twilio_client(): + try: + if settings.twilio_auth_token != "" and settings.twilio_sid != "": + auth_token = settings.twilio_auth_token + account_sid = settings.twilio_sid + twclient = Client(account_sid, auth_token) + # print(twclient) + return twclient + else: + # print(f"client not set") + return None + except Exception as e: + print(f"Twilio Connect error: {e}") + pass def write_data(path, data): with open(path, "w") as file: @@ -92,7 +109,10 @@ def return_data(path): def validate_data(test_data, control_data): - return test_data.keys() == control_data.keys() + for key in control_data.keys(): + if key not in test_data.keys(): + test_data[key] = control_data[key] + return test_data def data_exists(path): @@ -195,4 +215,34 @@ def log_webpage(path, output_type, data): fp.write(data) fp.close() return filename - \ No newline at end of file + +def alert(alert_type, msg): + # print(f"Alert {alert_type} - {msg}") + if alert_type == "error": + if settings.audio_on_error: + beep(2) + if settings.text_on_error: + send_text(msg) + + if alert_type == "stock": + if settings.audio_on_stock: + beep(4) + if settings.text_on_stock: + send_text(msg) + + if alert_type == "success": + if settings.audio_on_success: + beep(6) + if settings.text_on_success: + send_text(msg) + +def send_text(msg): + if settings.twclient: + message = settings.twclient.messages.create( + to=settings.toNumber, + from_=settings.fromNumber, + body=msg + ) + +def validate_number(): + pass \ No newline at end of file From 85bf8df0e7ef641e554938a33276b52d84e9463a Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 20 Sep 2021 20:41:46 -0700 Subject: [PATCH 3/7] Added test text button for twilio --- pages/settingspage.py | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/pages/settingspage.py b/pages/settingspage.py index 9e1f797..a537adb 100644 --- a/pages/settingspage.py +++ b/pages/settingspage.py @@ -5,7 +5,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets from theming.styles import globalStyles -from utils import return_data, write_data, Encryption, data_exists, BirdLogger, validate_data, create_twilio_client +from utils import return_data, send_text, write_data, Encryption, data_exists, BirdLogger, validate_data, create_twilio_client def no_abort(a, b, c): @@ -57,6 +57,9 @@ def create_edit(self, parent, rect, font, placeholder) -> QtWidgets.QLineEdit: def get_folder(self): self.geckodriver_path = QtWidgets.QFileDialog.getExistingDirectory(self, 'Select Folder') + + def send_test_text(self): + send_text("This is a test text from your PhoenixBot.") def setup_ui(self, settingspage): self.settingspage = settingspage @@ -129,23 +132,32 @@ def setup_ui(self, settingspage): self.small_font, "Gamestop.com Password") # Twilio stuff - self.twilio_auth_token_edit = self.create_edit(self.settings_card, QtCore.QRect(500, 50, 235, 20), + self.twilio_auth_token_edit = self.create_edit(self.settings_card, QtCore.QRect(600, 50, 235, 20), self.small_font, "Twilio Auth Token") - self.twilio_sid_edit = self.create_edit(self.settings_card, QtCore.QRect(500, 75, 235, 20), + self.twilio_sid_edit = self.create_edit(self.settings_card, QtCore.QRect(600, 75, 235, 20), self.small_font, "Twilio SID") - self.toNumber_edit = self.create_edit(self.settings_card, QtCore.QRect(500, 100, 235, 20), + self.toNumber_edit = self.create_edit(self.settings_card, QtCore.QRect(600, 100, 235, 20), self.small_font, "To Number") - self.fromNumber_edit = self.create_edit(self.settings_card, QtCore.QRect(500, 125, 235, 20), + self.fromNumber_edit = self.create_edit(self.settings_card, QtCore.QRect(600, 125, 235, 20), self.small_font, "From Number") - self.text_on_error_checkbox = self.create_checkbox(QtCore.QRect(500, 150, 400, 20), "Send Text on Error") - self.text_on_success_checkbox = self.create_checkbox(QtCore.QRect(500, 175, 400, 20), "Send Text on Success") - self.text_on_stock_checkbox = self.create_checkbox(QtCore.QRect(500, 200, 400, 20), "Send Text on Stock Alert") + self.text_on_error_checkbox = self.create_checkbox(QtCore.QRect(600, 150, 400, 20), "Send Text on Error") + self.text_on_success_checkbox = self.create_checkbox(QtCore.QRect(600, 175, 400, 20), "Send Text on Success") + self.text_on_stock_checkbox = self.create_checkbox(QtCore.QRect(600, 200, 400, 20), "Send Text on Stock Alert") # Audio stuff - self.audio_on_error_checkbox = self.create_checkbox(QtCore.QRect(500, 225, 400, 20), "Audio Alert on Error") - self.audio_on_success_checkbox = self.create_checkbox(QtCore.QRect(500, 250, 400, 20), "Audio Alert on Success") - self.audio_on_stock_checkbox = self.create_checkbox(QtCore.QRect(500, 275, 400, 20), "Audio Alert on Stock") - + self.audio_on_error_checkbox = self.create_checkbox(QtCore.QRect(600, 225, 400, 20), "Audio Alert on Error") + self.audio_on_success_checkbox = self.create_checkbox(QtCore.QRect(600, 250, 400, 20), "Audio Alert on Success") + self.audio_on_stock_checkbox = self.create_checkbox(QtCore.QRect(600, 275, 400, 20), "Audio Alert on Stock") + + self.testtext_btn = QtWidgets.QPushButton(self.settings_card) + self.testtext_btn.setGeometry(QtCore.QRect(600, 325, 86, 32)) + self.testtext_btn.setFont(self.small_font) + self.testtext_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) + self.testtext_btn.setStyleSheet( + "color: #FFFFFF;background-color: {};border-radius: 10px;border: 1px solid #2e2d2d;".format( + globalStyles["primary"])) + self.testtext_btn.setText("Send Test Text") + self.testtext_btn.clicked.connect(self.send_test_text) self.set_data() QtCore.QMetaObject.connectSlotsByName(settingspage) From f177d9977834f1d4dcee4505d825c9a4ae333622 Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 28 Sep 2021 11:32:39 -0700 Subject: [PATCH 4/7] Added error console print for bestbuy. Changed error catch on bestbuy to stopnow to prevent script from running when not logged in. --- pages/homepage.py | 2 +- sites/bestbuy.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pages/homepage.py b/pages/homepage.py index 95e6c78..7b36cdb 100644 --- a/pages/homepage.py +++ b/pages/homepage.py @@ -388,8 +388,8 @@ def update_status(self,msg): carted_count.setText(str(int(carted_count.text())+1)) elif msg["status"] == "stopnow": self.status_label.setStyleSheet("color: rgb(252, 81, 81);") - logger.error(self.task_id,msg["msg"]) self.stop(msg["msg"]) + logger.error(self.task_id, msg["msg"]) diff --git a/sites/bestbuy.py b/sites/bestbuy.py index 2311de3..7ad2fdc 100644 --- a/sites/bestbuy.py +++ b/sites/bestbuy.py @@ -225,10 +225,12 @@ def login(self): self.status_signal.emit(create_msg("Sign In - Add Recovery phone page hit, probably can ignore...","normal")) self.browser.get("https://www.bestbuy.com") except Exception as e: - self.status_signal.emit(create_msg("Bestbuy login error, see console for details","error")) + self.status_signal.emit(create_msg("Bestbuy login error, see console for details","stopnow")) print(f"Dumped webpage to file: {log_webpage('errors','bby_login',self.browser.page_source)}") + print(f"Error: {e}") alert("error",f"Bestbuy Login Error for sku: {self.sku_id}") + if self.verify_signed_in(): self.status_signal.emit(create_msg("Bestbuy successfully logged in.","normal")) time.sleep(2) From 51023d5848f7c23fa56e252e15e8cc3d3f406c0d Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 28 Sep 2021 11:35:17 -0700 Subject: [PATCH 5/7] Updated readme for powershell issues --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 7935beb..13af5b1 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,14 @@ Target Signin error work around - Wait for target browser to launch, once it's r ``` pip install pycryptodomex ``` + * If you get an error regarding executing scripts in Powershell, first ensure running powershell as administrator. Otherwise try this first + ``` + powershell -ExecutionPolicy Bypass -File ./env/scripts/Activate.ps1 + ``` + If that doesn't work, try the below followed by a "Y" + ``` + Set-ExecutionPolicy RemoteSigned + ``` 5. Run the following command: ``` python app.py From d1471126b5c4cbb668133e77879086ffd39894b3 Mon Sep 17 00:00:00 2001 From: Spencer Larsen Date: Wed, 29 Sep 2021 21:04:43 -0700 Subject: [PATCH 6/7] target fix --- sites/target.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sites/target.py b/sites/target.py index 7280867..fa43174 100644 --- a/sites/target.py +++ b/sites/target.py @@ -3,6 +3,7 @@ from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait as wait +from selenium.webdriver.support.wait import WebDriverWait # from selenium.webdriver.chrome.options import Options from webdriver_manager.chrome import ChromeDriverManager from chromedriver_py import binary_path as driver_path @@ -102,8 +103,10 @@ def login(self): EC.presence_of_element_located((By.XPATH,"//li[@id='accountNav-signIn']/a")) ) test.click() - self.fill_and_authenticate() + WebDriverWait(self.browser, 10).until(EC.presence_of_element_located((By.XPATH, '//input[@id="username"]'))) + self.fill_and_authenticate() + test = self.browser.find_element_by_xpath('//span[@data-test="accountUserName"]') time.sleep(1) if "sign in" in test.text.lower(): From 1f1d65127d548098df9e05d8a5a8474ea9a27a6d Mon Sep 17 00:00:00 2001 From: Spencer Date: Fri, 15 Oct 2021 09:48:29 -0700 Subject: [PATCH 7/7] Removed slow target username and password entry --- sites/target.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sites/target.py b/sites/target.py index fa43174..e7c66f7 100644 --- a/sites/target.py +++ b/sites/target.py @@ -126,13 +126,17 @@ def fill_and_authenticate(self): # slowly enters username and password with random waits between each character if self.browser.find_elements_by_id('username'): - for key in settings.target_user: - self.browser.find_element_by_xpath('//input[@id="username"]').send_keys(key) - time.sleep(random.uniform(0.5, 1.5)) + # for key in settings.target_user: + # self.browser.find_element_by_xpath('//input[@id="username"]').send_keys(key) + # time.sleep(random.uniform(0.5, 1.5)) + + self.browser.find_element_by_xpath('//input[@id="username"]').send_keys(settings.target_user) - for key in settings.target_pass: - self.browser.find_element_by_xpath('//input[@id="password"]').send_keys(key) - time.sleep(random.uniform(0.5, 1.5)) + # for key in settings.target_pass: + # self.browser.find_element_by_xpath('//input[@id="password"]').send_keys(key) + # time.sleep(random.uniform(0.5, 1.5)) + + self.browser.find_element_by_xpath('//input[@id="password"]').send_keys(settings.target_pass) loginBtn = wait(self.browser, self.TIMEOUT_LONG).until( EC.presence_of_element_located((By.XPATH,'//button[@id="login"]'))