Compare commits

..

No commits in common. "d3137f858ab7bf8ccf225355644e53357695053c" and "42c327b46830424043252cafb4174dda3f590d29" have entirely different histories.

74 changed files with 1080 additions and 5225 deletions

View File

@ -1,32 +1,8 @@
if you have a rare bug, i'll not fix it. If you have a reccurent bug, send :
| python version |
| --- |
| page.html |
screenshot of error
--- ---
name: Bug report and i may try to fix it.
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

@ -1,20 +0,0 @@
---
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.

19
.gitignore vendored
View File

@ -1,20 +1,9 @@
dev_build.sh /old
geckodriver.log geckodriver.log
.vscode/ config
.idea .vscode/settings.json
venv update.sh
/Git /Git
page.html page.html
screenshot.png screenshot.png
login.csv login.csv
data
**/__pycache__
user_data/*
install.sh
nohup.out
points.csv
file.png
user_data/configs.json
*.ts
LICENSE
README.md

View File

@ -1,22 +0,0 @@
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

View File

@ -1,442 +0,0 @@
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'])

View File

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

View File

@ -1,405 +0,0 @@
@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;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,102 +0,0 @@
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";
}

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

@ -1,79 +0,0 @@
<!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

@ -1,22 +0,0 @@
{% 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 %}

View File

@ -1,106 +0,0 @@
{% 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 %}

View File

@ -1,31 +0,0 @@
{% 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

@ -1,56 +0,0 @@
{% 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

@ -1,23 +0,0 @@
{% 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 %}

View File

@ -1,31 +0,0 @@
{% 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">
<script type="text/javascript">
const subdoc = document.getElementsByTagName("iframe")[0].contentWindow.document;
var script = document.createElement('script');
script.onload = function () {
const ansiOutput = subdoc.getElementsByTagName("body")[0].getElementsByTagName("pre")[0]
const ansi_up = new AnsiUp();
ansiOutput.innerHTML = ansi_up.ansi_to_html(ansiOutput.innerText)
};
script.src = "https://cdn.jsdelivr.net/npm/ansi_up@4.0.4/ansi_up.js";
document.head.appendChild(script);
</script>
{% endif %}
{% endblock %}

View File

@ -1,53 +0,0 @@
{% 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

@ -1,35 +0,0 @@
{% 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

@ -1,49 +0,0 @@
{% 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

@ -1,12 +0,0 @@
{% 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

@ -1,45 +0,0 @@
{% 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

@ -1,48 +0,0 @@
{% 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 %}
<link rel="stylesheet" href="{{ url_for('static', filename='node_modules/xterm/css/xterm.css') }}"/>
<script src="{{ url_for('static', filename='node_modules/xterm/lib/xterm.js') }}"></script>
<script src="{{ url_for('static', filename='node_modules/xterm-addon-fit/lib/xterm-addon-fit.js') }}"></script>
<script>
document.getElementsByClassName("content")[0].style.padding = "0 0"
</script>
<div style="height: 100%">
<div id="console" style="height: 100%; width: 20%; float:left;"></div>
<div style="height: 100%; width: 80%; float:left;">
<iframe src="/novnc/vnc.html?resize=scale&path=novnc/websockify&autoconnect=true&view_only"
width="100%" height="100%" frameborder="0"></iframe>
</div>
</div>
<script>
var term = new Terminal();
const fitAddon = new FitAddon.FitAddon();
term.loadAddon(fitAddon);
term.open(document.getElementById('console'));
fitAddon.fit()
addEventListener("transitionend", (event) => {
fitAddon.fit()
});
document.getElementById("console").style.textAlign = "left"
const eventSource = new EventSource('/stream');
eventSource.onmessage = (event) => {
term.writeln(event.data)
};
addEventListener("resize", (event) => {
fitAddon.fit()
});
</script>
{% endif %}
{% endblock %}

18
README.md Normal file
View File

@ -0,0 +1,18 @@
# MsReward
A Microsoft reward automator, designed to work headless on a raspberry pi. Tested with a pi 3b+ and a pi 4 2Gb .
Using a discord webhook to log everything.
Using Selenium and geckodriver.
You have to fill the config file.
you have to put your credentials in the same folder as the script, in a `.csv` file. You have to put login and password this way :
```
email1,password1
email2,password2
```
You have to put a list with a dictionnary in the same folder as the script, in a `.txt` file. It should have a lot of words, used to make random search on bing, as shown in the example file.
Tou can add a link to a website where content is only the link of the monthly fidelity card
You should limit to 6 account per IP.
![image](https://user-images.githubusercontent.com/74496300/155960737-061229ca-db8c-4e66-9aef-542d9e709bb2.png)

1021
V4.py Executable file

File diff suppressed because it is too large Load Diff

783
V6.py
View File

@ -1,783 +0,0 @@
#!/usr/bin/python3.10
from modules.Classes.Config import Config
from modules.Classes.DiscordLogger import DiscordLogger
from modules.Classes.UserCredentials import UserCredentials
from modules.Tools.logger import warning, critical
from modules.cards import *
from modules.config import *
from modules.db import add_to_database
from modules.driver_tools import *
from modules.error import *
# 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 config.proxy.is_enabled():
chrome_options.add_argument(f'--proxy-server={config.proxy.ip}:{config.proxy.port}')
driver = uc.Chrome(options=chrome_options)
set_language(driver)
return driver
# 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 = config.WebDriver.driver
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.")
driver = config.WebDriver.driver
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():
driver = config.WebDriver.driver
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(8):
try:
element = driver.find_element(By.ID, f"rqAnswerOption{i}")
if 'iscorrectoption="True"' in element.get_attribute("outerHTML"):
correct_answers.append(f'rqAnswerOption{i}')
except Exception as e:
warning(f"can't find rqAnswerOption{i}. 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(config)
correct_answers.append(answer_id)
except Exception as e:
log_error(f"{format_error(e)} \n Good answers : {' '.join(correct_answers)}")
raise ValueError(format_error(e))
info("Quiz 8 done.")
custom_sleep(3)
def play_quiz4(override: int = None):
info(f"Starting Quiz 4")
driver = config.WebDriver.driver
if not override:
try: # fidelity quiz are much longer than usual ones
override = int(findall('rqQuestionState([\d]{1,2})"', driver.page_source)[-1])
except Exception as err:
debug(err)
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 4 done.")
custom_sleep(3)
# do_poll() answer a random thing to poll, on of daily activities
def do_poll():
info("Starting poll")
driver = config.WebDriver.driver
try:
answer_elem = driver.find_element(By.ID, f"btoption{choice([0, 1])}")
try:
answer_elem.click()
except exceptions.ElementNotInteractableException:
warning("element not clickable. Waiting a bit and retrying.")
custom_sleep(uniform(2, 2.5))
driver.execute_script("arguments[0].click();", answer_elem)
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
def all_cards():
driver = config.WebDriver.driver
driver.get("https://rewards.bing.com")
wait_until_visible(By.CLASS_NAME, "c-card-content", 10, driver)
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content")
custom_sleep(2)
try:
promo()
except Exception as e:
debug(e)
info("no promo card")
if len(card_list) < 10: # most likely an error during loading
if "suspendu" in driver.page_source:
raise Banned()
driver.refresh()
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content")
if len(card_list) < 10:
log_error("Less than 10 cards. Most likely an error with login.")
return "Not enough cards"
for i in range(len(card_list)):
debug(f"carte {i}")
checked = False
try:
checked = "mee-icon-AddMedium" in card_list[i].get_attribute("innerHTML")
except StaleElementReferenceException:
driver.refresh()
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content")
warning(f"staled, {len(card_list)}")
checked = "mee-icon-AddMedium" in card_list[i].get_attribute("innerHTML")
except IndexError:
driver.get("https://rewards.bing.com")
custom_sleep(10)
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content")
try:
checked = "mee-icon-AddMedium" in card_list[i].get_attribute("innerHTML")
except IndexError:
if i == len(card_list) and i > 15:
checked = False
if checked:
custom_sleep(1.5)
driver.execute_script("arguments[0].scrollIntoView();", card_list[i])
custom_sleep(1.5)
card_list[i].click()
if len(driver.window_handles) > 1:
driver.switch_to.window(driver.window_handles[1])
try_play(driver.title)
close_tab(driver.window_handles[1])
try:
driver.refresh()
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content")
if "mee-icon-AddMedium" in card_list[i].get_attribute("innerHTML"):
driver.execute_script("arguments[0].scrollIntoView();", card_list[i])
card_list[i].click()
driver.switch_to.window(driver.window_handles[1])
custom_sleep(10)
log_error(f"Card {i} Can't be completed. Why MS ?")
try:
try_play(driver.title) # go back to the main page
try:
close_tab(driver.window_handles[1])
except Exception as e:
debug(e)
except Exception as e:
debug(e)
driver.get("https://rewards.bing.com")
except Exception as err:
log_error(err)
custom_sleep(3)
def promo():
driver = config.WebDriver.driver
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("There is more than 3 promo cards, most likely an unskippable one.")
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:
driver.execute_script("arguments[0].click();", elm)
warning(f"that shouldn't be there (promo), but the workaround 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 Exception as e:
warning(f"no new windows {format_error(e)}")
driver.get("https://rewards.bing.com")
driver.refresh()
custom_sleep(3)
# Find out which type of action to do
def try_play(nom="unknown"):
driver = config.WebDriver.driver
rgpd_popup(config)
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(config)
custom_sleep(5)
debug("Detected and fixed connection popup")
if "bt_PollRadio" in driver.page_source:
debug("Poll detected")
do_poll()
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)
elif "rqQuestionState" in driver.page_source:
number = driver.page_source.count("rqAnswerOption")
warning(f"recovery detected. quiz : {number}")
play(number - 1)
else:
info(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():
info("Starting part 1 of login")
driver = config.WebDriver.driver
driver.get("https://login.live.com")
wait_until_visible(By.ID, "i0116", browser=driver)
mail_elem = driver.find_element(By.ID, "i0116")
send_keys_wait(mail_elem, config.UserCredentials.get_mail())
mail_elem.send_keys(Keys.ENTER)
wait_until_visible(By.ID, "i0118", browser=driver)
pwd_elem = driver.find_element(By.ID, "i0118")
send_keys_wait(pwd_elem, config.UserCredentials.get_password())
pwd_elem.send_keys(Keys.ENTER)
custom_sleep(2)
# 2FA
if "Entrez le code de sécurité" in driver.page_source:
try:
a2f_elem = driver.find_element(By.ID, "idTxtBx_SAOTCC_OTC")
a2f_elem.send_keys(config.UserCredentials.get_tfa().now())
a2f_elem.send_keys(Keys.ENTER)
except Exception as err:
log_error(err)
# Accept all cookies question, and check if the account is locked
def login_part_2():
driver = config.WebDriver.driver
custom_sleep(5)
if 'Abuse' in driver.current_url:
raise Banned()
if 'identity' in driver.current_url:
raise Identity()
if 'notice' in driver.current_url:
driver.find_element(By.ID, "id__0").click()
if "proof" in driver.current_url:
driver.find_element(By.ID, "iLooksGood")
for elm_id in ["checkboxField", "KmsiCheckboxField", "acceptButton", "iNext", "id__0", "iLooksGood", "idSIButton9",
"iCancel"]:
if get_domain(driver) == "account.microsoft.com":
break
try:
driver.find_element(By.ID, elm_id).click()
except Exception as e:
debug(e)
wait_until_visible(By.CSS_SELECTOR, '[data-bi-id="sh-sharedshell-home"]', 20, driver)
# login() tries to login to your Microsoft account.
# it uses global variable g._mail and g._password to login
def login():
driver = config.WebDriver.driver
try:
login_part_1()
login_part_2()
driver.get("https://rewards.bing.com/")
except Banned:
raise Banned()
except Identity:
raise Banned()
except Exception as err:
critical("Error not caught during login." + format_error(err))
log_error(err)
driver.quit()
return False
# Makes 30 search as PC Edge
def bing_pc_search(override=randint(35, 40)):
driver = config.WebDriver.driver
driver.get(f"https://www.bing.com/search?q={config.wordlist.get_word().replace(' ', '+')}")
custom_sleep(uniform(1, 2))
rgpd_popup(config)
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 = config.wordlist.get_word()
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 current account's points to database
def log_points():
driver = config.WebDriver.driver
account = config.UserCredentials.get_mail()
driver.get("https://rewards.bing.com")
custom_sleep(1)
wait_until_visible(By.CSS_SELECTOR, 'span[mee-element-ready="$ctrl.loadCounterAnimation()"]', browser=driver)
try:
points = search('availablePoints\":([\d]+)', driver.page_source)[1]
except Exception as err:
log_error(
f"Dev error, checking why it doesn't work (waited a bit, is this still white ?) {format_error(err)}")
error(f"Can't get points. {format_error(err)}")
return -1
custom_sleep(uniform(3, 20))
account_name = account.split("@")[0]
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():
driver = config.WebDriver.driver
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]
button_card = driver.find_element(By.XPATH, f'//span[text()="{button_text}"]')
button_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"Fidelity: Multiples error - 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 err:
error(err)
info("fidelity - done")
except Exception as err:
log_error(err)
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"]')
pause.click()
except Exception as e:
error(f"Error while clicking pause. Probably no cards. {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()
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():
driver = config.WebDriver.driver
try:
alert = driver.switch_to.alert
alert.dismiss()
except exceptions.NoAlertPresentException:
pass
except Exception as err:
log_error(err)
# todo: be coherent with pc search regarding error management
def bing_mobile_search(cred: UserCredentials, override=randint(22, 25)):
config.WebDriver.set_mobile_driver(create_driver(mobile=True))
config.WebDriver.switch_to_driver("Mobile")
driver = config.WebDriver.driver
try:
login()
mot = config.wordlist.get_word().replace(" ", "+")
driver.get(f"https://www.bing.com/search?q={mot}")
custom_sleep(uniform(1, 2))
rgpd_popup(config)
custom_sleep(uniform(1, 1.5))
for i in range(override): # 20
try:
mot = config.wordlist.get_word()
send_keys_wait(driver.find_element(By.ID, "sb_form_q"), mot)
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)
driver.find_element(By.ID, "sb_form_q").clear()
except Exception as err:
error(err)
driver.refresh()
custom_sleep(30)
i -= 1
driver.quit()
except Exception as err:
log_error(err)
driver.quit()
finally:
config.WebDriver.switch_to_driver("PC")
def daily_routine(cred: UserCredentials, custom=False):
try:
if not custom: # custom already is logged in
login()
except Banned:
log_error("This account is locked.")
raise Banned()
except Identity:
log_error("This account has an issue.")
return
try:
all_cards()
except Banned:
log_error("banned")
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()
except Exception as err:
log_error(err)
def json_start(json_entry, cred: UserCredentials):
json_entry = json.loads(json_entry)
config.set_display(SmartDisplay(backend="xvnc", size=(1920, 1080), rfbport=2345, color_depth=24))
config.display.start()
account_id = 0
while config.UserCredentials.is_valid():
start = False
for action in ["unban", "tout", "pc", "mobile", "daily"]:
try:
if str(account_id) in json_entry[action]:
start = True
info(f"{cred.get_mail()} : {action}")
except KeyError:
pass
if start:
config.WebDriver.set_pc_driver(create_driver())
config.WebDriver.switch_to_driver("PC")
driver = config.WebDriver.driver
try:
if str(account_id) in json_entry["unban"]:
login_part_1()
info("\nGO TO example.com TO PROCEED or wait 1200 secs.")
for _ in range(1200):
sleep(1)
if driver.current_url == "https://example.com/":
info("proceeding")
break
else:
login()
except KeyError:
login()
try:
if str(account_id) in json_entry["tout"]:
daily_routine(cred, True)
except KeyError:
pass
else:
try:
if str(account_id) in json_entry["daily"]:
try:
all_cards()
except Exception as e:
log_error(e)
except KeyError:
pass
try:
if str(account_id) in json_entry["pc"]:
try:
bing_pc_search()
except Exception as e:
log_error(e)
except KeyError:
pass
try:
if str(account_id) in json_entry["mobile"]:
try:
bing_mobile_search(cred)
except Exception as e:
log_error(e)
except KeyError:
pass
try:
log_points()
except Exception as e:
error(f"CustomStart {e}")
driver.close()
cred.next_account()
account_id += 1
config.display.stop()
def default_start():
if config.vnc_enabled():
config.set_display(SmartDisplay(backend="xvnc", size=(1920, 1080), rfbport=config.vnc, color_depth=24))
else:
config.set_display(SmartDisplay(size=(1920, 1080)))
config.display.start()
while config.UserCredentials.is_valid():
custom_sleep(1)
info("Starting and configuring driver.")
config.WebDriver.set_pc_driver(create_driver())
config.WebDriver.switch_to_driver("PC")
info("Driver started.")
config.WebDriver.pc_driver.implicitly_wait(3)
try:
wait_time = uniform(1200, 3600)
wait_time = 10 # the display closes when I ctrl + c
info(f"Waiting for {round(wait_time / 60)}min before starting")
custom_sleep(wait_time)
daily_routine(config.UserCredentials)
config.WebDriver.pc_driver.quit()
except KeyboardInterrupt:
critical("Canceled by user. Closing driver and display.")
config.WebDriver.pc_driver.quit()
config.display.stop()
break
except Banned:
warning("this account is banned. Switching to next account")
except Exception as e:
log_error(f"Error not caught. Skipping this account. " + format_error(e))
critical(f"Error not caught. Skipping this account. {e}")
config.WebDriver.pc_driver.quit()
finally:
config.UserCredentials.next_account()
config.display.stop()
def log_error(msg):
DiscordLogger(config).send(msg)
def check_updated():
debug(f"updated: {config.has_been_updated()}")
if config.has_been_updated():
config.discord.wh.send(f"Updated to {config.version}", username="update",
avatar_url="https://cdn-icons-png.flaticon.com/512/1688/1688988.png")
if __name__ == "__main__":
config = Config(args)
check_updated()
match config.start:
case "json":
json_start(config.json_entry, config.UserCredentials)
case "default":
default_start()

View File

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

29
config Normal file
View File

@ -0,0 +1,29 @@
[PATH]
motpath = /your/path/to/words_list.txt
logpath = /your/path/to/loginandpass.csv
[SETTINGS]
FidelityLink = Null
embeds = False
Headless = True
[DISCORD]
successlink = https://discord.com/api/webhooks/[put your webhook here]
errorlink =https://discord.com/api/webhooks/[put your webhook here]
[PROXY]
enabled = False
url = Null
port = 0
[DATABASE]
enabled = False
host = Null
database = MsRewards
usr = root
pwd = password

View File

@ -1,193 +0,0 @@
{
"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": ""
}

View File

@ -1,66 +0,0 @@
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

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +0,0 @@
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

View File

@ -1,19 +0,0 @@
version: '3.8'
services:
msrewards:
build:
context: .
dockerfile: Dockerfile
# optional if you have Dockerfile in the same directory
# arguments:
# - ARG_NAME=value # If you have build arguments
image: msrewards
container_name: MsRewards
restart: unless-stopped
ports:
- "1234:1234"
- "2345:2345"
shm_size: 2gb
volumes:
- "./data/:/data"

1
liste.txt Normal file
View File

@ -0,0 +1 @@
you,have,to,add,a,lots,of,words,like,this,separated,with,a,coma

View File

@ -1,79 +0,0 @@
import json
from discord import Webhook, RequestsWebhookAdapter
from modules.Classes.DiscordConfig import DiscordConfig, FakeWebHook
from modules.Classes.Driver import Driver
from modules.Classes.Proxy import Proxy
from modules.Classes.UserCredentials import UserCredentials
from modules.Classes.WordList import WordList
class Config:
def __init__(self, args):
"""
open config file
"""
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)
"""
setup standalone stuff
"""
self.args = args
self.start = "json" if args.json else "default"
self.json_entry = args.json.replace("'", "\"")
self.wordlist = WordList("/usr/share/dict/french")
self.vnc = args.vnc
self.version = args.update_version
self.WebDriver = Driver()
self.display = None
"""
setup UserCredential
"""
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"])
"""
Setup discord
"""
self.discord = DiscordConfig()
self.discord.avatar_url = settings["avatarlink"]
self.discord.wh_link = discord[config[args.config]["discord"]]["errorsL"]
if self.discord.wh_link != "":
self.discord.wh = Webhook.from_url(self.discord.wh_link, adapter=RequestsWebhookAdapter())
else:
self.discord.wh = FakeWebHook()
"""
setup proxy
"""
proxy_conf = config[args.config]["proxy"]
if proxy_conf != "-1":
proxy_address = proxy[config[args.config]["proxy"]]["address"]
proxy_port = proxy[config[args.config]["proxy"]]["port"]
else:
proxy_address = ""
proxy_port = ""
self.proxy = Proxy(proxy_conf, proxy_address, proxy_port)
def vnc_enabled(self):
return self.vnc != "None"
def set_display(self, display):
self.display = display
def has_been_updated(self):
return self.version != "None"

View File

@ -1,14 +0,0 @@
from modules.Tools.logger import debug
class DiscordConfig:
def __init__(self):
self.avatar_url = ""
self.wh_link = None
self.wh = None
class FakeWebHook:
def send(self, *args):
debug(f"Used a webhook call without webhook url with {args}")

View File

@ -1,36 +0,0 @@
from discord import Embed, Colour, File
from modules.Classes.Config import Config
from modules.Tools.logger import error
from modules.Tools.tools import format_error
class DiscordLogger:
def __init__(self, config: Config):
self.config = config
def send(self, message: str):
driver = self.config.WebDriver.driver
if type(message) is not str:
message = format_error(message)
error(message)
with open("page.html", "w") as f:
try:
f.write(driver.page_source)
except Exception as e:
error(e)
f.write("the driver has closed or crashed. Can't access page content")
img = self.config.display.waitgrab()
img.save("screenshot.png")
embed = Embed(
title="An Error has occurred",
description=str(message),
colour=Colour.red(),
)
file = File("screenshot.png")
embed.set_image(url="attachment://screenshot.png")
embed.set_footer(text=self.config.UserCredentials.get_mail() + " - " + self.config.WebDriver.current_driver())
self.config.discord.wh.send(embed=embed, username="error", file=file, avatar_url=self.config.discord.avatar_url)
self.config.discord.wh.send(username="error", file=File("page.html"), avatar_url=self.config.discord.avatar_url)

View File

@ -1,25 +0,0 @@
class Driver:
def __init__(self):
self.pc_driver = None
self.mobile_driver = None
self.driver = None
def set_pc_driver(self, pc_driver):
self.pc_driver = pc_driver
def set_mobile_driver(self, mobile_driver):
self.mobile_driver = mobile_driver
def switch_to_driver(self, driver: str):
match driver:
case "pc" | "PC" | "Pc":
self.driver = self.pc_driver
case "mobile" | "Mobile":
self.driver = self.mobile_driver
case _:
raise ValueError("The driver must be either pc or mobile")
def current_driver(self):
return "PC" if self.pc_driver == self.driver else "Mobile"

View File

@ -1,9 +0,0 @@
class Proxy:
def __init__(self, enabled: str, ip: str = None, port: str = None):
self.ip = ip
self.port = port
self.enabled = enabled != "-1"
def is_enabled(self):
return self.enabled

View File

@ -1,44 +0,0 @@
from pyotp import TOTP
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 TOTP(self.data[self.current]["2fa"])
def next_account(self):
self.current += 1
if self.is_valid():
debug(f"New credentials: {self.data[self.current]}")
else:
debug("No new credentials.")
def is_valid(self):
return self.current < self.total

View File

@ -1,13 +0,0 @@
import random
class WordList:
def __init__(self, path):
with open(path, "r", encoding="utf-8") as h:
lines = h.readlines()
self.words = [x.replace('\n', "") for x in lines]
random.shuffle(self.words)
def get_word(self):
return self.words.pop(0)

View File

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

View File

@ -1,47 +0,0 @@
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(__name__)
root_logger.setLevel(logging.INFO)
# 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

View File

@ -1,36 +0,0 @@
from time import sleep
from modules.Tools.logger import info, error
# return current page domain
def get_domain(driver):
return driver.current_url.split("/")[2]
def custom_sleep(temps):
try:
if False: # todo: change this awful condition
points = ["", "", "", "", "", "", "", ""]
passe = 0
for _ 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:
info("Wait canceled.")
except Exception as err:
error(err)
def format_error(e) -> str:
tb = e.__traceback__
txt = ""
while tb is not None:
txt = txt + f" -> {tb.tb_frame.f_code.co_name} ({tb.tb_lineno}) "
tb = tb.tb_next
return txt + "\n" + str(e)

View File

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

View File

@ -1,34 +0,0 @@
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])

View File

@ -1,33 +0,0 @@
#!/usr/bin/python3.10
from modules.imports import *
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=""
)
args = parser.parse_args()

View File

@ -1,66 +0,0 @@
import sqlite3
# Create a new row, for the account [compte] whith [points] points
def add_row(account, points, mycursor, mydb):
sql = "INSERT INTO daily (compte, points, date) VALUES (?, ?, date())"
val = (account, points)
mycursor.execute(sql, val)
mydb.commit()
# printf(mycursor.rowcount, "record created.")
# update the ammount of points for the account [compte]
def update_row(account, points, mycursor, mydb):
sql = f"UPDATE daily SET points = {points} WHERE compte = '{account}' AND date = date() ;"
mycursor.execute(sql)
mydb.commit()
# update the value of last_pts for the table comptes
def update_last(account, points, mycursor, mydb):
sql1 = f"UPDATE comptes SET last_pts = {points} WHERE compte = '{account}';"
sql2 = f"select * from comptes where compte = '{account}'"
sql3 = f"INSERT INTO comptes (compte, last_pts,banned) VALUES ('{account}', {points}, 0)"
cmd = mycursor.execute(sql2)
if len(list(cmd)) == 0:
mycursor.execute(sql3)
else:
mycursor.execute(sql1)
mydb.commit()
# Return if there already is a line in the database for the account [account].
# if same_point is enabled, the line must also have the same number of points
# SQLITE
def get_row(account, points, mycursor, same_points=True):
if same_points:
mycursor.execute(f"SELECT * FROM daily WHERE points = {points} AND compte = '{account}' AND date = date() ;")
else:
mycursor.execute(f"SELECT * FROM daily WHERE compte = '{account}' AND date = date() ;")
myresult = mycursor.fetchall()
return (len(myresult) == 1)
def add_to_database(account, points):
if points is None:
pass
else:
mydb = sqlite3.connect("/app/MsRewards-Reborn/MsRewards.db")
mycursor = mydb.cursor()
if get_row(account, points, mycursor, True):
# check if the row exist with the same amount of points and do nothing if it does
pass
# check if the row exist, but without the same amount of points and update the point account then
elif get_row(account, points, mycursor, False):
update_row(account, points, mycursor, mydb)
else: # if the row don't exist, create it with the good amount of points
add_row(account, points, mycursor, mydb)
if int(points) > 10:
update_last(account, points, mycursor, mydb)
mycursor.close()
mydb.close()

View File

@ -1,72 +0,0 @@
from random import uniform
from selenium.common import TimeoutException
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from modules.Tools.logger import debug
from modules.Tools.tools import *
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()
# Deal with RGPD popup as well as some random popup like 'are you satisfied' one
def rgpd_popup(config) -> None:
for i in ["bnp_btn_accept", "bnp_hfly_cta2", "bnp_hfly_close"]:
try:
config.WebDriver.driver.find_element(By.ID, i).click()
except Exception as err:
debug(err)
"""
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: int = 20, browser=None) -> bool:
try:
WebDriverWait(browser, timeout).until(
expected_conditions.visibility_of_element_located((search_by, identifier)), "element not found")
return True
except TimeoutException as e:
error(f"element {identifier} not found after {timeout}s")
return False

View File

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

View File

@ -1,36 +0,0 @@
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

View File

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

3
sse.sh
View File

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

View File

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

View File

@ -1 +0,0 @@
{}

View File

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

View File

@ -1 +0,0 @@
{}

View File

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

View File

@ -1 +0,0 @@
*

View File

@ -1 +0,0 @@
{}

View File

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

View File

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