93 Commits

Author SHA1 Message Date
a197d44768 Added package tracking card 2024-06-18 20:37:14 +02:00
c7e110787b Debugging auto update 2024-05-16 17:52:12 +02:00
1db1de9606 oops, bad syntax 2024-05-15 10:03:01 +02:00
10d75f9d78 version bump 2024-05-15 09:52:56 +02:00
87fb791436 Explore on Bing: solve more challenges 2024-05-15 09:52:39 +02:00
6995bde8a6 Actualiser version 2024-05-12 18:55:51 +02:00
92069a013e Merge pull request 'Handle Streak-Protection Popup' (#12) from streak-protection into master
Reviewed-on: https://gitea.augustin64.fr/piair/MsRewards-Reborn/pulls/12
2024-05-12 18:55:34 +02:00
906d3e7822 catch another exception 2024-05-12 18:16:24 +02:00
778adc67d2 Et si on l'active ? 2024-05-12 18:07:36 +02:00
402b8cd3ef Close streak protection 2024-05-12 17:55:56 +02:00
3c74aa025e trying to fix web start 2024-04-26 16:06:33 +02:00
3123a1dbd0 trying to auto update chrome + fixed web override ? 2024-04-26 15:51:52 +02:00
27d45e88dd added more exploreOnBing 2024-04-26 15:43:23 +02:00
0685a42922 making scripts executables 2024-04-26 15:37:32 +02:00
6729703827 auto update hooks 2024-04-26 15:35:37 +02:00
304a222de1 fixed readlines ? 2024-04-26 14:51:23 +02:00
04d33f4ecd fixed readlines ? 2024-04-26 14:45:41 +02:00
ac22814605 fixed readlines ? 2024-04-26 14:41:00 +02:00
b7a89b56d0 fixed typo 2024-04-26 14:36:04 +02:00
127d16afea Changed version update logik 2024-04-26 14:32:01 +02:00
9bcbc81c2f Changement des musiques pour celles que Bing connais 2024-04-26 14:13:50 +02:00
12eba5cde7 top habitué au JS 2024-04-26 14:00:33 +02:00
22ea727c47 progress 2024-04-26 13:55:20 +02:00
05a1fd8557 pourquoi les custom attributes 2024-04-26 12:12:28 +02:00
39e226b564 pourquoi les custom attributes 2024-04-26 12:06:55 +02:00
cd00c8ca88 detecting which cutom search to do 2024-04-26 12:03:49 +02:00
43793d2c2c detecting which cutom search to do 2024-04-26 11:51:50 +02:00
ee6ec458fc switched to login on custom start 2024-04-26 11:27:16 +02:00
e6e8bdaa06 Trying to find out why the part1 is starting 2024-04-26 11:17:18 +02:00
9bd96ad876 version bump 2024-04-13 11:09:30 +02:00
ed517e7b03 Fix 'Welcome Tour'
Reviewed-on: https://gitea.augustin64.fr/piair/MsRewards-Reborn/pulls/11
2024-04-13 11:07:04 +02:00
01416c0e11 Check before scrolling, but should be good to merge ! 2024-04-13 11:01:25 +02:00
19cf77c6bd make an actual choice 2024-04-13 10:57:32 +02:00
feb7834d1f sleep a bit 2024-04-13 10:26:53 +02:00
1ea1ff776e wait until visible 2024-04-13 10:26:21 +02:00
ae023688a4 welcome-tour: URL check 2024-04-13 10:15:26 +02:00
fcb40537dc Use newer method 2024-04-13 10:06:21 +02:00
d7d2f49a3f pas la seule occurrence évidemment 2024-04-12 23:46:26 +02:00
68b5de32d6 fix de la popup "explorer le programme"
peu satisfaisant, consiste juste à fermer la popup à chaque fois qu'elle apparaît sans s'en débarrasser définitivement
2024-04-12 23:28:46 +02:00
c5e9fb1267 version bump ? 2024-04-12 20:01:53 +02:00
6ade73617a Make console resizable 2024-04-12 20:01:34 +02:00
37e8f6f61b Fix fake_popen 2024-04-12 16:20:25 +02:00
db6fa9b6b0 Add env variables arguments to flask app
- NO_SUBPROCESS to fake subprocesses calls
- APP_ROOT to use the app outside of Docker
2024-04-12 15:55:14 +02:00
d6988c03b4 Oops.. fixed nginx config 2024-04-12 15:51:20 +02:00
449d2da410 Use a different profile for mobile browser 2024-04-12 09:31:50 +02:00
3eb193eca3 Disable stream buffering on upstream nginx server 2024-04-10 14:24:57 +02:00
f566b2eeda Potentially fix no-discord 2024-04-10 12:15:14 +02:00
52e88f81b9 More checks on TFA 2024-04-10 12:14:41 +02:00
1a8137783c Even faster cached build 2024-04-10 11:43:54 +02:00
6f13b2532d Add clean.sh 2024-04-10 11:36:33 +02:00
3978c44bbc Use chrome profile to change language 2024-04-10 11:35:46 +02:00
49dc53ed32 Fix 2FA (language setting is not always working) 2024-04-10 11:04:54 +02:00
ba66a96c65 Check if creds are not empty 2024-04-10 10:29:53 +02:00
db157771de Merge flags 2024-04-08 16:51:43 +02:00
cbd1ad93a6 version bump 2024-04-08 16:38:16 +02:00
afabd94f0d Re-implement cookie login
with chrome profiles
2024-04-08 16:36:38 +02:00
81deaf05b0 Update chrome.deb URL 2024-04-08 16:34:36 +02:00
9af0f4aadb build.sh: check permissions 2024-04-03 15:42:14 +02:00
1d16294c04 Add more logs (custom.txt..) to logs view 2024-04-03 15:34:46 +02:00
fae2033061 Simplify Dockerfile to cache building layers 2024-04-03 15:26:02 +02:00
50c4036c73 Merge pull request 'Fix ANSI code formatting' (#9) from augustin64/MsRewards-Reborn:master into master
Reviewed-on: https://gitea.augustin64.fr/piair/MsRewards-Reborn/pulls/9
2024-03-31 12:05:17 +02:00
c683472895 Fix ANSI code formatting 2024-03-29 16:53:59 +01:00
178f2d472a Merge pull request 'Mise en forme des codes ANSI dans les logs' (#8) from augustin64/MsRewards-Reborn:augustin64-ansi-up into master
Reviewed-on: https://gitea.augustin64.fr/piair/MsRewards-Reborn/pulls/8
2024-03-28 23:56:33 +01:00
d3137f858a Mise en forme des codes ANSI dans les logs 2024-03-25 10:41:34 +01:00
d2ad467d4e Oops, pas push le bon fichier 2024-03-24 20:30:09 +01:00
b45e9e549f mise à jour automatique de chrome 2024-03-24 20:27:40 +01:00
200b0d8a86 Trying to fix an issue when an account fail 2024-03-05 21:18:39 +01:00
4a5af6455d Fixed 2FA issue + added better logs 2024-03-01 17:29:15 +01:00
49b691d736 not tested enough apparently 2024-02-28 18:22:15 +01:00
9549a6dea3 not tested enough apparently 2024-02-28 18:12:32 +01:00
8c224793b0 resize terminal on animation end 2024-02-28 15:04:19 +01:00
36fd92f71c fix json start 2024-02-28 14:44:11 +01:00
0a02eb2033 fix json start 2024-02-28 14:44:01 +01:00
6122d9ee13 fix json start + improve logs 2024-02-28 14:40:12 +01:00
a590d0f1b5 fix json start 2024-02-28 14:29:21 +01:00
64a018044c event on resize 2024-02-28 14:26:25 +01:00
91e7f31bac size issues 2024-02-28 14:17:55 +01:00
295f6d114d size issues 2024-02-28 14:14:29 +01:00
49e0d1b599 the issue seemed to be linked to the use of table instead of div 2024-02-28 14:13:19 +01:00
83eea03c73 the issue seemed to be linked to the use of table instead of div 2024-02-28 14:13:16 +01:00
3d096ec34c close to working 2024-02-28 12:09:32 +01:00
7bdf229fa8 je connais pas le js moi 2024-02-28 12:02:41 +01:00
0abb8a3494 weird errors 2024-02-28 12:00:19 +01:00
231c3b34e3 better terminal 2024-02-28 11:55:55 +01:00
c72aaf3fcc better terminal 2024-02-28 11:53:23 +01:00
675e67055b better terminal 2024-02-28 11:41:16 +01:00
a089fdfdf1 better terminal 2024-02-28 11:41:13 +01:00
0f60d67951 obsolete : module 2024-02-28 11:38:32 +01:00
1c7d1dfcd6 typo 2024-02-28 11:38:21 +01:00
19606e5f4c I'm just a clown, I didn't update... 2024-02-28 11:15:09 +01:00
aaafbb2257 some debug don't work at all ? 2024-02-28 11:13:09 +01:00
8d332f1c3c check if discord works 2024-02-28 11:10:48 +01:00
ced633dd68 refactored log_points 2024-02-28 10:49:27 +01:00
40 changed files with 1088 additions and 1746 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
**/.venv
user_data/*

8
.gitignore vendored
View File

@ -3,15 +3,17 @@ geckodriver.log
.vscode/ .vscode/
.idea .idea
venv venv
**/.venv
/Git /Git
page.html page.html
screenshot.png screenshot.png
login.csv
data data
**/__pycache__ **/__pycache__
user_data/* user_data/*
install.sh install.sh
nohup.out nohup.out
points.csv
file.png file.png
user_data/configs.json *.ts
LICENSE
README.md
venv

View File

@ -1,22 +1,43 @@
FROM python:3.10 FROM python:3.10
ENV DEBIAN_FRONTEND noninteractive ENV DEBIAN_FRONTEND noninteractive
WORKDIR /app/ WORKDIR /app/
# Initial apt install
RUN apt update
RUN apt install -y libgtk-4-1 libvulkan1 libxdamage1 \
novnc websockify xvfb nginx nano tzdata \
sqlite3 apt-transport-https software-properties-common \
wget wfrench tigervnc-standalone-server libasound2 \
libatk-bridge2.0-0 libnss3 libnspr4 xvfb libgbm1 libatk1.0-0 \
libu2f-udev libatspi2.0-0 libcups2 libxkbcommon0 libxrandr2 \
libdbus-1-3 xdg-utils fonts-liberation libdrm2
# Additional repos and packages
RUN wget http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.0g-2ubuntu4_amd64.deb \
&& dpkg -i libssl1.1_1.1.0g-2ubuntu4_amd64.deb
RUN curl -sSL http://mirror.cs.uchicago.edu/google-chrome/pool/main/g/google-chrome-stable/google-chrome-stable_123.0.6312.86-1_amd64.deb -o chrome.deb \
&& dpkg -i chrome.deb
RUN ln -fs /usr/share/zoneinfo/Europe/Paris /etc/localtime
RUN wget -q -O /usr/share/keyrings/grafana.key https://apt.grafana.com/gpg.key \
&& echo "deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main" | tee -a /etc/apt/sources.list.d/grafana.list
RUN curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
# Install from new repo
RUN apt update \ RUN apt update \
&& wget http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.0g-2ubuntu4_amd64.deb \ && apt install -y redis grafana
&& dpkg -i libssl1.1_1.1.0g-2ubuntu4_amd64.deb \
&& apt install redis libgtk-4-1 libvulkan1 libxdamage1 -y \ # Configure Grafana
&& curl -sSLO https://nc.piair.xyz/s/BKLsBWoZkTdYjfq/download/chrome.deb \ RUN grafana-cli plugins install frser-sqlite-datasource
&& ln -fs /usr/share/zoneinfo/Europe/Paris /etc/localtime \
&& git clone https://gitea.augustin64.fr/piair/MsRewards-Reborn \ COPY requirements.txt /app/requirements.txt
&& python3 -m pip install -r MsRewards-Reborn/requirements.txt \ RUN python3 -m pip install -r requirements.txt
&& wget -q -O /usr/share/keyrings/grafana.key https://apt.grafana.com/gpg.key \
&& curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg \ # Setup app
&& echo "deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main" | tee -a /etc/apt/sources.list.d/grafana.list \ RUN git clone https://gitea.augustin64.fr/piair/MsRewards-Reborn
&& apt update \ # Use this instead when developping locally:
&& apt install novnc websockify grafana xvfb nginx nano tzdata sqlite3 apt-transport-https software-properties-common wget wfrench tigervnc-standalone-server libasound2 libatk-bridge2.0-0 libnss3 libnspr4 xvfb libgbm1 libatk1.0-0 libu2f-udev libatspi2.0-0 libcups2 libxkbcommon0 libxrandr2 libdbus-1-3 xdg-utils fonts-liberation libdrm2 -y \ # COPY . /app/MsRewards-Reborn
&& bash MsRewards-Reborn/config/config.sh \
&& dpkg -i chrome.deb RUN bash MsRewards-Reborn/config/config.sh
ENV TZ="Europe/Paris" ENV TZ="Europe/Paris"
WORKDIR /app/MsRewards-Reborn/Flask/ WORKDIR /app/MsRewards-Reborn/Flask/
CMD bash start.sh CMD bash start.sh

View File

@ -13,11 +13,27 @@ import re
from requests import get from requests import get
import redis import redis
APP_ROOT = os.getenv("APP_ROOT")
if APP_ROOT is None:
APP_ROOT = "/app/MsRewards-Reborn/"
NO_SUBPROCESS = os.getenv("NO_SUBPROCESS")
if NO_SUBPROCESS is not None:
def fake_popen(*args, **kwargs):
print("Calling subprocess.Popen with", args, kwargs)
subprocess.Popen = fake_popen
print("Faking subprocess calls")
# redis part for live update # redis part for live update
pool = redis.ConnectionPool(host='localhost', port=6379, db=0) pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
r = redis.Redis(connection_pool=pool) r = redis.Redis(connection_pool=pool)
def get_path(path):
return os.path.join(APP_ROOT, path)
def generate_output(): def generate_output():
pubsub = r.pubsub() pubsub = r.pubsub()
pubsub.subscribe('console') pubsub.subscribe('console')
@ -32,7 +48,7 @@ def generate_output():
# the end # the end
global password global password
with open("/app/MsRewards-Reborn/user_data/flask.json", "r") as inFile: with open(get_path("user_data/flask.json"), "r") as inFile:
data = json.load(inFile) data = json.load(inFile)
password = data["password"] password = data["password"]
@ -40,7 +56,7 @@ secret = data["secret"]
if secret == "": if secret == "":
import secrets import secrets
secret = secrets.token_hex() secret = secrets.token_hex()
with open("/app/MsRewards-Reborn/user_data/flask.json", "w") as inFile: with open(get_path("user_data/flask.json"), "w") as inFile:
data = { data = {
"password": password, "password": password,
"secret": secret "secret": secret
@ -70,14 +86,14 @@ scheduler.add_job( # on relance le job
def start_ms(i): def start_ms(i):
print("\033[32m" + f"Starting config {i}" + "\033[0m") print("\033[32m" + f"Starting config {i}" + "\033[0m")
log = open(f"/app/MsRewards-Reborn/Flask/static/logs/{i}.txt", 'a') # so that data written to it will be appended log = open(get_path(f"Flask/static/logs/{i}.txt"), 'a') # so that data written to it will be appended
subprocess.Popen([f"python3 -u /app/MsRewards-Reborn/V6.py -c {i}"], stdout=log, stderr=log, shell=True) subprocess.Popen([f"python3 -u {get_path('V6.py')} -c {i}"], stdout=log, stderr=log, shell=True)
log.close() log.close()
TriggerDict = {} TriggerDict = {}
def update_jobs(): def update_jobs():
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: with open(get_path("user_data/configs.json"), "r") as inFile:
configs = json.load(inFile) configs = json.load(inFile)
for i in configs: for i in configs:
try : try :
@ -120,7 +136,7 @@ app = Flask(__name__)
@app.context_processor @app.context_processor
def inject_default_variables(): def inject_default_variables():
with open("/app/MsRewards-Reborn/version", "r") as f: with open(get_path("version"), "r") as f:
version = f.readline().replace("\n", '') version = f.readline().replace("\n", '')
return dict(version=version) return dict(version=version)
""" """
@ -175,7 +191,7 @@ def change_password():
if request.method == 'POST': if request.method == 'POST':
password = request.form["password"] password = request.form["password"]
subprocess.Popen(["grafana-cli", "admin", "reset-admin-password", password]) subprocess.Popen(["grafana-cli", "admin", "reset-admin-password", password])
with open("/app/MsRewards-Reborn/user_data/flask.json", "w") as inFile: with open(get_path("user_data/flask.json"), "w") as inFile:
data = { data = {
"password": password, "password": password,
"secret": secret "secret": secret
@ -201,21 +217,21 @@ def load_user(userid):
@app.route("/") @app.route("/")
def main(): def main():
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: with open(get_path("user_data/configs.json"), "r") as inFile:
configs = json.load(inFile) configs = json.load(inFile)
return(render_template("schedule.html", data=configs)) return(render_template("schedule.html", data=configs))
@app.route("/discord/") @app.route("/discord/")
def discord_get(): def discord_get():
with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile: with open(get_path("user_data/discord.json"), "r") as inFile:
data = json.load(inFile) data = json.load(inFile)
return(render_template("discord.html", data=data, len=maxi(data))) return(render_template("discord.html", data=data, len=maxi(data)))
@app.route("/discord/", methods=["post"]) @app.route("/discord/", methods=["post"])
def discord_post(): def discord_post():
with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile: with open(get_path("user_data/discord.json"), "r") as inFile:
data = json.load(inFile) data = json.load(inFile)
action = request.form action = request.form
if action['DISCORD'] == "delete" : if action['DISCORD'] == "delete" :
@ -237,7 +253,7 @@ def discord_post():
name = action["name"] if action["name"] else f"unnamed{action['select']}" name = action["name"] if action["name"] else f"unnamed{action['select']}"
data[config] = {"errorsL" : errorsL, "errorsT": errorsT, "successT": successT, "successL": successL, "name": name} data[config] = {"errorsL" : errorsL, "errorsT": errorsT, "successT": successT, "successL": successL, "name": name}
with open("/app/MsRewards-Reborn/user_data/discord.json", "w") as outFile: with open(get_path("user_data/discord.json"), "w") as outFile:
json.dump(data, outFile) json.dump(data, outFile)
return(render_template("discord.html", data=data, len=maxi(data))) return(render_template("discord.html", data=data, len=maxi(data)))
@ -249,7 +265,7 @@ def dev2():
@app.route("/settings/") @app.route("/settings/")
def settings_get(): def settings_get():
with open("/app/MsRewards-Reborn/user_data/settings.json", "r") as inFile: with open(get_path("user_data/settings.json"), "r") as inFile:
settings = json.load(inFile) settings = json.load(inFile)
return(render_template("settings.html", data=settings)) return(render_template("settings.html", data=settings))
@ -259,21 +275,21 @@ def settings_post():
settings = {} settings = {}
action = request.form action = request.form
settings['avatarlink'] = action["avatarlink"] settings['avatarlink'] = action["avatarlink"]
with open("/app/MsRewards-Reborn/user_data/settings.json", "w") as inFile: with open(get_path("user_data/settings.json"), "w") as inFile:
json.dump(settings, inFile) json.dump(settings, inFile)
return(render_template("settings.html", data=settings)) return(render_template("settings.html", data=settings))
@app.route("/proxy/") @app.route("/proxy/")
def proxy_get(): def proxy_get():
with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile: with open(get_path("user_data/proxy.json"), "r") as inFile:
j = json.load(inFile) j = json.load(inFile)
return(render_template("proxy.html", data=j, len=maxi(j))) return(render_template("proxy.html", data=j, len=maxi(j)))
@app.route("/proxy/", methods=["post"]) @app.route("/proxy/", methods=["post"])
def proxy_post(): def proxy_post():
with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile: with open(get_path("user_data/proxy.json"), "r") as inFile:
data = json.load(inFile) data = json.load(inFile)
action = request.form action = request.form
print(action) print(action)
@ -290,21 +306,21 @@ def proxy_post():
except : except :
print("error : probably bad config") print("error : probably bad config")
with open("/app/MsRewards-Reborn/user_data/proxy.json", "w") as outFile: with open(get_path("user_data/proxy.json"), "w") as outFile:
json.dump(data, outFile) json.dump(data, outFile)
return(render_template("proxy.html", data=data, len=maxi(data))) return(render_template("proxy.html", data=data, len=maxi(data)))
@app.route("/schedule/") @app.route("/schedule/")
def schedule_get(): def schedule_get():
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: with open(get_path("user_data/configs.json"), "r") as inFile:
configs = json.load(inFile) configs = json.load(inFile)
return(render_template("schedule.html", data=configs)) return(render_template("schedule.html", data=configs))
@app.route("/schedule/", methods=["post"]) @app.route("/schedule/", methods=["post"])
def schedule_post(): def schedule_post():
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: with open(get_path("user_data/configs.json"), "r") as inFile:
configs = json.load(inFile) configs = json.load(inFile)
data = dict(request.form) data = dict(request.form)
@ -318,7 +334,7 @@ def schedule_post():
configs[i]["time"] = data[f"time{i}"] configs[i]["time"] = data[f"time{i}"]
configs[i]["enabled"] = data[f"switch{i}"] == "on" configs[i]["enabled"] = data[f"switch{i}"] == "on"
with open("/app/MsRewards-Reborn/user_data/configs.json", "w") as inFile: with open(get_path("user_data/configs.json"), "w") as inFile:
json.dump(configs, inFile) json.dump(configs, inFile)
update_jobs() update_jobs()
return(render_template("schedule.html", data=configs)) return(render_template("schedule.html", data=configs))
@ -326,11 +342,11 @@ def schedule_post():
@app.route("/config/") @app.route("/config/")
def config_get(): def config_get():
with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile: with open(get_path("user_data/proxy.json"), "r") as inFile:
proxys = json.load(inFile) proxys = json.load(inFile)
with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile: with open(get_path("user_data/discord.json"), "r") as inFile:
discords = json.load(inFile) discords = json.load(inFile)
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: with open(get_path("user_data/configs.json"), "r") as inFile:
configs = json.load(inFile) configs = json.load(inFile)
return(render_template("config.html", data=configs, discords=discords, proxys=proxys, configs=configs, len=maxi(configs))) return(render_template("config.html", data=configs, discords=discords, proxys=proxys, configs=configs, len=maxi(configs)))
@ -338,11 +354,11 @@ def config_get():
@app.route("/config/", methods=["POST"]) @app.route("/config/", methods=["POST"])
def config_post(): def config_post():
action = request.form action = request.form
with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile: with open(get_path("user_data/proxy.json"), "r") as inFile:
proxys = json.load(inFile) proxys = json.load(inFile)
with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile: with open(get_path("user_data/discord.json"), "r") as inFile:
discords = json.load(inFile) discords = json.load(inFile)
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: with open(get_path("user_data/configs.json"), "r") as inFile:
configs = json.load(inFile) configs = json.load(inFile)
if action["data"] == "delete": if action["data"] == "delete":
@ -365,16 +381,26 @@ def config_post():
"enabled":"False", "enabled":"False",
"accounts": comptes "accounts": comptes
} }
with open("/app/MsRewards-Reborn/user_data/configs.json", "w") as outFile: with open(get_path("user_data/configs.json"), "w") as outFile:
json.dump(configs, outFile) json.dump(configs, outFile)
return(render_template("config.html", data=configs, discords=discords, proxys=proxys, configs=configs, len=maxi(configs))) return(render_template("config.html", data=configs, discords=discords, proxys=proxys, configs=configs, len=maxi(configs)))
@app.route("/logs/", methods=["GET", "POST"]) @app.route("/logs/", methods=["GET", "POST"])
def logs(): def logs():
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: with open(get_path("user_data/configs.json"), "r") as inFile:
configs = json.load(inFile) configs = json.load(inFile)
print(configs)
return(render_template("logs.html", data=configs)) files = [(configs[i]["name"], i) for i in configs]
config_files = [i[1] for i in files]
for f in os.listdir(get_path("Flask/static/logs")):
fid = ".".join(f.split(".")[:-1]) # filename without .txt
if f != ".gitignore" and fid not in config_files:
files.append((f, fid))
return render_template(
"logs.html",
files=files
)
@app.route("/stats/", methods=["GET", "POST"]) @app.route("/stats/", methods=["GET", "POST"])
@ -385,21 +411,21 @@ def stats():
@app.route("/override/", methods=["POST"]) @app.route("/override/", methods=["POST"])
def override_post(): def override_post():
json = request.form.to_dict(flat=False) json = request.form.to_dict(flat=False)
log = open(f"/app/MsRewards-Reborn/Flask/static/logs/custom.txt", 'w') # so that data written to it will be appended log = open(get_path("Flask/static/logs/custom.txt"), 'w') # so that data written to it will be appended
subprocess.Popen([f"python3 -u /app/MsRewards-Reborn/V6.py -c {json['config'][0]} --json \"{json}\""], stdout=log, stderr=log, shell=True) subprocess.Popen([f"python3 -u {get_path('V6.py')} -c {json['config'][0]} --json \"{json}\""], stdout=log, stderr=log, shell=True)
log.close() log.close()
return(render_template("vnc_post.html")) return(render_template("vnc_post.html"))
@app.route("/override/", methods=["GET"]) @app.route("/override/", methods=["GET"])
def override_get(): def override_get():
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: with open(get_path("user_data/configs.json"), "r") as inFile:
configs = json.load(inFile) configs = json.load(inFile)
return(render_template("vnc_get.html", configs=configs)) return(render_template("vnc_get.html", configs=configs))
@app.route('/download/<path:filename>', methods=['GET', 'POST']) @app.route('/download/<path:filename>', methods=['GET', 'POST'])
@login_required @login_required
def download(filename): def download(filename):
return send_from_directory(directory='/app/MsRewards-Reborn/user_data/', path=filename, as_attachment=True) return send_from_directory(directory=get_path("user_data/"), path=filename, as_attachment=True)
def allowed_file(filename): def allowed_file(filename):
@ -420,8 +446,8 @@ def upload_file():
elif file and allowed_file(file.filename): elif file and allowed_file(file.filename):
filename = secure_filename(file.filename) filename = secure_filename(file.filename)
print(os.path.join('/app/MsRewards-Reborn/user_data/', filename)) print(os.path.join(get_path("user_data/"), filename))
file.save(os.path.join('/app/MsRewards-Reborn/user_data/', filename)) file.save(os.path.join(get_path("user_data/"), filename))
i += 1 i += 1
print(i) print(i)
@ -439,4 +465,7 @@ def maxi(dict):
update_jobs() update_jobs()
subprocess.Popen(["bash",'/app/MsRewards-Reborn/config/request.sh']) subprocess.Popen(["bash", get_path("config/request.sh")])
if __name__ == "__main__":
app.run()

View File

@ -1,5 +1,5 @@
nohup bash /app/MsRewards-Reborn/sse.sh & nohup redis-server &> nohup_redis.out &
nohup bash /app/MsRewards-Reborn/sse.sh &> nohup_sse.out &
service grafana-server start service grafana-server start
service nginx start service nginx start
nohup redis-server &
gunicorn --reload --worker-class gevent -b 0.0.0.0:6666 'app:app' gunicorn --reload --worker-class gevent -b 0.0.0.0:6666 'app:app'

View File

@ -0,0 +1,17 @@
#console {
height: 100%;
width: 20%;
float: left;
resize: horizontal;
overflow: auto;
}
#vnc-container {
height: 100%;
width: 80%;
float: left;
}
.container {
height: 100%;
}

20
Flask/static/node_modules/.package-lock.json generated vendored Normal file
View File

@ -0,0 +1,20 @@
{
"name": "static",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/xterm": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/xterm/-/xterm-5.3.0.tgz",
"integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg=="
},
"node_modules/xterm-addon-fit": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.8.0.tgz",
"integrity": "sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw==",
"peerDependencies": {
"xterm": "^5.0.0"
}
}
}
}

View File

@ -0,0 +1,2 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,i=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(i.getPropertyValue("height")),s=Math.max(0,parseInt(i.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),l=o-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=s-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}}})(),e})()));
//# sourceMappingURL=xterm-addon-fit.js.map

File diff suppressed because one or more lines are too long

26
Flask/static/node_modules/xterm-addon-fit/package.json generated vendored Normal file
View File

@ -0,0 +1,26 @@
{
"name": "xterm-addon-fit",
"version": "0.8.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
},
"main": "lib/xterm-addon-fit.js",
"types": "typings/xterm-addon-fit.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"keywords": [
"terminal",
"xterm",
"xterm.js"
],
"scripts": {
"build": "../../node_modules/.bin/tsc -p .",
"prepackage": "npm run build",
"package": "../../node_modules/.bin/webpack",
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^5.0.0"
}
}

209
Flask/static/node_modules/xterm/css/xterm.css generated vendored Normal file
View File

@ -0,0 +1,209 @@
/**
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
* https://github.com/chjj/term.js
* @license MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Originally forked from (with the author's permission):
* Fabrice Bellard's javascript vt100 for jslinux:
* http://bellard.org/jslinux/
* Copyright (c) 2011 Fabrice Bellard
* The original design remains. The terminal itself
* has been extended to include xterm CSI codes, among
* other features.
*/
/**
* Default styles for xterm.js
*/
.xterm {
cursor: text;
position: relative;
user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
}
.xterm.focus,
.xterm:focus {
outline: none;
}
.xterm .xterm-helpers {
position: absolute;
top: 0;
/**
* The z-index of the helpers must be higher than the canvases in order for
* IMEs to appear on top.
*/
z-index: 5;
}
.xterm .xterm-helper-textarea {
padding: 0;
border: 0;
margin: 0;
/* Move textarea out of the screen to the far left, so that the cursor is not visible */
position: absolute;
opacity: 0;
left: -9999em;
top: 0;
width: 0;
height: 0;
z-index: -5;
/** Prevent wrapping so the IME appears against the textarea at the correct position */
white-space: nowrap;
overflow: hidden;
resize: none;
}
.xterm .composition-view {
/* TODO: Composition position got messed up somewhere */
background: #000;
color: #FFF;
display: none;
position: absolute;
white-space: nowrap;
z-index: 1;
}
.xterm .composition-view.active {
display: block;
}
.xterm .xterm-viewport {
/* On OS X this is required in order for the scroll bar to appear fully opaque */
background-color: #000;
overflow-y: scroll;
cursor: default;
position: absolute;
right: 0;
left: 0;
top: 0;
bottom: 0;
}
.xterm .xterm-screen {
position: relative;
}
.xterm .xterm-screen canvas {
position: absolute;
left: 0;
top: 0;
}
.xterm .xterm-scroll-area {
visibility: hidden;
}
.xterm-char-measure-element {
display: inline-block;
visibility: hidden;
position: absolute;
top: 0;
left: -9999em;
line-height: normal;
}
.xterm.enable-mouse-events {
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
cursor: default;
}
.xterm.xterm-cursor-pointer,
.xterm .xterm-cursor-pointer {
cursor: pointer;
}
.xterm.column-select.focus {
/* Column selection mode */
cursor: crosshair;
}
.xterm .xterm-accessibility,
.xterm .xterm-message {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 10;
color: transparent;
pointer-events: none;
}
.xterm .live-region {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
overflow: hidden;
}
.xterm-dim {
/* Dim should not apply to background, so the opacity of the foreground color is applied
* explicitly in the generated class and reset to 1 here */
opacity: 1 !important;
}
.xterm-underline-1 { text-decoration: underline; }
.xterm-underline-2 { text-decoration: double underline; }
.xterm-underline-3 { text-decoration: wavy underline; }
.xterm-underline-4 { text-decoration: dotted underline; }
.xterm-underline-5 { text-decoration: dashed underline; }
.xterm-overline {
text-decoration: overline;
}
.xterm-overline.xterm-underline-1 { text-decoration: overline underline; }
.xterm-overline.xterm-underline-2 { text-decoration: overline double underline; }
.xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; }
.xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; }
.xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; }
.xterm-strikethrough {
text-decoration: line-through;
}
.xterm-screen .xterm-decoration-container .xterm-decoration {
z-index: 6;
position: absolute;
}
.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer {
z-index: 7;
}
.xterm-decoration-overview-ruler {
z-index: 8;
position: absolute;
top: 0;
right: 0;
pointer-events: none;
}
.xterm-decoration-top {
z-index: 2;
position: relative;
}

2
Flask/static/node_modules/xterm/lib/xterm.js generated vendored Normal file

File diff suppressed because one or more lines are too long

1
Flask/static/node_modules/xterm/lib/xterm.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

100
Flask/static/node_modules/xterm/package.json generated vendored Normal file
View File

@ -0,0 +1,100 @@
{
"name": "xterm",
"description": "Full xterm terminal, in your browser",
"version": "5.3.0",
"main": "lib/xterm.js",
"style": "css/xterm.css",
"types": "typings/xterm.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"keywords": [
"cli",
"command-line",
"console",
"pty",
"shell",
"ssh",
"styles",
"terminal-emulator",
"terminal",
"tty",
"vt100",
"webgl",
"xterm"
],
"scripts": {
"prepackage": "npm run build",
"package": "webpack",
"package-headless": "webpack --config ./webpack.config.headless.js",
"postpackage-headless": "node ./bin/package_headless.js",
"start": "node demo/start",
"build-demo": "webpack --config ./demo/webpack.config.js",
"start-debug": "node --inspect-brk demo/start",
"lint": "eslint -c .eslintrc.json --max-warnings 0 --ext .ts src/ addons/",
"lint-api": "eslint --no-eslintrc -c .eslintrc.json.typings --max-warnings 0 --no-ignore --ext .d.ts typings/",
"test": "npm run test-unit",
"posttest": "npm run lint",
"test-api": "npm run test-api-chromium",
"test-api-chromium": "node ./bin/test_api.js --browser=chromium --timeout=20000",
"test-api-firefox": "node ./bin/test_api.js --browser=firefox --timeout=20000",
"test-api-webkit": "node ./bin/test_api.js --browser=webkit --timeout=20000",
"test-playwright": "playwright test -c ./out-test/playwright/playwright.config.js --workers 4",
"test-playwright-chromium": "playwright test -c ./out-test/playwright/playwright.config.js --workers 4 --project='Chrome Stable'",
"test-playwright-firefox": "playwright test -c ./out-test/playwright/playwright.config.js --workers 4 --project='Firefox Stable'",
"test-playwright-webkit": "playwright test -c ./out-test/playwright/playwright.config.js --workers 4 --project='WebKit'",
"test-playwright-debug": "playwright test -c ./out-test/playwright/playwright.config.js --headed --workers 1 --timeout 30000",
"test-unit": "node ./bin/test.js",
"test-unit-coverage": "node ./bin/test.js --coverage",
"test-unit-dev": "cross-env NODE_PATH='./out' mocha",
"build": "tsc -b ./tsconfig.all.json",
"install-addons": "node ./bin/install-addons.js",
"presetup": "npm run install-addons",
"setup": "npm run build",
"prepublishOnly": "npm run package",
"watch": "tsc -b -w ./tsconfig.all.json --preserveWatchOutput",
"benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json",
"benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/test/benchmark/*benchmark.js",
"benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/test/benchmark/*benchmark.js",
"clean": "rm -rf lib out addons/*/lib addons/*/out",
"vtfeatures": "node bin/extract_vtfeatures.js src/**/*.ts src/*.ts"
},
"devDependencies": {
"@playwright/test": "^1.37.1",
"@types/chai": "^4.2.22",
"@types/debug": "^4.1.7",
"@types/deep-equal": "^1.0.1",
"@types/express": "4",
"@types/express-ws": "^3.0.1",
"@types/glob": "^7.2.0",
"@types/jsdom": "^16.2.13",
"@types/mocha": "^9.0.0",
"@types/node": "^18.16.0",
"@types/utf8": "^3.0.0",
"@types/webpack": "^5.28.0",
"@types/ws": "^8.2.0",
"@typescript-eslint/eslint-plugin": "^6.2.00",
"@typescript-eslint/parser": "^6.2.00",
"chai": "^4.3.4",
"cross-env": "^7.0.3",
"deep-equal": "^2.0.5",
"eslint": "^8.45.0",
"eslint-plugin-jsdoc": "^39.3.6",
"express": "^4.17.1",
"express-ws": "^5.0.2",
"glob": "^7.2.0",
"jsdom": "^18.0.1",
"mocha": "^10.1.0",
"mustache": "^4.2.0",
"node-pty": "^0.10.1",
"nyc": "^15.1.0",
"source-map-loader": "^3.0.0",
"source-map-support": "^0.5.20",
"ts-loader": "^9.3.1",
"typescript": "^5.1.6",
"utf8": "^3.0.0",
"webpack": "^5.61.0",
"webpack-cli": "^4.9.1",
"ws": "^8.2.3",
"xterm-benchmark": "^0.3.1"
}
}

26
Flask/static/package-lock.json generated Normal file
View File

@ -0,0 +1,26 @@
{
"name": "static",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0"
}
},
"node_modules/xterm": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/xterm/-/xterm-5.3.0.tgz",
"integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg=="
},
"node_modules/xterm-addon-fit": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.8.0.tgz",
"integrity": "sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw==",
"peerDependencies": {
"xterm": "^5.0.0"
}
}
}
}

View File

@ -0,0 +1,6 @@
{
"dependencies": {
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0"
}
}

View File

@ -8,14 +8,37 @@
<select name="select" onchange="change_logs(this.value)"> <select name="select" onchange="change_logs(this.value)">
<option id="null" value="0">Choisir une config</option> <option id="null" value="0">Choisir une config</option>
{% for i in data %} {% for file in files %}
<option id="{{data[i]['name']}}" value="{{i}}">{{data[i]['name']}}</option> <option id="{{ file[0] }}" value="{{ file[1] }}">{{ file[0] }}</option>
{% endfor %} {% endfor %}
</select> </select>
<br><br> <br><br>
<iframe type="text/html" src="{{url_for('static', filename='logs/1.txt')}}" width="100%" height="85%" id="embed"> <iframe type="text/html" src="{{url_for('static', filename='logs/1.txt')}}" width="100%" height="85%" id="embed"></iframe>
<script defer>
const iframe = document.getElementsByTagName("iframe")[0];
var script = document.createElement('script');
// Wait until ansi_up load
script.onload = function () {
// Wait until iframe load
iframe.onload = function() {
const subdoc = iframe.contentWindow.document;
const subBody = subdoc.getElementsByTagName("body")[0]
let ansiOutput = subBody;
// Depending on the content encoding (and maybe on the browser)
// a <pre> is added around the content of the file
if (subBody.getElementsByTagName("pre").length > 0) {
ansiOutput = subBody.getElementsByTagName("pre")[0];
}
const ansi_up = new AnsiUp();
ansiOutput.innerHTML = ansi_up.ansi_to_html(ansiOutput.innerText);
}
};
script.src = "https://cdn.jsdelivr.net/npm/ansi_up@4.0.4/ansi_up.js";
document.head.appendChild(script);
</script>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -1,35 +1,48 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block left_pannel %}override{% endblock %} {% block left_pannel %}override{% endblock %}
{% block content %} {% block content %}
{%if not current_user.is_authenticated %} {% if not current_user.is_authenticated %}
<button class="unselected" onclick="location.href = '/login';">login</button> <button class="unselected" onclick="location.href = '/login';">login</button>
{% else %} {% else %}
<table> <link rel="stylesheet" href="{{ url_for('static', filename='css/vnc-post.css') }}"/>
<tr> <link rel="stylesheet" href="{{ url_for('static', filename='node_modules/xterm/css/xterm.css') }}"/>
<td width="20%" height="90%"> <script src="{{ url_for('static', filename='node_modules/xterm/lib/xterm.js') }}"></script>
<script src="{{ url_for('static', filename='node_modules/xterm-addon-fit/lib/xterm-addon-fit.js') }}"></script>
<script>
document.getElementsByClassName("content")[0].style.padding = "0 0"
</script>
<div class="container">
<div id="console"></div> <div id="console"></div>
</td> <div id="vnc-container">
<td width="80%"> <iframe src="/novnc/vnc.html?resize=scale&path=novnc/websockify&autoconnect=true&view_only"
<iframe src="/novnc/vnc.html?resize=scale&path=novnc/websockify&autoconnect=true&view_only" width="100%" height="100%" frameborder="0"></iframe> width="100%" height="100%" frameborder="0">
</td> </iframe>
</tr> </div>
</table> </div>
<script>
var term = new Terminal();
const fitAddon = new FitAddon.FitAddon();
term.loadAddon(fitAddon);
term.open(document.getElementById('console'));
fitAddon.fit()
addEventListener("transitionend", (event) => {
fitAddon.fit()
});
document.getElementById("console").style.textAlign = "left"
const eventSource = new EventSource('/stream');
<script> eventSource.onmessage = (event) => {
const consoleElement = document.getElementById('console'); term.writeln(event.data)
const eventSource = new EventSource('/stream'); };
addEventListener("resize", (event) => {
fitAddon.fit()
});
</script>
eventSource.onmessage = (event) => { {% endif %}
document.getElementById("console").innerHTML = event.data + document.getElementById("console").innerHTML
};
</script>
{% endif %} {% endblock %}
{%endblock %}

453
V6.py
View File

@ -1,8 +1,11 @@
#!/usr/bin/python3.10 #!/usr/bin/python3
import random
import subprocess
from modules.Classes.Config import Config from modules.Classes.Config import Config
from modules.Classes.DiscordLogger import DiscordLogger from modules.Classes.DiscordLogger import DiscordLogger
from modules.Classes.UserCredentials import UserCredentials from modules.Classes.UserCredentials import UserCredentials
from modules.Tools.logger import warning, critical from modules.Tools.logger import critical, warning
from modules.cards import * from modules.cards import *
from modules.config import * from modules.config import *
from modules.db import add_to_database from modules.db import add_to_database
@ -22,16 +25,33 @@ def create_driver(mobile=False):
"AppleWebKit/537.36 (KHTML, like Gecko)" "AppleWebKit/537.36 (KHTML, like Gecko)"
"Chrome/22 Mobile Safari/537.36" "Chrome/22 Mobile Safari/537.36"
) )
chrome_options = webdriver.ChromeOptions()
chrome_profile_dir = init_profile(config.UserCredentials.get_mail(), mobile=mobile)
# Full list on https://github.com/GoogleChrome/chrome-launcher/blob/main/docs/chrome-flags-for-tools.md
arguments = [
"--no-first-run",
"--ash-no-nudges",
"--no-default-browser-check",
"--disable-features=PrivacySandboxSettings4,Translate",
"--disable-search-engine-choice-screen",
f"--user-data-dir={chrome_profile_dir}/"
]
if mobile: if mobile:
chrome_options.add_argument(f"--user-agent={mobile_user_agent}") arguments.append(f"--user-agent={mobile_user_agent}")
else: else:
chrome_options.add_argument(f"--user-agent={pc_user_agent}") arguments.append(f"--user-agent={pc_user_agent}")
# disabled as it may cause detection # disabled as it may cause detection
if config.proxy.is_enabled(): if config.proxy.is_enabled():
chrome_options.add_argument(f'--proxy-server={config.proxy.ip}:{config.proxy.port}') arguments.append(f'--proxy-server={config.proxy.ip}:{config.proxy.port}')
chrome_options = webdriver.ChromeOptions()
for arg in arguments:
chrome_options.add_argument(arg)
driver = uc.Chrome(options=chrome_options) driver = uc.Chrome(options=chrome_options)
set_language(driver)
return driver return driver
@ -54,8 +74,12 @@ def play_quiz2(override=10) -> None:
custom_sleep(uniform(3, 5)) custom_sleep(uniform(3, 5))
js_function = """ js_function = """
function get_correct_answer(){ function get_correct_answer(){
function br(n) { for (var r, t = 0, i = 0; i < n.length; i++)t += n.charCodeAt(i); return r = parseInt(_G.IG.substr(_G.IG.length - 2), 16), t += r, t.toString() } // Ms check function function br(n) {
function namedRAValue() { //allow calls to getRAvalue for (var r, t = 0, i = 0; i < n.length; i++)
t += n.charCodeAt(i);
return r = parseInt(_G.IG.substr(_G.IG.length - 2), 16), t += r, t.toString()
} // Ms check function
function namedRAValue() { //allow calls to getRAValue
return _w.getRAValue() return _w.getRAValue()
}; };
if (br(document.getElementById("rqAnswerOption0").attributes["data-option"].value) == namedRAValue()){ if (br(document.getElementById("rqAnswerOption0").attributes["data-option"].value) == namedRAValue()){
@ -148,7 +172,7 @@ def play_quiz4(override: int = None):
except Exception as e: except Exception as e:
log_error(e) log_error(e)
raise ValueError(e) raise ValueError(e)
info("Quiz 8 done.") info("Quiz 4 done.")
custom_sleep(3) custom_sleep(3)
@ -161,6 +185,8 @@ def do_poll():
try: try:
answer_elem.click() answer_elem.click()
except exceptions.ElementNotInteractableException: except exceptions.ElementNotInteractableException:
warning("element not clickable. Waiting a bit and retrying.")
custom_sleep(uniform(2, 2.5))
driver.execute_script("arguments[0].click();", answer_elem) driver.execute_script("arguments[0].click();", answer_elem)
custom_sleep(uniform(2, 2.5)) custom_sleep(uniform(2, 2.5))
except Exception as err: except Exception as err:
@ -173,6 +199,43 @@ def do_poll():
# Find each playable card and tries to click on it to earn points # Find each playable card and tries to click on it to earn points
def all_cards(): def all_cards():
driver = config.WebDriver.driver driver = config.WebDriver.driver
def check_welcome_tour() -> bool:
if "rewards.bing.com/welcometour" not in driver.current_url:
return False
info("Popup 'Explorer le programme' reçue")
wait_until_visible(By.ID, "welcome-tour", timeout=5, browser=driver)
custom_sleep(1.5)
welcome_tour = driver.find_element(By.ID, "welcome-tour")
interest_button_box = welcome_tour.find_element(By.CLASS_NAME, "interest-buttons")
interests = interest_button_box.find_elements(By.CLASS_NAME, "ng-binding")
debug("Got the following interests: " + str(interests))
random.choice(interests).click() # Choose interest
custom_sleep(1.5)
claim_button = welcome_tour.find_element(By.ID, "claim-button")
claim_button.click() # submit
custom_sleep(1.5)
return True
def check_streak_protection() -> bool:
"""
Ne perdez plus jamais votre série !
"""
try:
streak_protection_close = driver.find_element(By.ID, "streak-protection-popup-close-cross")
streak_protection_activate = driver.find_elements(By.CLASS_NAME, "earningPagePopUpPopUpSelectButton")
streak_protection_activate[0].click()
info("Popup 'Streak Protection' reçue")
custom_sleep(1.5)
return True
except (exceptions.NoSuchElementException, exceptions.ElementNotInteractableException, IndexError):
# les éléments sont présents dans le DOM même quand la popup n'est pas visible apparemment
return False
driver.get("https://rewards.bing.com") driver.get("https://rewards.bing.com")
wait_until_visible(By.CLASS_NAME, "c-card-content", 10, driver) wait_until_visible(By.CLASS_NAME, "c-card-content", 10, driver)
@ -186,7 +249,7 @@ def all_cards():
info("no promo card") info("no promo card")
if len(card_list) < 10: # most likely an error during loading if len(card_list) < 10: # most likely an error during loading
if "suspendu" in driver.page_source: if "suspendu" in driver.page_source or "suspended" in driver.page_source:
raise Banned() raise Banned()
driver.refresh() driver.refresh()
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content") card_list = driver.find_elements(By.CLASS_NAME, "c-card-content")
@ -216,39 +279,55 @@ def all_cards():
if i == len(card_list) and i > 15: if i == len(card_list) and i > 15:
checked = False checked = False
if checked: if not checked:
custom_sleep(1.5) continue
try:
activity = findall("data-bi-id=\"([^\"]+)\"", card_list[i].get_attribute("innerHTML"))[0]
except Exception as e:
warning("Can't find activity." + str(e))
activity = ""
custom_sleep(1.5)
check_welcome_tour()
check_streak_protection()
driver.execute_script("arguments[0].scrollIntoView();", card_list[i])
custom_sleep(1.5)
card_list[i].click()
if len(driver.window_handles) > 1:
driver.switch_to.window(driver.window_handles[1])
try_play(driver.title, activity)
close_tab(driver.window_handles[1])
try:
driver.refresh()
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content")
if "mee-icon-AddMedium" not in card_list[i].get_attribute("innerHTML"):
continue
check_welcome_tour()
check_streak_protection()
driver.execute_script("arguments[0].scrollIntoView();", card_list[i]) driver.execute_script("arguments[0].scrollIntoView();", card_list[i])
custom_sleep(1.5)
card_list[i].click() card_list[i].click()
if len(driver.window_handles) > 1: driver.switch_to.window(driver.window_handles[1])
driver.switch_to.window(driver.window_handles[1]) custom_sleep(10)
try_play(driver.title) log_error(f"Card {i} Can't be completed. Why MS ?")
close_tab(driver.window_handles[1])
try: try:
driver.refresh() try_play(driver.title) # go back to the main page
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content") try:
close_tab(driver.window_handles[1])
except Exception as e:
debug(e)
except Exception as e:
debug(e)
driver.get("https://rewards.bing.com")
if "mee-icon-AddMedium" in card_list[i].get_attribute("innerHTML"): except Exception as err:
driver.execute_script("arguments[0].scrollIntoView();", card_list[i]) log_error(err)
card_list[i].click() custom_sleep(3)
driver.switch_to.window(driver.window_handles[1])
custom_sleep(10)
log_error(f"Card {i} Can't be completed. Why MS ?")
try:
try_play(driver.title) # go back to the main page
try:
close_tab(driver.window_handles[1])
except Exception as e:
debug(e)
except Exception as e:
debug(e)
driver.get("https://rewards.bing.com")
except Exception as err:
log_error(err)
custom_sleep(3)
def promo(): def promo():
@ -259,7 +338,7 @@ def promo():
if not elm: if not elm:
break break
if i > 3: if i > 3:
log_error("plus de 3 promo cards, probablement une pa skipable") log_error("There is more than 3 promo cards, most likely an unskippable one.")
try: try:
driver.find_element(By.CSS_SELECTOR, driver.find_element(By.CSS_SELECTOR,
'i[class="mee-icon pull-left icon mee-icon-Cancel ng-scope"]').click() 'i[class="mee-icon pull-left icon mee-icon-Cancel ng-scope"]').click()
@ -270,7 +349,7 @@ def promo():
elm.click() elm.click()
except Exception as e: except Exception as e:
driver.execute_script("arguments[0].click();", elm) driver.execute_script("arguments[0].click();", elm)
warning(f"that should't be there (promo), but the workarround seemed to work {e}") warning(f"that shouldn't be there (promo), but the workaround seemed to work {e}")
custom_sleep(3) custom_sleep(3)
if len(driver.window_handles) > 1: if len(driver.window_handles) > 1:
driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1]) driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1])
@ -279,15 +358,79 @@ def promo():
else: else:
try: try:
spotify(driver) spotify(driver)
except: except Exception as e:
warning("no new windows") warning(f"no new windows {format_error(e)}")
driver.get("https://rewards.bing.com") driver.get("https://rewards.bing.com")
driver.refresh() driver.refresh()
custom_sleep(3) custom_sleep(3)
def explore_on_bing(activity: str, config: Config):
driver = config.WebDriver.driver
def search_bing(txt):
search_elm = driver.find_element(By.ID, "sb_form_q")
send_keys_wait(search_elm, txt)
send_keys_wait(search_elm, Keys.ENTER)
if "lyrics" in activity:
search_bing(
f"paroles de {['Gata', 'Pyramide', 'Dolce Camara', 'Position', 'Mami Wata'][randint(0, 4)]}") # merci bing copilot pour les titres
elif "flight" in activity:
search_bing(
f"vol {['Paris - New York', 'Londres Amsterdam', 'Bora-Bora Miami', 'Los Angeles Toulouse', 'Rome Dubai'][randint(0, 4)]}")
elif "shopping" in activity:
search_bing(f"idée cadeau {['Noel', 'Anniversaire'][randint(0, 1)]}")
elif "movie" in activity:
search_bing(
f"Distribution {['Code 8 part 2', 'The Hunger Games: The ballad of Songbirds & Snakes', 'Rebel Moon: Part Two', 'Dune II', 'Wonka'][randint(0, 4)]}")
elif "translator" in activity:
search_bing(f"traduction {config.wordlist.get_word()} en anglais")
elif "map" in activity:
search_bing(f"{['Paris', 'Nice', 'Marseille', 'Bordeaux', 'Lyon'][randint(0, 4)]} carte")
elif "restaurant" in activity:
search_bing(random.choice([
"restaurant à proximité",
"restaurant pas loin",
"manger dans le coin"
]))
elif "recipe" in activity:
search_bing("recette de " + random.choice([
"cake aux olives",
"tarte tatin",
"Kouign Amann",
"poulet au Curry",
"lasagnes aux épinards et ricotta",
"Boeuf Bourguignon",
"dessert",
"gateau au chocolat",
"crêpe sucrée"
])) # Merci llama pour les idées
elif "currency" in activity:
currencies = [
"euros",
"dollars canadiens",
"dollars",
"livres",
"francs suisses",
"roubles",
"pesos",
"bitcoin",
"anciens francs"
]
chosen = random.sample(currencies, k=2)
search_bing(f"convertir {random.randint(2, 120)} {chosen[0]} en {chosen[1]}")
elif "weather" in activity:
search_bing(f"météo {['Paris', 'Nice', 'Marseille', 'Bordeaux', 'Lyon'][randint(0, 4)]}")
elif "packagetracking" in activity:
driver.get("https://www.bing.com/?setmkt=en-US&setlang=en-US")
search_bing(f"{['fedex', 'UPS', '']} package tracking")
else:
log_error(f"Explore on bing: {activity} not found.")
# Find out which type of action to do # Find out which type of action to do
def try_play(nom="inconnu"): def try_play(nom="unknown", activity=""):
driver = config.WebDriver.driver driver = config.WebDriver.driver
rgpd_popup(config) rgpd_popup(config)
@ -333,7 +476,7 @@ def try_play(nom="inconnu"):
info("On fidelity page.") info("On fidelity page.")
fidelity() fidelity()
elif wait_until_visible(By.ID, "rqStartQuiz", 5, driver): elif wait_until_visible(By.ID, "rqStartQuiz", 5, driver, raise_error=False):
custom_sleep(3) custom_sleep(3)
driver.find_element(By.ID, "rqStartQuiz").click() # start the quiz driver.find_element(By.ID, "rqStartQuiz").click() # start the quiz
answer_number = driver.page_source.count("rqAnswerOption") answer_number = driver.page_source.count("rqAnswerOption")
@ -341,11 +484,14 @@ def try_play(nom="inconnu"):
elif "rqQuestionState" in driver.page_source: elif "rqQuestionState" in driver.page_source:
number = driver.page_source.count("rqAnswerOption") number = driver.page_source.count("rqAnswerOption")
warning(f"recovery détecté. quiz : {number}") warning(f"recovery detected. quiz : {number}")
play(number - 1) play(number - 1)
elif "exploreonbing" in activity:
info(f"Explore on bing: {activity}")
explore_on_bing(activity, config)
custom_sleep(uniform(3, 5))
else: else:
info(f"Nothing to do on page `{nom}`") info(f"Nothing obvious to do on page `{nom}`.")
custom_sleep(uniform(3, 5)) custom_sleep(uniform(3, 5))
@ -356,22 +502,35 @@ def login_part_1():
driver = config.WebDriver.driver driver = config.WebDriver.driver
driver.get("https://login.live.com") driver.get("https://login.live.com")
wait_until_visible(By.ID, "i0116", browser=driver) wait_until_visible(By.ID, "i0116", browser=driver)
mail_elem = driver.find_element(By.ID, "i0116") send_wait_and_confirm(
send_keys_wait(mail_elem, config.UserCredentials.get_mail()) driver.find_element(By.ID, "i0116"),
mail_elem.send_keys(Keys.ENTER) config.UserCredentials.get_mail()
)
wait_until_visible(By.ID, "i0118", browser=driver) wait_until_visible(By.ID, "i0118", browser=driver)
pwd_elem = driver.find_element(By.ID, "i0118") send_wait_and_confirm(
send_keys_wait(pwd_elem, config.UserCredentials.get_password()) driver.find_element(By.ID, "i0118"),
pwd_elem.send_keys(Keys.ENTER) config.UserCredentials.get_password()
custom_sleep(2) )
# 2FA # 2FA
if "Entrez le code de sécurité" in driver.page_source: try:
try: if not wait_until_visible(By.ID, "idTxtBx_SAOTCC_OTC", browser=driver, timeout=5, raise_error=False):
a2f_elem = driver.find_element(By.ID, "idTxtBx_SAOTCC_OTC") custom_sleep(2)
a2f_elem.send_keys(config.UserCredentials.get_tfa().now()) return
a2f_elem.send_keys(Keys.ENTER)
except Exception as err: tfa = config.UserCredentials.get_tfa()
log_error(err) if tfa is None:
error("2FA needed but no code available for this account, sending error")
raise ValueError("2FA needed but no code available for this account")
else:
a2f_code = tfa.now()
info(f"Need 2FA, I have code: {a2f_code}")
send_wait_and_confirm(
driver.find_element(By.ID, "idTxtBx_SAOTCC_OTC"),
a2f_code
)
except Exception as err:
log_error(err)
# Accept all cookies question, and check if the account is locked # Accept all cookies question, and check if the account is locked
@ -406,9 +565,19 @@ def login_part_2():
# login() tries to login to your Microsoft account. # login() tries to login to your Microsoft account.
# it uses global variable g._mail and g._password to login # it uses global variable g._mail and g._password to login
def login(): def login():
def logged_in():
driver.get("https://login.live.com")
custom_sleep(10)
debug(get_domain(driver))
if get_domain(driver) == "account.microsoft.com":
return True
return False
info("Logging in...")
driver = config.WebDriver.driver driver = config.WebDriver.driver
try: try:
login_part_1() if not logged_in():
login_part_1()
login_part_2() login_part_2()
driver.get("https://rewards.bing.com/") driver.get("https://rewards.bing.com/")
except Banned: except Banned:
@ -457,26 +626,22 @@ def bing_pc_search(override=randint(35, 40)):
log_error(f"clear la barre de recherche - {format_error(e)}") # what is this message ??? todo log_error(f"clear la barre de recherche - {format_error(e)}") # what is this message ??? todo
# Sends points to database, discord and whatever service you want # Sends current account's points to database
# todo: refactor
def log_points(): def log_points():
driver = config.WebDriver.driver driver = config.WebDriver.driver
account = config.UserCredentials.get_mail() account = config.UserCredentials.get_mail()
def get_points(): driver.get("https://rewards.bing.com")
driver.get("https://rewards.bing.com") custom_sleep(1)
custom_sleep(1) wait_until_visible(By.CSS_SELECTOR, 'span[mee-element-ready="$ctrl.loadCounterAnimation()"]', browser=driver)
wait_until_visible(By.CSS_SELECTOR, 'span[mee-element-ready="$ctrl.loadCounterAnimation()"]', browser=driver) try:
try: points = search('availablePoints\":([\d]+)', driver.page_source)[1]
point = search('availablePoints\":([\d]+)', driver.page_source)[1] except Exception as err:
except Exception as e: log_error(
log_error( f"Dev error, checking why it doesn't work (waited a bit, is this still white ?) {format_error(err)}")
f"Dev error, checking why it doesn't work (waited a bit, is this still white ?) {format_error(e)}") error(f"Can't get points. {format_error(err)}")
error("Can't get points.") return -1
return -1
return point
points = get_points()
custom_sleep(uniform(3, 20)) custom_sleep(uniform(3, 20))
account_name = account.split("@")[0] account_name = account.split("@")[0]
@ -509,15 +674,15 @@ def fidelity():
try: try:
button_text = search('<span class="pull-left margin-right-15">([^<^>]+)</span>', button_text = search('<span class="pull-left margin-right-15">([^<^>]+)</span>',
card_elem.get_attribute("innerHTML"))[1] card_elem.get_attribute("innerHTML"))[1]
bouton_card = driver.find_element(By.XPATH, f'//span[text()="{button_text}"]') button_card = driver.find_element(By.XPATH, f'//span[text()="{button_text}"]')
bouton_card.click() button_card.click()
except Exception as e1: except Exception as e1:
try: try:
recover_elem = driver.find_element(By.XPATH, 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') '/html/body/div[1]/div[2]/main/div[2]/div[2]/div[7]/div[3]/div[1]/a')
recover_elem.click() recover_elem.click()
except Exception as e2: except Exception as e2:
log_error(f"fidélité - double erreur - e1 : {format_error(e1)} - e2 {format_error(e2)}") log_error(f"Fidelity: Multiples error - e1 : {format_error(e1)} - e2 {format_error(e2)}")
break break
custom_sleep(uniform(3, 5)) custom_sleep(uniform(3, 5))
driver.switch_to.window(driver.window_handles[2]) driver.switch_to.window(driver.window_handles[2])
@ -534,23 +699,26 @@ def fidelity():
if driver.current_url != "https://rewards.bing.com": if driver.current_url != "https://rewards.bing.com":
driver.get("https://rewards.bing.com") driver.get("https://rewards.bing.com")
try: try:
pause = driver.find_element(By.CSS_SELECTOR, pause = driver.find_element(By.CSS_SELECTOR, f'[class="c-action-toggle c-glyph f-toggle glyph-pause"]')
f'[class="c-action-toggle c-glyph f-toggle glyph-pause"]') # mettre le truc en pause
pause.click() pause.click()
except Exception as e: except Exception as e:
error(f"erreur lors du click de pause: probablement pas de cartes {e}") debug("No pause button.")
return "no cards"
cartes = driver.find_elements(By.CSS_SELECTOR, f'[ng-repeat="item in $ctrl.transcludedItems"]') cartes = driver.find_elements(By.CSS_SELECTOR, f'[ng-repeat="item in $ctrl.transcludedItems"]')
nb_cartes = len(cartes) nb_cartes = len(cartes)
if nb_cartes == 0:
warning("No fidelity cards detected")
return "No cards."
checked_list_all = driver.find_elements(By.CSS_SELECTOR, f'[ng-if="$ctrl.complete"]') checked_list_all = driver.find_elements(By.CSS_SELECTOR, f'[ng-if="$ctrl.complete"]')
for i in range(nb_cartes): for i in range(nb_cartes):
cartes[i].click() # affiche la bonne carte cartes[i].click()
checked_txt = checked_list_all[i].get_attribute("innerHTML") checked_txt = checked_list_all[i].get_attribute("innerHTML")
ok = checked_txt.count("StatusCircleOuter checkmark") ok = checked_txt.count("StatusCircleOuter checkmark")
total = checked_txt.count("StatusCircleOuter") total = checked_txt.count("StatusCircleOuter")
if (ok != total): if ok != total:
elm = driver.find_elements(By.CLASS_NAME, 'clickable-link')[i] elm = driver.find_elements(By.CLASS_NAME, 'clickable-link')[i]
if not "moviesandtv" in elm.get_attribute("innerHTML"): # not the film card # legacy code. Should be removed
if "moviesandtv" not in elm.get_attribute("innerHTML"): # not the film card
elm.click() elm.click()
driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1]) driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1])
sub_fidelity() sub_fidelity()
@ -611,9 +779,11 @@ def daily_routine(cred: UserCredentials, custom=False):
try: try:
if not custom: # custom already is logged in if not custom: # custom already is logged in
login() login()
except Banned: except Banned:
log_error("This account is locked.") log_error("This account is locked.")
return raise Banned()
except Identity: except Identity:
log_error("This account has an issue.") log_error("This account has an issue.")
return return
@ -652,8 +822,8 @@ def json_start(json_entry, cred: UserCredentials):
config.set_display(SmartDisplay(backend="xvnc", size=(1920, 1080), rfbport=2345, color_depth=24)) config.set_display(SmartDisplay(backend="xvnc", size=(1920, 1080), rfbport=2345, color_depth=24))
config.display.start() config.display.start()
account_id = 0
for account_id in range(5): while config.UserCredentials.is_valid():
start = False start = False
for action in ["unban", "tout", "pc", "mobile", "daily"]: for action in ["unban", "tout", "pc", "mobile", "daily"]:
try: try:
@ -664,56 +834,58 @@ def json_start(json_entry, cred: UserCredentials):
pass pass
if start: if start:
config.WebDriver.set_pc_driver(create_driver()) config.WebDriver.set_pc_driver(create_driver())
config.WebDriver.switch_to_driver("PC")
driver = config.WebDriver.driver driver = config.WebDriver.driver
try: if "unban" in json_entry and str(account_id) in json_entry["unban"]:
if str(account_id) in json_entry["unban"]: login()
login_part_1() info("\nGO TO example.com TO PROCEED or wait 1200 secs.")
info("\nGO TO example.com TO PROCEED or wait 1200 secs.") for _ in range(1200):
for _ in range(1200): sleep(1)
sleep(1) if driver.current_url == "https://example.com/":
if driver.current_url == "https://example.com/": info("proceeding")
info("proceeding") break
break else:
else:
login()
except KeyError:
login() login()
try: try:
if str(account_id) in json_entry["tout"]: if str(account_id) in json_entry["tout"]:
daily_routine(cred) daily_routine(cred, True)
else:
try:
if str(account_id) in json_entry["daily"]:
try:
all_cards()
except Exception as e:
log_error(e)
except KeyError:
pass
try:
if str(account_id) in json_entry["pc"]:
try:
bing_pc_search()
except Exception as e:
log_error(e)
except KeyError:
pass
try:
if str(account_id) in json_entry["mobile"]:
try:
bing_mobile_search(cred)
except Exception as e:
log_error(e)
except KeyError:
pass
except KeyError: except KeyError:
pass pass
else: try:
try: log_points()
if str(account_id) in json_entry["daily"]: except Exception as e:
try: error(f"CustomStart {e}")
all_cards()
except Exception as e:
log_error(e)
except KeyError:
pass
try:
if str(account_id) in json_entry["pc"]:
try:
bing_pc_search()
except Exception as e:
log_error(e)
except KeyError:
pass
try:
if str(account_id) in json_entry["mobile"]:
try:
bing_mobile_search(cred)
except Exception as e:
log_error(e)
except KeyError:
pass
try:
log_points()
except Exception as e:
error(f"CustomStart {e}")
driver.close() driver.close()
cred.next_account() cred.next_account()
account_id += 1
config.display.stop() config.display.stop()
@ -727,13 +899,17 @@ def default_start():
while config.UserCredentials.is_valid(): while config.UserCredentials.is_valid():
custom_sleep(1) custom_sleep(1)
info("Starting and configuring driver.") info("Starting and configuring driver.")
config.WebDriver.set_pc_driver(create_driver()) try:
config.WebDriver.set_pc_driver(create_driver())
except:
info("Could not configure driver. Trying to update it.")
subprocess.Popen(["python3", "/app/MsRewards-Reborn/modules/Tools/update_chrome.py"])
config.WebDriver.set_pc_driver(create_driver())
config.WebDriver.switch_to_driver("PC") config.WebDriver.switch_to_driver("PC")
info("Driver started.") info("Driver started.")
config.WebDriver.pc_driver.implicitly_wait(3) config.WebDriver.pc_driver.implicitly_wait(3)
try: try:
wait_time = uniform(1200, 3600) wait_time = uniform(1200, 3600)
wait_time = 10 # the display closes when I ctrl + c
info(f"Waiting for {round(wait_time / 60)}min before starting") info(f"Waiting for {round(wait_time / 60)}min before starting")
custom_sleep(wait_time) custom_sleep(wait_time)
daily_routine(config.UserCredentials) daily_routine(config.UserCredentials)
@ -743,12 +919,16 @@ def default_start():
config.WebDriver.pc_driver.quit() config.WebDriver.pc_driver.quit()
config.display.stop() config.display.stop()
break break
except Banned:
warning("this account is banned. Switching to next account")
except Exception as e: except Exception as e:
log_error(f"Error not caught. Skipping this account. " + format_error(e)) log_error(f"Error not caught. Skipping this account. " + format_error(e))
critical(f"Error not caught. Skipping this account. {e}") critical(f"Error not caught. Skipping this account. {e}")
config.WebDriver.pc_driver.quit() config.WebDriver.pc_driver.quit()
config.UserCredentials.next_account() finally:
config.UserCredentials.next_account()
config.display.stop() config.display.stop()
@ -765,7 +945,6 @@ def check_updated():
if __name__ == "__main__": if __name__ == "__main__":
config = Config(args) config = Config(args)
check_updated() check_updated()
match config.start: match config.start:
case "json": case "json":
json_start(config.json_entry, config.UserCredentials) json_start(config.json_entry, config.UserCredentials)

View File

@ -1 +1,12 @@
sudo docker build --no-cache --network host -t msrewards . && sudo docker run -d --restart unless-stopped -p 1234:1234 -p 2345:2345 -ti --shm-size=2gb --name MsRewards msrewards #!/bin/bash
docker-do () { # Check if sudo needs to be used
if id -nG "$(whoami)" | grep -qw "docker"; then
docker $@
else
sudo docker $@
fi
}
docker-do build --network host -t msrewards .
docker-do run -d --restart unless-stopped -p 1234:1234 -p 2345:2345 -ti --shm-size=2gb --name MsRewards msrewards

13
clean.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
docker-do () { # Check if sudo needs to be used
if id -nG "$(whoami)" | grep -qw "docker"; then
docker $@
else
sudo docker $@
fi
}
docker-do stop MsRewards
docker-do rm MsRewards
docker-do image rm msrewards

View File

@ -46,6 +46,7 @@ server {
proxy_pass "http://127.0.0.1:6666"; proxy_pass "http://127.0.0.1:6666";
chunked_transfer_encoding off; chunked_transfer_encoding off;
proxy_buffering off; proxy_buffering off;
add_header X-Accel-Buffering no;
} }
} }
@ -59,7 +60,6 @@ sqlite3 /app/MsRewards-Reborn/MsRewards.db "CREATE TABLE comptes (id INTEGER PRI
printf "\nconfigurating grafana\n" printf "\nconfigurating grafana\n"
cp /app/MsRewards-Reborn/config/grafana.ini /etc/grafana/ cp /app/MsRewards-Reborn/config/grafana.ini /etc/grafana/
grafana-cli plugins install frser-sqlite-datasource
printf "setting up default dashboard" printf "setting up default dashboard"
cp /app/MsRewards-Reborn/config/Stats-dashbord.json /usr/share/grafana/public/dashboards/home.json cp /app/MsRewards-Reborn/config/Stats-dashbord.json /usr/share/grafana/public/dashboards/home.json

File diff suppressed because it is too large Load Diff

View File

@ -16,4 +16,4 @@ services:
- "2345:2345" - "2345:2345"
shm_size: 2gb shm_size: 2gb
volumes: volumes:
- "./:/data" - "./data/:/data"

12
hooks/bump.sh Executable file
View File

@ -0,0 +1,12 @@
file=version
if [ "$(git diff HEAD version | wc -w)" -eq 0 ]
then
echo 'updating minor version'
new_path=$(cat $file | awk -F . '{print $1"."$2"."$3+1 }')
echo $new_path > $file
git add version
else
echo 'Version have been updated manually'
fi
echo Version: $(cat $file)

1
hooks/pre-commit Executable file
View File

@ -0,0 +1 @@
sh hooks/bump.sh

View File

@ -22,7 +22,8 @@ class Config:
proxy = json.load(inFile) proxy = json.load(inFile)
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
config = json.load(inFile) config = json.load(inFile)
with open("/app/MsRewards-Reborn/user_data/version", "r") as inFile:
version = inFile.readline()
""" """
setup standalone stuff setup standalone stuff
""" """
@ -31,10 +32,9 @@ class Config:
self.json_entry = args.json.replace("'", "\"") self.json_entry = args.json.replace("'", "\"")
self.wordlist = WordList("/usr/share/dict/french") self.wordlist = WordList("/usr/share/dict/french")
self.vnc = args.vnc self.vnc = args.vnc
self.version = args.update_version
self.WebDriver = Driver() self.WebDriver = Driver()
self.display = None self.display = None
self.version = version
""" """
setup UserCredential setup UserCredential
""" """
@ -50,9 +50,14 @@ class Config:
""" """
self.discord = DiscordConfig() self.discord = DiscordConfig()
self.discord.avatar_url = settings["avatarlink"] self.discord.avatar_url = settings["avatarlink"]
self.discord.wh_link = discord[config[args.config]["discord"]]["errorsL"]
if self.discord.wh_link != "": if (
"discord" in config[args.config]
and config[args.config]["discord"] in discord
and "errorsL" in discord[config[args.config]["discord"]]
and discord[config[args.config]["discord"]]["errorsL"] != ""
):
self.discord.wh_link = discord[config[args.config]["discord"]]["errorsL"]
self.discord.wh = Webhook.from_url(self.discord.wh_link, adapter=RequestsWebhookAdapter()) self.discord.wh = Webhook.from_url(self.discord.wh_link, adapter=RequestsWebhookAdapter())
else: else:
self.discord.wh = FakeWebHook() self.discord.wh = FakeWebHook()
@ -76,4 +81,11 @@ class Config:
self.display = display self.display = display
def has_been_updated(self): def has_been_updated(self):
return self.version != "None" with open('/app/MsRewards-Reborn/version', "r") as inFile:
in_file_content = inFile.readlines()
if self.version != in_file_content[0]:
self.version = in_file_content[0]
with open('/app/MsRewards-Reborn/user_data/version', "w") as outFile:
outFile.write(self.version)
return True
return False

View File

@ -9,6 +9,6 @@ class DiscordConfig:
class FakeWebHook: class FakeWebHook:
def send(self, *args): def send(self, *args, **kwargs):
debug(f"Used a webhook call without webhook url with {args}") debug(f"Used a webhook call without webhook url with {args} {kwargs}")

View File

@ -1,8 +1,7 @@
from discord import Embed, Colour, File from discord import Embed, Colour, File
from modules.Classes.Config import Config from modules.Classes.Config import Config
from modules.Classes.UserCredentials import UserCredentials from modules.Tools.logger import error
from modules.Tools.logger import info, warning, error, critical
from modules.Tools.tools import format_error from modules.Tools.tools import format_error
@ -25,14 +24,13 @@ class DiscordLogger:
img.save("screenshot.png") img.save("screenshot.png")
embed = Embed( embed = Embed(
title="An Error has occured", title="An Error has occurred",
description=str(message), description=str(message),
colour=Colour.red(), colour=Colour.red(),
) )
file = File("screenshot.png") file = File("screenshot.png")
embed.set_image(url="attachment://screenshot.png") embed.set_image(url="attachment://screenshot.png")
embed.set_footer(text=self.config.UserCredentials.creds.get_mail()) embed.set_footer(text=self.config.UserCredentials.get_mail() + " - " + self.config.WebDriver.current_driver())
self.config.discord.wh.send(embed=embed, username="error", file=file, avatar_url=self.config.discord.avatar_url) self.config.discord.wh.send(embed=embed, username="error", file=file, avatar_url=self.config.discord.avatar_url)
self.config.discord.wh.send(username="error", file=File("page.html"), avatar_url=self.config.discord.avatar_url) self.config.discord.wh.send(username="error", file=File("page.html"), avatar_url=self.config.discord.avatar_url)

View File

@ -11,12 +11,15 @@ class Driver:
self.mobile_driver = mobile_driver self.mobile_driver = mobile_driver
def switch_to_driver(self, driver: str): def switch_to_driver(self, driver: str):
match driver: match driver.lower():
case "pc" | "PC" | "Pc": case "pc":
self.driver = self.pc_driver self.driver = self.pc_driver
case "mobile" | "Mobile": case "mobile":
self.driver = self.mobile_driver self.driver = self.mobile_driver
case _: case _:
raise ValueError("The driver must be either pc or mobile") raise ValueError("The driver must be either pc or mobile")
def current_driver(self):
return "PC" if self.pc_driver == self.driver else "Mobile"

View File

@ -1,5 +1,3 @@
import json
from pyotp import TOTP from pyotp import TOTP
from modules.Tools.logger import debug, warning from modules.Tools.logger import debug, warning
@ -12,7 +10,8 @@ class UserCredentials:
self.total = 0 self.total = 0
def add(self, username: str, password: str, tfa: str = None): def add(self, username: str, password: str, tfa: str = None):
debug(f"adding account with data : Username: {username}, Password: {password}, 2FA: {'None' if tfa == '' else tfa}") debug(
f"adding account with data : Username: {username}, Password: {password}, 2FA: {'None' if tfa == '' else tfa}")
self.data[self.total] = { self.data[self.total] = {
"username": username, "username": username,
"password": password, "password": password,
@ -31,12 +30,17 @@ class UserCredentials:
def get_tfa(self): def get_tfa(self):
if not self.tfa_enable(): if not self.tfa_enable():
warning("Warning: TFA is not enabled. Calling get_tfa is an expected behaviour.") warning("Warning: TFA is not enabled. Can't get a TFA code.")
return TOTP(self.data[self.current]["tfa"]) return None
return TOTP(self.data[self.current]["2fa"])
def next_account(self): def next_account(self):
self.current += 1 self.current += 1
debug(f"New credentials: {self.data[self.current]}") if self.is_valid():
debug(f"New credentials: {self.data[self.current]}")
else:
debug("No new credentials.")
def is_valid(self): def is_valid(self):
return self.current < self.total return (self.current < self.total
and self.get_mail() != "" and self.get_mail is not None)

View File

@ -0,0 +1,8 @@
import undetected_chromedriver as uc
from pyvirtualdisplay.smartdisplay import SmartDisplay
display = SmartDisplay(size=(1920, 1080))
display.start()
driver = uc.Chrome()
driver.close()
driver.close()

View File

@ -30,7 +30,7 @@ class ColoredFormatter(logging.Formatter):
# Set up the root logger # Set up the root logger
root_logger = logging.getLogger(__name__) root_logger = logging.getLogger(__name__)
root_logger.setLevel(logging.DEBUG) root_logger.setLevel(logging.INFO)
# Create a console handler and set the formatter # Create a console handler and set the formatter
ch = logging.StreamHandler() ch = logging.StreamHandler()

View File

@ -10,7 +10,7 @@ def get_domain(driver):
def custom_sleep(temps): def custom_sleep(temps):
try: try:
if True: # todo: change this awful condition if False: # todo: change this awful condition
points = ["", "", "", "", "", "", "", ""] points = ["", "", "", "", "", "", "", ""]
passe = 0 passe = 0
for _ in range(int(temps)): for _ in range(int(temps)):

View File

@ -0,0 +1,50 @@
import requests
import re
from packaging import version
import subprocess
from logger import critical, info, error
errorMessage = subprocess.run(['python3', 'generate_error.py'], check=False, stdout=subprocess.PIPE,
stderr=subprocess.PIPE).stderr.decode("utf-8")
versionPattern = "This version of ChromeDriver only supports Chrome version ([0-9]+)"
try:
versionN = re.search(versionPattern, errorMessage)[1]
except Exception as e:
critical("Can't get version number from error")
error(e)
info("error message")
info(errorMessage)
exit(0)
info(f"Needed version : '{versionN}'")
downloadUrl = "http://mirror.cs.uchicago.edu/google-chrome/pool/main/g/google-chrome-stable/"
r = requests.get(downloadUrl)
content = r.text
exactVersionList = re.findall(f"(google-chrome-stable_({versionN}.[0-9.]+)[^<^>^\"]+)", content)
try:
best = exactVersionList[0]
except Exception as e:
critical("No version matches required version")
error(e)
exit(0)
for i in exactVersionList:
if version.parse(i[1]) > version.parse(best[1]):
best = i
chromeDebURL = f"http://mirror.cs.uchicago.edu/google-chrome/pool/main/g/google-chrome-stable/{best[0]}"
info(f"chrome deb URL : {chromeDebURL}")
info("downloading chrome")
subprocess.call(['wget', "-O", "/tmp/chrome.deb", chromeDebURL])
info("Chrome deb downloaded. Installing chrome")
subprocess.call(["dpkg", "-i", "/tmp/chrome.deb"])
info("Chrome installed")

View File

@ -1,7 +1,9 @@
import json
import os
from random import uniform from random import uniform
from selenium.common import TimeoutException from selenium.common import TimeoutException
from selenium.webdriver import ActionChains, Keys from selenium.webdriver import Keys
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support.wait import WebDriverWait
@ -10,33 +12,40 @@ from modules.Tools.logger import debug
from modules.Tools.tools import * from modules.Tools.tools import *
def set_language(ldriver): def init_profile(mail, mobile=False):
ldriver.get("chrome://settings/languages") if not mobile:
action = ActionChains(ldriver) chrome_profile_dir = "/app/MsRewards-Reborn/user_data/profile/" + mail
action.reset_actions() else:
# select language chrome_profile_dir = "/app/MsRewards-Reborn/user_data/profile/mobile-" + mail
x_coord = 1200
y_coord = 150 os.makedirs(chrome_profile_dir, exist_ok=True)
action.move_by_offset(x_coord, y_coord).click().perform()
sleep(0.5) preferences_file = os.path.join(chrome_profile_dir, "Default", "Preferences")
# scroll down if not os.path.exists(preferences_file):
action.reset_actions() os.makedirs(os.path.join(chrome_profile_dir, "Default"), exist_ok=True)
elm = ldriver.find_element(By.XPATH, "/html/body") with open(preferences_file, "w") as f:
ActionChains(ldriver) \ json.dump(
.send_keys("french") \ {
.pause(0.5) \ "intl": {
.send_keys(Keys.TAB + Keys.TAB + Keys.ENTER + Keys.TAB + Keys.TAB + Keys.ENTER) \ "accept_languages": "fr-FR,en-US,en",
.perform() "selected_languages": "fr-FR,en-US,en"
x_coord = 1163 }
y_coord = 717 }, f
action.move_by_offset(x_coord, y_coord).click().perform() )
# put to the top else:
sleep(0.5) with open(preferences_file, "r") as f:
action.reset_actions() settings = json.load(f)
x_coord = 1257
y_coord = 328 if "intl" not in settings:
action.move_by_offset(x_coord, y_coord).click().perform() settings["intl"] = {}
action.click().perform()
settings["intl"]["accept_languages"] = "fr-FR,en-US,en"
settings["intl"]["selected_languages"] = "fr-FR,en-US,en"
with open(preferences_file, "w") as f:
json.dump(settings, f)
return chrome_profile_dir
# Deal with RGPD popup as well as some random popup like 'are you satisfied' one # Deal with RGPD popup as well as some random popup like 'are you satisfied' one
@ -61,12 +70,18 @@ def send_keys_wait(element, keys: str) -> None:
sleep(uniform(0.1, 0.3)) sleep(uniform(0.1, 0.3))
def send_wait_and_confirm(element, keys: str) -> None:
send_keys_wait(element, keys)
element.send_keys(Keys.ENTER)
# Wait for the presence of the element identifier or [timeout]s # Wait for the presence of the element identifier or [timeout]s
def wait_until_visible(search_by: str, identifier: str, timeout: int = 20, browser=None) -> bool: def wait_until_visible(search_by: str, identifier: str, timeout: int = 20, browser=None, raise_error=True) -> bool:
try: try:
WebDriverWait(browser, timeout).until( WebDriverWait(browser, timeout).until(
expected_conditions.visibility_of_element_located((search_by, identifier)), "element not found") expected_conditions.visibility_of_element_located((search_by, identifier)), "element not found")
return True return True
except TimeoutException as e: except TimeoutException as e:
error(f"element {identifier} not found after {timeout}s") f = error if raise_error else debug
f(f"element {identifier} not found after {timeout}s")
return False return False

View File

@ -4,7 +4,7 @@ selenium
pillow pillow
pyvirtualdisplay pyvirtualdisplay
undetected_chromedriver undetected_chromedriver
requests requests>=2.31.0
flask flask
flask_sse flask_sse
EasyProcess EasyProcess

View File

@ -1,6 +0,0 @@
from modules.Classes.WordList import WordList
w = WordList("/usr/share/dict/french")
for i in range(100):
print(w.get_word())

1
user_data/version Normal file
View File

@ -0,0 +1 @@
0.0.0

View File

@ -1 +1 @@
v6.8.22 v6.8.57