mirror of
https://gitea.augustin64.fr/piair/MsRewards-Reborn.git
synced 2025-07-11 23:36:36 +02:00
Compare commits
243 Commits
b6e30888bb
...
v5.10.8
Author | SHA1 | Date | |
---|---|---|---|
ecd5a9f204 | |||
2b1c7b985e | |||
c9c93e9828 | |||
1a4ed4f4e7 | |||
0ba92798ad | |||
4aef2bf948 | |||
68395f4314 | |||
2ca2779ec9 | |||
5ef8d8b7ca | |||
f2d08e9137 | |||
d946298a38 | |||
ed1e91b304 | |||
710c272659 | |||
6f325c5a6a | |||
3b82419d4d | |||
9e0514a902 | |||
5c8730dc26 | |||
d11ae4055c | |||
2dc8cda167 | |||
fbb2feae31 | |||
8d53a2b30b | |||
f8f3ff2382 | |||
024b556f5e | |||
b4c9204bf2 | |||
f810f0ee3c | |||
afac7e9539 | |||
ae12c18270 | |||
dfd51bc040 | |||
12a6968ee0 | |||
8726785dc8 | |||
e9f629dee4 | |||
c9a838d368 | |||
808916ddd3 | |||
c51c463338 | |||
a22d1e6ddb | |||
93bb634f7c | |||
31426a1dfd | |||
f862af8699 | |||
78e7342acf | |||
4d8157ba1e | |||
661566aade | |||
eb2b9dc2d3 | |||
3385540350 | |||
0588180dda | |||
9995bc8e25 | |||
98ff0a183a | |||
d579a2c160 | |||
ccf284f6e2 | |||
f30832d8cd | |||
799d3d67d5 | |||
f10cd8d226 | |||
e41d28c142 | |||
b0c6a93951 | |||
012e923ab5 | |||
0cb0521da6 | |||
c5beafe036 | |||
098b934e96 | |||
339775bdf4 | |||
a2b07b9fcd | |||
d5bacd99a1 | |||
43035e115d | |||
c1bbb26c26 | |||
95156bacd8 | |||
f7c6d3f65e | |||
4ab2530f98 | |||
3ca16d1f37 | |||
db4ab3bf90 | |||
6fefaca00a | |||
a2328c2ca7 | |||
37f002049a | |||
8f655a04fb | |||
860c7b536a | |||
e93d4f0baf | |||
a72c74ec05 | |||
5282b4b434 | |||
0263f2e4c1 | |||
3324fa478d | |||
d14e0efad9 | |||
16ddd7aae9 | |||
66de4dbbd2 | |||
27237354b2 | |||
484a9692cf | |||
ebb1847a51 | |||
80f6cbc919 | |||
dff47887bc | |||
d0c78d7db1 | |||
ebd22102ef | |||
6ce85286dd | |||
db557c2e3c | |||
2888f1d761 | |||
84898cee76 | |||
cef0204868 | |||
93600dd78a | |||
7e64604e9f | |||
c28c2c573d | |||
1a4cd03ae1 | |||
87195de1e5 | |||
4631a6608c | |||
904ad83f36 | |||
cd5ce0f6c1 | |||
e28660ea7d | |||
bad8be5d1f | |||
87b47e97fd | |||
442dbb08d9 | |||
05b88945ed | |||
3770fa6451 | |||
cfbe5d7af8 | |||
510c7f7251 | |||
5b95893bf0 | |||
5e3282f873 | |||
deb77cd3d3 | |||
dda61eaef9 | |||
8943c0e15d | |||
90bcd29d36 | |||
bb5dbb3cbd | |||
ab8ac50fb2 | |||
994302d28d | |||
4723049615 | |||
dd7b62dc92 | |||
992465c562 | |||
fbc8dac0e9 | |||
ecfda92584 | |||
e4f6677da8 | |||
9f3ccafee8 | |||
5d64ebdb29 | |||
45cd381c3a | |||
ed47bae6b6 | |||
e7f375650d | |||
ad513bfe9c | |||
fe772048de | |||
08c069ca1e | |||
141e896cab | |||
05013cea75 | |||
26c83c38c6 | |||
347ba86a87 | |||
24b2af9d10 | |||
dd38060253 | |||
b0cdc9b779 | |||
31ec4a1200 | |||
48b4128632 | |||
b9adb9c59c | |||
ca0ae8917a | |||
80286b6a05 | |||
ebb1a8e48f | |||
f4ebb39fdf | |||
da5e60f8b7 | |||
0552338c22 | |||
0aa7fd3ec0 | |||
1be75f1987 | |||
5d4dd859bd | |||
4f21ef22d4 | |||
a19e40b7e5 | |||
81dc0f7310 | |||
437afdcce4 | |||
decd289994 | |||
3c5fb6e702 | |||
2f6b7f2ab9 | |||
741a136b12 | |||
24849a9418 | |||
eae66cb163 | |||
c3fc698e55 | |||
35db54edfc | |||
2fdfdb7410 | |||
368523135a | |||
3189359b81 | |||
bf675f8c96 | |||
e7584702a7 | |||
c651950e7c | |||
7ad71a1cf9 | |||
8d15a555e8 | |||
afec68a23e | |||
545eea48cc | |||
f668c49e09 | |||
11eaa5189a | |||
d84ae8dd57 | |||
c9aa75e61c | |||
945497c1d4 | |||
7882e1dbb3 | |||
92aa56aae0 | |||
4a434533a7 | |||
c665144b28 | |||
0af267e628 | |||
2709d7058a | |||
b28119baec | |||
3155b545dc | |||
f8e9daaedd | |||
c71aad15df | |||
a7d32b6064 | |||
941656100c | |||
170627f5a7 | |||
6796e71c5b | |||
1505e7b756 | |||
7c4de0ff5d | |||
8ab6c41200 | |||
ecd695f45c | |||
ac737cd048 | |||
48a1ca4617 | |||
b882d0deba | |||
ae3a89802b | |||
7137448f65 | |||
cd94f65045 | |||
a8dacf85d1 | |||
d157423bf7 | |||
fd69c86a86 | |||
e2a917925e | |||
f1b6232841 | |||
6526d83e27 | |||
14d15c4dea | |||
677b80f521 | |||
65f2705726 | |||
09fd450c59 | |||
fdcc34c054 | |||
db5973cf87 | |||
cec5505f3c | |||
ada8b95259 | |||
62f9410373 | |||
23a7131792 | |||
49cf33c780 | |||
9f5e722a9c | |||
6290fde32e | |||
3d90fa64bb | |||
1e54e8af9a | |||
43356e032a | |||
f357623a03 | |||
4d907ba81b | |||
950f8835c3 | |||
fd7feebca5 | |||
b8690dfd05 | |||
7789c89d64 | |||
dff9af6125 | |||
6d73ba94e0 | |||
dac3977c71 | |||
42ad01e493 | |||
d4d3213db1 | |||
a1933447f0 | |||
20f6eb6796 | |||
43e970a1aa | |||
994af56880 | |||
712cc14368 | |||
a1969a3e2e | |||
da33edb381 | |||
dea767a392 | |||
da2471f304 |
10
.gitignore
vendored
10
.gitignore
vendored
@ -1,10 +1,14 @@
|
|||||||
/old
|
/old
|
||||||
geckodriver.log
|
geckodriver.log
|
||||||
config
|
|
||||||
.vscode/
|
.vscode/
|
||||||
update.sh
|
|
||||||
/Git
|
/Git
|
||||||
page.html
|
page.html
|
||||||
screenshot.png
|
screenshot.png
|
||||||
login.csv
|
login.csv
|
||||||
requirements.txt
|
data
|
||||||
|
**/__pycache__
|
||||||
|
/user_data
|
||||||
|
install.sh
|
||||||
|
nohup.out
|
||||||
|
points.csv
|
||||||
|
/dev
|
||||||
|
127
CODE_OF_CONDUCT.md
Normal file
127
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at piair338@gmail.com.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
34
Dockerfile
34
Dockerfile
@ -4,43 +4,35 @@ ENV DEBIAN_FRONTEND noninteractive
|
|||||||
ENV GECKODRIVER_VER v0.31.0
|
ENV GECKODRIVER_VER v0.31.0
|
||||||
ENV FIREFOX_VER 87.0
|
ENV FIREFOX_VER 87.0
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& apt update \
|
&& apt update \
|
||||||
&& apt upgrade -y \
|
&& apt upgrade -y \
|
||||||
&& apt install -y \
|
&& apt install -y \
|
||||||
firefox-esr \
|
tzdata \
|
||||||
wfrench \
|
wfrench \
|
||||||
git \
|
git \
|
||||||
&& pip install \
|
|
||||||
requests \
|
|
||||||
selenium \
|
|
||||||
argparse \
|
|
||||||
discord \
|
|
||||||
configparser \
|
|
||||||
asyncio \
|
|
||||||
enquiries \
|
|
||||||
mysql-connector \
|
|
||||||
&& git clone https://github.com/piair338/MsReward
|
|
||||||
|
|
||||||
# Add latest FireFox
|
|
||||||
RUN set -x \
|
|
||||||
&& apt install -y \
|
|
||||||
libx11-xcb1 \
|
libx11-xcb1 \
|
||||||
libdbus-glib-1-2 \
|
libdbus-glib-1-2 \
|
||||||
|
libasound2 \
|
||||||
|
libgtk-3-0 \
|
||||||
|
xvfb \
|
||||||
|
nano \
|
||||||
|
tigervnc-standalone-server \
|
||||||
|
&& git clone https://github.com/piair338/MsRewards \
|
||||||
|
&& ln -fs /usr/share/zoneinfo/Europe/Paris /etc/localtime \
|
||||||
|
&& pip install -r MsRewards/requirements.txt \
|
||||||
&& curl -sSLO https://download-installer.cdn.mozilla.net/pub/firefox/releases/91.9.1esr/linux-x86_64/en-US/firefox-91.9.1esr.tar.bz2 \
|
&& curl -sSLO https://download-installer.cdn.mozilla.net/pub/firefox/releases/91.9.1esr/linux-x86_64/en-US/firefox-91.9.1esr.tar.bz2 \
|
||||||
&& tar -jxf firefox-* \
|
&& tar -jxf firefox-* \
|
||||||
&& mv firefox /opt/ \
|
&& mv firefox /opt/ \
|
||||||
&& chmod 755 /opt/firefox \
|
&& chmod 755 /opt/firefox \
|
||||||
&& chmod 755 /opt/firefox/firefox
|
&& chmod 755 /opt/firefox/firefox \
|
||||||
|
&& ln -s /opt/firefox/firefox /usr/bin/firefox \
|
||||||
# Add geckodriver
|
|
||||||
RUN set -x \
|
|
||||||
&& curl -sSLO https://github.com/mozilla/geckodriver/releases/download/${GECKODRIVER_VER}/geckodriver-${GECKODRIVER_VER}-linux64.tar.gz \
|
&& curl -sSLO https://github.com/mozilla/geckodriver/releases/download/${GECKODRIVER_VER}/geckodriver-${GECKODRIVER_VER}-linux64.tar.gz \
|
||||||
&& tar zxf geckodriver-*.tar.gz \
|
&& tar zxf geckodriver-*.tar.gz \
|
||||||
&& mv geckodriver /usr/bin/
|
&& mv geckodriver /usr/bin/
|
||||||
|
|
||||||
|
|
||||||
WORKDIR /app/MsReward
|
ENV TZ="Europe/Paris"
|
||||||
|
WORKDIR /app/MsRewards
|
||||||
CMD python main.py
|
CMD python main.py
|
||||||
|
|
||||||
|
103
README.md
103
README.md
@ -1,39 +1,92 @@
|
|||||||
# MsReward
|
# MsReward
|
||||||
|
|
||||||
|
|
||||||
A Microsoft reward automator, designed to work headless on a raspberry pi. Tested with a pi 3b+ and a pi 4 2Gb .
|
A Microsoft reward automator, designed to work headless on any x64 server.
|
||||||
Using a discord webhook or SQL to log points everydays.
|
Using a discord webhook or SQL to log points everyday.
|
||||||
Using Selenium and geckodriver.
|
Using Selenium and Geckodriver.
|
||||||
|
# Installation instruction
|
||||||
|
Make sure your contry is [supported by MS Rewards](https://support.microsoft.com/en-us/topic/microsoft-rewards-regions-9795ec47-c0f4-a33e-aede-738903359d63).
|
||||||
|
Create 5 Microsoft accounts, that the programm will use.
|
||||||
|
- [linux](#linux)
|
||||||
|
- [Windows](#windows)
|
||||||
|
- [MacOS](#macos)
|
||||||
|
- [Database configuration](#database)
|
||||||
|
- [Options](#options)
|
||||||
|
- [Flags](#flags)
|
||||||
|
## Linux
|
||||||
|
|
||||||
## If you're using docker (way easier)
|
### Using docker (Recommended)
|
||||||
to use docker, run
|
Make sure that [docker](https://docs.docker.com/get-docker/) and [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) are installed.
|
||||||
|
Clone the repository:
|
||||||
```
|
```
|
||||||
docker build Dockerfile
|
git clone https://github.com/piair338/MsRewards
|
||||||
#copy the build id
|
|
||||||
docker run -ti --name MsRewards [build id]
|
|
||||||
```
|
```
|
||||||
Then, fill the config and start the programm everydays with
|
Build the docker container:
|
||||||
```
|
```
|
||||||
docker start MsRewards
|
cd MsRewards
|
||||||
|
sudo docker build -t msrewards .
|
||||||
|
```
|
||||||
|
Run the app for the first time to configure it.
|
||||||
|
```
|
||||||
|
sudo docker run -ti --name MsRewards msrewards
|
||||||
|
```
|
||||||
|
Then to run it everyday, you can use cron and add the line:
|
||||||
|
```
|
||||||
|
10 10 * * * sudo docker start MsRewards
|
||||||
```
|
```
|
||||||
|
|
||||||
## Other configuration
|
### **not** using docker (not recommended)
|
||||||
|
**This is only a recommandation and shouldn't be used !**
|
||||||
|
```
|
||||||
|
sudo apt-get install xdg-utils libdbus-glib-1-2 bzip2 wfrench tigervnc-standalone-server xvnc -y
|
||||||
|
|
||||||
To use the database, I recommand MySql, Create a database with the name you want and create a table `daily`, like the one from the image :
|
curl -sSLO https://download-installer.cdn.mozilla.net/pub/firefox/releases/91.9.1esr/linux-x86_64/en-US/firefox-91.9.1esr.tar.bz2
|
||||||
|
tar -xjf firefox-91.9.1esr.tar.bz2
|
||||||
|
sudo mv firefox /opt/
|
||||||
|
sudo ln -s /opt/firefox/firefox /usr/bin/firefox
|
||||||
|
|
||||||
|
curl -sSLO https://github.com/mozilla/geckodriver/releases/download/v0.31.0/geckodriver-v0.31.0-linux64.tar.gz
|
||||||
|
tar zxf geckodriver-v0.31.0-linux64.tar.gz
|
||||||
|
sudo mv geckodriver /usr/bin/
|
||||||
|
|
||||||
|
rm geckodriver-v0.31.0-linux64.tar.gz
|
||||||
|
rm firefox-91.9.1esr.tar.bz2
|
||||||
|
```
|
||||||
|
## Windows
|
||||||
|
Not yet tested, but using docker should work.
|
||||||
|
## MacOS
|
||||||
|
I don't have a mac (yet) so i can't test, but again, install docker and follow the linux installation.
|
||||||
|
|
||||||
|
# Database
|
||||||
|
|
||||||
|
To use the database, I recommend MySql, Create a database with the name `MsRewards` and create a table `daily`, like the one from the image :
|
||||||

|

|
||||||
|
|
||||||
You have to use the default worldlist (`sudo apt install wfrench`). The language is french by default, but you can change it if you want.
|
# Options
|
||||||
You can add a link to a website where content is only the link of the monthly fidelity card.
|
TODO
|
||||||
|
# Flags
|
||||||
|
## Override
|
||||||
You should limit to 6 account per IP, and DON'T USE outlook account, they are banned.
|
Enable you to choose what action to perform on which account. **Linux only**
|
||||||

|
|
||||||
|
|
||||||
## if you're **not** using docker
|
|
||||||
You have to **fill the config file**.
|
|
||||||
|
|
||||||
you have to put your credentials in the same folder as the script, in a `.csv` file. You have to put login and password this way :
|
|
||||||
```
|
```
|
||||||
email1,password1
|
python3 main.py -o
|
||||||
email2,password2
|
|
||||||
```
|
```
|
||||||
|
## VNC
|
||||||
|
Enble a vnc to a specific port
|
||||||
|
```
|
||||||
|
python3 main.py -v 1234
|
||||||
|
```
|
||||||
|
## config
|
||||||
|
Tell the programm which config to use.
|
||||||
|
Default to ./user_data/config.cfg
|
||||||
|
Putting only a number is equivalent to ./user_data/config[number].cfg
|
||||||
|
```
|
||||||
|
python3 main.py -c 12
|
||||||
|
```
|
||||||
|
## Add points to database
|
||||||
|
Add points to the database using the credentials provided in the default config file.
|
||||||
|
argument : file with a list of `account,points`
|
||||||
|
```
|
||||||
|
python3 main.py -a file.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
844
V5.py
Executable file
844
V5.py
Executable file
@ -0,0 +1,844 @@
|
|||||||
|
#!/usr/bin/python3.10
|
||||||
|
from modules.imports import *
|
||||||
|
from modules.config import *
|
||||||
|
from modules.db import add_to_database
|
||||||
|
from modules.tools import *
|
||||||
|
from modules.error import *
|
||||||
|
from modules.driver_tools import *
|
||||||
|
from modules.cards import *
|
||||||
|
import modules.globals as g
|
||||||
|
|
||||||
|
|
||||||
|
driver = g.driver
|
||||||
|
display = g.display
|
||||||
|
|
||||||
|
|
||||||
|
# create a webdriver
|
||||||
|
def firefox_driver(mobile=False, headless=False):
|
||||||
|
PC_USER_AGENT = (
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
|
||||||
|
"AppleWebKit/537.36 (KHTML, like Gecko)"
|
||||||
|
"Chrome/112.0.0.0 Safari/537.36 Edg/110.0.1587.56")
|
||||||
|
MOBILE_USER_AGENT = (
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 15_5 like Mac OS X)"
|
||||||
|
"AppleWebKit/606.0.2 (KHTML, like Gecko)"
|
||||||
|
"CriOS/107.0.5060.64 Mobile/15E148 Safari/604.1"
|
||||||
|
)
|
||||||
|
options = Options()
|
||||||
|
options.set_preference('intl.accept_languages', 'fr-FR, fr')
|
||||||
|
options.set_preference("browser.link.open_newwindow", 3)
|
||||||
|
options.set_preference("dom.confirm_repost.testing.always_accept", True)
|
||||||
|
if g.fast:
|
||||||
|
options.set_preference("permissions.default.image", 2)
|
||||||
|
if headless:
|
||||||
|
options.add_argument("-headless")
|
||||||
|
if mobile :
|
||||||
|
options.set_preference("general.useragent.override", MOBILE_USER_AGENT)
|
||||||
|
driver = webdriver.Firefox(options=options)
|
||||||
|
driver.set_window_size(1070 + hash(g._mail)%10 , 1900 + hash(g._password + "salt")%20) # mobile resolution are crazy high now, right ?
|
||||||
|
else :
|
||||||
|
options.set_preference("general.useragent.override", PC_USER_AGENT)
|
||||||
|
driver = webdriver.Firefox(options=options)
|
||||||
|
driver.set_window_size(1900 + hash(g._mail)%20 , 1070 + hash(g._password + "salt")%10)
|
||||||
|
return(driver)
|
||||||
|
|
||||||
|
|
||||||
|
def log_error(error, ldriver=driver, log=g.full_log):
|
||||||
|
global driver
|
||||||
|
if ldriver is None:
|
||||||
|
ldriver = driver
|
||||||
|
if type(error) != str :
|
||||||
|
error = format_error(error)
|
||||||
|
printf(f"\n\n\033[93m Erreur : {str(error)} \033[0m\n\n")
|
||||||
|
if g.discord_enabled_error:
|
||||||
|
with open("page.html", "w") as f:
|
||||||
|
try :
|
||||||
|
f.write(ldriver.page_source)
|
||||||
|
except :
|
||||||
|
f.write("the driver has closed or crashed. Can't access page content")
|
||||||
|
try :
|
||||||
|
img = display.waitgrab()
|
||||||
|
img.save("screenshot.png")
|
||||||
|
except :
|
||||||
|
ldriver.save_screenshot("screenshot.png")
|
||||||
|
if not log:
|
||||||
|
embed = Embed(
|
||||||
|
title="An Error has occured",
|
||||||
|
description=str(error),
|
||||||
|
colour=Colour.red(),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
embed = Embed(
|
||||||
|
title="Full log is enabled",
|
||||||
|
description=str(error),
|
||||||
|
colour=Colour.blue(),
|
||||||
|
)
|
||||||
|
file = File("screenshot.png")
|
||||||
|
embed.set_image(url="attachment://screenshot.png")
|
||||||
|
embed.set_footer(text=g._mail)
|
||||||
|
webhookFailure.send(embed=embed, username="error", file=file, avatar_url = g.avatar_url)
|
||||||
|
webhookFailure.send(username="error", file=File("page.html"), avatar_url = g.avatar_url)
|
||||||
|
|
||||||
|
|
||||||
|
# close the tab currently on and go back to the one first, or the one specified
|
||||||
|
def close_tab(tab, SwitchTo=0) -> None:
|
||||||
|
driver.switch_to.window(tab)
|
||||||
|
driver.close()
|
||||||
|
driver.switch_to.window(driver.window_handles[SwitchTo])
|
||||||
|
|
||||||
|
|
||||||
|
# play_quiz[N]([int : override]) make the quiz with N choice each time. They usually have between 4 and 10 questions.
|
||||||
|
# override is the number of question, by default, it's the number of question in this specific quiz. Can be useful in some case, where the program crashes before finishing the quiz
|
||||||
|
def play_quiz2(override=10) -> None:
|
||||||
|
printf("starting play_quiz2")
|
||||||
|
for j in range(override):
|
||||||
|
try:
|
||||||
|
# rgpd_popup(driver)
|
||||||
|
custom_sleep(uniform(3, 5))
|
||||||
|
page_html = driver.page_source
|
||||||
|
secret_answer = search('IG:"([^"]+)"', page_html)[1] # variable used to calculate offset
|
||||||
|
answers_values = search('data-option="([^"]+)"', page_html)[1]
|
||||||
|
offset = int(secret_answer[-2:], 16) # the last two character converted to int are the offset
|
||||||
|
correct_answer_value = search('correctAnswer":"([0-9]+)', page_html)[1]
|
||||||
|
|
||||||
|
somme = 0
|
||||||
|
for answer in answers_values:
|
||||||
|
somme += ord(answer)
|
||||||
|
|
||||||
|
if somme + offset == int(correct_answer_value):
|
||||||
|
answer_elem = driver.find_element(By.ID, "rqAnswerOption0")
|
||||||
|
answer_elem.click()
|
||||||
|
if g.log:
|
||||||
|
progressBar(j, 10, name="quiz 2")
|
||||||
|
else:
|
||||||
|
answer_elem = driver.find_element(By.ID, "rqAnswerOption1")
|
||||||
|
answer_elem.click()
|
||||||
|
if g.log:
|
||||||
|
progressBar(j, 10, name="quiz 2")
|
||||||
|
|
||||||
|
except exceptions.ElementNotInteractableException as e:
|
||||||
|
driver.execute_script("arguments[0].click();", answer_elem)
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
break
|
||||||
|
printf("play_quiz2 done")
|
||||||
|
custom_sleep(3)
|
||||||
|
|
||||||
|
|
||||||
|
def play_quiz8():
|
||||||
|
override = len(findall("<span id=\"rqQuestionState.\" class=\"emptyCircle\"></span>", driver.page_source))+1
|
||||||
|
printf(f"play_quiz8 : start, override : {override}")
|
||||||
|
try:
|
||||||
|
counter = 0
|
||||||
|
# rgpd_popup(driver)
|
||||||
|
for _ in range(override):
|
||||||
|
sleep(uniform(3, 5))
|
||||||
|
correct_answers = []
|
||||||
|
for i in range(1,9):
|
||||||
|
try :
|
||||||
|
element = driver.find_element(By.ID, f"rqAnswerOption{i-1}")
|
||||||
|
if 'iscorrectoption="True"' in element.get_attribute("outerHTML"):
|
||||||
|
correct_answers.append(f'rqAnswerOption{i-1}')
|
||||||
|
except Exception as e :
|
||||||
|
printf(f"can't find rqAnswerOption{i-1}. Probably already clicked" + str(e))
|
||||||
|
shuffle(correct_answers)
|
||||||
|
for answer_id in correct_answers:
|
||||||
|
wait_until_visible(By.ID, answer_id, timeout = 20, browser=driver)
|
||||||
|
counter += 1
|
||||||
|
if g.log :
|
||||||
|
progressBar(counter, 16, name="Quiz 8")
|
||||||
|
try:
|
||||||
|
answer_elem = driver.find_element(By.ID, answer_id)
|
||||||
|
answer_elem.click()
|
||||||
|
sleep(1)
|
||||||
|
except exceptions.NoSuchElementException :
|
||||||
|
driver.refresh()
|
||||||
|
sleep(10)
|
||||||
|
answer_elem = driver.find_element(By.ID, answer_id)
|
||||||
|
answer_elem.click()
|
||||||
|
except ElementClickInterceptedException :
|
||||||
|
rgpd_popup(driver)
|
||||||
|
correct_answers.append(answer_id)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log_error(f"{format_error(e)} \n Good answers : {' '.join(correct_answers)}")
|
||||||
|
printf("play_quiz8 : fin ")
|
||||||
|
custom_sleep(3)
|
||||||
|
|
||||||
|
|
||||||
|
def play_quiz4(override=None):
|
||||||
|
printf("play_quiz4 : start")
|
||||||
|
if not override:
|
||||||
|
try: # fidelity quiz are much longer than usual ones
|
||||||
|
override = int(findall('rqQuestionState([\d]{1,2})"', driver.page_source)[-1])
|
||||||
|
printf(f"Override : {override}")
|
||||||
|
except:
|
||||||
|
override = 3
|
||||||
|
|
||||||
|
try:
|
||||||
|
for i in range(override):
|
||||||
|
custom_sleep(uniform(3, 5))
|
||||||
|
txt = driver.page_source
|
||||||
|
# rgpd_popup(driver)
|
||||||
|
answer_option = search('correctAnswer":"([^"]+)', txt)[1]
|
||||||
|
answer_option = answer_option.replace("\\u0027", "'") # replace Unicode weird symbols
|
||||||
|
try:
|
||||||
|
answer_element = driver.find_element(By.CSS_SELECTOR, f'[data-option="{answer_option}"]')
|
||||||
|
answer_element.click()
|
||||||
|
except exceptions.ElementNotInteractableException:
|
||||||
|
driver.execute_script("arguments[0].click();", answer_element)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
raise ValueError(e)
|
||||||
|
printf("play_quiz4 : end")
|
||||||
|
custom_sleep(3)
|
||||||
|
|
||||||
|
|
||||||
|
# do_poll() answer a random thing to poll, on of daily activities
|
||||||
|
def do_poll():
|
||||||
|
printf("do_poll : start")
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
answer_elem = driver.find_element(By.ID, f"btoption{choice([0,1])}")
|
||||||
|
answer_elem.click()
|
||||||
|
except exceptions.ElementNotInteractableException:
|
||||||
|
driver.execute_script("arguments[0].click();", answer_elem)
|
||||||
|
custom_sleep(uniform(2, 2.5))
|
||||||
|
except Exception as error:
|
||||||
|
log_error(error)
|
||||||
|
raise ValueError(error)
|
||||||
|
printf("do_poll : end")
|
||||||
|
custom_sleep(3)
|
||||||
|
|
||||||
|
|
||||||
|
def all_cards():
|
||||||
|
driver.get("https://rewards.bing.com")
|
||||||
|
wait_until_visible(By.CLASS_NAME, "c-card-content", 10, driver)
|
||||||
|
liste = driver.find_elements(By.CLASS_NAME, "c-card-content")
|
||||||
|
custom_sleep(2)
|
||||||
|
if "welcometour" in driver.current_url:
|
||||||
|
welcome_tour_NO(driver)
|
||||||
|
try :
|
||||||
|
promo()
|
||||||
|
except Exception as e:
|
||||||
|
printf("no promo card")
|
||||||
|
if(len(liste) < 10): #most likely an error during loading
|
||||||
|
driver.refresh()
|
||||||
|
liste = driver.find_elements(By.CLASS_NAME, "c-card-content")
|
||||||
|
if(len(liste) < 10):
|
||||||
|
log_error("Less than 10 cards. Most likely an error with login.", driver)
|
||||||
|
return("PAS ASSEZ DE CARTES")
|
||||||
|
if (len(liste) < 20): # most likely not in france
|
||||||
|
if not g.norvege : # TODO : rename norvege to not_france or smth like that
|
||||||
|
g.norvege = True
|
||||||
|
printf("Most likely not in France, thus disabling France specific action")
|
||||||
|
# TODO : check country for fidelity
|
||||||
|
for i in range(len(liste)):
|
||||||
|
printf(f"carte {i}")
|
||||||
|
try :
|
||||||
|
checked = ("mee-icon-AddMedium" in liste[i].get_attribute("innerHTML"))
|
||||||
|
except StaleElementReferenceException :
|
||||||
|
driver.refresh()
|
||||||
|
liste = driver.find_elements(By.CLASS_NAME, "c-card-content")
|
||||||
|
printf(f"staled, {len(liste)}")
|
||||||
|
checked = ("mee-icon-AddMedium" in liste[i].get_attribute("innerHTML"))
|
||||||
|
except IndexError:
|
||||||
|
driver.get("https://rewards.bing.com")
|
||||||
|
custom_sleep(10)
|
||||||
|
liste = driver.find_elements(By.CLASS_NAME, "c-card-content")
|
||||||
|
try :
|
||||||
|
checked = ("mee-icon-AddMedium" in liste[i].get_attribute("innerHTML"))
|
||||||
|
except :
|
||||||
|
log_error("IndexError", driver)
|
||||||
|
return("INDEX ERROR")
|
||||||
|
if checked:
|
||||||
|
custom_sleep(1.5)
|
||||||
|
driver.execute_script("arguments[0].scrollIntoView();", liste[i])
|
||||||
|
custom_sleep(1.5)
|
||||||
|
liste[i].click()
|
||||||
|
if len(driver.window_handles) > 1 :
|
||||||
|
driver.switch_to.window(driver.window_handles[1])
|
||||||
|
try_play(driver.title)
|
||||||
|
close_tab(driver.window_handles[1])
|
||||||
|
try :
|
||||||
|
driver.refresh()
|
||||||
|
liste = driver.find_elements(By.CLASS_NAME, "c-card-content")
|
||||||
|
if ("mee-icon-AddMedium" in liste[i].get_attribute("innerHTML")) :
|
||||||
|
printf(f"carte {i} not okay. Retrying.")
|
||||||
|
try :
|
||||||
|
liste[i].click()
|
||||||
|
except :
|
||||||
|
log_error("problème inconnu ? sauf si c'est un element obscure...", driver)
|
||||||
|
driver.get("https://rewards.bing.com")
|
||||||
|
checked = ("mee-icon-AddMedium" in liste[i].get_attribute("innerHTML"))
|
||||||
|
driver.switch_to.window(driver.window_handles[1])
|
||||||
|
try_play(driver.title)
|
||||||
|
close_tab(driver.window_handles[1])
|
||||||
|
if ("mee-icon-AddMedium" in liste[i].get_attribute("innerHTML")):
|
||||||
|
log_error(f"Card {i} Can't be completed. Why MS ?", driver)
|
||||||
|
except :
|
||||||
|
pass
|
||||||
|
else :
|
||||||
|
try :
|
||||||
|
welcome_tour(liste[i], driver)
|
||||||
|
except Exception as e:
|
||||||
|
printf("no new windows" + format_error(e))
|
||||||
|
driver.get("https://rewards.bing.com")
|
||||||
|
custom_sleep(3)
|
||||||
|
|
||||||
|
|
||||||
|
def promo():
|
||||||
|
elm = driver.find_element(By.ID, "promo-item")
|
||||||
|
for i in range(10):
|
||||||
|
if not elm:
|
||||||
|
break
|
||||||
|
if i > 8 :
|
||||||
|
log_error("chelou, plus de 8 truc", driver)
|
||||||
|
try :
|
||||||
|
elm.click()
|
||||||
|
except :
|
||||||
|
driver.execute_script("arguments[0].click();", elm)
|
||||||
|
printf("that should't be there (promo)")
|
||||||
|
custom_sleep(3)
|
||||||
|
if len(driver.window_handles) > 1 :
|
||||||
|
driver.switch_to.window(driver.window_handles[len(driver.window_handles)-1])
|
||||||
|
try_play(driver.title)
|
||||||
|
close_tab(driver.window_handles[1])
|
||||||
|
else :
|
||||||
|
try :
|
||||||
|
spotify(driver)
|
||||||
|
except :
|
||||||
|
printf("no new windows", driver)
|
||||||
|
driver.get("https://rewards.bing.com")
|
||||||
|
driver.refresh()
|
||||||
|
custom_sleep(3)
|
||||||
|
|
||||||
|
|
||||||
|
# Find out which type of action to do
|
||||||
|
def try_play(nom="inconnu"):
|
||||||
|
def play(number):
|
||||||
|
if number == 8 or number == 9:
|
||||||
|
try:
|
||||||
|
printf(f"\033[96mQuiz 8 detected on `{nom}` \033[0m")
|
||||||
|
play_quiz8()
|
||||||
|
printf(f"\033[92mQuiz 8 succeeded on `{nom}` \033[0m")
|
||||||
|
custom_sleep(uniform(3, 5))
|
||||||
|
except Exception as e:
|
||||||
|
printf(f"fail of PlayQuiz 8. Aborted {e} \033[0m")
|
||||||
|
|
||||||
|
elif number == 5 or number == 4:
|
||||||
|
try:
|
||||||
|
printf(f"\033[96mQuiz 4 detected on `{nom}` \033[0m")
|
||||||
|
play_quiz4()
|
||||||
|
printf(f"\033[92mQuiz 4 succeeded on `{nom}` \033[0m")
|
||||||
|
custom_sleep(uniform(3, 5))
|
||||||
|
except Exception as e:
|
||||||
|
printf(f"Fail of PlayQuiz 4. Aborted {e} \033[0m")
|
||||||
|
|
||||||
|
elif number == 3 or number == 2:
|
||||||
|
try:
|
||||||
|
printf(f"\033[96mQuiz 2 detected on `{nom}`\033[0m")
|
||||||
|
play_quiz2()
|
||||||
|
printf(f"\033[92mQuiz 2 succeeded on `{nom}`\033[0m")
|
||||||
|
except Exception as e:
|
||||||
|
printf(f"fail of PlayQuiz 2. Aborted {e}")
|
||||||
|
else:
|
||||||
|
printf("There is an error. rqAnswerOption present in page but no action to do. skipping.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
if wait_until_visible(By.ID, "rqStartQuiz", 5, driver):
|
||||||
|
custom_sleep(3)
|
||||||
|
driver.find_element(By.ID, "rqStartQuiz").click() # start the quiz
|
||||||
|
answer_number = driver.page_source.count("rqAnswerOption")
|
||||||
|
play(answer_number)
|
||||||
|
else :
|
||||||
|
raise (NameError("going to next part"))
|
||||||
|
except Exception as e: # if there is no start button, an error is thrown
|
||||||
|
if "bt_PollRadio" in driver.page_source:
|
||||||
|
try:
|
||||||
|
printf("Poll detected")
|
||||||
|
do_poll()
|
||||||
|
printf("Poll succeeded")
|
||||||
|
except Exception as e:
|
||||||
|
printf(f"try_play - 1 - Poll aborted {e}")
|
||||||
|
|
||||||
|
elif "rqQuestionState" in driver.page_source:
|
||||||
|
try:
|
||||||
|
number = driver.page_source.count("rqAnswerOption")
|
||||||
|
printf(f"recovery détecté. quiz : {number}")
|
||||||
|
play(number-1)
|
||||||
|
except Exception as e:
|
||||||
|
printf(f"try_play - 2 - {e}")
|
||||||
|
|
||||||
|
elif search("([0-9]) de ([0-9]) finalisée", driver.page_source):
|
||||||
|
printf("fidélité")
|
||||||
|
fidelity()
|
||||||
|
|
||||||
|
else:
|
||||||
|
printf(f"rien à faire sur la page {nom}")
|
||||||
|
custom_sleep(uniform(3, 5))
|
||||||
|
|
||||||
|
|
||||||
|
# Login with password or with cookies.
|
||||||
|
# The driver should be in the same state on both case
|
||||||
|
def pwd_login(ldriver):
|
||||||
|
printf("pwd_login : start")
|
||||||
|
ldriver.get("https://login.live.com")
|
||||||
|
wait_until_visible(By.ID, "i0116", browser = ldriver)
|
||||||
|
mail_elem = ldriver.find_element(By.ID, "i0116")
|
||||||
|
send_keys_wait(mail_elem, g._mail)
|
||||||
|
mail_elem.send_keys(Keys.ENTER)
|
||||||
|
wait_until_visible(By.ID, "i0118", browser = ldriver)
|
||||||
|
pwd_elem = ldriver.find_element(By.ID, "i0118")
|
||||||
|
send_keys_wait(pwd_elem, g._password)
|
||||||
|
pwd_elem.send_keys(Keys.ENTER)
|
||||||
|
custom_sleep(2)
|
||||||
|
# 2FA
|
||||||
|
if "Entrez le code de sécurité" in ldriver.page_source :
|
||||||
|
try :
|
||||||
|
a2f_elem = ldriver.find_element(By.ID, "idTxtBx_SAOTCC_OTC")
|
||||||
|
a2f_elem.send_keys(g._otp.now())
|
||||||
|
a2f_elem.send_keys(Keys.ENTER)
|
||||||
|
except Exception as e :
|
||||||
|
log_error(e)
|
||||||
|
|
||||||
|
|
||||||
|
def cookie_login(ldriver):
|
||||||
|
printf("cookies_login : start")
|
||||||
|
ldriver.get("https://login.live.com")
|
||||||
|
try :
|
||||||
|
load_cookies(ldriver)
|
||||||
|
except FileNotFoundError :
|
||||||
|
printf("No cookies file Found.")
|
||||||
|
return(False)
|
||||||
|
except Exception as e:
|
||||||
|
log_error(f"Error performing cookies login. Trying with password instead. \n{str(e)}", driver)
|
||||||
|
return(False)
|
||||||
|
try :
|
||||||
|
ldriver.refresh()
|
||||||
|
except Exception as e:
|
||||||
|
printf(format_error(e))
|
||||||
|
printf("FIX YOUR SITE MS.......")
|
||||||
|
|
||||||
|
return(True)
|
||||||
|
|
||||||
|
|
||||||
|
# Accept all cookies question, and check if the account is locked
|
||||||
|
def login_part_2(ldriver, cookies = False):
|
||||||
|
custom_sleep(5)
|
||||||
|
if ('Abuse' in ldriver.current_url) :
|
||||||
|
raise Banned()
|
||||||
|
if ('identity' in ldriver.current_url) :
|
||||||
|
raise Identity()
|
||||||
|
if ('notice' in ldriver.current_url) :
|
||||||
|
ldriver.find_element(By.ID, "id__0").click()
|
||||||
|
if ("proof" in ldriver.current_url):
|
||||||
|
ldriver.find_element(BY.ID, "iLooksGood")
|
||||||
|
if cookies:
|
||||||
|
save_cookies(ldriver)
|
||||||
|
for id in ["KmsiCheckboxField", "id__0", "iLooksGood", "idSIButton9", "iCancel"]:
|
||||||
|
if get_domain(ldriver) == "account.microsoft.com":
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
ldriver.find_element(By.ID, id).click()
|
||||||
|
restart = True
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
wait_until_visible(By.CSS_SELECTOR, '[data-bi-id="sh-sharedshell-home"]', 20, ldriver)
|
||||||
|
ldriver.get("https://www.bing.com/?setlang=fr&cc=fr&cc=FR")
|
||||||
|
rgpd_popup(ldriver)
|
||||||
|
ldriver.refresh()
|
||||||
|
rgpd_popup(ldriver)
|
||||||
|
|
||||||
|
|
||||||
|
# login() tries to login to your Microsoft account.
|
||||||
|
# it uses global variable g._mail and g._password to login
|
||||||
|
def login(ldriver):
|
||||||
|
try :
|
||||||
|
success_cookies = cookie_login(ldriver)
|
||||||
|
if not success_cookies:
|
||||||
|
pwd_login(ldriver)
|
||||||
|
login_part_2(ldriver, not success_cookies)
|
||||||
|
ldriver.get("https://rewards.bing.com/")
|
||||||
|
except Banned:
|
||||||
|
raise Banned()
|
||||||
|
except Identity:
|
||||||
|
raise Banned()
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
ldriver.quit()
|
||||||
|
return(False)
|
||||||
|
|
||||||
|
|
||||||
|
# Makes 30 search as PC Edge
|
||||||
|
def bing_pc_search(override=randint(35, 40)):
|
||||||
|
mot = choice(Liste_de_mot).replace(" ","+")
|
||||||
|
driver.get(f"https://www.bing.com/search?q={mot}") # {choice(Liste_de_mot)}')
|
||||||
|
custom_sleep(uniform(1, 2))
|
||||||
|
rgpd_popup(driver)
|
||||||
|
send_keys_wait(
|
||||||
|
driver.find_element(By.ID, "sb_form_q"),
|
||||||
|
Keys.BACKSPACE + Keys.BACKSPACE + Keys.BACKSPACE + Keys.BACKSPACE + Keys.BACKSPACE + Keys.BACKSPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
for _ in range(override):
|
||||||
|
word = choice(Liste_de_mot)
|
||||||
|
try:
|
||||||
|
send_keys_wait(driver.find_element(By.ID, "sb_form_q"), word)
|
||||||
|
driver.find_element(By.ID, "sb_form_q").send_keys(Keys.ENTER)
|
||||||
|
except Exception as e :
|
||||||
|
printf(e)
|
||||||
|
sleep(10)
|
||||||
|
driver.get(f'https://www.bing.com/search?q={word}')
|
||||||
|
sleep(3)
|
||||||
|
send_keys_wait(driver.find_element(By.ID, "sb_form_q"), word)
|
||||||
|
driver.find_element(By.ID, "sb_form_q").send_keys(Keys.ENTER)
|
||||||
|
custom_sleep(uniform(3, 7))
|
||||||
|
try:
|
||||||
|
driver.find_element(By.ID, "sb_form_q").clear()
|
||||||
|
except Exception as e:
|
||||||
|
printf(e)
|
||||||
|
try:
|
||||||
|
driver.get('https://www.bing.com/search?q=plans')
|
||||||
|
driver.find_element(By.ID, "sb_form_q").clear()
|
||||||
|
except Exception as e:
|
||||||
|
log_error(f"clear la barre de recherche - {format_error(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
# Unban an account, called with -u parameter. You will need a phone number
|
||||||
|
def unban() -> None:
|
||||||
|
driver.find_element(By.ID, "StartAction").click()
|
||||||
|
custom_sleep(2)
|
||||||
|
txt = driver.page_source
|
||||||
|
uuid0 = findall('wlspispHIPCountrySelect([a-z0-9]+)', txt)[0]
|
||||||
|
uuid1 = findall('wlspispHIPPhoneInput([a-z0-9]+)', txt)[0]
|
||||||
|
uuid2 = findall('wlspispHipSendCode([a-z0-9]+)', txt)[0]
|
||||||
|
uuid3 = findall('wlspispSolutionElement([a-z0-9]+)', txt)[0]
|
||||||
|
country_code_select = Select(driver.find_element(By.ID, "wlspispHIPCountrySelect" + uuid0))
|
||||||
|
country_code_input = input("enter Country code (FR, ...) ")
|
||||||
|
country_code_select.select_by_value(country_code_input)
|
||||||
|
wait_until_visible(By.ID, "wlspispHIPPhoneInput" + uuid1, browser=driver)
|
||||||
|
phone_input = input("phone number : +33")
|
||||||
|
phone_elem = driver.find_element(By.ID, "wlspispHIPPhoneInput" + uuid1)
|
||||||
|
phone_elem.send_keys(phone_input)
|
||||||
|
wait_until_visible(By.ID, "wlspispHipSendCode" + uuid2, browser=driver)
|
||||||
|
send_sms_elem = driver.find_element(By.ID, "wlspispHipSendCode" + uuid2)
|
||||||
|
send_sms_elem.click()
|
||||||
|
wait_until_visible(By.ID, "wlspispSolutionElement" + uuid3, browser=driver)
|
||||||
|
sms_code_elem = driver.find_element(By.ID, "wlspispSolutionElement" + uuid3)
|
||||||
|
sms_code_input = input("entrez le contenu du msg : ")
|
||||||
|
sms_code_elem.send_keys(sms_code_input)
|
||||||
|
send_box = driver.find_element(By.ID, "ProofAction")
|
||||||
|
send_box.click()
|
||||||
|
wait_until_visible(By.ID, "FinishAction", browser=driver)
|
||||||
|
end_elem = driver.find_element(By.ID, "FinishAction")
|
||||||
|
end_elem.click()
|
||||||
|
|
||||||
|
|
||||||
|
# Sends points to database, discord and whatever service you want
|
||||||
|
def log_points(account="unknown"):
|
||||||
|
def get_points():
|
||||||
|
driver.get("https://rewards.bing.com")
|
||||||
|
custom_sleep(1)
|
||||||
|
if "/proofs/" in driver.current_url:
|
||||||
|
for id in ["KmsiCheckboxField","iLooksGood", "idSIButton9", "iCancel"]:
|
||||||
|
try:
|
||||||
|
driver.find_element(By.ID, id).click()
|
||||||
|
restart = True
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
wait_until_visible(By.CSS_SELECTOR, 'span[mee-element-ready="$ctrl.loadCounterAnimation()"]', browser=driver)
|
||||||
|
try :
|
||||||
|
point = search('availablePoints\":([\d]+)', driver.page_source)[1]
|
||||||
|
except Exception as e:
|
||||||
|
sleep(5)
|
||||||
|
log_error(f"Dev error, checking why it doesn't work (waited a bit, is this still white ?) {format_error(e)}", driver, True)
|
||||||
|
driver.refresh()
|
||||||
|
sleep(5)
|
||||||
|
point = search('availablePoints\":([\d]+)', driver.page_source)[1]
|
||||||
|
return(point)
|
||||||
|
|
||||||
|
for _ in range (3):
|
||||||
|
try :
|
||||||
|
points = get_points()
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
custom_sleep(300)
|
||||||
|
log_error(e)
|
||||||
|
points = None
|
||||||
|
|
||||||
|
if not points :
|
||||||
|
log_error(f"impossible d'avoir les points")
|
||||||
|
|
||||||
|
custom_sleep(uniform(3, 20))
|
||||||
|
account_name = account.split("@")[0]
|
||||||
|
|
||||||
|
if g.discord_enabled_success:
|
||||||
|
if g.discord_embed:
|
||||||
|
embed = Embed(
|
||||||
|
title=f"{account_name} actuellement à {str(points)} points", colour=Colour.green()
|
||||||
|
)
|
||||||
|
embed.set_footer(text=account_name)
|
||||||
|
webhookSuccess.send(embed=embed)
|
||||||
|
else:
|
||||||
|
webhookSuccess.send(f"{account_name} actuellement à {str(points)} points")
|
||||||
|
|
||||||
|
if g.sql_enabled :
|
||||||
|
try :
|
||||||
|
add_to_database(account_name, points, g.sql_host, g.sql_usr, g.sql_pwd, g.sql_database)
|
||||||
|
except Exception as e:
|
||||||
|
if g.database_error_override:
|
||||||
|
printf("database error.")
|
||||||
|
else :
|
||||||
|
log_error(e)
|
||||||
|
|
||||||
|
|
||||||
|
def fidelity():
|
||||||
|
def sub_fidelity():
|
||||||
|
try:
|
||||||
|
wait_until_visible(By.CSS_SELECTOR, 'div[class="pull-left spacer-48-bottom punchcard-row"]', browser=driver)
|
||||||
|
answer_number = search("([0-9]) of ([0-9]) completed", driver.page_source)
|
||||||
|
if answer_number is None :
|
||||||
|
answer_number = search("([0-9]) défi\(s\) terminé\(s\) sur ([0-9])", driver.page_source)
|
||||||
|
if answer_number is None:
|
||||||
|
answer_number = search("([0-9]) de ([0-9]) finalisé", driver.page_source)
|
||||||
|
if answer_number is None :
|
||||||
|
answer_number = search("([0-9]) licence\(s\) sur ([0-9]) disponible\(s\)", driver.page_source)
|
||||||
|
if answer_number is None :
|
||||||
|
answer_number = [0,0,0]
|
||||||
|
for _ in range(int(answer_number[2]) - int(answer_number[1])):
|
||||||
|
driver.refresh()
|
||||||
|
custom_sleep(2)
|
||||||
|
card_elem = driver.find_element(By.CLASS_NAME, "spacer-48-bottom")
|
||||||
|
try :
|
||||||
|
button_text = search('<span class="pull-left margin-right-15">([^<^>]+)</span>',card_elem.get_attribute("innerHTML"))[1]
|
||||||
|
bouton_card = driver.find_element(By.XPATH, f'//span[text()="{button_text}"]')
|
||||||
|
bouton_card.click()
|
||||||
|
except Exception as e1 :
|
||||||
|
try :
|
||||||
|
recover_elem = driver.find_element(By.XPATH,'/html/body/div[1]/div[2]/main/div[2]/div[2]/div[7]/div[3]/div[1]/a')
|
||||||
|
recover_elem.click()
|
||||||
|
except Exception as e2 :
|
||||||
|
log_error(f"fidélité - double erreur - e1 : {format_error(e1)} - e2 {format_error(e2)}")
|
||||||
|
break
|
||||||
|
custom_sleep(uniform(3, 5))
|
||||||
|
driver.switch_to.window(driver.window_handles[2])
|
||||||
|
try_play(driver.title)
|
||||||
|
custom_sleep(uniform(3, 5))
|
||||||
|
try:
|
||||||
|
close_tab(driver.window_handles[2], 1)
|
||||||
|
except Exception as e:
|
||||||
|
printf(e)
|
||||||
|
printf("fidelity - done")
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
if driver.current_url != "https://rewards.bing.com":
|
||||||
|
driver.get("https://rewards.bing.com")
|
||||||
|
pause = driver.find_element(By.CSS_SELECTOR, f'[class="c-action-toggle c-glyph f-toggle glyph-pause"]') # mettre le truc en pause
|
||||||
|
pause.click()
|
||||||
|
cartes = driver.find_elements(By.CSS_SELECTOR, f'[ng-repeat="item in $ctrl.transcludedItems"]')
|
||||||
|
nb_cartes = len(cartes)
|
||||||
|
checked_list_all = driver.find_elements(By.CSS_SELECTOR, f'[ng-if="$ctrl.complete"]')
|
||||||
|
for i in range(nb_cartes):
|
||||||
|
cartes[i].click() # affiche la bonne carte
|
||||||
|
checked_txt = checked_list_all[i].get_attribute("innerHTML")
|
||||||
|
ok = checked_txt.count("StatusCircleOuter checkmark")
|
||||||
|
total = checked_txt.count("StatusCircleOuter")
|
||||||
|
if (ok != total) :
|
||||||
|
elm = driver.find_elements(By.CLASS_NAME, 'clickable-link')[i]
|
||||||
|
if not "moviesandtv" in elm.get_attribute("innerHTML"): # not the film card
|
||||||
|
elm.click()
|
||||||
|
driver.switch_to.window(driver.window_handles[len(driver.window_handles)-1])
|
||||||
|
sub_fidelity()
|
||||||
|
close_tab(driver.window_handles[1])
|
||||||
|
custom_sleep(1)
|
||||||
|
cartes = driver.find_elements(By.CSS_SELECTOR, f'[ng-repeat="item in $ctrl.transcludedItems"]')
|
||||||
|
checked_list_all = driver.find_elements(By.CSS_SELECTOR, f'[ng-if="$ctrl.complete"]')
|
||||||
|
|
||||||
|
def mobile_alert_popup():
|
||||||
|
try:
|
||||||
|
alert = mobile_driver.switch_to.alert
|
||||||
|
alert.dismiss()
|
||||||
|
except exceptions.NoAlertPresentException as e:
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e, mobile_driver)
|
||||||
|
|
||||||
|
|
||||||
|
def bing_mobile_search(override=randint(22, 25)):
|
||||||
|
global mobile_driver
|
||||||
|
mobile_driver = firefox_driver(mobile=True)
|
||||||
|
try:
|
||||||
|
login(mobile_driver)
|
||||||
|
mot = choice(Liste_de_mot).replace(" ","+")
|
||||||
|
mobile_driver.get(f"https://www.bing.com/search?q={mot}")
|
||||||
|
custom_sleep(uniform(1, 2))
|
||||||
|
rgpd_popup(mobile_driver)
|
||||||
|
custom_sleep(uniform(1, 1.5))
|
||||||
|
for i in range(override): # 20
|
||||||
|
try :
|
||||||
|
mot = choice(Liste_de_mot)
|
||||||
|
send_keys_wait(mobile_driver.find_element(By.ID, "sb_form_q"), mot)
|
||||||
|
mobile_driver.find_element(By.ID, "sb_form_q").send_keys(Keys.ENTER)
|
||||||
|
custom_sleep(uniform(3, 7))
|
||||||
|
mobile_alert_popup() # check for alert (asking for position or for allowing notifications)
|
||||||
|
mobile_driver.find_element(By.ID, "sb_form_q").clear()
|
||||||
|
except Exception as e:
|
||||||
|
printf(e)
|
||||||
|
mobile_driver.refresh()
|
||||||
|
custom_sleep(30)
|
||||||
|
i -= 1
|
||||||
|
mobile_driver.quit()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e, mobile_driver)
|
||||||
|
mobile_driver.quit()
|
||||||
|
|
||||||
|
|
||||||
|
def daily_routine(custom = False):
|
||||||
|
try :
|
||||||
|
if not custom: # custom already login
|
||||||
|
login(driver)
|
||||||
|
except Banned :
|
||||||
|
log_error("This account is locked. Fix that. (-U ?)", driver)
|
||||||
|
return()
|
||||||
|
except Identity :
|
||||||
|
log_error("This account has an issue. Fix that.", driver)
|
||||||
|
return()
|
||||||
|
|
||||||
|
try:
|
||||||
|
all_cards()
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
|
||||||
|
try:
|
||||||
|
fidelity()
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
|
||||||
|
try:
|
||||||
|
bing_pc_search()
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
|
||||||
|
try:
|
||||||
|
bing_mobile_search()
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
|
||||||
|
try:
|
||||||
|
log_points(g._mail)
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
|
||||||
|
|
||||||
|
def dev():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def CustomStart():
|
||||||
|
if not g.islinux :
|
||||||
|
raise NameError('You need to be on linux to do that, due to the utilisation of a module named enquieries, sorry.')
|
||||||
|
global driver
|
||||||
|
|
||||||
|
system("clear") # clear from previous command to allow a clean choice
|
||||||
|
actions = ["tout", "daily", "pc", "mobile", "log_points","fidelity", "dev"]
|
||||||
|
Actions = enquiries.choose("quels Actions ?", actions, multi=True)
|
||||||
|
liste = select_accounts()
|
||||||
|
g.start_time = time() # Reset timer to the start of the actions
|
||||||
|
|
||||||
|
for cred in liste:
|
||||||
|
g._mail = cred[0]
|
||||||
|
g._password = cred[1]
|
||||||
|
if len(cred) == 3:
|
||||||
|
g._otp = TOTP(cred[2])
|
||||||
|
|
||||||
|
driver = firefox_driver()
|
||||||
|
driver.implicitly_wait(3)
|
||||||
|
if login(driver) != "STOP":
|
||||||
|
if "tout" in Actions:
|
||||||
|
daily_routine(True)
|
||||||
|
if "daily" in Actions:
|
||||||
|
try:
|
||||||
|
all_cards()
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
if "pc" in Actions:
|
||||||
|
try:
|
||||||
|
bing_pc_search()
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
if "mobile" in Actions:
|
||||||
|
try:
|
||||||
|
bing_mobile_search()
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
if "fidelity" in Actions:
|
||||||
|
try :
|
||||||
|
fidelity()
|
||||||
|
except Exception as e :
|
||||||
|
log_error(e)
|
||||||
|
if "dev" in Actions:
|
||||||
|
try:
|
||||||
|
dev()
|
||||||
|
except Exception as e:
|
||||||
|
printf(e)
|
||||||
|
break
|
||||||
|
if not "tout" in Actions:
|
||||||
|
try:
|
||||||
|
log_points(g._mail)
|
||||||
|
except Exception as e:
|
||||||
|
printf(f"CustomStart {e}")
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
|
||||||
|
if g.vnc_enabled :
|
||||||
|
display = SmartDisplay(backend="xvnc", size=(2160, 2160), rfbport=g.vnc_port, color_depth=24)
|
||||||
|
else :
|
||||||
|
display = SmartDisplay(size=(2160, 2160))
|
||||||
|
display.start()
|
||||||
|
|
||||||
|
|
||||||
|
if g.custom_start:
|
||||||
|
CustomStart()
|
||||||
|
elif g.unban:
|
||||||
|
g._mail, g._password = select_accounts(False)[0]
|
||||||
|
driver = firefox_driver()
|
||||||
|
try :
|
||||||
|
login(driver)
|
||||||
|
except Banned:
|
||||||
|
unban()
|
||||||
|
driver.quit()
|
||||||
|
elif g.points_file != "":
|
||||||
|
save_points_from_file(g.points_file)
|
||||||
|
else:
|
||||||
|
if g.update_version != "None":
|
||||||
|
if g.discord_enabled_error:
|
||||||
|
webhookFailure.send(f"Updated to {g.update_version}", username="UPDATE", avatar_url="https://cdn-icons-png.flaticon.com/512/1688/1688988.png")
|
||||||
|
for cred in g._cred:
|
||||||
|
g._mail = cred[0]
|
||||||
|
g._password = cred[1]
|
||||||
|
if len(cred) == 3:
|
||||||
|
g._otp = TOTP(cred[2])
|
||||||
|
custom_sleep(1)
|
||||||
|
printf("Début du driver.")
|
||||||
|
driver = firefox_driver()
|
||||||
|
printf("Driver demarré.")
|
||||||
|
driver.implicitly_wait(3)
|
||||||
|
try:
|
||||||
|
daily_routine()
|
||||||
|
driver.quit()
|
||||||
|
attente = uniform(1200, 3600)
|
||||||
|
printf(f"finis. attente de {round(attente/60)}min")
|
||||||
|
custom_sleep(attente)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
printf("Canceled. Closing driver and display.")
|
||||||
|
driver.quit()
|
||||||
|
display.stop()
|
||||||
|
except Exception as e:
|
||||||
|
printf(f"Error not catched. Skipping this account. {e}")
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
display.stop()
|
30
config
30
config
@ -1,30 +0,0 @@
|
|||||||
[PATH]
|
|
||||||
|
|
||||||
motpath = /usr/share/dict/french
|
|
||||||
logpath = /your/path/to/loginandpass.csv
|
|
||||||
|
|
||||||
[SETTINGS]
|
|
||||||
|
|
||||||
FidelityLink = Null
|
|
||||||
embeds = False
|
|
||||||
Headless = True
|
|
||||||
|
|
||||||
[DISCORD]
|
|
||||||
|
|
||||||
enabled = True
|
|
||||||
successlink = https://discord.com/api/webhooks/[put your webhook here]
|
|
||||||
errorlink =https://discord.com/api/webhooks/[put your webhook here]
|
|
||||||
|
|
||||||
[PROXY]
|
|
||||||
|
|
||||||
enabled = False
|
|
||||||
url = Null
|
|
||||||
port = 0
|
|
||||||
|
|
||||||
[SQL]
|
|
||||||
|
|
||||||
enabled = False
|
|
||||||
host = Null
|
|
||||||
database = MsRewards
|
|
||||||
usr = root
|
|
||||||
pwd = password
|
|
92
database.py
Normal file
92
database.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import mysql.connector
|
||||||
|
import configparser
|
||||||
|
from os import path
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"-f",
|
||||||
|
"--file",
|
||||||
|
help="Choose a file",
|
||||||
|
type=argparse.FileType('r')
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-m",
|
||||||
|
"--manual",
|
||||||
|
help="add point manually do database",
|
||||||
|
dest="manual",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
MANUAL = args.manual
|
||||||
|
|
||||||
|
config_path = "./user_data/config.cfg"
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(config_path)
|
||||||
|
|
||||||
|
sql_usr = config["SQL"]["usr"]
|
||||||
|
sql_pwd = config["SQL"]["pwd"]
|
||||||
|
sql_host = config["SQL"]["host"]
|
||||||
|
sql_database = config["SQL"]["database"]
|
||||||
|
|
||||||
|
|
||||||
|
mydb = mysql.connector.connect(
|
||||||
|
host=sql_host,
|
||||||
|
user=sql_usr,
|
||||||
|
password=sql_pwd,
|
||||||
|
database = sql_database
|
||||||
|
)
|
||||||
|
mycursor = mydb.cursor()
|
||||||
|
|
||||||
|
def add_account(name: str, endroit: str, proprio: str):
|
||||||
|
command = f'INSERT INTO comptes (compte, proprio, endroit, last_pts) VALUES ("{name}", "{proprio}", "{endroit}",0);'
|
||||||
|
mycursor.execute(command)
|
||||||
|
|
||||||
|
|
||||||
|
def ban_account(name: str, pts = 0):
|
||||||
|
|
||||||
|
command1 = f"INSERT INTO banned (nom, total) VALUES ('{name}', {pts});"
|
||||||
|
command2 = f'DELETE FROM comptes WHERE compte = "{name}";'
|
||||||
|
mycursor.execute(command1)
|
||||||
|
mycursor.execute(command2)
|
||||||
|
|
||||||
|
def update_pts(name: str, pts = 0):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not MANUAL :
|
||||||
|
print("ajouter un compte : 1\nban un compte : 2")
|
||||||
|
i = input()
|
||||||
|
if i == "1":
|
||||||
|
if args.file :
|
||||||
|
l =[x.split(",")[0].split("@")[0] for x in args.file.readlines()]
|
||||||
|
endroit = input("ou est le bot ? ")
|
||||||
|
proprio = input("qui est le proprio ? ")
|
||||||
|
for name in l :
|
||||||
|
add_account(name, endroit, proprio)
|
||||||
|
else :
|
||||||
|
name = input("quel est le nom ? ").split("@")[0]
|
||||||
|
endroit = input("ou est le bot ? ")
|
||||||
|
proprio = input("qui est le proprio ? ")
|
||||||
|
add_account(name, endroit, proprio)
|
||||||
|
elif i == '2':
|
||||||
|
name = input("quel est le compte qui a été ban ? ")
|
||||||
|
pts = input("il avait combien de points ? ")
|
||||||
|
ban_account(name, pts)
|
||||||
|
|
||||||
|
mydb.commit()
|
||||||
|
mycursor.close()
|
||||||
|
mydb.close()
|
||||||
|
else :
|
||||||
|
import modules.db as datab
|
||||||
|
config_path = f"{path.abspath(path.dirname(path.dirname( __file__ )))}/MsRewards/user_data/config.cfg"
|
||||||
|
print(config_path)
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(config_path)
|
||||||
|
sql_usr = config["SQL"]["usr"]
|
||||||
|
sql_pwd = config["SQL"]["pwd"]
|
||||||
|
sql_host = config["SQL"]["host"]
|
||||||
|
sql_database = config["SQL"]["database"]
|
||||||
|
account_name = input("compte ? ")
|
||||||
|
points = int(input("points ? "))
|
||||||
|
datab.add_to_database(account_name, points, sql_host, sql_usr, sql_pwd, sql_database)
|
125
main.py
125
main.py
@ -1,12 +1,24 @@
|
|||||||
#/usr/bin/python3.10
|
#/usr/bin/python3.10
|
||||||
|
from packaging.version import parse as parse_version
|
||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
print
|
import shutil
|
||||||
config_path = f"{os.path.abspath( os.path.dirname( __file__ ) )}/config"
|
import requests
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
|
|
||||||
|
try :
|
||||||
|
config_path = f"{os.path.abspath( os.path.dirname( __file__ ) )}/user_data/config.cfg"
|
||||||
|
if config.read(config_path)==[] :
|
||||||
|
raise NameError("le fichier n'existe pas")
|
||||||
|
except :
|
||||||
|
default_config = f"{os.path.abspath( os.path.dirname( __file__ ) )}/user_data/config.default"
|
||||||
|
shutil.copyfile(default_config, config_path)
|
||||||
config.read(config_path)
|
config.read(config_path)
|
||||||
|
|
||||||
|
|
||||||
def confirm(texte, default = False):
|
def confirm(texte, default = False):
|
||||||
if default :
|
if default :
|
||||||
txt = '[Y/n]'
|
txt = '[Y/n]'
|
||||||
@ -22,7 +34,7 @@ def confirm(texte, default = False):
|
|||||||
return False
|
return False
|
||||||
return default
|
return default
|
||||||
|
|
||||||
lang = "fr"
|
lang = "en"
|
||||||
|
|
||||||
text = {"fr" : {
|
text = {"fr" : {
|
||||||
"compte" : "entrer l'adresse mail du compte ",
|
"compte" : "entrer l'adresse mail du compte ",
|
||||||
@ -30,12 +42,12 @@ text = {"fr" : {
|
|||||||
"next" : "voulez vous ajouter un compte ? ",
|
"next" : "voulez vous ajouter un compte ? ",
|
||||||
"finc" : "comptes en cours d'ajout ",
|
"finc" : "comptes en cours d'ajout ",
|
||||||
"ajout" : "comptes ajouté ",
|
"ajout" : "comptes ajouté ",
|
||||||
"fidelity" : "avez vous un lien sur lequel le lien vers la page fidelité du mois est le seul contenu de la page ?",
|
"fidelity" : "avez vous un lien sur lequel le lien vers la page fidélité du mois est le seul contenu de la page ? ",
|
||||||
"lien" : "entrez le lien ",
|
"lien" : "entrez le lien ",
|
||||||
"discorde" : "voulez vous envoyer les points sur discord ?",
|
"discorde" : "voulez vous envoyer les erreurs sur discord ? ",
|
||||||
"w1" : "entrez le lien du WebHook pour envoyer les points (https://support.discord.com/hc/fr/articles/228383668-Utiliser-les-Webhooks)",
|
"w1" : "entrez le lien du WebHook pour envoyer les points ",
|
||||||
"w2" : "entrez le lien du WebHook pour envoyer les erreurs ",
|
"w2" : "entrez le lien du WebHook pour envoyer les erreurs ",
|
||||||
"msqle" : "voulez vous untiliser une base de donnée",
|
"msqle" : "voulez vous utiliser une base de donnée ",
|
||||||
"msqll" : "entrez le lien de la base de donnée ",
|
"msqll" : "entrez le lien de la base de donnée ",
|
||||||
"msqlu" : "entrez l'utilisateur de la base de donnée ",
|
"msqlu" : "entrez l'utilisateur de la base de donnée ",
|
||||||
"msqlp" : "entrez le mot de passe de la base de donnée ",
|
"msqlp" : "entrez le mot de passe de la base de donnée ",
|
||||||
@ -43,7 +55,24 @@ text = {"fr" : {
|
|||||||
"proxye" : "voulez vous utiliser un proxy ",
|
"proxye" : "voulez vous utiliser un proxy ",
|
||||||
"proxyl" : "entrez le lien du proxy ",
|
"proxyl" : "entrez le lien du proxy ",
|
||||||
"proxyp" : "entrez le port du proxy "
|
"proxyp" : "entrez le port du proxy "
|
||||||
|
},
|
||||||
|
"en" : {
|
||||||
|
"compte" : "enter email of an account",
|
||||||
|
"mdp" : "enter password of this account ",
|
||||||
|
"next" : "Add another account ? ",
|
||||||
|
"finc" : "Adding accounts ",
|
||||||
|
"ajout" : "Accounts added ",
|
||||||
|
"discorde" : "Do you want to use discord ? (Highly recommended as it's untested without)",
|
||||||
|
"w1" : "Enter Webhook link for sending points everyday",
|
||||||
|
"w2" : "Enter Webhook link for errors ",
|
||||||
|
"msqle" : "Do you want to use a database ? ",
|
||||||
|
"msqll" : "database link ",
|
||||||
|
"msqlu" : "database username ",
|
||||||
|
"msqlp" : "database password ",
|
||||||
|
"msqlt" : "database name (should be MsRewards) ",
|
||||||
|
"proxye" : "Do you want to use a proxy ",
|
||||||
|
"proxyl" : "Proxy address ",
|
||||||
|
"proxyp" : "Proxy port "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,25 +92,30 @@ def setup_comptes():
|
|||||||
if confirm(t["next"], default = True):
|
if confirm(t["next"], default = True):
|
||||||
compte = input(t["compte"])
|
compte = input(t["compte"])
|
||||||
mdp = input(t["mdp"])
|
mdp = input(t["mdp"])
|
||||||
lc.append(f"{compte},{mdp}\n")
|
lc.append(f"{compte},{mdp}")
|
||||||
else:
|
else:
|
||||||
print(t["finc"])
|
print(t["finc"])
|
||||||
break
|
break
|
||||||
f = open('./login.csv', "w")
|
f = open('./user_data/login.csv', "w")
|
||||||
for i in lc :
|
for i in lc :
|
||||||
f.write(i)
|
f.write(i)
|
||||||
|
f.write("\n")
|
||||||
f.close()
|
f.close()
|
||||||
print(t["ajout"])
|
print(t["ajout"])
|
||||||
|
edit_config_txt("logpath",f'{os.getcwd()}/user_data/login.csv')
|
||||||
#modifie le fichier de configuration
|
|
||||||
edit_config(3,f'{os.getcwd()}/login.csv')
|
|
||||||
|
|
||||||
|
|
||||||
def edit_config(ligne, contenu):
|
def edit_config_txt(ligne, contenu):
|
||||||
f = open(config_path, "r")
|
f = open(config_path, "r")
|
||||||
txt = f.readlines()
|
txt = f.readlines()
|
||||||
txt[ligne] = f'{txt[ligne].split("=")[0]}= {contenu}\n'
|
|
||||||
f.close()
|
f.close()
|
||||||
|
if txt.count(txt) >1:
|
||||||
|
raise NameError("Fail")
|
||||||
|
|
||||||
|
for i in range(len(txt)) :
|
||||||
|
name = txt[i].split(" = ")[0]
|
||||||
|
if name == ligne:
|
||||||
|
txt[i] = name + " = " + str(contenu) + "\n"
|
||||||
|
|
||||||
f = open(config_path, "w")
|
f = open(config_path, "w")
|
||||||
for i in txt :
|
for i in txt :
|
||||||
@ -90,50 +124,75 @@ def edit_config(ligne, contenu):
|
|||||||
|
|
||||||
|
|
||||||
def setup_settings():
|
def setup_settings():
|
||||||
general()
|
|
||||||
discord()
|
discord()
|
||||||
proxy()
|
proxy()
|
||||||
sql()
|
sql()
|
||||||
|
|
||||||
def general():
|
|
||||||
if confirm(t["fidelity"]):
|
|
||||||
lien = input(t["lien"])
|
|
||||||
edit_config(7,lien)
|
|
||||||
|
|
||||||
def discord():
|
def discord():
|
||||||
enabled = confirm(t["discorde"], default = True)
|
enabled = confirm(t["discorde"], default = True)
|
||||||
if enabled :
|
if enabled :
|
||||||
edit_config(13, True)
|
edit_config_txt("DiscordErrorEnabled", True)
|
||||||
|
|
||||||
|
edit_config_txt('DiscordSuccessEnabled', confirm("send success ?", default = True))
|
||||||
w1 = input(t["w1"])
|
w1 = input(t["w1"])
|
||||||
edit_config(14,w1)
|
edit_config_txt("successlink",w1)
|
||||||
w2 = input(t["w2"])
|
w2 = input(t["w2"])
|
||||||
edit_config(15,w2)
|
edit_config_txt("errorlink",w2)
|
||||||
|
|
||||||
|
|
||||||
def sql() :
|
def sql() :
|
||||||
enabled = confirm(t["msqle"], default = False)
|
enabled = confirm(t["msqle"], default = False)
|
||||||
if enabled :
|
if enabled :
|
||||||
edit_config(25, True)
|
edit_config_txt("sql_enabled", True)
|
||||||
lien = input(t["msqll"])
|
lien = input(t["msqll"])
|
||||||
edit_config(26,lien)
|
edit_config_txt("host",lien)
|
||||||
table = input(t["msqlt"])
|
table = input(t["msqlt"])
|
||||||
edit_config(27,table)
|
edit_config_txt("database",table)
|
||||||
user = input(t["msqlu"])
|
user = input(t["msqlu"])
|
||||||
edit_config(28,user)
|
edit_config_txt("usr",user)
|
||||||
pwd = input(t["msqlp"])
|
pwd = input(t["msqlp"])
|
||||||
edit_config(29,pwd)
|
edit_config_txt("pwd",pwd)
|
||||||
|
|
||||||
|
|
||||||
def proxy() :
|
def proxy() :
|
||||||
enabled = confirm(t["proxye"], default = False)
|
enabled = confirm(t["proxye"], default = False)
|
||||||
if enabled :
|
if enabled :
|
||||||
edit_config(19, True)
|
edit_config_txt("proxy_enabled", True)
|
||||||
lien = input(t["proxyl"])
|
lien = input(t["proxyl"])
|
||||||
edit_config(20,lien)
|
edit_config_txt("url",lien)
|
||||||
port = input(t["proxyp"])
|
port = input(t["proxyp"])
|
||||||
edit_config(21,port)
|
edit_config_txt("port",port)
|
||||||
|
|
||||||
|
|
||||||
|
def check_update(args):
|
||||||
|
try :
|
||||||
|
latest = requests.get("https://api.github.com/repos/piair338/MsRewards/releases").json()[0]["tag_name"]
|
||||||
|
latest = parse_version(latest)
|
||||||
|
except Exception as e :
|
||||||
|
print(e)
|
||||||
|
return (args)
|
||||||
|
f = open("./version", 'r')
|
||||||
|
txt = f.readlines()[0].replace("\n","")
|
||||||
|
f.close()
|
||||||
|
cur = parse_version(txt)
|
||||||
|
if not (cur < latest) :
|
||||||
|
print("Already up to date.")
|
||||||
|
return(args)
|
||||||
|
else :
|
||||||
|
print(f"updating to {latest}")
|
||||||
|
os.system("git reset --hard")
|
||||||
|
os.system("git pull")
|
||||||
|
os.system("python3 -m pip install -r requirements.txt > update.result")
|
||||||
|
print(f"updated to {latest}")
|
||||||
|
return(args + f" --version {latest}")
|
||||||
|
|
||||||
LogPath = config["PATH"]["logpath"]
|
LogPath = config["PATH"]["logpath"]
|
||||||
if LogPath == "/your/path/to/loginandpass.csv" :
|
if LogPath == "/your/path/to/loginandpass.csv" :
|
||||||
setup()
|
setup()
|
||||||
else :
|
else :
|
||||||
os.system("python3.10 V4.py")
|
args = " ".join(sys.argv[1::])
|
||||||
|
args = check_update(args)
|
||||||
|
os.system("python3 V5.py " + args)
|
||||||
|
34
modules/cards.py
Normal file
34
modules/cards.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
from modules.imports import *
|
||||||
|
|
||||||
|
|
||||||
|
def welcome_tour(elm, driver):
|
||||||
|
try :
|
||||||
|
driver.find_element(By.CSS_SELECTOR, '[class="welcome-tour-next-button c-call-to-action c-glyph"]').click()
|
||||||
|
except :
|
||||||
|
pass
|
||||||
|
driver.find_element(By.CSS_SELECTOR, '[class="quiz-link gray-button c-call-to-action c-glyph f-lightweight"]').click()
|
||||||
|
sleep(5)
|
||||||
|
driver.find_element(By.CSS_SELECTOR, '[class="c-glyph glyph-cancel"]').click()
|
||||||
|
elm.click()
|
||||||
|
driver.find_element(By.CSS_SELECTOR, '[class="quiz-link gray-button c-call-to-action c-glyph f-lightweight"]').click()
|
||||||
|
sleep(5)
|
||||||
|
driver.find_element(By.CSS_SELECTOR, '[class="c-glyph glyph-cancel"]').click()
|
||||||
|
elm.click()
|
||||||
|
driver.find_element(By.CSS_SELECTOR, '[class="quiz-link gray-button c-call-to-action c-glyph f-lightweight"]').click()
|
||||||
|
sleep(5)
|
||||||
|
driver.find_element(By.CSS_SELECTOR, '[class="c-glyph glyph-cancel"]').click()
|
||||||
|
|
||||||
|
def welcome_tour_NO(driver):
|
||||||
|
try :
|
||||||
|
driver.find_element(By.CSS_SELECTOR, '[class="welcome-tour-next-button c-call-to-action c-glyph"]').click()
|
||||||
|
except :
|
||||||
|
pass
|
||||||
|
driver.find_element(By.CSS_SELECTOR, '[class="c-glyph glyph-cancel"]').click()
|
||||||
|
sleep(5)
|
||||||
|
|
||||||
|
|
||||||
|
def spotify(driver):
|
||||||
|
sleep(5)
|
||||||
|
driver.find_element(By.CSS_SELECTOR, '[data-bi-id="spotify-premium gratuit"]').click()
|
||||||
|
sleep(5)
|
||||||
|
close_tab(driver.window_handles[1])
|
179
modules/config.py
Normal file
179
modules/config.py
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
#!/usr/bin/python3.10
|
||||||
|
from modules.driver_tools import *
|
||||||
|
from modules.imports import *
|
||||||
|
import modules.globals as g
|
||||||
|
"""
|
||||||
|
Setup for option, like --override or --fulllog
|
||||||
|
"""
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-o",
|
||||||
|
"--override",
|
||||||
|
help="override",
|
||||||
|
dest="override",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-u",
|
||||||
|
"--unban",
|
||||||
|
help="unban an account",
|
||||||
|
dest="unban",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-l",
|
||||||
|
"--log",
|
||||||
|
dest="log",
|
||||||
|
help="enable logging in terminal",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-fl",
|
||||||
|
"--fulllog",
|
||||||
|
dest="fulllog",
|
||||||
|
help="enable full logging in discord",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-c",
|
||||||
|
"--config",
|
||||||
|
help="Choose a specific config file",
|
||||||
|
default=""
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-a",
|
||||||
|
"--add-points",
|
||||||
|
help="Add points to the database from a file and exit",
|
||||||
|
dest="points_file",
|
||||||
|
default=""
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-v",
|
||||||
|
"--vnc",
|
||||||
|
help="enable VNC",
|
||||||
|
dest="vnc",
|
||||||
|
default="None"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--version",
|
||||||
|
help="display a message on discord to tell that the bot have been updated",
|
||||||
|
dest="update_version",
|
||||||
|
default="None"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--dev",
|
||||||
|
help="dev option",
|
||||||
|
dest="dev",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
g.custom_start = args.override
|
||||||
|
g.unban = args.unban
|
||||||
|
g.log = args.log
|
||||||
|
g.full_log = args.fulllog
|
||||||
|
g.dev = args.dev
|
||||||
|
|
||||||
|
if g.custom_start :
|
||||||
|
g.log = True
|
||||||
|
|
||||||
|
g.vnc_enabled = args.vnc != "None"
|
||||||
|
g.vnc_port = args.vnc
|
||||||
|
g.points_file = args.points_file
|
||||||
|
g.update_version = args.update_version
|
||||||
|
# global variables used later in the code
|
||||||
|
g.islinux = platform == "linux" # if the computer running this program is Linux, it allow more things
|
||||||
|
g.start_time = time()
|
||||||
|
|
||||||
|
#reading configuration
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
|
||||||
|
if args.config :
|
||||||
|
try :
|
||||||
|
config_path =f"{path.abspath(path.dirname(path.dirname( __file__ )))}/user_data/config{args.config}.cfg"
|
||||||
|
config.read(config_path)
|
||||||
|
g.mot_path = config["PATH"]["motpath"]
|
||||||
|
except :
|
||||||
|
config_path = path.abspath(args.config)
|
||||||
|
config.read(config_path)
|
||||||
|
else :
|
||||||
|
config_path = f"{path.abspath(path.dirname(path.dirname( __file__ )))}/user_data/config.cfg"
|
||||||
|
config.read(config_path)
|
||||||
|
|
||||||
|
|
||||||
|
# path configurations
|
||||||
|
g.mot_path = config["PATH"]["motpath"]
|
||||||
|
g.credential_path = config["PATH"]["logpath"]
|
||||||
|
|
||||||
|
|
||||||
|
# discord configuration
|
||||||
|
g.discord_success_link = config["DISCORD"]["successlink"]
|
||||||
|
g.discord_error_link = config["DISCORD"]["errorlink"]
|
||||||
|
g.discord_enabled_error = config["DISCORD"]["DiscordErrorEnabled"] == "True"
|
||||||
|
g.discord_enabled_success = config["DISCORD"]["DiscordSuccessEnabled"]== "True"
|
||||||
|
try :
|
||||||
|
g.avatar_url = config["OTHER"]["avatar"]
|
||||||
|
except :
|
||||||
|
g.avatar_url = "https://cdn.discordapp.com/icons/793934298977009674/d8055bccef6eca4855c349e808d0d788.webp"
|
||||||
|
|
||||||
|
if g.discord_enabled_error:
|
||||||
|
webhookFailure = Webhook.from_url(g.discord_error_link, adapter=RequestsWebhookAdapter())
|
||||||
|
if g.discord_enabled_success:
|
||||||
|
webhookSuccess = Webhook.from_url(g.discord_success_link, adapter=RequestsWebhookAdapter())
|
||||||
|
|
||||||
|
# base settings
|
||||||
|
g.fidelity_link = config["SETTINGS"]["FidelityLink"]
|
||||||
|
g.discord_embed = config["SETTINGS"]["embeds"] == "True" #print new point value in an embed
|
||||||
|
g.headless = config["SETTINGS"]["headless"] == "True"
|
||||||
|
|
||||||
|
# proxy settings
|
||||||
|
g.proxy_enabled = config["PROXY"]["proxy_enabled"] == "True"
|
||||||
|
g.proxy_address = config["PROXY"]["url"]
|
||||||
|
g.proxy_port = config["PROXY"]["port"]
|
||||||
|
|
||||||
|
# MySQL settings
|
||||||
|
g.sql_enabled = config["SQL"]["sql_enabled"] == "True"
|
||||||
|
g.sql_usr = config["SQL"]["usr"]
|
||||||
|
g.sql_pwd = config["SQL"]["pwd"]
|
||||||
|
g.sql_host = config["SQL"]["host"]
|
||||||
|
g.sql_database = config["SQL"]["database"]
|
||||||
|
|
||||||
|
|
||||||
|
try :
|
||||||
|
g.database_error_override = config["OTHER"]["database_override"] == "True"
|
||||||
|
except :
|
||||||
|
pass
|
||||||
|
|
||||||
|
try :
|
||||||
|
g.fast = config["OTHER"]["fast"] == "True"
|
||||||
|
except :
|
||||||
|
pass
|
||||||
|
h = open(g.mot_path, "r", encoding="utf-8")
|
||||||
|
lines = h.readlines()
|
||||||
|
if len(lines) < 3 :
|
||||||
|
Liste_de_mot = list(lines[0].split(","))
|
||||||
|
else :
|
||||||
|
Liste_de_mot = [x.replace('\n', "") for x in lines]
|
||||||
|
h.close()
|
||||||
|
|
||||||
|
|
||||||
|
with open(g.credential_path) as f:
|
||||||
|
reader = reader(f)
|
||||||
|
Credentials = list(reader)
|
||||||
|
shuffle(Credentials)
|
||||||
|
g._cred = Credentials
|
||||||
|
|
||||||
|
if g.proxy_enabled :
|
||||||
|
setup_proxy(g.proxy_address,g.proxy_port)
|
70
modules/db.py
Normal file
70
modules/db.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import mysql.connector
|
||||||
|
|
||||||
|
|
||||||
|
def add_row(compte, points, mycursor, mydb):
|
||||||
|
sql = "INSERT INTO daily (compte, points, date) VALUES (%s, %s, current_date())"
|
||||||
|
val = (compte, points)
|
||||||
|
mycursor.execute(sql, val)
|
||||||
|
mydb.commit()
|
||||||
|
#printf(mycursor.rowcount, "record created.")
|
||||||
|
|
||||||
|
|
||||||
|
def update_row(compte, points, mycursor, mydb):
|
||||||
|
sql = f"UPDATE daily SET points = {points} WHERE compte = '{compte}' AND date = current_date() ;"
|
||||||
|
mycursor.execute(sql)
|
||||||
|
mydb.commit()
|
||||||
|
#printf(mycursor.rowcount, "record(s) updated")
|
||||||
|
|
||||||
|
|
||||||
|
def update_last(compte, points, mycursor, mydb):
|
||||||
|
sql = f"UPDATE comptes SET last_pts = {points} WHERE compte = '{compte}';"
|
||||||
|
mycursor.execute(sql)
|
||||||
|
mydb.commit()
|
||||||
|
#printf(mycursor.rowcount, "record(s) updated")
|
||||||
|
|
||||||
|
|
||||||
|
def get_row(compte, points, mycursor, same_points = True): #return if there is a line with the same ammount of point or with the same name as well as the same day
|
||||||
|
if same_points :
|
||||||
|
mycursor.execute(f"SELECT * FROM daily WHERE points = {points} AND compte = '{compte}' AND date = current_date() ;")
|
||||||
|
else :
|
||||||
|
mycursor.execute(f"SELECT * FROM daily WHERE compte = '{compte}' AND date = current_date() ;")
|
||||||
|
myresult = mycursor.fetchall()
|
||||||
|
return(len(myresult) == 1)
|
||||||
|
|
||||||
|
|
||||||
|
def add_to_database(compte, points, sql_host,sql_usr,sql_pwd,sql_database, save_if_fail=True):
|
||||||
|
if points is None:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
mydb = mysql.connector.connect(
|
||||||
|
host=sql_host,
|
||||||
|
user=sql_usr,
|
||||||
|
password=sql_pwd,
|
||||||
|
database = sql_database
|
||||||
|
)
|
||||||
|
mycursor = mydb.cursor()
|
||||||
|
|
||||||
|
if get_row(compte, points,mycursor, True): #check if the row exist with the same ammount of points and do nothind if it does
|
||||||
|
#printf("les points sont deja bon")
|
||||||
|
#return(0)
|
||||||
|
pass
|
||||||
|
elif get_row(compte, points,mycursor, False) : #check if the row exist, but without the same ammount of points and update the point account then
|
||||||
|
update_row(compte, points,mycursor,mydb)
|
||||||
|
#printf("row updated")
|
||||||
|
#return(1)
|
||||||
|
else : # if the row don't exist, create it with the good ammount of points
|
||||||
|
add_row(compte, points,mycursor,mydb)
|
||||||
|
#return(2) #printf("row added")
|
||||||
|
if int(points) > 10 :
|
||||||
|
update_last(compte, points, mycursor, mydb)
|
||||||
|
|
||||||
|
mycursor.close()
|
||||||
|
mydb.close()
|
||||||
|
except BaseException as e:
|
||||||
|
if save_if_fail:
|
||||||
|
print("\nLes points n'ont pas pu être ajoutés, enregistrement dans le fichier 'points.csv'\n")
|
||||||
|
with open("points.csv", "a") as file:
|
||||||
|
file.write(f"{compte},{points}\n")
|
||||||
|
raise e
|
||||||
|
|
63
modules/driver_tools.py
Normal file
63
modules/driver_tools.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
from modules.imports import *
|
||||||
|
from modules.config import *
|
||||||
|
from modules.tools import *
|
||||||
|
import modules.globals as g
|
||||||
|
|
||||||
|
def setup_proxy(ip, port) :
|
||||||
|
PROXY = f"{ip}:{port}"
|
||||||
|
webdriver.DesiredCapabilities.FIREFOX['proxy'] = {
|
||||||
|
"httpProxy": PROXY,
|
||||||
|
"sslProxy": PROXY,
|
||||||
|
"proxyType": "MANUAL",
|
||||||
|
}
|
||||||
|
|
||||||
|
#Deal with rgpd popup as well as some random popup like 'are you satisfied' one
|
||||||
|
def rgpd_popup(driver) -> None:
|
||||||
|
for i in ["bnp_btn_accept", "bnp_hfly_cta2", "bnp_hfly_close"] :
|
||||||
|
try:
|
||||||
|
driver.find_element(By.ID, i).click()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# save webdriver cookies
|
||||||
|
def save_cookies(driver):
|
||||||
|
if g.dev:
|
||||||
|
f = open(f"{'/'.join(__file__.split('/')[:-2])}/user_data/cookies/{g._mail}_unsafe.pkl", "w")
|
||||||
|
for i in driver.get_cookies():
|
||||||
|
f.write(str(i) + "\n")
|
||||||
|
f.close()
|
||||||
|
else :
|
||||||
|
pickle.dump(driver.get_cookies(), open(f"{'/'.join(__file__.split('/')[:-2])}/user_data/cookies/{g._mail}.pkl", "wb"))
|
||||||
|
|
||||||
|
# load cookies previously saved to the driver
|
||||||
|
def load_cookies(driver):
|
||||||
|
if g.dev:
|
||||||
|
f = open(f"{'/'.join(__file__.split('/')[:-2])}/user_data/cookies/{g._mail}_unsafe.pkl", "r")
|
||||||
|
lines = f.readlines()
|
||||||
|
f.close()
|
||||||
|
cookies = [literal_eval(x) for x in lines]
|
||||||
|
else :
|
||||||
|
cookies = pickle.load(open(f"{'/'.join(__file__.split('/')[:-2])}/user_data/cookies/{g._mail}.pkl", "rb"))
|
||||||
|
for cookie in cookies:
|
||||||
|
driver.add_cookie(cookie)
|
||||||
|
|
||||||
|
"""
|
||||||
|
send_keys_wait([selenium element:element, str:keys]) send the different keys to the field element, with a random time between each press to simulate human action.
|
||||||
|
keys can be an string, but also selenium keys
|
||||||
|
"""
|
||||||
|
def send_keys_wait(element, keys):
|
||||||
|
for i in keys:
|
||||||
|
element.send_keys(i)
|
||||||
|
sleep(uniform(0.1, 0.3))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Wait for the presence of the element identifier or [timeout]s
|
||||||
|
def wait_until_visible(search_by: str, identifier: str, timeout = 20, browser = None) -> None:
|
||||||
|
try :
|
||||||
|
WebDriverWait(browser, timeout).until(EC.visibility_of_element_located((search_by,identifier)), "element not found")
|
||||||
|
return(True)
|
||||||
|
except TimeoutException as e:
|
||||||
|
printf(f"element not found after {timeout}s")
|
||||||
|
return(False)
|
||||||
|
|
8
modules/error.py
Normal file
8
modules/error.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
class Banned(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class NotBanned(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Identity(Exception):
|
||||||
|
pass
|
38
modules/globals.py
Normal file
38
modules/globals.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
driver = None
|
||||||
|
_mail = '_mail temp'
|
||||||
|
_password = '_password temp'
|
||||||
|
_otp = '_otp temp'
|
||||||
|
display = None
|
||||||
|
_cred = []
|
||||||
|
custom_start = False
|
||||||
|
unban = False
|
||||||
|
log = False
|
||||||
|
full_log = False
|
||||||
|
vnc_enabled = False
|
||||||
|
vnc_port = 0
|
||||||
|
points_file = "/"
|
||||||
|
update_version = False
|
||||||
|
islinux = True
|
||||||
|
start_time = 0
|
||||||
|
mot_path = "/"
|
||||||
|
credential_path = "/"
|
||||||
|
discord_success_link = "https://example.com"
|
||||||
|
discord_error_link = "https://example.com"
|
||||||
|
discord_enabled_error = False
|
||||||
|
discord_enabled_success = False
|
||||||
|
avatar_url = ""
|
||||||
|
fidelity_link = "None"
|
||||||
|
discord_embed = False
|
||||||
|
headless = False
|
||||||
|
proxy_enabled = False
|
||||||
|
proxy_address = "0.0.0.0"
|
||||||
|
proxy_port = "0"
|
||||||
|
sql_enabled = False
|
||||||
|
sql_usr = "None"
|
||||||
|
sql_pwd = "azerty"
|
||||||
|
sql_host = "https://example.com"
|
||||||
|
sql_database = "MsRewards"
|
||||||
|
dev = False
|
||||||
|
norvege = False
|
||||||
|
database_error_override = False
|
||||||
|
fast = False
|
33
modules/imports.py
Normal file
33
modules/imports.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import argparse
|
||||||
|
import asyncio
|
||||||
|
import configparser
|
||||||
|
import pickle
|
||||||
|
from csv import reader
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from os import path, sys, system
|
||||||
|
from random import choice, randint, shuffle, uniform
|
||||||
|
from re import findall, search
|
||||||
|
from sys import platform
|
||||||
|
from time import sleep, time
|
||||||
|
|
||||||
|
from discord import Colour, Embed, File, RequestsWebhookAdapter, Webhook
|
||||||
|
from pyotp import TOTP
|
||||||
|
from pyvirtualdisplay import Display
|
||||||
|
from pyvirtualdisplay.smartdisplay import SmartDisplay
|
||||||
|
from requests import get
|
||||||
|
from selenium import webdriver
|
||||||
|
from selenium.common import exceptions
|
||||||
|
from selenium.common.exceptions import (ElementClickInterceptedException,
|
||||||
|
NoSuchElementException,
|
||||||
|
StaleElementReferenceException,
|
||||||
|
TimeoutException, WebDriverException)
|
||||||
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.common.keys import Keys
|
||||||
|
from selenium.webdriver.firefox.options import Options
|
||||||
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
from selenium.webdriver.support.ui import Select, WebDriverWait
|
||||||
|
from ast import literal_eval
|
||||||
|
try:
|
||||||
|
import enquiries
|
||||||
|
except:
|
||||||
|
system("") # enable colors in windows cmd
|
82
modules/tools.py
Normal file
82
modules/tools.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
from modules.imports import *
|
||||||
|
from modules.config import *
|
||||||
|
from modules.db import *
|
||||||
|
import modules.globals as g
|
||||||
|
# add the time arround the text given in [text]&
|
||||||
|
def Timer(text: str) -> str:
|
||||||
|
return(f"[{g._mail.split('@')[0]} - {datetime.today().strftime('%d/%m')} - {timedelta(seconds = round(float(time() - g.start_time)))}] " + str(text))
|
||||||
|
|
||||||
|
|
||||||
|
# replace the function print, with more options
|
||||||
|
# [txt] : string, [driver] : selenium webdriver
|
||||||
|
def printf(txt):
|
||||||
|
print(Timer(txt))
|
||||||
|
|
||||||
|
|
||||||
|
# return current page domain
|
||||||
|
def get_domain(driver):
|
||||||
|
return(driver.current_url.split("/")[2])
|
||||||
|
|
||||||
|
|
||||||
|
# check if the user is using IPV4 using ipify.org
|
||||||
|
# [driver] : selenium webdriver
|
||||||
|
# never used here
|
||||||
|
# can be useful as Ms had issues with IPV6 at some point
|
||||||
|
def check_ipv4(driver):
|
||||||
|
driver.get("https://api64.ipify.org")
|
||||||
|
elm = driver.find_element(By.TAG_NAME, "body")
|
||||||
|
if len(elm.text.split('.')) == 4 :
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def custom_sleep(temps):
|
||||||
|
try :
|
||||||
|
if g.log : #only print sleep when user see it
|
||||||
|
points = ["⢿", "⣻", "⣽", "⣾", "⣷", "⣯", "⣟", "⡿"]
|
||||||
|
passe = 0
|
||||||
|
for i in range(int(temps)):
|
||||||
|
for i in range(8):
|
||||||
|
sleep(0.125)
|
||||||
|
passe += 0.125
|
||||||
|
print(f"{points[i]} - {round(float(temps) - passe, 3)}", end="\r")
|
||||||
|
print(" ", end="\r")
|
||||||
|
else:
|
||||||
|
sleep(temps)
|
||||||
|
except KeyboardInterrupt :
|
||||||
|
print("attente annulée")
|
||||||
|
|
||||||
|
|
||||||
|
def format_error(e) -> str:
|
||||||
|
tb = e.__traceback__
|
||||||
|
txt = ""
|
||||||
|
while tb != None :
|
||||||
|
txt = txt + f" -> {tb.tb_frame.f_code.co_name} ({tb.tb_lineno}) "
|
||||||
|
tb = tb.tb_next
|
||||||
|
return(txt + "\n" + str(e))
|
||||||
|
|
||||||
|
|
||||||
|
def progressBar(current, total=30, barLength=20, name="Progress"):
|
||||||
|
percent = float(current + 1) * 100 / total
|
||||||
|
arrow = "-" * int(percent / 100 * barLength - 1) + ">"
|
||||||
|
spaces = " " * (barLength - len(arrow))
|
||||||
|
print(name + ": [%s%s] %d %%" % (arrow, spaces, percent), end="\r")
|
||||||
|
|
||||||
|
|
||||||
|
def save_points_from_file(file):
|
||||||
|
with open(file) as f:
|
||||||
|
read = reader(f)
|
||||||
|
points_list = list(read)
|
||||||
|
for item in points_list:
|
||||||
|
compte, points = item[0], item[1]
|
||||||
|
add_to_database(compte, points, g.sql_host,g.sql_usr,g.sql_pwd,g.sql_database, save_if_fail=False)
|
||||||
|
with open(file, "w") as f:
|
||||||
|
f.write("")
|
||||||
|
|
||||||
|
|
||||||
|
def select_accounts(multiple = True):
|
||||||
|
system("clear") # clear from previous command to allow a clean choice
|
||||||
|
emails = [x[0] for x in g._cred] # list of all email adresses
|
||||||
|
emails_selected = enquiries.choose(f"quel{'s' if multiple else ''} compte{'s' if multiple else ''} ?", emails, multi=multiple)
|
||||||
|
return([x for x in g._cred if x[0] in emails_selected])
|
||||||
|
|
@ -1,6 +1,12 @@
|
|||||||
mysql-connector-python
|
mysql-connector-python
|
||||||
argparse
|
argparse
|
||||||
discord.py==1.7.3
|
discord.py==1.7.3
|
||||||
discord==1.7.3
|
|
||||||
selenium
|
selenium
|
||||||
enquiries
|
enquiries
|
||||||
|
rich
|
||||||
|
requests
|
||||||
|
pyvirtualdisplay
|
||||||
|
pillow
|
||||||
|
EasyProcess
|
||||||
|
pyotp
|
||||||
|
packaging
|
||||||
|
37
user_data/config.default
Normal file
37
user_data/config.default
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
[PATH]
|
||||||
|
|
||||||
|
motpath = /usr/share/dict/french
|
||||||
|
logpath = /your/path/to/loginandpass.csv
|
||||||
|
|
||||||
|
[SETTINGS]
|
||||||
|
|
||||||
|
embeds = False
|
||||||
|
Headless = True
|
||||||
|
|
||||||
|
[DISCORD]
|
||||||
|
|
||||||
|
DiscordErrorEnabled = True
|
||||||
|
DiscordSuccessEnabled = True
|
||||||
|
successlink = https://discord.com/api/webhooks/[put your webhook here]
|
||||||
|
errorlink = https://discord.com/api/webhooks/[put your webhook here]
|
||||||
|
|
||||||
|
[PROXY]
|
||||||
|
|
||||||
|
proxy_enabled = False
|
||||||
|
url = Null
|
||||||
|
port = 0
|
||||||
|
|
||||||
|
[SQL]
|
||||||
|
|
||||||
|
sql_enabled = False
|
||||||
|
host = Null
|
||||||
|
database = MsRewards
|
||||||
|
usr = root
|
||||||
|
pwd = password
|
||||||
|
|
||||||
|
[OTHER]
|
||||||
|
|
||||||
|
avatar = https://cdn.discordapp.com/icons/793934298977009674/d8055bccef6eca4855c349e808d0d788.webp
|
||||||
|
ipv6 = False
|
||||||
|
database_override = False
|
||||||
|
fast = False
|
4
user_data/cookies/.gitignore
vendored
Normal file
4
user_data/cookies/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
Reference in New Issue
Block a user