├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── LICENSE ├── README.md ├── app.py ├── data ├── .DS_Store └── settings_default.json ├── encrypt.js ├── images ├── birdbot.png ├── cart.png ├── down_icon.png ├── downarrow_icon.png ├── edit.png ├── home.png ├── home_alt.png ├── play.png ├── profiles.png ├── profiles_alt.png ├── proxies.png ├── proxies_alt.png ├── settings.png ├── settings_alt.png ├── stop.png ├── success.png ├── tasks.png ├── trash.png └── uparrow_icon.png ├── pages ├── .DS_Store ├── createdialog.py ├── homepage.py ├── pollbrowser.py ├── profilespage.py ├── proxiespage.py └── settingspage.py ├── requirements.in ├── requirements.txt ├── settings.py ├── sites ├── .DS_Store ├── __init__.py ├── bestbuy.py ├── gamestop.py ├── target.py ├── walmart.py └── walmart_encryption.py ├── theming ├── styleHelper.js └── styles.py ├── utils ├── __init__.py ├── json_utils.py └── selenium_utils.py └── webhook.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve our project! 4 | title: "[BUG] EWW! I FOUND A BUG" 5 | labels: bug 6 | assignees: broglea, gr33k, listonjesse, rodriada000, Strip3s 7 | 8 | --- 9 | 10 | # Expected Behavior 11 | 12 | Give a description of what you expected the application to behave. 13 | 14 | # Actual Behaviour 15 | 16 | Give a description of what the application actually done. 17 | 18 | # Screenshots 19 | 20 | Place any informational screenshots here. 21 | 22 | # Repro Steps 23 | 24 | Please include the steps of how we can reproduce this issue on our end. 25 | 26 | I.E. 27 | 28 | 1. Go to '...' 29 | 2. Click on '....' 30 | 3. Scroll down to '....' 31 | 4. See error 32 | 33 | # Desktop Configuration 34 | - OS: [e.g. iOS] 35 | - Browser [e.g. chrome, safari] 36 | - Version [e.g. 22] 37 | 38 | # Additional Context 39 | 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE] Implement A New Feature :)" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Summary 11 | 12 | Give a short description of the goal of this feature along with why it would be beneficial to the project. 13 | 14 | # MVP Features 15 | 16 | What would be required at a minimum in order to get the feature implemented within the source? 17 | 18 | # Additional Information 19 | 20 | Please let our team know any additional information you would like to provide. 21 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '29 2 * * 5' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript', 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | - name: Setup Python 45 | uses: actions/setup-python@v2.2.2 46 | 47 | # Initializes the CodeQL tools for scanning. 48 | - name: Initialize CodeQL 49 | uses: github/codeql-action/init@v1 50 | with: 51 | languages: ${{ matrix.language }} 52 | # If you wish to specify custom queries, you can do so here or in a config file. 53 | # By default, queries listed here will override any specified in a config file. 54 | # Prefix the list here with "+" to use these queries and those in the config file. 55 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 56 | 57 | 58 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 59 | # If this step fails, then you should remove it and run the build manually (see below) 60 | - name: Autobuild 61 | uses: github/codeql-action/autobuild@v1 62 | 63 | # ℹ️ Command-line programs to run using the OS shell. 64 | # 📚 https://git.io/JvXDl 65 | 66 | 67 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 68 | # and modify them (or add more) to build your code if your project 69 | # uses a compiled language 70 | 71 | #- run: | 72 | # make bootstrap 73 | # make release 74 | 75 | - name: Perform CodeQL Analysis 76 | uses: github/codeql-action/analyze@v1 77 | 78 | 79 | #- name: Install Dependencies and venv 80 | # run: | 81 | # python3 -m venv ./env 82 | # ./env/Scripts/activate 83 | #python3 -m pip install --upgrade pip 84 | #pip3 install -r requirements.txt 85 | #- name: run app 86 | # run: python3 app.py 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Avoid user data, include defaults 2 | data/* 3 | *.json 4 | !*_default.json 5 | __pycache__/ 6 | debug.log 7 | 8 | # IDEs 9 | .idea/ 10 | # Environments 11 | .env 12 | .venv 13 | env/ 14 | venv/ 15 | ENV/ 16 | env.bak/ 17 | venv.bak/ 18 | # OSX 19 | .DS_Store 20 | error_dumps/* 21 | test.py 22 | selenium/* 23 | *.exe 24 | *.log 25 | output/* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Nate 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Phoenix Bot 2 | [Discord](https://discord.gg/mTp4awX9wB)

3 | Phoenix Bot is inspired by Natewong1313's Bird Bot project yet due to lack of activity by their team. We have decided to revive this project to achieve a common goal. Due to the recent insurgence of botters/scalpers taking advantage, our goal is to enable everyone the ability to combat these botters/scalpers by implementing their own botting system. Currently, this auto-checkout bot will support Walmart, Best Buy, Gamestop, & Target. There are more plans for future implementations later on. 4 | 5 | * Easy to use interface built on PyQt5 6 | * Waits for items to restock if they are out of stock 7 | * Optional price checker 8 | * Lighting fast auto-checkout 9 | 10 | ## Current Functionality 11 | 12 | | **Website** | **Auto Checkout** | **Open Cart Link** | **Work In Progress** | 13 | |:---:|:---:|:---:|:---:| 14 | | amazon.com | | |`✔`| 15 | | bestbuy.com |`✔`|`✔`| | 16 | | gamestop.com | |`✔`|`✔`| 17 | | target.com |`✔`| |`✔`| 18 | | walmart.com |`✔`| | | 19 | 20 | Gamestop - Captcha work-arounds in progress, will attempt to add to cart currently and then open browser for you to click captcha's. 21 | 22 | Target Signin error work around - Wait for target browser to launch, once it's refreshing on product page, open new tab in browser, navigate to target.com and signin manually, then close that tab. 23 | 24 |

25 | Phoenix Bot UI 26 |

27 | 28 | 29 | ## Phoenix Bot Repository Link 30 | [View The Repo Here](https://github.com/Strip3s/PhoenixBot.git/) 31 | 32 | ## Quick Install for Windows 33 | 1. Make sure your Chrome browser is updated to the latest 34 | 2. Install the latest version of [Git](https://git-scm.com/downloads) & [Python](https://www.python.org/downloads/) 35 | 3. Open Powershell as Administrator within your desired directory for the application to live. 36 | 4. Run the following commands: 37 | ``` 38 | git clone https://github.com/Strip3s/PhoenixBot/ 39 | ``` 40 | ``` 41 | cd PhoenixBot 42 | ``` 43 | ``` 44 | python -m venv ./env 45 | ``` 46 | ``` 47 | ./env/Scripts/activate 48 | ``` 49 | ``` 50 | python -m pip install --upgrade pip 51 | ``` 52 | ``` 53 | pip install -r requirements.txt 54 | ``` 55 | 4. If you encounter any errors during installation, please consider the following: 56 | * If you get a red text error remove and you previously installed pyqt5 or lxml on your python, remove the versions from the **requirements.in** for both lxml and pyqt5, then run the following commands: 57 | ``` 58 | pip install pip-tools==5.5.0 59 | ``` 60 | ``` 61 | pip-compile --generate-hashes --no-index --output-file=requirements.txt requirements.in 62 | ``` 63 | ``` 64 | pip install -r requirements.txt 65 | ``` 66 | * If you get an error with red text run the following: 67 | ``` 68 | pip install pycryptodomex 69 | ``` 70 | 5. Run the following command: 71 | ``` 72 | python app.py 73 | ``` 74 | 75 | 6. To Run Target.com bot, download GeckoDriver and select the folder of it's location in the settings. 76 | * Latest Driver releases can be found at: https://github.com/mozilla/geckodriver/releases 77 | 78 | ## Quick Install for Mac 79 | **It is highly recommended you install brew and update python3 to latest version** 80 | 81 | 1. Make sure your Chrome browser is updated to the latest 82 | 2. Run the following commands: 83 | ``` 84 | git clone https://github.com/Strip3s/PhoenixBot/ 85 | ``` 86 | ``` 87 | cd PhoenixBot 88 | ``` 89 | ``` 90 | python3 -m venv ./env 91 | ``` 92 | ``` 93 | source env/bin/activate 94 | ``` 95 | ``` 96 | python3 -m pip3 install --upgrade pip 97 | ``` 98 | ``` 99 | pip3 install -r requirements.txt 100 | ``` 101 | 3. If you encounter any errors during installation, please consider the following: 102 | * If you get a red text error remove and you previously installed pyqt5 or lxml on your python, remove the versions from the **requirements.in** for both lxml and pyqt5, then run the following commands: 103 | ``` 104 | pip3 install pip-tools==5.4.0 105 | ``` 106 | ``` 107 | pip-compile --generate-hashes --no-index --output-file=requirements.txt requirements.in 108 | ``` 109 | ``` 110 | pip3 install -r requirements.txt 111 | ``` 112 | * If you get an error with red text run the following: 113 | ``` 114 | pip3 install pycryptodomex 115 | ``` 116 | 4. Run the following command: 117 | 118 | ``` 119 | python3 app.py 120 | ``` 121 | 122 | 5. To Run Target.com bot, download GeckoDriver and select the folder of it's location in the settings. 123 | * Latest Driver releases can be found at: https://github.com/mozilla/geckodriver/releases 124 | 125 | 126 | ## Windows FAQs 127 | To resume working on the bot for testing after closing powershell, navigate again to the folder and run the following commands: 128 | ``` 129 | ./env/Scripts/activate 130 | python app.py 131 | ``` 132 | 133 | 134 | ## Contributing 135 | This project uses pip-compile with the --generate-hashes flag to validate dependencies via checksums. This helps prevent updates to existing package versions on PyPI from adding new code to our project. When changing requirements, make updates in the requirements.in file, then compile using the command below to update requirements.txt before committing. 136 | ``` 137 | pip-compile --generate-hashes --no-index --output-file=requirements.txt requirements.in 138 | ``` 139 | If you receive an error when running this command related to pinning a version of pip, make sure your system or virtualenv pip version is up to date by running the following command: 140 | ``` 141 | python -m pip install --upgrade pip 142 | ``` 143 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from theming.styles import globalStyles 2 | from PyQt5 import QtCore, QtGui, QtWidgets 3 | from pages.homepage import HomePage, TaskTab 4 | from pages.createdialog import CreateDialog 5 | from pages.profilespage import ProfilesPage 6 | from pages.proxiespage import ProxiesPage 7 | from pages.settingspage import SettingsPage 8 | from pages.pollbrowser import PollBrowserDialog 9 | import sys, os, settings 10 | from theming.styles import globalStyles 11 | 12 | 13 | def no_abort(a, b, c): 14 | sys.__excepthook__(a, b, c) 15 | 16 | 17 | sys.excepthook = no_abort 18 | 19 | 20 | class MainWindow(QtWidgets.QMainWindow): 21 | 22 | def __init__(self, parent=None): 23 | super(MainWindow, self).__init__(parent=parent) 24 | self.setupUi(self) 25 | self.show() 26 | 27 | def setupUi(self, MainWindow): 28 | MainWindow.setFixedSize(1109, 600) 29 | # background color for main UI 30 | MainWindow.setStyleSheet("background-color: {};".format(globalStyles["backgroundDark"])) 31 | MainWindow.setWindowTitle("Phoenix Bot") 32 | self.centralwidget = QtWidgets.QWidget(MainWindow) 33 | self.centralwidget.setStyleSheet("QMessageBox QLabel { color: #FFFFFF; }QMessageBox QPushButton { background-color: %s;color: #FFFFFF;}" % (globalStyles["primary"]) ) 34 | self.sidebar = QtWidgets.QWidget(self.centralwidget) 35 | self.sidebar.setGeometry(QtCore.QRect(0, 0, 61, 601)) 36 | # SIDE BAR COLOR 37 | self.sidebar.setStyleSheet('background-color: {};border-right: 1px solid #2e2d2d;'.format(globalStyles['backgroundLight'])) 38 | self.home_tab = QtWidgets.QWidget(self.sidebar) 39 | self.home_tab.setGeometry(QtCore.QRect(0, 85, 60, 45)) 40 | self.home_tab.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 41 | #initial selected color background 42 | self.home_tab.setStyleSheet("background-color: {};border: none;".format(globalStyles['primaryAscent'])) 43 | self.home_active_tab = QtWidgets.QWidget(self.home_tab) 44 | self.home_active_tab.setGeometry(QtCore.QRect(0, 0, 4, 45)) 45 | #initial selected color side bar 46 | self.home_active_tab.setStyleSheet("background-color: {};border: none;".format(globalStyles["primary"])) 47 | self.home_active_tab.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 48 | self.home_icon = QtWidgets.QLabel(self.home_tab) 49 | self.home_icon.setGeometry(QtCore.QRect(21, 13, 20, 20)) 50 | self.home_icon.setStyleSheet("border: none;") 51 | self.home_icon.setText("") 52 | self.home_icon.setPixmap(QtGui.QPixmap("images/home_alt.png")) 53 | self.home_icon.setScaledContents(True) 54 | self.home_icon.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 55 | self.profiles_tab = QtWidgets.QWidget(self.sidebar) 56 | self.profiles_tab.setGeometry(QtCore.QRect(0, 130, 60, 45)) 57 | self.profiles_tab.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 58 | self.profiles_tab.setStyleSheet("background-color: transparent;border: none;") 59 | self.profiles_active_tab = QtWidgets.QWidget(self.profiles_tab) 60 | self.profiles_active_tab.setGeometry(QtCore.QRect(0, 0, 4, 45)) 61 | self.profiles_active_tab.setStyleSheet("background-color: transparent;border: none;") 62 | self.profiles_active_tab.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 63 | self.profiles_icon = QtWidgets.QLabel(self.profiles_tab) 64 | self.profiles_icon.setGeometry(QtCore.QRect(21, 13, 20, 20)) 65 | self.profiles_icon.setStyleSheet("border: none;") 66 | self.profiles_icon.setText("") 67 | self.profiles_icon.setPixmap(QtGui.QPixmap("images/profiles.png")) 68 | self.profiles_icon.setScaledContents(True) 69 | self.profiles_icon.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 70 | self.proxies_tab = QtWidgets.QWidget(self.sidebar) 71 | self.proxies_tab.setGeometry(QtCore.QRect(0, 175, 60, 45)) 72 | self.proxies_tab.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 73 | self.proxies_tab.setStyleSheet("background-color: transparent;border: none;") 74 | self.proxies_active_tab = QtWidgets.QWidget(self.proxies_tab) 75 | self.proxies_active_tab.setGeometry(QtCore.QRect(0, 0, 4, 45)) 76 | self.proxies_active_tab.setStyleSheet("background-color: transparent;border: none;") 77 | self.proxies_icon = QtWidgets.QLabel(self.proxies_tab) 78 | self.proxies_icon.setGeometry(QtCore.QRect(21, 13, 20, 20)) 79 | self.proxies_icon.setStyleSheet("border: none;") 80 | self.proxies_icon.setPixmap(QtGui.QPixmap("images/proxies.png")) 81 | self.proxies_icon.setScaledContents(True) 82 | self.settings_tab = QtWidgets.QWidget(self.sidebar) 83 | self.settings_tab.setGeometry(QtCore.QRect(0, 220, 60, 45)) 84 | self.settings_tab.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 85 | self.settings_tab.setStyleSheet("background-color: transparent;border: none;") 86 | self.settings_active_tab = QtWidgets.QWidget(self.settings_tab) 87 | self.settings_active_tab.setGeometry(QtCore.QRect(0, 0, 4, 45)) 88 | self.settings_active_tab.setStyleSheet("background-color: transparent;border: none;") 89 | self.settings_icon = QtWidgets.QLabel(self.settings_tab) 90 | self.settings_icon.setGeometry(QtCore.QRect(21, 13, 20, 20)) 91 | self.settings_icon.setStyleSheet("border: none;") 92 | self.settings_icon.setPixmap(QtGui.QPixmap("images/settings.png")) 93 | self.settings_icon.setScaledContents(True) 94 | self.logo = QtWidgets.QLabel(self.sidebar) 95 | self.logo.setGeometry(QtCore.QRect(10, 23, 41, 41)) 96 | self.logo.setStyleSheet("border: none;color:red;") 97 | self.logo.setText("") 98 | self.logo.setPixmap(QtGui.QPixmap("images/birdbot.png")) 99 | self.logo.setScaledContents(True) 100 | self.homepage = HomePage(self.centralwidget) 101 | self.createdialog = CreateDialog(self) 102 | self.createdialog.addtask_btn.clicked.connect(self.create_task) 103 | self.createdialog.setWindowIcon(QtGui.QIcon("images/birdbot.png")) 104 | self.createdialog.hide() 105 | self.profilespage = ProfilesPage(self.centralwidget) 106 | self.profilespage.hide() 107 | self.proxiespage = ProxiesPage(self.centralwidget) 108 | self.proxiespage.hide() 109 | self.settingspage = SettingsPage(self.centralwidget) 110 | self.settingspage.hide() 111 | MainWindow.setCentralWidget(self.centralwidget) 112 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 113 | self.set_functions() 114 | 115 | def set_functions(self): 116 | self.current_page = "home" 117 | self.home_tab.mousePressEvent = lambda event: self.change_page(event, "home") 118 | self.profiles_tab.mousePressEvent = lambda event: self.change_page(event, "profiles") 119 | self.proxies_tab.mousePressEvent = lambda event: self.change_page(event, "proxies") 120 | self.settings_tab.mousePressEvent = lambda event: self.change_page(event, "settings") 121 | self.homepage.newtask_btn.clicked.connect(self.createdialog.show) 122 | 123 | def change_page(self,event,current_page): 124 | eval('self.{}_active_tab.setStyleSheet("background-color: transparent;border: none;")'.format(self.current_page)) 125 | # reseting image after deselect 126 | eval('self.{}_icon.setPixmap(QtGui.QPixmap("images/{}.png"))'.format(self.current_page,self.current_page)) 127 | eval('self.{}_tab.setStyleSheet("background-color: transparent;border: none;")'.format(self.current_page)) 128 | eval("self.{}page.hide()".format(self.current_page)) 129 | self.current_page = current_page 130 | # after initial tab side color 131 | eval('self.{}_active_tab.setStyleSheet("background-color: {};border: none;")'.format(self.current_page,globalStyles["primary"])) 132 | # grabing same image for selected tab 133 | eval('self.{}_icon.setPixmap(QtGui.QPixmap("images/{}_alt.png"))'.format(self.current_page,self.current_page)) 134 | # after initial tab side background color 135 | eval('self.{}_tab.setStyleSheet("background-color: {};border: none;")'.format(self.current_page,globalStyles["primaryAscent"])) 136 | eval("self.{}page.show()".format(self.current_page)) 137 | 138 | def create_task(self): 139 | site = self.createdialog.site_box.currentText() 140 | product = self.createdialog.input_edit.text() 141 | profile = self.createdialog.profile_box.currentText() 142 | proxies = self.createdialog.proxies_box.currentText() 143 | monitor_delay = self.createdialog.monitor_edit.text() 144 | error_delay = self.createdialog.error_edit.text() 145 | max_price = self.createdialog.price_edit.text() if self.createdialog.maxprice_checkbox.isChecked() else "" 146 | if site != "Site" and product != "" and profile != "Profile" and monitor_delay != "" and error_delay != "": 147 | for i in range(self.createdialog.taskcount_spinbox.value()): 148 | self.homepage.verticalLayout.takeAt(self.homepage.verticalLayout.count() - 1) 149 | tab = TaskTab( 150 | site, 151 | product, 152 | profile, 153 | proxies, 154 | monitor_delay, 155 | error_delay, 156 | max_price, 157 | self.homepage.stop_all_tasks, 158 | self.homepage.scrollAreaWidgetContents) 159 | self.homepage.verticalLayout.addWidget(tab) 160 | spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, 161 | QtWidgets.QSizePolicy.Expanding) 162 | self.homepage.verticalLayout.addItem(spacerItem) 163 | 164 | # (.*) 165 | 166 | 167 | if __name__ == "__main__": 168 | ui_app = QtWidgets.QApplication(sys.argv) 169 | # trayIcon = QtWidgets.QSystemTrayIcon(QtGui.QIcon("images/birdbot.py"), parent=ui_app) 170 | # trayIcon.setToolTip('Phoenix Bot') 171 | # trayIcon.show() 172 | ui = MainWindow() 173 | ui.setWindowIcon(QtGui.QIcon("images/birdbot.png")) 174 | os._exit(ui_app.exec_()) 175 | -------------------------------------------------------------------------------- /data/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/data/.DS_Store -------------------------------------------------------------------------------- /data/settings_default.json: -------------------------------------------------------------------------------- 1 | { 2 | "webhook": "", 3 | "webhookonbrowser": true, 4 | "webhookonorder": true, 5 | "webhookonfailed": true, 6 | "browseronfailed": true, 7 | "runheadless": true, 8 | "bb_ac_beta": false, 9 | "onlybuyone": true, 10 | "dont_buy": true, 11 | "random_delay_start": "", 12 | "random_delay_stop": "", 13 | "bestbuy_user": "", 14 | "bestbuy_pass": "", 15 | "target_user": "", 16 | "target_pass": "", 17 | "gamestop_user": "", 18 | "gamestop_pass": "", 19 | "geckodriver":"" 20 | } 21 | -------------------------------------------------------------------------------- /encrypt.js: -------------------------------------------------------------------------------- 1 | var n = {}; 2 | n.base10 = "0123456789", n.base62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", n.luhn = function (e) { 3 | for (var t = e.length - 1, n = 0; t >= 0;) n += parseInt(e.substr(t, 1), 10), t -= 2; 4 | for (t = e.length - 2; t >= 0;) { 5 | var r = 2 * parseInt(e.substr(t, 1), 10); 6 | n += r < 10 ? r : r - 9, t -= 2 7 | } 8 | return n % 10 9 | }, n.fixluhn = function (e, t, r) { 10 | var a = n.luhn(e); 11 | return a < r ? a += 10 - r : a -= r, 0 != a ? (a = (e.length - t) % 2 != 0 ? 10 - a : a % 2 == 0 ? 5 - a / 2 : (9 - a) / 2 + 5, e.substr(0, t) + a + e.substr(t + 1)) : e 12 | }, n.distill = function (e) { 13 | for (var t = "", r = 0; r < e.length; ++r) n.base10.indexOf(e.charAt(r)) >= 0 && (t += e.substr(r, 1)); 14 | return t 15 | }, n.reformat = function (e, t) { 16 | for (var r = "", a = 0, i = 0; i < t.length; ++i) a < e.length && n.base10.indexOf(t.charAt(i)) >= 0 ? (r += e.substr(a, 1), ++a) : r += t.substr(i, 1); 17 | return r 18 | }, n.integrity = function (e, t, n) { 19 | var o = String.fromCharCode(0) + String.fromCharCode(t.length) + t + String.fromCharCode(0) + String.fromCharCode(n.length) + n, 20 | c = a.HexToWords(e); 21 | c[3] ^= 1; 22 | var u = new r.cipher.aes(c), 23 | s = i.compute(u, o); 24 | return a.WordToHex(s[0]) + a.WordToHex(s[1]) 25 | } 26 | var r = { 27 | cipher: {}, 28 | hash: {}, 29 | mode: {}, 30 | misc: {}, 31 | codec: {}, 32 | exception: { 33 | corrupt: function (e) { 34 | this.toString = function () { 35 | return "CORRUPT: " + this.message 36 | }, this.message = e 37 | }, 38 | invalid: function (e) { 39 | this.toString = function () { 40 | return "INVALID: " + this.message 41 | }, this.message = e 42 | }, 43 | bug: function (e) { 44 | this.toString = function () { 45 | return "BUG: " + this.message 46 | }, this.message = e 47 | } 48 | } 49 | }; 50 | r.cipher.aes = function (e) { 51 | this._tables[0][0][0] || this._precompute(); 52 | var t, n, a, i, o, c = this._tables[0][4], 53 | u = this._tables[1], 54 | s = e.length, 55 | d = 1; 56 | if (4 !== s && 6 !== s && 8 !== s) throw new r.exception.invalid("invalid aes key size"); 57 | for (this._key = [i = e.slice(0), o = []], t = s; t < 4 * s + 28; t++) a = i[t - 1], (t % s == 0 || 8 === s && t % s == 4) && (a = c[a >>> 24] << 24 ^ c[a >> 16 & 255] << 16 ^ c[a >> 8 & 255] << 8 ^ c[255 & a], t % s == 0 && (a = a << 8 ^ a >>> 24 ^ d << 24, d = d << 1 ^ 283 * (d >> 7))), i[t] = i[t - s] ^ a; 58 | for (n = 0; t; n++, t--) a = i[3 & n ? t : t - 4], o[n] = t <= 4 || n < 4 ? a : u[0][c[a >>> 24]] ^ u[1][c[a >> 16 & 255]] ^ u[2][c[a >> 8 & 255]] ^ u[3][c[255 & a]] 59 | }, r.cipher.aes.prototype = { 60 | encrypt: function (e) { 61 | return this._crypt(e, 0) 62 | }, 63 | decrypt: function (e) { 64 | return this._crypt(e, 1) 65 | }, 66 | _tables: [ 67 | [ 68 | [], 69 | [], 70 | [], 71 | [], 72 | [] 73 | ], 74 | [ 75 | [], 76 | [], 77 | [], 78 | [], 79 | [] 80 | ] 81 | ], 82 | _precompute: function () { 83 | var e, t, n, r, a, i, o, c, u = this._tables[0], 84 | s = this._tables[1], 85 | d = u[4], 86 | l = s[4], 87 | f = [], 88 | p = []; 89 | for (e = 0; e < 256; e++) p[(f[e] = e << 1 ^ 283 * (e >> 7)) ^ e] = e; 90 | for (t = n = 0; !d[t]; t ^= 0 == r ? 1 : r, n = 0 == p[n] ? 1 : p[n]) 91 | for (i = (i = n ^ n << 1 ^ n << 2 ^ n << 3 ^ n << 4) >> 8 ^ 255 & i ^ 99, d[t] = i, l[i] = t, c = 16843009 * f[a = f[r = f[t]]] ^ 65537 * a ^ 257 * r ^ 16843008 * t, o = 257 * f[i] ^ 16843008 * i, e = 0; e < 4; e++) u[e][t] = o = o << 24 ^ o >>> 8, s[e][i] = c = c << 24 ^ c >>> 8; 92 | for (e = 0; e < 5; e++) u[e] = u[e].slice(0), s[e] = s[e].slice(0) 93 | }, 94 | _crypt: function (e, t) { 95 | if (4 !== e.length) throw new r.exception.invalid("invalid aes block size"); 96 | var n, a, i, o, c = this._key[t], 97 | u = e[0] ^ c[0], 98 | s = e[t ? 3 : 1] ^ c[1], 99 | d = e[2] ^ c[2], 100 | l = e[t ? 1 : 3] ^ c[3], 101 | f = c.length / 4 - 2, 102 | p = 4, 103 | m = [0, 0, 0, 0], 104 | h = this._tables[t], 105 | b = h[0], 106 | v = h[1], 107 | y = h[2], 108 | E = h[3], 109 | g = h[4]; 110 | for (o = 0; o < f; o++) n = b[u >>> 24] ^ v[s >> 16 & 255] ^ y[d >> 8 & 255] ^ E[255 & l] ^ c[p], a = b[s >>> 24] ^ v[d >> 16 & 255] ^ y[l >> 8 & 255] ^ E[255 & u] ^ c[p + 1], i = b[d >>> 24] ^ v[l >> 16 & 255] ^ y[u >> 8 & 255] ^ E[255 & s] ^ c[p + 2], l = b[l >>> 24] ^ v[u >> 16 & 255] ^ y[s >> 8 & 255] ^ E[255 & d] ^ c[p + 3], p += 4, u = n, s = a, d = i; 111 | for (o = 0; o < 4; o++) m[t ? 3 & -o : o] = g[u >>> 24] << 24 ^ g[s >> 16 & 255] << 16 ^ g[d >> 8 & 255] << 8 ^ g[255 & l] ^ c[p++], n = u, u = s, s = d, d = l, l = n; 112 | return m 113 | } 114 | }; 115 | var a = { 116 | HexToKey: function (e) { 117 | return new r.cipher.aes(a.HexToWords(e)) 118 | }, 119 | HexToWords: function (e) { 120 | var t = new Array(4); 121 | if (32 != e.length) return null; 122 | for (var n = 0; n < 4; n++) t[n] = parseInt(e.substr(8 * n, 8), 16); 123 | return t 124 | }, 125 | Hex: "0123456789abcdef", 126 | WordToHex: function (e) { 127 | for (var t = 32, n = ""; t > 0;) t -= 4, n += a.Hex.substr(e >>> t & 15, 1); 128 | return n 129 | } 130 | }, 131 | i = {}; 132 | i.MSBnotZero = function (e) { 133 | return 2147483647 != (2147483647 | e) 134 | }, i.leftShift = function (e) { 135 | e[0] = (2147483647 & e[0]) << 1 | e[1] >>> 31, e[1] = (2147483647 & e[1]) << 1 | e[2] >>> 31, e[2] = (2147483647 & e[2]) << 1 | e[3] >>> 31, e[3] = (2147483647 & e[3]) << 1 136 | }, i.const_Rb = 135, i.compute = function (e, t) { 137 | var n = [0, 0, 0, 0], 138 | r = e.encrypt(n), 139 | a = r[0]; 140 | i.leftShift(r), i.MSBnotZero(a) && (r[3] ^= i.const_Rb); 141 | for (var o = 0; o < t.length;) n[o >> 2 & 3] ^= (255 & t.charCodeAt(o)) << 8 * (3 - (3 & o)), 0 == (15 & ++o) && o < t.length && (n = e.encrypt(n)); 142 | return 0 != o && 0 == (15 & o) || (a = r[0], i.leftShift(r), i.MSBnotZero(a) && (r[3] ^= i.const_Rb), n[o >> 2 & 3] ^= 128 << 8 * (3 - (3 & o))), n[0] ^= r[0], n[1] ^= r[1], n[2] ^= r[2], n[3] ^= r[3], e.encrypt(n) 143 | }; 144 | var o = { 145 | alphabet: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"], 146 | precompF: function (e, t, n, r) { 147 | var a = new Array(4), 148 | i = n.length; 149 | return a[0] = 16908544 | r >> 16 & 255, a[1] = (r >> 8 & 255) << 24 | (255 & r) << 16 | 2560 | 255 & Math.floor(t / 2), a[2] = t, a[3] = i, e.encrypt(a) 150 | }, 151 | precompb: function (e, t) { 152 | for (var n = Math.ceil(t / 2), r = 0, a = 1; n > 0;) --n, (a *= e) >= 256 && (a /= 256, ++r); 153 | return a > 1 && ++r, r 154 | }, 155 | bnMultiply: function (e, t, n) { 156 | var r, a = 0; 157 | for (r = e.length - 1; r >= 0; --r) { 158 | var i = e[r] * n + a; 159 | e[r] = i % t, a = (i - e[r]) / t 160 | } 161 | }, 162 | bnAdd: function (e, t, n) { 163 | for (var r = e.length - 1, a = n; r >= 0 && a > 0;) { 164 | var i = e[r] + a; 165 | e[r] = i % t, a = (i - e[r]) / t, --r 166 | } 167 | }, 168 | convertRadix: function (e, t, n, r, a) { 169 | var i, c = new Array(r); 170 | for (i = 0; i < r; ++i) c[i] = 0; 171 | for (var u = 0; u < t; ++u) o.bnMultiply(c, a, n), o.bnAdd(c, a, e[u]); 172 | return c 173 | }, 174 | cbcmacq: function (e, t, n, r) { 175 | for (var a = new Array(4), i = 0; i < 4; ++i) a[i] = e[i]; 176 | for (var o = 0; 4 * o < n;) { 177 | for (i = 0; i < 4; ++i) a[i] = a[i] ^ (t[4 * (o + i)] << 24 | t[4 * (o + i) + 1] << 16 | t[4 * (o + i) + 2] << 8 | t[4 * (o + i) + 3]); 178 | a = r.encrypt(a), o += 4 179 | } 180 | return a 181 | }, 182 | F: function (e, t, n, r, a, i, c, u, s) { 183 | var d = Math.ceil(s / 4) + 1, 184 | l = n.length + s + 1 & 15; 185 | l > 0 && (l = 16 - l); 186 | var f, p = new Array(n.length + l + s + 1); 187 | for (f = 0; f < n.length; f++) p[f] = n.charCodeAt(f); 188 | for (; f < l + n.length; f++) p[f] = 0; 189 | p[p.length - s - 1] = t; 190 | for (var m = o.convertRadix(r, a, u, s, 256), h = 0; h < s; h++) p[p.length - s + h] = m[h]; 191 | var b, v = o.cbcmacq(c, p, p.length, e), 192 | y = v, 193 | E = new Array(2 * d); 194 | for (f = 0; f < d; ++f) f > 0 && 0 == (3 & f) && (b = f >> 2 & 255, b |= b << 8 | b << 16 | b << 24, y = e.encrypt([v[0] ^ b, v[1] ^ b, v[2] ^ b, v[3] ^ b])), E[2 * f] = y[3 & f] >>> 16, E[2 * f + 1] = 65535 & y[3 & f]; 195 | return o.convertRadix(E, 2 * d, 65536, i, u) 196 | }, 197 | DigitToVal: function (e, t, n) { 198 | var r = new Array(t); 199 | if (256 == n) { 200 | for (var a = 0; a < t; a++) r[a] = e.charCodeAt(a); 201 | return r 202 | } 203 | for (var i = 0; i < t; i++) { 204 | var o = parseInt(e.charAt(i), n); 205 | if (NaN == o || !(o < n)) return ""; 206 | r[i] = o 207 | } 208 | return r 209 | }, 210 | ValToDigit: function (e, t) { 211 | var n, r = ""; 212 | if (256 == t) 213 | for (n = 0; n < e.length; n++) r += String.fromCharCode(e[n]); 214 | else 215 | for (n = 0; n < e.length; n++) r += o.alphabet[e[n]]; 216 | return r 217 | }, 218 | encryptWithCipher: function (e, t, n, r) { 219 | var a = e.length, 220 | i = Math.floor(a / 2), 221 | c = o.precompF(n, a, t, r), 222 | u = o.precompb(r, a), 223 | s = o.DigitToVal(e, i, r), 224 | d = o.DigitToVal(e.substr(i), a - i, r); 225 | if ("" == s || "" == d) return ""; 226 | for (var l = 0; l < 5; l++) { 227 | var f, p = o.F(n, 2 * l, t, d, d.length, s.length, c, r, u); 228 | f = 0; 229 | for (var m = s.length - 1; m >= 0; --m) { 230 | (h = s[m] + p[m] + f) < r ? (s[m] = h, f = 0) : (s[m] = h - r, f = 1) 231 | } 232 | p = o.F(n, 2 * l + 1, t, s, s.length, d.length, c, r, u); 233 | f = 0; 234 | for (m = d.length - 1; m >= 0; --m) { 235 | var h; 236 | (h = d[m] + p[m] + f) < r ? (d[m] = h, f = 0) : (d[m] = h - r, f = 1) 237 | } 238 | } 239 | return o.ValToDigit(s, r) + o.ValToDigit(d, r) 240 | }, 241 | encrypt: function (e, t, n, r) { 242 | var i = a.HexToKey(n); 243 | return null == i ? "" : o.encryptWithCipher(e, t, i, r) 244 | } 245 | } 246 | function encrypt(e,t,PIE_L,PIE_E,PIE_K,PIE_key_id,PIE_phase){ 247 | PIE = {L: parseInt(PIE_L), E: parseInt(PIE_E), K: PIE_K, key_id: PIE_key_id, phase: parseInt(PIE_phase)} 248 | var a_var = n.distill(e) 249 | var i_var = n.distill(t) 250 | var c_var = a_var.substr(0, PIE.L) + a_var.substring(a_var.length - PIE.E) 251 | var u_var = n.luhn(a_var) 252 | var s_var = a_var.substring(PIE.L + 1, a_var.length - PIE.E) 253 | var d_var = o.encrypt(s_var + i_var, c_var, PIE.K, 10) 254 | var l_var = a_var.substr(0, PIE.L) + "0" + d_var.substr(0, d_var.length - i_var.length) + a_var.substring(a_var.length - PIE.E) 255 | var f_var = n.reformat(n.fixluhn(l_var, PIE.L, u_var), e) 256 | var p_var = n.reformat(d_var.substring(d_var.length - i_var.length), t) 257 | return [f_var, p_var, n.integrity(PIE.K, f_var, p_var)] 258 | } -------------------------------------------------------------------------------- /images/birdbot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/birdbot.png -------------------------------------------------------------------------------- /images/cart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/cart.png -------------------------------------------------------------------------------- /images/down_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/down_icon.png -------------------------------------------------------------------------------- /images/downarrow_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/downarrow_icon.png -------------------------------------------------------------------------------- /images/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/edit.png -------------------------------------------------------------------------------- /images/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/home.png -------------------------------------------------------------------------------- /images/home_alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/home_alt.png -------------------------------------------------------------------------------- /images/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/play.png -------------------------------------------------------------------------------- /images/profiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/profiles.png -------------------------------------------------------------------------------- /images/profiles_alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/profiles_alt.png -------------------------------------------------------------------------------- /images/proxies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/proxies.png -------------------------------------------------------------------------------- /images/proxies_alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/proxies_alt.png -------------------------------------------------------------------------------- /images/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/settings.png -------------------------------------------------------------------------------- /images/settings_alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/settings_alt.png -------------------------------------------------------------------------------- /images/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/stop.png -------------------------------------------------------------------------------- /images/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/success.png -------------------------------------------------------------------------------- /images/tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/tasks.png -------------------------------------------------------------------------------- /images/trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/trash.png -------------------------------------------------------------------------------- /images/uparrow_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/images/uparrow_icon.png -------------------------------------------------------------------------------- /pages/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/pages/.DS_Store -------------------------------------------------------------------------------- /pages/createdialog.py: -------------------------------------------------------------------------------- 1 | from theming.styles import globalStyles 2 | from PyQt5 import QtCore, QtGui, QtWidgets 3 | import sys,platform 4 | def no_abort(a, b, c): 5 | sys.__excepthook__(a, b, c) 6 | sys.excepthook = no_abort 7 | 8 | class CreateDialog(QtWidgets.QDialog): 9 | def __init__(self,parent=None): 10 | super(CreateDialog, self).__init__(parent) 11 | self.setupUi(self) 12 | self.show() 13 | def setupUi(self, CreateDialog): 14 | self.CreateDialog = CreateDialog 15 | CreateDialog.setFixedSize(647, 164) 16 | CreateDialog.setStyleSheet("QComboBox::drop-down { border: 0px;}QComboBox::down-arrow { image: url(images/down_icon.png); width: 14px; height: 14px;}QComboBox{ padding: 1px 0px 1px 3px;}QLineEdit:focus { border: none; outline: none;} QSpinBox::up-button {subcontrol-origin: border;subcontrol-position: top right;width: 8px; border-image: url(images/uparrow_icon.png) 1;border-width: 1px;}QSpinBox::down-button {subcontrol-origin: border;subcontrol-position: bottom right;width: 8px;border-image: url(images/downarrow_icon.png) 1;border-width: 1px;border-top-width: 0;}") 17 | CreateDialog.setWindowTitle("Create Tasks") 18 | self.background = QtWidgets.QWidget(CreateDialog) 19 | self.background.setGeometry(QtCore.QRect(0, 0, 691, 391)) 20 | self.background.setStyleSheet("background-color: {};".format(globalStyles["backgroundDark"])) 21 | font = QtGui.QFont() 22 | font.setPointSize(13) if platform.system() == "Darwin" else font.setPointSize(13*.75) 23 | font.setFamily("Arial") 24 | self.site_box = QtWidgets.QComboBox(self.background) 25 | self.site_box.setGeometry(QtCore.QRect(50, 20, 151, 21)) 26 | self.site_box.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 27 | self.site_box.addItem("Site") 28 | self.site_box.setFont(font) 29 | self.input_edit = QtWidgets.QLineEdit(self.background) 30 | self.input_edit.setGeometry(QtCore.QRect(250, 20, 151, 21)) 31 | self.input_edit.setFocusPolicy(QtCore.Qt.ClickFocus) 32 | self.input_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 33 | self.input_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 34 | self.input_edit.setPlaceholderText("Link") 35 | self.input_edit.setFont(font) 36 | self.input_edit.textEdited.connect(self.autofill) 37 | self.profile_box = QtWidgets.QComboBox(self.background) 38 | self.profile_box.setGeometry(QtCore.QRect(450, 20, 151, 21)) 39 | self.profile_box.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 40 | self.profile_box.addItem("Profile") 41 | self.profile_box.setFont(font) 42 | self.proxies_box = QtWidgets.QComboBox(self.background) 43 | self.proxies_box.setGeometry(QtCore.QRect(450, 70, 151, 21)) 44 | self.proxies_box.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 45 | self.proxies_box.addItem("Proxy List") 46 | self.proxies_box.addItem("None") 47 | self.proxies_box.setFont(font) 48 | self.monitor_edit = QtWidgets.QLineEdit(self.background) 49 | self.monitor_edit.setGeometry(QtCore.QRect(50, 70, 61, 21)) 50 | self.monitor_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 51 | self.monitor_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 52 | self.monitor_edit.setPlaceholderText("Monitor") 53 | self.monitor_edit.setFont(font) 54 | self.monitor_edit.setText("5.0") 55 | self.only_float = QtGui.QDoubleValidator() 56 | self.monitor_edit.setValidator(self.only_float) 57 | self.error_edit = QtWidgets.QLineEdit(self.background) 58 | self.error_edit.setGeometry(QtCore.QRect(140, 70, 61, 21)) 59 | self.error_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 60 | self.error_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 61 | self.error_edit.setPlaceholderText("Error") 62 | self.error_edit.setFont(font) 63 | self.error_edit.setText("5.0") 64 | self.error_edit.setValidator(self.only_float) 65 | self.addtask_btn = QtWidgets.QPushButton(self.background) 66 | self.addtask_btn.setGeometry(QtCore.QRect(250, 110, 151, 32)) 67 | self.addtask_btn.setText("Add Task") 68 | self.maxprice_checkbox = QtWidgets.QCheckBox(self.background) 69 | self.maxprice_checkbox.setGeometry(QtCore.QRect(250, 70, 87, 20)) 70 | font = QtGui.QFont() 71 | font.setPointSize(13) if platform.system() == "Darwin" else font.setPointSize(13*.75) 72 | font.setFamily("Arial") 73 | self.maxprice_checkbox.setFont(font) 74 | self.maxprice_checkbox.setStyleSheet("color: #FFFFFF;") 75 | self.maxprice_checkbox.setText("Max Price") 76 | self.price_edit = QtWidgets.QLineEdit(self.background) 77 | self.price_edit.setGeometry(QtCore.QRect(350, 70, 51, 21)) 78 | self.price_edit.setFont(font) 79 | self.price_edit.setFocusPolicy(QtCore.Qt.ClickFocus) 80 | self.price_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 81 | self.price_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 82 | self.only_int = QtGui.QIntValidator() 83 | self.price_edit.setValidator(self.only_int) 84 | font = QtGui.QFont() 85 | font.setFamily("Arial") 86 | font.setPointSize(14) if platform.system() == "Darwin" else font.setPointSize(14*.75) 87 | self.addtask_btn.setFont(font) 88 | self.addtask_btn.setStyleSheet("border-radius: 10px;background-color: {};color: #FFFFFF;".format(globalStyles["primary"])) 89 | self.addtask_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 90 | self.taskcount_spinbox = QtWidgets.QSpinBox(self.background) 91 | self.taskcount_spinbox.setGeometry(QtCore.QRect(420, 115, 41, 21)) 92 | self.taskcount_spinbox.setStyleSheet("border: 1px solid {};border-width: 0 0 2px;color: #FFFFFF;".format(globalStyles["primary"])) 93 | self.taskcount_spinbox.setMinimum(1) 94 | self.taskcount_spinbox.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 95 | 96 | self.site_box.addItem("Bestbuy") 97 | self.site_box.addItem("Walmart") 98 | self.site_box.addItem("Target") 99 | self.site_box.addItem("GameStop") 100 | 101 | QtCore.QMetaObject.connectSlotsByName(CreateDialog) 102 | def autofill(self): 103 | if "bestbuy" in self.input_edit.text(): 104 | self.site_box.setCurrentIndex(self.site_box.findText("Bestbuy")) 105 | elif "walmart" in self.input_edit.text(): 106 | self.site_box.setCurrentIndex(self.site_box.findText("Walmart")) 107 | elif "target" in self.input_edit.text(): 108 | self.site_box.setCurrentIndex(self.site_box.findText("Target")) 109 | elif "gamestop" in self.input_edit.text(): 110 | self.site_box.setCurrentIndex(self.site_box.findText("GameStop")) 111 | 112 | def load_data(self, task_tab): 113 | self.site_box.setCurrentText(task_tab.site) 114 | self.input_edit.setText(task_tab.product) 115 | self.profile_box.setCurrentText(task_tab.profile) 116 | self.proxies_box.setCurrentText(task_tab.proxies) 117 | self.monitor_edit.setText(task_tab.monitor_delay) 118 | self.error_edit.setText(task_tab.error_delay) 119 | self.price_edit.setText(task_tab.max_price) 120 | self.maxprice_checkbox.setChecked(task_tab.max_price != '') 121 | self.addtask_btn.setText('Update Task') 122 | 123 | 124 | -------------------------------------------------------------------------------- /pages/pollbrowser.py: -------------------------------------------------------------------------------- 1 | from theming.styles import globalStyles 2 | from pages.createdialog import CreateDialog 3 | from PyQt5 import QtCore, QtGui, QtWidgets 4 | import sys,platform 5 | 6 | 7 | 8 | class PollBrowserDialog(QtWidgets.QDialog): 9 | def __init__(self, parent=None): 10 | super(PollBrowserDialog, self).__init__(parent) 11 | self.acceptListeners = [] 12 | self.setupUi(self) 13 | self.show() 14 | 15 | 16 | 17 | def setupUi(self, PbdObj): 18 | self.PollBrowserDialog = PbdObj 19 | PbdObj.setFixedSize(380, 200) 20 | PbdObj.setWindowTitle("Confirm Captcha") 21 | 22 | self.background = QtWidgets.QWidget(PbdObj) 23 | self.background.setGeometry(QtCore.QRect(0,0,380, 200)) 24 | 25 | 26 | self.background.setStyleSheet("* {background-color: {}} QPushButton { background-color: %s;color: #FFFFFF;}" % (globalStyles["primary"]) .format(globalStyles["backgroundDark"])) 27 | font = QtGui.QFont() 28 | font.setPointSize(16) if platform.system() == "Darwin" else font.setPointSize(16 * .75) 29 | font.setFamily("Arial") 30 | 31 | self.label = QtWidgets.QLabel(self.background) 32 | self.label.setGeometry(QtCore.QRect(10, 50, 370, 40)) 33 | self.label.setText("Solve any Captchas in the New Browser Window\nClick Below When Finished") 34 | self.label.setStyleSheet("color: rgb(234, 239, 239); text-align: center; qproperty-alignment: 'AlignCenter'") 35 | self.label.setFont(font) 36 | 37 | self.confirmButton = QtWidgets.QPushButton(self.background) 38 | self.confirmButton.setGeometry(QtCore.QRect(150, 150, 100, 25)) 39 | self.confirmButton.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 40 | self.confirmButton.setFont(font) 41 | self.confirmButton.setStyleSheet("color: rgb(234, 239, 239); background-color: {}".format(globalStyles["primary"])) 42 | self.confirmButton.setText("All Done.") 43 | 44 | 45 | self.confirmButton.clicked.connect(self.accept) 46 | 47 | 48 | QtCore.QMetaObject.connectSlotsByName(PbdObj) 49 | -------------------------------------------------------------------------------- /pages/profilespage.py: -------------------------------------------------------------------------------- 1 | from theming.styles import globalStyles 2 | from PyQt5 import QtCore, QtGui, QtWidgets 3 | from utils import return_data,write_data,get_profile,Encryption 4 | import sys,platform 5 | def no_abort(a, b, c): 6 | sys.__excepthook__(a, b, c) 7 | sys.excepthook = no_abort 8 | 9 | class ProfilesPage(QtWidgets.QWidget): 10 | 11 | def __init__(self,parent=None): 12 | super(ProfilesPage, self).__init__(parent) 13 | self.setupUi(self) 14 | 15 | def setupUi(self, profilespage): 16 | self.profilespage = profilespage 17 | self.profilespage.setAttribute(QtCore.Qt.WA_StyledBackground, True) 18 | self.profilespage.setGeometry(QtCore.QRect(60, 0, 1041, 601)) 19 | self.profilespage.setStyleSheet("QComboBox::drop-down { border: 0px;}QComboBox::down-arrow { image: url(images/down_icon.png); width: 14px; height: 14px;}QComboBox{ padding: 1px 0px 1px 3px;}QLineEdit:focus { border: none; outline: none;}") 20 | self.shipping_card = QtWidgets.QWidget(self.profilespage) 21 | self.shipping_card.setGeometry(QtCore.QRect(30, 70, 313, 501)) 22 | self.shipping_card.setStyleSheet("background-color: {};border-radius: 20px;border: 1px solid #2e2d2d;".format(globalStyles["backgroundLight"])) 23 | self.shipping_fname_edit = QtWidgets.QLineEdit(self.shipping_card) 24 | self.shipping_fname_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 25 | self.shipping_fname_edit.setGeometry(QtCore.QRect(30, 50, 113, 21)) 26 | font = QtGui.QFont() 27 | font.setPointSize(13) if platform.system() == "Darwin" else font.setPointSize(13*.75) 28 | font.setFamily("Arial") 29 | self.shipping_fname_edit.setFont(font) 30 | self.shipping_fname_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 31 | self.shipping_fname_edit.setPlaceholderText("First Name") 32 | self.shipping_header = QtWidgets.QLabel(self.shipping_card) 33 | self.shipping_header.setGeometry(QtCore.QRect(20, 10, 81, 31)) 34 | font.setPointSize(18) if platform.system() == "Darwin" else font.setPointSize(18*.75) 35 | font.setBold(False) 36 | font.setWeight(50) 37 | self.shipping_header.setFont(font) 38 | self.shipping_header.setStyleSheet("color: rgb(212, 214, 214);border: none;") 39 | self.shipping_header.setText("Shipping") 40 | self.shipping_lname_edit = QtWidgets.QLineEdit(self.shipping_card) 41 | self.shipping_lname_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 42 | self.shipping_lname_edit.setGeometry(QtCore.QRect(170, 50, 113, 21)) 43 | font = QtGui.QFont() 44 | font.setPointSize(13) if platform.system() == "Darwin" else font.setPointSize(13*.75) 45 | font.setFamily("Arial") 46 | self.shipping_lname_edit.setFont(font) 47 | self.shipping_lname_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 48 | self.shipping_lname_edit.setPlaceholderText("Last Name") 49 | self.shipping_email_edit = QtWidgets.QLineEdit(self.shipping_card) 50 | self.shipping_email_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 51 | self.shipping_email_edit.setGeometry(QtCore.QRect(30, 100, 253, 21)) 52 | self.shipping_email_edit.setFont(font) 53 | self.shipping_email_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 54 | self.shipping_email_edit.setPlaceholderText("Email Address") 55 | self.shipping_phone_edit = QtWidgets.QLineEdit(self.shipping_card) 56 | self.shipping_phone_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 57 | self.shipping_phone_edit.setGeometry(QtCore.QRect(30, 150, 253, 21)) 58 | self.shipping_phone_edit.setFont(font) 59 | self.shipping_phone_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 60 | self.shipping_phone_edit.setPlaceholderText("Phone Number") 61 | self.shipping_address1_edit = QtWidgets.QLineEdit(self.shipping_card) 62 | self.shipping_address1_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 63 | self.shipping_address1_edit.setGeometry(QtCore.QRect(30, 200, 151, 21)) 64 | self.shipping_address1_edit.setFont(font) 65 | self.shipping_address1_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 66 | self.shipping_address1_edit.setPlaceholderText("Address 1") 67 | self.shipping_address2_edit = QtWidgets.QLineEdit(self.shipping_card) 68 | self.shipping_address2_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 69 | self.shipping_address2_edit.setGeometry(QtCore.QRect(208, 200, 75, 21)) 70 | self.shipping_address2_edit.setFont(font) 71 | self.shipping_address2_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 72 | self.shipping_address2_edit.setPlaceholderText("Address 2") 73 | self.shipping_city_edit = QtWidgets.QLineEdit(self.shipping_card) 74 | self.shipping_city_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 75 | self.shipping_city_edit.setGeometry(QtCore.QRect(30, 250, 151, 21)) 76 | self.shipping_city_edit.setFont(font) 77 | self.shipping_city_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 78 | self.shipping_city_edit.setPlaceholderText("City") 79 | self.shipping_zipcode_edit = QtWidgets.QLineEdit(self.shipping_card) 80 | self.shipping_zipcode_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 81 | self.shipping_zipcode_edit.setGeometry(QtCore.QRect(208, 250, 75, 21)) 82 | self.shipping_zipcode_edit.setFont(font) 83 | self.shipping_zipcode_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 84 | self.shipping_zipcode_edit.setPlaceholderText("Zip Code") 85 | self.shipping_state_box = QtWidgets.QComboBox(self.shipping_card) 86 | self.shipping_state_box.setGeometry(QtCore.QRect(30, 300, 253, 26)) 87 | self.shipping_state_box.setFont(font) 88 | self.shipping_state_box.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 89 | self.shipping_state_box.addItem("State") 90 | self.shipping_country_box = QtWidgets.QComboBox(self.shipping_card) 91 | self.shipping_country_box.setGeometry(QtCore.QRect(30, 360, 253, 26)) 92 | self.shipping_country_box.setFont(font) 93 | self.shipping_country_box.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 94 | self.shipping_country_box.addItem("Country") 95 | self.shipping_country_box.addItem("United States") 96 | self.shipping_country_box.addItem("Canada") 97 | self.profiles_header = QtWidgets.QLabel(self.profilespage) 98 | self.profiles_header.setGeometry(QtCore.QRect(30, 10, 81, 31)) 99 | font.setPointSize(22) if platform.system() == "Darwin" else font.setPointSize(22*.75) 100 | font.setBold(False) 101 | font.setWeight(50) 102 | self.profiles_header.setFont(font) 103 | self.profiles_header.setStyleSheet("color: rgb(234, 239, 239);") 104 | self.profiles_header.setText("Profiles") 105 | self.billing_card = QtWidgets.QWidget(self.profilespage) 106 | self.billing_card.setGeometry(QtCore.QRect(365, 70, 313, 501)) 107 | self.billing_card.setStyleSheet("background-color: {};border-radius: 20px;border: 1px solid #2e2d2d;".format(globalStyles["backgroundLight"])) 108 | self.billing_fname_edit = QtWidgets.QLineEdit(self.billing_card) 109 | self.billing_fname_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 110 | self.billing_fname_edit.setGeometry(QtCore.QRect(30, 50, 113, 21)) 111 | font = QtGui.QFont() 112 | font.setPointSize(13) if platform.system() == "Darwin" else font.setPointSize(13*.75) 113 | font.setFamily("Arial") 114 | self.billing_fname_edit.setFont(font) 115 | self.billing_fname_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 116 | self.billing_fname_edit.setPlaceholderText("First Name") 117 | self.billing_header = QtWidgets.QLabel(self.billing_card) 118 | self.billing_header.setGeometry(QtCore.QRect(20, 10, 51, 31)) 119 | font.setPointSize(18) if platform.system() == "Darwin" else font.setPointSize(18*.75) 120 | font.setBold(False) 121 | font.setWeight(50) 122 | self.billing_header.setFont(font) 123 | self.billing_header.setStyleSheet("color: rgb(212, 214, 214);border: none;") 124 | self.billing_header.setText("Billing") 125 | font = QtGui.QFont() 126 | font.setPointSize(13) if platform.system() == "Darwin" else font.setPointSize(13*.75) 127 | font.setFamily("Arial") 128 | self.billing_lname_edit = QtWidgets.QLineEdit(self.billing_card) 129 | self.billing_lname_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 130 | self.billing_lname_edit.setGeometry(QtCore.QRect(170, 50, 113, 21)) 131 | self.billing_lname_edit.setFont(font) 132 | self.billing_lname_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 133 | self.billing_lname_edit.setPlaceholderText("Last Name") 134 | self.billing_email_edit = QtWidgets.QLineEdit(self.billing_card) 135 | self.billing_email_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 136 | self.billing_email_edit.setGeometry(QtCore.QRect(30, 100, 253, 21)) 137 | self.billing_email_edit.setFont(font) 138 | self.billing_email_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 139 | self.billing_email_edit.setPlaceholderText("Email Address") 140 | self.billing_phone_edit = QtWidgets.QLineEdit(self.billing_card) 141 | self.billing_phone_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 142 | self.billing_phone_edit.setGeometry(QtCore.QRect(30, 150, 253, 21)) 143 | self.billing_phone_edit.setFont(font) 144 | self.billing_phone_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 145 | self.billing_phone_edit.setPlaceholderText("Phone Number") 146 | self.billing_address1_edit = QtWidgets.QLineEdit(self.billing_card) 147 | self.billing_address1_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 148 | self.billing_address1_edit.setGeometry(QtCore.QRect(30, 200, 151, 21)) 149 | self.billing_address1_edit.setFont(font) 150 | self.billing_address1_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 151 | self.billing_address1_edit.setPlaceholderText("Address 1") 152 | self.billing_address2_edit = QtWidgets.QLineEdit(self.billing_card) 153 | self.billing_address2_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 154 | self.billing_address2_edit.setGeometry(QtCore.QRect(208, 200, 75, 21)) 155 | self.billing_address2_edit.setFont(font) 156 | self.billing_address2_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 157 | self.billing_address2_edit.setPlaceholderText("Address 2") 158 | self.billing_city_edit = QtWidgets.QLineEdit(self.billing_card) 159 | self.billing_city_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 160 | self.billing_city_edit.setGeometry(QtCore.QRect(30, 250, 151, 21)) 161 | self.billing_city_edit.setFont(font) 162 | self.billing_city_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 163 | self.billing_city_edit.setPlaceholderText("City") 164 | self.billing_zipcode_edit = QtWidgets.QLineEdit(self.billing_card) 165 | self.billing_zipcode_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 166 | self.billing_zipcode_edit.setGeometry(QtCore.QRect(208, 250, 75, 21)) 167 | self.billing_zipcode_edit.setFont(font) 168 | self.billing_zipcode_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 169 | self.billing_zipcode_edit.setPlaceholderText("Zip Code") 170 | self.billing_state_box = QtWidgets.QComboBox(self.billing_card) 171 | self.billing_state_box.setGeometry(QtCore.QRect(30, 300, 253, 26)) 172 | self.billing_state_box.setFont(font) 173 | self.billing_state_box.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 174 | self.billing_state_box.addItem("State") 175 | self.billing_country_box = QtWidgets.QComboBox(self.billing_card) 176 | self.billing_country_box.setGeometry(QtCore.QRect(30, 360, 253, 26)) 177 | self.billing_country_box.setFont(font) 178 | self.billing_country_box.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 179 | self.billing_country_box.addItem("Country") 180 | self.billing_country_box.addItem("United States") 181 | self.billing_country_box.addItem("Canada") 182 | self.same_shipping_checkbox = QtWidgets.QCheckBox(self.billing_card) 183 | self.same_shipping_checkbox.setGeometry(QtCore.QRect(160, 16, 131, 20)) 184 | self.same_shipping_checkbox.setFont(font) 185 | self.same_shipping_checkbox.setStyleSheet("border:none;color: rgb(234, 239, 239);") 186 | self.same_shipping_checkbox.setText("Same as shipping") 187 | self.same_shipping_checkbox.stateChanged.connect(self.same_shipping_checkbox_clicked) 188 | self.tasks_card_3 = QtWidgets.QWidget(self.profilespage) 189 | self.tasks_card_3.setGeometry(QtCore.QRect(700, 70, 313, 501)) 190 | self.tasks_card_3.setStyleSheet("background-color: {};border-radius: 20px;border: 1px solid #2e2d2d;".format(globalStyles["backgroundLight"])) 191 | self.payment_header = QtWidgets.QLabel(self.tasks_card_3) 192 | self.payment_header.setGeometry(QtCore.QRect(20, 10, 81, 31)) 193 | font.setPointSize(18) if platform.system() == "Darwin" else font.setPointSize(18*.75) 194 | font.setBold(False) 195 | font.setWeight(50) 196 | self.payment_header.setFont(font) 197 | self.payment_header.setStyleSheet("color: rgb(212, 214, 214);border: none;") 198 | self.payment_header.setText("Payment") 199 | self.cardnumber_edit = QtWidgets.QLineEdit(self.tasks_card_3) 200 | self.cardnumber_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 201 | self.cardnumber_edit.setGeometry(QtCore.QRect(30, 100, 151, 21)) 202 | font = QtGui.QFont() 203 | font.setFamily("Arial") 204 | self.cardnumber_edit.setFont(font) 205 | self.cardnumber_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 206 | self.cardnumber_edit.setPlaceholderText("Card Number") 207 | self.cardcvv_edit = QtWidgets.QLineEdit(self.tasks_card_3) 208 | self.cardcvv_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 209 | self.cardcvv_edit.setGeometry(QtCore.QRect(208, 100, 75, 21)) 210 | self.cardcvv_edit.setFont(font) 211 | self.cardcvv_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 212 | self.cardcvv_edit.setPlaceholderText("CVV") 213 | self.save_btn = QtWidgets.QPushButton(self.tasks_card_3) 214 | self.save_btn.setGeometry(QtCore.QRect(70, 300, 86, 32)) 215 | self.save_btn.setFont(font) 216 | self.save_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 217 | self.save_btn.setStyleSheet("color: #FFFFFF;background-color: {};border-radius: 10px;border: 1px solid #2e2d2d;".format(globalStyles["primary"])) 218 | self.save_btn.setText("Save") 219 | self.save_btn.clicked.connect(self.save_profile) 220 | self.cardtype_box = QtWidgets.QComboBox(self.tasks_card_3) 221 | self.cardtype_box.setGeometry(QtCore.QRect(30, 50, 253, 26)) 222 | self.cardtype_box.setFont(font) 223 | self.cardtype_box.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 224 | self.cardtype_box.addItem("Card Type") 225 | self.cardmonth_box = QtWidgets.QComboBox(self.tasks_card_3) 226 | self.cardmonth_box.setGeometry(QtCore.QRect(30, 150, 113, 26)) 227 | self.cardmonth_box.setFont(font) 228 | self.cardmonth_box.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 229 | self.cardmonth_box.addItem("Month") 230 | self.cardyear_box = QtWidgets.QComboBox(self.tasks_card_3) 231 | self.cardyear_box.setGeometry(QtCore.QRect(170, 150, 113, 26)) 232 | self.cardyear_box.setFont(font) 233 | self.cardyear_box.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 234 | self.cardyear_box.addItem("Year") 235 | self.profile_header = QtWidgets.QLabel(self.tasks_card_3) 236 | self.profile_header.setGeometry(QtCore.QRect(20, 220, 81, 31)) 237 | font.setPointSize(18) if platform.system() == "Darwin" else font.setPointSize(18*.75) 238 | font.setBold(False) 239 | font.setWeight(50) 240 | self.profile_header.setFont(font) 241 | self.profile_header.setStyleSheet("color: rgb(212, 214, 214);border: none;") 242 | self.profile_header.setText("Profile") 243 | self.profilename_edit = QtWidgets.QLineEdit(self.tasks_card_3) 244 | self.profilename_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 245 | self.profilename_edit.setGeometry(QtCore.QRect(30, 260, 253, 21)) 246 | font = QtGui.QFont() 247 | font.setPointSize(13) if platform.system() == "Darwin" else font.setPointSize(13*.75) 248 | font.setFamily("Arial") 249 | self.profilename_edit.setFont(font) 250 | self.profilename_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 251 | self.profilename_edit.setPlaceholderText("Profile Name") 252 | self.loadprofile_box = QtWidgets.QComboBox(self.tasks_card_3) 253 | self.loadprofile_box.setGeometry(QtCore.QRect(30, 350, 253, 26)) 254 | self.loadprofile_box.setFont(font) 255 | self.loadprofile_box.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 256 | self.loadprofile_box.addItem("Load Profile") 257 | self.loadprofile_box.currentTextChanged.connect(self.load_profile) 258 | self.delete_btn = QtWidgets.QPushButton(self.tasks_card_3) 259 | self.delete_btn.setGeometry(QtCore.QRect(167, 300, 86, 32)) 260 | self.delete_btn.setFont(font) 261 | self.delete_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 262 | self.delete_btn.setStyleSheet("color: #FFFFFF;background-color: {};border-radius: 10px;border: 1px solid #2e2d2d;".format(globalStyles["primary"])) 263 | self.delete_btn.setText("Delete") 264 | self.delete_btn.clicked.connect(self.delete_profile) 265 | self.set_data() 266 | QtCore.QMetaObject.connectSlotsByName(profilespage) 267 | 268 | def updateShippingStateBox(self, newValue): 269 | self.shipping_state_box.clear() 270 | if newValue == "United States": 271 | for state in ["AL", "AK", "AS", "AZ", "AR", "CA", "CO", "CT", "DE", "DC", "FM", "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MH", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", "OR", "PW", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VI", "VA", "WA", "WV", "WI", "WY"]: 272 | self.shipping_state_box.addItem(state) 273 | elif newValue == "Canada": 274 | for state in ["AB", "BC", "MB", "NB", "NL", "NS", "NT", "NU", "ON", "PE", "QC", "SK", "YT"]: 275 | self.shipping_state_box.addItem(state) 276 | 277 | def updateBillingStateBox(self, newValue): 278 | self.billing_state_box.clear() 279 | if newValue == "United States": 280 | for state in ["AL", "AK", "AS", "AZ", "AR", "CA", "CO", "CT", "DE", "DC", "FM", "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MH", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", "OR", "PW", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VI", "VA", "WA", "WV", "WI", "WY"]: 281 | self.billing_state_box.addItem(state) 282 | elif newValue == "Canada": 283 | for state in ["AB", "BC", "MB", "NB", "NL", "NS", "NT", "NU", "ON", "PE", "QC", "SK", "YT"]: 284 | self.billing_state_box.addItem(state) 285 | 286 | def set_data(self): 287 | self.shipping_state_box.clear() 288 | self.billing_state_box.clear() 289 | self.shipping_country_box.currentTextChanged.connect(self.updateShippingStateBox) 290 | self.billing_country_box.currentTextChanged.connect(self.updateBillingStateBox) 291 | for month in range(1,13): 292 | self.cardmonth_box.addItem(str(month)) if month>9 else self.cardmonth_box.addItem("0"+str(month)) 293 | for year in range(2020,2031): 294 | self.cardyear_box.addItem(str(year)) 295 | for card_type in ["Visa", "Mastercard", "American Express", "Discover"]: 296 | self.cardtype_box.addItem(card_type) 297 | profiles = return_data("./data/profiles.json") 298 | for profile in profiles: 299 | profile_name = profile["profile_name"] 300 | self.loadprofile_box.addItem(profile_name) 301 | self.parent().parent().createdialog.profile_box.addItem(profile_name) 302 | 303 | def same_shipping_checkbox_clicked(self): 304 | if self.same_shipping_checkbox.isChecked(): 305 | self.billing_country_box.setCurrentIndex(self.billing_country_box.findText(self.shipping_country_box.currentText())) 306 | self.billing_fname_edit.setText(self.shipping_fname_edit.text()) 307 | self.billing_lname_edit.setText(self.shipping_lname_edit.text()) 308 | self.billing_email_edit.setText(self.shipping_email_edit.text()) 309 | self.billing_phone_edit.setText(self.shipping_phone_edit.text()) 310 | self.billing_address1_edit.setText(self.shipping_address1_edit.text()) 311 | self.billing_address2_edit.setText(self.shipping_address2_edit.text()) 312 | self.billing_city_edit.setText(self.shipping_city_edit.text()) 313 | self.billing_zipcode_edit.setText(self.shipping_zipcode_edit.text()) 314 | self.billing_state_box.setCurrentIndex(self.billing_state_box.findText(self.shipping_state_box.currentText())) 315 | 316 | def load_profile(self): 317 | profile_name = self.loadprofile_box.currentText() 318 | p = get_profile(profile_name) 319 | if p is not None: 320 | self.profilename_edit.setText(p["profile_name"]) 321 | self.shipping_fname_edit.setText(p["shipping_fname"]) 322 | self.shipping_lname_edit.setText(p["shipping_lname"]) 323 | self.shipping_email_edit.setText(p["shipping_email"]) 324 | self.shipping_phone_edit.setText(p["shipping_phone"]) 325 | self.shipping_address1_edit.setText(p["shipping_a1"]) 326 | self.shipping_address2_edit.setText(p["shipping_a2"]) 327 | self.shipping_city_edit.setText(p["shipping_city"]) 328 | self.shipping_zipcode_edit.setText(p["shipping_zipcode"]) 329 | self.shipping_country_box.setCurrentIndex(self.shipping_country_box.findText(p["shipping_country"])) 330 | self.shipping_state_box.setCurrentIndex(self.shipping_state_box.findText(p["shipping_state"])) 331 | self.billing_fname_edit.setText(p["billing_fname"]) 332 | self.billing_lname_edit.setText(p["billing_lname"]) 333 | self.billing_email_edit.setText(p["billing_email"]) 334 | self.billing_phone_edit.setText(p["billing_phone"]) 335 | self.billing_address1_edit.setText(p["billing_a1"]) 336 | self.billing_address2_edit.setText(p["billing_a2"]) 337 | self.billing_city_edit.setText(p["billing_city"]) 338 | self.billing_zipcode_edit.setText(p["billing_zipcode"]) 339 | self.billing_country_box.setCurrentIndex(self.billing_country_box.findText(p["billing_country"])) 340 | self.billing_state_box.setCurrentIndex(self.billing_state_box.findText(p["billing_state"])) 341 | self.cardnumber_edit.setText(p["card_number"]) 342 | self.cardmonth_box.setCurrentIndex(self.cardmonth_box.findText(p["card_month"])) 343 | self.cardyear_box.setCurrentIndex(self.cardyear_box.findText(p["card_year"])) 344 | self.cardtype_box.setCurrentIndex(self.cardtype_box.findText(p["card_type"])) 345 | self.cardcvv_edit.setText(p["card_cvv"]) 346 | return 347 | def save_profile(self): 348 | profile_name = self.profilename_edit.text() 349 | profile_data={ 350 | "profile_name":profile_name, 351 | "shipping_fname": self.shipping_fname_edit.text(), 352 | "shipping_lname": self.shipping_lname_edit.text(), 353 | "shipping_email": self.shipping_email_edit.text(), 354 | "shipping_phone": self.shipping_phone_edit.text(), 355 | "shipping_a1": self.shipping_address1_edit.text(), 356 | "shipping_a2": self.shipping_address2_edit.text(), 357 | "shipping_city": self.shipping_city_edit.text(), 358 | "shipping_zipcode": self.shipping_zipcode_edit.text(), 359 | "shipping_state": self.shipping_state_box.currentText(), 360 | "shipping_country": self.shipping_country_box.currentText(), 361 | "billing_fname": self.billing_fname_edit.text(), 362 | "billing_lname": self.billing_lname_edit.text(), 363 | "billing_email": self.billing_email_edit.text(), 364 | "billing_phone": self.billing_phone_edit.text(), 365 | "billing_a1": self.billing_address1_edit.text(), 366 | "billing_a2": self.billing_address2_edit.text(), 367 | "billing_city": self.billing_city_edit.text(), 368 | "billing_zipcode": self.billing_zipcode_edit.text(), 369 | "billing_state": self.billing_state_box.currentText(), 370 | "billing_country": self.billing_country_box.currentText(), 371 | "card_number": (Encryption().encrypt(self.cardnumber_edit.text())).decode("utf-8"), 372 | "card_month": self.cardmonth_box.currentText(), 373 | "card_year": self.cardyear_box.currentText(), 374 | "card_type": self.cardtype_box.currentText(), 375 | "card_cvv": self.cardcvv_edit.text() 376 | } 377 | profiles = return_data("./data/profiles.json") 378 | for p in profiles: 379 | if p["profile_name"] == profile_name: 380 | profiles.remove(p) 381 | break 382 | profiles.append(profile_data) 383 | write_data("./data/profiles.json",profiles) 384 | if self.loadprofile_box.findText(profile_name) == -1: 385 | self.loadprofile_box.addItem(profile_name) 386 | self.parent().parent().createdialog.profile_box.addItem(profile_name) 387 | QtWidgets.QMessageBox.information(self, "Phoenix Bot", "Saved Profile") 388 | 389 | def delete_profile(self): 390 | profile_name = self.profilename_edit.text() 391 | profiles = return_data("./data/profiles.json") 392 | for profile in profiles: 393 | if profile["profile_name"] == profile_name: 394 | profiles.remove(profile) 395 | break 396 | write_data("./data/profiles.json",profiles) 397 | self.loadprofile_box.removeItem(self.loadprofile_box.findText(profile_name)) 398 | self.parent().parent().createdialog.profile_box.removeItem(self.parent().parent().createdialog.profile_box.findText(profile_name)) 399 | 400 | self.loadprofile_box.setCurrentIndex(0) 401 | self.profilename_edit.setText("") 402 | self.profilename_edit.setText("") 403 | self.shipping_fname_edit.setText("") 404 | self.shipping_lname_edit.setText("") 405 | self.shipping_email_edit.setText("") 406 | self.shipping_phone_edit.setText("") 407 | self.shipping_address1_edit.setText("") 408 | self.shipping_address2_edit.setText("") 409 | self.shipping_city_edit.setText("") 410 | self.shipping_zipcode_edit.setText("") 411 | self.shipping_state_box.setCurrentIndex(0) 412 | self.shipping_country_box.setCurrentIndex(0) 413 | self.billing_fname_edit.setText("") 414 | self.billing_lname_edit.setText("") 415 | self.billing_email_edit.setText("") 416 | self.billing_phone_edit.setText("") 417 | self.billing_address1_edit.setText("") 418 | self.billing_address2_edit.setText("") 419 | self.billing_city_edit.setText("") 420 | self.billing_zipcode_edit.setText("") 421 | self.billing_state_box.setCurrentIndex(0) 422 | self.billing_country_box.setCurrentIndex(0) 423 | self.cardnumber_edit.setText("") 424 | self.cardmonth_box.setCurrentIndex(0) 425 | self.cardyear_box.setCurrentIndex(0) 426 | self.cardtype_box.setCurrentIndex(0) 427 | self.cardcvv_edit.setText("") 428 | QtWidgets.QMessageBox.information(self, "Phoenix Bot", "Deleted Profile") 429 | -------------------------------------------------------------------------------- /pages/proxiespage.py: -------------------------------------------------------------------------------- 1 | from theming.styles import globalStyles 2 | from PyQt5 import QtCore, QtGui, QtWidgets 3 | from utils import return_data,write_data 4 | import sys,platform 5 | def no_abort(a, b, c): 6 | sys.__excepthook__(a, b, c) 7 | sys.excepthook = no_abort 8 | 9 | class ProxiesPage(QtWidgets.QWidget): 10 | def __init__(self,parent=None): 11 | super(ProxiesPage, self).__init__(parent) 12 | self.setupUi(self) 13 | def setupUi(self, proxiespage): 14 | self.proxiespage = proxiespage 15 | self.proxiespage.setAttribute(QtCore.Qt.WA_StyledBackground, True) 16 | self.proxiespage.setGeometry(QtCore.QRect(60, 0, 1041, 601)) 17 | self.proxiespage.setStyleSheet("QComboBox::drop-down { border: 0px;}QComboBox::down-arrow { image: url(images/down_icon.png); width: 14px; height: 14px;}QComboBox{ padding: 1px 0px 1px 3px;}QLineEdit:focus { border: none; outline: none;}") 18 | self.proxies_card = QtWidgets.QWidget(self.proxiespage) 19 | self.proxies_card.setGeometry(QtCore.QRect(30, 70, 981, 501)) 20 | font = QtGui.QFont() 21 | font.setPointSize(13) if platform.system() == "Darwin" else font.setPointSize(13*.75) 22 | font.setFamily("Arial") 23 | self.proxies_card.setFont(font) 24 | self.proxies_card.setStyleSheet("background-color: {};border-radius: 20px;border: 1px solid #2e2d2d;".format(globalStyles["backgroundLight"])) 25 | self.listname_edit = QtWidgets.QLineEdit(self.proxies_card) 26 | self.listname_edit.setGeometry(QtCore.QRect(20, 50, 161, 21)) 27 | self.listname_edit.setFont(font) 28 | self.listname_edit.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 29 | self.listname_edit.setPlaceholderText("List Name") 30 | self.listname_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 31 | self.editproxies_header = QtWidgets.QLabel(self.proxies_card) 32 | self.editproxies_header.setGeometry(QtCore.QRect(20, 10, 101, 31)) 33 | font = QtGui.QFont() 34 | font.setFamily("Arial") 35 | font.setPointSize(18) if platform.system() == "Darwin" else font.setPointSize(18*.75) 36 | font.setWeight(50) 37 | self.editproxies_header.setFont(font) 38 | self.editproxies_header.setStyleSheet("color: rgb(212, 214, 214);border: none;") 39 | self.editproxies_header.setText("Edit Proxies") 40 | self.loadlist_box = QtWidgets.QComboBox(self.proxies_card) 41 | self.loadlist_box.setGeometry(QtCore.QRect(210, 50, 161, 21)) 42 | font = QtGui.QFont() 43 | font.setPointSize(13) if platform.system() == "Darwin" else font.setPointSize(13*.75) 44 | font.setFamily("Arial") 45 | self.loadlist_box.setFont(font) 46 | self.loadlist_box.setStyleSheet("outline: 0;border: 1px solid {};border-width: 0 0 2px;color: rgb(234, 239, 239);".format(globalStyles["primary"])) 47 | self.loadlist_box.addItem("Load List") 48 | self.loadlist_box.currentTextChanged.connect(self.load_proxies) 49 | self.saveproxies_btn = QtWidgets.QPushButton(self.proxies_card) 50 | self.saveproxies_btn.setGeometry(QtCore.QRect(400, 450, 86, 32)) 51 | self.saveproxies_btn.setFont(font) 52 | self.saveproxies_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 53 | self.saveproxies_btn.setStyleSheet("color: #FFFFFF;background-color: {};border-radius: 10px;border: 1px solid #2e2d2d;".format(globalStyles["primary"])) 54 | self.saveproxies_btn.setText("Save") 55 | self.saveproxies_btn.clicked.connect(self.save_proxies) 56 | self.proxies_edit = QtWidgets.QTextEdit(self.proxies_card) 57 | self.proxies_edit.setGeometry(QtCore.QRect(20, 90, 941, 341)) 58 | self.proxies_edit.setFont(font) 59 | self.proxies_edit.setStyleSheet("color: #FFFFFF;padding: 10px;") 60 | self.proxies_edit.setPlaceholderText("ip:port or ip:port:user:pass") 61 | self.proxies_edit.setAcceptRichText(False) 62 | self.deleteproxies_btn = QtWidgets.QPushButton(self.proxies_card) 63 | self.deleteproxies_btn.setGeometry(QtCore.QRect(500, 450, 86, 32)) 64 | self.deleteproxies_btn.setFont(font) 65 | self.deleteproxies_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 66 | self.deleteproxies_btn.setStyleSheet("color: #FFFFFF;background-color: {};border-radius: 10px;border: 1px solid #2e2d2d;".format(globalStyles["primary"])) 67 | self.deleteproxies_btn.setText("Delete") 68 | self.deleteproxies_btn.clicked.connect(self.delete_proxies) 69 | self.proxies_header = QtWidgets.QLabel(self.proxiespage) 70 | self.proxies_header.setGeometry(QtCore.QRect(30, 10, 81, 31)) 71 | font = QtGui.QFont() 72 | font.setFamily("Arial") 73 | font.setPointSize(22) if platform.system() == "Darwin" else font.setPointSize(22*.75) 74 | font.setWeight(50) 75 | self.proxies_header.setFont(font) 76 | self.proxies_header.setStyleSheet("color: rgb(234, 239, 239);") 77 | self.proxies_header.setText("Proxies") 78 | self.set_data() 79 | QtCore.QMetaObject.connectSlotsByName(proxiespage) 80 | 81 | def set_data(self): 82 | proxies = return_data("./data/proxies.json") 83 | for proxies_list in proxies: 84 | list_name = proxies_list["list_name"] 85 | self.loadlist_box.addItem(list_name) 86 | self.parent().parent().createdialog.proxies_box.addItem(list_name) 87 | 88 | def load_proxies(self): 89 | list_name = self.loadlist_box.currentText() 90 | if list_name !="Load Proxies": 91 | proxies = return_data("./data/proxies.json") 92 | for proxies_list in proxies: 93 | if proxies_list["list_name"] == list_name: 94 | self.listname_edit.setText(list_name) 95 | self.proxies_edit.setText(proxies_list["proxies"]) 96 | 97 | def save_proxies(self): 98 | list_name = self.listname_edit.text() 99 | proxies = self.proxies_edit.toPlainText() 100 | if proxies != "" and list_name != "": 101 | for item in proxies.splitlines(): 102 | if ":" not in item or item == "": 103 | QtWidgets.QMessageBox.critical(self, "Phoenix Bot", "Incorrect Proxies") 104 | return 105 | proxies_data = { 106 | "list_name": list_name, 107 | "proxies": self.proxies_edit.toPlainText() 108 | } 109 | proxies = return_data("./data/proxies.json") 110 | for p in proxies: 111 | if p["list_name"] == list_name: 112 | proxies.remove(p) 113 | break 114 | proxies.append(proxies_data) 115 | write_data("./data/proxies.json",proxies) 116 | if self.loadlist_box.findText(list_name) == -1: 117 | self.loadlist_box.addItem(list_name) 118 | self.parent().parent().createdialog.proxies_box.addItem(list_name) 119 | QtWidgets.QMessageBox.information(self, "Phoenix Bot", "Saved Proxies") 120 | else: 121 | QtWidgets.QMessageBox.critical(self, "Phoenix Bot", "Missing Fields") 122 | 123 | def delete_proxies(self): 124 | list_name = self.listname_edit.text() 125 | proxies = return_data("./data/proxies.json") 126 | for p in proxies: 127 | if p["list_name"] == list_name: 128 | proxies.remove(p) 129 | break 130 | write_data("./data/proxies.json",proxies) 131 | self.loadlist_box.removeItem(self.loadlist_box.findText(list_name)) 132 | self.parent().parent().createdialog.proxies_box.removeItem(self.parent().parent().createdialog.proxies_box.findText(list_name)) 133 | self.loadlist_box.setCurrentIndex(0) 134 | self.listname_edit.setText("") 135 | self.proxies_edit.setText("") 136 | QtWidgets.QMessageBox.information(self, "Phoenix Bot", "Deleted Proxy List") -------------------------------------------------------------------------------- /pages/settingspage.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import settings 3 | import sys 4 | 5 | from PyQt5 import QtCore, QtGui, QtWidgets 6 | 7 | from theming.styles import globalStyles 8 | from utils import return_data, write_data, Encryption, data_exists, BirdLogger, validate_data 9 | 10 | 11 | def no_abort(a, b, c): 12 | sys.__excepthook__(a, b, c) 13 | 14 | 15 | sys.excepthook = no_abort 16 | logger = BirdLogger() 17 | 18 | 19 | class SettingsPage(QtWidgets.QWidget): 20 | def __init__(self, parent=None): 21 | super(SettingsPage, self).__init__(parent) 22 | self.header_font = self.create_font("Arial", 18) 23 | self.small_font = self.create_font("Arial", 13) 24 | self.setup_ui(self) 25 | 26 | def create_font(self, family, pt_size) -> QtGui.QFont: 27 | font = QtGui.QFont() 28 | font.setPointSize(pt_size) if platform.system() == "Darwin" else font.setPointSize(pt_size * .75) 29 | font.setFamily(family) 30 | font.setWeight(50) 31 | return font 32 | 33 | def create_header(self, parent, rect, font, text) -> QtWidgets.QLabel: 34 | header = QtWidgets.QLabel(self.settings_card) 35 | header.setParent(parent) 36 | header.setGeometry(rect) 37 | header.setFont(font) 38 | header.setStyleSheet("color: rgb(212, 214, 214);border: none;") 39 | header.setText(text) 40 | return header 41 | 42 | def create_checkbox(self, rect, text) -> QtWidgets.QCheckBox: 43 | checkbox = QtWidgets.QCheckBox(self.settings_card) 44 | checkbox.setGeometry(rect) 45 | checkbox.setStyleSheet("color: #FFFFFF;border: none;") 46 | checkbox.setText(text) 47 | return checkbox 48 | 49 | def create_edit(self, parent, rect, font, placeholder) -> QtWidgets.QLineEdit: 50 | edit = QtWidgets.QLineEdit(parent) 51 | edit.setGeometry(rect) 52 | edit.setStyleSheet("outline: 0;border: 1px solid #5D43FB;border-width: 0 0 2px;color: rgb(234, 239, 239);") 53 | edit.setFont(font) 54 | edit.setPlaceholderText(placeholder) 55 | edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) 56 | return edit 57 | 58 | def get_folder(self): 59 | self.geckodriver_path = QtWidgets.QFileDialog.getExistingDirectory(self, 'Select Folder') 60 | 61 | def setup_ui(self, settingspage): 62 | self.settingspage = settingspage 63 | self.geckodriver_path = '' 64 | self.settingspage.setAttribute(QtCore.Qt.WA_StyledBackground, True) 65 | self.settingspage.setGeometry(QtCore.QRect(60, 0, 1041, 601)) 66 | self.settingspage.setStyleSheet( 67 | "QComboBox::drop-down { border: 0px;}QComboBox::down-arrow { image: url(images/down_icon.png); width: 14px; height: 14px;}QComboBox{ padding: 1px 0px 1px 3px;}QLineEdit:focus { border: none; outline: none;}") 68 | self.settings_card = QtWidgets.QWidget(self.settingspage) 69 | self.settings_card.setGeometry(QtCore.QRect(30, 70, 941, 501)) 70 | self.settings_card.setFont(self.small_font) 71 | self.settings_card.setStyleSheet("background-color: #232323;border-radius: 20px;border: 1px solid #2e2d2d;") 72 | 73 | self.webhook_edit = self.create_edit(self.settings_card, QtCore.QRect(30, 50, 411, 21), self.small_font, 74 | "Webhook Link") 75 | self.webhook_header = self.create_header(self.settings_card, QtCore.QRect(20, 10, 101, 31), self.header_font, 76 | "Webhook") 77 | 78 | self.savesettings_btn = QtWidgets.QPushButton(self.settings_card) 79 | self.savesettings_btn.setGeometry(QtCore.QRect(190, 450, 86, 32)) 80 | self.savesettings_btn.setFont(self.small_font) 81 | self.savesettings_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 82 | self.savesettings_btn.setStyleSheet( 83 | "color: #FFFFFF;background-color: {};border-radius: 10px;border: 1px solid #2e2d2d;".format( 84 | globalStyles["primary"])) 85 | self.savesettings_btn.setText("Save") 86 | self.savesettings_btn.clicked.connect(self.save_settings) 87 | 88 | self.geckodriver = QtWidgets.QPushButton(self.settings_card) 89 | # self.getfolder_btn.setGeometry(QtCore.QRect(QtCore.QPoint(250,100), QtCore.QSize(10, 5))) 90 | self.geckodriver.setGeometry(QtCore.QRect(300, 450, 150, 32)) 91 | self.geckodriver.setFont(self.small_font) 92 | self.geckodriver.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 93 | self.geckodriver.setStyleSheet("color: #FFFFFF;background-color: {};border-radius: 10px;border: 1px solid #2e2d2d;".format(globalStyles["primary"])) 94 | self.geckodriver.setText("GeckoDriver Location") 95 | self.geckodriver.clicked.connect(self.get_folder) 96 | 97 | 98 | 99 | self.browser_checkbox = self.create_checkbox(QtCore.QRect(30, 90, 300, 20), "Browser Opened") 100 | self.order_checkbox = self.create_checkbox(QtCore.QRect(30, 120, 221, 20), "Order Placed") 101 | self.paymentfailed_checkbox = self.create_checkbox(QtCore.QRect(30, 150, 121, 20), "Payment Failed") 102 | 103 | self.general_header = self.create_header(self.settings_card, QtCore.QRect(20, 180, 101, 31), self.header_font, 104 | "General") 105 | 106 | self.headless_checkbox = self.create_checkbox(QtCore.QRect(30, 220, 221, 20), "Run Headless (hidden browser windows)") 107 | self.onfailed_checkbox = self.create_checkbox(QtCore.QRect(30, 240, 221, 20), "Open browser on payment failed") 108 | self.bb_ac_beta_checkbox = self.create_checkbox(QtCore.QRect(30, 260, 221, 20), "Enable Best Buy Auto Checkout (BETA)") 109 | self.buy_one_checkbox = self.create_checkbox(QtCore.QRect(30, 280, 221, 20), "Stop All after success") 110 | self.dont_buy_checkbox = self.create_checkbox(QtCore.QRect(30, 300, 400, 20), 111 | "Don't actually buy items. (Used for dev and testing)") 112 | self.random_delay_start = self.create_edit(self.settings_card, QtCore.QRect(30, 330, 235, 20), 113 | self.small_font, "Random Start Delay (Default is 10ms)") 114 | self.random_delay_stop = self.create_edit(self.settings_card, QtCore.QRect(30, 360, 235, 20), 115 | self.small_font, "Random Stop Delay (Default is 40ms)") 116 | self.proxies_header = self.create_header(self.settingspage, QtCore.QRect(30, 10, 81, 31), 117 | self.create_font("Arial", 22), "Settings") 118 | self.bestbuy_user_edit = self.create_edit(self.settings_card, QtCore.QRect(300, 330, 235, 20), 119 | self.small_font, "Bestbuy.com Username (Email)") 120 | self.bestbuy_pass_edit = self.create_edit(self.settings_card, QtCore.QRect(300, 360, 235, 20), 121 | self.small_font, "Bestbuy.com Password") 122 | self.target_user_edit = self.create_edit(self.settings_card, QtCore.QRect(30, 390, 235, 20), 123 | self.small_font, "Target.com Username (Email/Cell #)") 124 | self.target_pass_edit = self.create_edit(self.settings_card, QtCore.QRect(30, 415, 235, 20), 125 | self.small_font, "Target.com Password") 126 | self.gamestop_user_edit = self.create_edit(self.settings_card, QtCore.QRect(300, 390, 235, 20), 127 | self.small_font, "Gamestop.com Username (Email)") 128 | self.gamestop_pass_edit = self.create_edit(self.settings_card, QtCore.QRect(300, 415, 235, 20), 129 | self.small_font, "Gamestop.com Password") 130 | 131 | self.set_data() 132 | QtCore.QMetaObject.connectSlotsByName(settingspage) 133 | 134 | def set_data(self): 135 | 136 | settings_default = return_data("./data/settings_default.json") 137 | if data_exists("./data/settings.json"): 138 | settings = return_data("./data/settings.json") 139 | else: 140 | logger.alt("Set-Settings-Data", "No existing settings found to be parsed, creating new from default.") 141 | write_data("./data/settings.json", settings_default) 142 | settings = return_data("./data/settings.json") 143 | 144 | if not validate_data(settings, settings_default): 145 | logger.error("Set-Settings-Data", "Parsed settings data is malformed! " 146 | "This will most likely cause a fatal exception. " 147 | "Try removing existing settings.json") 148 | 149 | self.webhook_edit.setText(settings["webhook"]) 150 | if settings["webhookonbrowser"]: 151 | self.browser_checkbox.setChecked(True) 152 | if settings["webhookonorder"]: 153 | self.order_checkbox.setChecked(True) 154 | if settings["webhookonfailed"]: 155 | self.paymentfailed_checkbox.setChecked(True) 156 | if settings["browseronfailed"]: 157 | self.onfailed_checkbox.setChecked(True) 158 | if settings["runheadless"]: 159 | self.headless_checkbox.setChecked(True) 160 | if settings["bb_ac_beta"]: 161 | self.bb_ac_beta_checkbox.setChecked(True) 162 | if settings['onlybuyone']: 163 | self.buy_one_checkbox.setChecked(True) 164 | if settings['dont_buy']: 165 | self.dont_buy_checkbox.setChecked(True) 166 | if settings['random_delay_start']: 167 | self.random_delay_start.setText(settings["random_delay_start"]) 168 | if settings['random_delay_stop']: 169 | self.random_delay_stop.setText(settings["random_delay_stop"]) 170 | 171 | self.geckodriver_path = settings["geckodriver"] 172 | 173 | # try: 174 | # self.geckodriver.setText(settings["geckodriver"]) 175 | # except: 176 | # self.geckodriver.setText("") 177 | 178 | try: 179 | self.bestbuy_user_edit.setText(settings["bestbuy_user"]) 180 | except: 181 | self.bestbuy_user_edit.setText("") 182 | 183 | try: 184 | self.bestbuy_pass_edit.setText( 185 | (Encryption().decrypt(settings["bestbuy_pass"].encode("utf-8"))).decode("utf-8")) 186 | except: 187 | self.bestbuy_pass_edit.setText("") 188 | 189 | try: 190 | self.target_user_edit.setText(settings["target_user"]) 191 | except: 192 | self.target_user_edit.setText("") 193 | 194 | try: 195 | self.target_pass_edit.setText( 196 | (Encryption().decrypt(settings["target_pass"].encode("utf-8"))).decode("utf-8")) 197 | except: 198 | self.target_pass_edit.setText("") 199 | 200 | try: 201 | self.gamestop_user_edit.setText(settings["gamestop_user"]) 202 | except: 203 | self.gamestop_user_edit.setText("") 204 | 205 | try: 206 | self.gamestop_pass_edit.setText( 207 | (Encryption().decrypt(settings["gamestop_pass"].encode("utf-8"))).decode("utf-8")) 208 | except: 209 | self.gamestop_pass_edit.setText("") 210 | 211 | self.update_settings(settings) 212 | 213 | def save_settings(self): 214 | print(f"Saving path: {self.geckodriver_path}") 215 | settings = {"webhook": self.webhook_edit.text(), 216 | "webhookonbrowser": self.browser_checkbox.isChecked(), 217 | "webhookonorder": self.order_checkbox.isChecked(), 218 | "webhookonfailed": self.paymentfailed_checkbox.isChecked(), 219 | "browseronfailed": self.onfailed_checkbox.isChecked(), 220 | "runheadless": self.headless_checkbox.isChecked(), 221 | "bb_ac_beta": self.bb_ac_beta_checkbox.isChecked(), 222 | "onlybuyone": self.buy_one_checkbox.isChecked(), 223 | "dont_buy": self.dont_buy_checkbox.isChecked(), 224 | "random_delay_start": self.random_delay_start.text(), 225 | "random_delay_stop": self.random_delay_stop.text(), 226 | "bestbuy_user": self.bestbuy_user_edit.text(), 227 | "bestbuy_pass": Encryption().encrypt(self.bestbuy_pass_edit.text()).decode("utf-8"), 228 | "target_user": self.target_user_edit.text(), 229 | "target_pass": Encryption().encrypt(self.target_pass_edit.text()).decode("utf-8"), 230 | "gamestop_user": self.gamestop_user_edit.text(), 231 | "gamestop_pass": Encryption().encrypt(self.gamestop_pass_edit.text()).decode("utf-8"), 232 | "geckodriver" : self.geckodriver_path 233 | } 234 | 235 | write_data("./data/settings.json", settings) 236 | self.update_settings(settings) 237 | QtWidgets.QMessageBox.information(self, "Phoenix Bot", "Saved Settings") 238 | 239 | def update_settings(self, settings_data): 240 | 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 241 | 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'] 242 | 243 | if settings_data.get("random_delay_start", "") != "": 244 | settings.random_delay_start = settings_data["random_delay_start"] 245 | if settings_data.get("random_delay_stop", "") != "": 246 | settings.random_delay_stop = settings_data["random_delay_stop"] 247 | if settings_data.get("bestbuy_user", "") != "": 248 | settings.bestbuy_user = settings_data["bestbuy_user"] 249 | if settings_data.get("bestbuy_pass", "") != "": 250 | settings.bestbuy_pass = (Encryption().decrypt(settings_data["bestbuy_pass"].encode("utf-8"))).decode( 251 | "utf-8") 252 | if settings_data.get("target_user", "") != "": 253 | settings.target_user = settings_data["target_user"] 254 | if settings_data.get("target_pass", "") != "": 255 | settings.target_pass = (Encryption().decrypt(settings_data["target_pass"].encode("utf-8"))).decode("utf-8") 256 | if settings_data.get("gamestop_user", "") != "": 257 | settings.gamestop_user = settings_data["gamestop_user"] 258 | if settings_data.get("gamestop_pass", "") != "": 259 | settings.gamestop_pass = (Encryption().decrypt(settings_data["gamestop_pass"].encode("utf-8"))).decode( 260 | "utf-8") 261 | if settings_data.get("geckodriver","") != "": 262 | settings.geckodriver_path = settings_data["geckodriver"] 263 | -------------------------------------------------------------------------------- /requirements.in: -------------------------------------------------------------------------------- 1 | pyqt5==5.15.4 2 | js2py==0.70 3 | lxml==4.6.5 4 | darkdetect==0.2.0 5 | colorama==0.4.4 6 | selenium==3.141.0 7 | chromedriver-py==92.0.4515.43 8 | pycryptodome 9 | webdriver_manager==3.3.0 10 | pip-tools==5.5.0 11 | requests==2.25.1 12 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile 3 | # To update, run: 4 | # 5 | # pip-compile --generate-hashes --no-index --output-file=requirements.txt requirements.in 6 | # 7 | certifi==2020.11.8 \ 8 | --hash=sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd \ 9 | --hash=sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4 10 | # via requests 11 | chardet==3.0.4 \ 12 | --hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae \ 13 | --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691 14 | # via requests 15 | chromedriver-py==92.0.4515.43 \ 16 | --hash=sha256:0121d9a299d1eb9f858934f1f7253a18eb1bbd8282f1b0a2f44df4188a916470 \ 17 | --hash=sha256:8ee55bc0675ee9720b40dadbca9d1e0cef4fa15b0185a79a77f65a35d3462c65 18 | # via -r requirements.in 19 | click==7.1.2 \ 20 | --hash=sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a \ 21 | --hash=sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc 22 | # via pip-tools 23 | colorama==0.4.4 \ 24 | --hash=sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b \ 25 | --hash=sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2 26 | # via 27 | # -r requirements.in 28 | # crayons 29 | configparser==5.0.1 \ 30 | --hash=sha256:005c3b102c96f4be9b8f40dafbd4997db003d07d1caa19f37808be8031475f2a \ 31 | --hash=sha256:08e8a59ef1817ac4ed810bb8e17d049566dd6e024e7566f6285c756db2bb4ff8 32 | # via webdriver-manager 33 | crayons==0.4.0 \ 34 | --hash=sha256:bd33b7547800f2cfbd26b38431f9e64b487a7de74a947b0fafc89b45a601813f \ 35 | --hash=sha256:e73ad105c78935d71fe454dd4b85c5c437ba199294e7ffd3341842bc683654b1 36 | # via webdriver-manager 37 | darkdetect==0.2.0 \ 38 | --hash=sha256:3b0e90b6e4e2d8e10bf45a87de110a7a23faa9ca04e41721eee31a271a152ecf \ 39 | --hash=sha256:dd9f04d39a9f6b45456af579cc688ee4a3e9ebadd6db9022712549244584d837 40 | # via -r requirements.in 41 | idna==2.10 \ 42 | --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \ 43 | --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0 44 | # via requests 45 | js2py==0.70 \ 46 | --hash=sha256:7568b33f6bd15ee8ab1f3655928ed05481a6236ad2acca4598703ae501571661 \ 47 | --hash=sha256:f78e76c88942585973129a0a10e7054b5ea1602d761d4c0ccfa0593bc819f0bb 48 | # via -r requirements.in 49 | lxml==4.6.5 \ 50 | --hash=sha256:11ae552a78612620afd15625be9f1b82e3cc2e634f90d6b11709b10a100cba59 \ 51 | --hash=sha256:121fc6f71c692b49af6c963b84ab7084402624ffbe605287da362f8af0668ea3 \ 52 | --hash=sha256:124f09614f999551ac65e5b9875981ce4b66ac4b8e2ba9284572f741935df3d9 \ 53 | --hash=sha256:12ae2339d32a2b15010972e1e2467345b7bf962e155671239fba74c229564b7f \ 54 | --hash=sha256:12d8d6fe3ddef629ac1349fa89a638b296a34b6529573f5055d1cb4e5245f73b \ 55 | --hash=sha256:1a2a7659b8eb93c6daee350a0d844994d49245a0f6c05c747f619386fb90ba04 \ 56 | --hash=sha256:1ccbfe5d17835db906f2bab6f15b34194db1a5b07929cba3cf45a96dbfbfefc0 \ 57 | --hash=sha256:2f77556266a8fe5428b8759fbfc4bd70be1d1d9c9b25d2a414f6a0c0b0f09120 \ 58 | --hash=sha256:3534d7c468c044f6aef3c0aff541db2826986a29ea73f2ca831f5d5284d9b570 \ 59 | --hash=sha256:3884476a90d415be79adfa4e0e393048630d0d5bcd5757c4c07d8b4b00a1096b \ 60 | --hash=sha256:3b95fb7e6f9c2f53db88f4642231fc2b8907d854e614710996a96f1f32018d5c \ 61 | --hash=sha256:46515773570a33eae13e451c8fcf440222ef24bd3b26f40774dd0bd8b6db15b2 \ 62 | --hash=sha256:46f21f2600d001af10e847df9eb3b832e8a439f696c04891bcb8a8cedd859af9 \ 63 | --hash=sha256:473701599665d874919d05bb33b56180447b3a9da8d52d6d9799f381ce23f95c \ 64 | --hash=sha256:4b9390bf973e3907d967b75be199cf1978ca8443183cf1e78ad80ad8be9cf242 \ 65 | --hash=sha256:4f415624cf8b065796649a5e4621773dc5c9ea574a944c76a7f8a6d3d2906b41 \ 66 | --hash=sha256:534032a5ceb34bba1da193b7d386ac575127cc39338379f39a164b10d97ade89 \ 67 | --hash=sha256:558485218ee06458643b929765ac1eb04519ca3d1e2dcc288517de864c747c33 \ 68 | --hash=sha256:57cf05466917e08f90e323f025b96f493f92c0344694f5702579ab4b7e2eb10d \ 69 | --hash=sha256:59d77bfa3bea13caee95bc0d3f1c518b15049b97dd61ea8b3d71ce677a67f808 \ 70 | --hash=sha256:5d5254c815c186744c8f922e2ce861a2bdeabc06520b4b30b2f7d9767791ce6e \ 71 | --hash=sha256:5ea121cb66d7e5cb396b4c3ca90471252b94e01809805cfe3e4e44be2db3a99c \ 72 | --hash=sha256:60aeb14ff9022d2687ef98ce55f6342944c40d00916452bb90899a191802137a \ 73 | --hash=sha256:642eb4cabd997c9b949a994f9643cd8ae00cf4ca8c5cd9c273962296fadf1c44 \ 74 | --hash=sha256:6548fc551de15f310dd0564751d9dc3d405278d45ea9b2b369ed1eccf142e1f5 \ 75 | --hash=sha256:68a851176c931e2b3de6214347b767451243eeed3bea34c172127bbb5bf6c210 \ 76 | --hash=sha256:6e84edecc3a82f90d44ddee2ee2a2630d4994b8471816e226d2b771cda7ac4ca \ 77 | --hash=sha256:73e8614258404b2689a26cb5d002512b8bc4dfa18aca86382f68f959aee9b0c8 \ 78 | --hash=sha256:7679bb6e4d9a3978a46ab19a3560e8d2b7265ef3c88152e7fdc130d649789887 \ 79 | --hash=sha256:76b6c296e4f7a1a8a128aec42d128646897f9ae9a700ef6839cdc9b3900db9b5 \ 80 | --hash=sha256:7f00cc64b49d2ef19ddae898a3def9dd8fda9c3d27c8a174c2889ee757918e71 \ 81 | --hash=sha256:8021eeff7fabde21b9858ed058a8250ad230cede91764d598c2466b0ba70db8b \ 82 | --hash=sha256:87f8f7df70b90fbe7b49969f07b347e3f978f8bd1046bb8ecae659921869202b \ 83 | --hash=sha256:916d457ad84e05b7db52700bad0a15c56e0c3000dcaf1263b2fb7a56fe148996 \ 84 | --hash=sha256:925174cafb0f1179a7fd38da90302555d7445e34c9ece68019e53c946be7f542 \ 85 | --hash=sha256:9801bcd52ac9c795a7d81ea67471a42cffe532e46cfb750cd5713befc5c019c0 \ 86 | --hash=sha256:99cf827f5a783038eb313beee6533dddb8bdb086d7269c5c144c1c952d142ace \ 87 | --hash=sha256:a21b78af7e2e13bec6bea12fc33bc05730197674f3e5402ce214d07026ccfebd \ 88 | --hash=sha256:a52e8f317336a44836475e9c802f51c2dc38d612eaa76532cb1d17690338b63b \ 89 | --hash=sha256:a702005e447d712375433ed0499cb6e1503fadd6c96a47f51d707b4d37b76d3c \ 90 | --hash=sha256:a708c291900c40a7ecf23f1d2384ed0bc0604e24094dd13417c7e7f8f7a50d93 \ 91 | --hash=sha256:a7790a273225b0c46e5f859c1327f0f659896cc72eaa537d23aa3ad9ff2a1cc1 \ 92 | --hash=sha256:abcf7daa5ebcc89328326254f6dd6d566adb483d4d00178892afd386ab389de2 \ 93 | --hash=sha256:add017c5bd6b9ec3a5f09248396b6ee2ce61c5621f087eb2269c813cd8813808 \ 94 | --hash=sha256:af4139172ff0263d269abdcc641e944c9de4b5d660894a3ec7e9f9db63b56ac9 \ 95 | --hash=sha256:b4015baed99d046c760f09a4c59d234d8f398a454380c3cf0b859aba97136090 \ 96 | --hash=sha256:ba0006799f21d83c3717fe20e2707a10bbc296475155aadf4f5850f6659b96b9 \ 97 | --hash=sha256:bdb98f4c9e8a1735efddfaa995b0c96559792da15d56b76428bdfc29f77c4cdb \ 98 | --hash=sha256:c34234a1bc9e466c104372af74d11a9f98338a3f72fae22b80485171a64e0144 \ 99 | --hash=sha256:c580c2a61d8297a6e47f4d01f066517dbb019be98032880d19ece7f337a9401d \ 100 | --hash=sha256:ca9a40497f7e97a2a961c04fa8a6f23d790b0521350a8b455759d786b0bcb203 \ 101 | --hash=sha256:cab343b265e38d4e00649cbbad9278b734c5715f9bcbb72c85a1f99b1a58e19a \ 102 | --hash=sha256:ce52aad32ec6e46d1a91ff8b8014a91538800dd533914bfc4a82f5018d971408 \ 103 | --hash=sha256:da07c7e7fc9a3f40446b78c54dbba8bfd5c9100dfecb21b65bfe3f57844f5e71 \ 104 | --hash=sha256:dc8a0dbb2a10ae8bb609584f5c504789f0f3d0d81840da4849102ec84289f952 \ 105 | --hash=sha256:e5b4b0d9440046ead3bd425eb2b852499241ee0cef1ae151038e4f87ede888c4 \ 106 | --hash=sha256:f33d8efb42e4fc2b31b3b4527940b25cdebb3026fb56a80c1c1c11a4271d2352 \ 107 | --hash=sha256:f6befb83bca720b71d6bd6326a3b26e9496ae6649e26585de024890fe50f49b8 \ 108 | --hash=sha256:fcc849b28f584ed1dbf277291ded5c32bb3476a37032df4a1d523b55faa5f944 \ 109 | --hash=sha256:ff44de36772b05c2eb74f2b4b6d1ae29b8f41ed5506310ce1258d44826ee38c1 110 | # via -r requirements.in 111 | pip-tools==5.5.0 \ 112 | --hash=sha256:10841c1e56c234d610d0466447685b9ea4ee4a2c274f858c0ef3c33d9bd0d985 \ 113 | --hash=sha256:cb0108391366b3ef336185097b3c2c0f3fa115b15098dafbda5e78aef70ea114 114 | # via -r requirements.in 115 | pycryptodome==3.9.9 \ 116 | --hash=sha256:19cb674df6c74a14b8b408aa30ba8a89bd1c01e23505100fb45f930fbf0ed0d9 \ 117 | --hash=sha256:1cfdb92dca388e27e732caa72a1cc624520fe93752a665c3b6cd8f1a91b34916 \ 118 | --hash=sha256:27397aee992af69d07502126561d851ba3845aa808f0e55c71ad0efa264dd7d4 \ 119 | --hash=sha256:28f75e58d02019a7edc7d4135203d2501dfc47256d175c72c9798f9a129a49a7 \ 120 | --hash=sha256:2a68df525b387201a43b27b879ce8c08948a430e883a756d6c9e3acdaa7d7bd8 \ 121 | --hash=sha256:411745c6dce4eff918906eebcde78771d44795d747e194462abb120d2e537cd9 \ 122 | --hash=sha256:46e96aeb8a9ca8b1edf9b1fd0af4bf6afcf3f1ca7fa35529f5d60b98f3e4e959 \ 123 | --hash=sha256:4ed27951b0a17afd287299e2206a339b5b6d12de9321e1a1575261ef9c4a851b \ 124 | --hash=sha256:50826b49fbca348a61529693b0031cdb782c39060fb9dca5ac5dff858159dc5a \ 125 | --hash=sha256:5598dc6c9dbfe882904e54584322893eff185b98960bbe2cdaaa20e8a437b6e5 \ 126 | --hash=sha256:5c3c4865730dfb0263f822b966d6d58429d8b1e560d1ddae37685fd9e7c63161 \ 127 | --hash=sha256:5f19e6ef750f677d924d9c7141f54bade3cd56695bbfd8a9ef15d0378557dfe4 \ 128 | --hash=sha256:60febcf5baf70c566d9d9351c47fbd8321da9a4edf2eff45c4c31c86164ca794 \ 129 | --hash=sha256:62c488a21c253dadc9f731a32f0ac61e4e436d81a1ea6f7d1d9146ed4d20d6bd \ 130 | --hash=sha256:6d3baaf82681cfb1a842f1c8f77beac791ceedd99af911e4f5fabec32bae2259 \ 131 | --hash=sha256:6e4227849e4231a3f5b35ea5bdedf9a82b3883500e5624f00a19156e9a9ef861 \ 132 | --hash=sha256:6e89bb3826e6f84501e8e3b205c22595d0c5492c2f271cbb9ee1c48eb1866645 \ 133 | --hash=sha256:70d807d11d508433daf96244ec1c64e55039e8a35931fc5ea9eee94dbe3cb6b5 \ 134 | --hash=sha256:76b1a34d74bb2c91bce460cdc74d1347592045627a955e9a252554481c17c52f \ 135 | --hash=sha256:7798e73225a699651888489fbb1dbc565e03a509942a8ce6194bbe6fb582a41f \ 136 | --hash=sha256:834b790bbb6bd18956f625af4004d9c15eed12d5186d8e57851454ae76d52215 \ 137 | --hash=sha256:843e5f10ecdf9d307032b8b91afe9da1d6ed5bb89d0bbec5c8dcb4ba44008e11 \ 138 | --hash=sha256:8f9f84059039b672a5a705b3c5aa21747867bacc30a72e28bf0d147cc8ef85ed \ 139 | --hash=sha256:9000877383e2189dafd1b2fc68c6c726eca9a3cfb6d68148fbb72ccf651959b6 \ 140 | --hash=sha256:910e202a557e1131b1c1b3f17a63914d57aac55cf9fb9b51644962841c3995c4 \ 141 | --hash=sha256:946399d15eccebafc8ce0257fc4caffe383c75e6b0633509bd011e357368306c \ 142 | --hash=sha256:a199e9ca46fc6e999e5f47fce342af4b56c7de85fae893c69ab6aa17531fb1e1 \ 143 | --hash=sha256:a3d8a9efa213be8232c59cdc6b65600276508e375e0a119d710826248fd18d37 \ 144 | --hash=sha256:a4599c0ca0fc027c780c1c45ed996d5bef03e571470b7b1c7171ec1e1a90914c \ 145 | --hash=sha256:b4e6b269a8ddaede774e5c3adbef6bf452ee144e6db8a716d23694953348cd86 \ 146 | --hash=sha256:b68794fba45bdb367eeb71249c26d23e61167510a1d0c3d6cf0f2f14636e62ee \ 147 | --hash=sha256:d7ec2bd8f57c559dd24e71891c51c25266a8deb66fc5f02cc97c7fb593d1780a \ 148 | --hash=sha256:e15bde67ccb7d4417f627dd16ffe2f5a4c2941ce5278444e884cb26d73ecbc61 \ 149 | --hash=sha256:eb01f9997e4d6a8ec8a1ad1f676ba5a362781ff64e8189fe2985258ba9cb9706 \ 150 | --hash=sha256:faa682c404c218e8788c3126c9a4b8fbcc54dc245b5b6e8ea5b46f3b63bd0c84 151 | # via -r requirements.in 152 | pyjsparser==2.7.1 \ 153 | --hash=sha256:2b12842df98d83f65934e0772fa4a5d8b123b3bc79f1af1789172ac70265dd21 \ 154 | --hash=sha256:be60da6b778cc5a5296a69d8e7d614f1f870faf94e1b1b6ac591f2ad5d729579 155 | # via js2py 156 | pyqt5==5.15.4 \ 157 | --hash=sha256:213bebd51821ed89b4d5b35bb10dbe67564228b3568f463a351a08e8b1677025 \ 158 | --hash=sha256:2a69597e0dd11caabe75fae133feca66387819fc9bc050f547e5551bce97e5be \ 159 | --hash=sha256:883a549382fc22d29a0568f3ef20b38c8e7ab633a59498ac4eb63a3bf36d3fd3 \ 160 | --hash=sha256:8c0848ba790a895801d5bfd171da31cad3e551dbcc4e59677a3b622de2ceca98 \ 161 | --hash=sha256:a88526a271e846e44779bb9ad7a738c6d3c4a9d01e15a128ecfc6dd4696393b7 162 | # via -r requirements.in 163 | pyqt5-qt5==5.15.2 \ 164 | --hash=sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a \ 165 | --hash=sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962 \ 166 | --hash=sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154 \ 167 | --hash=sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327 168 | # via pyqt5 169 | pyqt5-sip==12.8.1 \ 170 | --hash=sha256:0304ca9114b9817a270f67f421355075b78ff9fc25ac58ffd72c2601109d2194 \ 171 | --hash=sha256:0cd969be528c27bbd4755bd323dff4a79a8fdda28215364e6ce3e069cb56c2a9 \ 172 | --hash=sha256:2f35e82fd7ec1e1f6716e9154721c7594956a4f5bd4f826d8c6a6453833cc2f0 \ 173 | --hash=sha256:30e944db9abee9cc757aea16906d4198129558533eb7fadbe48c5da2bd18e0bd \ 174 | --hash=sha256:34dcd29be47553d5f016ff86e89e24cbc5eebae92eb2f96fb32d2d7ba028c43c \ 175 | --hash=sha256:5a011aeff89660622a6d5c3388d55a9d76932f3b82c95e82fc31abd8b1d2990d \ 176 | --hash=sha256:6c1ebee60f1d2b3c70aff866b7933d8d8d7646011f7c32f9321ee88c290aa4f9 \ 177 | --hash=sha256:7b81382ce188d63890a0e35abe0f9bb946cabc873a31873b73583b0fc84ac115 \ 178 | --hash=sha256:832fd60a264de4134c2824d393320838f3ab648180c9c357ec58a74524d24507 \ 179 | --hash=sha256:84ba7746762bd223bed22428e8561aa267a229c28344c2d28c5d5d3f8970cffb \ 180 | --hash=sha256:9312ec47cac4e33c11503bc1cbeeb0bdae619620472f38e2078c5a51020a930f \ 181 | --hash=sha256:a1b8ef013086e224b8e86c93f880f776d01b59195bdfa2a8e0b23f0480678fec \ 182 | --hash=sha256:a29e2ac399429d3b7738f73e9081e50783e61ac5d29344e0802d0dcd6056c5a2 \ 183 | --hash=sha256:b6d42250baec52a5f77de64e2951d001c5501c3a2df2179f625b241cbaec3369 \ 184 | --hash=sha256:bb5a87b66fc1445915104ee97f7a20a69decb42f52803e3b0795fa17ff88226c \ 185 | --hash=sha256:c317ab1263e6417c498b81f5c970a9b1af7acefab1f80b4cc0f2f8e661f29fc5 \ 186 | --hash=sha256:c9800729badcb247765e4ffe2241549d02da1fa435b9db224845bc37c3e99cb0 \ 187 | --hash=sha256:c9d6d448c29dc6606bb7974696608f81f4316c8234f7c7216396ed110075e777 \ 188 | --hash=sha256:da9c9f1e65b9d09e73bd75befc82961b6b61b5a3b9d0a7c832168e1415f163c6 \ 189 | --hash=sha256:ed897c58acf4a3cdca61469daa31fe6e44c33c6c06a37c3f21fab31780b3b86a \ 190 | --hash=sha256:f168f0a7f32b81bfeffdf003c36f25d81c97dee5eb67072a5183e761fe250f13 191 | # via pyqt5 192 | pytz==2020.4 \ 193 | --hash=sha256:3e6b7dd2d1e0a59084bcee14a17af60c5c562cdc16d828e8eba2e683d3a7e268 \ 194 | --hash=sha256:5c55e189b682d420be27c6995ba6edce0c0a77dd67bfbe2ae6607134d5851ffd 195 | # via tzlocal 196 | requests==2.25.1 \ 197 | --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \ 198 | --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e 199 | # via 200 | # -r requirements.in 201 | # webdriver-manager 202 | selenium==3.141.0 \ 203 | --hash=sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c \ 204 | --hash=sha256:deaf32b60ad91a4611b98d8002757f29e6f2c2d5fcaf202e1c9ad06d6772300d 205 | # via -r requirements.in 206 | six==1.15.0 \ 207 | --hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259 \ 208 | --hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced 209 | # via js2py 210 | tzlocal==2.1 \ 211 | --hash=sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44 \ 212 | --hash=sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4 213 | # via js2py 214 | urllib3==1.26.5 \ 215 | --hash=sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c \ 216 | --hash=sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098 217 | # via 218 | # requests 219 | # selenium 220 | webdriver_manager==3.3.0 \ 221 | --hash=sha256:22ae0ace4da117472302c21ead8ec05eb5c1312eeeba97547395d79f83afb241 \ 222 | --hash=sha256:6b54b18f75379f97f9e49f504a8e79010d47af1da8e7b5d7d7a103ca535b1e46 223 | # via -r requirements.in 224 | 225 | # WARNING: The following packages were not pinned, but pip requires them to be 226 | # pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag. 227 | # pip 228 | -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | ####### Global Variables ###### 2 | global webhook 3 | webhook = "" 4 | 5 | global webhook_on_browser 6 | webhook_on_browser = True 7 | 8 | global webhook_on_order 9 | webhook_on_order = True 10 | 11 | global webhook_on_failed 12 | webhook_on_failed = True 13 | 14 | global browser_on_failed 15 | browser_on_failed = True 16 | 17 | global run_headless 18 | run_headless = True 19 | 20 | global bb_ac_beta 21 | bb_ac_beta = False 22 | 23 | global dont_buy 24 | dont_buy = False 25 | 26 | # TODO: Rename this to match proper format 27 | global userAgent 28 | userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36" 29 | 30 | global random_delay_start 31 | random_delay_start = 10 32 | 33 | global random_delay_stop 34 | random_delay_stop = 40 35 | 36 | global bestbuy_user 37 | bestbuy_user = "" 38 | 39 | global bestbuy_pass 40 | bestbuy_pass = "" 41 | 42 | global target_user 43 | target_user = "" 44 | 45 | global target_pass 46 | target_pass = "" 47 | 48 | global gamestop_user 49 | gamestop_user = "" 50 | 51 | global gamestop_pass 52 | gamestop_pass = "" 53 | 54 | global geckodriver_path 55 | geckodriver_path = "" 56 | ################################################# 57 | -------------------------------------------------------------------------------- /sites/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/sites/.DS_Store -------------------------------------------------------------------------------- /sites/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Strip3s/PhoenixBot/45030b08f460d03b1cb65019d56b3c2593bc52f3/sites/__init__.py -------------------------------------------------------------------------------- /sites/bestbuy.py: -------------------------------------------------------------------------------- 1 | import json, settings, webbrowser, urllib3, requests, time 2 | from time import sleep 3 | from selenium.webdriver.support import expected_conditions as EC 4 | from selenium.webdriver.common.by import By 5 | from urllib import parse 6 | from chromedriver_py import binary_path # this will get you the path variable 7 | from selenium import webdriver 8 | from selenium.webdriver.chrome.options import Options 9 | from selenium.webdriver.support.ui import WebDriverWait 10 | from requests.adapters import HTTPAdapter 11 | from requests.packages.urllib3.util.retry import Retry 12 | from webdriver_manager.chrome import ChromeDriverManager 13 | from selenium.common.exceptions import NoSuchElementException, TimeoutException, ElementNotInteractableException, ElementClickInterceptedException 14 | 15 | from utils.json_utils import find_values 16 | from utils.selenium_utils import enable_headless # not sure this actually works since we call options() below 17 | from utils import create_msg, log_webpage 18 | 19 | try: 20 | from Crypto.PublicKey import RSA 21 | from Crypto.Cipher import PKCS1_OAEP 22 | except: 23 | from Cryptodome.PublicKey import RSA 24 | from Cryptodome.Cipher import PKCS1_OAEP 25 | 26 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 27 | 28 | # https://github.com/Hari-Nagarajan/nvidia-bot/blob/master/stores/bestbuy.py 29 | 30 | BEST_BUY_PDP_URL = "https://api.bestbuy.com/click/5592e2b895800000/{sku}/pdp" 31 | BEST_BUY_CART_URL = "https://api.bestbuy.com/click/5592e2b895800000/{sku}/cart" 32 | 33 | BEST_BUY_ADD_TO_CART_API_URL = "https://www.bestbuy.com/cart/api/v1/addToCart" 34 | BEST_BUY_CHECKOUT_URL = "https://www.bestbuy.com/checkout/c/orders/{order_id}/" 35 | 36 | DEFAULT_HEADERS = { 37 | "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", 38 | "accept-encoding": "gzip, deflate, br", 39 | "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7", 40 | "user-agent": settings.userAgent, 41 | "origin": "https://www.bestbuy.com", 42 | } 43 | 44 | 45 | options = Options() 46 | options.page_load_strategy = "eager" 47 | options.add_experimental_option("excludeSwitches", ["enable-automation"]) 48 | options.add_experimental_option("useAutomationExtension", False) 49 | 50 | prefs = { 51 | "profile.managed_default_content_settings.images":2, 52 | "profile.default_content_setting_values.notifications":2, 53 | "profile.managed_default_content_settings.stylesheets":2, 54 | "profile.managed_default_content_settings.cookies":1, 55 | "profile.managed_default_content_settings.javascript":1, 56 | "profile.managed_default_content_settings.plugins":1, 57 | "profile.managed_default_content_settings.popups":2, 58 | "profile.managed_default_content_settings.geolocation":1, 59 | "profile.managed_default_content_settings.media_stream":2, 60 | } 61 | options.add_experimental_option("prefs", prefs) 62 | options.add_argument("user-data-dir=.profile-bb") 63 | # print(options.to_capabilities(),flush=True) 64 | 65 | def driver_click(driver, find_type, selector): 66 | """Driver Wait and Click Settings.""" 67 | while True: 68 | if find_type == 'css': 69 | try: 70 | driver.find_element_by_css_selector(selector).click() 71 | break 72 | except NoSuchElementException: 73 | driver.implicitly_wait(1) 74 | elif find_type == 'name': 75 | try: 76 | driver.find_element_by_name(selector).click() 77 | break 78 | except NoSuchElementException: 79 | driver.implicitly_wait(1) 80 | elif find_type == 'xpath': 81 | try: 82 | driver.find_element_by_xpath(f"//*[@class='{selector}']").click() 83 | break 84 | except NoSuchElementException: 85 | driver.implicitly_wait(1) 86 | 87 | class BestBuy: 88 | 89 | def __init__(self, status_signal, image_signal, product, profile, proxy, monitor_delay, error_delay): 90 | self.status_signal, self.image_signal, self.product, self.profile, self.monitor_delay, self.error_delay = status_signal, image_signal, product, profile, float(monitor_delay), float(error_delay) 91 | self.sku_id = parse.parse_qs(parse.urlparse(self.product).query)['skuId'][0] 92 | self.session = requests.Session() 93 | # TODO: Refactor Bird Bot Auto Checkout Functionality. 94 | self.browser = self.init_driver() 95 | starting_msg = "Starting Best Buy Task" 96 | if settings.dont_buy: 97 | starting_msg = "Starting Best Buy Task in dev mode - Phoenix Bot will not actually checkout. Check Settings page to disable Dev Mode" 98 | self.status_signal.emit(create_msg(starting_msg, "normal")) 99 | 100 | # TODO: Add Product Image To UI 101 | 102 | if proxy: 103 | self.session.proxies.update(proxy) 104 | 105 | adapter = HTTPAdapter( 106 | max_retries=Retry( 107 | total=3, 108 | backoff_factor=1, 109 | status_forcelist=[429, 500, 502, 503, 504], 110 | method_whitelist=["HEAD", "GET", "OPTIONS", "POST"], 111 | ) 112 | ) 113 | self.session.mount("https://", adapter) 114 | self.session.mount("http://", adapter) 115 | 116 | response = self.session.get( 117 | BEST_BUY_PDP_URL.format(sku=self.sku_id), headers=DEFAULT_HEADERS 118 | ) 119 | self.status_signal.emit(create_msg(f"PDP Request: {response.status_code}", "normal")) 120 | self.product = response.url 121 | self.status_signal.emit(create_msg(f"Product URL: {self.product}", "normal")) 122 | self.session.get(self.product) 123 | self.status_signal.emit(create_msg(f"Product URL Request: {response.status_code}", "normal")) 124 | # self.status_signal.emit(create_msg("Loading headless driver.", "normal")) 125 | 126 | 127 | self.status_signal.emit(create_msg("Loading https://www.bestbuy.com/", "normal")) 128 | self.login() 129 | 130 | self.browser.get(self.product) 131 | cookies = self.browser.get_cookies() 132 | 133 | [ 134 | self.session.cookies.set_cookie( 135 | requests.cookies.create_cookie( 136 | domain=cookie["domain"], 137 | name=cookie["name"], 138 | value=cookie["value"] 139 | ) 140 | ) 141 | for cookie in cookies 142 | ] 143 | 144 | self.status_signal.emit(create_msg("Calling location/v1/US/approximate", "normal")) 145 | status_code = self.session.get( 146 | "https://www.bestbuy.com/location/v1/US/approximate", 147 | headers=DEFAULT_HEADERS 148 | ).status_code 149 | self.status_signal.emit(create_msg(f"{status_code}", "normal") 150 | ) 151 | self.status_signal.emit(create_msg("Calling basket/v1/basketCount", "normal")) 152 | status_code = self.session.get( 153 | "https://www.bestbuy.com/basket/v1/basketCount", 154 | headers=DEFAULT_HEADERS 155 | ).status_code 156 | self.status_signal.emit(create_msg(f"{status_code}", "normal")) 157 | self.check_stock() 158 | 159 | def init_driver(self): 160 | 161 | chrome_options = Options() 162 | if settings.run_headless: 163 | self.status_signal.emit(create_msg("Running headless","normal")) 164 | chrome_options.add_argument("--headless") 165 | chrome_options.add_argument(f"User-Agent={settings.userAgent}") 166 | 167 | driver = webdriver.Chrome(ChromeDriverManager().install(),options=chrome_options) 168 | driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { 169 | "source": """ 170 | Object.defineProperty(navigator, 'webdriver', { 171 | get: () => undefined 172 | }) 173 | """ 174 | }) 175 | 176 | return driver 177 | 178 | def login(self): 179 | try: 180 | self.status_signal.emit(create_msg("Logging in...", "normal")) 181 | self.browser.get("https://www.bestbuy.com/identity/global/signin") 182 | 183 | time.sleep(5) 184 | # set remember me to true, probably don't need this TBH 185 | WebDriverWait(self.browser, 10).until( 186 | EC.presence_of_element_located((By.ID, "ca-remember-me")) 187 | ).click() 188 | 189 | # Fill email field 190 | WebDriverWait(self.browser, 10).until( 191 | EC.presence_of_element_located((By.ID, "fld-e")) 192 | ).send_keys(settings.bestbuy_user) 193 | 194 | # Fill password field 195 | WebDriverWait(self.browser, 10).until( 196 | EC.presence_of_element_located((By.ID, "fld-p1")) 197 | ).send_keys(settings.bestbuy_pass) 198 | 199 | time.sleep(2) 200 | signInButton = WebDriverWait(self.browser, 10).until( 201 | EC.presence_of_element_located((By.XPATH,"//button[contains(@class,'cia-form__controls__submit')]")) 202 | ) 203 | signInButton.click() 204 | 205 | WebDriverWait(self.browser, 10).until( 206 | lambda x: "Official Online Store" in self.browser.title or "Sign In - Add Recovery Phone" in self.browser.title 207 | ) 208 | 209 | if "Sign In - Add Recovery Phone" in self.browser.title: 210 | self.status_signal.emit(create_msg("Sign In - Add Recovery phone page hit, probably can ignore...","normal")) 211 | # skipBtn = WebDriverWait(self.browser, 10).until( 212 | # EC.presence_of_element_located((By.XPATH,"//button[@text()='Skip for now']")) 213 | # ) 214 | # skipBtn.click() 215 | self.browser.get("https://www.bestbuy.com") 216 | except Exception as e: 217 | self.status_signal.emit(create_msg("Bestbuy login error, see console for details","error")) 218 | print(f"Dumped webpage to file: {log_webpage('errors','bby_login',self.browser.page_source)}") 219 | 220 | if not settings.run_headless: 221 | try: 222 | closeModal = WebDriverWait(self.browser, 10).until( 223 | EC.presence_of_element_located((By.XPATH, "//button[@class='c-close-icon c-modal-close-icon']")) 224 | ) 225 | if closeModal: 226 | closeModal.click() 227 | self.status_signal.emit(create_msg("Closing annoying modal", "normal")) 228 | except Exception as e: 229 | pass 230 | 231 | def verify_signed_in(self): 232 | signedIn = WebDriverWait(self.browser, 10).until( 233 | EC.presence_of_element_located((By.XPATH, "//span[@class='plButton-label v-ellipsis']")) 234 | ) 235 | if signedIn: 236 | return True 237 | else: 238 | return False 239 | 240 | 241 | def check_stock(self): 242 | if self.verify_signed_in(): 243 | self.status_signal.emit(create_msg("Bestbuy successfully logged in.","normal")) 244 | time.sleep(2) 245 | # verify we are on the product page here and prep to add to cart 246 | self.browser.get(self.product) 247 | 248 | # this queries the bestbuy api for stock 249 | while not self.in_stock(): 250 | sleep(5) 251 | self.status_signal.emit(create_msg(f"Item {self.sku_id} is in stock!", "normal")) 252 | # TODO: Refactor Bird Bot Auto Checkout Functionality. 253 | self.browser.refresh() 254 | self.status_signal.emit(create_msg(f"SKU: {self.sku_id} in stock: {BEST_BUY_CART_URL.format(sku=self.sku_id)}", "normal")) 255 | self.add_to_cart() 256 | sleep(5) 257 | 258 | def in_stock(self): 259 | self.status_signal.emit(create_msg("Checking stock", "normal")) 260 | 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( 261 | self.sku_id 262 | ) 263 | # TODO: Add random delay configuration 264 | response = self.session.get(url, headers=DEFAULT_HEADERS) 265 | self.status_signal.emit(create_msg(f"Stock check response code: {response.status_code}", "normal")) 266 | try: 267 | response_json = response.json() 268 | item_json = find_values( 269 | json.dumps(response_json), "buttonStateResponseInfos" 270 | ) 271 | item_state = item_json[0][0]["buttonState"] 272 | self.status_signal.emit(create_msg(f"Item state is: {item_state}", "normal")) 273 | if item_json[0][0]["skuId"] == self.sku_id and item_state in [ 274 | "ADD_TO_CART", 275 | "PRE_ORDER" 276 | ]: 277 | return True 278 | else: 279 | return False 280 | except Exception as e: 281 | self.status_signal.emit(create_msg("Error parsing json. Using string search to determine state.", "error")) 282 | self.status_signal.emit(create_msg(f"{response_json}", "normal")) 283 | self.status_signal.emit(create_msg(f"{e}", "error")) 284 | if "ADD_TO_CART" in response.text: #TODO: Make this case insensitive 285 | self.status_signal.emit(create_msg("Item is in stock!", "normal")) 286 | return True 287 | else: 288 | self.status_signal.emit(create_msg("Item is out of stock", "normal")) 289 | webdriver.refresh() 290 | return False 291 | 292 | 293 | def add_to_cart(self, retry=False): 294 | self.status_signal.emit(create_msg("Opening Cart", "normal")) 295 | # webbrowser.open_new(BEST_BUY_CART_URL.format(sku=self.sku_id)) 296 | 297 | if retry: 298 | self.browser.get(self.product) 299 | 300 | atcBtn = WebDriverWait(self.browser, 10).until( 301 | EC.element_to_be_clickable((By.CSS_SELECTOR, ".add-to-cart-button")) 302 | ) 303 | if atcBtn: 304 | atcBtn.click() 305 | self.status_signal.emit(create_msg("Add to cart button is live!", "normal")) 306 | 307 | # Queue system logic - courtesy RTX-3070-BEST-BUY-BOT 308 | WebDriverWait(self.browser, 15).until( 309 | EC.element_to_be_clickable((By.CSS_SELECTOR,".add-to-cart-button")) 310 | ) 311 | 312 | driver_click(self.browser, 'css', '.add-to-cart-button') 313 | 314 | self.status_signal.emit(create_msg("In Queue, refreshing page until our turn", "normal")) 315 | # send text message here 316 | time.sleep(5) 317 | self.browser.refresh() 318 | 319 | while True: 320 | try: 321 | add_to_cart = self.browser.find_element_by_css_selector(".add-to-cart-button") 322 | please_wait_enabled = add_to_cart.get_attribute('aria-describedby') 323 | 324 | if please_wait_enabled: 325 | print("Please wait enabled",flush=True) 326 | self.browser.refresh() 327 | time.sleep(15) 328 | else: # When Add to Cart appears. This will click button. 329 | WebDriverWait(self.browser, 5).until( 330 | EC.presence_of_element_located((By.CSS_SELECTOR, ".add-to-cart-button")) 331 | ) 332 | time.sleep(2) 333 | driver_click(self.browser, 'css', '.add-to-cart-button') 334 | time.sleep(2) 335 | break 336 | except(NoSuchElementException, TimeoutException) as error: 337 | print(f'Queue System Refresh Error: ${error}',flush=True) 338 | 339 | self.browser.get('https://www.bestbuy.com/cart') 340 | 341 | try: 342 | WebDriverWait(self.browser, 15).until( 343 | EC.presence_of_element_located((By.XPATH, "//*[@class='btn btn-lg btn-block btn-primary']")) 344 | ) 345 | time.sleep(1) 346 | driver_click(self.browser, 'xpath', 'btn btn-lg btn-block btn-primary') 347 | self.status_signal.emit(create_msg("Product still in cart", "normal")) 348 | self.start_checkout() 349 | except (NoSuchElementException, TimeoutException): 350 | self.status_signal.emit(create_msg("Item is not in cart anymore, Retrying...","normal")) 351 | time.sleep(3) 352 | self.add_to_cart(True) 353 | 354 | def start_checkout(self): 355 | 356 | self.status_signal.emit(create_msg("Attempting Checkout", "normal")) 357 | 358 | if "Returning Customer" in self.browser.page_source: 359 | # Fill password field 360 | WebDriverWait(self.browser, 10).until( 361 | EC.presence_of_element_located((By.ID, "fld-p1")) 362 | ).send_keys(settings.bestbuy_pass) 363 | 364 | time.sleep(2) 365 | signInButton = WebDriverWait(self.browser, 10).until( 366 | EC.presence_of_element_located((By.XPATH,"//button[contains(@class,'cia-form__controls__submit')]")) 367 | ) 368 | signInButton.click() 369 | 370 | 371 | #### keep this for now, not sure if we still need it 372 | # # click shipping option if available, currently sets it to ISPU (in store pick up) 373 | # try: 374 | 375 | # self.status_signal.emit(create_msg("Selecting Shipping Checkout", "normal")) 376 | # WebDriverWait(self.browser, 5).until( 377 | # EC.presence_of_element_located((By.XPATH, "//*[@class='btn btn-lg btn-block btn-primary button__fast-track']")) 378 | # ) 379 | # time.sleep(2) 380 | # shipping_class = self.browser.find_element_by_xpath("//*[@class='ispu-card__switch']") 381 | # shipping_class.click() 382 | # except (NoSuchElementException, TimeoutException, ElementNotInteractableException, ElementClickInterceptedException) as error: 383 | # print(f'shipping error: {error}',flush=True) 384 | 385 | 386 | try: 387 | self.status_signal.emit(create_msg("Trying CVV Number.","normal")) 388 | security_code = WebDriverWait(self.browser, 5).until( 389 | EC.presence_of_element_located((By.ID, "credit-card-cvv")) 390 | ) 391 | # time.sleep(1) 392 | # security_code = self.browser.find_element_by_id("cvv") 393 | time.sleep(1) 394 | security_code.send_keys(self.profile['card_cvv']) 395 | except (NoSuchElementException, TimeoutException): 396 | pass 397 | 398 | self.did_submit = False 399 | while not self.did_submit: 400 | try: 401 | WebDriverWait(self.browser, 5).until( 402 | EC.presence_of_element_located((By.XPATH, "//*[@class='btn btn-lg btn-block btn-primary button__fast-track']")) 403 | ) 404 | # comment the one down below. vv 405 | if not settings.dont_buy: 406 | driver_click(self.browser, 'xpath', 'btn btn-lg btn-block btn-primary button__fast-track') 407 | 408 | if 'https://www.bestbuy.com/checkout/r/thank-you' in self.browser.current_url or settings.dont_buy: 409 | if settings.dont_buy: 410 | self.status_signal.emit(create_msg("Mock Order Placed", "success")) 411 | self.did_submit = True 412 | else: 413 | self.status_signal.emit(create_msg("Order Placed", "success")) 414 | self.did_submit = True 415 | except (NoSuchElementException, TimeoutException, ElementNotInteractableException): 416 | print("Could Not Complete Checkout.",flush=True) 417 | pass 418 | except: 419 | self.status_signal.emit(create_msg('Retrying submit order until success', 'normal')) -------------------------------------------------------------------------------- /sites/gamestop.py: -------------------------------------------------------------------------------- 1 | from requests.sessions import default_headers 2 | from selenium import webdriver 3 | from selenium.webdriver.support import expected_conditions as EC 4 | from selenium.webdriver.common.by import By 5 | from selenium.webdriver.support.ui import WebDriverWait as wait 6 | from selenium.webdriver.chrome.options import Options 7 | from webdriver_manager.chrome import ChromeDriverManager 8 | from chromedriver_py import binary_path as driver_path 9 | from utils import random_delay, send_webhook, create_msg 10 | from utils.selenium_utils import change_driver 11 | import settings, time 12 | 13 | options = Options() 14 | options.page_load_strategy = "eager" 15 | options.add_experimental_option("excludeSwitches", ["enable-automation"]) 16 | options.add_experimental_option("useAutomationExtension", False) 17 | 18 | ### Need to optimize options below for pageload but not piss off gamestop 19 | 20 | prefs = { 21 | "profile.managed_default_content_settings.images":2, 22 | # "profile.default_content_setting_values.notifications":2, 23 | # "profile.managed_default_content_settings.stylesheets":2, 24 | # "profile.managed_default_content_settings.cookies":1, 25 | # "profile.managed_default_content_settings.javascript":1, 26 | # "profile.managed_default_content_settings.plugins":1, 27 | # "profile.managed_default_content_settings.popups":2, 28 | # "profile.managed_default_content_settings.geolocation":1, 29 | # "profile.managed_default_content_settings.media_stream":2, 30 | } 31 | 32 | options.add_experimental_option("prefs", prefs) 33 | options.add_argument(f"User-Agent={settings.userAgent}") 34 | 35 | class GameStop: 36 | def __init__(self, task_id, status_signal, image_signal, product, profile, proxy, monitor_delay, error_delay, max_price): 37 | 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( 38 | monitor_delay), float(error_delay), max_price 39 | 40 | starting_msg = "Starting GameStop" 41 | self.browser = self.init_driver() 42 | self.product_image = None 43 | 44 | self.SHORT_TIMEOUT = 5 45 | self.LONG_TIMEOUT = 20 46 | 47 | if settings.dont_buy: 48 | starting_msg = "Starting GameStop in dev mode; will not actually checkout." 49 | 50 | self.status_signal.emit(create_msg(starting_msg, "normal")) 51 | self.login() 52 | self.monitor() 53 | self.add_to_cart() 54 | 55 | #### Need to setup captcha solvers to work the below, TODO Still needed?? 56 | self.submit_billing() 57 | self.submit_order() 58 | 59 | def init_driver(self): 60 | if settings.run_headless: 61 | self.status_signal.emit(create_msg("Running headless","normal")) 62 | options.add_argument("--headless") 63 | 64 | ## Gamestop does not like it when we do not have a user-agent 65 | 66 | 67 | driver = webdriver.Chrome(ChromeDriverManager().install(),options=options) 68 | driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { 69 | "source": """ 70 | Object.defineProperty(navigator, 'webdriver', { 71 | get: () => undefined 72 | }) 73 | """ 74 | }) 75 | driver.minimize_window() 76 | return driver 77 | 78 | def login(self): 79 | self.status_signal.emit(create_msg("Logging In..", "normal")) 80 | 81 | #load home page so we get the cookies and referrer crap 82 | self.browser.get('https://www.gamestop.com/') 83 | 84 | time.sleep(5) 85 | 86 | if not settings.run_headless: 87 | # self.browser.maximize_window() 88 | self.browser.get("https://www.gamestop.com/?openLoginModal=accountModal") 89 | 90 | time.sleep(5) 91 | 92 | self.browser.find_element_by_xpath('//a[@id="account-modal-link-nocache"]').click() 93 | else: 94 | self.browser.get("https://www.gamestop.com/login/") 95 | 96 | wait(self.browser, self.LONG_TIMEOUT).until(EC.element_to_be_clickable((By.ID, "login-form-email"))) 97 | 98 | email = self.browser.find_element_by_id("login-form-email") 99 | email.send_keys(settings.gamestop_user) 100 | 101 | wait(self.browser, self.LONG_TIMEOUT).until(EC.element_to_be_clickable((By.ID, "login-form-password"))) 102 | time.sleep(5) 103 | password = self.browser.find_element_by_id("login-form-password") 104 | password.send_keys(settings.gamestop_pass) 105 | 106 | time.sleep(2) # slight delay for in-between filling out login info and clicking Sign In 107 | 108 | sign_in_btn = wait(self.browser, self.LONG_TIMEOUT).until( 109 | EC.presence_of_element_located((By.XPATH, "//button[@class='btn btn-block mb-2 sign-in-submit']")) 110 | ) 111 | sign_in_btn.click() 112 | time.sleep(10) 113 | 114 | def monitor(self): 115 | 116 | ## verify we have signed successfully else we should abort the task or attempt sign-in again 117 | # (TODO: add max attempts to sign-in before exiting task) 118 | if "user-message-initial" in self.browser.page_source: 119 | self.status_signal.emit(create_msg("Gamestop Successfully logged in...", "normal")) 120 | else: 121 | self.status_signal.emit(create_msg("Error logging in... please restart task","stopnow")) 122 | 123 | # TODO: Exit task if we are not signed in 124 | self.status_signal.emit(create_msg("Checking Stock..", "normal")) 125 | 126 | # self.browser.set_window_size(900, 900) 127 | 128 | self.browser.get(self.product) 129 | wait(self.browser, self.LONG_TIMEOUT).until(lambda _: self.browser.current_url == self.product) 130 | 131 | in_stock = False 132 | 133 | while not in_stock: 134 | try: 135 | 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"]'))) 136 | add_to_cart_btn = self.browser.find_element_by_xpath('//button[@data-buttontext="Add to Cart"]') 137 | add_to_cart_btn.click() 138 | time.sleep(1) 139 | if not add_to_cart_btn.is_enabled(): 140 | self.status_signal.emit(create_msg("Waiting For Restock", "normal")) 141 | self.browser.refresh() 142 | continue 143 | in_stock = True 144 | self.status_signal.emit(create_msg("Added to cart", "normal")) 145 | self.browser.maximize_window() 146 | # remove stop temporarily to see if gamestop captcha is an issue 147 | # self.status_signal.emit(create_msg("Added to cart, check for captcha","stopnow")) 148 | self.browser.get("https://www.gamestop.com/cart/") 149 | except: 150 | self.status_signal.emit(create_msg("Waiting For Restock", "normal")) 151 | self.browser.refresh() 152 | 153 | def add_to_cart(self): 154 | wait(self.browser, self.LONG_TIMEOUT).until(lambda _: self.browser.current_url == "https://www.gamestop.com/cart/") 155 | 156 | ##### THERE IS NOW A CAPTCHA HERE (POPUP) 157 | 158 | self.status_signal.emit(create_msg("Checking Age Verification", "normal")) 159 | 160 | try: 161 | seventeen_or_older_btn = self.browser.find_element_by_xpath('//*[@id="age-gate-modal"]/div/div/div[2]/div/div[2]/button') 162 | seventeen_or_older_btn.click() 163 | time.sleep(2) # short delay for age verification modal to disappear 164 | self.browser.get("https://www.gamestop.com/checkout/?stage=payment#payment") 165 | except: 166 | self.browser.get("https://www.gamestop.com/checkout/?stage=payment#payment") 167 | 168 | def submit_billing(self): 169 | wait(self.browser, self.LONG_TIMEOUT).until(lambda _: self.browser.current_url == "https://www.gamestop.com/checkout/?stage=payment#payment") 170 | 171 | self.status_signal.emit(create_msg("Entering CVV #", "normal")) 172 | 173 | wait(self.browser, self.LONG_TIMEOUT).until(EC.element_to_be_clickable((By.ID, "saved-payment-security-code"))) 174 | cvv_input = self.browser.find_element_by_id("saved-payment-security-code") 175 | cvv_input.send_keys(self.profile["card_cvv"]) 176 | order_review_btn = self.browser.find_element_by_class_name("btn.btn-primary.btn-block.submit-payment") 177 | order_review_btn.click() 178 | 179 | def submit_order(self): 180 | wait(self.browser, self.LONG_TIMEOUT).until(lambda _: self.browser.current_url == "https://www.gamestop.com/checkout/?stage=placeOrder#placeOrder") 181 | 182 | self.status_signal.emit(create_msg("Submitting Order..", "normal")) 183 | 184 | ##### THERE IS NOW A CAPTCHA HERE (POPULATED - NEED TO CLICK) 185 | 186 | wait(self.browser, self.LONG_TIMEOUT).until(EC.element_to_be_clickable((By.CLASS_NAME, 'btn.btn-primary.btn-block.place-order'))) 187 | 188 | if not settings.dont_buy: 189 | order_review_btn = self.browser.find_element_by_class_name("btn.btn-primary.btn-block.place-order") 190 | order_review_btn.click() 191 | self.status_signal.emit(create_msg("Order Placed", "success")) 192 | send_webhook("OP", "GameStop", self.profile["profile_name"], self.task_id, self.product_image) 193 | else: 194 | self.status_signal.emit(create_msg("Mock Order Placed", "success")) 195 | send_webhook("OP", "GameStop", self.profile["profile_name"], self.task_id, self.product_image) 196 | 197 | # TODO: when running with headless == False it would be good to quit browsers when task is stopped (might be good to keep it open if it errors out however for diagnostics) 198 | # def stop(self): 199 | # self.browser.quit() -------------------------------------------------------------------------------- /sites/target.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.support import expected_conditions as EC 3 | from selenium.webdriver.common.by import By 4 | from selenium.webdriver.common.keys import Keys 5 | from selenium.webdriver.support.ui import WebDriverWait as wait 6 | # from selenium.webdriver.chrome.options import Options 7 | from webdriver_manager.chrome import ChromeDriverManager 8 | from chromedriver_py import binary_path as driver_path 9 | from selenium.webdriver.firefox.options import Options 10 | 11 | from utils import random_delay, send_webhook, create_msg 12 | from utils.selenium_utils import change_driver 13 | import settings, time, random 14 | 15 | # options = Options() 16 | 17 | # ### Need to experiment with options below to optimize page load but not piss off target 18 | 19 | # # options.page_load_strategy = "eager" 20 | # # options.add_experimental_option("excludeSwitches", ["enable-automation"]) 21 | # # options.add_experimental_option("useAutomationExtension", False) 22 | # options.add_argument('--disable-blink-features=AutomationControlled') 23 | 24 | # # prefs = { 25 | # # "profile.managed_default_content_settings.images":1, 26 | # # "profile.default_content_setting_values.notifications":1, 27 | # # "profile.managed_default_content_settings.stylesheets":1, 28 | # # "profile.managed_default_content_settings.cookies":1, 29 | # # "profile.managed_default_content_settings.javascript":1, 30 | # # "profile.managed_default_content_settings.plugins":1, 31 | # # "profile.managed_default_content_settings.popups":1, 32 | # # "profile.managed_default_content_settings.geolocation":1, 33 | # # "profile.managed_default_content_settings.media_stream":1, 34 | # # } 35 | 36 | # # options.add_experimental_option("prefs", prefs) 37 | # options.add_argument(f"user-data-dir={mypath}") 38 | # # options.add_argument("user-data-dir=C:\\Users\\larse\\AppData\\Local\\Google\\Chrome\\User Data\\") 39 | # options.add_argument(f"User-Agent={settings.userAgent}") 40 | 41 | class Target: 42 | def __init__(self, task_id, status_signal, image_signal, product, profile, proxy, monitor_delay, error_delay): 43 | self.task_id, self.status_signal, self.image_signal, self.product, self.profile, self.monitor_delay, self.error_delay = task_id, status_signal, image_signal, product, profile, float( 44 | monitor_delay), float(error_delay) 45 | self.xpath_sequence = [ 46 | {'type': 'method', 'path': '//button[@data-test="orderPickupButton"] | //button[@data-test="shipItButton"]', 'method': self.find_and_click_atc, 'message': 'Added to cart', 'message_type': 'normal', 'optional': False} 47 | , {'type': 'button', 'path': '//button[@data-test="espModalContent-declineCoverageButton"]', 'message': 'Declining Coverage', 'message_type': 'normal', 'optional': True} 48 | , {'type': 'button', 'path': '//button[@data-test="addToCartModalViewCartCheckout"]', 'message': 'Viewing Cart before Checkout', 'message_type': 'normal', 'optional': False} 49 | , {'type': 'button', 'path': '//button[@data-test="checkout-button"]', 'message': 'Checking out', 'message_type': 'normal', 'optional': False} 50 | , {'type': 'method', 'path': '//button[@data-test="placeOrderButton"]', 'method': self.submit_order, 'message': 'Submitting order', 'message_type': 'normal', 'optional': False} 51 | ] 52 | self.possible_interruptions = [ 53 | {'type': 'method', 'path': '//input[@id="password"]', 'method': self.quick_fill_for_checkout, 'message': 'Authenticating', 'message_type': 'normal'}, 54 | {'type': 'input', 'path': '//input[@id="creditCardInput-cardNumber"]', 'args': {'value': self.profile['card_number'], 'confirm_button': '//button[@data-test="verify-card-button"]'}, 'message': 'Entering CC #', 'message_type': 'normal'}, 55 | {'type': 'input', 'path': '//input[@id="creditCardInput-cvv"]', 'args': {'value': self.profile['card_cvv']}, 'message': 'Entering CC #', 'message_type': 'normal'} 56 | ] 57 | starting_msg = "Starting Target" 58 | self.browser = self.init_driver() 59 | if self.browser: 60 | self.product_image = None 61 | self.TIMEOUT_SHORT = 5 62 | self.TIMEOUT_LONG = 20 63 | self.did_submit = False 64 | self.failed = False 65 | self.retry_attempts = 10 66 | if settings.dont_buy: 67 | starting_msg = "Starting Target in dev mode; will not actually checkout" 68 | self.status_signal.emit(create_msg(starting_msg, "normal")) 69 | self.status_signal.emit(create_msg("Logging In..", "normal")) 70 | self.login() 71 | self.img_found = False 72 | self.product_loop() 73 | send_webhook("OP", "Target", self.profile["profile_name"], self.task_id, self.product_image) 74 | 75 | def init_driver(self): 76 | options = Options() 77 | if settings.run_headless: 78 | self.status_signal.emit(create_msg("Running headless","normal")) 79 | options.headless = True 80 | 81 | profile = webdriver.FirefoxProfile() 82 | profile.set_preference("general.useragent.override", settings.userAgent) 83 | if settings.geckodriver_path == "": 84 | self.status_signal.emit(create_msg("Install geckodriver & set folder location","stopnow")) 85 | 86 | return False 87 | else: 88 | driver = webdriver.Firefox(firefox_profile=profile, firefox_options=options, executable_path='geckodriver') 89 | 90 | return driver 91 | 92 | def login(self): 93 | self.browser.get("https://www.target.com") 94 | accountBtn = wait(self.browser, self.TIMEOUT_LONG).until( 95 | EC.presence_of_element_located((By.ID, "account")) 96 | ) 97 | accountBtn.click() 98 | 99 | test = wait(self.browser, 5).until( 100 | EC.presence_of_element_located((By.XPATH,"//li[@id='accountNav-signIn']/a")) 101 | ) 102 | test.click() 103 | time.sleep(10) 104 | self.fill_and_authenticate() 105 | 106 | test = self.browser.find_element_by_xpath('//span[@data-test="accountUserName"]') 107 | time.sleep(1) 108 | if "sign in" in test.text.lower(): 109 | if settings.run_headless: 110 | self.status_signal.emit(create_msg("Did not detect username on target page. Got \"{}\"".format(test.text),"stopnow")) 111 | else: 112 | self.status_signal.emit(create_msg("Did not detect username on target page. Got \"{}\"".format(test.text),"normal")) 113 | else: 114 | self.status_signal.emit(create_msg("Succesfully signed in as {}".format(test.text),"normal")) 115 | # # Gives it time for the login to complete 116 | time.sleep(random_delay(self.monitor_delay, settings.random_delay_start, settings.random_delay_stop)) 117 | 118 | #TODO verify we logged in here 119 | 120 | def fill_and_authenticate(self): 121 | 122 | # slowly enters username and password with random waits between each character 123 | 124 | if self.browser.find_elements_by_id('username'): 125 | for key in settings.target_user: 126 | self.browser.find_element_by_xpath('//input[@id="username"]').send_keys(key) 127 | time.sleep(random.uniform(0.5, 1.5)) 128 | 129 | for key in settings.target_pass: 130 | self.browser.find_element_by_xpath('//input[@id="password"]').send_keys(key) 131 | time.sleep(random.uniform(0.5, 1.5)) 132 | 133 | loginBtn = wait(self.browser, self.TIMEOUT_LONG).until( 134 | EC.presence_of_element_located((By.XPATH,'//button[@id="login"]')) 135 | ) 136 | loginBtn.click() 137 | 138 | time.sleep(2) 139 | else: 140 | self.browser.find_element_by_xpath('//input[@id="password"]').send_keys(settings.target_pass) 141 | 142 | loginBtn = wait(self.browser, self.TIMEOUT_LONG).until( 143 | EC.presence_of_element_located((By.XPATH,'//button[@id="login"]')) 144 | ) 145 | loginBtn.click() 146 | 147 | def quick_fill_for_checkout(self): 148 | self.browser.find_element_by_xpath('//input[@id="password"]').send_keys(settings.target_pass) 149 | time.sleep(random.uniform(0.5,2.5)) 150 | 151 | time.sleep(2) 152 | # self.browser.find_element_by_xpath('//button[@id="login"]').click() 153 | loginBtn = wait(self.browser, 5).until( 154 | EC.presence_of_element_located((By.XPATH,'//button[@id="login"]')) 155 | ) 156 | loginBtn.click() 157 | time.sleep(2) 158 | 159 | def product_loop(self): 160 | while not self.did_submit and not self.failed: 161 | self.monitor() 162 | self.atc_and_checkout() 163 | 164 | def check_stock(self, new_tab=False): 165 | stock = False 166 | if new_tab: 167 | windows_before = self.browser.window_handles 168 | self.browser.execute_script(f'window.open("{self.product}")') 169 | wait(self.browser, 10).until(EC.number_of_windows_to_be(2)) 170 | new_window = [x for x in self.browser.window_handles if x not in windows_before][0] 171 | self.browser.switch_to_window(new_window) 172 | if len(self.browser.find_elements_by_xpath('//button[@data-test="orderPickupButton"]')) > 0 or len(self.browser.find_elements_by_xpath('//button[@data-test="shipItButton"]')) > 0: 173 | stock = True 174 | if new_tab: 175 | self.browser.close() 176 | wait(self.browser, 10).until(EC.number_of_windows_to_be(1)) 177 | old_window = self.browser.window_handles[0] 178 | self.browser.switch_to_window(old_window) 179 | return False 180 | return stock 181 | 182 | def monitor(self): 183 | self.in_stock = False 184 | self.browser.get(self.product) 185 | wait(self.browser, self.TIMEOUT_LONG).until(lambda _: self.browser.current_url == self.product) 186 | 187 | if "product not available" in self.browser.page_source: 188 | # self.browser.quit() 189 | self.status_signal.emit(create_msg("Invalid URL","stopnow")) 190 | else: 191 | while not self.img_found: 192 | try: 193 | if not self.img_found: 194 | product_img = self.browser.find_elements_by_class_name('slideDeckPicture')[0].find_element_by_tag_name( 195 | "img") 196 | self.image_signal.emit(product_img.get_attribute("src")) 197 | self.product_image = product_img.get_attribute("src") 198 | self.img_found = True 199 | except Exception as e: 200 | continue 201 | 202 | while not self.in_stock: 203 | self.in_stock = self.check_stock() 204 | if self.in_stock: 205 | continue 206 | else: 207 | self.status_signal.emit(create_msg("Waiting on Restock", "normal")) 208 | time.sleep(random_delay(self.monitor_delay, settings.random_delay_start, settings.random_delay_stop)) 209 | self.browser.refresh() 210 | 211 | def atc_and_checkout(self): 212 | while not self.did_submit: 213 | for xpath_step in self.xpath_sequence: 214 | for attempt in range(self.retry_attempts + 1): 215 | try: 216 | wait(self.browser, 10).until(EC.presence_of_element_located((By.XPATH, xpath_step['path']))) 217 | self.process_step(xpath_step) 218 | break 219 | except: 220 | if xpath_step['optional']: 221 | break 222 | elif attempt == self.retry_attempts: 223 | if not self.check_stock(new_tab=True): 224 | self.status_signal.emit(create_msg('Product is out of stock. Resuming monitoring.', 'error')) 225 | return 226 | else: 227 | self.status_signal.emit(create_msg('Encountered unknown page while product in stock. Quitting.', 'error')) 228 | self.failed = True 229 | return 230 | self.process_interruptions(attempt=attempt) 231 | 232 | def submit_order(self): 233 | self.did_submit = False 234 | url = self.browser.current_url 235 | while not self.did_submit: 236 | try: 237 | self.process_interruptions(silent=True) 238 | if not settings.dont_buy: 239 | self.browser.find_element_by_xpath('//button[@data-test="placeOrderButton"]').click() 240 | time.sleep(5) 241 | if 'https://www.target.com/co-thankyou' in self.browser.current_url or settings.dont_buy: 242 | if settings.dont_buy: 243 | self.status_signal.emit(create_msg("Mock Order Placed", "success")) 244 | else: 245 | self.status_signal.emit(create_msg("Order Placed", "success")) 246 | send_webhook("OP", "Target", self.profile["profile_name"], self.task_id, self.product_image) 247 | self.did_submit = True 248 | except: 249 | self.status_signal.emit(create_msg('Retrying submit order until success', 'normal')) 250 | 251 | def find_and_click(self, xpath): 252 | self.browser.find_element_by_xpath(xpath).click() 253 | 254 | def find_and_click_atc(self): 255 | if self.browser.current_url == self.product and self.check_stock(): 256 | if self.browser.find_elements_by_xpath('//button[@data-test="orderPickupButton"]'): 257 | button = self.browser.find_element_by_xpath('//button[@data-test="orderPickupButton"]') 258 | elif self.browser.find_elements_by_xpath('//button[@data-test="shipItButton"]'): 259 | button = self.browser.find_element_by_xpath('//button[@data-test="shipItButton"]') 260 | else: 261 | button = None 262 | if button: 263 | self.browser.execute_script("return arguments[0].scrollIntoView(true);", button) 264 | self.atc_clicked = True 265 | button.click() 266 | 267 | def fill_field_and_proceed(self, xpath, args): 268 | input_field = self.browser.find_element_by_xpath(xpath) 269 | if len(input_field.get_attribute('value')) == 0: 270 | input_field.send_keys(args['value']) 271 | if 'confirm_button' in args: 272 | if self.browser.find_elements_by_xpath(args['confirm_button']): 273 | self.find_and_click(args['confirm_button']) 274 | 275 | def process_step(self, xpath_step, wait_after=False, silent=False): 276 | if self.browser.find_elements_by_xpath(xpath_step['path']): 277 | if not silent: 278 | self.status_signal.emit(create_msg(xpath_step['message'], xpath_step['message_type'])) 279 | if xpath_step['type'] == 'button': 280 | self.find_and_click(xpath_step['path']) 281 | elif xpath_step['type'] == 'method': 282 | xpath_step['method']() 283 | elif xpath_step['type'] == 'input': 284 | self.fill_field_and_proceed(xpath_step['path'], xpath_step['args']) 285 | if wait_after: 286 | time.sleep(self.TIMEOUT_SHORT) 287 | 288 | def process_interruptions(self, attempt=0, silent=False): 289 | if not silent: 290 | self.status_signal.emit(create_msg(f'Interrupted, attempting to resolve ({attempt+1}/{self.retry_attempts})', 'error')) 291 | for xpath_step in self.xpath_sequence: 292 | if xpath_step['optional']: 293 | self.process_step(xpath_step, wait_after=True, silent=True) 294 | for xpath_step in self.possible_interruptions: 295 | self.process_step(xpath_step, wait_after=True, silent=True) 296 | 297 | 298 | # TODO: when running with headless == False it would be good to quit browsers when task is stopped (might be good to keep it open if it errors out however for diagnostics) 299 | # def stop(self): 300 | # self.browser.quit() 301 | -------------------------------------------------------------------------------- /sites/walmart.py: -------------------------------------------------------------------------------- 1 | from sites.walmart_encryption import walmart_encryption as w_e 2 | from utils import send_webhook, random_delay 3 | from selenium import webdriver 4 | from webdriver_manager.chrome import ChromeDriverManager 5 | from PyQt5 import QtCore 6 | import urllib, requests, time, lxml.html, json, sys, settings 7 | 8 | 9 | class Walmart: 10 | def __init__(self, task_id, status_signal, image_signal, wait_poll_signal, polling_wait_condition, product, profile, 11 | proxy, monitor_delay, error_delay, max_price): 12 | 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( 13 | monitor_delay), float(error_delay), max_price 14 | 15 | ####### Browser/Captcha Polling Variables ###### 16 | self.captcha_mutex = QtCore.QMutex() 17 | self.captcha_wait_condition = polling_wait_condition 18 | self.wait_poll_signal = wait_poll_signal 19 | ################################################# 20 | 21 | self.session = requests.Session() 22 | if proxy != False: 23 | self.session.proxies.update(proxy) 24 | starting_msg = "Starting" 25 | if settings.dont_buy: 26 | starting_msg = "Starting in dev mode - Phoenix Bot will not actually checkout (dont_buy = True)" 27 | self.status_signal.emit({"msg": starting_msg, "status": "normal"}) 28 | self.product_image, offer_id = self.monitor() 29 | did_add = self.atc(offer_id) 30 | while did_add is False: 31 | did_add = self.atc(offer_id) 32 | 33 | item_id, fulfillment_option, ship_method = self.check_cart_items() 34 | self.submit_shipping_method(item_id, fulfillment_option, ship_method) 35 | self.submit_shipping_address() 36 | card_data, PIE_key_id, PIE_phase = self.get_PIE() 37 | pi_hash = self.submit_payment(card_data, PIE_key_id, PIE_phase) 38 | self.submit_billing(pi_hash) 39 | self.submit_order() 40 | 41 | def monitor(self): 42 | headers = { 43 | "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", 44 | "accept-encoding": "gzip, deflate, br", 45 | "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7", 46 | "cache-control": "max-age=0", 47 | "upgrade-insecure-requests": "1", 48 | "user-agent": settings.userAgent 49 | } 50 | image_found = False 51 | product_image = "" 52 | while True: 53 | self.status_signal.emit({"msg": "Loading Product Page", "status": "normal"}) 54 | try: 55 | r = self.session.get(self.product, headers=headers) 56 | if r.status_code == 200: 57 | # check for captcha page 58 | if self.is_captcha(r.text): 59 | self.status_signal.emit({"msg": "CAPTCHA - Opening Product Page", "status": "error"}) 60 | self.handle_captcha(self.product) 61 | continue 62 | 63 | doc = lxml.html.fromstring(r.text) 64 | if not image_found: 65 | product_image = doc.xpath('//meta[@property="og:image"]/@content')[0] 66 | self.image_signal.emit(product_image) 67 | image_found = True 68 | price = float(doc.xpath('//span[@itemprop="price"]/@content')[0]) 69 | if "add to cart" in r.text.lower(): 70 | if self.max_price != "": 71 | if float(self.max_price) < price: 72 | self.status_signal.emit({"msg": "Waiting For Price Restock", "status": "normal"}) 73 | self.session.cookies.clear() 74 | time.sleep(random_delay(self.monitor_delay, settings.random_delay_start, 75 | settings.random_delay_stop)) 76 | continue 77 | offer_id = json.loads(doc.xpath('//script[@id="item"]/text()')[0])["item"]["product"]["buyBox"][ 78 | "products"][0]["offerId"] 79 | return product_image, offer_id 80 | self.status_signal.emit({"msg": "Waiting For Restock", "status": "normal"}) 81 | self.session.cookies.clear() 82 | time.sleep(random_delay(self.monitor_delay, settings.random_delay_start, settings.random_delay_stop)) 83 | else: 84 | self.status_signal.emit({"msg": "Product Not Found", "status": "normal"}) 85 | time.sleep(random_delay(self.monitor_delay, settings.random_delay_start, settings.random_delay_stop)) 86 | except Exception as e: 87 | self.status_signal.emit({"msg": "Error Loading Product Page (line {} {} {})".format( 88 | sys.exc_info()[-1].tb_lineno, type(e).__name__, e), "status": "error"}) 89 | time.sleep(self.error_delay) 90 | 91 | def atc(self, offer_id): 92 | headers = { 93 | "accept": "application/json", 94 | "accept-encoding": "gzip, deflate, br", 95 | "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7", 96 | "content-type": "application/json", 97 | "origin": "https://www.walmart.com", 98 | "referer": self.product, 99 | "user-agent": settings.userAgent, 100 | "wm_offer_id": offer_id 101 | } 102 | body = {"offerId": offer_id, "quantity": 1, 103 | "location": {"postalCode": self.profile["shipping_zipcode"], "city": self.profile["shipping_city"], 104 | "state": self.profile["shipping_state"], "isZipLocated": True}, 105 | "shipMethodDefaultRule": "SHIP_RULE_1"} 106 | 107 | while True: 108 | self.status_signal.emit({"msg": "Adding To Cart", "status": "normal"}) 109 | try: 110 | r = self.session.post("https://www.walmart.com/api/v3/cart/guest/:CID/items", json=body, 111 | headers=headers) 112 | 113 | # check for captcha page 114 | if self.is_captcha(r.text): 115 | self.status_signal.emit({"msg": "Opening CAPTCHA", "status": "error"}) 116 | self.handle_captcha(self.product) 117 | return False 118 | 119 | if r.status_code == 201 or r.status_code == 200 and json.loads(r.text)["checkoutable"] == True: 120 | self.status_signal.emit({"msg": "Added To Cart", "status": "carted"}) 121 | return True 122 | else: 123 | self.handle_captcha("https://www.walmart.com/cart") 124 | self.status_signal.emit({"msg": "Error Adding To Cart", "status": "error"}) 125 | time.sleep(self.error_delay) 126 | return False 127 | except Exception as e: 128 | self.status_signal.emit({"msg": "Error Adding To Cart (line {} {} {})".format( 129 | sys.exc_info()[-1].tb_lineno, type(e).__name__, e), "status": "error"}) 130 | time.sleep(self.error_delay) 131 | return False 132 | 133 | def check_cart_items(self): 134 | headers = { 135 | "accept": "application/json, text/javascript, */*; q=0.01", 136 | "accept-encoding": "gzip, deflate, br", 137 | "accept-language": "en-US,en;q=0.9", 138 | "content-type": "application/json", 139 | "origin": "https://www.walmart.com", 140 | "referer": "https://www.walmart.com/checkout/", 141 | "user-agent": settings.userAgent, 142 | "wm_vertical_id": "0", 143 | "wm_cvv_in_session": "true", 144 | } 145 | 146 | profile = self.profile 147 | body = {"postalCode": profile["shipping_zipcode"], "city": profile["shipping_city"], 148 | "state": profile["shipping_state"], "isZipLocated": True, "crt:CRT": "", "customerId:CID": "", 149 | "customerType:type": "", "affiliateInfo:com.wm.reflector": "", "storeList": []} 150 | 151 | while True: 152 | self.status_signal.emit({"msg": "Loading Cart Items", "status": "normal"}) 153 | try: 154 | r = self.session.post("https://www.walmart.com/api/checkout/v3/contract?page=CHECKOUT_VIEW", json=body, 155 | headers=headers) 156 | print( 157 | r.text) # this sometimes returns json data related to loading a captcha.js file so that could be intercepted when requests fail 158 | 159 | if r.status_code == 201 or r.status_code == 200: 160 | r = json.loads(r.text)["items"][0] 161 | item_id = r["id"] 162 | fulfillment_option = r["fulfillmentSelection"]["fulfillmentOption"] 163 | ship_method = r["fulfillmentSelection"]["shipMethod"] 164 | self.status_signal.emit({"msg": "Loaded Cart Items", "status": "normal"}) 165 | return item_id, fulfillment_option, ship_method 166 | else: 167 | if json.loads(r.text)["message"] == "Item is no longer in stock.": 168 | self.status_signal.emit({"msg": "Waiting For Restock", "status": "normal"}) 169 | time.sleep( 170 | random_delay(self.monitor_delay, settings.random_delay_start, settings.random_delay_stop)) 171 | else: 172 | if self.is_captcha(r.text): 173 | self.handle_captcha("https://www.walmart.com/checkout") 174 | self.status_signal.emit( 175 | {"msg": "Error Loading Cart Items, Got Response: " + str(r.text), "status": "error"}) 176 | time.sleep(self.error_delay) 177 | except Exception as e: 178 | self.status_signal.emit({"msg": "Error Loading Cart Items (line {} {} {})".format( 179 | sys.exc_info()[-1].tb_lineno, type(e).__name__, e), "status": "error"}) 180 | time.sleep(self.error_delay) 181 | 182 | def submit_shipping_method(self, item_id, fulfillment_option, ship_method): 183 | headers = { 184 | "accept": "application/json, text/javascript, */*; q=0.01", 185 | "accept-encoding": "gzip, deflate, br", 186 | "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7", 187 | "content-type": "application/json", 188 | "origin": "https://www.walmart.com", 189 | "referer": "https://www.walmart.com/checkout/", 190 | "user-agent": settings.userAgent, 191 | "wm_vertical_id": "0" 192 | } 193 | body = {"groups": [{"fulfillmentOption": fulfillment_option, "itemIds": [item_id], "shipMethod": ship_method}]} 194 | while True: 195 | self.status_signal.emit({"msg": "Submitting Shipping Method", "status": "normal"}) 196 | try: 197 | r = self.session.post("https://www.walmart.com/api/checkout/v3/contract/:PCID/fulfillment", json=body, 198 | headers=headers) 199 | if r.status_code == 200: 200 | try: 201 | r = json.loads(r.text) 202 | self.status_signal.emit({"msg": "Submitted Shipping Method", "status": "normal"}) 203 | return 204 | except: 205 | pass 206 | self.status_signal.emit({"msg": "Error Submitting Shipping Method", "status": "error"}) 207 | time.sleep(self.error_delay) 208 | except Exception as e: 209 | self.status_signal.emit({"msg": "Error Submitting Shipping Method (line {} {} {})".format( 210 | sys.exc_info()[-1].tb_lineno, type(e).__name__, e), "status": "error"}) 211 | time.sleep(self.error_delay) 212 | 213 | def submit_shipping_address(self): 214 | headers = { 215 | "accept": "application/json, text/javascript, */*; q=0.01", 216 | "accept-encoding": "gzip, deflate, br", 217 | "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7", 218 | "content-type": "application/json", 219 | "inkiru_precedence": "false", 220 | "origin": "https://www.walmart.com", 221 | "referer": "https://www.walmart.com/checkout/", 222 | "user-agent": settings.userAgent, 223 | "wm_vertical_id": "0" 224 | } 225 | profile = self.profile 226 | body = { 227 | "addressLineOne": profile["shipping_a1"], 228 | "city": profile["shipping_city"], 229 | "firstName": profile["shipping_fname"], 230 | "lastName": profile["shipping_lname"], 231 | "phone": profile["shipping_phone"], 232 | "email": profile["shipping_email"], 233 | "marketingEmailPref": False, 234 | "postalCode": profile["shipping_zipcode"], 235 | "state": profile["shipping_state"], 236 | "countryCode": "USA", 237 | "addressType": "RESIDENTIAL", 238 | "changedFields": [] 239 | } 240 | if profile["shipping_a2"] != "": 241 | body.update({"addressLineTwo": profile["shipping_a2"]}) 242 | while True: 243 | self.status_signal.emit({"msg": "Submitting Shipping Address", "status": "normal"}) 244 | try: 245 | r = self.session.post("https://www.walmart.com/api/checkout/v3/contract/:PCID/shipping-address", 246 | json=body, headers=headers) 247 | if r.status_code == 200: 248 | try: 249 | r = json.loads(r.text) 250 | self.status_signal.emit({"msg": "Submitted Shipping Address", "status": "normal"}) 251 | return 252 | except: 253 | pass 254 | self.status_signal.emit({"msg": "Error Submitting Shipping Address", "status": "error"}) 255 | time.sleep(self.error_delay) 256 | except Exception as e: 257 | self.status_signal.emit({"msg": "Error Submitting Shipping Address (line {} {} {})".format( 258 | sys.exc_info()[-1].tb_lineno, type(e).__name__, e), "status": "error"}) 259 | time.sleep(self.error_delay) 260 | 261 | def get_PIE(self): 262 | headers = { 263 | "Accept": "*/*", 264 | "Accept-Encoding": "gzip, deflate, br", 265 | "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7", 266 | "Connection": "keep-alive", 267 | "Host": "securedataweb.walmart.com", 268 | "Referer": "https://www.walmart.com/", 269 | "User-Agent": settings.userAgent 270 | } 271 | profile = self.profile 272 | while True: 273 | self.status_signal.emit({"msg": "Getting Checkout Data", "status": "normal"}) 274 | try: 275 | r = self.session.get( 276 | "https://securedataweb.walmart.com/pie/v1/wmcom_us_vtg_pie/getkey.js?bust=" + str(int(time.time())), 277 | headers=headers) 278 | if r.status_code == 200: 279 | PIE_L = int(r.text.split("PIE.L = ")[1].split(";")[0]) 280 | PIE_E = int(r.text.split("PIE.E = ")[1].split(";")[0]) 281 | PIE_K = str(r.text.split('PIE.K = "')[1].split('";')[0]) 282 | PIE_key_id = str(r.text.split('PIE.key_id = "')[1].split('";')[0]) 283 | PIE_phase = int(r.text.split('PIE.phase = ')[1].split(';')[0]) 284 | card_data = w_e.encrypt(profile["card_number"], profile["card_cvv"], PIE_L, PIE_E, PIE_K, 285 | PIE_key_id, PIE_phase) 286 | self.status_signal.emit({"msg": "Got Checkout Data", "status": "normal"}) 287 | return card_data, PIE_key_id, PIE_phase 288 | self.status_signal.emit({"msg": "Error Getting Checkout Data", "status": "error"}) 289 | time.sleep(self.error_delay) 290 | except Exception as e: 291 | self.status_signal.emit({"msg": "Error Getting Checkout Data (line {} {} {})".format( 292 | sys.exc_info()[-1].tb_lineno, type(e).__name__, e), "status": "error"}) 293 | time.sleep(self.error_delay) 294 | 295 | def submit_payment(self, card_data, PIE_key_id, PIE_phase): 296 | headers = { 297 | "accept": "application/json", 298 | "accept-encoding": "gzip, deflate, br", 299 | "accept-language": "en-US,en;q=0.9", 300 | "content-type": "application/json", 301 | "origin": "https://www.walmart.com", 302 | "referer": "https://www.walmart.com/checkout/", 303 | "user-agent": settings.userAgent 304 | } 305 | # "inkiru_precedence": "false", 306 | profile = self.profile 307 | body = { 308 | "encryptedPan": card_data[0], 309 | "encryptedCvv": card_data[1], 310 | "integrityCheck": card_data[2], 311 | "keyId": PIE_key_id, 312 | "phase": PIE_phase, 313 | "state": profile["billing_state"], 314 | "postalCode": profile["billing_zipcode"], 315 | "addressLineOne": profile["billing_a1"], 316 | "addressLineTwo": profile["billing_a2"], 317 | "city": profile["billing_city"], 318 | "firstName": profile["billing_fname"], 319 | "lastName": profile["billing_lname"], 320 | "expiryMonth": profile["card_month"], 321 | "expiryYear": profile["card_year"], 322 | "phone": profile["billing_phone"], 323 | "cardType": profile["card_type"].upper(), 324 | "isGuest": True 325 | } 326 | while True: 327 | self.status_signal.emit({"msg": "Submitting Payment", "status": "normal"}) 328 | try: 329 | r = self.session.post("https://www.walmart.com/api/checkout-customer/:CID/credit-card", json=body, 330 | headers=headers) 331 | if r.status_code == 200: 332 | pi_hash = json.loads(r.text)["piHash"] 333 | self.status_signal.emit({"msg": "Submitted Payment", "status": "normal"}) 334 | return pi_hash 335 | self.status_signal.emit({"msg": "Error Submitting Payment", "status": "error"}) 336 | if self.check_browser(): 337 | return 338 | time.sleep(self.error_delay) 339 | except Exception as e: 340 | self.status_signal.emit({"msg": "Error Submitting Payment (line {} {} {})".format( 341 | sys.exc_info()[-1].tb_lineno, type(e).__name__, e), "status": "error"}) 342 | time.sleep(self.error_delay) 343 | 344 | def submit_billing(self, pi_hash): 345 | headers = { 346 | "accept": "application/json, text/javascript, */*; q=0.01", 347 | "accept-encoding": "gzip, deflate, br", 348 | "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7", 349 | "content-type": "application/json", 350 | "origin": "https://www.walmart.com", 351 | "referer": "https://www.walmart.com/checkout/", 352 | "user-agent": settings.userAgent, 353 | "wm_vertical_id": "0" 354 | } 355 | 356 | profile = self.profile 357 | card_data, PIE_key_id, PIE_phase = self.get_PIE() 358 | body = { 359 | "payments": [{ 360 | "paymentType": "CREDITCARD", 361 | "cardType": profile["card_type"].upper(), 362 | "firstName": profile["billing_fname"], 363 | "lastName": profile["billing_lname"], 364 | "addressLineOne": profile["billing_a1"], 365 | "addressLineTwo": profile["billing_a2"], 366 | "city": profile["billing_city"], 367 | "state": profile["billing_state"], 368 | "postalCode": profile["billing_zipcode"], 369 | "expiryMonth": profile["card_month"], 370 | "expiryYear": profile["card_year"], 371 | "email": profile["billing_email"], 372 | "phone": profile["billing_phone"], 373 | "encryptedPan": card_data[0], 374 | "encryptedCvv": card_data[1], 375 | "integrityCheck": card_data[2], 376 | "keyId": PIE_key_id, 377 | "phase": PIE_phase, 378 | "piHash": pi_hash 379 | }] 380 | } 381 | while True: 382 | self.status_signal.emit({"msg": "Submitting Billing", "status": "normal"}) 383 | try: 384 | r = self.session.post("https://www.walmart.com/api/checkout/v3/contract/:PCID/payment", json=body, 385 | headers=headers) 386 | if r.status_code == 200: 387 | try: 388 | r = json.loads(r.text) 389 | self.status_signal.emit({"msg": "Submitted Billing", "status": "normal"}) 390 | return 391 | except: 392 | pass 393 | self.status_signal.emit({"msg": "Error Submitting Billing", "status": "error"}) 394 | if self.check_browser(): 395 | return 396 | time.sleep(self.error_delay) 397 | except Exception as e: 398 | self.status_signal.emit({"msg": "Error Submitting Billing (line {} {} {})".format( 399 | sys.exc_info()[-1].tb_lineno, type(e).__name__, e), "status": "error"}) 400 | time.sleep(self.error_delay) 401 | 402 | def submit_order(self): 403 | headers = { 404 | "accept": "application/json, text/javascript, */*; q=0.01", 405 | "accept-encoding": "gzip, deflate, br", 406 | "accept-language": "en-US,en;q=0.9", 407 | "content-type": "application/json", 408 | "origin": "https://www.walmart.com", 409 | "referer": "https://www.walmart.com/checkout/", 410 | "user-agent": settings.userAgent, 411 | "wm_vertical_id": "0" 412 | } 413 | 414 | if settings.dont_buy is True: 415 | # TODO: this used to open the page up with everything filled out but only works for some users 416 | self.status_signal.emit({"msg":"Opening Checkout Page","status":"alt"}) 417 | self.handle_captcha("https://www.walmart.com/checkout/#/payment", close_window_after=False,redirect=True) # OPEN BROWSER TO SEE IF SHIT WORKED 418 | self.check_browser() 419 | return 420 | 421 | while True: 422 | self.status_signal.emit({"msg": "Submitting Order", "status": "alt"}) 423 | try: 424 | r = self.session.put("https://www.walmart.com/api/checkout/v3/contract/:PCID/order", json={}, 425 | headers=headers) 426 | try: 427 | json.loads(r.text)["order"] 428 | self.status_signal.emit({"msg": "Order Placed", "status": "success"}) 429 | send_webhook("OP", "Walmart", self.profile["profile_name"], self.task_id, self.product_image) 430 | return 431 | except: 432 | self.status_signal.emit({"msg": "Payment Failed", "status": "error"}) 433 | 434 | # open the page for checkout if failed to auto submit 435 | self.handle_captcha("https://www.walmart.com/checkout/#/payment") 436 | if self.check_browser(): 437 | return 438 | 439 | send_webhook("PF", "Walmart", self.profile["profile_name"], self.task_id, self.product_image) 440 | return 441 | except Exception as e: 442 | self.status_signal.emit({"msg": "Error Submitting Order (line {} {} {})".format( 443 | sys.exc_info()[-1].tb_lineno, type(e).__name__, e), "status": "error"}) 444 | time.sleep(self.error_delay) 445 | 446 | def check_browser(self): 447 | if settings.browser_on_failed: 448 | self.status_signal.emit( 449 | {"msg": "Browser Ready", "status": "alt", "url": "https://www.walmart.com/checkout/#/payment", 450 | "cookies": [{"name": cookie.name, "value": cookie.value, "domain": cookie.domain} for cookie in 451 | self.session.cookies]}) 452 | send_webhook("B", "Walmart", self.profile["profile_name"], self.task_id, self.product_image) 453 | return True 454 | return False 455 | 456 | def handle_captcha(self, url_to_open, close_window_after=True,redirect=False): #added redirect arg since captchas are lost when redirecting to the page that triggered them 457 | # this opens up chrome browser to get prompted with captcha 458 | options = webdriver.ChromeOptions() 459 | options.add_argument('--ignore-certificate-errors') #removes SSL errors from terminal 460 | options.add_experimental_option("excludeSwitches", ["enable-logging"]) #removes device adapter errors from terminal 461 | browser = webdriver.Chrome(ChromeDriverManager().install(),chrome_options=options) 462 | browser.get("https://www.walmart.com") 463 | 464 | # pass current session cookies to browser before loading url 465 | for c in self.session.cookies: 466 | browser.add_cookie({ 467 | 'name': c.name, 468 | 'value': c.value, 469 | 'domain': c.domain, 470 | 'path': c.path 471 | }) 472 | 473 | if redirect: 474 | browser.get(url_to_open) 475 | 476 | # lock the mutex and let the thread handler know we are awaiting 477 | # a gui event 478 | self.captcha_mutex.lock() 479 | self.wait_poll_signal.emit() 480 | try: 481 | # wait for condition to be released 482 | self.captcha_wait_condition.wait(self.captcha_mutex) 483 | finally: 484 | # unlock the thread 485 | self.captcha_mutex.unlock() 486 | 487 | for c in browser.get_cookies(): 488 | if c['name'] not in [x.name for x in self.session.cookies]: 489 | self.session.cookies.set(c['name'], c['value'], path=c['path'], domain=c['domain']) 490 | 491 | if close_window_after: 492 | browser.quit() 493 | 494 | def is_captcha(self, text): 495 | return '
' in text 496 | 497 | # TODO: when running with headless == False it would be good to quit browsers when task is stopped (might be good to keep it open if it errors out however for diagnostics) 498 | # def stop(self): 499 | # self.browser.quit() 500 | -------------------------------------------------------------------------------- /theming/styleHelper.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is to help the process of creating style variables 3 | 1. Add a variable to global styles and set it to the current value that you want to replace 4 | 2. Run this file from the root directory and it will link the color to the variable. There can be a conflict and it will alert you to them 5 | 3. Profit!! 6 | */ 7 | const fs = require('fs') 8 | 9 | // Read in the globalStyles python dict and add it to the JS global scope 10 | let importedPythonStyleDict = fs.readFileSync('theming/styles.py',{encoding:'utf-8'}) 11 | eval(importedPythonStyleDict) 12 | 13 | 14 | 15 | const files = fs.readdirSync('./pages') 16 | .filter(s => s.includes('.py')) 17 | .map(fileName => `./pages/${fileName}`) 18 | 19 | files.push('./app.py') 20 | 21 | files.forEach(doModifications) 22 | 23 | 24 | function doModifications(filePath) { 25 | let data = fs.readFileSync(filePath,{encoding:'utf-8'}) 26 | if(!data.includes('from theming.styles import globalStyles')) { 27 | data = 'from theming.styles import globalStyles\n' + data 28 | } 29 | 30 | for(let variableReplace in globalStyles) { 31 | const styleTarget = globalStyles[variableReplace] 32 | 33 | const regex = new RegExp(`${styleTarget}.+`,'g') 34 | const matches = data.match(regex) 35 | 36 | if(!matches) { 37 | //console.log(`NO MATCHES FOUND IN ${filePath} for ${variableReplace}`) 38 | continue 39 | } 40 | 41 | matches.forEach(str => { 42 | if(str.includes('format')) { 43 | console.log(`assistance needed in file ${filePath}`,str) 44 | return 45 | } 46 | 47 | let replace = (str.substr(0,str.length-1) + `.format(globalStyles["${variableReplace}"]))`) 48 | .replace(styleTarget,'{}') 49 | 50 | data = data.replace(str,replace) 51 | }) 52 | } 53 | 54 | 55 | fs.writeFileSync(filePath,data,{encoding:'utf-8'}) 56 | } 57 | -------------------------------------------------------------------------------- /theming/styles.py: -------------------------------------------------------------------------------- 1 | globalStyles = { 2 | "backgroundLight": "#232323", 3 | "backgroundDark":"#1E1E1E", 4 | "primaryAscent": "#402721", 5 | "primary":"#E22F09", 6 | "cartedColor":"#ccc61e" 7 | } 8 | 9 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from datetime import datetime 3 | import hashlib 4 | import json 5 | import platform 6 | import random 7 | import string 8 | import os 9 | import pathlib 10 | 11 | from Crypto import Random 12 | from Crypto.Cipher import AES 13 | from colorama import Fore, init 14 | 15 | import settings 16 | from webhook import DiscordEmbed, DiscordWebhook 17 | 18 | normal_color = Fore.CYAN 19 | 20 | 21 | def write_data(path, data): 22 | with open(path, "w") as file: 23 | json.dump(data, file) 24 | 25 | 26 | # TODO: Enable this as an app setting for user to choose their own optional key & regenerate key on the fly button 27 | try: 28 | with open("./data/vault.json", "r") as file: 29 | keys = json.load(file) 30 | except FileNotFoundError: 31 | generateKeySecret = "".join(random.choices(string.ascii_letters + string.digits, k=16)) 32 | write_data("./data/vault.json", [{"generated_key_secret": generateKeySecret}]) 33 | with open("./data/vault.json", "r") as file: 34 | keys = json.load(file) 35 | 36 | e_key = keys[0]['generated_key_secret'].encode() 37 | BLOCK_SIZE = 16 38 | if platform.system() == "Windows": 39 | init(convert=True) 40 | else: 41 | init() 42 | 43 | print(normal_color + "Welcome To Phoenix Bot - In times of crisis, " 44 | "the wise build bridges while the foolish build barriers.") 45 | 46 | 47 | class BirdLogger: 48 | @staticmethod 49 | def ts(): 50 | return str(datetime.now())[:-7] 51 | 52 | def normal(self, task_id, msg): 53 | print(normal_color + "[{}][TASK {}] {}".format(self.ts(), task_id, msg)) 54 | 55 | def alt(self, task_id, msg): 56 | print(Fore.MAGENTA + "[{}][TASK {}] {}".format(self.ts(), task_id, msg)) 57 | 58 | def error(self, task_id, msg): 59 | print(Fore.RED + "[{}][TASK {}] {}".format(self.ts(), task_id, msg)) 60 | 61 | def success(self, task_id, msg): 62 | print(Fore.GREEN + "[{}][TASK {}] {}".format(self.ts(), task_id, msg)) 63 | 64 | 65 | class Encryption: 66 | def encrypt(self, msg): 67 | IV = Random.new().read(BLOCK_SIZE) 68 | aes = AES.new(self.trans(e_key), AES.MODE_CFB, IV) 69 | return base64.b64encode(IV + aes.encrypt(msg.encode("utf-8"))) 70 | 71 | def decrypt(self, msg): 72 | msg = base64.b64decode(msg) 73 | IV = msg[:BLOCK_SIZE] 74 | aes = AES.new(self.trans(e_key), AES.MODE_CFB, IV) 75 | return aes.decrypt(msg[BLOCK_SIZE:]) 76 | 77 | @staticmethod 78 | def trans(key): 79 | return hashlib.md5(key).digest() 80 | 81 | 82 | def return_data(path): 83 | try: 84 | with open(path, "r") as file: 85 | data = json.load(file) 86 | return data 87 | except FileNotFoundError: 88 | write_data(path, []) 89 | with open(path, "r") as file: 90 | data = json.load(file) 91 | return data 92 | 93 | 94 | def validate_data(test_data, control_data): 95 | return test_data.keys() == control_data.keys() 96 | 97 | 98 | def data_exists(path): 99 | try: 100 | open(path, "r") 101 | return True 102 | except FileNotFoundError: 103 | return False 104 | 105 | 106 | def get_profile(profile_name): 107 | profiles = return_data("./data/profiles.json") 108 | for p in profiles: 109 | if p["profile_name"] == profile_name: 110 | try: 111 | p["card_number"] = (Encryption().decrypt(p["card_number"].encode("utf-8"))).decode("utf-8") 112 | except ValueError: 113 | pass 114 | return p 115 | return None 116 | 117 | 118 | def get_proxy(list_name): 119 | if list_name == "Proxy List" or list_name == "None": 120 | return False 121 | proxies = return_data("./data/proxies.json") 122 | for proxy_list in proxies: 123 | if proxy_list["list_name"] == list_name: 124 | return format_proxy(random.choice(proxy_list["proxies"].splitlines())) 125 | return None 126 | 127 | 128 | def format_proxy(proxy): 129 | try: 130 | proxy_parts = proxy.split(":") 131 | ip, port, user, passw = proxy_parts[0], proxy_parts[1], proxy_parts[2], proxy_parts[3] 132 | return { 133 | "http": "http://{}:{}@{}:{}".format(user, passw, ip, port), 134 | "https": "https://{}:{}@{}:{}".format(user, passw, ip, port) 135 | } 136 | except IndexError: 137 | return {"http": "http://" + proxy, "https": "https://" + proxy} 138 | 139 | 140 | def send_webhook(webhook_type, site, profile, task_id, image_url): 141 | if settings.webhook != "": 142 | webhook = DiscordWebhook(url=settings.webhook, username="Phoenix Bot", 143 | avatar_url="https://i.imgur.com/60G42xE.png") 144 | if webhook_type == "OP": 145 | if not settings.webhook_on_order: 146 | return 147 | embed = DiscordEmbed(title="Order Placed", color=0x34c693) 148 | elif webhook_type == "B": 149 | if not settings.webhook_on_browser: 150 | return 151 | embed = DiscordEmbed(title="Complete Order in Browser", color=0xf2a689) 152 | elif webhook_type == "PF": 153 | if not settings.webhook_on_failed: 154 | return 155 | embed = DiscordEmbed(title="Payment Failed", color=0xfc5151) 156 | embed.set_footer(text="Via Phoenix Bot", icon_url="https://i.imgur.com/60G42xE.png") 157 | embed.add_embed_field(name="Site", value=site, inline=True) 158 | embed.add_embed_field(name="Profile", value=profile, inline=True) 159 | embed.add_embed_field(name="Task ID", value=task_id, inline=True) 160 | embed.set_thumbnail(url=image_url) 161 | webhook.add_embed(embed) 162 | try: 163 | webhook.execute() 164 | except: 165 | pass 166 | 167 | 168 | def random_delay(delay, start, stop): 169 | """ 170 | Returns the delay argument combined with a random number between start 171 | and stop divided by 1000. 172 | """ 173 | return delay + (random.randint(int(start), int(stop)) / 1000) 174 | 175 | 176 | def create_msg(msg, status): 177 | return {"msg": msg, "status": status} 178 | 179 | def log_webpage(path, output_type, data): 180 | cwd = pathlib.Path().parent.resolve() 181 | 182 | try: 183 | os.makedirs(str(cwd) + "/output/errors") 184 | except FileExistsError: 185 | pass 186 | 187 | try: 188 | os.makedirs(str(cwd) + "/output/success") 189 | except FileExistsError: 190 | pass 191 | 192 | filename = f"{cwd}/output/{path}/{output_type}_{datetime.strftime(datetime.now(),'%Y-%m-%d_%H-%M-%S')}.html" 193 | 194 | with open(filename, "w+",encoding="utf-8") as fp: 195 | fp.write(data) 196 | fp.close() 197 | return filename 198 | -------------------------------------------------------------------------------- /utils/json_utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | # https://github.com/Hari-Nagarajan/nvidia-bot/blob/master/utils/json_utils.py 5 | 6 | def find_values(json_repr, id): 7 | results = [] 8 | 9 | def _decode_dict(a_dict): 10 | try: 11 | results.append(a_dict[id]) 12 | except KeyError: 13 | pass 14 | return a_dict 15 | 16 | json.loads(json_repr, object_hook=_decode_dict) # Return value ignored. 17 | return results 18 | 19 | 20 | class InvalidAutoBuyConfigException(Exception): 21 | def __init__(self, message): 22 | super().__init__(message) 23 | -------------------------------------------------------------------------------- /utils/selenium_utils.py: -------------------------------------------------------------------------------- 1 | from chromedriver_py import binary_path as driver_path 2 | from selenium.webdriver import DesiredCapabilities 3 | from selenium.webdriver import Chrome, ChromeOptions # TODO: Combine these two dependencies. Leaving it for now since it touches too many sites atm. 4 | from selenium.webdriver.chrome.options import Options 5 | from selenium.webdriver.common.action_chains import ActionChains 6 | from selenium.webdriver.common.by import By 7 | from selenium.webdriver.support import expected_conditions as ec 8 | from selenium.webdriver.support.wait import WebDriverWait 9 | from utils import create_msg 10 | import random, re, requests, string, threading, settings 11 | 12 | # https://github.com/Hari-Nagarajan/nvidia-bot/blob/master/utils/selenium_utils.py 13 | 14 | options = Options() 15 | options.add_experimental_option( 16 | "excludeSwitches", ["enable-automation", "enable-logging"], 17 | ) 18 | options.add_experimental_option("useAutomationExtension", False) 19 | 20 | 21 | class AnyEc: 22 | """Use with WebDriverWait to combine expected_conditions 23 | in an OR. 24 | """ 25 | 26 | def __init__(self, *args): 27 | self.ecs = args 28 | 29 | def __call__(self, driver): 30 | for fn in self.ecs: 31 | try: 32 | if fn(driver): 33 | return True 34 | except: 35 | pass 36 | 37 | 38 | def no_amazon_image(): 39 | prefs = {"profile.managed_default_content_settings.images": 2} 40 | options.add_experimental_option("prefs", prefs) 41 | 42 | 43 | def yes_amazon_image(): 44 | prefs = {"profile.managed_default_content_settings.images": 0} 45 | options.add_experimental_option("prefs", prefs) 46 | 47 | 48 | def wait_for_element(d, e_id, time=30): 49 | """ 50 | Uses webdriver(d) to wait for page title(title) to become visible 51 | """ 52 | return WebDriverWait(d, time).until(ec.presence_of_element_located((By.ID, e_id))) 53 | 54 | 55 | def wait_for_element_by_xpath(d, e_path, time=30): 56 | return WebDriverWait(d, time).until( 57 | ec.presence_of_element_located((By.XPATH, e_path)) 58 | ) 59 | 60 | 61 | def wait_for_element_by_class(d, e_class, time=30): 62 | """ 63 | Uses webdriver(d) to wait for page title(title) to become visible 64 | """ 65 | return WebDriverWait(d, time).until( 66 | ec.presence_of_element_located((By.CLASS_NAME, e_class)) 67 | ) 68 | 69 | 70 | def wait_for_title(d, title, path): 71 | """ 72 | Uses webdriver(d) to navigate to get(path) until it equals title(title) 73 | """ 74 | while d.title != title: 75 | d.get(path) 76 | WebDriverWait(d, 1000) 77 | 78 | 79 | def wait_for_page(d, title, time=30): 80 | """ 81 | Uses webdriver(d) to wait for page title(title) to become visible 82 | """ 83 | WebDriverWait(d, time).until(ec.title_is(title)) 84 | 85 | 86 | def wait_for_either_title(d, title1, title2, time=30): 87 | """ 88 | Uses webdriver(d) to wait for page title(title1 or title2) to become visible 89 | """ 90 | try: 91 | WebDriverWait(d, time).until(AnyEc(ec.title_is(title1), ec.title_is(title2))) 92 | except Exception: 93 | pass 94 | 95 | 96 | def wait_for_any_title(d, titles, time=30): 97 | """ 98 | Uses webdriver(d) to wait for page title(any in the list of titles) to become visible 99 | """ 100 | WebDriverWait(d, time).until(AnyEc(*[ec.title_is(title) for title in titles])) 101 | 102 | 103 | def button_click_using_xpath(d, xpath): 104 | """ 105 | Uses webdriver(d) to click a button using an XPath(xpath) 106 | """ 107 | button_menu = WebDriverWait(d, 10).until( 108 | ec.element_to_be_clickable((By.XPATH, xpath)) 109 | ) 110 | action = ActionChains(d) 111 | action.move_to_element(button_menu).pause(1).click().perform() 112 | 113 | 114 | def field_send_keys(d, field, keys): 115 | """ 116 | Uses webdriver(d) to fiend a field(field), clears it and sends keys(keys) 117 | """ 118 | elem = d.find_element_by_name(field) 119 | elem.clear() 120 | elem.send_keys(keys) 121 | 122 | 123 | def has_class(element, class_name): 124 | classes = element.get_attribute("class") 125 | 126 | return class_name in classes 127 | 128 | 129 | def add_cookies_to_session_from_driver(driver, session): 130 | cookies = driver.get_cookies() 131 | 132 | [ 133 | session.cookies.set_cookie( 134 | requests.cookies.create_cookie( 135 | domain=cookie["domain"], 136 | name=cookie["name"], 137 | value=cookie["value"], 138 | ) 139 | ) 140 | for cookie in cookies 141 | ] 142 | 143 | 144 | def enable_headless(): 145 | options.add_argument("--headless") 146 | options.add_argument("--no-sandbox") 147 | options.add_argument("--disable-dev-shm-usage") 148 | 149 | return options 150 | 151 | 152 | # https://stackoverflow.com/questions/33225947/can-a-website-detect-when-you-are-using-selenium-with-chromedriver 153 | 154 | 155 | def change_driver(status_signal, loc): 156 | fin = open(loc, 'rb') 157 | data = fin.read() 158 | val = "$" + "".join(random.choices(string.ascii_lowercase, k=3)) + "_" + \ 159 | "".join(random.choices(string.ascii_letters + string.digits, k=22)) + "_" 160 | 161 | result = re.search(b"[$][a-z]{3}_[a-zA-Z0-9]{22}_", data) 162 | 163 | if result is not None: 164 | try: 165 | status_signal.emit(create_msg("Changing value in Chromedriver", "normal")) 166 | data = data.replace(result.group(0), val.encode()) 167 | fin.close() 168 | fin = open(loc, 'wb') 169 | fin.truncate() 170 | fin.write(data) 171 | fin.close() 172 | except: 173 | status_signal.emit(create_msg("Error modifying chromedriver", "error")) 174 | else: 175 | fin.close() 176 | 177 | 178 | def open_browser(link, cookies): 179 | threading.Thread(target=start_browser, args=(link, cookies)).start() 180 | 181 | 182 | def start_browser(link, cookies): 183 | caps = DesiredCapabilities().CHROME 184 | caps["pageLoadStrategy"] = "eager" 185 | chrome_options = ChromeOptions() 186 | chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"]) 187 | chrome_options.add_experimental_option("useAutomationExtension", False) 188 | driver = Chrome(desired_capabilities=caps, executable_path=driver_path, options=chrome_options) 189 | driver.execute_cdp_cmd( 190 | "Page.addScriptToEvaluateOnNewDocument", 191 | { 192 | "source": """ 193 | Object.defineProperty(window, 'navigator', { 194 | value: new Proxy(navigator, { 195 | has: (target, key) => (key === 'webdriver' ? false : key in target), 196 | get: (target, key) => 197 | key === 'webdriver' 198 | ? undefined 199 | : typeof target[key] === 'function' 200 | ? target[key].bind(target) 201 | : target[key] 202 | }) 203 | }) 204 | """ 205 | }, 206 | ) 207 | driver.get(link) 208 | for cookie in cookies: 209 | driver.add_cookie({ 210 | "name": cookie["name"], 211 | "value": cookie["value"], 212 | "domain": cookie["domain"] 213 | }) 214 | driver.get(link) 215 | -------------------------------------------------------------------------------- /webhook.py: -------------------------------------------------------------------------------- 1 | import requests, time, datetime, json 2 | 3 | 4 | # modified from https://github.com/lovvskillz/python-discord-webhook 5 | 6 | class DiscordWebhook: 7 | def __init__(self, url, **kwargs): 8 | self.url = url 9 | self.content = kwargs.get("content") 10 | self.username = kwargs.get("username") 11 | self.avatar_url = kwargs.get("avatar_url") 12 | self.tts = kwargs.get("tts", False) 13 | self.files = kwargs.get("files", dict()) 14 | self.embeds = kwargs.get("embeds", []) 15 | self.proxies = kwargs.get("proxies", None) 16 | 17 | def add_file(self, file, filename): 18 | self.files["_{}".format(filename)] = (filename, file) 19 | 20 | def add_embed(self, embed): 21 | self.embeds.append(embed.__dict__ if isinstance(embed, DiscordEmbed) else embed) 22 | 23 | def remove_embed(self, index): 24 | self.embeds.pop(index) 25 | 26 | def get_embeds(self): 27 | return self.embeds 28 | 29 | def set_proxies(self, proxies): 30 | self.proxies = proxies 31 | 32 | @property 33 | def json(self): 34 | data = dict() 35 | embeds = self.embeds 36 | self.embeds = list() 37 | for embed in embeds: 38 | self.add_embed(embed) 39 | for key, value in self.__dict__.items(): 40 | if value and key not in ["url", "files", "filename"]: 41 | data[key] = value 42 | embeds_empty = all(not embed for embed in data["embeds"]) if "embeds" in data else True 43 | return data 44 | 45 | def execute(self): 46 | if bool(self.files) is False: 47 | response = requests.post(self.url, json=self.json, proxies=self.proxies) 48 | else: 49 | self.files["payload_json"] = (None, json.dumps(self.json)) 50 | response = requests.post(self.url, files=self.files, proxies=self.proxies) 51 | 52 | 53 | class DiscordEmbed: 54 | def __init__(self, **kwargs): 55 | self.title = kwargs.get("title") 56 | self.description = kwargs.get("description") 57 | self.url = kwargs.get("url") 58 | self.timestamp = kwargs.get("timestamp") 59 | self.color = kwargs.get("color") 60 | self.footer = kwargs.get("footer") 61 | self.image = kwargs.get("image") 62 | self.thumbnail = kwargs.get("thumbnail") 63 | self.video = kwargs.get("video") 64 | self.provider = kwargs.get("provider") 65 | self.author = kwargs.get("author") 66 | self.fields = kwargs.get("fields", []) 67 | 68 | def set_title(self, title): 69 | self.title = title 70 | 71 | def set_description(self, description): 72 | self.description = description 73 | 74 | def set_url(self, url): 75 | self.url = url 76 | 77 | def set_timestamp(self, timestamp=str(datetime.datetime.utcfromtimestamp(time.time()))): 78 | self.timestamp = timestamp 79 | 80 | def set_color(self, color): 81 | self.color = color 82 | 83 | def set_footer(self, **kwargs): 84 | self.footer = { 85 | "text": kwargs.get("text"), 86 | "icon_url": kwargs.get("icon_url"), 87 | "proxy_icon_url": kwargs.get("proxy_icon_url") 88 | } 89 | 90 | def set_image(self, **kwargs): 91 | self.image = { 92 | "url": kwargs.get("url"), 93 | "proxy_url": kwargs.get("proxy_url"), 94 | "height": kwargs.get("height"), 95 | "width": kwargs.get("width"), 96 | } 97 | 98 | def set_thumbnail(self, **kwargs): 99 | self.thumbnail = { 100 | "url": kwargs.get("url"), 101 | "proxy_url": kwargs.get("proxy_url"), 102 | "height": kwargs.get("height"), 103 | "width": kwargs.get("width"), 104 | } 105 | 106 | def set_video(self, **kwargs): 107 | self.video = { 108 | "url": kwargs.get("url"), 109 | "height": kwargs.get("height"), 110 | "width": kwargs.get("width"), 111 | } 112 | 113 | def set_provider(self, **kwargs): 114 | self.provider = { 115 | "name": kwargs.get("name"), 116 | "url": kwargs.get("url"), 117 | } 118 | 119 | def set_author(self, **kwargs): 120 | self.author = { 121 | "name": kwargs.get("name"), 122 | "url": kwargs.get("url"), 123 | "icon_url": kwargs.get("icon_url"), 124 | "proxy_icon_url": kwargs.get("proxy_icon_url"), 125 | } 126 | 127 | def add_embed_field(self, **kwargs): 128 | self.fields.append({ 129 | "name": kwargs.get("name"), 130 | "value": kwargs.get("value"), 131 | "inline": kwargs.get("inline", True) 132 | }) 133 | 134 | def del_embed_field(self, index): 135 | self.fields.pop(index) 136 | 137 | def get_embed_fields(self): 138 | return self.fields 139 | --------------------------------------------------------------------------------