updated webUI according to param changes

This commit is contained in:
piair 2024-02-27 11:26:55 +01:00
commit a0a4535c35
54 changed files with 350498 additions and 0 deletions

32
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem, and the html file if there is an error.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

17
.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
dev_build.sh
geckodriver.log
.vscode/
.idea
venv
/Git
page.html
screenshot.png
login.csv
data
**/__pycache__
user_data/*
install.sh
nohup.out
points.csv
file.png
user_data/configs.json

22
Dockerfile Normal file
View File

@ -0,0 +1,22 @@
FROM python:3.10
ENV DEBIAN_FRONTEND noninteractive
WORKDIR /app/
RUN apt update \
&& 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 \
&& apt install redis libgtk-4-1 libvulkan1 libxdamage1 -y \
&& curl -sSLO https://nc.piair.xyz/s/BKLsBWoZkTdYjfq/download/chrome.deb \
&& ln -fs /usr/share/zoneinfo/Europe/Paris /etc/localtime \
&& git clone https://gitea.augustin64.fr/piair/MsRewards-Reborn \
&& python3 -m pip install -r MsRewards-Reborn/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 \
&& echo "deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main" | tee -a /etc/apt/sources.list.d/grafana.list \
&& apt update \
&& 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 \
&& bash MsRewards-Reborn/config/config.sh \
&& dpkg -i chrome.deb
ENV TZ="Europe/Paris"
WORKDIR /app/MsRewards-Reborn/Flask/
CMD bash start.sh

442
Flask/app.py Normal file
View File

@ -0,0 +1,442 @@
import gevent.monkey
gevent.monkey.patch_all()
from time import sleep
import subprocess
import os
from flask import Flask, Response, redirect, url_for, request, session, abort, render_template, send_from_directory
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from flask_login import LoginManager, UserMixin, login_required, login_user, logout_user
from werkzeug.utils import secure_filename
import json
import re
from requests import get
import redis
# redis part for live update
pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
r = redis.Redis(connection_pool=pool)
def generate_output():
pubsub = r.pubsub()
pubsub.subscribe('console')
try :
for message in pubsub.listen():
if message['type'] == 'message':
print(message)
yield f"data: {message['data'].decode()}\n\n"
except Exception as e:
print(f"ya eu une erreur sad {e}")
# the end
global password
with open("/app/MsRewards-Reborn/user_data/flask.json", "r") as inFile:
data = json.load(inFile)
password = data["password"]
secret = data["secret"]
if secret == "":
import secrets
secret = secrets.token_hex()
with open("/app/MsRewards-Reborn/user_data/flask.json", "w") as inFile:
data = {
"password": password,
"secret": secret
}
json.dump(data, inFile)
"""
#Automatic start of MsRewards
"""
def daily_command():
subprocess.Popen(["git",'pull'])
subprocess.Popen(["pkill","-9","chrome"])
subprocess.Popen(["pkill","-9","Xvfb"])
subprocess.Popen(["pkill","-9","Xvnc"])
subprocess.Popen(["pkill","-9", "undetected_chromedriver"])
scheduler = BackgroundScheduler()
scheduler.start()
scheduler.add_job( # on relance le job
daily_command, # ---
trigger=CronTrigger(
year="*", month="*", day="*", hour="0", minute="0", second="0"
), # ---
name="Daily refresh", # ---
id="99" # ---
)
def start_ms(i):
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
subprocess.Popen([f"python3 -u /app/MsRewards-Reborn/V6.py -c {i}"], stdout=log, stderr=log, shell=True)
log.close()
TriggerDict = {}
def update_jobs():
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
configs = json.load(inFile)
for i in configs:
try :
h, m = configs[i]["time"].split(":")
print("\033[36m" + f"config {i} : {h}:{m}" + "\033[0m")
TriggerDict[i] = CronTrigger(
year="*", month="*", day="*", hour=h, minute=m, second="0"
)
if configs[i]["enabled"]:
try :
scheduler.remove_job(i) # on reset le job
except Exception as e:
print(f"\033[33merror with deleting config {i} : {e}\033[0m")
try :
scheduler.add_job( # on relance le job
start_ms, # ---
trigger=TriggerDict[i], # ---
args=[i], # ---
name="Daily start", # ---
id=i # ---
)
print("\033[36m" + f"successfully created config {i}" + "\033[0m")
except Exception as e:
print(f"\033[33merror with creating config {i} : {e}\033[0m")
else :
try :
scheduler.remove_job(i)
except Exception as e :
print(f"\033[33merror with deleting config {i} : {e}\033[0m")
except Exception as e:
print(e)
"""
#Flask app
"""
app = Flask(__name__)
@app.context_processor
def inject_default_variables():
with open("/app/MsRewards-Reborn/version", "r") as f:
version = f.readline().replace("\n", '')
return dict(version=version)
"""
#Login stuff
"""
# config
app.config["TEMPLATES_AUTO_RELOAD"] = True
app.config.update(
SECRET_KEY = secret
)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "login"
# silly user model
class User(UserMixin):
def __init__(self, id):
self.id = id
self.name = "user" + str(id)
self.password = password
def __repr__(self):
return "%d/%s/%s" % (self.id, self.name, self.password)
users = [User(1)]
@app.route('/stream')
def stream():
return Response(generate_output(), content_type='text/event-stream')
@app.route("/login/", methods=["GET", "POST"])
def login():
if request.method == 'POST':
if request.form['password'] == password:
user = User(id)
login_user(user)
if password == "ChangeMe":
return(redirect('/change_password'))
return(redirect('/schedule'))
else:
return abort(401)
else:
return(render_template("login.html"))
@app.route("/change_password/", methods=["GET", "POST"])
@login_required
def change_password():
global password
if request.method == 'POST':
password = request.form["password"]
subprocess.Popen(["grafana-cli", "admin", "reset-admin-password", password])
with open("/app/MsRewards-Reborn/user_data/flask.json", "w") as inFile:
data = {
"password": password,
"secret": secret
}
json.dump(data, inFile)
return(render_template("change_password.html"))
# handle login failed
@app.errorhandler(401)
def unauthorized(e):
return(redirect("/login"))
# callback to reload the user object
@login_manager.user_loader
def load_user(userid):
return User(userid)
"""
#end of login stuff
"""
@app.route("/")
def main():
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
configs = json.load(inFile)
return(render_template("schedule.html", data=configs))
@app.route("/discord/")
def discord_get():
with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile:
data = json.load(inFile)
return(render_template("discord.html", data=data, len=maxi(data)))
@app.route("/discord/", methods=["post"])
def discord_post():
with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile:
data = json.load(inFile)
action = request.form
if action['DISCORD'] == "delete" :
data.pop(action["select"], None)
else :
config = action["select"]
successL = action["successL"]
try :
a = action["successT"]
successT = "True"
except:
successT = "False"
try :
a = action["errorsT"]
errorsT = "True"
except:
errorsT = "False"
errorsL = action["errorsL"]
name = action["name"] if action["name"] else f"unnamed{action['select']}"
data[config] = {"errorsL" : errorsL, "errorsT": errorsT, "successT": successT, "successL": successL, "name": name}
with open("/app/MsRewards-Reborn/user_data/discord.json", "w") as outFile:
json.dump(data, outFile)
return(render_template("discord.html", data=data, len=maxi(data)))
@app.route("/dev/")
def dev2():
return(render_template("dev.html"))
@app.route("/settings/")
def settings_get():
with open("/app/MsRewards-Reborn/user_data/settings.json", "r") as inFile:
settings = json.load(inFile)
return(render_template("settings.html", data=settings))
@app.route("/settings/", methods=["post"])
def settings_post():
settings = {}
action = request.form
settings['avatarlink'] = action["avatarlink"]
with open("/app/MsRewards-Reborn/user_data/settings.json", "w") as inFile:
json.dump(settings, inFile)
return(render_template("settings.html", data=settings))
@app.route("/proxy/")
def proxy_get():
with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile:
j = json.load(inFile)
return(render_template("proxy.html", data=j, len=maxi(j)))
@app.route("/proxy/", methods=["post"])
def proxy_post():
with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile:
data = json.load(inFile)
action = request.form
print(action)
if action['PROXY'] == "delete" :
print(action)
data.pop(action["select"], None)
else :
try :
config = action["select"]
address = action["address"]
port = action["port"]
name = action["name"] if action["name"] else f"@unnamed{action['select']}"
data[config] = {"address" : address, "port": port, "name": name}
except :
print("error : probably bad config")
with open("/app/MsRewards-Reborn/user_data/proxy.json", "w") as outFile:
json.dump(data, outFile)
return(render_template("proxy.html", data=data, len=maxi(data)))
@app.route("/schedule/")
def schedule_get():
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
configs = json.load(inFile)
return(render_template("schedule.html", data=configs))
@app.route("/schedule/", methods=["post"])
def schedule_post():
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
configs = json.load(inFile)
data = dict(request.form)
for i in configs:
try :
data[f'switch{i}']
except :
data[f'switch{i}'] = "off"
for i in configs:
configs[i]["time"] = data[f"time{i}"]
configs[i]["enabled"] = data[f"switch{i}"] == "on"
with open("/app/MsRewards-Reborn/user_data/configs.json", "w") as inFile:
json.dump(configs, inFile)
update_jobs()
return(render_template("schedule.html", data=configs))
@app.route("/config/")
def config_get():
with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile:
proxys = json.load(inFile)
with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile:
discords = json.load(inFile)
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
configs = json.load(inFile)
return(render_template("config.html", data=configs, discords=discords, proxys=proxys, configs=configs, len=maxi(configs)))
@app.route("/config/", methods=["POST"])
def config_post():
action = request.form
with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile:
proxys = json.load(inFile)
with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile:
discords = json.load(inFile)
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
configs = json.load(inFile)
if action["data"] == "delete":
print(action["config"])
configs.pop(action["config"])
else :
comptes = {
"1":{"mail": action["mail1"], "pwd": action["pwd1"], "2fa": action["2fa1"]},
"2":{"mail": action["mail2"], "pwd": action["pwd2"], "2fa": action["2fa2"]},
"3":{"mail": action["mail3"], "pwd": action["pwd3"], "2fa": action["2fa3"]},
"4":{"mail": action["mail4"], "pwd": action["pwd4"], "2fa": action["2fa4"]},
"5":{"mail": action["mail5"], "pwd": action["pwd5"], "2fa": action["2fa5"]}
}
configs[action["config"]] = {
"name" : action["name"] if action["name"] != "" else f"unnamed{action['config']}",
"proxy": action["proxy"],
"discord": action["discord"],
"time":"",
"enabled":"False",
"accounts": comptes
}
with open("/app/MsRewards-Reborn/user_data/configs.json", "w") as outFile:
json.dump(configs, outFile)
return(render_template("config.html", data=configs, discords=discords, proxys=proxys, configs=configs, len=maxi(configs)))
@app.route("/logs/", methods=["GET", "POST"])
def logs():
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
configs = json.load(inFile)
print(configs)
return(render_template("logs.html", data=configs))
@app.route("/stats/", methods=["GET", "POST"])
def stats():
return(render_template("stats.html"))
@app.route("/override/", methods=["POST"])
def override_post():
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
subprocess.Popen([f"python3 -u /app/MsRewards-Reborn/V6.py -c {json['config'][0]} --json \"{json}\""], stdout=log, stderr=log, shell=True)
log.close()
return(render_template("vnc_post.html"))
@app.route("/override/", methods=["GET"])
def override_get():
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
configs = json.load(inFile)
return(render_template("vnc_get.html", configs=configs))
@app.route('/download/<path:filename>', methods=['GET', 'POST'])
@login_required
def download(filename):
return send_from_directory(directory='/app/MsRewards-Reborn/user_data/', path=filename, as_attachment=True)
def allowed_file(filename):
ALLOWED_EXTENSIONS = ["json"]
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/upload_file/', methods=['POST'])
@login_required
def upload_file():
print(request.files)
i = 1
while f'file{i}' in request.files :
file = request.files[f'file{i}']
if file.filename == '':
print('end of files')
return redirect(url_for('settings_get'))
elif file and allowed_file(file.filename):
filename = secure_filename(file.filename)
print(os.path.join('/app/MsRewards-Reborn/user_data/', filename))
file.save(os.path.join('/app/MsRewards-Reborn/user_data/', filename))
i += 1
print(i)
print(f'file{i}' in request.files)
print("requete bizarre")
return redirect(url_for('settings_get'))
def maxi(dict):
m = 0
for i in dict :
if int(i) >= m:
m = int(i)
return(m+1)
update_jobs()
subprocess.Popen(["bash",'/app/MsRewards-Reborn/config/request.sh'])

5
Flask/start.sh Normal file
View File

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

405
Flask/static/css/flask.css Normal file
View File

@ -0,0 +1,405 @@
@import url('https://fonts.googleapis.com/css?family=Montserrat');
/* Colors */
:root {
--color-background0: #212121;
--color-background1: #212121;
--color-background2: dimgray;
--color-border0: #FFFFFF;
--color-border1: grey;
--color-text: #FFFFFF;
--color-green: green;
--color-lime: #BADA55;
--color-red: red;
--color-switch-button: #FFFFFF;
--color-switch-background: grey;
--color-transparent: rgba(0, 0, 0, 0);
}
/* Sidebar size is the left panel size when opened */
:root {
--sidebar-size: max(20vw, 160px);
--sidebar-sz-plus10: calc(var(--sidebar-size) + 10px);
--sidebar-sz-plus30: calc(var(--sidebar-size) + 30px);
}
html {
text-align: center;
font-family: Montserrat;
height: 95%;
}
form {
width: 100%;
}
table {
width: 100%;
height: 100%;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
a {
color: var(--color-text);
}
iframe{
border: none;
}
input[type=text] {
width: 90%;
padding: 12px 20px;
margin: 8px 0;
background-color: var(--color-background1);
border: 2px solid var(--color-border1);
border-radius: 4px;
outline: none;
color: var(--color-text);
text-align: center;
}
input[type=password] {
width: 90%;
padding: 12px 20px;
margin: 8px 0;
text-align: center;
border: 2px solid var(--color-border1);
border-radius: 4px;
outline: none;
color: var(--color-text);
background-color: var(--color-background1);
}
.button{
border-radius: 4px;
border: 2px solid var(--color-border1);
background-color: var(--color-background1);
color: var(--color-text);
padding: 12px 20px;
width: 70%;
margin: 10px;
}
.left-button{
width: 10%;
}
.ban{
border-radius: 4px;
border: 2px solid var(--color-red);
background-size: 200% 100%;
background-image: linear-gradient(to right, var(--color-background1) 50%, var(--color-red) 50%);
-webkit-transition: background-position 0.5s;
-moz-transition: background-position 0.5s;
transition: background-position 0.5s;
color: var(--color-text);
padding: 12px 0px;
width: 70%;
margin: 10px;
}
.ban:hover{
background-position: -100% 0;
}
.confirm{
color: var(--color-text);
padding: 12px 20px;
width: 70%;
margin: 10px;
background-size: 200% 100%;
background-image: linear-gradient(to right, var(--color-background1) 50%, var(--color-green) 50%);
-webkit-transition: background-position 0.5s;
-moz-transition: background-position 0.5s;
transition: background-position 0.5s;
border: 2px solid var(--color-border1);
}
.confirm:hover{
background-position: -100% 0;
}
.confirm{
color: var(--color-text);
padding: 12px 20px;
width: 70%;
margin: 10px;
background-size: 200% 100%;
background-image: linear-gradient(to right, var(--color-background1) 50%, var(--color-green) 50%);
-webkit-transition: background-position 0.5s;
-moz-transition: background-position 0.5s;
transition: background-position 0.5s;
border: 2px solid var(--color-border1);
}
.confirm:hover{
background-position: -100% 0;
}
.unselected{
border-radius: 4px;
border: 2px solid var(--color-border1);
background-color: var(--color-background1);
color: var(--color-text);
padding: 12px 20px;
width: 70%;
margin: 10px;
}
.selected{
border-radius: 4px;
border: 2px solid var(--color-border1);
background-color: var(--color-background2);
color: var(--color-text);
padding: 12px 20px;
width: 70%;
margin: 10px;
}
button:hover{
border: 2px solid var(--color-border0);
cursor: pointer;
}
input:hover{
border: 2px solid var(--color-border0);
}
.submit{
border-radius: 4px;
border: 2px solid var(--color-border1);
background-color: var(--color-background1);
color: var(--color-text);
padding: 12px 20px;
width: 100%;
margin: 10px;
}
body {
background-color: var(--color-background0);
color: var(--color-text);
height: 100%;
}
.left-pannel{
flex: 1;
margin: 20px;
border: 1px solid var(--color-border0);
border-radius: 5px;
padding: 20px;
width: 80%;
height: 90%;
}
.content{
flex: 3;
margin: 20px;
border: 1px solid var(--color-border0);
border-radius: 5px;
padding: 20px;
height: 90%;
}
.content > ul {
display: inline-block;
}
.container {
display: flex;
width: 100%;
height: 100%;
}
.row-item {
margin: 10px 0;
text-align: center;
}
#image img {
display: block;
margin: auto;
width: 20%;
}
.comlumn-name{
width: 20%;
}
/*
For toggle switch
*/
input.toogle[type=checkbox]{
height: 0;
width: 0;
visibility: hidden;
}
label {
padding: 8px;
margin: auto;
cursor: pointer;
text-indent: -9999px;
width: 100px;
height: 50px;
background: var(--color-switch-background);
display: block;
border-radius: 100px;
position: relative;
}
/*
width: height of label /2 ;
height: height of label /2px;
*/
label:after {
content: '';
position: absolute;
top: 5px;
left: 5px;
width: 40px;
height: 40px;
background: var(--color-switch-button);
border-radius: 90px;
transition: 0.3s;
}
input:checked + label {
background: var(--color-lime);
}
input:checked + label:after {
left: calc(100% - 5px);
transform: translateX(-100%);
}
/*changer l'épaisseur du click */
label:active:after {
width: 30px;
}
select {
width: 90%;
/*height: 100%;*/
padding: 12px 20px;
margin: 8px 0;
background-color: var(--color-background1);
border: 2px solid var(--color-border1);
border-radius: 4px;
outline: none;
color: var(--color-text);
text-align: center;
}
select:hover {
border: 2px solid var(--color-border0);
}
input[type="time"] {
width: 70%;
padding: 8px 12px;
margin: 8px 0;
background-color: var(--color-background1);
border: 2px solid var(--color-border1);
border-radius: 4px;
outline: none;
color: var(--color-text);
text-align: center;
}
input[type="time"]::-webkit-calendar-picker-indicator {
filter: invert(1);
opacity: 0.7;
cursor: pointer;
}
input[type="time"]::-webkit-calendar-picker-indicator:hover {
opacity: 1;
}
/** Sidebar stuff */
#slide-sidebar {
display: none;
}
label[for="slide-sidebar"] {
all: initial;
z-index: 1; /* Always on top */
position: absolute;
top: 20px;
left: var(--sidebar-sz-plus30);
-moz-transition: left 0.5s ease;
transition: left 0.5s ease;
background: var(--color-transparent);
}
label[for="slide-sidebar"]:after {
all:unset;
}
input:checked + label[for="slide-sidebar"] {
background: var(--color-transparent);
}
#slide {
border-style: solid;
border-radius: 4px;
border-width: 1px;
border-color: var(--color-border0);
color: var(--color-text);
background-color: var(--color-background0);
padding: 8px 8px 2px 8px;
}
/* When opened behaviour */
.left-pannel {
width: var(--sidebar-size);
position: fixed;
top: 0;
left: 0;
bottom: 0;
}
.content {
position: absolute;
top: 0;
left: var(--sidebar-sz-plus10);
right: 0;
bottom: 0;
-moz-transition: left 0.5s ease;
transition: left 0.5s ease;
padding: 0 25px;
background-color: var(--color-background0); /* we need no transparency when switching */
overflow: hidden;
}
/* When closed behaviour */
input:checked#slide-sidebar~label {
left: 20px;
}
input:checked#slide-sidebar~.left-pannel {
display: none;
transition: display 0s 0.5s;
}
input:checked#slide-sidebar~.content {
left: 0;
}

BIN
Flask/static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 21 0 C 19.355469 0 18 1.355469 18 3 L 18 5 L 10.1875 5 C 10.0625 4.976563 9.9375 4.976563 9.8125 5 L 8 5 C 7.96875 5 7.9375 5 7.90625 5 C 7.355469 5.027344 6.925781 5.496094 6.953125 6.046875 C 6.980469 6.597656 7.449219 7.027344 8 7 L 9.09375 7 L 12.6875 47.5 C 12.8125 48.898438 14.003906 50 15.40625 50 L 34.59375 50 C 35.996094 50 37.1875 48.898438 37.3125 47.5 L 40.90625 7 L 42 7 C 42.359375 7.003906 42.695313 6.816406 42.878906 6.503906 C 43.058594 6.191406 43.058594 5.808594 42.878906 5.496094 C 42.695313 5.183594 42.359375 4.996094 42 5 L 32 5 L 32 3 C 32 1.355469 30.644531 0 29 0 Z M 21 2 L 29 2 C 29.5625 2 30 2.4375 30 3 L 30 5 L 20 5 L 20 3 C 20 2.4375 20.4375 2 21 2 Z M 11.09375 7 L 38.90625 7 L 35.3125 47.34375 C 35.28125 47.691406 34.910156 48 34.59375 48 L 15.40625 48 C 15.089844 48 14.71875 47.691406 14.6875 47.34375 Z M 18.90625 9.96875 C 18.863281 9.976563 18.820313 9.988281 18.78125 10 C 18.316406 10.105469 17.988281 10.523438 18 11 L 18 44 C 17.996094 44.359375 18.183594 44.695313 18.496094 44.878906 C 18.808594 45.058594 19.191406 45.058594 19.503906 44.878906 C 19.816406 44.695313 20.003906 44.359375 20 44 L 20 11 C 20.011719 10.710938 19.894531 10.433594 19.6875 10.238281 C 19.476563 10.039063 19.191406 9.941406 18.90625 9.96875 Z M 24.90625 9.96875 C 24.863281 9.976563 24.820313 9.988281 24.78125 10 C 24.316406 10.105469 23.988281 10.523438 24 11 L 24 44 C 23.996094 44.359375 24.183594 44.695313 24.496094 44.878906 C 24.808594 45.058594 25.191406 45.058594 25.503906 44.878906 C 25.816406 44.695313 26.003906 44.359375 26 44 L 26 11 C 26.011719 10.710938 25.894531 10.433594 25.6875 10.238281 C 25.476563 10.039063 25.191406 9.941406 24.90625 9.96875 Z M 30.90625 9.96875 C 30.863281 9.976563 30.820313 9.988281 30.78125 10 C 30.316406 10.105469 29.988281 10.523438 30 11 L 30 44 C 29.996094 44.359375 30.183594 44.695313 30.496094 44.878906 C 30.808594 45.058594 31.191406 45.058594 31.503906 44.878906 C 31.816406 44.695313 32.003906 44.359375 32 44 L 32 11 C 32.011719 10.710938 31.894531 10.433594 31.6875 10.238281 C 31.476563 10.039063 31.191406 9.941406 30.90625 9.96875 Z"/></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

102
Flask/static/js/main.js Normal file
View File

@ -0,0 +1,102 @@
function addline(ligne){
var lplus = parseInt(ligne) + 1;
document.getElementById("table").insertRow(lplus).insertCell(0).innerHTML = '<input type="file" id="file' + lplus + '" name="file' + lplus + '" onchange="addline(' + lplus + ')">'
}
function change_discord(value, data, len) {
if (value == len){
document.getElementById("name").value = "";
document.getElementById("submit").value = "Create !";
document.getElementById("successT").checked = false;
document.getElementById("errorsT").checked = false;
document.getElementById("successL").value = "";
document.getElementById("errorsL").value = "";
}
else {
console.log(data[parseInt(value)]["successL"]);
document.getElementById("submit").value = "Update";
document.getElementById("successT").checked = data[parseInt(value)]["successT"] == "True";
document.getElementById("errorsT").checked = data[parseInt(value)]["errorsT"] == "True";
document.getElementById("successL").value = data[parseInt(value)]["successL"];
document.getElementById("errorsL").value = data[parseInt(value)]["errorsL"];
document.getElementById("name").value = data[parseInt(value)]["name"];
}
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function change_config(value, data, len) {
console.log(data[2]);
document.getElementById("delete").style.visibility = "hidden";
if (value == len){
document.getElementById("submit").value = "Create !";
document.getElementById("discord").value = "-1";
document.getElementById("proxy").value = "-1";
document.getElementById("name").value = "";
for (let i = 1; i < 6; i++) {
document.getElementById("mail"+ i).value = "";
document.getElementById("pwd"+ i).value = "" ;
document.getElementById("2fa"+ i).value = "" ;
}
}
else {
document.getElementById("delete").style.visibility = "visible";
document.getElementById("submit").value = "Update !";
document.getElementById("discord").value = data[parseInt(value)]["discord"];
document.getElementById("proxy").value = data[parseInt(value)]["proxy"];
document.getElementById("name").value = data[parseInt(value)]["name"];
for (let i = 1; i < 6; i++) {
document.getElementById("mail"+ i).value = data[parseInt(value)]['accounts'][i]["mail"] ;
document.getElementById("pwd"+ i).value = data[parseInt(value)]['accounts'][i]["pwd"] ;
document.getElementById("2fa"+ i).value = data[parseInt(value)]['accounts'][i]["2fa"] ;
}
}
}
function change_proxy(value, data, len) {
if (value == len){
document.getElementById("submit").value = "Create";
document.getElementById("address").value = "";
document.getElementById("port").value = "";
document.getElementById("name").value = "";
}
else {
document.getElementById("submit").value = "Update";
document.getElementById("address").value = data[parseInt(value)]["address"];
document.getElementById("port").value = data[parseInt(value)]["port"];
document.getElementById("name").value = data[parseInt(value)]["name"];
}
}
function change_override(value, data) {
for (let i = 1; i < 6; i++) {
document.getElementById("compte_"+ i).innerHTML = data[parseInt(value)]['accounts'][i]["mail"];
if (!(data[parseInt(value)]['accounts'][i]["mail"])){
console.log("hiding" + i);
for (let j = 1; j <= 5; j++) {
document.getElementById(i.toString() +j.toString()).style.visibility = "hidden";
}
} else {
console.log("showing" + i);
for (let j = 1; j <= 5; j++) {
console.log("element " + i.toString()+j.toString());
document.getElementById(i.toString()+j.toString()).style.visibility = "visible";
}
}
}
}
function change_logs(value) {
var myIframe = document.getElementById('embed');
myIframe.addEventListener("load", function() {
let doc = myIframe.contentDocument;
doc.body.innerHTML = doc.body.innerHTML + '<style>html{color:white;}</style>';
myIframe.contentWindow.scrollTo(0, myIframe.contentDocument.body.scrollHeight);
});
myIframe.src = "/static/logs/" + value + ".txt";
}

1
Flask/static/logs/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.txt

79
Flask/templates/base.html Normal file
View File

@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<head>
<meta name="robots" content="noindex, nofollow">
<meta charset="utf-8">
<link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon.ico') }}">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MS Rewards</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/flask.css')}}"/>
<script src="{{ url_for('static', filename='js/main.js')}}"></script>
</head>
<body>
<div class="container">
<input id="slide-sidebar" type="checkbox" role="button"/>
<label for="slide-sidebar">
<div id="slide">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-menu-2" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M4 6l16 0"></path>
<path d="M4 12l16 0"></path>
<path d="M4 18l16 0"></path>
</svg>
</div>
</label>
<div class="left-pannel">
<table>
<tr>
<td>
<button class="unselected" id="sidebar_schedule" onclick="location.href = '/schedule';">schedule</button>
</td>
</tr>
<tr>
<td>
<button class="unselected" id="sidebar_config" onclick="location.href = '/config';">config</button>
</td>
</tr>
<tr>
<td>
<button class="unselected" id="sidebar_discord" onclick="location.href = '/discord';">discord</button>
</td>
</tr>
<tr>
<td>
<button class="unselected" id="sidebar_proxy" onclick="location.href = '/proxy';">proxy</button>
</td>
</tr>
<tr>
<td>
<button class="unselected" id="sidebar_logs" onclick="location.href = '/logs';">logs</button>
</td>
</tr>
<tr>
<td>
<button class="unselected" id="sidebar_stats" onclick="location.href = '/stats';">stats</button>
</td>
</tr>
<tr>
<td>
<button class="unselected" id="sidebar_override" onclick="location.href = '/override';">Override</button>
</td>
</tr>
<tr>
<td>
<button class="unselected" id="sidebar_settings" onclick="location.href = '/settings';">settings</button>
</td>
</tr>
</table>
<script>document.getElementById("sidebar_{% block left_pannel %}{% endblock %}").className = "selected"</script>
</div>
<div class="content">
{% block content %}
{% endblock %}
</div>
</div>
<div class="footer">{{version}}</div>
</body>
</html>

View File

@ -0,0 +1,22 @@
{% extends "base.html" %}
{% block left_pannel %}
{% endblock %}
{% block content %}
{%if not current_user.is_authenticated %}
<h1>Already logged in</h1>
{% else %}
<form method="post" action="/change_password/">
<table>
<tr>
<td class="comlumn-name">Change password</td>
<td><input type="text" name="password"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" name="NewPassword" value="send" class="button"/></td>
</tr>
</table>
</form>
{% endif %}
{% endblock %}

106
Flask/templates/config.html Normal file
View File

@ -0,0 +1,106 @@
{% extends "base.html" %}
{% block left_pannel %}config{% endblock %}
{% block content %}
<script>data2 = JSON.parse('{{configs|tojson}}'); </script>
{%if not current_user.is_authenticated %}
<button class="unselected" onclick="location.href = '/login';">login</button>
{% else %}
<form method="post" action="/config/">
<table>
<tr>
<td style="width: 10%;">config : </td>
<td style="width: 40%;">
<select onchange="change_config(this.value, data2, '{{len}}')" name="config">
<option id="config" value="{{len}}">New config</option>
{% for i in configs %}
<option id="{{configs[i]['name']}}" value="{{i}}">{{configs[i]['name']}}</option>
{% endfor %}
</select>
</td>
<td style="width: 10%;">name : </td>
<td>
<input type="text" id="name" name="name" value = "">
</td>
</tr>
<tr>
<td style="width: 10%;">Proxy : </td>
<td style="width: 40%;">
<select id="proxy" name="proxy">
<option id="" value="-1">No proxy</option>
{% for i in proxys %}
<option id="{{proxys[i]['name']}}" value="{{i}}">{{proxys[i]['name']}}</option>
{% endfor %}
</select>
</td>
<td style="width: 10%;">Discord : </td>
<td>
<select id="discord" name="discord">
<option id="no discord" value="-1">No discord (not sure about the support)</option>
{% for i in discords %}
<option id="{{discords[i]['name']}}" value="{{i}}">{{discords[i]['name']}}</option>
{% endfor %}
</select>
</td>
</tr>
</table>
<br><br>
<table>
<tr>
<td>Mail</td>
<td>Password</td>
<td>2FA</td>
</tr>
<tr>
<td><input type="text" id="mail1" name="mail1" value=""></td>
<td><input type="text" id="pwd1" name="pwd1" value=""></td>
<td><input type="text" id="2fa1" name="2fa1" value=""></td>
<td class="left-button"><button class="ban" name="ban" value="">ban</button></td>
</tr>
<tr>
<td><input type="text" id="mail2" name="mail2" value=""></td>
<td><input type="text" id="pwd2" name="pwd2" value=""></td>
<td><input type="text" id="2fa2" name="2fa2" value=""></td>
<td class="left-button"><button class="ban" name="ban" value="">ban</button></td>
</tr>
<tr>
<td><input type="text" id="mail3" name="mail3" value=""></td>
<td><input type="text" id="pwd3" name="pwd3" value="" ></td>
<td><input type="text" id="2fa3" name="2fa3" value=""></td>
<td class="left-button"><button class="ban" name="ban" value="">ban</button></td>
</tr>
<tr>
<td><input type="text" id="mail4" name="mail4" value=""></td>
<td><input type="text" id="pwd4" name="pwd4" value="" ></td>
<td><input type="text" id="2fa4" name="2fa4" value=""></td>
<td class="left-button"><button class="ban" name="ban" value="">ban</button></td>
</tr>
<tr>
<td><input type="text" id="mail5" name="mail5" value=""></td>
<td><input type="text" id="pwd5" name="pwd5" value=""></td>
<td><input type="text" id="2fa5" name="2fa5" value=""></td>
<td class="left-button"><button class="ban" name="ban" value="">ban</button></td>
</tr>
</table>
<table>
<tr>
<td>
<input type="submit" class="confirm" name="data" id="submit" value="Create !" class="button" style="width:97%;"/>
</td>
<td class="left-button">
<input type="submit" name="data" id="delete" value="delete" class="ban" style="visibility: hidden;"/>
</td>
</tr>
</table>
</form>
{% endif %}
{% endblock %}

31
Flask/templates/dev.html Normal file
View File

@ -0,0 +1,31 @@
{% extends "base.html" %}
{% block left_pannel %}{% endblock %}
{% block content %}
{%if not current_user.is_authenticated %}
<button class="unselected" onclick="location.href = '/login';">login</button>
{% else %}
<table>
<tr>
<td width="20%" height="90%">
<div id="console"></div>
</td>
</tr>
</table>
<script>
const consoleElement = document.getElementById('console');
const eventSource = new EventSource('/stream');
eventSource.onmessage = (event) => {
console.log("je ne comprend vraiment plus rien, meme git me casse les couilles");
const newOutput = document.createElement('div');
newOutput.textContent = event.data;
consoleElement.appendChild(newOutput);
};
</script>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,56 @@
{% extends "base.html" %}
{% block left_pannel %}discord{% endblock %}
{% block content %}
<script>data = JSON.parse('{{data|tojson}}'); </script>
{%if not current_user.is_authenticated %}
<button class="unselected" onclick="location.href = '/login';">login</button>
{% else %}
<form method="post" action="/discord/">
<table>
<tr>
<td></td>
<td>
<select name="select" onchange="change_discord(this.value, data, '{{len}}')">
<option selected id="new" value="{{ len }}">Create new Discord config</option>
{% for i in data %}
<option id="{{data[i]['name']}}" value="{{i}}">{{data[i]['name']}}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td class="comlumn-name">name</td>
<td><input type="text" id="name" name="name" value=""></td>
</tr>
<tr>
<td class="comlumn-name">Send errors</td>
<td><input type="checkbox" id="errorsT" name="errorsT" class="toogle"/><label for="errorsT">Toggle</label></td>
</tr>
<tr>
<td class="comlumn-name">Send success</td>
<td><input type="checkbox" id="successT" name="successT" class="toogle"/><label for="successT">Toggle</label></td>
</tr>
<tr>
<td class="comlumn-name">Success link</td>
<td><input type="text" id="successL" name="successL" value=""></td>
</tr>
<tr>
<td class="comlumn-name">Failure Link</td>
<td><input type="text" id="errorsL" name="errorsL" value=""></td>
</tr>
</table>
<table>
<tr>
<td class="comlumn-name"></td>
<td style="width:60%;"><input type="submit" name="DISCORD" id="submit" value="Create !" class="confirm" style="width:86%;"/></td>
<td style="padding-right: 20px;">
<input type="submit" name="DISCORD" id="submit" value="delete" class="ban" style="width:60%;"/>
</td>
</tr>
</table>
</form>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends "base.html" %}
{% block left_pannel %}login{% endblock %}
{% block content %}
{%if not current_user.is_authenticated %}
<form method="post" action="/login/">
<table>
<tr>
<td class="comlumn-name">password</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" name="DISCORD" value="send" class="button"/></td>
</tr>
</table>
</form>
{% else %}
<h1>Already logged in</h1>
{% endif %}
{% endblock %}

21
Flask/templates/logs.html Normal file
View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block left_pannel %}logs{% endblock %}
{% block content %}
{%if not current_user.is_authenticated %}
<h1>Already logged in</h1>
{% else %}
<select name="select" onchange="change_logs(this.value)">
<option id="null" value="0">Choisir une config</option>
{% for i in data %}
<option id="{{data[i]['name']}}" value="{{i}}">{{data[i]['name']}}</option>
{% endfor %}
</select>
<br><br>
<iframe type="text/html" src="{{url_for('static', filename='logs/1.txt')}}" width="100%" height="85%" id="embed">
{% endif %}
{% endblock %}

View File

@ -0,0 +1,53 @@
{% extends "base.html" %}
{% block left_pannel %}proxy{% endblock %}
{% block content %}
{%if not current_user.is_authenticated %}
<button class="unselected" onclick="location.href = '/login';">login</button>
{% else %}
<form method="post" action="/proxy/">
<table>
<tr>
<td class="comlumn-name"></td>
<td>
<script>data = JSON.parse('{{data|tojson}}');</script>
<select name="select" onchange="change_proxy(this.value, data, '{{len}}')">
<option selected id="new" value="{{ len }}">Create new proxy</option>
{% for i in data %}
<option id="{{data[i]['name']}}" value="{{i}}">{{data[i]['name']}}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td class="comlumn-name">name : </td>
<td><input type="text" name="name" value="" id="name"></td>
</tr>
<tr>
<td class="comlumn-name">address : </td>
<td><input type="text" name="address" value="" id="address"></td>
</tr>
<tr>
<td class="comlumn-name">port : </td>
<td><input type="text" name="port" value="" id="port"></td>
</tr>
</table>
<table>
<tr>
<td class="comlumn-name"></td>
<td style="width:60%;"><input type="submit" name="PROXY" id="submit" value="Create !" class="confirm" style="width:86%;"/></td>
<td style="padding-right: 20px;"><input type="submit" name="PROXY" id="submit" value="delete" class="ban" style="width:60%;"/></td>
</tr>
</table>
</form>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,35 @@
{% extends "base.html" %}
{% block left_pannel %}schedule{% endblock %}
{% block content %}
{%if not current_user.is_authenticated %}
<button class="unselected" onclick="location.href = '/login';">login</button>
{% else %}
<form method="post" action="/schedule/">
<table>
{% for i in data %}
<tr>
<td>{{data[i]['name']}}</td>
<td>
<input type="time" id="{{i}}" name="time{{i}}" value="{{data[i]['time']}}" required>
</td>
<td>
<input type="checkbox" class="toogle" id="switch{{i}}" name="switch{{i}}" {{ "checked" if data[i]['enabled'] == True else "" }} />
<label for="switch{{i}}">
Toggle
</label>
</td>
</tr>
{% endfor %}
</table>
<input type="submit" name="data" value="Update" class="button"/>
</form>
{% endif %}
{%endblock %}

View File

@ -0,0 +1,49 @@
{% extends "base.html" %}
{% block left_pannel %}settings{% endblock %}
{% block content %}
{%if not current_user.is_authenticated %}
<button class="unselected" onclick="location.href = '/login';">login</button>
{% else %}
<form method="post" action="/settings/">
<table>
<tr>
<td class="comlumn-name">avatar url :</td>
<td><input type="text" name="avatarlink" value="{{data['avatarlink']}}"></td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" name="settings" id="submit" value="update" class="button"/>
</td>
</tr>
</table>
</form>
<h2>TODO</h2>
<ul>
<li>ban button</li>
</ul>
<br><br>
<h2>Backup config files</h2>
<ul>
<li><a href="/download/configs.json">download account config</a></li>
<li><a href="/download/discord.json">download discord config</a></li>
<li><a href="/download/flask.json">download flask config</a></li>
<li><a href="/download/proxy.json">download proxy config</a></li>
<li><a href="/download/settings.json">download settings</a></li>
</ul>
<br><br>
<h2>Upload your files</h2>
<form method="POST" action="/upload_file/" enctype=multipart/form-data>
<table id="table">
<tr><td><input type="file" id="file1" name="file1" onchange='addline(0)'></td></tr>
<tr><td><input type="submit" name="settings" id="submit" value="send file" class="button"/></td></tr>
</table>
</form>
{% endif %}
{%endblock %}

View File

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block left_pannel %}stats{% endblock %}
{% block content %}
{%if not current_user.is_authenticated %}
<button class="unselected" onclick="location.href = '/login';">login</button>
{% else %}
<iframe src="/grafana?orgId=1&kiosk" width="100%" height="100%" frameborder="0"></iframe>
{% endif %}
{%endblock %}

View File

@ -0,0 +1,45 @@
{% extends "base.html" %}
{% block left_pannel %}override{% endblock %}
{% block content %}
{%if not current_user.is_authenticated %}
<button class="unselected" onclick="location.href = '/login';">login</button>
{% else %}
<script>data = JSON.parse('{{configs|tojson}}'); </script>
<form method="post" action="/override/" height="90%">
<select onchange="change_override(this.value, data)" name="config">
<option id="-1" value="-1" selected>Select a config</option>
{% for i in configs %}
<option id="{{configs[i]['name']}}" value="{{i}}">{{configs[i]['name']}}</option>
{% endfor %}
</select>
<table height="90%" class="lines">
<tr>
<td width="30%">name</td>
<td width="14%">Unban</td>
<td width="14%">tout</td>
<td width="14%">daily</td>
<td width="14%">pc</td>
<td width="14%">mobile</td>
</tr>
{% for i in range(0,5) %}
<tr heigh="15%">
<td id="compte_{{i+1}}"></td>
<td id="{{i+1}}1" style="visibility: hidden;"><input type="checkbox" id="compte_{{i+1}}" value="{{i}}" name="unban"></td>
<td id="{{i+1}}2" style="visibility: hidden;"><input type="checkbox" id="compte_{{i+1}}" value="{{i}}" name="tout"></td>
<td id="{{i+1}}3" style="visibility: hidden;"><input type="checkbox" id="compte_{{i+1}}" value="{{i}}" name="daily"></td>
<td id="{{i+1}}4" style="visibility: hidden;"><input type="checkbox" id="compte_{{i+1}}" value="{{i}}" name="pc"></td>
<td id="{{i+1}}5" style="visibility: hidden;"><input type="checkbox" id="compte_{{i+1}}" value="{{i}}" name="mobile"></td>
</tr>
{% endfor %}
</table>
<input type="submit" name="VNC" id="submit" value="Start" class="confirm" style="width:60%;"/>
</form>
{% endif %}
{%endblock %}

View File

@ -0,0 +1,35 @@
{% extends "base.html" %}
{% block left_pannel %}override{% endblock %}
{% block content %}
{%if not current_user.is_authenticated %}
<button class="unselected" onclick="location.href = '/login';">login</button>
{% else %}
<table>
<tr>
<td width="20%" height="90%">
<div id="console"></div>
</td>
<td width="80%">
<iframe src="/novnc/vnc.html?resize=scale&path=novnc/websockify&autoconnect=true&view_only" width="100%" height="100%" frameborder="0"></iframe>
</td>
</tr>
</table>
<script>
const consoleElement = document.getElementById('console');
const eventSource = new EventSource('/stream');
eventSource.onmessage = (event) => {
document.getElementById("console").innerHTML = event.data + document.getElementById("console").innerHTML
};
</script>
{% endif %}
{%endblock %}

806
V6.py Executable file
View File

@ -0,0 +1,806 @@
#!/usr/bin/python3.10
from modules.config import *
from modules.error import *
from modules.driver_tools import *
from modules.cards import *
import modules.globals as g
from modules.Classes.Config import Config
from modules.Classes.UserCredentials import UserCredentials
from modules.Tools.logger import warning, info, debug, error, critical
driver = g.driver
display = g.display
# create a webdriver
def create_driver(mobile=False):
PC_USER_AGENT = (
"Mozilla/5.0 (X11; Linux x86_64)"
"AppleWebKit/537.36 (KHTML, like Gecko)"
"Chrome/122.0.0.0 Safari/537.36 Edg/122.0.2088.46"
)
MOBILE_USER_AGENT = (
"Mozilla/5.0 (Linux; Android 7.0; Nexus 5 Build/MRA58N)"
"AppleWebKit/537.36 (KHTML, like Gecko)"
"Chrome/22 Mobile Safari/537.36"
)
chrome_options = webdriver.ChromeOptions()
if mobile:
chrome_options.add_argument(f"--user-agent={MOBILE_USER_AGENT}")
else:
chrome_options.add_argument(f"--user-agent={PC_USER_AGENT}")
# disabled as it may cause detection
if g.proxy_enabled:
chrome_options.add_argument(f'--proxy-server={g.proxy_address}:{g.proxy_port}')
driver = uc.Chrome(options=chrome_options)
set_language(driver)
return driver
def log_error(error_message, l_driver=driver, log=g.full_log):
global driver
if l_driver is None:
l_driver = driver
if type(error_message) is not str:
error_message = format_error(error_message)
error(str(error_message))
if g.discord_enabled_error:
with open("page.html", "w") as f:
try:
f.write(l_driver.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:
l_driver.save_screenshot("screenshot.png")
if not log:
embed = Embed(
title="An Error has occured",
description=str(error_message),
colour=Colour.red(),
)
else:
embed = Embed(
title="Full log is enabled",
description=str(error_message),
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, switch_to: int = 0) -> None:
driver.switch_to.window(tab)
driver.close()
driver.switch_to.window(driver.window_handles[switch_to])
# 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:
info("Starting to play quiz 2.")
debug(f"override: {override}")
for j in range(override):
custom_sleep(uniform(3, 5))
js_function = """
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 namedRAValue() { //allow calls to getRAvalue
return _w.getRAValue()
};
if (br(document.getElementById("rqAnswerOption0").attributes["data-option"].value) == namedRAValue()){
return(0);
}
else {
return(1);
}
};
return(get_correct_answer())
"""
correct_answer_value = driver.execute_script(js_function)
try:
answer_elem = driver.find_element(By.ID, f"rqAnswerOption{correct_answer_value}")
answer_elem.click()
except exceptions.ElementNotInteractableException:
answer_elem = driver.find_element(By.ID, f"rqAnswerOption{correct_answer_value}")
driver.execute_script("arguments[0].click();", answer_elem)
except Exception as e:
log_error(e)
break
info("Quiz 2 done.")
custom_sleep(3)
def play_quiz8():
info(f"Starting Quiz 8")
override = len(findall("<span id=\"rqQuestionState.\" class=\"emptyCircle\"></span>", driver.page_source)) + 1
debug(f"override : {override}")
correct_answers = ["Should", "be", "reset", "before", "you", "see", "this."] # supress warning
try:
for _ in range(override):
sleep(uniform(3, 5))
correct_answers = []
for i in range(1, 9): # todo: remove this odd 1-offset
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:
warning(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)
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)}")
info("Quiz 8 done.")
custom_sleep(3)
def play_quiz4(override: int = None):
info(f"Starting Quiz 4")
if not override:
try: # fidelity quiz are much longer than usual ones
override = int(findall('rqQuestionState([\d]{1,2})"', driver.page_source)[-1])
except:
override = 3
debug(f"Override : {override}")
try:
for i in range(override):
custom_sleep(uniform(3, 5))
txt = driver.page_source
answer_option = search('correctAnswer":"([^"]+)', txt)[1]
answer_option = answer_option.replace("\\u0027", "'") # replace Unicode weird symbols
answer_element = driver.find_element(By.CSS_SELECTOR, f'[data-option="{answer_option}"]')
try:
answer_element.click()
except exceptions.ElementNotInteractableException:
driver.execute_script("arguments[0].click();", answer_element)
except Exception as e:
log_error(e)
raise ValueError(e)
info("Quiz 8 done.")
custom_sleep(3)
# do_poll() answer a random thing to poll, on of daily activities
def do_poll():
info("Starting poll")
try:
answer_elem = driver.find_element(By.ID, f"btoption{choice([0, 1])}")
try:
answer_elem.click()
except exceptions.ElementNotInteractableException:
driver.execute_script("arguments[0].click();", answer_elem)
custom_sleep(uniform(2, 2.5))
except Exception as err:
log_error(err)
raise ValueError(err)
info("Poll done.")
custom_sleep(3)
# Find each playable card and tries to click on it to earn points
# todo : refactor
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)
try:
promo()
except Exception as e:
info("no promo card")
if (len(liste) < 10): # most likely an error during loading
if "suspendu" in driver.page_source:
raise Banned()
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
printf("moins de 20 cartes. Probablement pas en France.")
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 IndexError:
if i == len(liste) & i > 15:
checked = False
# input("2")
if checked:
custom_sleep(1.5)
driver.execute_script("arguments[0].scrollIntoView();", liste[i])
custom_sleep(1.5)
# input("3")
liste[i].click()
# input("4")
if len(driver.window_handles) > 1:
driver.switch_to.window(driver.window_handles[1])
# input("5")
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")):
driver.execute_script("arguments[0].scrollIntoView();", liste[i])
log_error(f"Card {i} Can't be completed. Why MS ?", driver)
liste[i].click()
driver.switch_to.window(driver.window_handles[1])
log_error(f"Cart completion - log - 2", driver)
custom_sleep(10)
log_error(f"Cart completion - log - 3 - after 10 sec", driver)
try:
try_play(driver.title) # go back to the main page
try:
close_tab(driver.window_handles[1])
except:
pass
except:
driver.get("https://rewards.bing.com")
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():
for i in range(5):
elm = driver.find_element(By.ID, "promo-item")
wait_until_visible(By.ID, "promo-item", 5, driver)
if not elm:
break
if i > 3:
log_error("plus de 3 promo cards, probablement une pa skipable", driver)
try:
driver.find_element(By.CSS_SELECTOR,
'i[class="mee-icon pull-left icon mee-icon-Cancel ng-scope"]').click()
except Exception as e:
log_error(f"can't click to close : {e}")
return ()
try:
elm.click()
except Exception as e:
# log_error(e, driver)
driver.execute_script("arguments[0].click();", elm)
# log_error(e, driver)
printf(f"that should't be there (promo), but the workarround seemed to work {e}")
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:
warning("no new windows")
driver.get("https://rewards.bing.com")
driver.refresh()
custom_sleep(3)
# Find out which type of action to do
def try_play(nom="inconnu"):
rgpd_popup(driver)
def play(number):
if number in [8, 9]:
try:
debug(f"Quiz 8 detected on `{nom}`.")
play_quiz8()
except Exception as err:
error(f"fail of PlayQuiz 8. Aborted {err}")
elif number in [4, 5]:
try:
debug(f"Quiz 4 detected on `{nom}`")
play_quiz4()
except Exception as err:
error(f"Fail of PlayQuiz 4. Aborted {err}.")
elif number in [2, 3]:
try:
debug(f"\033[96mQuiz 2 detected on `{nom}`\033[0m")
play_quiz2()
except Exception as err:
error(f"fail of PlayQuiz 2. Aborted {err}")
else:
error("`rqAnswerOption` present in page but no action to do.")
custom_sleep(uniform(3, 5))
if "pas connecté à Microsoft Rewards" in driver.page_source:
custom_sleep(5)
driver.find_element(By.CSS_SELECTOR, '[onclick="setsrchusr()"]').click()
custom_sleep(5)
rgpd_popup(driver)
custom_sleep(5)
debug("Detected and fixed connection popup")
if "bt_PollRadio" in driver.page_source:
debug("Poll detected")
do_poll()
elif "rqQuestionState" in driver.page_source:
number = driver.page_source.count("rqAnswerOption")
warning(f"recovery détecté. quiz : {number}")
play(number - 1)
elif search("([0-9]) de ([0-9]) finalisée", driver.page_source):
info("On fidelity page.")
fidelity()
elif 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:
printf(f"Nothing to do on 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 login_part_1(ldriver, cred: UserCredentials):
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, cred.get_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, cred.get_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)
# Accept all cookies question, and check if the account is locked
def login_part_2(ldriver):
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")
for elm_id in ["iNext", "KmsiCheckboxField", "id__0", "iLooksGood", "idSIButton9", "iCancel"]:
if get_domain(ldriver) == "account.microsoft.com":
break
try:
ldriver.find_element(By.ID, elm_id).click()
except Exception as e:
pass
wait_until_visible(By.CSS_SELECTOR, '[data-bi-id="sh-sharedshell-home"]', 20, ldriver)
# login() tries to login to your Microsoft account.
# it uses global variable g._mail and g._password to login
def login(ldriver, cred: UserCredentials):
try:
login_part_1(ldriver, cred)
login_part_2(ldriver)
ldriver.get("https://rewards.bing.com/")
except Banned:
raise Banned()
except Identity:
raise Banned()
except Exception as e:
critical("Error not caught during login.")
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}")
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:
error(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:
error(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)}") # what is this message ??? todo
# Sends points to database, discord and whatever service you want
# todo: refactor
def log_points(account="unknown"):
def get_points():
driver.get("https://rewards.bing.com")
custom_sleep(1)
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:
log_error(
f"Dev error, checking why it doesn't work (waited a bit, is this still white ?) {format_error(e)}",
driver)
error("Can't get points.")
return -1
return point
points = get_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")
try:
add_to_database(account_name, points)
except Exception as e:
log_error(e)
# todo: refactor and check if it works at all
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])&nbsp;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")
try:
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()
except Exception as e:
printf(f"erreur lors du click de pause: probablement pas de cartes {e}")
return ("no cards")
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:
pass
except Exception as err:
log_error(err, mobile_driver)
# todo: be coherent with pc search regarding error management
def bing_mobile_search(cred: UserCredentials, override=randint(22, 25)):
global mobile_driver
mobile_driver = create_driver(mobile=True)
try:
login(mobile_driver, cred)
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 err:
error(err)
mobile_driver.refresh()
custom_sleep(30)
i -= 1
mobile_driver.quit()
except Exception as err:
log_error(err, mobile_driver)
mobile_driver.quit()
def daily_routine(cred: UserCredentials, custom=False):
try:
if not custom: # custom already is logged in
login(driver, cred)
except Banned:
log_error("This account is locked.", driver)
return
except Identity:
log_error("This account has an issue.", driver)
return
try:
all_cards()
except Banned:
log_error("banned", driver)
raise Banned
except Exception as err:
log_error(err)
try:
fidelity()
except Exception as err:
log_error(err)
try:
bing_pc_search()
except Exception as err:
log_error(err)
try:
bing_mobile_search(cred)
except Exception as err:
log_error(err)
try:
log_points(cred.get_mail())
except Exception as err:
log_error(err)
def json_start(json_entry, cred: UserCredentials):
global driver
display = SmartDisplay(backend="xvnc", size=(1920, 1080), rfbport=2345, color_depth=24)
display.start()
for i in range(5):
start = False
for j in ["unban", "tout", "pc", "mobile", "daily"]:
try:
if str(i) in json_entry[j]:
start = True
info(f"{cred.get_mail()} : {j}")
except KeyError:
pass
if start:
driver = create_driver()
try:
if str(i) in json_entry["unban"]:
login_part_1(driver, cred)
print("\nGO TO example.com TO PROCEED or wait 1200 secs.")
for _ in range(1200):
sleep(1)
if driver.current_url == "https://example.com/":
print("proceeding")
break
else:
login(driver, cred)
except KeyError:
login(driver, cred)
try:
if str(i) in json_entry["tout"]:
daily_routine(cred)
except KeyError:
pass
# print("none is set to \"tout\"")
else:
try:
if str(i) in json_entry["daily"]:
try:
all_cards()
except Exception as e:
log_error(e)
except KeyError:
pass
# print("none is set to \"daily\"")
try:
if str(i) in json_entry["pc"]:
try:
bing_pc_search()
except Exception as e:
log_error(e)
except KeyError:
pass
try:
if str(i) in json_entry["mobile"]:
try:
bing_mobile_search(cred)
except Exception as e:
log_error(e)
except KeyError:
pass
try:
log_points(g._mail)
except Exception as e:
printf(f"CustomStart {e}")
driver.close()
cred.next_account()
display.stop()
c = Config(args)
if g.json_start:
dict_data = json.loads(g.json_start.replace("'", "\""))
json_start(dict_data, c.UserCredentials)
else:
if g.vnc_enabled or g.dev:
display = SmartDisplay(backend="xvnc", size=(1920, 1080), rfbport=g.vnc_port, color_depth=24)
else:
display = SmartDisplay(size=(1920, 1080))
display.start()
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")
while c.UserCredentials.is_valid():
custom_sleep(1)
info("Starting driver.")
driver = create_driver()
info("Driver started.")
driver.implicitly_wait(3)
try:
wait_time = uniform(1200, 3600)
info(f"Waiting for {round(wait_time / 60)}min before starting")
daily_routine(c.UserCredentials)
driver.quit()
custom_sleep(wait_time)
except KeyboardInterrupt:
critical("Canceled by user. Closing driver and display.")
driver.quit()
display.stop()
break
except Exception as e:
log_error(f"Error not caught. Skipping this account. " + format_error(e), driver)
critical(f"Error not caught. Skipping this account. {e}")
driver.quit()
c.UserCredentials.next_account()
display.stop()

1
build.sh Executable file
View File

@ -0,0 +1 @@
sudo docker build --no-cache --network host -t msrewards . && sudo docker run -d --restart unless-stopped -p 1234:1234 -ti --shm-size=2gb --name MsRewards msrewards

193
config/Stats-dashbord.json Normal file
View File

@ -0,0 +1,193 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": false,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 1,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "frser-sqlite-datasource",
"uid": "bed26262-6b98-4dfc-a95d-f8bd39b5d09c"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"max": 10000,
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "dark-red",
"value": null
},
{
"color": "dark-yellow",
"value": 2000
},
{
"color": "green",
"value": 7500
},
{
"color": "blue",
"value": 10000
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 11,
"w": 24,
"x": 0,
"y": 0
},
"id": 3,
"options": {
"displayMode": "gradient",
"minVizHeight": 10,
"minVizWidth": 0,
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showUnfilled": true,
"valueMode": "color"
},
"pluginVersion": "10.0.3",
"targets": [
{
"datasource": {
"type": "frser-sqlite-datasource",
"uid": "bed26262-6b98-4dfc-a95d-f8bd39b5d09c"
},
"queryText": "SELECT\n unixepoch() as time,\n c.compte,\n c.last_pts as \"\"\nFROM \n comptes c;\n",
"queryType": "time series",
"rawQueryText": "SELECT\n unixepoch() as time,\n c.compte,\n c.last_pts as \"\"\nFROM \n comptes c;\n",
"refId": "A",
"timeColumns": [
"time",
"ts"
]
}
],
"title": "Account points",
"type": "bargauge"
},
{
"datasource": {
"type": "frser-sqlite-datasource",
"uid": "bed26262-6b98-4dfc-a95d-f8bd39b5d09c"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"max": 300,
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "dark-red",
"value": null
},
{
"color": "dark-green",
"value": 200
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 11,
"w": 24,
"x": 0,
"y": 11
},
"id": 4,
"options": {
"displayMode": "gradient",
"minVizHeight": 10,
"minVizWidth": 0,
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showUnfilled": true,
"valueMode": "color"
},
"pluginVersion": "10.0.3",
"targets": [
{
"datasource": {
"type": "frser-sqlite-datasource",
"uid": "bed26262-6b98-4dfc-a95d-f8bd39b5d09c"
},
"queryText": "SELECT\n unixepoch() as time,\n c.compte as metric,\n c.last_pts - d2.points as ''\nFROM \n comptes c \nLEFT OUTER JOIN \n daily d1 \nON \n c.compte = d1.compte \nAND \n d1.date = date() \n LEFT OUTER JOIN daily d2 ON c.compte = d2.compte AND d2.date = DATE('now','-1 day')\nORDER BY d1.points DESC",
"queryType": "time series",
"rawQueryText": "SELECT\n unixepoch() as time,\n c.compte as metric,\n c.last_pts - d2.points as ''\nFROM \n comptes c \nLEFT OUTER JOIN \n daily d1 \nON \n c.compte = d1.compte \nAND \n d1.date = date() \n LEFT OUTER JOIN daily d2 ON c.compte = d2.compte AND d2.date = DATE('now','-1 day')\nORDER BY d1.points DESC",
"refId": "A",
"timeColumns": [
"time",
"ts"
]
}
],
"title": "Daily progress",
"type": "bargauge"
}
],
"refresh": "",
"schemaVersion": 38,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Stats",
"uid": "e70d4980-36d1-4107-90b0-d9164ae8ead4",
"version": 14,
"weekStart": ""
}

66
config/config.sh Normal file
View File

@ -0,0 +1,66 @@
printf "\nsetting up NGINX\n"
rm /etc/nginx/sites-available/default
echo "
map \$http_upgrade \$connection_upgrade {
default upgrade;
'' close;
}
upstream grafana {
server localhost:3000;
}
server {
listen 1234;
server_name localhost;
location /grafana {
proxy_pass http://localhost:3000;
rewrite ^/grafana/(.*) /\$1 break;
proxy_set_header Host \$host;
}
location /grafana/api/live/ {
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection \$connection_upgrade;
proxy_set_header Host \$http_host;
proxy_pass http://grafana;
rewrite ^/grafana/(.*) /\$1 break;
}
location /novnc/ {
proxy_pass http://127.0.0.1:6080/;
}
location /novnc/websockify {
proxy_pass http://127.0.0.1:6080/;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host \$host;
}
location / {
proxy_set_header X-Forwarded-For \$remote_addr;
proxy_set_header Host \$http_host;
proxy_pass "http://127.0.0.1:6666";
chunked_transfer_encoding off;
proxy_buffering off;
}
}
" >> /etc/nginx/sites-available/default
printf "\nNGINX configuration successfull\n"
printf "\ncreating sqlite databases\n"
sqlite3 /app/MsRewards-Reborn/MsRewards.db "CREATE TABLE daily (id INTEGER PRIMARY KEY,compte TEXT,points int,date TEXT);"
sqlite3 /app/MsRewards-Reborn/MsRewards.db "CREATE TABLE comptes (id INTEGER PRIMARY KEY,compte TEXT,last_pts int, banned int);"
printf "\nconfigurating grafana\n"
cp /app/MsRewards-Reborn/config/grafana.ini /etc/grafana/
grafana-cli plugins install frser-sqlite-datasource
printf "setting up default dashboard"
cp /app/MsRewards-Reborn/config/Stats-dashbord.json /usr/share/grafana/public/dashboards/home.json

1469
config/grafana.ini Executable file

File diff suppressed because it is too large Load Diff

19
config/request.sh Normal file
View File

@ -0,0 +1,19 @@
sleep 30
curl -X "POST" "http://localhost:1234/grafana/api/datasources" \
-H "Content-Type: application/json" \
--user admin:admin \
--data-raw $'{"id":1,"uid":"bed26262-6b98-4dfc-a95d-f8bd39b5d09c","orgId":1,"name":"SQLite","type":"frser-sqlite-datasource","typeName":"SQLite","typeLogoUrl":"public/plugins/frser-sqlite-datasource/img/logo.svg","access":"proxy","url":"","user":"","database":"","basicAuth":false,"isDefault":true,"jsonData":{"attachLimit":0,"path":"/app/MsRewards-Reborn/MsRewards.db","pathPefix":"file:"},"readOnly":false}'
curl 'http://localhost:1234/grafana/api/dashboards/import' \
-H 'content-type: application/json' \
-H 'x-grafana-org-id: 1' \
--user admin:admin \
--data-raw $'{"dashboard":{"annotations":{"list":[{"builtIn":1,"datasource":{"type":"grafana","uid":"-- Grafana --"},"enable":true,"hide":true,"iconColor":"rgba(0, 211, 255, 1)","name":"Annotations & Alerts","type":"dashboard"}]},"editable":true,"fiscalYearStartMonth":0,"graphTooltip":0,"id":null,"links":[],"liveNow":false,"panels":[{"datasource":{"type":"frser-sqlite-datasource","uid":"bed26262-6b98-4dfc-a95d-f8bd39b5d09c"},"fieldConfig":{"defaults":{"color":{"mode":"thresholds"},"mappings":[],"max":10000,"thresholds":{"mode":"absolute","steps":[{"color":"dark-red","value":null},{"color":"dark-yellow","value":2000},{"color":"green","value":7500},{"color":"blue","value":10000}]}},"overrides":[]},"gridPos":{"h":11,"w":24,"x":0,"y":0},"id":3,"options":{"displayMode":"gradient","minVizHeight":10,"minVizWidth":0,"orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showUnfilled":true,"valueMode":"color"},"pluginVersion":"10.0.3","targets":[{"datasource":{"type":"frser-sqlite-datasource","uid":"bed26262-6b98-4dfc-a95d-f8bd39b5d09c"},"queryText":"SELECT\\n unixepoch() as time,\\n c.compte,\\n c.last_pts as \\"\\"\\nFROM \\n comptes c;\\n","queryType":"time series","rawQueryText":"SELECT\\n unixepoch() as time,\\n c.compte,\\n c.last_pts as \\"\\"\\nFROM \\n comptes c;\\n","refId":"A","timeColumns":["time","ts"]}],"title":"Account points","type":"bargauge"},{"datasource":{"type":"frser-sqlite-datasource","uid":"bed26262-6b98-4dfc-a95d-f8bd39b5d09c"},"fieldConfig":{"defaults":{"color":{"mode":"thresholds"},"mappings":[],"max":300,"thresholds":{"mode":"absolute","steps":[{"color":"dark-red","value":null},{"color":"dark-green","value":200}]}},"overrides":[]},"gridPos":{"h":11,"w":24,"x":0,"y":11},"id":4,"options":{"displayMode":"gradient","minVizHeight":10,"minVizWidth":0,"orientation":"auto","reduceOptions":{"calcs":["lastNotNull"],"fields":"","values":false},"showUnfilled":true,"valueMode":"color"},"pluginVersion":"10.0.3","targets":[{"datasource":{"type":"frser-sqlite-datasource","uid":"bed26262-6b98-4dfc-a95d-f8bd39b5d09c"},"queryText":"SELECT\\n unixepoch() as time,\\n c.compte as metric,\\n c.last_pts - d2.points as \'\'\\nFROM \\n comptes c \\nLEFT OUTER JOIN \\n daily d1 \\nON \\n c.compte = d1.compte \\nAND \\n d1.date = date() \\n LEFT OUTER JOIN daily d2 ON c.compte = d2.compte AND d2.date = DATE(\'now\',\'-1 day\')\\nORDER BY d1.points DESC","queryType":"time series","rawQueryText":"SELECT\\n unixepoch() as time,\\n c.compte as metric,\\n c.last_pts - d2.points as \'\'\\nFROM \\n comptes c \\nLEFT OUTER JOIN \\n daily d1 \\nON \\n c.compte = d1.compte \\nAND \\n d1.date = date() \\n LEFT OUTER JOIN daily d2 ON c.compte = d2.compte AND d2.date = DATE(\'now\',\'-1 day\')\\nORDER BY d1.points DESC","refId":"A","timeColumns":["time","ts"]}],"title":"Daily progress","type":"bargauge"}],"refresh":"","schemaVersion":38,"style":"dark","tags":[],"templating":{"list":[]},"time":{"from":"now-6h","to":"now"},"timepicker":{},"timezone":"","title":"main","uid":"stats","version":14,"weekStart":""},"overwrite":true,"inputs":[],"folderUid":""}' \
--compressed \
--insecure
websockify -D \
--web /usr/share/novnc/ \
6080 \
localhost:2345

15
modules/Classes/Config.py Normal file
View File

@ -0,0 +1,15 @@
import json
from modules.Classes.UserCredentials import UserCredentials
class Config:
def __init__(self, args):
self.args = args
self.UserCredentials = UserCredentials()
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
configs = json.load(inFile)
for i in configs[str(args.config)]["accounts"]:
d = configs[str(args.config)]["accounts"][i]
self.UserCredentials.add(d["mail"], d["pwd"], d["2fa"])

View File

@ -0,0 +1,39 @@
import json
from modules.Tools.logger import debug, warning
class UserCredentials:
def __init__(self):
self.data = {}
self.current = 0
self.total = 0
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}")
self.data[self.total] = {
"username": username,
"password": password,
"2fa": None if tfa == '' else tfa
}
self.total += 1
def tfa_enable(self):
return self.data[self.current]["2fa"] is not None
def get_mail(self):
return self.data[self.current]["username"]
def get_password(self):
return self.data[self.current]["password"]
def get_tfa(self):
if not self.tfa_enable():
warning("Warning: TFA is not enabled. Calling get_tfa is an expected behaviour.")
return self.data[self.current]["tfa"]
def next_account(self):
self.current += 1
debug(f"New credentials: {self.data[self.current]}")
def is_valid(self):
return self.current < self.total

47
modules/Tools/logger.py Normal file
View File

@ -0,0 +1,47 @@
import logging
# ANSI escape codes for colors
COLOR_CODES = {
'RESET': '\033[0m',
'BOLD': '\033[1m',
'RED': '\033[31m',
'GREEN': '\033[32m',
'YELLOW': '\033[33m',
'BLUE': '\033[34m',
}
# Define colors for each log level
LOG_COLORS = {
'DEBUG': COLOR_CODES['BLUE'],
'INFO': COLOR_CODES['GREEN'],
'WARNING': COLOR_CODES['YELLOW'],
'ERROR': COLOR_CODES['RED'],
'CRITICAL': COLOR_CODES['BOLD'] + COLOR_CODES['RED'],
}
# Create a formatter with colors
class ColoredFormatter(logging.Formatter):
def format(self, record):
log_level = record.levelname
record.levelname = f"{LOG_COLORS.get(log_level, '')}{record.levelname}{COLOR_CODES['RESET']}"
return super().format(record)
# Set up the root logger
root_logger = logging.getLogger()
root_logger.setLevel(logging.DEBUG)
# Create a console handler and set the formatter
ch = logging.StreamHandler()
ch.setFormatter(ColoredFormatter('%(levelname)s: %(message)s'))
# Add the console handler to the root logger
root_logger.addHandler(ch)
# Define log level functions
debug = root_logger.debug
info = root_logger.info
warning = root_logger.warning
error = root_logger.error
critical = root_logger.critical

34
modules/cards.py Normal file
View 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])

107
modules/config.py Normal file
View File

@ -0,0 +1,107 @@
#!/usr/bin/python3.10
from modules.driver_tools import *
from modules.imports import *
import modules.globals as g
import json
class FakeWebHook:
def send(self, text="", username='', avatar_url='', embed="", file=""):
print(text)
parser = argparse.ArgumentParser()
parser.add_argument(
"-c",
"--config",
help="Choose a specific config file",
default=""
)
parser.add_argument(
"-v",
"--vnc",
help="enable VNC",
dest="vnc"
)
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(
"--json",
help="input json to start the bot with custom parameters",
default=""
)
with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile:
discord = json.load(inFile)
with open("/app/MsRewards-Reborn/user_data/settings.json", "r") as inFile:
settings = json.load(inFile)
with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile:
proxy = json.load(inFile)
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
config = json.load(inFile)
args = parser.parse_args()
g.json_start = args.json
g.vnc_enabled = args.vnc != "None"
g.vnc_port = args.vnc
g.update_version = args.update_version
# global variables used later in the code
g.start_time = time()
# path configurations
g.mot_path = "/usr/share/dict/french"
g.credential_path = "/app/MsRewards-Reborn/user_data/login.csv"
discord_conf = config[args.config]["discord"]
# discord configuration
g.discord_success_link = discord[discord_conf]["successL"]
g.discord_error_link = discord[discord_conf]["errorsL"]
g.discord_enabled_error = discord[discord_conf]["errorsT"] == "True"
g.discord_enabled_success = discord[discord_conf]["successT"] == "True"
g.avatar_url = settings["avatarlink"]
if not g.json_start:
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())
else:
webhookFailure = FakeWebHook()
webhookSuccess = FakeWebHook()
# base settings
g.discord_embed = False # send new point value in an embed, fixed for now
g.headless = False
# proxy settings
g.proxy_enabled = config[args.config]["proxy"] != "-1"
if g.proxy_enabled:
g.proxy_address = proxy[config[args.config]["proxy"]]["address"]
g.proxy_port = proxy[config[args.config]["proxy"]]["port"]
# list of words
with open(g.mot_path, "r", encoding="utf-8") as h:
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]
if g.proxy_enabled:
setup_proxy(g.proxy_address, g.proxy_port)

67
modules/db.py Normal file
View File

@ -0,0 +1,67 @@
import sqlite3
#Create a new row, for the account [compte] whith [points] points
def add_row(compte, points, mycursor, mydb):
sql = "INSERT INTO daily (compte, points, date) VALUES (?, ?, date())"
val = (compte, points)
mycursor.execute(sql, val)
mydb.commit()
#printf(mycursor.rowcount, "record created.")
#update the ammount of points for the account [compte]
def update_row(compte, points, mycursor, mydb):
sql = f"UPDATE daily SET points = {points} WHERE compte = '{compte}' AND date = date() ;"
mycursor.execute(sql)
mydb.commit()
#printf(mycursor.rowcount, "record(s) updated")
# update the value of last_pts for the table comptes
def update_last(compte, points, mycursor, mydb):
sql1 = f"UPDATE comptes SET last_pts = {points} WHERE compte = '{compte}';"
sql2 = f"select * from comptes where compte = '{compte}'"
sql3 = f"INSERT INTO comptes (compte, last_pts,banned) VALUES ('{compte}', {points}, 0)"
cmd = mycursor.execute(sql2)
if len(list(cmd)) == 0:
mycursor.execute(sql3)
else :
mycursor.execute(sql1)
mydb.commit()
#printf(mycursor.rowcount, "record(s) updated")
# if return if there already is a line in the database for the account [compte]. if same_point is enabled, the line must also have the same number of points
# SQLITE
def get_row(compte, points, mycursor, same_points = True):
if same_points :
mycursor.execute(f"SELECT * FROM daily WHERE points = {points} AND compte = '{compte}' AND date = date() ;")
else :
mycursor.execute(f"SELECT * FROM daily WHERE compte = '{compte}' AND date = date() ;")
myresult = mycursor.fetchall()
return(len(myresult) == 1)
def add_to_database(compte, points, save_if_fail=True):
if points is None:
pass
else:
mydb = sqlite3.connect("/app/MsRewards-Reborn/MsRewards.db")
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()

96
modules/driver_tools.py Normal file
View File

@ -0,0 +1,96 @@
from modules.imports import *
from modules.config import *
from modules.tools import *
import modules.globals as g
def set_language(ldriver):
ldriver.get("chrome://settings/languages")
action = ActionChains(ldriver)
action.reset_actions()
# select language
x_coord = 1200
y_coord = 150
action.move_by_offset(x_coord, y_coord).click().perform()
sleep(0.5)
# scroll down
action.reset_actions()
elm = ldriver.find_element(By.XPATH, "/html/body")
ActionChains(ldriver)\
.send_keys("french")\
.pause(0.5)\
.send_keys(Keys.TAB + Keys.TAB + Keys.ENTER + Keys.TAB + Keys.TAB + Keys.ENTER)\
.perform()
x_coord = 1163
y_coord = 717
action.move_by_offset(x_coord, y_coord).click().perform()
# put to the top
sleep(0.5)
action.reset_actions()
x_coord = 1257
y_coord = 328
action.move_by_offset(x_coord, y_coord).click().perform()
action.click().perform()
def setup_proxy(ip: str, port: str) -> None:
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) -> None:
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) -> None:
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: str) -> None:
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 {identifier} not found after {timeout}s")
return(False)

8
modules/error.py Normal file
View File

@ -0,0 +1,8 @@
class Banned(Exception):
pass
class NotBanned(Exception):
pass
class Identity(Exception):
pass

30
modules/globals.py Normal file
View File

@ -0,0 +1,30 @@
driver = None
display = None
log = False
full_log = False
vnc_enabled = False
vnc_port = 2345
points_file = "/"
update_version = False
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"
norvege = False
database_error_override = False
json_start = ""

36
modules/imports.py Normal file
View File

@ -0,0 +1,36 @@
import argparse
import asyncio
import json
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 import ActionChains
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
import undetected_chromedriver as uc

82
modules/tools.py Normal file
View 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])

17
requirements.txt Normal file
View File

@ -0,0 +1,17 @@
argparse
discord.py==1.7.3
selenium
pillow
pyvirtualdisplay
undetected_chromedriver
requests
flask
flask_sse
EasyProcess
pyotp
packaging
apscheduler
flask_login
gunicorn
gevent
redis

3
sse.sh Normal file
View File

@ -0,0 +1,3 @@
while IFS= read -r newline; do
redis-cli publish console "$newline"
done < <(tail -f /app/MsRewards-Reborn/Flask/static/logs/custom.txt)

4
user_data/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

1
user_data/configs.json Normal file
View File

@ -0,0 +1 @@
{}

4
user_data/cookies/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

1
user_data/discord.json Normal file
View File

@ -0,0 +1 @@
{}

4
user_data/flask.json Normal file
View File

@ -0,0 +1,4 @@
{
"password": "ChangeMe",
"secret": ""
}

345731
user_data/french Normal file

File diff suppressed because it is too large Load Diff

1
user_data/logs/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*

View File

1
user_data/proxy.json Normal file
View File

@ -0,0 +1 @@
{}

1
user_data/settings.json Normal file
View File

@ -0,0 +1 @@
{"avatarlink":"https://cdn.discordapp.com/icons/793934298977009674/d8055bccef6eca4855c349e808d0d788.webp"}

1
version Normal file
View File

@ -0,0 +1 @@
v6.8.0