mirror of
				https://gitea.augustin64.fr/piair/MsRewards-Reborn.git
				synced 2025-10-31 14:23:53 +01:00 
			
		
		
		
	Compare commits
	
		
			41 Commits
		
	
	
		
			dev
			...
			9bd96ad876
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 9bd96ad876 | ||
|  | ed517e7b03 | ||
|  | 01416c0e11 | ||
|  | 19cf77c6bd | ||
|  | feb7834d1f | ||
|  | 1ea1ff776e | ||
|  | ae023688a4 | ||
|  | fcb40537dc | ||
|  | d7d2f49a3f | ||
|  | 68b5de32d6 | ||
|  | c5e9fb1267 | ||
|  | 6ade73617a | ||
|  | 37e8f6f61b | ||
|  | db6fa9b6b0 | ||
|  | d6988c03b4 | ||
|  | 449d2da410 | ||
|  | 3eb193eca3 | ||
|  | f566b2eeda | ||
|  | 52e88f81b9 | ||
|  | 1a8137783c | ||
|  | 6f13b2532d | ||
|  | 3978c44bbc | ||
|  | 49dc53ed32 | ||
|  | ba66a96c65 | ||
|  | db157771de | ||
|  | cbd1ad93a6 | ||
|  | afabd94f0d | ||
|  | 81deaf05b0 | ||
|  | 9af0f4aadb | ||
|  | 1d16294c04 | ||
|  | fae2033061 | ||
|  | 50c4036c73 | ||
|  | c683472895 | ||
|  | 178f2d472a | ||
|  | d3137f858a | ||
|  | d2ad467d4e | ||
|  | b45e9e549f | ||
|  | 200b0d8a86 | ||
|  | 4a5af6455d | ||
|  | 49b691d736 | ||
|  | 9549a6dea3 | 
							
								
								
									
										2
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| **/.venv | ||||
| user_data/* | ||||
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -3,18 +3,16 @@ geckodriver.log | ||||
| .vscode/ | ||||
| .idea | ||||
| venv | ||||
| **/.venv | ||||
| /Git | ||||
| page.html | ||||
| screenshot.png | ||||
| login.csv | ||||
| data | ||||
| **/__pycache__ | ||||
| user_data/* | ||||
| install.sh | ||||
| nohup.out | ||||
| points.csv | ||||
| file.png | ||||
| user_data/configs.json | ||||
| *.ts | ||||
| LICENSE | ||||
| README.md | ||||
| README.md | ||||
							
								
								
									
										53
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -1,22 +1,43 @@ | ||||
| FROM python:3.10 | ||||
| ENV DEBIAN_FRONTEND noninteractive | ||||
| 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 \ | ||||
|     && 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"     | ||||
|     && apt install -y redis grafana | ||||
|  | ||||
| # Configure Grafana | ||||
| RUN grafana-cli plugins install frser-sqlite-datasource | ||||
|  | ||||
| COPY requirements.txt /app/requirements.txt | ||||
| RUN python3 -m pip install -r requirements.txt | ||||
|  | ||||
| # Setup app | ||||
| RUN git clone https://gitea.augustin64.fr/piair/MsRewards-Reborn | ||||
| # Use this instead when developping locally: | ||||
| # COPY . /app/MsRewards-Reborn | ||||
|  | ||||
| RUN bash MsRewards-Reborn/config/config.sh | ||||
|  | ||||
| ENV TZ="Europe/Paris" | ||||
| WORKDIR /app/MsRewards-Reborn/Flask/ | ||||
| CMD bash start.sh | ||||
|   | ||||
							
								
								
									
										101
									
								
								Flask/app.py
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								Flask/app.py
									
									
									
									
									
								
							| @@ -13,11 +13,27 @@ import re | ||||
| from requests import get | ||||
| 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 | ||||
| pool = redis.ConnectionPool(host='localhost', port=6379, db=0) | ||||
| r = redis.Redis(connection_pool=pool) | ||||
|  | ||||
| def get_path(path): | ||||
|     return os.path.join(APP_ROOT, path) | ||||
|  | ||||
| def generate_output(): | ||||
|     pubsub = r.pubsub() | ||||
|     pubsub.subscribe('console') | ||||
| @@ -32,7 +48,7 @@ def generate_output(): | ||||
| # the end | ||||
|  | ||||
| global password | ||||
| with open("/app/MsRewards-Reborn/user_data/flask.json", "r") as inFile: | ||||
| with open(get_path("user_data/flask.json"), "r") as inFile: | ||||
|     data = json.load(inFile) | ||||
|  | ||||
| password = data["password"] | ||||
| @@ -40,7 +56,7 @@ secret = data["secret"] | ||||
| if secret == "": | ||||
|     import secrets | ||||
|     secret = secrets.token_hex() | ||||
|     with open("/app/MsRewards-Reborn/user_data/flask.json", "w") as inFile: | ||||
|     with open(get_path("user_data/flask.json"), "w") as inFile: | ||||
|         data = { | ||||
|             "password": password, | ||||
|             "secret": secret | ||||
| @@ -70,14 +86,14 @@ scheduler.add_job(                  # on relance le job | ||||
|  | ||||
| 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 = open(get_path(f"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) | ||||
|     log.close() | ||||
|  | ||||
|  | ||||
| TriggerDict = {} | ||||
| def update_jobs(): | ||||
|     with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: | ||||
|     with open(get_path("user_data/configs.json"), "r") as inFile: | ||||
|         configs = json.load(inFile) | ||||
|     for i in configs: | ||||
|         try :  | ||||
| @@ -120,7 +136,7 @@ app = Flask(__name__) | ||||
|  | ||||
| @app.context_processor | ||||
| def inject_default_variables(): | ||||
|     with open("/app/MsRewards-Reborn/version", "r") as f: | ||||
|     with open(get_path("version"), "r") as f: | ||||
|         version = f.readline().replace("\n", '') | ||||
|     return dict(version=version) | ||||
| """ | ||||
| @@ -175,7 +191,7 @@ def change_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: | ||||
|         with open(get_path("user_data/flask.json"), "w") as inFile: | ||||
|             data = { | ||||
|                 "password": password,  | ||||
|                 "secret": secret | ||||
| @@ -201,21 +217,21 @@ def load_user(userid): | ||||
|  | ||||
| @app.route("/") | ||||
| def main(): | ||||
|     with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: | ||||
|     with open(get_path("user_data/configs.json"), "r") as inFile: | ||||
|         configs = json.load(inFile) | ||||
|     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: | ||||
|     with open(get_path("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: | ||||
|     with open(get_path("user_data/discord.json"), "r") as inFile: | ||||
|         data = json.load(inFile)     | ||||
|     action = request.form | ||||
|     if action['DISCORD'] == "delete" : | ||||
| @@ -237,7 +253,7 @@ def discord_post(): | ||||
|         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: | ||||
|     with open(get_path("user_data/discord.json"), "w") as outFile: | ||||
|         json.dump(data, outFile) | ||||
|     return(render_template("discord.html", data=data, len=maxi(data))) | ||||
|  | ||||
| @@ -249,7 +265,7 @@ def dev2(): | ||||
|  | ||||
| @app.route("/settings/") | ||||
| def settings_get(): | ||||
|     with open("/app/MsRewards-Reborn/user_data/settings.json", "r") as inFile: | ||||
|     with open(get_path("user_data/settings.json"), "r") as inFile: | ||||
|         settings = json.load(inFile) | ||||
|     return(render_template("settings.html", data=settings)) | ||||
|  | ||||
| @@ -259,21 +275,21 @@ def settings_post(): | ||||
|     settings = {} | ||||
|     action = request.form | ||||
|     settings['avatarlink'] = action["avatarlink"] | ||||
|     with open("/app/MsRewards-Reborn/user_data/settings.json", "w") as inFile: | ||||
|     with open(get_path("user_data/settings.json"), "w") as inFile: | ||||
|         json.dump(settings, inFile) | ||||
|     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: | ||||
|     with open(get_path("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: | ||||
|     with open(get_path("user_data/proxy.json"), "r") as inFile: | ||||
|         data = json.load(inFile)     | ||||
|     action = request.form | ||||
|     print(action) | ||||
| @@ -290,21 +306,21 @@ def proxy_post(): | ||||
|         except : | ||||
|             print("error : probably bad config") | ||||
|  | ||||
|     with open("/app/MsRewards-Reborn/user_data/proxy.json", "w") as outFile: | ||||
|     with open(get_path("user_data/proxy.json"), "w") as outFile: | ||||
|         json.dump(data, outFile) | ||||
|     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: | ||||
|     with open(get_path("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: | ||||
|     with open(get_path("user_data/configs.json"), "r") as inFile: | ||||
|         configs = json.load(inFile) | ||||
|  | ||||
|     data = dict(request.form) | ||||
| @@ -318,7 +334,7 @@ def schedule_post(): | ||||
|         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: | ||||
|     with open(get_path("user_data/configs.json"), "w") as inFile: | ||||
|         json.dump(configs, inFile) | ||||
|     update_jobs() | ||||
|     return(render_template("schedule.html", data=configs)) | ||||
| @@ -326,11 +342,11 @@ def schedule_post(): | ||||
|  | ||||
| @app.route("/config/") | ||||
| def config_get(): | ||||
|     with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile: | ||||
|     with open(get_path("user_data/proxy.json"), "r") as inFile: | ||||
|         proxys = json.load(inFile) | ||||
|     with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile: | ||||
|     with open(get_path("user_data/discord.json"), "r") as inFile: | ||||
|         discords = json.load(inFile) | ||||
|     with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: | ||||
|     with open(get_path("user_data/configs.json"), "r") as inFile: | ||||
|         configs = json.load(inFile) | ||||
|     return(render_template("config.html", data=configs, discords=discords, proxys=proxys, configs=configs, len=maxi(configs))) | ||||
|  | ||||
| @@ -338,11 +354,11 @@ def config_get(): | ||||
| @app.route("/config/", methods=["POST"]) | ||||
| def config_post(): | ||||
|     action = request.form | ||||
|     with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile: | ||||
|     with open(get_path("user_data/proxy.json"), "r") as inFile: | ||||
|         proxys = json.load(inFile) | ||||
|     with open("/app/MsRewards-Reborn/user_data/discord.json", "r") as inFile: | ||||
|     with open(get_path("user_data/discord.json"), "r") as inFile: | ||||
|         discords = json.load(inFile) | ||||
|     with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile: | ||||
|     with open(get_path("user_data/configs.json"), "r") as inFile: | ||||
|         configs = json.load(inFile) | ||||
|  | ||||
|     if action["data"] == "delete": | ||||
| @@ -365,16 +381,26 @@ def config_post(): | ||||
|             "enabled":"False", | ||||
|             "accounts": comptes | ||||
|             } | ||||
|     with open("/app/MsRewards-Reborn/user_data/configs.json", "w") as outFile: | ||||
|     with open(get_path("user_data/configs.json"), "w") as outFile: | ||||
|         json.dump(configs, outFile) | ||||
|     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: | ||||
|     with open(get_path("user_data/configs.json"), "r") as inFile: | ||||
|         configs = json.load(inFile) | ||||
|     print(configs) | ||||
|     return(render_template("logs.html", data=configs)) | ||||
|      | ||||
|     files = [(configs[i]["name"], i) for i in configs] | ||||
|     config_files = [i[1] for i in files] | ||||
|     for f in os.listdir(get_path("Flask/static/logs")): | ||||
|         fid = ".".join(f.split(".")[:-1]) # filename without .txt | ||||
|         if f != ".gitignore" and fid not in config_files: | ||||
|             files.append((f, fid)) | ||||
|  | ||||
|     return render_template( | ||||
|         "logs.html", | ||||
|         files=files | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @app.route("/stats/", methods=["GET", "POST"]) | ||||
| @@ -385,21 +411,21 @@ def stats(): | ||||
| @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 = open(get_path("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) | ||||
|     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: | ||||
|     with open(get_path("user_data/configs.json"), "r") as inFile: | ||||
|         configs = json.load(inFile) | ||||
|     return(render_template("vnc_get.html", configs=configs)) | ||||
|  | ||||
| @app.route('/download/<path:filename>', methods=['GET', 'POST']) | ||||
| @login_required | ||||
| def download(filename): | ||||
|     return send_from_directory(directory='/app/MsRewards-Reborn/user_data/', path=filename, as_attachment=True) | ||||
|     return send_from_directory(directory=get_path("user_data/"), path=filename, as_attachment=True) | ||||
|  | ||||
|  | ||||
| def allowed_file(filename): | ||||
| @@ -420,8 +446,8 @@ def upload_file(): | ||||
|  | ||||
|         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)) | ||||
|             print(os.path.join(get_path("user_data/"), filename)) | ||||
|             file.save(os.path.join(get_path("user_data/"), filename)) | ||||
|          | ||||
|         i += 1 | ||||
|     print(i) | ||||
| @@ -439,4 +465,7 @@ def maxi(dict): | ||||
|  | ||||
|  | ||||
| update_jobs() | ||||
| subprocess.Popen(["bash",'/app/MsRewards-Reborn/config/request.sh']) | ||||
| subprocess.Popen(["bash", get_path("config/request.sh")]) | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     app.run() | ||||
|   | ||||
							
								
								
									
										17
									
								
								Flask/static/css/vnc-post.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Flask/static/css/vnc-post.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| #console { | ||||
|     height: 100%; | ||||
|     width: 20%; | ||||
|     float: left; | ||||
|     resize: horizontal; | ||||
|     overflow: auto; | ||||
| } | ||||
|  | ||||
| #vnc-container { | ||||
|     height: 100%; | ||||
|     width: 80%; | ||||
|     float: left; | ||||
| } | ||||
|  | ||||
| .container { | ||||
|     height: 100%; | ||||
| } | ||||
| @@ -8,14 +8,37 @@ | ||||
|  | ||||
| <select name="select" onchange="change_logs(this.value)"> | ||||
|     <option id="null" value="0">Choisir une config</option> | ||||
|     {% for i in data %} | ||||
|     <option id="{{data[i]['name']}}" value="{{i}}">{{data[i]['name']}}</option> | ||||
|     {% for file in files %} | ||||
|     <option id="{{ file[0] }}" value="{{ file[1] }}">{{ file[0] }}</option> | ||||
|     {% endfor %} | ||||
| </select> | ||||
| <br><br> | ||||
| <iframe type="text/html" src="{{url_for('static', filename='logs/1.txt')}}"  width="100%" height="85%" id="embed"> | ||||
| <iframe type="text/html" src="{{url_for('static', filename='logs/1.txt')}}"  width="100%" height="85%" id="embed"></iframe> | ||||
|  | ||||
| <script defer> | ||||
|     const iframe = document.getElementsByTagName("iframe")[0]; | ||||
|     var script = document.createElement('script'); | ||||
|  | ||||
|     // Wait until ansi_up load | ||||
|     script.onload = function () { | ||||
|         // Wait until iframe load | ||||
|         iframe.onload = function() { | ||||
|             const subdoc = iframe.contentWindow.document; | ||||
|             const subBody = subdoc.getElementsByTagName("body")[0] | ||||
|             let ansiOutput = subBody; | ||||
|             // Depending on the content encoding (and maybe on the browser) | ||||
|             // a <pre> is added around the content of the file | ||||
|             if (subBody.getElementsByTagName("pre").length > 0) { | ||||
|                 ansiOutput = subBody.getElementsByTagName("pre")[0]; | ||||
|             } | ||||
|             const ansi_up = new AnsiUp(); | ||||
|             ansiOutput.innerHTML = ansi_up.ansi_to_html(ansiOutput.innerText); | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     script.src = "https://cdn.jsdelivr.net/npm/ansi_up@4.0.4/ansi_up.js"; | ||||
|     document.head.appendChild(script); | ||||
| </script> | ||||
|  | ||||
| {% endif %} | ||||
| {% endblock %} | ||||
| @@ -6,19 +6,19 @@ | ||||
|     {% if not current_user.is_authenticated %} | ||||
|         <button class="unselected" onclick="location.href = '/login';">login</button> | ||||
|     {% 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') }}"/> | ||||
|         <script src="{{ url_for('static', filename='node_modules/xterm/lib/xterm.js') }}"></script> | ||||
|         <script src="{{ url_for('static', filename='node_modules/xterm-addon-fit/lib/xterm-addon-fit.js') }}"></script> | ||||
|         <script> | ||||
|             document.getElementsByClassName("content")[0].style.padding = "0 0" | ||||
|         </script> | ||||
|         <div style="height: 100%"> | ||||
|  | ||||
|             <div id="console" style="height: 100%; width: 20%; float:left;"></div> | ||||
|  | ||||
|             <div style="height: 100%; width: 80%; float:left;"> | ||||
|         <div class="container"> | ||||
|             <div id="console"></div> | ||||
|             <div id="vnc-container"> | ||||
|                 <iframe src="/novnc/vnc.html?resize=scale&path=novnc/websockify&autoconnect=true&view_only" | ||||
|                         width="100%" height="100%" frameborder="0"></iframe> | ||||
|                         width="100%" height="100%" frameborder="0"> | ||||
|                 </iframe> | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|   | ||||
							
								
								
									
										187
									
								
								V6.py
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								V6.py
									
									
									
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| #!/usr/bin/python3.10 | ||||
| #!/usr/bin/python3 | ||||
| from modules.Classes.Config import Config | ||||
| from modules.Classes.DiscordLogger import DiscordLogger | ||||
| from modules.Classes.UserCredentials import UserCredentials | ||||
| @@ -9,6 +9,8 @@ from modules.db import add_to_database | ||||
| from modules.driver_tools import * | ||||
| from modules.error import * | ||||
|  | ||||
| import random | ||||
|  | ||||
|  | ||||
| # create a webdriver  | ||||
| def create_driver(mobile=False): | ||||
| @@ -22,16 +24,33 @@ def create_driver(mobile=False): | ||||
|         "AppleWebKit/537.36 (KHTML, like Gecko)" | ||||
|         "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: | ||||
|         chrome_options.add_argument(f"--user-agent={mobile_user_agent}") | ||||
|         arguments.append(f"--user-agent={mobile_user_agent}") | ||||
|     else: | ||||
|         chrome_options.add_argument(f"--user-agent={pc_user_agent}") | ||||
|         arguments.append(f"--user-agent={pc_user_agent}") | ||||
|  | ||||
|     # disabled as it may cause detection | ||||
|     if config.proxy.is_enabled(): | ||||
|         chrome_options.add_argument(f'--proxy-server={config.proxy.ip}:{config.proxy.port}') | ||||
|         arguments.append(f'--proxy-server={config.proxy.ip}:{config.proxy.port}') | ||||
|  | ||||
|     chrome_options = webdriver.ChromeOptions() | ||||
|     for arg in arguments: | ||||
|         chrome_options.add_argument(arg) | ||||
|  | ||||
|     driver = uc.Chrome(options=chrome_options) | ||||
|     set_language(driver) | ||||
|     return driver | ||||
|  | ||||
|  | ||||
| @@ -152,7 +171,7 @@ def play_quiz4(override: int = None): | ||||
|     except Exception as e: | ||||
|         log_error(e) | ||||
|         raise ValueError(e) | ||||
|     info("Quiz 8 done.") | ||||
|     info("Quiz 4 done.") | ||||
|     custom_sleep(3) | ||||
|  | ||||
|  | ||||
| @@ -165,6 +184,8 @@ def do_poll(): | ||||
|         try: | ||||
|             answer_elem.click() | ||||
|         except exceptions.ElementNotInteractableException: | ||||
|             warning("element not clickable. Waiting a bit and retrying.") | ||||
|             custom_sleep(uniform(2, 2.5)) | ||||
|             driver.execute_script("arguments[0].click();", answer_elem) | ||||
|         custom_sleep(uniform(2, 2.5)) | ||||
|     except Exception as err: | ||||
| @@ -177,6 +198,27 @@ def do_poll(): | ||||
| # Find each playable card and tries to click on it to earn points | ||||
| def all_cards(): | ||||
|     driver = config.WebDriver.driver | ||||
|  | ||||
|     def check_welcome_tour() -> bool: | ||||
|         if "rewards.bing.com/welcometour" not in driver.current_url: | ||||
|             return False | ||||
|  | ||||
|         info("Popup 'Explorer le programme' reçue") | ||||
|         wait_until_visible(By.ID, "welcome-tour", timeout=5, browser=driver) | ||||
|         custom_sleep(1.5) | ||||
|  | ||||
|         welcome_tour = driver.find_element(By.ID, "welcome-tour") | ||||
|         interest_button_box = welcome_tour.find_element(By.CLASS_NAME, "interest-buttons") | ||||
|         interests = interest_button_box.find_elements(By.CLASS_NAME, "ng-binding") | ||||
|         debug("Got the following interests: "+str(interests)) | ||||
|         random.choice(interests).click() # Choose interest | ||||
|         custom_sleep(1.5) | ||||
|  | ||||
|         claim_button = welcome_tour.find_element(By.ID, "claim-button") | ||||
|         claim_button.click() # submit | ||||
|         custom_sleep(1.5) | ||||
|         return True | ||||
|  | ||||
|     driver.get("https://rewards.bing.com") | ||||
|     wait_until_visible(By.CLASS_NAME, "c-card-content", 10, driver) | ||||
|  | ||||
| @@ -190,7 +232,7 @@ def all_cards(): | ||||
|         info("no promo card") | ||||
|  | ||||
|     if len(card_list) < 10:  # most likely an error during loading | ||||
|         if "suspendu" in driver.page_source: | ||||
|         if "suspendu" in driver.page_source or "suspended" in driver.page_source: | ||||
|             raise Banned() | ||||
|         driver.refresh() | ||||
|         card_list = driver.find_elements(By.CLASS_NAME, "c-card-content") | ||||
| @@ -220,39 +262,47 @@ def all_cards(): | ||||
|                 if i == len(card_list) and i > 15: | ||||
|                     checked = False | ||||
|  | ||||
|         if checked: | ||||
|             custom_sleep(1.5) | ||||
|         if not checked: | ||||
|             continue | ||||
|  | ||||
|         custom_sleep(1.5) | ||||
|         check_welcome_tour() | ||||
|         driver.execute_script("arguments[0].scrollIntoView();", card_list[i]) | ||||
|         custom_sleep(1.5) | ||||
|         card_list[i].click() | ||||
|  | ||||
|         if len(driver.window_handles) > 1: | ||||
|             driver.switch_to.window(driver.window_handles[1]) | ||||
|             try_play(driver.title) | ||||
|             close_tab(driver.window_handles[1]) | ||||
|  | ||||
|         try: | ||||
|             driver.refresh() | ||||
|             card_list = driver.find_elements(By.CLASS_NAME, "c-card-content") | ||||
|  | ||||
|             if "mee-icon-AddMedium" not in card_list[i].get_attribute("innerHTML"): | ||||
|                 continue | ||||
|  | ||||
|             check_welcome_tour() | ||||
|             driver.execute_script("arguments[0].scrollIntoView();", card_list[i]) | ||||
|             custom_sleep(1.5) | ||||
|             card_list[i].click() | ||||
|  | ||||
|             if len(driver.window_handles) > 1: | ||||
|                 driver.switch_to.window(driver.window_handles[1]) | ||||
|                 try_play(driver.title) | ||||
|                 close_tab(driver.window_handles[1]) | ||||
|  | ||||
|             driver.switch_to.window(driver.window_handles[1]) | ||||
|             custom_sleep(10) | ||||
|             log_error(f"Card {i} Can't be completed. Why MS ?") | ||||
|             try: | ||||
|                 driver.refresh() | ||||
|                 card_list = driver.find_elements(By.CLASS_NAME, "c-card-content") | ||||
|                 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") | ||||
|  | ||||
|                 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) | ||||
|         except Exception as err: | ||||
|             log_error(err) | ||||
|         custom_sleep(3) | ||||
|  | ||||
|  | ||||
| def promo(): | ||||
| @@ -360,22 +410,35 @@ def login_part_1(): | ||||
|     driver = config.WebDriver.driver | ||||
|     driver.get("https://login.live.com") | ||||
|     wait_until_visible(By.ID, "i0116", browser=driver) | ||||
|     mail_elem = driver.find_element(By.ID, "i0116") | ||||
|     send_keys_wait(mail_elem, config.UserCredentials.get_mail()) | ||||
|     mail_elem.send_keys(Keys.ENTER) | ||||
|     send_wait_and_confirm( | ||||
|         driver.find_element(By.ID, "i0116"), | ||||
|         config.UserCredentials.get_mail() | ||||
|     ) | ||||
|     wait_until_visible(By.ID, "i0118", browser=driver) | ||||
|     pwd_elem = driver.find_element(By.ID, "i0118") | ||||
|     send_keys_wait(pwd_elem, config.UserCredentials.get_password()) | ||||
|     pwd_elem.send_keys(Keys.ENTER) | ||||
|     custom_sleep(2) | ||||
|     send_wait_and_confirm( | ||||
|         driver.find_element(By.ID, "i0118"), | ||||
|         config.UserCredentials.get_password() | ||||
|     ) | ||||
|     # 2FA | ||||
|     if "Entrez le code de sécurité" in driver.page_source: | ||||
|         try: | ||||
|             a2f_elem = driver.find_element(By.ID, "idTxtBx_SAOTCC_OTC") | ||||
|             a2f_elem.send_keys(config.UserCredentials.get_tfa().now()) | ||||
|             a2f_elem.send_keys(Keys.ENTER) | ||||
|         except Exception as err: | ||||
|             log_error(err) | ||||
|     try: | ||||
|         if not wait_until_visible(By.ID, "idTxtBx_SAOTCC_OTC", browser=driver, timeout=5, raise_error=False): | ||||
|             custom_sleep(2) | ||||
|             return | ||||
|  | ||||
|         tfa = config.UserCredentials.get_tfa() | ||||
|         if tfa is None: | ||||
|             error("2FA needed but no code available for this account, sending error") | ||||
|             raise ValueError("2FA needed but no code available for this account") | ||||
|         else: | ||||
|             a2f_code = tfa.now() | ||||
|  | ||||
|         info(f"Need 2FA, I have code: {a2f_code}") | ||||
|         send_wait_and_confirm( | ||||
|             driver.find_element(By.ID, "idTxtBx_SAOTCC_OTC"), | ||||
|             a2f_code | ||||
|         ) | ||||
|     except Exception as err: | ||||
|         log_error(err) | ||||
|  | ||||
|  | ||||
| # Accept all cookies question, and check if the account is locked | ||||
| @@ -410,9 +473,17 @@ def login_part_2(): | ||||
| # login() tries to login to your Microsoft account. | ||||
| # it uses global variable g._mail and g._password to login | ||||
| def login(): | ||||
|     def logged_in(): | ||||
|         driver.get("https://login.live.com") | ||||
|         custom_sleep(10) | ||||
|         if get_domain(driver) == "account.microsoft.com": | ||||
|             return True | ||||
|         return False | ||||
|  | ||||
|     driver = config.WebDriver.driver | ||||
|     try: | ||||
|         login_part_1() | ||||
|         if not logged_in(): | ||||
|             login_part_1() | ||||
|         login_part_2() | ||||
|         driver.get("https://rewards.bing.com/") | ||||
|     except Banned: | ||||
| @@ -610,9 +681,11 @@ def daily_routine(cred: UserCredentials, custom=False): | ||||
|     try: | ||||
|         if not custom:  # custom already is logged in | ||||
|             login() | ||||
|  | ||||
|     except Banned: | ||||
|         log_error("This account is locked.") | ||||
|         return | ||||
|         raise Banned() | ||||
|  | ||||
|     except Identity: | ||||
|         log_error("This account has an issue.") | ||||
|         return | ||||
| @@ -666,7 +739,7 @@ def json_start(json_entry, cred: UserCredentials): | ||||
|             config.WebDriver.switch_to_driver("PC") | ||||
|             driver = config.WebDriver.driver | ||||
|             try: | ||||
|                 if str(account_id) in json_entry["unban"]: | ||||
|                 if "unban" in json_entry and str(account_id) in json_entry["unban"]: | ||||
|                     login_part_1() | ||||
|                     info("\nGO TO example.com TO PROCEED or wait 1200 secs.") | ||||
|                     for _ in range(1200): | ||||
| @@ -680,7 +753,7 @@ def json_start(json_entry, cred: UserCredentials): | ||||
|                 login() | ||||
|             try: | ||||
|                 if str(account_id) in json_entry["tout"]: | ||||
|                     daily_routine(cred) | ||||
|                     daily_routine(cred, True) | ||||
|             except KeyError: | ||||
|                 pass | ||||
|             else: | ||||
| @@ -744,12 +817,16 @@ def default_start(): | ||||
|             config.WebDriver.pc_driver.quit() | ||||
|             config.display.stop() | ||||
|             break | ||||
|         except Banned: | ||||
|             warning("this account is banned. Switching to next account") | ||||
|         except Exception as e: | ||||
|             log_error(f"Error not caught. Skipping this account. " + format_error(e)) | ||||
|             critical(f"Error not caught. Skipping this account. {e}") | ||||
|             config.WebDriver.pc_driver.quit() | ||||
|  | ||||
|         config.UserCredentials.next_account() | ||||
|         finally: | ||||
|             config.UserCredentials.next_account() | ||||
|  | ||||
|     config.display.stop() | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										13
									
								
								build.sh
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								build.sh
									
									
									
									
									
								
							| @@ -1 +1,12 @@ | ||||
| sudo docker build --no-cache --network host -t msrewards . && sudo docker run -d --restart unless-stopped -p 1234:1234 -p 2345:2345 -ti --shm-size=2gb --name MsRewards msrewards  | ||||
| #!/bin/bash | ||||
|  | ||||
| docker-do () { # Check if sudo needs to be used | ||||
|     if id -nG "$(whoami)" | grep -qw "docker"; then | ||||
|         docker $@ | ||||
|     else | ||||
|         sudo docker $@ | ||||
|     fi | ||||
| } | ||||
|  | ||||
| docker-do build --network host -t msrewards . | ||||
| docker-do run -d --restart unless-stopped -p 1234:1234 -p 2345:2345 -ti --shm-size=2gb --name MsRewards msrewards  | ||||
|   | ||||
							
								
								
									
										13
									
								
								clean.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										13
									
								
								clean.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| docker-do () { # Check if sudo needs to be used | ||||
|     if id -nG "$(whoami)" | grep -qw "docker"; then | ||||
|         docker $@ | ||||
|     else | ||||
|         sudo docker $@ | ||||
|     fi | ||||
| } | ||||
|  | ||||
| docker-do stop MsRewards | ||||
| docker-do rm MsRewards | ||||
| docker-do image rm msrewards | ||||
| @@ -46,6 +46,7 @@ server { | ||||
|         proxy_pass         "http://127.0.0.1:6666"; | ||||
|         chunked_transfer_encoding off; | ||||
|         proxy_buffering off; | ||||
|         add_header X-Accel-Buffering no; | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -59,7 +60,6 @@ sqlite3 /app/MsRewards-Reborn/MsRewards.db "CREATE TABLE comptes (id INTEGER PRI | ||||
| printf  "\nconfigurating grafana\n" | ||||
|  | ||||
| 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  | ||||
|   | ||||
							
								
								
									
										1459
									
								
								config/grafana.ini
									
									
									
									
									
								
							
							
						
						
									
										1459
									
								
								config/grafana.ini
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -50,9 +50,14 @@ class Config: | ||||
|         """ | ||||
|         self.discord = DiscordConfig() | ||||
|         self.discord.avatar_url = settings["avatarlink"] | ||||
|         self.discord.wh_link = discord[config[args.config]["discord"]]["errorsL"] | ||||
|  | ||||
|         if self.discord.wh_link != "": | ||||
|         if ( | ||||
|             "discord" in config[args.config] | ||||
|             and config[args.config]["discord"] in discord | ||||
|             and "errorsL" in discord[config[args.config]["discord"]] | ||||
|             and discord[config[args.config]["discord"]]["errorsL"] != "" | ||||
|             ): | ||||
|             self.discord.wh_link = discord[config[args.config]["discord"]]["errorsL"] | ||||
|             self.discord.wh = Webhook.from_url(self.discord.wh_link, adapter=RequestsWebhookAdapter()) | ||||
|         else: | ||||
|             self.discord.wh = FakeWebHook() | ||||
|   | ||||
| @@ -9,6 +9,6 @@ class DiscordConfig: | ||||
|  | ||||
|  | ||||
| class FakeWebHook: | ||||
|     def send(self, *args): | ||||
|         debug(f"Used a webhook call without webhook url with {args}") | ||||
|     def send(self, *args, **kwargs): | ||||
|         debug(f"Used a webhook call without webhook url with {args} {kwargs}") | ||||
|          | ||||
|   | ||||
| @@ -30,7 +30,7 @@ class DiscordLogger: | ||||
|         ) | ||||
|         file = File("screenshot.png") | ||||
|         embed.set_image(url="attachment://screenshot.png") | ||||
|         embed.set_footer(text=self.config.UserCredentials.creds.get_mail()) | ||||
|         embed.set_footer(text=self.config.UserCredentials.get_mail() + " - " + self.config.WebDriver.current_driver()) | ||||
|  | ||||
|         self.config.discord.wh.send(embed=embed, username="error", file=file, avatar_url=self.config.discord.avatar_url) | ||||
|         self.config.discord.wh.send(username="error", file=File("page.html"), avatar_url=self.config.discord.avatar_url) | ||||
|   | ||||
| @@ -11,12 +11,15 @@ class Driver: | ||||
|         self.mobile_driver = mobile_driver | ||||
|  | ||||
|     def switch_to_driver(self, driver: str): | ||||
|         match driver: | ||||
|             case "pc" | "PC" | "Pc": | ||||
|         match driver.lower(): | ||||
|             case "pc": | ||||
|                 self.driver = self.pc_driver | ||||
|  | ||||
|             case "mobile" | "Mobile": | ||||
|             case "mobile": | ||||
|                 self.driver = self.mobile_driver | ||||
|  | ||||
|             case _: | ||||
|                 raise ValueError("The driver must be either pc or mobile") | ||||
|  | ||||
|     def current_driver(self): | ||||
|         return "PC" if self.pc_driver == self.driver else "Mobile" | ||||
|   | ||||
| @@ -30,8 +30,9 @@ class UserCredentials: | ||||
|  | ||||
|     def get_tfa(self): | ||||
|         if not self.tfa_enable(): | ||||
|             warning("Warning: TFA is not enabled. Calling get_tfa is an expected behaviour.") | ||||
|         return TOTP(self.data[self.current]["tfa"]) | ||||
|             warning("Warning: TFA is not enabled. Can't get a TFA code.") | ||||
|             return None | ||||
|         return TOTP(self.data[self.current]["2fa"]) | ||||
|  | ||||
|     def next_account(self): | ||||
|         self.current += 1 | ||||
| @@ -41,4 +42,5 @@ class UserCredentials: | ||||
|             debug("No new credentials.") | ||||
|  | ||||
|     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) | ||||
|   | ||||
							
								
								
									
										8
									
								
								modules/Tools/generate_error.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								modules/Tools/generate_error.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| import undetected_chromedriver as uc | ||||
| from pyvirtualdisplay.smartdisplay import SmartDisplay | ||||
|  | ||||
| display = SmartDisplay(size=(1920, 1080)) | ||||
| display.start() | ||||
| driver = uc.Chrome() | ||||
| driver.close() | ||||
| driver.close() | ||||
| @@ -30,7 +30,7 @@ class ColoredFormatter(logging.Formatter): | ||||
|  | ||||
| # Set up the root logger | ||||
| root_logger = logging.getLogger(__name__) | ||||
| root_logger.setLevel(logging.DEBUG) | ||||
| root_logger.setLevel(logging.INFO) | ||||
|  | ||||
| # Create a console handler and set the formatter | ||||
| ch = logging.StreamHandler() | ||||
|   | ||||
| @@ -10,7 +10,7 @@ def get_domain(driver): | ||||
|  | ||||
| def custom_sleep(temps): | ||||
|     try: | ||||
|         if True:  # todo: change this awful condition | ||||
|         if False:  # todo: change this awful condition | ||||
|             points = ["⢿", "⣻", "⣽", "⣾", "⣷", "⣯", "⣟", "⡿"] | ||||
|             passe = 0 | ||||
|             for _ in range(int(temps)): | ||||
|   | ||||
							
								
								
									
										48
									
								
								modules/Tools/update_chrome.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								modules/Tools/update_chrome.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| import requests | ||||
| import re | ||||
| from packaging import version | ||||
| import subprocess | ||||
|  | ||||
| from logger import critical, info, error | ||||
|  | ||||
| errorMessage = subprocess.run(['python3', 'generate_error.py'], check=False, stdout=subprocess.PIPE, | ||||
|                               stderr=subprocess.PIPE).stderr.decode("utf-8") | ||||
|  | ||||
| versionPattern = "This version of ChromeDriver only supports Chrome version ([0-9]+)" | ||||
|  | ||||
| try: | ||||
|     versionN = re.search(versionPattern, errorMessage)[1] | ||||
| except Exception as e: | ||||
|     critical("Can't get version number from error") | ||||
|     error(e) | ||||
|     exit(0) | ||||
|  | ||||
| info(f"Needed version : '{versionN}'") | ||||
|  | ||||
| downloadUrl = "http://mirror.cs.uchicago.edu/google-chrome/pool/main/g/google-chrome-stable/" | ||||
| r = requests.get(downloadUrl) | ||||
|  | ||||
| content = r.text | ||||
|  | ||||
| exactVersionList = re.findall(f"(google-chrome-stable_({versionN}.[0-9.]+)[^<^>^\"]+)", content) | ||||
|  | ||||
| try: | ||||
|     best = exactVersionList[0] | ||||
| except Exception as e: | ||||
|     critical("No version matches required version") | ||||
|     error(e) | ||||
|     exit(0) | ||||
|  | ||||
| for i in exactVersionList: | ||||
|     if version.parse(i[1]) > version.parse(best[1]): | ||||
|         best = i | ||||
|  | ||||
| chromeDebURL = f"http://mirror.cs.uchicago.edu/google-chrome/pool/main/g/google-chrome-stable/{best[0]}" | ||||
| info(f"chrome deb URL : {chromeDebURL}") | ||||
| info("downloading chrome") | ||||
|  | ||||
| subprocess.call(['wget', "-O", "/tmp/chrome.deb", chromeDebURL]) | ||||
| info("Chrome deb downloaded. Installing chrome") | ||||
|  | ||||
| subprocess.call(["dpkg", "-i", "/tmp/chrome.deb"]) | ||||
| info("Chrome installed") | ||||
| @@ -1,3 +1,5 @@ | ||||
| import os | ||||
| import json | ||||
| from random import uniform | ||||
|  | ||||
| from selenium.common import TimeoutException | ||||
| @@ -10,33 +12,40 @@ from modules.Tools.logger import debug | ||||
| from modules.Tools.tools import * | ||||
|  | ||||
|  | ||||
| def set_language(ldriver): | ||||
|     ldriver.get("chrome://settings/languages") | ||||
|     action = ActionChains(ldriver) | ||||
|     action.reset_actions() | ||||
|     # select language | ||||
|     x_coord = 1200 | ||||
|     y_coord = 150 | ||||
|     action.move_by_offset(x_coord, y_coord).click().perform() | ||||
|     sleep(0.5) | ||||
|     # scroll down | ||||
|     action.reset_actions() | ||||
|     elm = ldriver.find_element(By.XPATH, "/html/body") | ||||
|     ActionChains(ldriver) \ | ||||
|         .send_keys("french") \ | ||||
|         .pause(0.5) \ | ||||
|         .send_keys(Keys.TAB + Keys.TAB + Keys.ENTER + Keys.TAB + Keys.TAB + Keys.ENTER) \ | ||||
|         .perform() | ||||
|     x_coord = 1163 | ||||
|     y_coord = 717 | ||||
|     action.move_by_offset(x_coord, y_coord).click().perform() | ||||
|     # put to the top | ||||
|     sleep(0.5) | ||||
|     action.reset_actions() | ||||
|     x_coord = 1257 | ||||
|     y_coord = 328 | ||||
|     action.move_by_offset(x_coord, y_coord).click().perform() | ||||
|     action.click().perform() | ||||
| def init_profile(mail, mobile=False): | ||||
|     if not mobile: | ||||
|         chrome_profile_dir = "/app/MsRewards-Reborn/user_data/profile/"+mail | ||||
|     else: | ||||
|         chrome_profile_dir = "/app/MsRewards-Reborn/user_data/profile/mobile-"+mail | ||||
|  | ||||
|     os.makedirs(chrome_profile_dir, exist_ok=True) | ||||
|  | ||||
|     preferences_file = os.path.join(chrome_profile_dir, "Default", "Preferences") | ||||
|     if not os.path.exists(preferences_file): | ||||
|         os.makedirs(os.path.join(chrome_profile_dir, "Default"), exist_ok=True) | ||||
|         with open(preferences_file, "w") as f: | ||||
|             json.dump( | ||||
|                 { | ||||
|                     "intl": { | ||||
|                         "accept_languages": "fr-FR,en-US,en", | ||||
|                         "selected_languages": "fr-FR,en-US,en" | ||||
|                     } | ||||
|                }, f | ||||
|             ) | ||||
|     else: | ||||
|         with open(preferences_file, "r") as f: | ||||
|             settings = json.load(f) | ||||
|          | ||||
|         if "intl" not in settings: | ||||
|             settings["intl"] = {} | ||||
|  | ||||
|         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 | ||||
| @@ -60,13 +69,18 @@ def send_keys_wait(element, keys: str) -> None: | ||||
|         element.send_keys(i) | ||||
|         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 | ||||
| def wait_until_visible(search_by: str, identifier: str, timeout: int = 20, browser=None) -> bool: | ||||
| def wait_until_visible(search_by: str, identifier: str, timeout: int = 20, browser=None, raise_error=True) -> bool: | ||||
|     try: | ||||
|         WebDriverWait(browser, timeout).until( | ||||
|             expected_conditions.visibility_of_element_located((search_by, identifier)), "element not found") | ||||
|         return True | ||||
|     except TimeoutException as e: | ||||
|         error(f"element {identifier} not found after {timeout}s") | ||||
|         if raise_error: | ||||
|             error(f"element {identifier} not found after {timeout}s") | ||||
|         return False | ||||
|   | ||||
		Reference in New Issue
	
	Block a user