├── .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 |
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 |
--------------------------------------------------------------------------------