From a0a4535c3501eba3453ab33901b60441cf6bd306 Mon Sep 17 00:00:00 2001 From: piair Date: Tue, 27 Feb 2024 11:26:55 +0100 Subject: [PATCH] updated webUI according to param changes --- .github/ISSUE_TEMPLATE/bug_report.md | 32 + .github/ISSUE_TEMPLATE/feature_request.md | 20 + .gitignore | 17 + Dockerfile | 22 + Flask/app.py | 442 + Flask/start.sh | 5 + Flask/static/css/flask.css | 405 + Flask/static/favicon.ico | Bin 0 -> 3976 bytes Flask/static/images/trash.svg | 1 + Flask/static/js/main.js | 102 + Flask/static/logs/.gitignore | 1 + Flask/templates/base.html | 79 + Flask/templates/change_password.html | 22 + Flask/templates/config.html | 106 + Flask/templates/dev.html | 31 + Flask/templates/discord.html | 56 + Flask/templates/login.html | 23 + Flask/templates/logs.html | 21 + Flask/templates/proxy.html | 53 + Flask/templates/schedule.html | 35 + Flask/templates/settings.html | 49 + Flask/templates/stats.html | 12 + Flask/templates/vnc_get.html | 45 + Flask/templates/vnc_post.html | 35 + V6.py | 806 + build.sh | 1 + config/Stats-dashbord.json | 193 + config/config.sh | 66 + config/grafana.ini | 1469 + config/request.sh | 19 + modules/Classes/Config.py | 15 + modules/Classes/UserCredentials.py | 39 + modules/Tools/logger.py | 47 + modules/cards.py | 34 + modules/config.py | 107 + modules/db.py | 67 + modules/driver_tools.py | 96 + modules/error.py | 8 + modules/globals.py | 30 + modules/imports.py | 36 + modules/tools.py | 82 + requirements.txt | 17 + sse.sh | 3 + user_data/.gitignore | 4 + user_data/configs.json | 1 + user_data/cookies/.gitignore | 4 + user_data/discord.json | 1 + user_data/flask.json | 4 + user_data/french | 345731 +++++++++++++++++++ user_data/logs/.gitignore | 1 + user_data/logs/custom.txt | 0 user_data/proxy.json | 1 + user_data/settings.json | 1 + version | 1 + 54 files changed, 350498 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Flask/app.py create mode 100644 Flask/start.sh create mode 100644 Flask/static/css/flask.css create mode 100644 Flask/static/favicon.ico create mode 100644 Flask/static/images/trash.svg create mode 100644 Flask/static/js/main.js create mode 100644 Flask/static/logs/.gitignore create mode 100644 Flask/templates/base.html create mode 100644 Flask/templates/change_password.html create mode 100644 Flask/templates/config.html create mode 100644 Flask/templates/dev.html create mode 100644 Flask/templates/discord.html create mode 100644 Flask/templates/login.html create mode 100644 Flask/templates/logs.html create mode 100644 Flask/templates/proxy.html create mode 100644 Flask/templates/schedule.html create mode 100644 Flask/templates/settings.html create mode 100644 Flask/templates/stats.html create mode 100644 Flask/templates/vnc_get.html create mode 100644 Flask/templates/vnc_post.html create mode 100755 V6.py create mode 100755 build.sh create mode 100644 config/Stats-dashbord.json create mode 100644 config/config.sh create mode 100755 config/grafana.ini create mode 100644 config/request.sh create mode 100644 modules/Classes/Config.py create mode 100644 modules/Classes/UserCredentials.py create mode 100644 modules/Tools/logger.py create mode 100644 modules/cards.py create mode 100644 modules/config.py create mode 100644 modules/db.py create mode 100644 modules/driver_tools.py create mode 100644 modules/error.py create mode 100644 modules/globals.py create mode 100644 modules/imports.py create mode 100644 modules/tools.py create mode 100644 requirements.txt create mode 100644 sse.sh create mode 100644 user_data/.gitignore create mode 100644 user_data/configs.json create mode 100644 user_data/cookies/.gitignore create mode 100644 user_data/discord.json create mode 100644 user_data/flask.json create mode 100644 user_data/french create mode 100644 user_data/logs/.gitignore create mode 100644 user_data/logs/custom.txt create mode 100644 user_data/proxy.json create mode 100644 user_data/settings.json create mode 100644 version diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..ad78d4d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem, and the html file if there is an error. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5e3eadb --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +dev_build.sh +geckodriver.log +.vscode/ +.idea +venv +/Git +page.html +screenshot.png +login.csv +data +**/__pycache__ +user_data/* +install.sh +nohup.out +points.csv +file.png +user_data/configs.json diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b95c762 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM python:3.10 +ENV DEBIAN_FRONTEND noninteractive +WORKDIR /app/ +RUN apt update \ + && wget http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.0g-2ubuntu4_amd64.deb \ + && dpkg -i libssl1.1_1.1.0g-2ubuntu4_amd64.deb \ + && apt install redis libgtk-4-1 libvulkan1 libxdamage1 -y \ + && curl -sSLO https://nc.piair.xyz/s/BKLsBWoZkTdYjfq/download/chrome.deb \ + && ln -fs /usr/share/zoneinfo/Europe/Paris /etc/localtime \ + && git clone https://gitea.augustin64.fr/piair/MsRewards-Reborn \ + && python3 -m pip install -r MsRewards-Reborn/requirements.txt \ + && wget -q -O /usr/share/keyrings/grafana.key https://apt.grafana.com/gpg.key \ + && curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg \ + && echo "deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main" | tee -a /etc/apt/sources.list.d/grafana.list \ + && apt update \ + && apt install novnc websockify grafana xvfb nginx nano tzdata sqlite3 apt-transport-https software-properties-common wget wfrench tigervnc-standalone-server libasound2 libatk-bridge2.0-0 libnss3 libnspr4 xvfb libgbm1 libatk1.0-0 libu2f-udev libatspi2.0-0 libcups2 libxkbcommon0 libxrandr2 libdbus-1-3 xdg-utils fonts-liberation libdrm2 -y \ + && bash MsRewards-Reborn/config/config.sh \ + && dpkg -i chrome.deb + +ENV TZ="Europe/Paris" +WORKDIR /app/MsRewards-Reborn/Flask/ +CMD bash start.sh diff --git a/Flask/app.py b/Flask/app.py new file mode 100644 index 0000000..bf55f0a --- /dev/null +++ b/Flask/app.py @@ -0,0 +1,442 @@ +import gevent.monkey +gevent.monkey.patch_all() +from time import sleep +import subprocess +import os +from flask import Flask, Response, redirect, url_for, request, session, abort, render_template, send_from_directory +from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.triggers.cron import CronTrigger +from flask_login import LoginManager, UserMixin, login_required, login_user, logout_user +from werkzeug.utils import secure_filename +import json +import re +from requests import get +import redis + + +# redis part for live update +pool = redis.ConnectionPool(host='localhost', port=6379, db=0) +r = redis.Redis(connection_pool=pool) + +def generate_output(): + pubsub = r.pubsub() + pubsub.subscribe('console') + try : + for message in pubsub.listen(): + if message['type'] == 'message': + print(message) + yield f"data: {message['data'].decode()}\n\n" + except Exception as e: + print(f"ya eu une erreur sad {e}") + +# the end + +global password +with open("/app/MsRewards-Reborn/user_data/flask.json", "r") as inFile: + data = json.load(inFile) + +password = data["password"] +secret = data["secret"] +if secret == "": + import secrets + secret = secrets.token_hex() + with open("/app/MsRewards-Reborn/user_data/flask.json", "w") as inFile: + data = { + "password": password, + "secret": secret + } + json.dump(data, inFile) + +""" +#Automatic start of MsRewards +""" +def daily_command(): + subprocess.Popen(["git",'pull']) + subprocess.Popen(["pkill","-9","chrome"]) + subprocess.Popen(["pkill","-9","Xvfb"]) + subprocess.Popen(["pkill","-9","Xvnc"]) + subprocess.Popen(["pkill","-9", "undetected_chromedriver"]) + +scheduler = BackgroundScheduler() +scheduler.start() +scheduler.add_job( # on relance le job + daily_command, # --- + trigger=CronTrigger( + year="*", month="*", day="*", hour="0", minute="0", second="0" + ), # --- + name="Daily refresh", # --- + id="99" # --- +) + +def start_ms(i): + print("\033[32m" + f"Starting config {i}" + "\033[0m") + log = open(f"/app/MsRewards-Reborn/Flask/static/logs/{i}.txt", 'a') # so that data written to it will be appended + subprocess.Popen([f"python3 -u /app/MsRewards-Reborn/V6.py -c {i}"], stdout=log, stderr=log, shell=True) + log.close() + + +TriggerDict = {} +def update_jobs(): + with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: + configs = json.load(inFile) + for i in configs: + try : + h, m = configs[i]["time"].split(":") + print("\033[36m" + f"config {i} : {h}:{m}" + "\033[0m") + TriggerDict[i] = CronTrigger( + year="*", month="*", day="*", hour=h, minute=m, second="0" + ) + if configs[i]["enabled"]: + try : + scheduler.remove_job(i) # on reset le job + except Exception as e: + print(f"\033[33merror with deleting config {i} : {e}\033[0m") + + try : + scheduler.add_job( # on relance le job + start_ms, # --- + trigger=TriggerDict[i], # --- + args=[i], # --- + name="Daily start", # --- + id=i # --- + ) + print("\033[36m" + f"successfully created config {i}" + "\033[0m") + except Exception as e: + print(f"\033[33merror with creating config {i} : {e}\033[0m") + else : + try : + scheduler.remove_job(i) + except Exception as e : + print(f"\033[33merror with deleting config {i} : {e}\033[0m") + except Exception as e: + print(e) + + +""" +#Flask app +""" + +app = Flask(__name__) + +@app.context_processor +def inject_default_variables(): + with open("/app/MsRewards-Reborn/version", "r") as f: + version = f.readline().replace("\n", '') + return dict(version=version) +""" +#Login stuff +""" +# config +app.config["TEMPLATES_AUTO_RELOAD"] = True +app.config.update( + SECRET_KEY = secret +) + +login_manager = LoginManager() +login_manager.init_app(app) +login_manager.login_view = "login" + +# silly user model +class User(UserMixin): + def __init__(self, id): + self.id = id + self.name = "user" + str(id) + self.password = password + + def __repr__(self): + return "%d/%s/%s" % (self.id, self.name, self.password) + +users = [User(1)] + +@app.route('/stream') +def stream(): + return Response(generate_output(), content_type='text/event-stream') + + +@app.route("/login/", methods=["GET", "POST"]) +def login(): + if request.method == 'POST': + if request.form['password'] == password: + user = User(id) + login_user(user) + if password == "ChangeMe": + return(redirect('/change_password')) + return(redirect('/schedule')) + else: + return abort(401) + else: + return(render_template("login.html")) + + +@app.route("/change_password/", methods=["GET", "POST"]) +@login_required +def change_password(): + global password + if request.method == 'POST': + password = request.form["password"] + subprocess.Popen(["grafana-cli", "admin", "reset-admin-password", password]) + with open("/app/MsRewards-Reborn/user_data/flask.json", "w") as inFile: + data = { + "password": password, + "secret": secret + } + json.dump(data, inFile) + return(render_template("change_password.html")) + + +# handle login failed +@app.errorhandler(401) +def unauthorized(e): + return(redirect("/login")) + + +# callback to reload the user object +@login_manager.user_loader +def load_user(userid): + return User(userid) + +""" +#end of login stuff +""" + +@app.route("/") +def main(): + with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: + configs = json.load(inFile) + return(render_template("schedule.html", data=configs)) + + +@app.route("/discord/") +def discord_get(): + with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile: + data = json.load(inFile) + return(render_template("discord.html", data=data, len=maxi(data))) + + +@app.route("/discord/", methods=["post"]) +def discord_post(): + with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile: + data = json.load(inFile) + action = request.form + if action['DISCORD'] == "delete" : + data.pop(action["select"], None) + else : + config = action["select"] + successL = action["successL"] + try : + a = action["successT"] + successT = "True" + except: + successT = "False" + try : + a = action["errorsT"] + errorsT = "True" + except: + errorsT = "False" + errorsL = action["errorsL"] + name = action["name"] if action["name"] else f"unnamed{action['select']}" + data[config] = {"errorsL" : errorsL, "errorsT": errorsT, "successT": successT, "successL": successL, "name": name} + + with open("/app/MsRewards-Reborn/user_data/discord.json", "w") as outFile: + json.dump(data, outFile) + return(render_template("discord.html", data=data, len=maxi(data))) + + +@app.route("/dev/") +def dev2(): + return(render_template("dev.html")) + + +@app.route("/settings/") +def settings_get(): + with open("/app/MsRewards-Reborn/user_data/settings.json", "r") as inFile: + settings = json.load(inFile) + return(render_template("settings.html", data=settings)) + + +@app.route("/settings/", methods=["post"]) +def settings_post(): + settings = {} + action = request.form + settings['avatarlink'] = action["avatarlink"] + with open("/app/MsRewards-Reborn/user_data/settings.json", "w") as inFile: + json.dump(settings, inFile) + return(render_template("settings.html", data=settings)) + + +@app.route("/proxy/") +def proxy_get(): + with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile: + j = json.load(inFile) + return(render_template("proxy.html", data=j, len=maxi(j))) + + +@app.route("/proxy/", methods=["post"]) +def proxy_post(): + with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile: + data = json.load(inFile) + action = request.form + print(action) + if action['PROXY'] == "delete" : + print(action) + data.pop(action["select"], None) + else : + try : + config = action["select"] + address = action["address"] + port = action["port"] + name = action["name"] if action["name"] else f"@unnamed{action['select']}" + data[config] = {"address" : address, "port": port, "name": name} + except : + print("error : probably bad config") + + with open("/app/MsRewards-Reborn/user_data/proxy.json", "w") as outFile: + json.dump(data, outFile) + return(render_template("proxy.html", data=data, len=maxi(data))) + + +@app.route("/schedule/") +def schedule_get(): + with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: + configs = json.load(inFile) + return(render_template("schedule.html", data=configs)) + + +@app.route("/schedule/", methods=["post"]) +def schedule_post(): + with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: + configs = json.load(inFile) + + data = dict(request.form) + for i in configs: + try : + data[f'switch{i}'] + except : + data[f'switch{i}'] = "off" + + for i in configs: + configs[i]["time"] = data[f"time{i}"] + configs[i]["enabled"] = data[f"switch{i}"] == "on" + + with open("/app/MsRewards-Reborn/user_data/configs.json", "w") as inFile: + json.dump(configs, inFile) + update_jobs() + return(render_template("schedule.html", data=configs)) + + +@app.route("/config/") +def config_get(): + with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile: + proxys = json.load(inFile) + with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile: + discords = json.load(inFile) + with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: + configs = json.load(inFile) + return(render_template("config.html", data=configs, discords=discords, proxys=proxys, configs=configs, len=maxi(configs))) + + +@app.route("/config/", methods=["POST"]) +def config_post(): + action = request.form + with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile: + proxys = json.load(inFile) + with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile: + discords = json.load(inFile) + with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: + configs = json.load(inFile) + + if action["data"] == "delete": + print(action["config"]) + configs.pop(action["config"]) + else : + comptes = { + "1":{"mail": action["mail1"], "pwd": action["pwd1"], "2fa": action["2fa1"]}, + "2":{"mail": action["mail2"], "pwd": action["pwd2"], "2fa": action["2fa2"]}, + "3":{"mail": action["mail3"], "pwd": action["pwd3"], "2fa": action["2fa3"]}, + "4":{"mail": action["mail4"], "pwd": action["pwd4"], "2fa": action["2fa4"]}, + "5":{"mail": action["mail5"], "pwd": action["pwd5"], "2fa": action["2fa5"]} + } + + configs[action["config"]] = { + "name" : action["name"] if action["name"] != "" else f"unnamed{action['config']}", + "proxy": action["proxy"], + "discord": action["discord"], + "time":"", + "enabled":"False", + "accounts": comptes + } + with open("/app/MsRewards-Reborn/user_data/configs.json", "w") as outFile: + json.dump(configs, outFile) + return(render_template("config.html", data=configs, discords=discords, proxys=proxys, configs=configs, len=maxi(configs))) + +@app.route("/logs/", methods=["GET", "POST"]) +def logs(): + with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: + configs = json.load(inFile) + print(configs) + return(render_template("logs.html", data=configs)) + + +@app.route("/stats/", methods=["GET", "POST"]) +def stats(): + return(render_template("stats.html")) + + +@app.route("/override/", methods=["POST"]) +def override_post(): + json = request.form.to_dict(flat=False) + log = open(f"/app/MsRewards-Reborn/Flask/static/logs/custom.txt", 'w') # so that data written to it will be appended + subprocess.Popen([f"python3 -u /app/MsRewards-Reborn/V6.py -c {json['config'][0]} --json \"{json}\""], stdout=log, stderr=log, shell=True) + log.close() + return(render_template("vnc_post.html")) + +@app.route("/override/", methods=["GET"]) +def override_get(): + with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: + configs = json.load(inFile) + return(render_template("vnc_get.html", configs=configs)) + +@app.route('/download/', 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']) diff --git a/Flask/start.sh b/Flask/start.sh new file mode 100644 index 0000000..9098adc --- /dev/null +++ b/Flask/start.sh @@ -0,0 +1,5 @@ +nohup bash /app/MsRewards-Reborn/sse.sh & +service grafana-server start +service nginx start +nohup redis-server & +gunicorn --reload --worker-class gevent -b 0.0.0.0:6666 'app:app' diff --git a/Flask/static/css/flask.css b/Flask/static/css/flask.css new file mode 100644 index 0000000..e5e0a3e --- /dev/null +++ b/Flask/static/css/flask.css @@ -0,0 +1,405 @@ +@import url('https://fonts.googleapis.com/css?family=Montserrat'); + +/* Colors */ +:root { + --color-background0: #212121; + --color-background1: #212121; + --color-background2: dimgray; + --color-border0: #FFFFFF; + --color-border1: grey; + --color-text: #FFFFFF; + --color-green: green; + --color-lime: #BADA55; + --color-red: red; + --color-switch-button: #FFFFFF; + --color-switch-background: grey; + --color-transparent: rgba(0, 0, 0, 0); +} + +/* Sidebar size is the left panel size when opened */ +:root { + --sidebar-size: max(20vw, 160px); + --sidebar-sz-plus10: calc(var(--sidebar-size) + 10px); + --sidebar-sz-plus30: calc(var(--sidebar-size) + 30px); +} + +html { + text-align: center; + font-family: Montserrat; + height: 95%; +} + +form { + width: 100%; +} + +table { + width: 100%; + height: 100%; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +a { + color: var(--color-text); +} + +iframe{ + border: none; +} + +input[type=text] { + + width: 90%; + padding: 12px 20px; + margin: 8px 0; + background-color: var(--color-background1); + border: 2px solid var(--color-border1); + border-radius: 4px; + outline: none; + color: var(--color-text); + text-align: center; +} + +input[type=password] { + + width: 90%; + padding: 12px 20px; + margin: 8px 0; + text-align: center; + border: 2px solid var(--color-border1); + border-radius: 4px; + outline: none; + color: var(--color-text); + background-color: var(--color-background1); +} + +.button{ + border-radius: 4px; + border: 2px solid var(--color-border1); + background-color: var(--color-background1); + color: var(--color-text); + padding: 12px 20px; + width: 70%; + margin: 10px; +} + +.left-button{ + width: 10%; +} + +.ban{ + border-radius: 4px; + border: 2px solid var(--color-red); + background-size: 200% 100%; + background-image: linear-gradient(to right, var(--color-background1) 50%, var(--color-red) 50%); + -webkit-transition: background-position 0.5s; + -moz-transition: background-position 0.5s; + transition: background-position 0.5s; + color: var(--color-text); + padding: 12px 0px; + width: 70%; + margin: 10px; +} +.ban:hover{ + background-position: -100% 0; +} + +.confirm{ + color: var(--color-text); + padding: 12px 20px; + width: 70%; + margin: 10px; + background-size: 200% 100%; + background-image: linear-gradient(to right, var(--color-background1) 50%, var(--color-green) 50%); + -webkit-transition: background-position 0.5s; + -moz-transition: background-position 0.5s; + transition: background-position 0.5s; + border: 2px solid var(--color-border1); +} + +.confirm:hover{ + background-position: -100% 0; +} + +.confirm{ + color: var(--color-text); + padding: 12px 20px; + width: 70%; + margin: 10px; + background-size: 200% 100%; + background-image: linear-gradient(to right, var(--color-background1) 50%, var(--color-green) 50%); + -webkit-transition: background-position 0.5s; + -moz-transition: background-position 0.5s; + transition: background-position 0.5s; + border: 2px solid var(--color-border1); +} + +.confirm:hover{ + background-position: -100% 0; +} +.unselected{ + border-radius: 4px; + border: 2px solid var(--color-border1); + background-color: var(--color-background1); + color: var(--color-text); + padding: 12px 20px; + width: 70%; + margin: 10px; +} +.selected{ + border-radius: 4px; + border: 2px solid var(--color-border1); + background-color: var(--color-background2); + color: var(--color-text); + padding: 12px 20px; + width: 70%; + margin: 10px; +} + + +button:hover{ + border: 2px solid var(--color-border0); + cursor: pointer; +} +input:hover{ + border: 2px solid var(--color-border0); +} + + + + +.submit{ + border-radius: 4px; + border: 2px solid var(--color-border1); + background-color: var(--color-background1); + color: var(--color-text); + padding: 12px 20px; + width: 100%; + margin: 10px; +} + +body { + background-color: var(--color-background0); + color: var(--color-text); + height: 100%; +} + + + +.left-pannel{ + flex: 1; + margin: 20px; + border: 1px solid var(--color-border0); + border-radius: 5px; + padding: 20px; + width: 80%; + height: 90%; + +} + +.content{ + flex: 3; + margin: 20px; + border: 1px solid var(--color-border0); + border-radius: 5px; + padding: 20px; + height: 90%; + +} + +.content > ul { + display: inline-block; +} +.container { + display: flex; + width: 100%; + height: 100%; +} + +.row-item { + margin: 10px 0; + text-align: center; +} + +#image img { + display: block; + margin: auto; + width: 20%; +} +.comlumn-name{ + width: 20%; +} + + +/* + For toggle switch +*/ + + +input.toogle[type=checkbox]{ + height: 0; + width: 0; + visibility: hidden; +} + +label { + padding: 8px; + margin: auto; + cursor: pointer; + text-indent: -9999px; + width: 100px; + height: 50px; + background: var(--color-switch-background); + display: block; + border-radius: 100px; + position: relative; +} + +/* +width: height of label /2 ; +height: height of label /2px; +*/ +label:after { + content: ''; + position: absolute; + top: 5px; + left: 5px; + width: 40px; + height: 40px; + background: var(--color-switch-button); + border-radius: 90px; + transition: 0.3s; +} + +input:checked + label { + background: var(--color-lime); +} + +input:checked + label:after { + left: calc(100% - 5px); + transform: translateX(-100%); +} +/*changer l'épaisseur du click */ +label:active:after { + width: 30px; +} + +select { + width: 90%; + /*height: 100%;*/ + padding: 12px 20px; + margin: 8px 0; + background-color: var(--color-background1); + border: 2px solid var(--color-border1); + border-radius: 4px; + outline: none; + color: var(--color-text); + text-align: center; +} + +select:hover { + border: 2px solid var(--color-border0); +} + + +input[type="time"] { + width: 70%; + padding: 8px 12px; + margin: 8px 0; + background-color: var(--color-background1); + border: 2px solid var(--color-border1); + border-radius: 4px; + outline: none; + color: var(--color-text); + text-align: center; +} + +input[type="time"]::-webkit-calendar-picker-indicator { + filter: invert(1); + opacity: 0.7; + cursor: pointer; +} + +input[type="time"]::-webkit-calendar-picker-indicator:hover { + opacity: 1; +} + + +/** Sidebar stuff */ +#slide-sidebar { + display: none; +} + +label[for="slide-sidebar"] { + all: initial; + z-index: 1; /* Always on top */ + position: absolute; + top: 20px; + left: var(--sidebar-sz-plus30); + + -moz-transition: left 0.5s ease; + transition: left 0.5s ease; + background: var(--color-transparent); +} + +label[for="slide-sidebar"]:after { + all:unset; +} + +input:checked + label[for="slide-sidebar"] { + background: var(--color-transparent); +} + +#slide { + border-style: solid; + border-radius: 4px; + border-width: 1px; + border-color: var(--color-border0); + + color: var(--color-text); + background-color: var(--color-background0); + padding: 8px 8px 2px 8px; +} + +/* When opened behaviour */ +.left-pannel { + width: var(--sidebar-size); + position: fixed; + top: 0; + left: 0; + bottom: 0; +} + +.content { + position: absolute; + top: 0; + left: var(--sidebar-sz-plus10); + right: 0; + bottom: 0; + + -moz-transition: left 0.5s ease; + transition: left 0.5s ease; + + padding: 0 25px; + background-color: var(--color-background0); /* we need no transparency when switching */ + overflow: hidden; +} + +/* When closed behaviour */ +input:checked#slide-sidebar~label { + left: 20px; +} + +input:checked#slide-sidebar~.left-pannel { + display: none; + transition: display 0s 0.5s; +} + +input:checked#slide-sidebar~.content { + left: 0; +} \ No newline at end of file diff --git a/Flask/static/favicon.ico b/Flask/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ffab6e379060ffddf27edc3d9215cde192ae9670 GIT binary patch literal 3976 zcmeHKe^^uJ6+ZXol3WnTjYP!4;1aDMT7_0nteQZGK-nO)w%F=y#G*rSBKpjzXl_6y zg0nK`WJ(3Cbhe7l$7-EA8Ef+FIS#fBKZ?$&=jg z-t(UKyyyMCd)S~&jP-DP!VLgC;^ShT2cQM~04v6o!K**J3qY+)SF5%0YBiskwRm27 zMjAkH;p)OEac8vhbxAYJUK;J`En9L{`^Fek{=@}C^0k2xw5Ex5;0r)HhqF)}6T4*|=JGfA+k0E{1vT-JZ5* z@RGnSKOd5F@VZeN=+2J_d!fY3GGg+L8#`CN-JUwR=0r#RKVHllDaxj;V?OdI(C6w` ze)PA<4zJi5$-iH_&{o3^8=waTe?=IX^6!+H^4QG^B zY?`d*EHC8~8A_C=hU5E}!1_zRv1%I?B1 z*UY$=mtqSqkPj6dyhD$Rl4bEf(@5F|QEo0zoIUJt$9rqzW1^<(FWqWLPHYX8tCEX* zJ0i1R7k})1WK-*^Im8Vt|+V0Q<`g{6oOl@eBOsVh6w$*G5%Ps?$;9;Jhlf==G z!3iC#GLVJLT(HO3T%$m65oJzu{?O2!`8F>j2;lyg5|#l*7wU-Nfrngc5KRH)vr#Ey zfi>&w8&seIacdGo10CvPP;kHr75{DMS&1%;udZ}})*Gmfjcp#z=5*X&}-#Cr2pxKGJVrWAQ_0&@G0 zMao1JkO*NyP$MOzcmy7*9-8jO!sKdkrWfrG z`tzJ@$Ld{#lqf>jA+v&cxLTEQ$8re^s27*Aa=g=cl5Q;8EP*voSE`#5j=D0i(^Vxy zdV&s_q9lw+%OGXmM^I=nt^(vnqGo@%Dik!NiX1FRgKE0}82UZQMEN{P6lPU2;sumj zX2cz(Ax1$T#W46PkQ>o3#822YTFanuh_BG(j}nBv6(ELmniEWajX`JqeKs+b=nrwqv`KU|DbLUN4i%4 ziv1|*$@UnK)_E~Nibq5msNelTwX0(VqAjc9h_)0Nmzc^ zRsJRHKPTpz0gjxwB2wn&M6l;qfQV35fs>R+_2>pQdF*4jfg8^Y;~YB6V(V4o@j z;AZWOpHv1YrILdN>S&q>1B6TJQ^Qqu>t)>^dqtmY_aK-Hq<1!fwMwb3=$2w?o0kFo z#2N4orcm1{Y^VXVlKfT)y1^dkaPq(eG-u7Ti3lH#1XTtM2qG73OVa;<(4Q6&NZJib zOxsdF3K#MFF}Xa5WKt5Dw4@r`hf1u5Rgr37+ve}CI>=FsZ&l73M1mELuY>W$hf?rh zAyP1<1PRX$qTj!9;ra?HnG0@O2O(5aRt+c-{NHznaHH zLJ6>!P~MNrlDE8}e`dhC$;^N%IA;b22xMl!203R2CGccsz)&)7Af+E6qHf_{J{t=S;pVCHv?CWG0{iBd9{?XHRh5~^HdAeU|IV2q7`g0K;oE*pNg zI}V}gP1I${Aso%}Wqi#$+i0W-YG@wlh4>P^ry9K!vef{?z~Tzd`NS Ze2;X`jQz#0e5VlPcuiu=foNUMKLOgRkYE4+ literal 0 HcmV?d00001 diff --git a/Flask/static/images/trash.svg b/Flask/static/images/trash.svg new file mode 100644 index 0000000..720b21a --- /dev/null +++ b/Flask/static/images/trash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Flask/static/js/main.js b/Flask/static/js/main.js new file mode 100644 index 0000000..74807fa --- /dev/null +++ b/Flask/static/js/main.js @@ -0,0 +1,102 @@ +function addline(ligne){ + var lplus = parseInt(ligne) + 1; + document.getElementById("table").insertRow(lplus).insertCell(0).innerHTML = '' +} + +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 + ''; + myIframe.contentWindow.scrollTo(0, myIframe.contentDocument.body.scrollHeight); + }); + myIframe.src = "/static/logs/" + value + ".txt"; +} diff --git a/Flask/static/logs/.gitignore b/Flask/static/logs/.gitignore new file mode 100644 index 0000000..314f02b --- /dev/null +++ b/Flask/static/logs/.gitignore @@ -0,0 +1 @@ +*.txt \ No newline at end of file diff --git a/Flask/templates/base.html b/Flask/templates/base.html new file mode 100644 index 0000000..9960b71 --- /dev/null +++ b/Flask/templates/base.html @@ -0,0 +1,79 @@ + + + + + + + + + MS Rewards + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ {% block content %} + {% endblock %} +
+
+ + + + diff --git a/Flask/templates/change_password.html b/Flask/templates/change_password.html new file mode 100644 index 0000000..e5851fd --- /dev/null +++ b/Flask/templates/change_password.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% block left_pannel %} +{% endblock %} +{% block content %} + +{%if not current_user.is_authenticated %} +

Already logged in

+{% else %} +
+ + + + + + + + + +
Change password
+
+{% endif %} +{% endblock %} \ No newline at end of file diff --git a/Flask/templates/config.html b/Flask/templates/config.html new file mode 100644 index 0000000..eb3fc6f --- /dev/null +++ b/Flask/templates/config.html @@ -0,0 +1,106 @@ +{% extends "base.html" %} +{% block left_pannel %}config{% endblock %} + + + +{% block content %} + +{%if not current_user.is_authenticated %} + +{% else %} +
+ + + + + + + + + + + + + +
config : + + name : + +
Proxy : + + Discord : + +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MailPassword2FA
+ + + + + +
+ + + +
+ +
+ + +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/Flask/templates/dev.html b/Flask/templates/dev.html new file mode 100644 index 0000000..06b79eb --- /dev/null +++ b/Flask/templates/dev.html @@ -0,0 +1,31 @@ +{% extends "base.html" %} + +{% block left_pannel %}{% endblock %} + + +{% block content %} +{%if not current_user.is_authenticated %} + +{% else %} + + + + +
+
+
+ + +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/Flask/templates/discord.html b/Flask/templates/discord.html new file mode 100644 index 0000000..0a9d782 --- /dev/null +++ b/Flask/templates/discord.html @@ -0,0 +1,56 @@ +{% extends "base.html" %} +{% block left_pannel %}discord{% endblock %} +{% block content %} + +{%if not current_user.is_authenticated %} + +{% else %} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
name
Send errors
Send success
Success link
Failure Link
+ + + + + + +
+ +
+
+ +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/Flask/templates/login.html b/Flask/templates/login.html new file mode 100644 index 0000000..e64fc31 --- /dev/null +++ b/Flask/templates/login.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} +{% block left_pannel %}login{% endblock %} +{% block content %} + +{%if not current_user.is_authenticated %} +
+ + + + + + + + + +
password
+ +
+ +{% else %} +

Already logged in

+{% endif %} +{% endblock %} \ No newline at end of file diff --git a/Flask/templates/logs.html b/Flask/templates/logs.html new file mode 100644 index 0000000..37e8d5f --- /dev/null +++ b/Flask/templates/logs.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} +{% block left_pannel %}logs{% endblock %} +{% block content %} + +{%if not current_user.is_authenticated %} +

Already logged in

+{% else %} + + +

+ +{% endif %} + +{%endblock %} \ No newline at end of file diff --git a/Flask/templates/vnc_get.html b/Flask/templates/vnc_get.html new file mode 100644 index 0000000..93be418 --- /dev/null +++ b/Flask/templates/vnc_get.html @@ -0,0 +1,45 @@ + +{% extends "base.html" %} +{% block left_pannel %}override{% endblock %} + +{% block content %} + +{%if not current_user.is_authenticated %} + +{% else %} + + + +
+ + + + + + + + + + + {% for i in range(0,5) %} + + + + + + + + + {% endfor %} + +
nameUnbantoutdailypcmobile
+ + +
+{% endif %} +{%endblock %} \ No newline at end of file diff --git a/Flask/templates/vnc_post.html b/Flask/templates/vnc_post.html new file mode 100644 index 0000000..67d6fbb --- /dev/null +++ b/Flask/templates/vnc_post.html @@ -0,0 +1,35 @@ + +{% extends "base.html" %} +{% block left_pannel %}override{% endblock %} + +{% block content %} + +{%if not current_user.is_authenticated %} + +{% else %} + + + + + +
+
+
+ +
+ + + + + +{% endif %} + +{%endblock %} \ No newline at end of file diff --git a/V6.py b/V6.py new file mode 100755 index 0000000..4a132e5 --- /dev/null +++ b/V6.py @@ -0,0 +1,806 @@ +#!/usr/bin/python3.10 +from modules.config import * +from modules.error import * +from modules.driver_tools import * +from modules.cards import * +import modules.globals as g + +from modules.Classes.Config import Config +from modules.Classes.UserCredentials import UserCredentials +from modules.Tools.logger import warning, info, debug, error, critical + + +driver = g.driver +display = g.display + + +# create a webdriver +def create_driver(mobile=False): + PC_USER_AGENT = ( + "Mozilla/5.0 (X11; Linux x86_64)" + "AppleWebKit/537.36 (KHTML, like Gecko)" + "Chrome/122.0.0.0 Safari/537.36 Edg/122.0.2088.46" + ) + MOBILE_USER_AGENT = ( + "Mozilla/5.0 (Linux; Android 7.0; Nexus 5 Build/MRA58N)" + "AppleWebKit/537.36 (KHTML, like Gecko)" + "Chrome/22 Mobile Safari/537.36" + ) + chrome_options = webdriver.ChromeOptions() + if mobile: + chrome_options.add_argument(f"--user-agent={MOBILE_USER_AGENT}") + else: + chrome_options.add_argument(f"--user-agent={PC_USER_AGENT}") + # disabled as it may cause detection + if g.proxy_enabled: + chrome_options.add_argument(f'--proxy-server={g.proxy_address}:{g.proxy_port}') + driver = uc.Chrome(options=chrome_options) + set_language(driver) + return driver + + +def log_error(error_message, l_driver=driver, log=g.full_log): + global driver + if l_driver is None: + l_driver = driver + if type(error_message) is not str: + error_message = format_error(error_message) + + error(str(error_message)) + if g.discord_enabled_error: + with open("page.html", "w") as f: + try: + f.write(l_driver.page_source) + except: + f.write("the driver has closed or crashed. Can't access page content") + try: + img = display.waitgrab() + img.save("screenshot.png") + except: + l_driver.save_screenshot("screenshot.png") + if not log: + embed = Embed( + title="An Error has occured", + description=str(error_message), + colour=Colour.red(), + ) + else: + embed = Embed( + title="Full log is enabled", + description=str(error_message), + colour=Colour.blue(), + ) + file = File("screenshot.png") + embed.set_image(url="attachment://screenshot.png") + embed.set_footer(text=g._mail) + webhookFailure.send(embed=embed, username="error", file=file, avatar_url=g.avatar_url) + webhookFailure.send(username="error", file=File("page.html"), avatar_url=g.avatar_url) + + +# close the tab currently on and go back to the one first, or the one specified +def close_tab(tab, switch_to: int = 0) -> None: + driver.switch_to.window(tab) + driver.close() + driver.switch_to.window(driver.window_handles[switch_to]) + + +# play_quiz[N]([int : override]) make the quiz with N choice each time. They usually have between 4 and 10 questions. +# override is the number of question, by default, it's the number of question in this specific quiz. +# Can be useful in some case, where the program crashes before finishing the quiz +def play_quiz2(override=10) -> None: + info("Starting to play quiz 2.") + debug(f"override: {override}") + for j in range(override): + custom_sleep(uniform(3, 5)) + js_function = """ + function get_correct_answer(){ + function br(n) { for (var r, t = 0, i = 0; i < n.length; i++)t += n.charCodeAt(i); return r = parseInt(_G.IG.substr(_G.IG.length - 2), 16), t += r, t.toString() } // Ms check function + function namedRAValue() { //allow calls to getRAvalue + return _w.getRAValue() + }; + if (br(document.getElementById("rqAnswerOption0").attributes["data-option"].value) == namedRAValue()){ + return(0); + } + else { + return(1); + } + }; + return(get_correct_answer()) + """ + correct_answer_value = driver.execute_script(js_function) + try: + answer_elem = driver.find_element(By.ID, f"rqAnswerOption{correct_answer_value}") + answer_elem.click() + except exceptions.ElementNotInteractableException: + answer_elem = driver.find_element(By.ID, f"rqAnswerOption{correct_answer_value}") + driver.execute_script("arguments[0].click();", answer_elem) + except Exception as e: + log_error(e) + break + info("Quiz 2 done.") + custom_sleep(3) + + +def play_quiz8(): + info(f"Starting Quiz 8") + override = len(findall("", driver.page_source)) + 1 + debug(f"override : {override}") + correct_answers = ["Should", "be", "reset", "before", "you", "see", "this."] # supress warning + try: + for _ in range(override): + sleep(uniform(3, 5)) + correct_answers = [] + for i in range(1, 9): # todo: remove this odd 1-offset + try: + element = driver.find_element(By.ID, f"rqAnswerOption{i - 1}") + if 'iscorrectoption="True"' in element.get_attribute("outerHTML"): + correct_answers.append(f'rqAnswerOption{i - 1}') + except Exception as e: + warning(f"can't find rqAnswerOption{i - 1}. Probably already clicked" + str(e)) + shuffle(correct_answers) + for answer_id in correct_answers: + wait_until_visible(By.ID, answer_id, timeout=20, browser=driver) + try: + answer_elem = driver.find_element(By.ID, answer_id) + answer_elem.click() + sleep(1) + except exceptions.NoSuchElementException: + driver.refresh() + sleep(10) + answer_elem = driver.find_element(By.ID, answer_id) + answer_elem.click() + except ElementClickInterceptedException: + rgpd_popup(driver) + correct_answers.append(answer_id) + + except Exception as e: + log_error(f"{format_error(e)} \n Good answers : {' '.join(correct_answers)}") + info("Quiz 8 done.") + custom_sleep(3) + + +def play_quiz4(override: int = None): + info(f"Starting Quiz 4") + + if not override: + try: # fidelity quiz are much longer than usual ones + override = int(findall('rqQuestionState([\d]{1,2})"', driver.page_source)[-1]) + except: + override = 3 + debug(f"Override : {override}") + + try: + for i in range(override): + custom_sleep(uniform(3, 5)) + txt = driver.page_source + answer_option = search('correctAnswer":"([^"]+)', txt)[1] + answer_option = answer_option.replace("\\u0027", "'") # replace Unicode weird symbols + answer_element = driver.find_element(By.CSS_SELECTOR, f'[data-option="{answer_option}"]') + try: + answer_element.click() + except exceptions.ElementNotInteractableException: + driver.execute_script("arguments[0].click();", answer_element) + + except Exception as e: + log_error(e) + raise ValueError(e) + info("Quiz 8 done.") + custom_sleep(3) + + +# do_poll() answer a random thing to poll, on of daily activities +def do_poll(): + info("Starting poll") + try: + answer_elem = driver.find_element(By.ID, f"btoption{choice([0, 1])}") + try: + answer_elem.click() + except exceptions.ElementNotInteractableException: + driver.execute_script("arguments[0].click();", answer_elem) + custom_sleep(uniform(2, 2.5)) + except Exception as err: + log_error(err) + raise ValueError(err) + info("Poll done.") + custom_sleep(3) + + +# Find each playable card and tries to click on it to earn points +# todo : refactor +def all_cards(): + driver.get("https://rewards.bing.com") + wait_until_visible(By.CLASS_NAME, "c-card-content", 10, driver) + liste = driver.find_elements(By.CLASS_NAME, "c-card-content") + custom_sleep(2) + try: + promo() + except Exception as e: + info("no promo card") + if (len(liste) < 10): # most likely an error during loading + if "suspendu" in driver.page_source: + raise Banned() + driver.refresh() + liste = driver.find_elements(By.CLASS_NAME, "c-card-content") + if (len(liste) < 10): + log_error("Less than 10 cards. Most likely an error with login.", driver) + return ("PAS ASSEZ DE CARTES") + if (len(liste) < 20): # most likely not in france + printf("moins de 20 cartes. Probablement pas en France.") + for i in range(len(liste)): + printf(f"carte {i}") + try: + checked = ("mee-icon-AddMedium" in liste[i].get_attribute("innerHTML")) + except StaleElementReferenceException: + driver.refresh() + liste = driver.find_elements(By.CLASS_NAME, "c-card-content") + printf(f"staled, {len(liste)}") + checked = ("mee-icon-AddMedium" in liste[i].get_attribute("innerHTML")) + except IndexError: + driver.get("https://rewards.bing.com") + custom_sleep(10) + liste = driver.find_elements(By.CLASS_NAME, "c-card-content") + try: + checked = ("mee-icon-AddMedium" in liste[i].get_attribute("innerHTML")) + except IndexError: + if i == len(liste) & i > 15: + checked = False + # input("2") + if checked: + custom_sleep(1.5) + driver.execute_script("arguments[0].scrollIntoView();", liste[i]) + custom_sleep(1.5) + # input("3") + liste[i].click() + # input("4") + if len(driver.window_handles) > 1: + driver.switch_to.window(driver.window_handles[1]) + # input("5") + try_play(driver.title) + close_tab(driver.window_handles[1]) + try: + driver.refresh() + liste = driver.find_elements(By.CLASS_NAME, "c-card-content") + if ("mee-icon-AddMedium" in liste[i].get_attribute("innerHTML")): + printf(f"carte {i} not okay. Retrying.") + try: + liste[i].click() + except: + log_error("problème inconnu ? sauf si c'est un element obscure...", driver) + driver.get("https://rewards.bing.com") + checked = ("mee-icon-AddMedium" in liste[i].get_attribute("innerHTML")) + driver.switch_to.window(driver.window_handles[1]) + try_play(driver.title) + close_tab(driver.window_handles[1]) + if ("mee-icon-AddMedium" in liste[i].get_attribute("innerHTML")): + driver.execute_script("arguments[0].scrollIntoView();", liste[i]) + log_error(f"Card {i} Can't be completed. Why MS ?", driver) + liste[i].click() + driver.switch_to.window(driver.window_handles[1]) + log_error(f"Cart completion - log - 2", driver) + custom_sleep(10) + log_error(f"Cart completion - log - 3 - after 10 sec", driver) + try: + try_play(driver.title) # go back to the main page + try: + close_tab(driver.window_handles[1]) + except: + pass + except: + driver.get("https://rewards.bing.com") + except: + pass + else: + try: + welcome_tour(liste[i], driver) + except Exception as e: + printf("no new windows" + format_error(e)) + driver.get("https://rewards.bing.com") + custom_sleep(3) + + +def promo(): + for i in range(5): + elm = driver.find_element(By.ID, "promo-item") + wait_until_visible(By.ID, "promo-item", 5, driver) + if not elm: + break + if i > 3: + log_error("plus de 3 promo cards, probablement une pa skipable", driver) + try: + driver.find_element(By.CSS_SELECTOR, + 'i[class="mee-icon pull-left icon mee-icon-Cancel ng-scope"]').click() + except Exception as e: + log_error(f"can't click to close : {e}") + return () + try: + elm.click() + except Exception as e: + # log_error(e, driver) + driver.execute_script("arguments[0].click();", elm) + # log_error(e, driver) + printf(f"that should't be there (promo), but the workarround seemed to work {e}") + custom_sleep(3) + if len(driver.window_handles) > 1: + driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1]) + try_play(driver.title) + close_tab(driver.window_handles[1]) + else: + try: + spotify(driver) + except: + warning("no new windows") + driver.get("https://rewards.bing.com") + driver.refresh() + custom_sleep(3) + + +# Find out which type of action to do +def try_play(nom="inconnu"): + rgpd_popup(driver) + + def play(number): + if number in [8, 9]: + try: + debug(f"Quiz 8 detected on `{nom}`.") + play_quiz8() + except Exception as err: + error(f"fail of PlayQuiz 8. Aborted {err}") + + elif number in [4, 5]: + try: + debug(f"Quiz 4 detected on `{nom}`") + play_quiz4() + except Exception as err: + error(f"Fail of PlayQuiz 4. Aborted {err}.") + + elif number in [2, 3]: + try: + debug(f"\033[96mQuiz 2 detected on `{nom}`\033[0m") + play_quiz2() + except Exception as err: + error(f"fail of PlayQuiz 2. Aborted {err}") + else: + error("`rqAnswerOption` present in page but no action to do.") + + custom_sleep(uniform(3, 5)) + + if "pas connecté à Microsoft Rewards" in driver.page_source: + custom_sleep(5) + driver.find_element(By.CSS_SELECTOR, '[onclick="setsrchusr()"]').click() + custom_sleep(5) + rgpd_popup(driver) + custom_sleep(5) + debug("Detected and fixed connection popup") + + if "bt_PollRadio" in driver.page_source: + debug("Poll detected") + do_poll() + + elif "rqQuestionState" in driver.page_source: + number = driver.page_source.count("rqAnswerOption") + warning(f"recovery détecté. quiz : {number}") + play(number - 1) + + elif search("([0-9]) de ([0-9]) finalisée", driver.page_source): + info("On fidelity page.") + fidelity() + + elif wait_until_visible(By.ID, "rqStartQuiz", 5, driver): + custom_sleep(3) + driver.find_element(By.ID, "rqStartQuiz").click() # start the quiz + answer_number = driver.page_source.count("rqAnswerOption") + play(answer_number) + + else: + printf(f"Nothing to do on page `{nom}`") + custom_sleep(uniform(3, 5)) + + +# Login with password or with cookies. +# The driver should be in the same state on both case +def login_part_1(ldriver, cred: UserCredentials): + printf("pwd_login : start") + ldriver.get("https://login.live.com") + wait_until_visible(By.ID, "i0116", browser=ldriver) + mail_elem = ldriver.find_element(By.ID, "i0116") + send_keys_wait(mail_elem, cred.get_mail()) + mail_elem.send_keys(Keys.ENTER) + wait_until_visible(By.ID, "i0118", browser=ldriver) + pwd_elem = ldriver.find_element(By.ID, "i0118") + send_keys_wait(pwd_elem, cred.get_password()) + pwd_elem.send_keys(Keys.ENTER) + custom_sleep(2) + # 2FA + if "Entrez le code de sécurité" in ldriver.page_source: + try: + a2f_elem = ldriver.find_element(By.ID, "idTxtBx_SAOTCC_OTC") + a2f_elem.send_keys(g._otp.now()) + a2f_elem.send_keys(Keys.ENTER) + except Exception as e: + log_error(e) + + +# Accept all cookies question, and check if the account is locked +def login_part_2(ldriver): + custom_sleep(5) + + if 'Abuse' in ldriver.current_url: + raise Banned() + + if 'identity' in ldriver.current_url: + raise Identity() + + if 'notice' in ldriver.current_url: + ldriver.find_element(By.ID, "id__0").click() + + if "proof" in ldriver.current_url: + ldriver.find_element(By.ID, "iLooksGood") + + for elm_id in ["iNext", "KmsiCheckboxField", "id__0", "iLooksGood", "idSIButton9", "iCancel"]: + if get_domain(ldriver) == "account.microsoft.com": + break + try: + ldriver.find_element(By.ID, elm_id).click() + except Exception as e: + pass + + wait_until_visible(By.CSS_SELECTOR, '[data-bi-id="sh-sharedshell-home"]', 20, ldriver) + + +# login() tries to login to your Microsoft account. +# it uses global variable g._mail and g._password to login +def login(ldriver, cred: UserCredentials): + try: + login_part_1(ldriver, cred) + login_part_2(ldriver) + ldriver.get("https://rewards.bing.com/") + except Banned: + raise Banned() + except Identity: + raise Banned() + except Exception as e: + critical("Error not caught during login.") + log_error(e) + ldriver.quit() + return False + + +# Makes 30 search as PC Edge +def bing_pc_search(override=randint(35, 40)): + mot = choice(Liste_de_mot).replace(" ", "+") + driver.get(f"https://www.bing.com/search?q={mot}") + custom_sleep(uniform(1, 2)) + rgpd_popup(driver) + send_keys_wait( + driver.find_element(By.ID, "sb_form_q"), + Keys.BACKSPACE + Keys.BACKSPACE + Keys.BACKSPACE + Keys.BACKSPACE + Keys.BACKSPACE + Keys.BACKSPACE + ) + + for _ in range(override): + word = choice(Liste_de_mot) + try: + send_keys_wait(driver.find_element(By.ID, "sb_form_q"), word) + driver.find_element(By.ID, "sb_form_q").send_keys(Keys.ENTER) + except Exception as e: + error(e) + sleep(10) + driver.get(f'https://www.bing.com/search?q={word}') + sleep(3) + send_keys_wait(driver.find_element(By.ID, "sb_form_q"), word) + driver.find_element(By.ID, "sb_form_q").send_keys(Keys.ENTER) + custom_sleep(uniform(3, 7)) + try: + driver.find_element(By.ID, "sb_form_q").clear() + except Exception as e: + error(e) + try: + driver.get('https://www.bing.com/search?q=plans') + driver.find_element(By.ID, "sb_form_q").clear() + except Exception as e: + log_error(f"clear la barre de recherche - {format_error(e)}") # what is this message ??? todo + + +# Sends points to database, discord and whatever service you want +# todo: refactor +def log_points(account="unknown"): + def get_points(): + driver.get("https://rewards.bing.com") + custom_sleep(1) + wait_until_visible(By.CSS_SELECTOR, 'span[mee-element-ready="$ctrl.loadCounterAnimation()"]', browser=driver) + try: + point = search('availablePoints\":([\d]+)', driver.page_source)[1] + except Exception as e: + log_error( + f"Dev error, checking why it doesn't work (waited a bit, is this still white ?) {format_error(e)}", + driver) + error("Can't get points.") + return -1 + return point + + points = get_points() + custom_sleep(uniform(3, 20)) + account_name = account.split("@")[0] + + if g.discord_enabled_success: + if g.discord_embed: + embed = Embed( + title=f"{account_name} actuellement à {str(points)} points", colour=Colour.green() + ) + embed.set_footer(text=account_name) + webhookSuccess.send(embed=embed) + else: + webhookSuccess.send(f"{account_name} actuellement à {str(points)} points") + + try: + add_to_database(account_name, points) + except Exception as e: + log_error(e) + + +# todo: refactor and check if it works at all +def fidelity(): + def sub_fidelity(): + try: + wait_until_visible(By.CSS_SELECTOR, 'div[class="pull-left spacer-48-bottom punchcard-row"]', browser=driver) + answer_number = search("([0-9]) of ([0-9]) completed", driver.page_source) + if answer_number is None: + answer_number = search("([0-9]) 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('([^<^>]+)', + card_elem.get_attribute("innerHTML"))[1] + bouton_card = driver.find_element(By.XPATH, f'//span[text()="{button_text}"]') + bouton_card.click() + except Exception as e1: + try: + recover_elem = driver.find_element(By.XPATH, + '/html/body/div[1]/div[2]/main/div[2]/div[2]/div[7]/div[3]/div[1]/a') + recover_elem.click() + except Exception as e2: + log_error(f"fidélité - double erreur - e1 : {format_error(e1)} - e2 {format_error(e2)}") + break + custom_sleep(uniform(3, 5)) + driver.switch_to.window(driver.window_handles[2]) + try_play(driver.title) + custom_sleep(uniform(3, 5)) + try: + close_tab(driver.window_handles[2], 1) + except Exception as e: + printf(e) + printf("fidelity - done") + except Exception as e: + log_error(e) + + if driver.current_url != "https://rewards.bing.com": + driver.get("https://rewards.bing.com") + try: + pause = driver.find_element(By.CSS_SELECTOR, + f'[class="c-action-toggle c-glyph f-toggle glyph-pause"]') # mettre le truc en pause + pause.click() + except Exception as e: + printf(f"erreur lors du click de pause: probablement pas de cartes {e}") + return ("no cards") + cartes = driver.find_elements(By.CSS_SELECTOR, f'[ng-repeat="item in $ctrl.transcludedItems"]') + nb_cartes = len(cartes) + checked_list_all = driver.find_elements(By.CSS_SELECTOR, f'[ng-if="$ctrl.complete"]') + for i in range(nb_cartes): + cartes[i].click() # affiche la bonne carte + checked_txt = checked_list_all[i].get_attribute("innerHTML") + ok = checked_txt.count("StatusCircleOuter checkmark") + total = checked_txt.count("StatusCircleOuter") + if (ok != total): + elm = driver.find_elements(By.CLASS_NAME, 'clickable-link')[i] + if not "moviesandtv" in elm.get_attribute("innerHTML"): # not the film card + elm.click() + driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1]) + sub_fidelity() + close_tab(driver.window_handles[1]) + custom_sleep(1) + cartes = driver.find_elements(By.CSS_SELECTOR, f'[ng-repeat="item in $ctrl.transcludedItems"]') + checked_list_all = driver.find_elements(By.CSS_SELECTOR, f'[ng-if="$ctrl.complete"]') + + +def mobile_alert_popup(): + try: + alert = mobile_driver.switch_to.alert + alert.dismiss() + except exceptions.NoAlertPresentException: + pass + except Exception as err: + log_error(err, mobile_driver) + + +# todo: be coherent with pc search regarding error management +def bing_mobile_search(cred: UserCredentials, override=randint(22, 25)): + global mobile_driver + mobile_driver = create_driver(mobile=True) + try: + login(mobile_driver, cred) + mot = choice(Liste_de_mot).replace(" ", "+") + mobile_driver.get(f"https://www.bing.com/search?q={mot}") + custom_sleep(uniform(1, 2)) + rgpd_popup(mobile_driver) + custom_sleep(uniform(1, 1.5)) + for i in range(override): # 20 + try: + mot = choice(Liste_de_mot) + send_keys_wait(mobile_driver.find_element(By.ID, "sb_form_q"), mot) + mobile_driver.find_element(By.ID, "sb_form_q").send_keys(Keys.ENTER) + custom_sleep(uniform(3, 7)) + mobile_alert_popup() # check for alert (asking for position or for allowing notifications) + mobile_driver.find_element(By.ID, "sb_form_q").clear() + except Exception as err: + error(err) + mobile_driver.refresh() + custom_sleep(30) + i -= 1 + mobile_driver.quit() + + except Exception as err: + log_error(err, mobile_driver) + mobile_driver.quit() + + +def daily_routine(cred: UserCredentials, custom=False): + try: + if not custom: # custom already is logged in + login(driver, cred) + except Banned: + log_error("This account is locked.", driver) + return + except Identity: + log_error("This account has an issue.", driver) + return + + try: + all_cards() + except Banned: + log_error("banned", driver) + raise Banned + except Exception as err: + log_error(err) + + try: + fidelity() + except Exception as err: + log_error(err) + + try: + bing_pc_search() + except Exception as err: + log_error(err) + + try: + bing_mobile_search(cred) + except Exception as err: + log_error(err) + + try: + log_points(cred.get_mail()) + except Exception as err: + log_error(err) + + +def json_start(json_entry, cred: UserCredentials): + global driver + display = SmartDisplay(backend="xvnc", size=(1920, 1080), rfbport=2345, color_depth=24) + display.start() + + for i in range(5): + start = False + for j in ["unban", "tout", "pc", "mobile", "daily"]: + try: + if str(i) in json_entry[j]: + start = True + info(f"{cred.get_mail()} : {j}") + except KeyError: + pass + if start: + driver = create_driver() + try: + if str(i) in json_entry["unban"]: + login_part_1(driver, cred) + print("\nGO TO example.com TO PROCEED or wait 1200 secs.") + for _ in range(1200): + sleep(1) + if driver.current_url == "https://example.com/": + print("proceeding") + break + else: + login(driver, cred) + except KeyError: + login(driver, cred) + try: + if str(i) in json_entry["tout"]: + daily_routine(cred) + except KeyError: + pass + # print("none is set to \"tout\"") + else: + try: + if str(i) in json_entry["daily"]: + try: + all_cards() + except Exception as e: + log_error(e) + except KeyError: + pass + # print("none is set to \"daily\"") + try: + if str(i) in json_entry["pc"]: + try: + bing_pc_search() + except Exception as e: + log_error(e) + except KeyError: + pass + try: + if str(i) in json_entry["mobile"]: + try: + bing_mobile_search(cred) + except Exception as e: + log_error(e) + except KeyError: + pass + try: + log_points(g._mail) + except Exception as e: + printf(f"CustomStart {e}") + driver.close() + cred.next_account() + display.stop() + + +c = Config(args) + +if g.json_start: + dict_data = json.loads(g.json_start.replace("'", "\"")) + json_start(dict_data, c.UserCredentials) + +else: + + if g.vnc_enabled or g.dev: + display = SmartDisplay(backend="xvnc", size=(1920, 1080), rfbport=g.vnc_port, color_depth=24) + else: + display = SmartDisplay(size=(1920, 1080)) + display.start() + + if g.update_version != "None": + if g.discord_enabled_error: + webhookFailure.send(f"Updated to {g.update_version}", username="UPDATE", + avatar_url="https://cdn-icons-png.flaticon.com/512/1688/1688988.png") + + while c.UserCredentials.is_valid(): + custom_sleep(1) + info("Starting driver.") + driver = create_driver() + info("Driver started.") + driver.implicitly_wait(3) + try: + wait_time = uniform(1200, 3600) + info(f"Waiting for {round(wait_time / 60)}min before starting") + daily_routine(c.UserCredentials) + driver.quit() + custom_sleep(wait_time) + except KeyboardInterrupt: + critical("Canceled by user. Closing driver and display.") + driver.quit() + display.stop() + break + except Exception as e: + log_error(f"Error not caught. Skipping this account. " + format_error(e), driver) + critical(f"Error not caught. Skipping this account. {e}") + driver.quit() + + c.UserCredentials.next_account() + +display.stop() diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..0c40ec3 --- /dev/null +++ b/build.sh @@ -0,0 +1 @@ +sudo docker build --no-cache --network host -t msrewards . && sudo docker run -d --restart unless-stopped -p 1234:1234 -ti --shm-size=2gb --name MsRewards msrewards diff --git a/config/Stats-dashbord.json b/config/Stats-dashbord.json new file mode 100644 index 0000000..1626cee --- /dev/null +++ b/config/Stats-dashbord.json @@ -0,0 +1,193 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": false, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "frser-sqlite-datasource", + "uid": "bed26262-6b98-4dfc-a95d-f8bd39b5d09c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 10000, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "dark-yellow", + "value": 2000 + }, + { + "color": "green", + "value": 7500 + }, + { + "color": "blue", + "value": 10000 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "frser-sqlite-datasource", + "uid": "bed26262-6b98-4dfc-a95d-f8bd39b5d09c" + }, + "queryText": "SELECT\n unixepoch() as time,\n c.compte,\n c.last_pts as \"\"\nFROM \n comptes c;\n", + "queryType": "time series", + "rawQueryText": "SELECT\n unixepoch() as time,\n c.compte,\n c.last_pts as \"\"\nFROM \n comptes c;\n", + "refId": "A", + "timeColumns": [ + "time", + "ts" + ] + } + ], + "title": "Account points", + "type": "bargauge" + }, + { + "datasource": { + "type": "frser-sqlite-datasource", + "uid": "bed26262-6b98-4dfc-a95d-f8bd39b5d09c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 300, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "dark-green", + "value": 200 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 11 + }, + "id": 4, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "frser-sqlite-datasource", + "uid": "bed26262-6b98-4dfc-a95d-f8bd39b5d09c" + }, + "queryText": "SELECT\n unixepoch() as time,\n c.compte as metric,\n c.last_pts - d2.points as ''\nFROM \n comptes c \nLEFT OUTER JOIN \n daily d1 \nON \n c.compte = d1.compte \nAND \n d1.date = date() \n LEFT OUTER JOIN daily d2 ON c.compte = d2.compte AND d2.date = DATE('now','-1 day')\nORDER BY d1.points DESC", + "queryType": "time series", + "rawQueryText": "SELECT\n unixepoch() as time,\n c.compte as metric,\n c.last_pts - d2.points as ''\nFROM \n comptes c \nLEFT OUTER JOIN \n daily d1 \nON \n c.compte = d1.compte \nAND \n d1.date = date() \n LEFT OUTER JOIN daily d2 ON c.compte = d2.compte AND d2.date = DATE('now','-1 day')\nORDER BY d1.points DESC", + "refId": "A", + "timeColumns": [ + "time", + "ts" + ] + } + ], + "title": "Daily progress", + "type": "bargauge" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Stats", + "uid": "e70d4980-36d1-4107-90b0-d9164ae8ead4", + "version": 14, + "weekStart": "" +} \ No newline at end of file diff --git a/config/config.sh b/config/config.sh new file mode 100644 index 0000000..086a29d --- /dev/null +++ b/config/config.sh @@ -0,0 +1,66 @@ +printf "\nsetting up NGINX\n" + +rm /etc/nginx/sites-available/default +echo " +map \$http_upgrade \$connection_upgrade { + default upgrade; + '' close; +} + +upstream grafana { +server localhost:3000; +} + +server { + listen 1234; + server_name localhost; + location /grafana { + proxy_pass http://localhost:3000; + rewrite ^/grafana/(.*) /\$1 break; + proxy_set_header Host \$host; + } + location /grafana/api/live/ { + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection \$connection_upgrade; + proxy_set_header Host \$http_host; + proxy_pass http://grafana; + rewrite ^/grafana/(.*) /\$1 break; + } + + location /novnc/ { + proxy_pass http://127.0.0.1:6080/; + } + + location /novnc/websockify { + proxy_pass http://127.0.0.1:6080/; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host \$host; + } + + location / { + proxy_set_header X-Forwarded-For \$remote_addr; + proxy_set_header Host \$http_host; + proxy_pass "http://127.0.0.1:6666"; + chunked_transfer_encoding off; + proxy_buffering off; + } + +} +" >> /etc/nginx/sites-available/default +printf "\nNGINX configuration successfull\n" + +printf "\ncreating sqlite databases\n" +sqlite3 /app/MsRewards-Reborn/MsRewards.db "CREATE TABLE daily (id INTEGER PRIMARY KEY,compte TEXT,points int,date TEXT);" +sqlite3 /app/MsRewards-Reborn/MsRewards.db "CREATE TABLE comptes (id INTEGER PRIMARY KEY,compte TEXT,last_pts int, banned int);" + +printf "\nconfigurating grafana\n" + +cp /app/MsRewards-Reborn/config/grafana.ini /etc/grafana/ +grafana-cli plugins install frser-sqlite-datasource + +printf "setting up default dashboard" +cp /app/MsRewards-Reborn/config/Stats-dashbord.json /usr/share/grafana/public/dashboards/home.json + diff --git a/config/grafana.ini b/config/grafana.ini new file mode 100755 index 0000000..ea72204 --- /dev/null +++ b/config/grafana.ini @@ -0,0 +1,1469 @@ +##################### Grafana Configuration Example ##################### +# +# Everything has defaults so you only need to uncomment things you want to +# change + +# possible values : production, development +;app_mode = production + +# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty +;instance_name = ${HOSTNAME} + +# force migration will run migrations that might cause dataloss +;force_migration = false + +#################################### Paths #################################### +[paths] +# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) +;data = /var/lib/grafana + +# Temporary files in `data` directory older than given duration will be removed +;temp_data_lifetime = 24h + +# Directory where grafana can store logs +;logs = /var/log/grafana + +# Directory where grafana will automatically scan and look for plugins +;plugins = /var/lib/grafana/plugins + +# folder that contains provisioning config files that grafana will apply on startup and while running. +;provisioning = conf/provisioning + +#################################### Server #################################### +[server] +# Protocol (http, https, h2, socket) +;protocol = http + +# The ip address to bind to, empty will bind to all interfaces +;http_addr = + +# The http port to use +;http_port = 3000 + +# The public facing domain name used to access grafana from a browser +;domain = localhost + +# Redirect to correct domain if host header does not match domain +# Prevents DNS rebinding attacks +;enforce_domain = false + +# The full public facing url you use in browser, used for redirects and emails +# If you use reverse proxy and sub path specify full url (with sub path) +root_url = %(protocol)s://localhost:3000/grafana/ + +# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons. +;serve_from_sub_path = false + +# Log web requests +;router_logging = false + +# the path relative working path +;static_root_path = public + +# enable gzip +;enable_gzip = false + +# https certs & key file +;cert_file = +;cert_key = + +# Unix socket gid +# Changing the gid of a file without privileges requires that the target group is in the group of the process and that the process is the file owner +# It is recommended to set the gid as http server user gid +# Not set when the value is -1 +;socket_gid = + +# Unix socket mode +;socket_mode = + +# Unix socket path +;socket = + +# CDN Url +;cdn_url = + +# Sets the maximum time using a duration format (5s/5m/5ms) before timing out read of an incoming request and closing idle connections. +# `0` means there is no timeout for reading the request. +;read_timeout = 0 + +# This setting enables you to specify additional headers that the server adds to HTTP(S) responses. +[server.custom_response_headers] +#exampleHeader1 = exampleValue1 +#exampleHeader2 = exampleValue2 + +#################################### GRPC Server ######################### +;[grpc_server] +;network = "tcp" +;address = "127.0.0.1:10000" +;use_tls = false +;cert_file = +;key_file = + +#################################### Database #################################### +[database] +# You can configure the database connection by specifying type, host, name, user and password +# as separate properties or as on string using the url properties. + +# Either "mysql", "postgres" or "sqlite3", it's your choice +;type = sqlite3 +;host = 127.0.0.1:3306 +;name = grafana +;user = root +# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" +;password = + +# Use either URL or the previous fields to configure the database +# Example: mysql://user:secret@host:port/database +;url = + +# For "postgres", use either "disable", "require" or "verify-full" +# For "mysql", use either "true", "false", or "skip-verify". +;ssl_mode = disable + +# Database drivers may support different transaction isolation levels. +# Currently, only "mysql" driver supports isolation levels. +# If the value is empty - driver's default isolation level is applied. +# For "mysql" use "READ-UNCOMMITTED", "READ-COMMITTED", "REPEATABLE-READ" or "SERIALIZABLE". +;isolation_level = + +;ca_cert_path = +;client_key_path = +;client_cert_path = +;server_cert_name = + +# For "sqlite3" only, path relative to data_path setting +;path = grafana.db + +# Max idle conn setting default is 2 +;max_idle_conn = 2 + +# Max conn setting default is 0 (mean not set) +;max_open_conn = + +# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours) +;conn_max_lifetime = 14400 + +# Set to true to log the sql calls and execution times. +;log_queries = + +# For "sqlite3" only. cache mode setting used for connecting to the database. (private, shared) +;cache_mode = private + +# For "sqlite3" only. Enable/disable Write-Ahead Logging, https://sqlite.org/wal.html. Default is false. +;wal = false + +# For "mysql" only if migrationLocking feature toggle is set. How many seconds to wait before failing to lock the database for the migrations, default is 0. +;locking_attempt_timeout_sec = 0 + +# For "sqlite" only. How many times to retry query in case of database is locked failures. Default is 0 (disabled). +;query_retries = 0 + +# For "sqlite" only. How many times to retry transaction in case of database is locked failures. Default is 5. +;transaction_retries = 5 + +# Set to true to add metrics and tracing for database queries. +;instrument_queries = false + +################################### Data sources ######################### +[datasources] +# Upper limit of data sources that Grafana will return. This limit is a temporary configuration and it will be deprecated when pagination will be introduced on the list data sources API. +;datasource_limit = 5000 + +#################################### Cache server ############################# +[remote_cache] +# Either "redis", "memcached" or "database" default is "database" +;type = database + +# cache connectionstring options +# database: will use Grafana primary database. +# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'. +# memcache: 127.0.0.1:11211 +;connstr = + +# prefix prepended to all the keys in the remote cache +; prefix = + +# This enables encryption of values stored in the remote cache +;encryption = + +#################################### Data proxy ########################### +[dataproxy] + +# This enables data proxy logging, default is false +;logging = false + +# How long the data proxy waits to read the headers of the response before timing out, default is 30 seconds. +# This setting also applies to core backend HTTP data sources where query requests use an HTTP client with timeout set. +;timeout = 30 + +# How long the data proxy waits to establish a TCP connection before timing out, default is 10 seconds. +;dialTimeout = 10 + +# How many seconds the data proxy waits before sending a keepalive probe request. +;keep_alive_seconds = 30 + +# How many seconds the data proxy waits for a successful TLS Handshake before timing out. +;tls_handshake_timeout_seconds = 10 + +# How many seconds the data proxy will wait for a server's first response headers after +# fully writing the request headers if the request has an "Expect: 100-continue" +# header. A value of 0 will result in the body being sent immediately, without +# waiting for the server to approve. +;expect_continue_timeout_seconds = 1 + +# Optionally limits the total number of connections per host, including connections in the dialing, +# active, and idle states. On limit violation, dials will block. +# A value of zero (0) means no limit. +;max_conns_per_host = 0 + +# The maximum number of idle connections that Grafana will keep alive. +;max_idle_connections = 100 + +# How many seconds the data proxy keeps an idle connection open before timing out. +;idle_conn_timeout_seconds = 90 + +# If enabled and user is not anonymous, data proxy will add X-Grafana-User header with username into the request, default is false. +;send_user_header = false + +# Limit the amount of bytes that will be read/accepted from responses of outgoing HTTP requests. +;response_limit = 0 + +# Limits the number of rows that Grafana will process from SQL data sources. +;row_limit = 1000000 + +# Sets a custom value for the `User-Agent` header for outgoing data proxy requests. If empty, the default value is `Grafana/` (for example `Grafana/9.0.0`). +;user_agent = + +#################################### Analytics #################################### +[analytics] +# Server reporting, sends usage counters to stats.grafana.org every 24 hours. +# No ip addresses are being tracked, only simple counters to track +# running instances, dashboard and error counts. It is very helpful to us. +# Change this option to false to disable reporting. +;reporting_enabled = true + +# The name of the distributor of the Grafana instance. Ex hosted-grafana, grafana-labs +;reporting_distributor = grafana-labs + +# Set to false to disable all checks to https://grafana.com +# for new versions of grafana. The check is used +# in some UI views to notify that a grafana update exists. +# This option does not cause any auto updates, nor send any information +# only a GET request to https://raw.githubusercontent.com/grafana/grafana/main/latest.json to get the latest version. +;check_for_updates = true + +# Set to false to disable all checks to https://grafana.com +# for new versions of plugins. The check is used +# in some UI views to notify that a plugin update exists. +# This option does not cause any auto updates, nor send any information +# only a GET request to https://grafana.com to get the latest versions. +;check_for_plugin_updates = true + +# Google Analytics universal tracking code, only enabled if you specify an id here +;google_analytics_ua_id = + +# Google Analytics 4 tracking code, only enabled if you specify an id here +;google_analytics_4_id = + +# When Google Analytics 4 Enhanced event measurement is enabled, we will try to avoid sending duplicate events and let Google Analytics 4 detect navigation changes, etc. +;google_analytics_4_send_manual_page_views = false + +# Google Tag Manager ID, only enabled if you specify an id here +;google_tag_manager_id = + +# Rudderstack write key, enabled only if rudderstack_data_plane_url is also set +;rudderstack_write_key = + +# Rudderstack data plane url, enabled only if rudderstack_write_key is also set +;rudderstack_data_plane_url = + +# Rudderstack SDK url, optional, only valid if rudderstack_write_key and rudderstack_data_plane_url is also set +;rudderstack_sdk_url = + +# Rudderstack Config url, optional, used by Rudderstack SDK to fetch source config +;rudderstack_config_url = + +# Intercom secret, optional, used to hash user_id before passing to Intercom via Rudderstack +;intercom_secret = + +# Controls if the UI contains any links to user feedback forms +;feedback_links_enabled = true + +#################################### Security #################################### +[security] +# disable creation of admin user on first start of grafana +;disable_initial_admin_creation = false + +# default admin user, created on startup +;admin_user = admin + +# default admin password, can be changed before first start of grafana, or in profile settings +;admin_password = admin + +# default admin email, created on startup +;admin_email = admin@localhost + +# used for signing +;secret_key = SW2YcwTIb9zpOOhoPsMm + +# current key provider used for envelope encryption, default to static value specified by secret_key +;encryption_provider = secretKey.v1 + +# list of configured key providers, space separated (Enterprise only): e.g., awskms.v1 azurekv.v1 +;available_encryption_providers = + +# disable gravatar profile images +;disable_gravatar = false + +# data source proxy whitelist (ip_or_domain:port separated by spaces) +;data_source_proxy_whitelist = + +# disable protection against brute force login attempts +;disable_brute_force_login_protection = false + +# set to true if you host Grafana behind HTTPS. default is false. +;cookie_secure = false + +# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled" +;cookie_samesite = lax + +# set to true if you want to allow browsers to render Grafana in a ,