forked from piair/MsRewards-Reborn
Compare commits
79 Commits
50c4036c73
...
dcb2134744
Author | SHA1 | Date | |
---|---|---|---|
dcb2134744 | |||
d824bf4c1b | |||
5e5f7f7407 | |||
0cc42823ff | |||
56c858fa90 | |||
adf91ac8f0 | |||
2721c76686 | |||
|
45ea72a354 | ||
|
18b35f6f68 | ||
c53125479e | |||
df256f2405 | |||
31b4d5d7da | |||
574fa45813 | |||
|
3bb49d1618 | ||
|
e25334f1bd | ||
|
41312b53b9 | ||
|
1ac98f14c4 | ||
|
a197d44768 | ||
|
f9a21f5b98 | ||
|
82161973a9 | ||
|
c7e110787b | ||
1db1de9606 | |||
10d75f9d78 | |||
87fb791436 | |||
6995bde8a6 | |||
92069a013e | |||
906d3e7822 | |||
778adc67d2 | |||
402b8cd3ef | |||
|
3c74aa025e | ||
|
3123a1dbd0 | ||
|
27d45e88dd | ||
|
0685a42922 | ||
|
6729703827 | ||
|
304a222de1 | ||
|
04d33f4ecd | ||
|
ac22814605 | ||
|
b7a89b56d0 | ||
|
127d16afea | ||
|
9bcbc81c2f | ||
|
12eba5cde7 | ||
|
22ea727c47 | ||
|
05a1fd8557 | ||
|
39e226b564 | ||
|
cd00c8ca88 | ||
|
43793d2c2c | ||
|
ee6ec458fc | ||
|
e6e8bdaa06 | ||
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 |
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
**/.venv
|
||||||
|
user_data/*
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -3,18 +3,17 @@ geckodriver.log
|
|||||||
.vscode/
|
.vscode/
|
||||||
.idea
|
.idea
|
||||||
venv
|
venv
|
||||||
|
**/.venv
|
||||||
/Git
|
/Git
|
||||||
page.html
|
page.html
|
||||||
screenshot.png
|
screenshot.png
|
||||||
login.csv
|
|
||||||
data
|
data
|
||||||
**/__pycache__
|
**/__pycache__
|
||||||
user_data/*
|
user_data/*
|
||||||
install.sh
|
install.sh
|
||||||
nohup.out
|
nohup.out
|
||||||
points.csv
|
|
||||||
file.png
|
file.png
|
||||||
user_data/configs.json
|
|
||||||
*.ts
|
*.ts
|
||||||
LICENSE
|
LICENSE
|
||||||
README.md
|
README.md
|
||||||
|
venv
|
||||||
|
51
Dockerfile
51
Dockerfile
@ -1,21 +1,42 @@
|
|||||||
FROM python:3.10
|
FROM python:3.10
|
||||||
ENV DEBIAN_FRONTEND noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
WORKDIR /app/
|
WORKDIR /app/
|
||||||
RUN apt update \
|
|
||||||
&& wget http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.0g-2ubuntu4_amd64.deb \
|
# Initial apt install
|
||||||
&& dpkg -i libssl1.1_1.1.0g-2ubuntu4_amd64.deb \
|
RUN apt update
|
||||||
&& apt install redis libgtk-4-1 libvulkan1 libxdamage1 -y \
|
RUN apt install -y libgtk-4-1 libvulkan1 libxdamage1 \
|
||||||
&& curl -sSLO https://nc.piair.xyz/s/BKLsBWoZkTdYjfq/download/chrome.deb \
|
novnc websockify xvfb nginx nano tzdata \
|
||||||
&& ln -fs /usr/share/zoneinfo/Europe/Paris /etc/localtime \
|
sqlite3 apt-transport-https software-properties-common \
|
||||||
&& git clone https://gitea.augustin64.fr/piair/MsRewards-Reborn \
|
wget wfrench tigervnc-standalone-server libasound2 \
|
||||||
&& python3 -m pip install -r MsRewards-Reborn/requirements.txt \
|
libatk-bridge2.0-0 libnss3 libnspr4 xvfb libgbm1 libatk1.0-0 \
|
||||||
&& wget -q -O /usr/share/keyrings/grafana.key https://apt.grafana.com/gpg.key \
|
libu2f-udev libatspi2.0-0 libcups2 libxkbcommon0 libxrandr2 \
|
||||||
&& curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg \
|
libdbus-1-3 xdg-utils fonts-liberation libdrm2
|
||||||
&& 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 \
|
# Additional repos and packages
|
||||||
&& 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 \
|
RUN wget http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.0g-2ubuntu4_amd64.deb \
|
||||||
&& bash MsRewards-Reborn/config/config.sh \
|
&& 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
|
&& 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 \
|
||||||
|
&& 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"
|
ENV TZ="Europe/Paris"
|
||||||
WORKDIR /app/MsRewards-Reborn/Flask/
|
WORKDIR /app/MsRewards-Reborn/Flask/
|
||||||
|
103
Flask/app.py
103
Flask/app.py
@ -13,11 +13,27 @@ import re
|
|||||||
from requests import get
|
from requests import get
|
||||||
import redis
|
import redis
|
||||||
|
|
||||||
|
APP_ROOT = os.getenv("APP_ROOT")
|
||||||
|
if APP_ROOT is None:
|
||||||
|
APP_ROOT = "/app/MsRewards-Reborn/"
|
||||||
|
|
||||||
|
NO_SUBPROCESS = os.getenv("NO_SUBPROCESS")
|
||||||
|
if NO_SUBPROCESS is not None:
|
||||||
|
def fake_popen(*args, **kwargs):
|
||||||
|
print("Calling subprocess.Popen with", args, kwargs)
|
||||||
|
|
||||||
|
subprocess.Popen = fake_popen
|
||||||
|
print("Faking subprocess calls")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# redis part for live update
|
# redis part for live update
|
||||||
pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
|
pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
|
||||||
r = redis.Redis(connection_pool=pool)
|
r = redis.Redis(connection_pool=pool)
|
||||||
|
|
||||||
|
def get_path(path):
|
||||||
|
return os.path.join(APP_ROOT, path)
|
||||||
|
|
||||||
def generate_output():
|
def generate_output():
|
||||||
pubsub = r.pubsub()
|
pubsub = r.pubsub()
|
||||||
pubsub.subscribe('console')
|
pubsub.subscribe('console')
|
||||||
@ -32,7 +48,7 @@ def generate_output():
|
|||||||
# the end
|
# the end
|
||||||
|
|
||||||
global password
|
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)
|
data = json.load(inFile)
|
||||||
|
|
||||||
password = data["password"]
|
password = data["password"]
|
||||||
@ -40,7 +56,7 @@ secret = data["secret"]
|
|||||||
if secret == "":
|
if secret == "":
|
||||||
import secrets
|
import secrets
|
||||||
secret = secrets.token_hex()
|
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 = {
|
data = {
|
||||||
"password": password,
|
"password": password,
|
||||||
"secret": secret
|
"secret": secret
|
||||||
@ -51,7 +67,7 @@ if secret == "":
|
|||||||
#Automatic start of MsRewards
|
#Automatic start of MsRewards
|
||||||
"""
|
"""
|
||||||
def daily_command():
|
def daily_command():
|
||||||
subprocess.Popen(["git",'pull'])
|
subprocess.Popen(["git","pull"])
|
||||||
subprocess.Popen(["pkill","-9","chrome"])
|
subprocess.Popen(["pkill","-9","chrome"])
|
||||||
subprocess.Popen(["pkill","-9","Xvfb"])
|
subprocess.Popen(["pkill","-9","Xvfb"])
|
||||||
subprocess.Popen(["pkill","-9","Xvnc"])
|
subprocess.Popen(["pkill","-9","Xvnc"])
|
||||||
@ -70,14 +86,14 @@ scheduler.add_job( # on relance le job
|
|||||||
|
|
||||||
def start_ms(i):
|
def start_ms(i):
|
||||||
print("\033[32m" + f"Starting config {i}" + "\033[0m")
|
print("\033[32m" + f"Starting config {i}" + "\033[0m")
|
||||||
log = open(f"/app/MsRewards-Reborn/Flask/static/logs/{i}.txt", 'a') # so that data written to it will be appended
|
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 /app/MsRewards-Reborn/V6.py -c {i}"], stdout=log, stderr=log, shell=True)
|
subprocess.Popen([f"python3 -u {get_path('V6.py')} -c {i}"], stdout=log, stderr=log, shell=True)
|
||||||
log.close()
|
log.close()
|
||||||
|
|
||||||
|
|
||||||
TriggerDict = {}
|
TriggerDict = {}
|
||||||
def update_jobs():
|
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)
|
configs = json.load(inFile)
|
||||||
for i in configs:
|
for i in configs:
|
||||||
try :
|
try :
|
||||||
@ -120,7 +136,7 @@ app = Flask(__name__)
|
|||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_default_variables():
|
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", '')
|
version = f.readline().replace("\n", '')
|
||||||
return dict(version=version)
|
return dict(version=version)
|
||||||
"""
|
"""
|
||||||
@ -175,7 +191,7 @@ def change_password():
|
|||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
password = request.form["password"]
|
password = request.form["password"]
|
||||||
subprocess.Popen(["grafana-cli", "admin", "reset-admin-password", password])
|
subprocess.Popen(["grafana-cli", "admin", "reset-admin-password", password])
|
||||||
with open("/app/MsRewards-Reborn/user_data/flask.json", "w") as inFile:
|
with open(get_path("user_data/flask.json"), "w") as inFile:
|
||||||
data = {
|
data = {
|
||||||
"password": password,
|
"password": password,
|
||||||
"secret": secret
|
"secret": secret
|
||||||
@ -201,21 +217,21 @@ def load_user(userid):
|
|||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def main():
|
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)
|
configs = json.load(inFile)
|
||||||
return(render_template("schedule.html", data=configs))
|
return(render_template("schedule.html", data=configs))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/discord/")
|
@app.route("/discord/")
|
||||||
def discord_get():
|
def discord_get():
|
||||||
with open("/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)
|
data = json.load(inFile)
|
||||||
return(render_template("discord.html", data=data, len=maxi(data)))
|
return(render_template("discord.html", data=data, len=maxi(data)))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/discord/", methods=["post"])
|
@app.route("/discord/", methods=["post"])
|
||||||
def discord_post():
|
def discord_post():
|
||||||
with open("/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)
|
data = json.load(inFile)
|
||||||
action = request.form
|
action = request.form
|
||||||
if action['DISCORD'] == "delete" :
|
if action['DISCORD'] == "delete" :
|
||||||
@ -237,7 +253,7 @@ def discord_post():
|
|||||||
name = action["name"] if action["name"] else f"unnamed{action['select']}"
|
name = action["name"] if action["name"] else f"unnamed{action['select']}"
|
||||||
data[config] = {"errorsL" : errorsL, "errorsT": errorsT, "successT": successT, "successL": successL, "name": name}
|
data[config] = {"errorsL" : errorsL, "errorsT": errorsT, "successT": successT, "successL": successL, "name": name}
|
||||||
|
|
||||||
with open("/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)
|
json.dump(data, outFile)
|
||||||
return(render_template("discord.html", data=data, len=maxi(data)))
|
return(render_template("discord.html", data=data, len=maxi(data)))
|
||||||
|
|
||||||
@ -249,7 +265,7 @@ def dev2():
|
|||||||
|
|
||||||
@app.route("/settings/")
|
@app.route("/settings/")
|
||||||
def settings_get():
|
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)
|
settings = json.load(inFile)
|
||||||
return(render_template("settings.html", data=settings))
|
return(render_template("settings.html", data=settings))
|
||||||
|
|
||||||
@ -259,21 +275,21 @@ def settings_post():
|
|||||||
settings = {}
|
settings = {}
|
||||||
action = request.form
|
action = request.form
|
||||||
settings['avatarlink'] = action["avatarlink"]
|
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)
|
json.dump(settings, inFile)
|
||||||
return(render_template("settings.html", data=settings))
|
return(render_template("settings.html", data=settings))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/proxy/")
|
@app.route("/proxy/")
|
||||||
def proxy_get():
|
def proxy_get():
|
||||||
with open("/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)
|
j = json.load(inFile)
|
||||||
return(render_template("proxy.html", data=j, len=maxi(j)))
|
return(render_template("proxy.html", data=j, len=maxi(j)))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/proxy/", methods=["post"])
|
@app.route("/proxy/", methods=["post"])
|
||||||
def proxy_post():
|
def proxy_post():
|
||||||
with open("/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)
|
data = json.load(inFile)
|
||||||
action = request.form
|
action = request.form
|
||||||
print(action)
|
print(action)
|
||||||
@ -290,21 +306,21 @@ def proxy_post():
|
|||||||
except :
|
except :
|
||||||
print("error : probably bad config")
|
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)
|
json.dump(data, outFile)
|
||||||
return(render_template("proxy.html", data=data, len=maxi(data)))
|
return(render_template("proxy.html", data=data, len=maxi(data)))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/schedule/")
|
@app.route("/schedule/")
|
||||||
def schedule_get():
|
def schedule_get():
|
||||||
with open("/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)
|
configs = json.load(inFile)
|
||||||
return(render_template("schedule.html", data=configs))
|
return(render_template("schedule.html", data=configs))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/schedule/", methods=["post"])
|
@app.route("/schedule/", methods=["post"])
|
||||||
def schedule_post():
|
def schedule_post():
|
||||||
with open("/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)
|
configs = json.load(inFile)
|
||||||
|
|
||||||
data = dict(request.form)
|
data = dict(request.form)
|
||||||
@ -318,7 +334,7 @@ def schedule_post():
|
|||||||
configs[i]["time"] = data[f"time{i}"]
|
configs[i]["time"] = data[f"time{i}"]
|
||||||
configs[i]["enabled"] = data[f"switch{i}"] == "on"
|
configs[i]["enabled"] = data[f"switch{i}"] == "on"
|
||||||
|
|
||||||
with open("/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)
|
json.dump(configs, inFile)
|
||||||
update_jobs()
|
update_jobs()
|
||||||
return(render_template("schedule.html", data=configs))
|
return(render_template("schedule.html", data=configs))
|
||||||
@ -326,11 +342,11 @@ def schedule_post():
|
|||||||
|
|
||||||
@app.route("/config/")
|
@app.route("/config/")
|
||||||
def config_get():
|
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)
|
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)
|
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)
|
configs = json.load(inFile)
|
||||||
return(render_template("config.html", data=configs, discords=discords, proxys=proxys, configs=configs, len=maxi(configs)))
|
return(render_template("config.html", data=configs, discords=discords, proxys=proxys, configs=configs, len=maxi(configs)))
|
||||||
|
|
||||||
@ -338,11 +354,11 @@ def config_get():
|
|||||||
@app.route("/config/", methods=["POST"])
|
@app.route("/config/", methods=["POST"])
|
||||||
def config_post():
|
def config_post():
|
||||||
action = request.form
|
action = request.form
|
||||||
with open("/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)
|
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)
|
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)
|
configs = json.load(inFile)
|
||||||
|
|
||||||
if action["data"] == "delete":
|
if action["data"] == "delete":
|
||||||
@ -365,16 +381,26 @@ def config_post():
|
|||||||
"enabled":"False",
|
"enabled":"False",
|
||||||
"accounts": comptes
|
"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)
|
json.dump(configs, outFile)
|
||||||
return(render_template("config.html", data=configs, discords=discords, proxys=proxys, configs=configs, len=maxi(configs)))
|
return(render_template("config.html", data=configs, discords=discords, proxys=proxys, configs=configs, len=maxi(configs)))
|
||||||
|
|
||||||
@app.route("/logs/", methods=["GET", "POST"])
|
@app.route("/logs/", methods=["GET", "POST"])
|
||||||
def logs():
|
def logs():
|
||||||
with open("/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)
|
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"])
|
@app.route("/stats/", methods=["GET", "POST"])
|
||||||
@ -385,21 +411,21 @@ def stats():
|
|||||||
@app.route("/override/", methods=["POST"])
|
@app.route("/override/", methods=["POST"])
|
||||||
def override_post():
|
def override_post():
|
||||||
json = request.form.to_dict(flat=False)
|
json = request.form.to_dict(flat=False)
|
||||||
log = open(f"/app/MsRewards-Reborn/Flask/static/logs/custom.txt", 'w') # so that data written to it will be appended
|
log = open(get_path("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)
|
subprocess.Popen([f"python3 -u {get_path('V6.py')} -c {json['config'][0]} --json \"{json}\""], stdout=log, stderr=log, shell=True)
|
||||||
log.close()
|
log.close()
|
||||||
return(render_template("vnc_post.html"))
|
return(render_template("vnc_post.html"))
|
||||||
|
|
||||||
@app.route("/override/", methods=["GET"])
|
@app.route("/override/", methods=["GET"])
|
||||||
def override_get():
|
def override_get():
|
||||||
with open("/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)
|
configs = json.load(inFile)
|
||||||
return(render_template("vnc_get.html", configs=configs))
|
return(render_template("vnc_get.html", configs=configs))
|
||||||
|
|
||||||
@app.route('/download/<path:filename>', methods=['GET', 'POST'])
|
@app.route('/download/<path:filename>', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def download(filename):
|
def download(filename):
|
||||||
return send_from_directory(directory='/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):
|
def allowed_file(filename):
|
||||||
@ -420,8 +446,8 @@ def upload_file():
|
|||||||
|
|
||||||
elif file and allowed_file(file.filename):
|
elif file and allowed_file(file.filename):
|
||||||
filename = secure_filename(file.filename)
|
filename = secure_filename(file.filename)
|
||||||
print(os.path.join('/app/MsRewards-Reborn/user_data/', filename))
|
print(os.path.join(get_path("user_data/"), filename))
|
||||||
file.save(os.path.join('/app/MsRewards-Reborn/user_data/', filename))
|
file.save(os.path.join(get_path("user_data/"), filename))
|
||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
print(i)
|
print(i)
|
||||||
@ -439,4 +465,7 @@ def maxi(dict):
|
|||||||
|
|
||||||
|
|
||||||
update_jobs()
|
update_jobs()
|
||||||
subprocess.Popen(["bash",'/app/MsRewards-Reborn/config/request.sh'])
|
subprocess.Popen(["bash", get_path("config/request.sh")])
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run()
|
||||||
|
@ -231,7 +231,7 @@ body {
|
|||||||
margin: auto;
|
margin: auto;
|
||||||
width: 20%;
|
width: 20%;
|
||||||
}
|
}
|
||||||
.comlumn-name{
|
.column-name{
|
||||||
width: 20%;
|
width: 20%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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%;
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
<form method="post" action="/change_password/">
|
<form method="post" action="/change_password/">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name">Change password</td>
|
<td class="column-name">Change password</td>
|
||||||
<td><input type="text" name="password"></td>
|
<td><input type="text" name="password"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -20,30 +20,30 @@
|
|||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name">name</td>
|
<td class="column-name">name</td>
|
||||||
<td><input type="text" id="name" name="name" value=""></td>
|
<td><input type="text" id="name" name="name" value=""></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name">Send errors</td>
|
<td class="column-name">Send errors</td>
|
||||||
<td><input type="checkbox" id="errorsT" name="errorsT" class="toogle"/><label for="errorsT">Toggle</label></td>
|
<td><input type="checkbox" id="errorsT" name="errorsT" class="toogle"/><label for="errorsT">Toggle</label></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name">Send success</td>
|
<td class="column-name">Send success</td>
|
||||||
<td><input type="checkbox" id="successT" name="successT" class="toogle"/><label for="successT">Toggle</label></td>
|
<td><input type="checkbox" id="successT" name="successT" class="toogle"/><label for="successT">Toggle</label></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name">Success link</td>
|
<td class="column-name">Success link</td>
|
||||||
<td><input type="text" id="successL" name="successL" value=""></td>
|
<td><input type="text" id="successL" name="successL" value=""></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name">Failure Link</td>
|
<td class="column-name">Failure Link</td>
|
||||||
<td><input type="text" id="errorsL" name="errorsL" value=""></td>
|
<td><input type="text" id="errorsL" name="errorsL" value=""></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name"></td>
|
<td class="column-name"></td>
|
||||||
<td style="width:60%;"><input type="submit" name="DISCORD" id="submit" value="Create !" class="confirm" style="width:86%;"/></td>
|
<td style="width:60%;"><input type="submit" name="DISCORD" id="submit" value="Create !" class="confirm" style="width:86%;"/></td>
|
||||||
<td style="padding-right: 20px;">
|
<td style="padding-right: 20px;">
|
||||||
<input type="submit" name="DISCORD" id="submit" value="delete" class="ban" style="width:60%;"/>
|
<input type="submit" name="DISCORD" id="submit" value="delete" class="ban" style="width:60%;"/>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<form method="post" action="/login/">
|
<form method="post" action="/login/">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name">password</td>
|
<td class="column-name">password</td>
|
||||||
<td><input type="password" name="password"></td>
|
<td><input type="password" name="password"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
|
|
||||||
<select name="select" onchange="change_logs(this.value)">
|
<select name="select" onchange="change_logs(this.value)">
|
||||||
<option id="null" value="0">Choisir une config</option>
|
<option id="null" value="0">Choisir une config</option>
|
||||||
{% for i in data %}
|
{% for file in files %}
|
||||||
<option id="{{data[i]['name']}}" value="{{i}}">{{data[i]['name']}}</option>
|
<option id="{{ file[0] }}" value="{{ file[1] }}">{{ file[0] }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<br><br>
|
<br><br>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name"></td>
|
<td class="column-name"></td>
|
||||||
<td>
|
<td>
|
||||||
<script>data = JSON.parse('{{data|tojson}}');</script>
|
<script>data = JSON.parse('{{data|tojson}}');</script>
|
||||||
<select name="select" onchange="change_proxy(this.value, data, '{{len}}')">
|
<select name="select" onchange="change_proxy(this.value, data, '{{len}}')">
|
||||||
@ -24,21 +24,21 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name">name : </td>
|
<td class="column-name">name : </td>
|
||||||
<td><input type="text" name="name" value="" id="name"></td>
|
<td><input type="text" name="name" value="" id="name"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name">address : </td>
|
<td class="column-name">address : </td>
|
||||||
<td><input type="text" name="address" value="" id="address"></td>
|
<td><input type="text" name="address" value="" id="address"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name">port : </td>
|
<td class="column-name">port : </td>
|
||||||
<td><input type="text" name="port" value="" id="port"></td>
|
<td><input type="text" name="port" value="" id="port"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name"></td>
|
<td class="column-name"></td>
|
||||||
<td style="width:60%;"><input type="submit" name="PROXY" id="submit" value="Create !" class="confirm" style="width:86%;"/></td>
|
<td style="width:60%;"><input type="submit" name="PROXY" id="submit" value="Create !" class="confirm" style="width:86%;"/></td>
|
||||||
<td style="padding-right: 20px;"><input type="submit" name="PROXY" id="submit" value="delete" class="ban" style="width:60%;"/></td>
|
<td style="padding-right: 20px;"><input type="submit" name="PROXY" id="submit" value="delete" class="ban" style="width:60%;"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<form method="post" action="/settings/">
|
<form method="post" action="/settings/">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="comlumn-name">avatar url :</td>
|
<td class="column-name">avatar url :</td>
|
||||||
<td><input type="text" name="avatarlink" value="{{data['avatarlink']}}"></td>
|
<td><input type="text" name="avatarlink" value="{{data['avatarlink']}}"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -6,19 +6,19 @@
|
|||||||
{% if not current_user.is_authenticated %}
|
{% if not current_user.is_authenticated %}
|
||||||
<button class="unselected" onclick="location.href = '/login';">login</button>
|
<button class="unselected" onclick="location.href = '/login';">login</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/vnc-post.css') }}"/>
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='node_modules/xterm/css/xterm.css') }}"/>
|
<link rel="stylesheet" href="{{ url_for('static', filename='node_modules/xterm/css/xterm.css') }}"/>
|
||||||
<script src="{{ url_for('static', filename='node_modules/xterm/lib/xterm.js') }}"></script>
|
<script src="{{ url_for('static', filename='node_modules/xterm/lib/xterm.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='node_modules/xterm-addon-fit/lib/xterm-addon-fit.js') }}"></script>
|
<script src="{{ url_for('static', filename='node_modules/xterm-addon-fit/lib/xterm-addon-fit.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
document.getElementsByClassName("content")[0].style.padding = "0 0"
|
document.getElementsByClassName("content")[0].style.padding = "0 0"
|
||||||
</script>
|
</script>
|
||||||
<div style="height: 100%">
|
<div class="container">
|
||||||
|
<div id="console"></div>
|
||||||
<div id="console" style="height: 100%; width: 20%; float:left;"></div>
|
<div id="vnc-container">
|
||||||
|
|
||||||
<div style="height: 100%; width: 80%; float:left;">
|
|
||||||
<iframe src="/novnc/vnc.html?resize=scale&path=novnc/websockify&autoconnect=true&view_only"
|
<iframe src="/novnc/vnc.html?resize=scale&path=novnc/websockify&autoconnect=true&view_only"
|
||||||
width="100%" height="100%" frameborder="0"></iframe>
|
width="100%" height="100%" frameborder="0">
|
||||||
|
</iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
460
V6.py
460
V6.py
@ -1,8 +1,11 @@
|
|||||||
#!/usr/bin/python3.10
|
#!/usr/bin/python3
|
||||||
|
import random
|
||||||
|
import subprocess
|
||||||
|
|
||||||
from modules.Classes.Config import Config
|
from modules.Classes.Config import Config
|
||||||
from modules.Classes.DiscordLogger import DiscordLogger
|
from modules.Classes.DiscordLogger import DiscordLogger
|
||||||
from modules.Classes.UserCredentials import UserCredentials
|
from modules.Classes.UserCredentials import UserCredentials
|
||||||
from modules.Tools.logger import warning, critical
|
from modules.Tools.logger import critical, warning
|
||||||
from modules.cards import *
|
from modules.cards import *
|
||||||
from modules.config import *
|
from modules.config import *
|
||||||
from modules.db import add_to_database
|
from modules.db import add_to_database
|
||||||
@ -22,16 +25,33 @@ def create_driver(mobile=False):
|
|||||||
"AppleWebKit/537.36 (KHTML, like Gecko)"
|
"AppleWebKit/537.36 (KHTML, like Gecko)"
|
||||||
"Chrome/22 Mobile Safari/537.36"
|
"Chrome/22 Mobile Safari/537.36"
|
||||||
)
|
)
|
||||||
chrome_options = webdriver.ChromeOptions()
|
|
||||||
|
chrome_profile_dir = init_profile(config.UserCredentials.get_mail(), mobile=mobile)
|
||||||
|
|
||||||
|
# Full list on https://github.com/GoogleChrome/chrome-launcher/blob/main/docs/chrome-flags-for-tools.md
|
||||||
|
arguments = [
|
||||||
|
"--no-first-run",
|
||||||
|
"--ash-no-nudges",
|
||||||
|
"--no-default-browser-check",
|
||||||
|
"--disable-features=PrivacySandboxSettings4,Translate",
|
||||||
|
"--disable-search-engine-choice-screen",
|
||||||
|
f"--user-data-dir={chrome_profile_dir}/"
|
||||||
|
]
|
||||||
|
|
||||||
if mobile:
|
if mobile:
|
||||||
chrome_options.add_argument(f"--user-agent={mobile_user_agent}")
|
arguments.append(f"--user-agent={mobile_user_agent}")
|
||||||
else:
|
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
|
# disabled as it may cause detection
|
||||||
if config.proxy.is_enabled():
|
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)
|
driver = uc.Chrome(options=chrome_options)
|
||||||
set_language(driver)
|
|
||||||
return driver
|
return driver
|
||||||
|
|
||||||
|
|
||||||
@ -179,6 +199,68 @@ def do_poll():
|
|||||||
# Find each playable card and tries to click on it to earn points
|
# Find each playable card and tries to click on it to earn points
|
||||||
def all_cards():
|
def all_cards():
|
||||||
driver = config.WebDriver.driver
|
driver = config.WebDriver.driver
|
||||||
|
|
||||||
|
def check_popups() -> bool:
|
||||||
|
"""
|
||||||
|
Check various annoying popups
|
||||||
|
"""
|
||||||
|
def check_welcome_tour() -> bool:
|
||||||
|
if "rewards.bing.com/welcometour" not in driver.current_url:
|
||||||
|
return False
|
||||||
|
|
||||||
|
info("Popup 'Explorer le programme' reçue")
|
||||||
|
wait_until_visible(By.ID, "welcome-tour", timeout=5, browser=driver)
|
||||||
|
custom_sleep(1.5)
|
||||||
|
|
||||||
|
welcome_tour = driver.find_element(By.ID, "welcome-tour")
|
||||||
|
interest_button_box = welcome_tour.find_element(By.CLASS_NAME, "interest-buttons")
|
||||||
|
interests = interest_button_box.find_elements(By.CLASS_NAME, "ng-binding")
|
||||||
|
debug("Got the following interests: " + str(interests))
|
||||||
|
random.choice(interests).click() # Choose interest
|
||||||
|
custom_sleep(1.5)
|
||||||
|
|
||||||
|
claim_button = welcome_tour.find_element(By.ID, "claim-button")
|
||||||
|
claim_button.click() # submit
|
||||||
|
custom_sleep(1.5)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_streak_protection() -> bool:
|
||||||
|
"""
|
||||||
|
Ne perdez plus jamais votre série !
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
streak_protection_close = driver.find_element(By.ID, "streak-protection-popup-close-cross")
|
||||||
|
streak_protection_activate = driver.find_elements(By.CLASS_NAME, "earningPagePopUpPopUpSelectButton")
|
||||||
|
streak_protection_activate[0].click()
|
||||||
|
info("Popup 'Streak Protection' reçue")
|
||||||
|
custom_sleep(1.5)
|
||||||
|
|
||||||
|
return True
|
||||||
|
except (exceptions.NoSuchElementException, exceptions.ElementNotInteractableException, IndexError):
|
||||||
|
# les éléments sont présents dans le DOM même quand la popup n'est pas visible apparemment
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_streak_protection_expired() -> bool:
|
||||||
|
"""
|
||||||
|
Non, vous avez utilisé tous vos jours de protection contre les séries !
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
streak_protection_close = driver.find_element(By.ID, "streak-protection-popup-close-cross")
|
||||||
|
ok_sad_button = driver.find_elements(By.CLASS_NAME, "dashboardPopUpPopUpSelectButton")
|
||||||
|
ok_sad_button[0].click()
|
||||||
|
info("Popup 'Streak Protection fully used' reçue")
|
||||||
|
custom_sleep(1.5)
|
||||||
|
|
||||||
|
return True
|
||||||
|
except (exceptions.NoSuchElementException, exceptions.ElementNotInteractableException, IndexError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return (
|
||||||
|
check_welcome_tour()
|
||||||
|
or check_streak_protection()
|
||||||
|
or check_streak_protection_expired()
|
||||||
|
)
|
||||||
|
|
||||||
driver.get("https://rewards.bing.com")
|
driver.get("https://rewards.bing.com")
|
||||||
wait_until_visible(By.CLASS_NAME, "c-card-content", 10, driver)
|
wait_until_visible(By.CLASS_NAME, "c-card-content", 10, driver)
|
||||||
|
|
||||||
@ -192,7 +274,7 @@ def all_cards():
|
|||||||
info("no promo card")
|
info("no promo card")
|
||||||
|
|
||||||
if len(card_list) < 10: # most likely an error during loading
|
if len(card_list) < 10: # most likely an error during loading
|
||||||
if "suspendu" in driver.page_source:
|
if "suspendu" in driver.page_source or "suspended" in driver.page_source:
|
||||||
raise Banned()
|
raise Banned()
|
||||||
driver.refresh()
|
driver.refresh()
|
||||||
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content")
|
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content")
|
||||||
@ -222,39 +304,53 @@ def all_cards():
|
|||||||
if i == len(card_list) and i > 15:
|
if i == len(card_list) and i > 15:
|
||||||
checked = False
|
checked = False
|
||||||
|
|
||||||
if checked:
|
if not checked:
|
||||||
custom_sleep(1.5)
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
activity = findall("data-bi-id=\"([^\"]+)\"", card_list[i].get_attribute("innerHTML"))[0]
|
||||||
|
except Exception as e:
|
||||||
|
warning("Can't find activity." + str(e))
|
||||||
|
activity = ""
|
||||||
|
|
||||||
|
custom_sleep(1.5)
|
||||||
|
check_popups()
|
||||||
|
driver.execute_script("arguments[0].scrollIntoView();", card_list[i])
|
||||||
|
custom_sleep(1.5)
|
||||||
|
card_list[i].click()
|
||||||
|
|
||||||
|
if len(driver.window_handles) > 1:
|
||||||
|
driver.switch_to.window(driver.window_handles[1])
|
||||||
|
try_play(driver.title, activity)
|
||||||
|
close_tab(driver.window_handles[1])
|
||||||
|
|
||||||
|
try:
|
||||||
|
driver.refresh()
|
||||||
|
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content")
|
||||||
|
|
||||||
|
if "mee-icon-AddMedium" not in card_list[i].get_attribute("innerHTML"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
check_popups()
|
||||||
driver.execute_script("arguments[0].scrollIntoView();", card_list[i])
|
driver.execute_script("arguments[0].scrollIntoView();", card_list[i])
|
||||||
custom_sleep(1.5)
|
|
||||||
card_list[i].click()
|
card_list[i].click()
|
||||||
|
|
||||||
if len(driver.window_handles) > 1:
|
driver.switch_to.window(driver.window_handles[1])
|
||||||
driver.switch_to.window(driver.window_handles[1])
|
custom_sleep(10)
|
||||||
try_play(driver.title)
|
log_error(f"Card {i} Can't be completed. Why MS ?")
|
||||||
close_tab(driver.window_handles[1])
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
driver.refresh()
|
try_play(driver.title) # go back to the main page
|
||||||
card_list = driver.find_elements(By.CLASS_NAME, "c-card-content")
|
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"):
|
except Exception as err:
|
||||||
driver.execute_script("arguments[0].scrollIntoView();", card_list[i])
|
log_error(err)
|
||||||
card_list[i].click()
|
custom_sleep(3)
|
||||||
driver.switch_to.window(driver.window_handles[1])
|
|
||||||
custom_sleep(10)
|
|
||||||
log_error(f"Card {i} Can't be completed. Why MS ?")
|
|
||||||
try:
|
|
||||||
try_play(driver.title) # go back to the main page
|
|
||||||
try:
|
|
||||||
close_tab(driver.window_handles[1])
|
|
||||||
except Exception as e:
|
|
||||||
debug(e)
|
|
||||||
except Exception as e:
|
|
||||||
debug(e)
|
|
||||||
driver.get("https://rewards.bing.com")
|
|
||||||
except Exception as err:
|
|
||||||
log_error(err)
|
|
||||||
custom_sleep(3)
|
|
||||||
|
|
||||||
|
|
||||||
def promo():
|
def promo():
|
||||||
@ -292,8 +388,124 @@ def promo():
|
|||||||
custom_sleep(3)
|
custom_sleep(3)
|
||||||
|
|
||||||
|
|
||||||
|
def explore_on_bing(activity: str, config: Config):
|
||||||
|
driver = config.WebDriver.driver
|
||||||
|
|
||||||
|
def search_bing(txt):
|
||||||
|
search_elm = driver.find_element(By.ID, "sb_form_q")
|
||||||
|
send_keys_wait(search_elm, txt)
|
||||||
|
send_keys_wait(search_elm, Keys.ENTER)
|
||||||
|
|
||||||
|
if "lyrics" in activity:
|
||||||
|
search_bing(
|
||||||
|
f"paroles de {['Gata', 'Pyramide', 'Dolce Camara', 'Position', 'Mami Wata'][randint(0, 4)]}") # merci bing copilot pour les titres
|
||||||
|
elif "flight" in activity:
|
||||||
|
search_bing(
|
||||||
|
f"vol {['Paris - New York', 'Londres Amsterdam', 'Bora-Bora Miami', 'Los Angeles Toulouse', 'Rome Dubai'][randint(0, 4)]}")
|
||||||
|
elif "shopping" in activity:
|
||||||
|
search_bing(f"idée cadeau {['Noel', 'Anniversaire'][randint(0, 1)]}")
|
||||||
|
elif "movie" in activity:
|
||||||
|
search_bing(
|
||||||
|
f"Distribution {['Code 8 part 2', 'The Hunger Games: The ballad of Songbirds & Snakes', 'Rebel Moon: Part Two', 'Dune II', 'Wonka'][randint(0, 4)]}")
|
||||||
|
elif "translator" in activity:
|
||||||
|
search_bing(f"traduction {config.wordlist.get_word()} en anglais")
|
||||||
|
elif "map" in activity:
|
||||||
|
search_bing(f"{['Paris', 'Nice', 'Marseille', 'Bordeaux', 'Lyon'][randint(0, 4)]} carte")
|
||||||
|
elif "restaurant" in activity:
|
||||||
|
search_bing(random.choice([
|
||||||
|
"restaurant à proximité",
|
||||||
|
"restaurant pas loin",
|
||||||
|
"manger dans le coin"
|
||||||
|
]))
|
||||||
|
elif "recipe" in activity:
|
||||||
|
search_bing("recette de " + random.choice([
|
||||||
|
"cake aux olives",
|
||||||
|
"tarte tatin",
|
||||||
|
"Kouign Amann",
|
||||||
|
"poulet au Curry",
|
||||||
|
"lasagnes aux épinards et ricotta",
|
||||||
|
"Boeuf Bourguignon",
|
||||||
|
"dessert",
|
||||||
|
"gateau au chocolat",
|
||||||
|
"crêpe sucrée"
|
||||||
|
])) # Merci llama pour les idées
|
||||||
|
elif "currency" in activity:
|
||||||
|
currencies = [
|
||||||
|
"euros",
|
||||||
|
"dollars canadiens",
|
||||||
|
"dollars",
|
||||||
|
"livres",
|
||||||
|
"francs suisses",
|
||||||
|
"roubles",
|
||||||
|
"pesos",
|
||||||
|
"bitcoin",
|
||||||
|
"anciens francs"
|
||||||
|
]
|
||||||
|
chosen = random.sample(currencies, k=2)
|
||||||
|
search_bing(f"convertir {random.randint(2, 120)} {chosen[0]} en {chosen[1]}")
|
||||||
|
elif "weather" in activity:
|
||||||
|
search_bing(f"météo {['Paris', 'Nice', 'Marseille', 'Bordeaux', 'Lyon'][randint(0, 4)]}")
|
||||||
|
elif "packagetracking" in activity:
|
||||||
|
driver.get("https://www.bing.com/?setmkt=en-US&setlang=en-US")
|
||||||
|
search_bing(f"{['fedex', 'UPS'][randint(0, 1)]} package tracking")
|
||||||
|
elif "hotel" in activity:
|
||||||
|
search_bing(f" {['Paris', 'Nice', 'Marseille', 'Bordeaux', 'Lyon'][randint(0, 4)]}")
|
||||||
|
elif "health" in activity:
|
||||||
|
search_bing(f"{['toux', 'douleur dos', 'nez qui coule', 'mal au genoux', 'otite'][randint(0, 4)]}")
|
||||||
|
elif "news" in activity:
|
||||||
|
search_bing(["actualités", "news"][randint(0, 1)])
|
||||||
|
elif "jobs" in activity:
|
||||||
|
search_bing("jobs")
|
||||||
|
elif "realestate" in activity:
|
||||||
|
search_bing(f"appartement à louer {['Paris', 'Nice', 'Marseille', 'Bordeaux', 'Lyon'][randint(0, 4)]}")
|
||||||
|
elif "financemarket" in activity:
|
||||||
|
search_bing(
|
||||||
|
f"cours action {['AIR LIQUIDE', 'Airbus', 'BNP Paribas', 'Michelin', 'Stellantis', 'Vinci'][randint(0, 5)]}")
|
||||||
|
elif "sports" in activity:
|
||||||
|
equipes = random.sample([
|
||||||
|
"Toulon", "toulouse",
|
||||||
|
"stade Rochelais", "castres",
|
||||||
|
"clermont", "perpignan",
|
||||||
|
"aviron bayonnais", "vannes"
|
||||||
|
], k=2)
|
||||||
|
search_bing(f"{['score', 'résultats'][randint(0, 1)]} rugby {equipes[0]} {equipes[1]}")
|
||||||
|
elif "videogames" in activity:
|
||||||
|
search_bing(random.choice([
|
||||||
|
"Minecraft", "GTA V",
|
||||||
|
"Tetris", "PUBG",
|
||||||
|
"Mario Kart 8",
|
||||||
|
"Red Dead Redemption II",
|
||||||
|
"Terraria",
|
||||||
|
"The Witcher",
|
||||||
|
"Animal Crossing",
|
||||||
|
"Pokémon rouge & bleu",
|
||||||
|
"Super Mario Bros",
|
||||||
|
"The legend of Zelda BOTW",
|
||||||
|
"Call of Duty Modern Warfare",
|
||||||
|
"The Witcher III"
|
||||||
|
]))
|
||||||
|
elif "dictionary" in activity:
|
||||||
|
search_bing(f"définition {config.wordlist.get_word()}")
|
||||||
|
elif "election" in activity or "timezonedates" in activity:
|
||||||
|
#! I don't understand the name of "timezonedates" but this seems to be the same activity
|
||||||
|
state = random.choice([
|
||||||
|
"Alaska", "Alabama", "Arkansas", "American Samoa", "Arizona", "California", "Colorado",
|
||||||
|
"Connecticut", "District ", "of Columbia", "Delaware", "Florida", "Georgia", "Guam",
|
||||||
|
"Hawaii", "Iowa", "Idaho", "Illinois", "Indiana", "Kansas", "Kentucky", "Louisiana",
|
||||||
|
"Massachusetts", "Maryland", "Maine", "Michigan", "Minnesota", "Missouri", "Mississippi",
|
||||||
|
"Montana", "North Carolina", "North Dakota", "Nebraska", "New Hampshire", "New Jersey",
|
||||||
|
"New Mexico", "Nevada", "New York", "Ohio", "Oklahoma", "Oregon", "Pennsylvania",
|
||||||
|
"Puerto Rico", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas",
|
||||||
|
"Utah", "Virginia", "Virgin Islands", "Vermont", "Washington", "Wisconsin",
|
||||||
|
"West Virginia", "Wyoming"
|
||||||
|
])
|
||||||
|
search_bing(f"governor candidates {state} 2024")
|
||||||
|
else:
|
||||||
|
log_error(f"Explore on bing: {activity} not found.")
|
||||||
|
|
||||||
|
|
||||||
# Find out which type of action to do
|
# Find out which type of action to do
|
||||||
def try_play(nom="unknown"):
|
def try_play(nom="unknown", activity=""):
|
||||||
driver = config.WebDriver.driver
|
driver = config.WebDriver.driver
|
||||||
rgpd_popup(config)
|
rgpd_popup(config)
|
||||||
|
|
||||||
@ -339,7 +551,7 @@ def try_play(nom="unknown"):
|
|||||||
info("On fidelity page.")
|
info("On fidelity page.")
|
||||||
fidelity()
|
fidelity()
|
||||||
|
|
||||||
elif wait_until_visible(By.ID, "rqStartQuiz", 5, driver):
|
elif wait_until_visible(By.ID, "rqStartQuiz", 5, driver, raise_error=False):
|
||||||
custom_sleep(3)
|
custom_sleep(3)
|
||||||
driver.find_element(By.ID, "rqStartQuiz").click() # start the quiz
|
driver.find_element(By.ID, "rqStartQuiz").click() # start the quiz
|
||||||
answer_number = driver.page_source.count("rqAnswerOption")
|
answer_number = driver.page_source.count("rqAnswerOption")
|
||||||
@ -349,9 +561,14 @@ def try_play(nom="unknown"):
|
|||||||
number = driver.page_source.count("rqAnswerOption")
|
number = driver.page_source.count("rqAnswerOption")
|
||||||
warning(f"recovery detected. quiz : {number}")
|
warning(f"recovery detected. quiz : {number}")
|
||||||
play(number - 1)
|
play(number - 1)
|
||||||
|
elif "exploreonbing" in activity:
|
||||||
|
info(f"Explore on bing: {activity}")
|
||||||
|
explore_on_bing(activity, config)
|
||||||
|
custom_sleep(uniform(3, 5))
|
||||||
|
elif "Optimisez votre sécurité avec Microsoft" in driver.page_source:
|
||||||
|
info("Still not paying Office 365 ?")
|
||||||
else:
|
else:
|
||||||
info(f"Nothing to do on page `{nom}`")
|
info(f"Nothing obvious to do on page `{nom}`.")
|
||||||
custom_sleep(uniform(3, 5))
|
custom_sleep(uniform(3, 5))
|
||||||
|
|
||||||
|
|
||||||
@ -361,23 +578,40 @@ def login_part_1():
|
|||||||
info("Starting part 1 of login")
|
info("Starting part 1 of login")
|
||||||
driver = config.WebDriver.driver
|
driver = config.WebDriver.driver
|
||||||
driver.get("https://login.live.com")
|
driver.get("https://login.live.com")
|
||||||
wait_until_visible(By.ID, "i0116", browser=driver)
|
try:
|
||||||
mail_elem = driver.find_element(By.ID, "i0116")
|
wait_until_visible(By.ID, "i0116", browser=driver)
|
||||||
send_keys_wait(mail_elem, config.UserCredentials.get_mail())
|
send_wait_and_confirm(
|
||||||
mail_elem.send_keys(Keys.ENTER)
|
driver.find_element(By.ID, "i0116"),
|
||||||
|
config.UserCredentials.get_mail()
|
||||||
|
)
|
||||||
|
except exceptions.NoSuchElementException:
|
||||||
|
info("Unable to find mail input for login, might be semi-logged-in")
|
||||||
|
|
||||||
wait_until_visible(By.ID, "i0118", browser=driver)
|
wait_until_visible(By.ID, "i0118", browser=driver)
|
||||||
pwd_elem = driver.find_element(By.ID, "i0118")
|
send_wait_and_confirm(
|
||||||
send_keys_wait(pwd_elem, config.UserCredentials.get_password())
|
driver.find_element(By.ID, "i0118"),
|
||||||
pwd_elem.send_keys(Keys.ENTER)
|
config.UserCredentials.get_password()
|
||||||
custom_sleep(2)
|
)
|
||||||
# 2FA
|
# 2FA
|
||||||
if "Entrez le code de sécurité" in driver.page_source:
|
try:
|
||||||
try:
|
if not wait_until_visible(By.ID, "idTxtBx_SAOTCC_OTC", browser=driver, timeout=5, raise_error=False):
|
||||||
a2f_elem = driver.find_element(By.ID, "idTxtBx_SAOTCC_OTC")
|
custom_sleep(2)
|
||||||
a2f_elem.send_keys(config.UserCredentials.get_tfa().now())
|
return
|
||||||
a2f_elem.send_keys(Keys.ENTER)
|
|
||||||
except Exception as err:
|
tfa = config.UserCredentials.get_tfa()
|
||||||
log_error(err)
|
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
|
# Accept all cookies question, and check if the account is locked
|
||||||
@ -412,9 +646,19 @@ def login_part_2():
|
|||||||
# login() tries to login to your Microsoft account.
|
# login() tries to login to your Microsoft account.
|
||||||
# it uses global variable g._mail and g._password to login
|
# it uses global variable g._mail and g._password to login
|
||||||
def login():
|
def login():
|
||||||
|
def logged_in():
|
||||||
|
driver.get("https://login.live.com")
|
||||||
|
custom_sleep(10)
|
||||||
|
debug(get_domain(driver))
|
||||||
|
if get_domain(driver) == "account.microsoft.com":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
info("Logging in...")
|
||||||
driver = config.WebDriver.driver
|
driver = config.WebDriver.driver
|
||||||
try:
|
try:
|
||||||
login_part_1()
|
if not logged_in():
|
||||||
|
login_part_1()
|
||||||
login_part_2()
|
login_part_2()
|
||||||
driver.get("https://rewards.bing.com/")
|
driver.get("https://rewards.bing.com/")
|
||||||
except Banned:
|
except Banned:
|
||||||
@ -539,19 +783,23 @@ def fidelity():
|
|||||||
pause = driver.find_element(By.CSS_SELECTOR, f'[class="c-action-toggle c-glyph f-toggle glyph-pause"]')
|
pause = driver.find_element(By.CSS_SELECTOR, f'[class="c-action-toggle c-glyph f-toggle glyph-pause"]')
|
||||||
pause.click()
|
pause.click()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error(f"Error while clicking pause. Probably no cards. {e}")
|
debug("No pause button.")
|
||||||
return "no cards"
|
|
||||||
cartes = driver.find_elements(By.CSS_SELECTOR, f'[ng-repeat="item in $ctrl.transcludedItems"]')
|
cartes = driver.find_elements(By.CSS_SELECTOR, f'[ng-repeat="item in $ctrl.transcludedItems"]')
|
||||||
nb_cartes = len(cartes)
|
nb_cartes = len(cartes)
|
||||||
|
if nb_cartes == 0:
|
||||||
|
warning("No fidelity cards detected")
|
||||||
|
return "No cards."
|
||||||
checked_list_all = driver.find_elements(By.CSS_SELECTOR, f'[ng-if="$ctrl.complete"]')
|
checked_list_all = driver.find_elements(By.CSS_SELECTOR, f'[ng-if="$ctrl.complete"]')
|
||||||
for i in range(nb_cartes):
|
for i in range(nb_cartes):
|
||||||
cartes[i].click()
|
cartes[i].click()
|
||||||
checked_txt = checked_list_all[i].get_attribute("innerHTML")
|
checked_txt = checked_list_all[i].get_attribute("innerHTML")
|
||||||
ok = checked_txt.count("StatusCircleOuter checkmark")
|
ok = checked_txt.count("StatusCircleOuter checkmark")
|
||||||
total = checked_txt.count("StatusCircleOuter")
|
total = checked_txt.count("StatusCircleOuter")
|
||||||
if (ok != total):
|
if ok != total:
|
||||||
elm = driver.find_elements(By.CLASS_NAME, 'clickable-link')[i]
|
elm = driver.find_elements(By.CLASS_NAME, 'clickable-link')[i]
|
||||||
if not "moviesandtv" in elm.get_attribute("innerHTML"): # not the film card
|
# legacy code. Should be removed
|
||||||
|
if "moviesandtv" not in elm.get_attribute("innerHTML"): # not the film card
|
||||||
elm.click()
|
elm.click()
|
||||||
driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1])
|
driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1])
|
||||||
sub_fidelity()
|
sub_fidelity()
|
||||||
@ -669,53 +917,53 @@ def json_start(json_entry, cred: UserCredentials):
|
|||||||
config.WebDriver.set_pc_driver(create_driver())
|
config.WebDriver.set_pc_driver(create_driver())
|
||||||
config.WebDriver.switch_to_driver("PC")
|
config.WebDriver.switch_to_driver("PC")
|
||||||
driver = config.WebDriver.driver
|
driver = config.WebDriver.driver
|
||||||
try:
|
if "unban" in json_entry and str(account_id) in json_entry["unban"]:
|
||||||
if str(account_id) in json_entry["unban"]:
|
login()
|
||||||
login_part_1()
|
info("\nGO TO example.com TO PROCEED or wait 1200 secs.")
|
||||||
info("\nGO TO example.com TO PROCEED or wait 1200 secs.")
|
for _ in range(1200):
|
||||||
for _ in range(1200):
|
sleep(1)
|
||||||
sleep(1)
|
if driver.current_url == "https://example.com/":
|
||||||
if driver.current_url == "https://example.com/":
|
info("proceeding")
|
||||||
info("proceeding")
|
break
|
||||||
break
|
else:
|
||||||
else:
|
|
||||||
login()
|
|
||||||
except KeyError:
|
|
||||||
login()
|
login()
|
||||||
try:
|
try:
|
||||||
if str(account_id) in json_entry["tout"]:
|
if str(account_id) in json_entry["tout"]:
|
||||||
daily_routine(cred, True)
|
daily_routine(cred, True)
|
||||||
|
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
if str(account_id) in json_entry["daily"]:
|
||||||
|
try:
|
||||||
|
all_cards()
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
if str(account_id) in json_entry["pc"]:
|
||||||
|
try:
|
||||||
|
bing_pc_search()
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
if str(account_id) in json_entry["mobile"]:
|
||||||
|
try:
|
||||||
|
bing_mobile_search(cred)
|
||||||
|
except Exception as e:
|
||||||
|
log_error(e)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
else:
|
try:
|
||||||
try:
|
log_points()
|
||||||
if str(account_id) in json_entry["daily"]:
|
except Exception as e:
|
||||||
try:
|
error(f"CustomStart {e}")
|
||||||
all_cards()
|
|
||||||
except Exception as e:
|
|
||||||
log_error(e)
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
if str(account_id) in json_entry["pc"]:
|
|
||||||
try:
|
|
||||||
bing_pc_search()
|
|
||||||
except Exception as e:
|
|
||||||
log_error(e)
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
if str(account_id) in json_entry["mobile"]:
|
|
||||||
try:
|
|
||||||
bing_mobile_search(cred)
|
|
||||||
except Exception as e:
|
|
||||||
log_error(e)
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
log_points()
|
|
||||||
except Exception as e:
|
|
||||||
error(f"CustomStart {e}")
|
|
||||||
driver.close()
|
driver.close()
|
||||||
cred.next_account()
|
cred.next_account()
|
||||||
account_id += 1
|
account_id += 1
|
||||||
@ -732,13 +980,17 @@ def default_start():
|
|||||||
while config.UserCredentials.is_valid():
|
while config.UserCredentials.is_valid():
|
||||||
custom_sleep(1)
|
custom_sleep(1)
|
||||||
info("Starting and configuring driver.")
|
info("Starting and configuring driver.")
|
||||||
config.WebDriver.set_pc_driver(create_driver())
|
try:
|
||||||
|
config.WebDriver.set_pc_driver(create_driver())
|
||||||
|
except:
|
||||||
|
info("Could not configure driver. Trying to update it.")
|
||||||
|
subprocess.Popen(["python3", "/app/MsRewards-Reborn/modules/Tools/update_chrome.py"])
|
||||||
|
config.WebDriver.set_pc_driver(create_driver())
|
||||||
config.WebDriver.switch_to_driver("PC")
|
config.WebDriver.switch_to_driver("PC")
|
||||||
info("Driver started.")
|
info("Driver started.")
|
||||||
config.WebDriver.pc_driver.implicitly_wait(3)
|
config.WebDriver.pc_driver.implicitly_wait(3)
|
||||||
try:
|
try:
|
||||||
wait_time = uniform(1200, 3600)
|
wait_time = uniform(1200, 3600)
|
||||||
wait_time = 10 # the display closes when I ctrl + c
|
|
||||||
info(f"Waiting for {round(wait_time / 60)}min before starting")
|
info(f"Waiting for {round(wait_time / 60)}min before starting")
|
||||||
custom_sleep(wait_time)
|
custom_sleep(wait_time)
|
||||||
daily_routine(config.UserCredentials)
|
daily_routine(config.UserCredentials)
|
||||||
@ -766,8 +1018,8 @@ def log_error(msg):
|
|||||||
|
|
||||||
|
|
||||||
def check_updated():
|
def check_updated():
|
||||||
debug(f"updated: {config.has_been_updated()}")
|
|
||||||
if config.has_been_updated():
|
if config.has_been_updated():
|
||||||
|
subprocess.Popen(["pip","install", "-r", "requirements.txt"])
|
||||||
config.discord.wh.send(f"Updated to {config.version}", username="update",
|
config.discord.wh.send(f"Updated to {config.version}", username="update",
|
||||||
avatar_url="https://cdn-icons-png.flaticon.com/512/1688/1688988.png")
|
avatar_url="https://cdn-icons-png.flaticon.com/512/1688/1688988.png")
|
||||||
|
|
||||||
|
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";
|
proxy_pass "http://127.0.0.1:6666";
|
||||||
chunked_transfer_encoding off;
|
chunked_transfer_encoding off;
|
||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
|
add_header X-Accel-Buffering no;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -59,7 +60,6 @@ sqlite3 /app/MsRewards-Reborn/MsRewards.db "CREATE TABLE comptes (id INTEGER PRI
|
|||||||
printf "\nconfigurating grafana\n"
|
printf "\nconfigurating grafana\n"
|
||||||
|
|
||||||
cp /app/MsRewards-Reborn/config/grafana.ini /etc/grafana/
|
cp /app/MsRewards-Reborn/config/grafana.ini /etc/grafana/
|
||||||
grafana-cli plugins install frser-sqlite-datasource
|
|
||||||
|
|
||||||
printf "setting up default dashboard"
|
printf "setting up default dashboard"
|
||||||
cp /app/MsRewards-Reborn/config/Stats-dashbord.json /usr/share/grafana/public/dashboards/home.json
|
cp /app/MsRewards-Reborn/config/Stats-dashbord.json /usr/share/grafana/public/dashboards/home.json
|
||||||
|
1459
config/grafana.ini
1459
config/grafana.ini
File diff suppressed because it is too large
Load Diff
12
hooks/bump.sh
Executable file
12
hooks/bump.sh
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
file=version
|
||||||
|
|
||||||
|
if [ "$(git diff HEAD version | wc -w)" -eq 0 ]
|
||||||
|
then
|
||||||
|
echo 'updating minor version'
|
||||||
|
new_path=$(cat $file | awk -F . '{print $1"."$2"."$3+1 }')
|
||||||
|
echo $new_path > $file
|
||||||
|
git add version
|
||||||
|
else
|
||||||
|
echo 'Version have been updated manually'
|
||||||
|
fi
|
||||||
|
echo Version: $(cat $file)
|
1
hooks/pre-commit
Executable file
1
hooks/pre-commit
Executable file
@ -0,0 +1 @@
|
|||||||
|
sh hooks/bump.sh
|
@ -22,7 +22,8 @@ class Config:
|
|||||||
proxy = json.load(inFile)
|
proxy = json.load(inFile)
|
||||||
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
|
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
|
||||||
config = json.load(inFile)
|
config = json.load(inFile)
|
||||||
|
with open("/app/MsRewards-Reborn/user_data/version", "r") as inFile:
|
||||||
|
version = inFile.readline()
|
||||||
"""
|
"""
|
||||||
setup standalone stuff
|
setup standalone stuff
|
||||||
"""
|
"""
|
||||||
@ -31,10 +32,9 @@ class Config:
|
|||||||
self.json_entry = args.json.replace("'", "\"")
|
self.json_entry = args.json.replace("'", "\"")
|
||||||
self.wordlist = WordList("/usr/share/dict/french")
|
self.wordlist = WordList("/usr/share/dict/french")
|
||||||
self.vnc = args.vnc
|
self.vnc = args.vnc
|
||||||
self.version = args.update_version
|
|
||||||
self.WebDriver = Driver()
|
self.WebDriver = Driver()
|
||||||
self.display = None
|
self.display = None
|
||||||
|
self.version = version
|
||||||
"""
|
"""
|
||||||
setup UserCredential
|
setup UserCredential
|
||||||
"""
|
"""
|
||||||
@ -50,9 +50,14 @@ class Config:
|
|||||||
"""
|
"""
|
||||||
self.discord = DiscordConfig()
|
self.discord = DiscordConfig()
|
||||||
self.discord.avatar_url = settings["avatarlink"]
|
self.discord.avatar_url = settings["avatarlink"]
|
||||||
self.discord.wh_link = discord[config[args.config]["discord"]]["errorsL"]
|
|
||||||
|
|
||||||
if 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())
|
self.discord.wh = Webhook.from_url(self.discord.wh_link, adapter=RequestsWebhookAdapter())
|
||||||
else:
|
else:
|
||||||
self.discord.wh = FakeWebHook()
|
self.discord.wh = FakeWebHook()
|
||||||
@ -76,4 +81,11 @@ class Config:
|
|||||||
self.display = display
|
self.display = display
|
||||||
|
|
||||||
def has_been_updated(self):
|
def has_been_updated(self):
|
||||||
return self.version != "None"
|
with open('/app/MsRewards-Reborn/version', "r") as inFile:
|
||||||
|
in_file_content = inFile.readlines()
|
||||||
|
if self.version != in_file_content[0]:
|
||||||
|
self.version = in_file_content[0]
|
||||||
|
with open('/app/MsRewards-Reborn/user_data/version', "w") as outFile:
|
||||||
|
outFile.write(self.version)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
@ -9,6 +9,6 @@ class DiscordConfig:
|
|||||||
|
|
||||||
|
|
||||||
class FakeWebHook:
|
class FakeWebHook:
|
||||||
def send(self, *args):
|
def send(self, *args, **kwargs):
|
||||||
debug(f"Used a webhook call without webhook url with {args}")
|
debug(f"Used a webhook call without webhook url with {args} {kwargs}")
|
||||||
|
|
||||||
|
@ -11,11 +11,11 @@ class Driver:
|
|||||||
self.mobile_driver = mobile_driver
|
self.mobile_driver = mobile_driver
|
||||||
|
|
||||||
def switch_to_driver(self, driver: str):
|
def switch_to_driver(self, driver: str):
|
||||||
match driver:
|
match driver.lower():
|
||||||
case "pc" | "PC" | "Pc":
|
case "pc":
|
||||||
self.driver = self.pc_driver
|
self.driver = self.pc_driver
|
||||||
|
|
||||||
case "mobile" | "Mobile":
|
case "mobile":
|
||||||
self.driver = self.mobile_driver
|
self.driver = self.mobile_driver
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
|
@ -30,7 +30,8 @@ class UserCredentials:
|
|||||||
|
|
||||||
def get_tfa(self):
|
def get_tfa(self):
|
||||||
if not self.tfa_enable():
|
if not self.tfa_enable():
|
||||||
warning("Warning: TFA is not enabled. Calling get_tfa is an expected behaviour.")
|
warning("Warning: TFA is not enabled. Can't get a TFA code.")
|
||||||
|
return None
|
||||||
return TOTP(self.data[self.current]["2fa"])
|
return TOTP(self.data[self.current]["2fa"])
|
||||||
|
|
||||||
def next_account(self):
|
def next_account(self):
|
||||||
@ -41,4 +42,5 @@ class UserCredentials:
|
|||||||
debug("No new credentials.")
|
debug("No new credentials.")
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
return self.current < self.total
|
return (self.current < self.total
|
||||||
|
and self.get_mail() != "" and self.get_mail is not None)
|
||||||
|
@ -5,7 +5,7 @@ import subprocess
|
|||||||
|
|
||||||
from logger import critical, info, error
|
from logger import critical, info, error
|
||||||
|
|
||||||
errorMessage = subprocess.run(['python3', 'generate_error.py'], check=False, stdout=subprocess.PIPE,
|
errorMessage = subprocess.run(['python3', '/app/MsRewards-Reborn/modules/Tools/generate_error.py'], check=False, stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE).stderr.decode("utf-8")
|
stderr=subprocess.PIPE).stderr.decode("utf-8")
|
||||||
|
|
||||||
versionPattern = "This version of ChromeDriver only supports Chrome version ([0-9]+)"
|
versionPattern = "This version of ChromeDriver only supports Chrome version ([0-9]+)"
|
||||||
@ -15,6 +15,8 @@ try:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
critical("Can't get version number from error")
|
critical("Can't get version number from error")
|
||||||
error(e)
|
error(e)
|
||||||
|
info("error message")
|
||||||
|
info(errorMessage)
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
info(f"Needed version : '{versionN}'")
|
info(f"Needed version : '{versionN}'")
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
from random import uniform
|
from random import uniform
|
||||||
|
|
||||||
from selenium.common import TimeoutException
|
from selenium.common import TimeoutException
|
||||||
from selenium.webdriver import ActionChains, Keys
|
from selenium.webdriver import Keys
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
from selenium.webdriver.support import expected_conditions
|
from selenium.webdriver.support import expected_conditions
|
||||||
from selenium.webdriver.support.wait import WebDriverWait
|
from selenium.webdriver.support.wait import WebDriverWait
|
||||||
@ -10,33 +12,40 @@ from modules.Tools.logger import debug
|
|||||||
from modules.Tools.tools import *
|
from modules.Tools.tools import *
|
||||||
|
|
||||||
|
|
||||||
def set_language(ldriver):
|
def init_profile(mail, mobile=False):
|
||||||
ldriver.get("chrome://settings/languages")
|
if not mobile:
|
||||||
action = ActionChains(ldriver)
|
chrome_profile_dir = "/app/MsRewards-Reborn/user_data/profile/" + mail
|
||||||
action.reset_actions()
|
else:
|
||||||
# select language
|
chrome_profile_dir = "/app/MsRewards-Reborn/user_data/profile/mobile-" + mail
|
||||||
x_coord = 1200
|
|
||||||
y_coord = 150
|
os.makedirs(chrome_profile_dir, exist_ok=True)
|
||||||
action.move_by_offset(x_coord, y_coord).click().perform()
|
|
||||||
sleep(0.5)
|
preferences_file = os.path.join(chrome_profile_dir, "Default", "Preferences")
|
||||||
# scroll down
|
if not os.path.exists(preferences_file):
|
||||||
action.reset_actions()
|
os.makedirs(os.path.join(chrome_profile_dir, "Default"), exist_ok=True)
|
||||||
elm = ldriver.find_element(By.XPATH, "/html/body")
|
with open(preferences_file, "w") as f:
|
||||||
ActionChains(ldriver) \
|
json.dump(
|
||||||
.send_keys("french") \
|
{
|
||||||
.pause(0.5) \
|
"intl": {
|
||||||
.send_keys(Keys.TAB + Keys.TAB + Keys.ENTER + Keys.TAB + Keys.TAB + Keys.ENTER) \
|
"accept_languages": "fr-FR,en-US,en",
|
||||||
.perform()
|
"selected_languages": "fr-FR,en-US,en"
|
||||||
x_coord = 1163
|
}
|
||||||
y_coord = 717
|
}, f
|
||||||
action.move_by_offset(x_coord, y_coord).click().perform()
|
)
|
||||||
# put to the top
|
else:
|
||||||
sleep(0.5)
|
with open(preferences_file, "r") as f:
|
||||||
action.reset_actions()
|
settings = json.load(f)
|
||||||
x_coord = 1257
|
|
||||||
y_coord = 328
|
if "intl" not in settings:
|
||||||
action.move_by_offset(x_coord, y_coord).click().perform()
|
settings["intl"] = {}
|
||||||
action.click().perform()
|
|
||||||
|
settings["intl"]["accept_languages"] = "fr-FR,en-US,en"
|
||||||
|
settings["intl"]["selected_languages"] = "fr-FR,en-US,en"
|
||||||
|
|
||||||
|
with open(preferences_file, "w") as f:
|
||||||
|
json.dump(settings, f)
|
||||||
|
|
||||||
|
return chrome_profile_dir
|
||||||
|
|
||||||
|
|
||||||
# Deal with RGPD popup as well as some random popup like 'are you satisfied' one
|
# Deal with RGPD popup as well as some random popup like 'are you satisfied' one
|
||||||
@ -61,12 +70,18 @@ def send_keys_wait(element, keys: str) -> None:
|
|||||||
sleep(uniform(0.1, 0.3))
|
sleep(uniform(0.1, 0.3))
|
||||||
|
|
||||||
|
|
||||||
|
def send_wait_and_confirm(element, keys: str) -> None:
|
||||||
|
send_keys_wait(element, keys)
|
||||||
|
element.send_keys(Keys.ENTER)
|
||||||
|
|
||||||
|
|
||||||
# Wait for the presence of the element identifier or [timeout]s
|
# Wait for the presence of the element identifier or [timeout]s
|
||||||
def wait_until_visible(search_by: str, identifier: str, timeout: int = 20, browser=None) -> bool:
|
def wait_until_visible(search_by: str, identifier: str, timeout: int = 20, browser=None, raise_error=True) -> bool:
|
||||||
try:
|
try:
|
||||||
WebDriverWait(browser, timeout).until(
|
WebDriverWait(browser, timeout).until(
|
||||||
expected_conditions.visibility_of_element_located((search_by, identifier)), "element not found")
|
expected_conditions.visibility_of_element_located((search_by, identifier)), "element not found")
|
||||||
return True
|
return True
|
||||||
except TimeoutException as e:
|
except TimeoutException as e:
|
||||||
error(f"element {identifier} not found after {timeout}s")
|
f = error if raise_error else debug
|
||||||
|
f(f"element {identifier} not found after {timeout}s")
|
||||||
return False
|
return False
|
||||||
|
1
user_data/version
Normal file
1
user_data/version
Normal file
@ -0,0 +1 @@
|
|||||||
|
0.0.0
|
Loading…
Reference in New Issue
Block a user