Compare commits

..

No commits in common. "master" and "dev" have entirely different histories.
master ... dev

26 changed files with 1683 additions and 702 deletions

View File

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

5
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,19 +6,19 @@
{% if not current_user.is_authenticated %} {% if not current_user.is_authenticated %}
<button class="unselected" onclick="location.href = '/login';">login</button> <button class="unselected" onclick="location.href = '/login';">login</button>
{% else %} {% else %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/vnc-post.css') }}"/>
<link rel="stylesheet" href="{{ url_for('static', filename='node_modules/xterm/css/xterm.css') }}"/> <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/lib/xterm.js') }}"></script>
<script src="{{ url_for('static', filename='node_modules/xterm-addon-fit/lib/xterm-addon-fit.js') }}"></script> <script src="{{ url_for('static', filename='node_modules/xterm-addon-fit/lib/xterm-addon-fit.js') }}"></script>
<script> <script>
document.getElementsByClassName("content")[0].style.padding = "0 0" document.getElementsByClassName("content")[0].style.padding = "0 0"
</script> </script>
<div class="container"> <div style="height: 100%">
<div id="console"></div>
<div id="vnc-container"> <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" <iframe src="/novnc/vnc.html?resize=scale&path=novnc/websockify&autoconnect=true&view_only"
width="100%" height="100%" frameborder="0"> width="100%" height="100%" frameborder="0"></iframe>
</iframe>
</div> </div>
</div> </div>

477
V6.py
View File

@ -1,11 +1,8 @@
#!/usr/bin/python3 #!/usr/bin/python3.10
import random
import subprocess
from modules.Classes.Config import Config from modules.Classes.Config import Config
from modules.Classes.DiscordLogger import DiscordLogger from modules.Classes.DiscordLogger import DiscordLogger
from modules.Classes.UserCredentials import UserCredentials from modules.Classes.UserCredentials import UserCredentials
from modules.Tools.logger import critical, warning from modules.Tools.logger import warning, critical
from modules.cards import * from modules.cards import *
from modules.config import * from modules.config import *
from modules.db import add_to_database from modules.db import add_to_database
@ -25,33 +22,16 @@ def create_driver(mobile=False):
"AppleWebKit/537.36 (KHTML, like Gecko)" "AppleWebKit/537.36 (KHTML, like Gecko)"
"Chrome/22 Mobile Safari/537.36" "Chrome/22 Mobile Safari/537.36"
) )
chrome_options = webdriver.ChromeOptions()
chrome_profile_dir = init_profile(config.UserCredentials.get_mail(), mobile=mobile)
# Full list on https://github.com/GoogleChrome/chrome-launcher/blob/main/docs/chrome-flags-for-tools.md
arguments = [
"--no-first-run",
"--ash-no-nudges",
"--no-default-browser-check",
"--disable-features=PrivacySandboxSettings4,Translate",
"--disable-search-engine-choice-screen",
f"--user-data-dir={chrome_profile_dir}/"
]
if mobile: if mobile:
arguments.append(f"--user-agent={mobile_user_agent}") chrome_options.add_argument(f"--user-agent={mobile_user_agent}")
else: else:
arguments.append(f"--user-agent={pc_user_agent}") chrome_options.add_argument(f"--user-agent={pc_user_agent}")
# disabled as it may cause detection # disabled as it may cause detection
if config.proxy.is_enabled(): if config.proxy.is_enabled():
arguments.append(f'--proxy-server={config.proxy.ip}:{config.proxy.port}') chrome_options.add_argument(f'--proxy-server={config.proxy.ip}:{config.proxy.port}')
chrome_options = webdriver.ChromeOptions()
for arg in arguments:
chrome_options.add_argument(arg)
driver = uc.Chrome(options=chrome_options) driver = uc.Chrome(options=chrome_options)
set_language(driver)
return driver return driver
@ -172,7 +152,7 @@ def play_quiz4(override: int = None):
except Exception as e: except Exception as e:
log_error(e) log_error(e)
raise ValueError(e) raise ValueError(e)
info("Quiz 4 done.") info("Quiz 8 done.")
custom_sleep(3) custom_sleep(3)
@ -185,8 +165,6 @@ def do_poll():
try: try:
answer_elem.click() answer_elem.click()
except exceptions.ElementNotInteractableException: except exceptions.ElementNotInteractableException:
warning("element not clickable. Waiting a bit and retrying.")
custom_sleep(uniform(2, 2.5))
driver.execute_script("arguments[0].click();", answer_elem) driver.execute_script("arguments[0].click();", answer_elem)
custom_sleep(uniform(2, 2.5)) custom_sleep(uniform(2, 2.5))
except Exception as err: except Exception as err:
@ -199,68 +177,6 @@ def do_poll():
# Find each playable card and tries to click on it to earn points # Find each playable card and tries to click on it to earn points
def all_cards(): def all_cards():
driver = config.WebDriver.driver driver = config.WebDriver.driver
def check_popups() -> bool:
"""
Check various annoying popups
"""
def check_welcome_tour() -> bool:
if "rewards.bing.com/welcometour" not in driver.current_url:
return False
info("Popup 'Explorer le programme' reçue")
wait_until_visible(By.ID, "welcome-tour", timeout=5, browser=driver)
custom_sleep(1.5)
welcome_tour = driver.find_element(By.ID, "welcome-tour")
interest_button_box = welcome_tour.find_element(By.CLASS_NAME, "interest-buttons")
interests = interest_button_box.find_elements(By.CLASS_NAME, "ng-binding")
debug("Got the following interests: " + str(interests))
random.choice(interests).click() # Choose interest
custom_sleep(1.5)
claim_button = welcome_tour.find_element(By.ID, "claim-button")
claim_button.click() # submit
custom_sleep(1.5)
return True
def check_streak_protection() -> bool:
"""
Ne perdez plus jamais votre série !
"""
try:
streak_protection_close = driver.find_element(By.ID, "streak-protection-popup-close-cross")
streak_protection_activate = driver.find_elements(By.CLASS_NAME, "earningPagePopUpPopUpSelectButton")
streak_protection_activate[0].click()
info("Popup 'Streak Protection' reçue")
custom_sleep(1.5)
return True
except (exceptions.NoSuchElementException, exceptions.ElementNotInteractableException, IndexError):
# les éléments sont présents dans le DOM même quand la popup n'est pas visible apparemment
return False
def check_streak_protection_expired() -> bool:
"""
Non, vous avez utilisé tous vos jours de protection contre les séries !
"""
try:
streak_protection_close = driver.find_element(By.ID, "streak-protection-popup-close-cross")
ok_sad_button = driver.find_elements(By.CLASS_NAME, "dashboardPopUpPopUpSelectButton")
ok_sad_button[0].click()
info("Popup 'Streak Protection fully used' reçue")
custom_sleep(1.5)
return True
except (exceptions.NoSuchElementException, exceptions.ElementNotInteractableException, IndexError):
return False
return (
check_welcome_tour()
or check_streak_protection()
or check_streak_protection_expired()
)
driver.get("https://rewards.bing.com") driver.get("https://rewards.bing.com")
wait_until_visible(By.CLASS_NAME, "c-card-content", 10, driver) wait_until_visible(By.CLASS_NAME, "c-card-content", 10, driver)
@ -274,7 +190,7 @@ def all_cards():
info("no promo card") info("no promo card")
if len(card_list) < 10: # most likely an error during loading if len(card_list) < 10: # most likely an error during loading
if "suspendu" in driver.page_source or "suspended" in driver.page_source: if "suspendu" in driver.page_source:
raise Banned() raise Banned()
driver.refresh() driver.refresh()
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content") card_list = driver.find_elements(By.CLASS_NAME, "c-card-content")
@ -304,53 +220,39 @@ def all_cards():
if i == len(card_list) and i > 15: if i == len(card_list) and i > 15:
checked = False checked = False
if not checked: if checked:
continue custom_sleep(1.5)
try:
activity = findall("data-bi-id=\"([^\"]+)\"", card_list[i].get_attribute("innerHTML"))[0]
except Exception as e:
warning("Can't find activity." + str(e))
activity = ""
custom_sleep(1.5)
check_popups()
driver.execute_script("arguments[0].scrollIntoView();", card_list[i])
custom_sleep(1.5)
card_list[i].click()
if len(driver.window_handles) > 1:
driver.switch_to.window(driver.window_handles[1])
try_play(driver.title, activity)
close_tab(driver.window_handles[1])
try:
driver.refresh()
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content")
if "mee-icon-AddMedium" not in card_list[i].get_attribute("innerHTML"):
continue
check_popups()
driver.execute_script("arguments[0].scrollIntoView();", card_list[i]) driver.execute_script("arguments[0].scrollIntoView();", card_list[i])
custom_sleep(1.5)
card_list[i].click() card_list[i].click()
driver.switch_to.window(driver.window_handles[1]) if len(driver.window_handles) > 1:
custom_sleep(10) driver.switch_to.window(driver.window_handles[1])
log_error(f"Card {i} Can't be completed. Why MS ?") try_play(driver.title)
try: close_tab(driver.window_handles[1])
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: try:
log_error(err) driver.refresh()
custom_sleep(3) 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(): def promo():
@ -388,124 +290,8 @@ def promo():
custom_sleep(3) custom_sleep(3)
def explore_on_bing(activity: str, config: Config):
driver = config.WebDriver.driver
def search_bing(txt):
search_elm = driver.find_element(By.ID, "sb_form_q")
send_keys_wait(search_elm, txt)
send_keys_wait(search_elm, Keys.ENTER)
if "lyrics" in activity:
search_bing(
f"paroles de {['Gata', 'Pyramide', 'Dolce Camara', 'Position', 'Mami Wata'][randint(0, 4)]}") # merci bing copilot pour les titres
elif "flight" in activity:
search_bing(
f"vol {['Paris - New York', 'Londres Amsterdam', 'Bora-Bora Miami', 'Los Angeles Toulouse', 'Rome Dubai'][randint(0, 4)]}")
elif "shopping" in activity:
search_bing(f"idée cadeau {['Noel', 'Anniversaire'][randint(0, 1)]}")
elif "movie" in activity:
search_bing(
f"Distribution {['Code 8 part 2', 'The Hunger Games: The ballad of Songbirds & Snakes', 'Rebel Moon: Part Two', 'Dune II', 'Wonka'][randint(0, 4)]}")
elif "translator" in activity:
search_bing(f"traduction {config.wordlist.get_word()} en anglais")
elif "map" in activity:
search_bing(f"{['Paris', 'Nice', 'Marseille', 'Bordeaux', 'Lyon'][randint(0, 4)]} carte")
elif "restaurant" in activity:
search_bing(random.choice([
"restaurant à proximité",
"restaurant pas loin",
"manger dans le coin"
]))
elif "recipe" in activity:
search_bing("recette de " + random.choice([
"cake aux olives",
"tarte tatin",
"Kouign Amann",
"poulet au Curry",
"lasagnes aux épinards et ricotta",
"Boeuf Bourguignon",
"dessert",
"gateau au chocolat",
"crêpe sucrée"
])) # Merci llama pour les idées
elif "currency" in activity:
currencies = [
"euros",
"dollars canadiens",
"dollars",
"livres",
"francs suisses",
"roubles",
"pesos",
"bitcoin",
"anciens francs"
]
chosen = random.sample(currencies, k=2)
search_bing(f"convertir {random.randint(2, 120)} {chosen[0]} en {chosen[1]}")
elif "weather" in activity:
search_bing(f"météo {['Paris', 'Nice', 'Marseille', 'Bordeaux', 'Lyon'][randint(0, 4)]}")
elif "packagetracking" in activity:
driver.get("https://www.bing.com/?setmkt=en-US&setlang=en-US")
search_bing(f"{['fedex', 'UPS'][randint(0, 1)]} package tracking")
elif "hotel" in activity:
search_bing(f" {['Paris', 'Nice', 'Marseille', 'Bordeaux', 'Lyon'][randint(0, 4)]}")
elif "health" in activity:
search_bing(f"{['toux', 'douleur dos', 'nez qui coule', 'mal au genoux', 'otite'][randint(0, 4)]}")
elif "news" in activity:
search_bing(["actualités", "news"][randint(0, 1)])
elif "jobs" in activity:
search_bing("jobs")
elif "realestate" in activity:
search_bing(f"appartement à louer {['Paris', 'Nice', 'Marseille', 'Bordeaux', 'Lyon'][randint(0, 4)]}")
elif "financemarket" in activity:
search_bing(
f"cours action {['AIR LIQUIDE', 'Airbus', 'BNP Paribas', 'Michelin', 'Stellantis', 'Vinci'][randint(0, 5)]}")
elif "sports" in activity:
equipes = random.sample([
"Toulon", "toulouse",
"stade Rochelais", "castres",
"clermont", "perpignan",
"aviron bayonnais", "vannes"
], k=2)
search_bing(f"{['score', 'résultats'][randint(0, 1)]} rugby {equipes[0]} {equipes[1]}")
elif "videogames" in activity:
search_bing(random.choice([
"Minecraft", "GTA V",
"Tetris", "PUBG",
"Mario Kart 8",
"Red Dead Redemption II",
"Terraria",
"The Witcher",
"Animal Crossing",
"Pokémon rouge & bleu",
"Super Mario Bros",
"The legend of Zelda BOTW",
"Call of Duty Modern Warfare",
"The Witcher III"
]))
elif "dictionary" in activity:
search_bing(f"définition {config.wordlist.get_word()}")
elif "election" in activity or "timezonedates" in activity:
#! I don't understand the name of "timezonedates" but this seems to be the same activity
state = random.choice([
"Alaska", "Alabama", "Arkansas", "American Samoa", "Arizona", "California", "Colorado",
"Connecticut", "District ", "of Columbia", "Delaware", "Florida", "Georgia", "Guam",
"Hawaii", "Iowa", "Idaho", "Illinois", "Indiana", "Kansas", "Kentucky", "Louisiana",
"Massachusetts", "Maryland", "Maine", "Michigan", "Minnesota", "Missouri", "Mississippi",
"Montana", "North Carolina", "North Dakota", "Nebraska", "New Hampshire", "New Jersey",
"New Mexico", "Nevada", "New York", "Ohio", "Oklahoma", "Oregon", "Pennsylvania",
"Puerto Rico", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas",
"Utah", "Virginia", "Virgin Islands", "Vermont", "Washington", "Wisconsin",
"West Virginia", "Wyoming"
])
search_bing(f"governor candidates {state} 2024")
else:
log_error(f"Explore on bing: {activity} not found.")
# Find out which type of action to do # Find out which type of action to do
def try_play(nom="unknown", activity=""): def try_play(nom="unknown"):
driver = config.WebDriver.driver driver = config.WebDriver.driver
rgpd_popup(config) rgpd_popup(config)
@ -551,7 +337,7 @@ def try_play(nom="unknown", activity=""):
info("On fidelity page.") info("On fidelity page.")
fidelity() fidelity()
elif wait_until_visible(By.ID, "rqStartQuiz", 5, driver, raise_error=False): elif wait_until_visible(By.ID, "rqStartQuiz", 5, driver):
custom_sleep(3) custom_sleep(3)
driver.find_element(By.ID, "rqStartQuiz").click() # start the quiz driver.find_element(By.ID, "rqStartQuiz").click() # start the quiz
answer_number = driver.page_source.count("rqAnswerOption") answer_number = driver.page_source.count("rqAnswerOption")
@ -561,14 +347,9 @@ def try_play(nom="unknown", activity=""):
number = driver.page_source.count("rqAnswerOption") number = driver.page_source.count("rqAnswerOption")
warning(f"recovery detected. quiz : {number}") warning(f"recovery detected. quiz : {number}")
play(number - 1) play(number - 1)
elif "exploreonbing" in activity:
info(f"Explore on bing: {activity}")
explore_on_bing(activity, config)
custom_sleep(uniform(3, 5))
elif "Optimisez votre sécurité avec Microsoft" in driver.page_source:
info("Still not paying Office 365 ?")
else: else:
info(f"Nothing obvious to do on page `{nom}`.") info(f"Nothing to do on page `{nom}`")
custom_sleep(uniform(3, 5)) custom_sleep(uniform(3, 5))
@ -578,40 +359,23 @@ def login_part_1():
info("Starting part 1 of login") info("Starting part 1 of login")
driver = config.WebDriver.driver driver = config.WebDriver.driver
driver.get("https://login.live.com") driver.get("https://login.live.com")
try: wait_until_visible(By.ID, "i0116", browser=driver)
wait_until_visible(By.ID, "i0116", browser=driver) mail_elem = driver.find_element(By.ID, "i0116")
send_wait_and_confirm( send_keys_wait(mail_elem, config.UserCredentials.get_mail())
driver.find_element(By.ID, "i0116"), mail_elem.send_keys(Keys.ENTER)
config.UserCredentials.get_mail()
)
except exceptions.NoSuchElementException:
info("Unable to find mail input for login, might be semi-logged-in")
wait_until_visible(By.ID, "i0118", browser=driver) wait_until_visible(By.ID, "i0118", browser=driver)
send_wait_and_confirm( pwd_elem = driver.find_element(By.ID, "i0118")
driver.find_element(By.ID, "i0118"), send_keys_wait(pwd_elem, config.UserCredentials.get_password())
config.UserCredentials.get_password() pwd_elem.send_keys(Keys.ENTER)
) custom_sleep(2)
# 2FA # 2FA
try: if "Entrez le code de sécurité" in driver.page_source:
if not wait_until_visible(By.ID, "idTxtBx_SAOTCC_OTC", browser=driver, timeout=5, raise_error=False): try:
custom_sleep(2) a2f_elem = driver.find_element(By.ID, "idTxtBx_SAOTCC_OTC")
return a2f_elem.send_keys(config.UserCredentials.get_tfa().now())
a2f_elem.send_keys(Keys.ENTER)
tfa = config.UserCredentials.get_tfa() except Exception as err:
if tfa is None: log_error(err)
error("2FA needed but no code available for this account, sending error")
raise ValueError("2FA needed but no code available for this account")
else:
a2f_code = tfa.now()
info(f"Need 2FA, I have code: {a2f_code}")
send_wait_and_confirm(
driver.find_element(By.ID, "idTxtBx_SAOTCC_OTC"),
a2f_code
)
except Exception as err:
log_error(err)
# Accept all cookies question, and check if the account is locked # Accept all cookies question, and check if the account is locked
@ -646,19 +410,9 @@ def login_part_2():
# login() tries to login to your Microsoft account. # login() tries to login to your Microsoft account.
# it uses global variable g._mail and g._password to login # it uses global variable g._mail and g._password to login
def login(): def login():
def logged_in():
driver.get("https://login.live.com")
custom_sleep(10)
debug(get_domain(driver))
if get_domain(driver) == "account.microsoft.com":
return True
return False
info("Logging in...")
driver = config.WebDriver.driver driver = config.WebDriver.driver
try: try:
if not logged_in(): login_part_1()
login_part_1()
login_part_2() login_part_2()
driver.get("https://rewards.bing.com/") driver.get("https://rewards.bing.com/")
except Banned: except Banned:
@ -783,23 +537,19 @@ def fidelity():
pause = driver.find_element(By.CSS_SELECTOR, f'[class="c-action-toggle c-glyph f-toggle glyph-pause"]') pause = driver.find_element(By.CSS_SELECTOR, f'[class="c-action-toggle c-glyph f-toggle glyph-pause"]')
pause.click() pause.click()
except Exception as e: except Exception as e:
debug("No pause button.") 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"]') cartes = driver.find_elements(By.CSS_SELECTOR, f'[ng-repeat="item in $ctrl.transcludedItems"]')
nb_cartes = len(cartes) nb_cartes = len(cartes)
if nb_cartes == 0:
warning("No fidelity cards detected")
return "No cards."
checked_list_all = driver.find_elements(By.CSS_SELECTOR, f'[ng-if="$ctrl.complete"]') checked_list_all = driver.find_elements(By.CSS_SELECTOR, f'[ng-if="$ctrl.complete"]')
for i in range(nb_cartes): for i in range(nb_cartes):
cartes[i].click() cartes[i].click()
checked_txt = checked_list_all[i].get_attribute("innerHTML") checked_txt = checked_list_all[i].get_attribute("innerHTML")
ok = checked_txt.count("StatusCircleOuter checkmark") ok = checked_txt.count("StatusCircleOuter checkmark")
total = checked_txt.count("StatusCircleOuter") total = checked_txt.count("StatusCircleOuter")
if ok != total: if (ok != total):
elm = driver.find_elements(By.CLASS_NAME, 'clickable-link')[i] elm = driver.find_elements(By.CLASS_NAME, 'clickable-link')[i]
# legacy code. Should be removed if not "moviesandtv" in elm.get_attribute("innerHTML"): # not the film card
if "moviesandtv" not in elm.get_attribute("innerHTML"): # not the film card
elm.click() elm.click()
driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1]) driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1])
sub_fidelity() sub_fidelity()
@ -860,11 +610,9 @@ def daily_routine(cred: UserCredentials, custom=False):
try: try:
if not custom: # custom already is logged in if not custom: # custom already is logged in
login() login()
except Banned: except Banned:
log_error("This account is locked.") log_error("This account is locked.")
raise Banned() return
except Identity: except Identity:
log_error("This account has an issue.") log_error("This account has an issue.")
return return
@ -917,53 +665,53 @@ def json_start(json_entry, cred: UserCredentials):
config.WebDriver.set_pc_driver(create_driver()) config.WebDriver.set_pc_driver(create_driver())
config.WebDriver.switch_to_driver("PC") config.WebDriver.switch_to_driver("PC")
driver = config.WebDriver.driver driver = config.WebDriver.driver
if "unban" in json_entry and str(account_id) in json_entry["unban"]: try:
login() if str(account_id) in json_entry["unban"]:
info("\nGO TO example.com TO PROCEED or wait 1200 secs.") login_part_1()
for _ in range(1200): info("\nGO TO example.com TO PROCEED or wait 1200 secs.")
sleep(1) for _ in range(1200):
if driver.current_url == "https://example.com/": sleep(1)
info("proceeding") if driver.current_url == "https://example.com/":
break info("proceeding")
else: break
else:
login()
except KeyError:
login() login()
try: try:
if str(account_id) in json_entry["tout"]: if str(account_id) in json_entry["tout"]:
daily_routine(cred, True) daily_routine(cred)
else:
try:
if str(account_id) in json_entry["daily"]:
try:
all_cards()
except Exception as e:
log_error(e)
except KeyError:
pass
try:
if str(account_id) in json_entry["pc"]:
try:
bing_pc_search()
except Exception as e:
log_error(e)
except KeyError:
pass
try:
if str(account_id) in json_entry["mobile"]:
try:
bing_mobile_search(cred)
except Exception as e:
log_error(e)
except KeyError:
pass
except KeyError: except KeyError:
pass pass
try: else:
log_points() try:
except Exception as e: if str(account_id) in json_entry["daily"]:
error(f"CustomStart {e}") 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() driver.close()
cred.next_account() cred.next_account()
account_id += 1 account_id += 1
@ -980,17 +728,13 @@ def default_start():
while config.UserCredentials.is_valid(): while config.UserCredentials.is_valid():
custom_sleep(1) custom_sleep(1)
info("Starting and configuring driver.") info("Starting and configuring driver.")
try: config.WebDriver.set_pc_driver(create_driver())
config.WebDriver.set_pc_driver(create_driver())
except:
info("Could not configure driver. Trying to update it.")
subprocess.Popen(["python3", "/app/MsRewards-Reborn/modules/Tools/update_chrome.py"])
config.WebDriver.set_pc_driver(create_driver())
config.WebDriver.switch_to_driver("PC") config.WebDriver.switch_to_driver("PC")
info("Driver started.") info("Driver started.")
config.WebDriver.pc_driver.implicitly_wait(3) config.WebDriver.pc_driver.implicitly_wait(3)
try: try:
wait_time = uniform(1200, 3600) wait_time = uniform(1200, 3600)
wait_time = 10 # the display closes when I ctrl + c
info(f"Waiting for {round(wait_time / 60)}min before starting") info(f"Waiting for {round(wait_time / 60)}min before starting")
custom_sleep(wait_time) custom_sleep(wait_time)
daily_routine(config.UserCredentials) daily_routine(config.UserCredentials)
@ -1000,16 +744,12 @@ def default_start():
config.WebDriver.pc_driver.quit() config.WebDriver.pc_driver.quit()
config.display.stop() config.display.stop()
break break
except Banned:
warning("this account is banned. Switching to next account")
except Exception as e: except Exception as e:
log_error(f"Error not caught. Skipping this account. " + format_error(e)) log_error(f"Error not caught. Skipping this account. " + format_error(e))
critical(f"Error not caught. Skipping this account. {e}") critical(f"Error not caught. Skipping this account. {e}")
config.WebDriver.pc_driver.quit() config.WebDriver.pc_driver.quit()
finally: config.UserCredentials.next_account()
config.UserCredentials.next_account()
config.display.stop() config.display.stop()
@ -1018,6 +758,7 @@ def log_error(msg):
def check_updated(): def check_updated():
debug(f"updated: {config.has_been_updated()}")
if config.has_been_updated(): if config.has_been_updated():
config.discord.wh.send(f"Updated to {config.version}", username="update", config.discord.wh.send(f"Updated to {config.version}", username="update",
avatar_url="https://cdn-icons-png.flaticon.com/512/1688/1688988.png") avatar_url="https://cdn-icons-png.flaticon.com/512/1688/1688988.png")

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@ -30,7 +30,7 @@ class DiscordLogger:
) )
file = File("screenshot.png") file = File("screenshot.png")
embed.set_image(url="attachment://screenshot.png") embed.set_image(url="attachment://screenshot.png")
embed.set_footer(text=self.config.UserCredentials.get_mail() + " - " + self.config.WebDriver.current_driver()) embed.set_footer(text=self.config.UserCredentials.creds.get_mail())
self.config.discord.wh.send(embed=embed, username="error", file=file, avatar_url=self.config.discord.avatar_url) self.config.discord.wh.send(embed=embed, username="error", file=file, avatar_url=self.config.discord.avatar_url)
self.config.discord.wh.send(username="error", file=File("page.html"), avatar_url=self.config.discord.avatar_url) self.config.discord.wh.send(username="error", file=File("page.html"), avatar_url=self.config.discord.avatar_url)

View File

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

View File

@ -30,9 +30,8 @@ class UserCredentials:
def get_tfa(self): def get_tfa(self):
if not self.tfa_enable(): if not self.tfa_enable():
warning("Warning: TFA is not enabled. Can't get a TFA code.") warning("Warning: TFA is not enabled. Calling get_tfa is an expected behaviour.")
return None return TOTP(self.data[self.current]["tfa"])
return TOTP(self.data[self.current]["2fa"])
def next_account(self): def next_account(self):
self.current += 1 self.current += 1
@ -42,5 +41,4 @@ class UserCredentials:
debug("No new credentials.") debug("No new credentials.")
def is_valid(self): def is_valid(self):
return (self.current < self.total return self.current < self.total
and self.get_mail() != "" and self.get_mail is not None)

View File

@ -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

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

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
0.0.0

View File

@ -1 +1 @@
v6.8.65 v6.8.38