Compare commits

..

No commits in common. "d4920ec32249b31a6884e8f9af6c72e6eaea8ef5" and "fc8e6669276a96ec8d61a747960dca45d2fc7d62" have entirely different histories.

18 changed files with 830 additions and 297 deletions

582
V6.py
View File

@ -5,26 +5,21 @@ from modules.driver_tools import *
from modules.cards import *
import modules.globals as g
from modules.Classes.Config import Config
from modules.Classes.UserCredentials import UserCredentials
from modules.Tools.logger import warning, info, debug, error, critical
driver = g.driver
display = g.display
# create a webdriver
def create_driver(mobile=False):
def create_driver(mobile=False, headless=False):
PC_USER_AGENT = (
"Mozilla/5.0 (X11; Linux x86_64)"
"AppleWebKit/537.36 (KHTML, like Gecko)"
"Chrome/122.0.0.0 Safari/537.36 Edg/122.0.2088.46"
"Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.46"
)
MOBILE_USER_AGENT = (
"Mozilla/5.0 (Linux; Android 7.0; Nexus 5 Build/MRA58N)"
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N)"
"AppleWebKit/537.36 (KHTML, like Gecko)"
"Chrome/22 Mobile Safari/537.36"
"Chrome/%s Mobile Safari/537.36"
)
chrome_options = webdriver.ChromeOptions()
if mobile:
@ -33,20 +28,20 @@ def create_driver(mobile=False):
chrome_options.add_argument(f"--user-agent={PC_USER_AGENT}")
# disabled as it may cause detection
if g.proxy_enabled:
chrome_options.add_argument(f'--proxy-server={g.proxy_address}:{g.proxy_port}')
PROXY = f"{g.proxy_address}:{g.proxy_port}" # IP:PORT or HOST:PORT
chrome_options.add_argument(f'--proxy-server={PROXY}')
driver = uc.Chrome(options=chrome_options)
set_language(driver)
return driver
return (driver)
def log_error(error_message, l_driver=driver, log=g.full_log):
def log_error(error, l_driver=driver, log=g.full_log):
global driver
if l_driver is None:
l_driver = driver
if type(error_message) is not str:
error_message = format_error(error_message)
error(str(error_message))
if type(error) is not str:
error = format_error(error)
printf(f"\n\n\033[93m Erreur : {str(error)} \033[0m\n\n")
if g.discord_enabled_error:
with open("page.html", "w") as f:
try:
@ -61,13 +56,13 @@ def log_error(error_message, l_driver=driver, log=g.full_log):
if not log:
embed = Embed(
title="An Error has occured",
description=str(error_message),
description=str(error),
colour=Colour.red(),
)
else:
embed = Embed(
title="Full log is enabled",
description=str(error_message),
description=str(error),
colour=Colour.blue(),
)
file = File("screenshot.png")
@ -78,68 +73,73 @@ def log_error(error_message, l_driver=driver, log=g.full_log):
# close the tab currently on and go back to the one first, or the one specified
def close_tab(tab, switch_to: int = 0) -> None:
def close_tab(tab, SwitchTo=0) -> None:
driver.switch_to.window(tab)
driver.close()
driver.switch_to.window(driver.window_handles[switch_to])
driver.switch_to.window(driver.window_handles[SwitchTo])
# play_quiz[N]([int : override]) make the quiz with N choice each time. They usually have between 4 and 10 questions.
# override is the number of question, by default, it's the number of question in this specific quiz.
# Can be useful in some case, where the program crashes before finishing the quiz
# override is the number of question, by default, it's the number of question in this specific quiz. Can be useful in some case, where the program crashes before finishing the quiz
def play_quiz2(override=10) -> None:
info("Starting to play quiz 2.")
debug(f"override: {override}")
printf("starting play_quiz2")
for j in range(override):
custom_sleep(uniform(3, 5))
js_function = """
function get_correct_answer(){
function br(n) { for (var r, t = 0, i = 0; i < n.length; i++)t += n.charCodeAt(i); return r = parseInt(_G.IG.substr(_G.IG.length - 2), 16), t += r, t.toString() } // Ms check function
function namedRAValue() { //allow calls to getRAvalue
return _w.getRAValue()
};
if (br(document.getElementById("rqAnswerOption0").attributes["data-option"].value) == namedRAValue()){
return(0);
}
else {
return(1);
}
};
return(get_correct_answer())
"""
correct_answer_value = driver.execute_script(js_function)
try:
# rgpd_popup(driver)
custom_sleep(uniform(3, 5))
js_function = """
function get_correct_answer(){
function br(n) { for (var r, t = 0, i = 0; i < n.length; i++)t += n.charCodeAt(i); return r = parseInt(_G.IG.substr(_G.IG.length - 2), 16), t += r, t.toString() } // Ms check function
function namedRAValue() { //allow calls to getRAvalue
return _w.getRAValue()
};
if (br(document.getElementById("rqAnswerOption0").attributes["data-option"].value) == namedRAValue()){
return(0);
}
else {
return(1);
}
};
return(get_correct_answer())
"""
correct_answer_value = driver.execute_script(js_function)
answer_elem = driver.find_element(By.ID, f"rqAnswerOption{correct_answer_value}")
answer_elem.click()
except exceptions.ElementNotInteractableException:
answer_elem = driver.find_element(By.ID, f"rqAnswerOption{correct_answer_value}")
if g.log:
progressBar(j, 10, name="quiz 2")
except exceptions.ElementNotInteractableException as e:
driver.execute_script("arguments[0].click();", answer_elem)
except Exception as e:
log_error(e)
break
info("Quiz 2 done.")
printf("play_quiz2 done")
custom_sleep(3)
def play_quiz8():
info(f"Starting Quiz 8")
override = len(findall("<span id=\"rqQuestionState.\" class=\"emptyCircle\"></span>", driver.page_source)) + 1
debug(f"override : {override}")
correct_answers = ["Should", "be", "reset", "before", "you", "see", "this."] # supress warning
printf(f"play_quiz8 : start, override : {override}")
try:
counter = 0
# rgpd_popup(driver)
for _ in range(override):
sleep(uniform(3, 5))
correct_answers = []
for i in range(1, 9): # todo: remove this odd 1-offset
for i in range(1, 9):
try:
element = driver.find_element(By.ID, f"rqAnswerOption{i - 1}")
if 'iscorrectoption="True"' in element.get_attribute("outerHTML"):
correct_answers.append(f'rqAnswerOption{i - 1}')
except Exception as e:
warning(f"can't find rqAnswerOption{i - 1}. Probably already clicked" + str(e))
printf(f"can't find rqAnswerOption{i - 1}. Probably already clicked" + str(e))
shuffle(correct_answers)
for answer_id in correct_answers:
wait_until_visible(By.ID, answer_id, timeout=20, browser=driver)
counter += 1
if g.log:
progressBar(counter, 16, name="Quiz 8")
try:
answer_elem = driver.find_element(By.ID, answer_id)
answer_elem.click()
@ -155,28 +155,27 @@ def play_quiz8():
except Exception as e:
log_error(f"{format_error(e)} \n Good answers : {' '.join(correct_answers)}")
info("Quiz 8 done.")
custom_sleep(3)
def play_quiz4(override: int = None):
info(f"Starting Quiz 4")
printf("play_quiz4 : start")
if not override:
try: # fidelity quiz are much longer than usual ones
override = int(findall('rqQuestionState([\d]{1,2})"', driver.page_source)[-1])
printf(f"Override : {override}")
except:
override = 3
debug(f"Override : {override}")
try:
for i in range(override):
custom_sleep(uniform(3, 5))
txt = driver.page_source
# rgpd_popup(driver)
answer_option = search('correctAnswer":"([^"]+)', txt)[1]
answer_option = answer_option.replace("\\u0027", "'") # replace Unicode weird symbols
answer_element = driver.find_element(By.CSS_SELECTOR, f'[data-option="{answer_option}"]')
try:
answer_element = driver.find_element(By.CSS_SELECTOR, f'[data-option="{answer_option}"]')
answer_element.click()
except exceptions.ElementNotInteractableException:
driver.execute_script("arguments[0].click();", answer_element)
@ -184,30 +183,27 @@ def play_quiz4(override: int = None):
except Exception as e:
log_error(e)
raise ValueError(e)
info("Quiz 8 done.")
custom_sleep(3)
# do_poll() answer a random thing to poll, on of daily activities
def do_poll():
info("Starting poll")
printf("do_poll : start")
try:
answer_elem = driver.find_element(By.ID, f"btoption{choice([0, 1])}")
try:
answer_elem = driver.find_element(By.ID, f"btoption{choice([0, 1])}")
answer_elem.click()
except exceptions.ElementNotInteractableException:
driver.execute_script("arguments[0].click();", answer_elem)
custom_sleep(uniform(2, 2.5))
except Exception as err:
log_error(err)
raise ValueError(err)
info("Poll done.")
except Exception as error:
log_error(error)
raise ValueError(error)
custom_sleep(3)
# Find each playable card and tries to click on it to earn points
# todo : refactor
def all_cards():
# input("1")
driver.get("https://rewards.bing.com")
wait_until_visible(By.CLASS_NAME, "c-card-content", 10, driver)
liste = driver.find_elements(By.CLASS_NAME, "c-card-content")
@ -215,7 +211,7 @@ def all_cards():
try:
promo()
except Exception as e:
info("no promo card")
printf("no promo card")
if (len(liste) < 10): # most likely an error during loading
if "suspendu" in driver.page_source:
raise Banned()
@ -328,7 +324,7 @@ def promo():
try:
spotify(driver)
except:
warning("no new windows")
printf("no new windows", driver)
driver.get("https://rewards.bing.com")
driver.refresh()
custom_sleep(3)
@ -339,30 +335,33 @@ def try_play(nom="inconnu"):
rgpd_popup(driver)
def play(number):
if number in [8, 9]:
if number == 8 or number == 9:
try:
debug(f"Quiz 8 detected on `{nom}`.")
printf(f"\033[96mQuiz 8 detected on `{nom}` \033[0m")
play_quiz8()
except Exception as err:
error(f"fail of PlayQuiz 8. Aborted {err}")
printf(f"\033[92mQuiz 8 succeeded on `{nom}` \033[0m")
custom_sleep(uniform(3, 5))
except Exception as e:
printf(f"fail of PlayQuiz 8. Aborted {e} \033[0m")
elif number in [4, 5]:
elif number == 5 or number == 4:
try:
debug(f"Quiz 4 detected on `{nom}`")
printf(f"\033[96mQuiz 4 detected on `{nom}` \033[0m")
play_quiz4()
except Exception as err:
error(f"Fail of PlayQuiz 4. Aborted {err}.")
printf(f"\033[92mQuiz 4 succeeded on `{nom}` \033[0m")
custom_sleep(uniform(3, 5))
except Exception as e:
printf(f"Fail of PlayQuiz 4. Aborted {e} \033[0m")
elif number in [2, 3]:
elif number == 3 or number == 2:
try:
debug(f"\033[96mQuiz 2 detected on `{nom}`\033[0m")
printf(f"\033[96mQuiz 2 detected on `{nom}`\033[0m")
play_quiz2()
except Exception as err:
error(f"fail of PlayQuiz 2. Aborted {err}")
printf(f"\033[92mQuiz 2 succeeded on `{nom}`\033[0m")
except Exception as e:
printf(f"fail of PlayQuiz 2. Aborted {e}")
else:
error("`rqAnswerOption` present in page but no action to do.")
custom_sleep(uniform(3, 5))
printf("There is an error. rqAnswerOption present in page but no action to do. skipping.")
if "pas connecté à Microsoft Rewards" in driver.page_source:
custom_sleep(5)
@ -370,44 +369,57 @@ def try_play(nom="inconnu"):
custom_sleep(5)
rgpd_popup(driver)
custom_sleep(5)
debug("Detected and fixed connection popup")
printf("not connected, fixed")
if "legaltextbox" in driver.current_url:
log_error("dev1")
driver.find_element(By.CLASS_NAME, "actionLink").click()
log_error("dev2")
try:
if wait_until_visible(By.ID, "rqStartQuiz", 5, driver):
custom_sleep(3)
driver.find_element(By.ID, "rqStartQuiz").click() # start the quiz
answer_number = driver.page_source.count("rqAnswerOption")
play(answer_number)
else:
raise (NameError("going to next part"))
except Exception as e: # if there is no start button, an error is thrown
if "bt_PollRadio" in driver.page_source:
try:
printf("Poll detected")
do_poll()
printf("Poll succeeded")
except Exception as e:
printf(f"try_play - 1 - Poll aborted {e}")
if "bt_PollRadio" in driver.page_source:
debug("Poll detected")
do_poll()
elif "rqQuestionState" in driver.page_source:
try:
number = driver.page_source.count("rqAnswerOption")
printf(f"recovery détecté. quiz : {number}")
play(number - 1)
except Exception as e:
printf(f"try_play - 2 - {e}")
elif "rqQuestionState" in driver.page_source:
number = driver.page_source.count("rqAnswerOption")
warning(f"recovery détecté. quiz : {number}")
play(number - 1)
elif search("([0-9]) de ([0-9]) finalisée", driver.page_source):
printf("fidélité")
fidelity()
elif search("([0-9]) de ([0-9]) finalisée", driver.page_source):
info("On fidelity page.")
fidelity()
elif wait_until_visible(By.ID, "rqStartQuiz", 5, driver):
custom_sleep(3)
driver.find_element(By.ID, "rqStartQuiz").click() # start the quiz
answer_number = driver.page_source.count("rqAnswerOption")
play(answer_number)
else:
printf(f"Nothing to do on page `{nom}`")
custom_sleep(uniform(3, 5))
else:
printf(f"rien à faire sur la page {nom}")
custom_sleep(uniform(3, 5))
# Login with password or with cookies.
# The driver should be in the same state on both case
def login_part_1(ldriver, cred: UserCredentials):
def pwd_login(ldriver):
printf("pwd_login : start")
ldriver.get("https://login.live.com")
wait_until_visible(By.ID, "i0116", browser=ldriver)
mail_elem = ldriver.find_element(By.ID, "i0116")
send_keys_wait(mail_elem, cred.get_mail())
send_keys_wait(mail_elem, g._mail)
mail_elem.send_keys(Keys.ENTER)
wait_until_visible(By.ID, "i0118", browser=ldriver)
pwd_elem = ldriver.find_element(By.ID, "i0118")
send_keys_wait(pwd_elem, cred.get_password())
send_keys_wait(pwd_elem, g._password)
pwd_elem.send_keys(Keys.ENTER)
custom_sleep(2)
# 2FA
@ -420,55 +432,71 @@ def login_part_1(ldriver, cred: UserCredentials):
log_error(e)
def cookie_login(ldriver):
printf("cookies_login : start")
ldriver.get("https://login.live.com")
try:
load_cookies(ldriver)
except FileNotFoundError:
printf("No cookies file Found.")
return (False)
except Exception as e:
# log_error(f"Error performing cookies login. Trying with password instead. \n{str(e)}", driver)
print("error with cookies login. IDK why (yet)")
return (False)
try:
ldriver.refresh()
except Exception as e:
printf(format_error(e))
printf("FIX YOUR SITE MS.......")
return (True)
# Accept all cookies question, and check if the account is locked
def login_part_2(ldriver):
def login_part_2(ldriver, cookies=False):
custom_sleep(5)
if 'Abuse' in ldriver.current_url:
if ('Abuse' in ldriver.current_url):
raise Banned()
if 'identity' in ldriver.current_url:
if ('identity' in ldriver.current_url):
raise Identity()
if 'notice' in ldriver.current_url:
if ('notice' in ldriver.current_url):
ldriver.find_element(By.ID, "id__0").click()
if "proof" in ldriver.current_url:
if ("proof" in ldriver.current_url):
ldriver.find_element(By.ID, "iLooksGood")
for elm_id in ["iNext", "KmsiCheckboxField", "id__0", "iLooksGood", "idSIButton9", "iCancel"]:
if cookies:
save_cookies(ldriver)
for id in ["iNext", "KmsiCheckboxField", "id__0", "iLooksGood", "idSIButton9", "iCancel"]:
if get_domain(ldriver) == "account.microsoft.com":
break
try:
ldriver.find_element(By.ID, elm_id).click()
ldriver.find_element(By.ID, id).click()
except Exception as e:
pass
wait_until_visible(By.CSS_SELECTOR, '[data-bi-id="sh-sharedshell-home"]', 20, ldriver)
# login() tries to login to your Microsoft account.
# it uses global variable g._mail and g._password to login
def login(ldriver, cred: UserCredentials):
def login(ldriver):
try:
login_part_1(ldriver, cred)
login_part_2(ldriver)
pwd_login(ldriver)
login_part_2(ldriver, 0)
ldriver.get("https://rewards.bing.com/")
except Banned:
raise Banned()
except Identity:
raise Banned()
except Exception as e:
critical("Error not caught during login.")
log_error(e)
ldriver.quit()
return False
return (False)
# Makes 30 search as PC Edge
def bing_pc_search(override=randint(35, 40)):
mot = choice(Liste_de_mot).replace(" ", "+")
driver.get(f"https://www.bing.com/search?q={mot}")
driver.get(f"https://www.bing.com/search?q={mot}") # {choice(Liste_de_mot)}')
custom_sleep(uniform(1, 2))
rgpd_popup(driver)
send_keys_wait(
@ -482,7 +510,7 @@ def bing_pc_search(override=randint(35, 40)):
send_keys_wait(driver.find_element(By.ID, "sb_form_q"), word)
driver.find_element(By.ID, "sb_form_q").send_keys(Keys.ENTER)
except Exception as e:
error(e)
printf(e)
sleep(10)
driver.get(f'https://www.bing.com/search?q={word}')
sleep(3)
@ -492,32 +520,82 @@ def bing_pc_search(override=randint(35, 40)):
try:
driver.find_element(By.ID, "sb_form_q").clear()
except Exception as e:
error(e)
printf(e)
try:
driver.get('https://www.bing.com/search?q=plans')
driver.find_element(By.ID, "sb_form_q").clear()
except Exception as e:
log_error(f"clear la barre de recherche - {format_error(e)}") # what is this message ??? todo
log_error(f"clear la barre de recherche - {format_error(e)}")
# Unban an account, called with -u parameter. You will need a phone number
def unban() -> None:
driver.find_element(By.ID, "StartAction").click()
custom_sleep(2)
txt = driver.page_source
uuid0 = findall('wlspispHIPCountrySelect([a-z0-9]+)', txt)[0]
uuid1 = findall('wlspispHIPPhoneInput([a-z0-9]+)', txt)[0]
uuid2 = findall('wlspispHipSendCode([a-z0-9]+)', txt)[0]
uuid3 = findall('wlspispSolutionElement([a-z0-9]+)', txt)[0]
country_code_select = Select(driver.find_element(By.ID, "wlspispHIPCountrySelect" + uuid0))
country_code_input = input("enter Country code (FR, ...) ")
country_code_select.select_by_value(country_code_input)
wait_until_visible(By.ID, "wlspispHIPPhoneInput" + uuid1, browser=driver)
phone_input = input("phone number : +33")
phone_elem = driver.find_element(By.ID, "wlspispHIPPhoneInput" + uuid1)
phone_elem.send_keys(phone_input)
wait_until_visible(By.ID, "wlspispHipSendCode" + uuid2, browser=driver)
send_sms_elem = driver.find_element(By.ID, "wlspispHipSendCode" + uuid2)
send_sms_elem.click()
wait_until_visible(By.ID, "wlspispSolutionElement" + uuid3, browser=driver)
sms_code_elem = driver.find_element(By.ID, "wlspispSolutionElement" + uuid3)
sms_code_input = input("entrez le contenu du msg : ")
sms_code_elem.send_keys(sms_code_input)
send_box = driver.find_element(By.ID, "ProofAction")
send_box.click()
wait_until_visible(By.ID, "FinishAction", browser=driver)
end_elem = driver.find_element(By.ID, "FinishAction")
end_elem.click()
# Sends points to database, discord and whatever service you want
# todo: refactor
def log_points(account="unknown"):
def get_points():
driver.get("https://rewards.bing.com")
custom_sleep(1)
if "/proofs/" in driver.current_url:
webhookFailure.send(f"Is /proof Useful in logpoints?", username="Dev tests",
avatar_url="https://cdn.discordapp.com/attachments/793934298977009677/1144560898879541268/image.png")
for id in ["KmsiCheckboxField", "iLooksGood", "idSIButton9", "iCancel"]:
try:
driver.find_element(By.ID, id).click()
except Exception as e:
pass
wait_until_visible(By.CSS_SELECTOR, 'span[mee-element-ready="$ctrl.loadCounterAnimation()"]', browser=driver)
try:
point = search('availablePoints\":([\d]+)', driver.page_source)[1]
except Exception as e:
sleep(5)
log_error(
f"Dev error, checking why it doesn't work (waited a bit, is this still white ?) {format_error(e)}",
driver)
error("Can't get points.")
return -1
return point
driver, True)
driver.refresh()
sleep(5)
point = search('availablePoints\":([\d]+)', driver.page_source)[1]
return (point)
for _ in range(3):
try:
points = get_points()
break
except Exception as e:
custom_sleep(300)
log_error(e)
points = None
if not points:
log_error(f"impossible d'avoir les points")
points = get_points()
custom_sleep(uniform(3, 20))
account_name = account.split("@")[0]
@ -537,7 +615,6 @@ def log_points(account="unknown"):
log_error(e)
# todo: refactor and check if it works at all
def fidelity():
def sub_fidelity():
try:
@ -613,18 +690,17 @@ def mobile_alert_popup():
try:
alert = mobile_driver.switch_to.alert
alert.dismiss()
except exceptions.NoAlertPresentException:
except exceptions.NoAlertPresentException as e:
pass
except Exception as err:
log_error(err, mobile_driver)
except Exception as e:
log_error(e, mobile_driver)
# todo: be coherent with pc search regarding error management
def bing_mobile_search(cred: UserCredentials, override=randint(22, 25)):
def bing_mobile_search(override=randint(22, 25)):
global mobile_driver
mobile_driver = create_driver(mobile=True)
try:
login(mobile_driver, cred)
login(mobile_driver)
mot = choice(Liste_de_mot).replace(" ", "+")
mobile_driver.get(f"https://www.bing.com/search?q={mot}")
custom_sleep(uniform(1, 2))
@ -638,77 +714,145 @@ def bing_mobile_search(cred: UserCredentials, override=randint(22, 25)):
custom_sleep(uniform(3, 7))
mobile_alert_popup() # check for alert (asking for position or for allowing notifications)
mobile_driver.find_element(By.ID, "sb_form_q").clear()
except Exception as err:
error(err)
except Exception as e:
printf(e)
mobile_driver.refresh()
custom_sleep(30)
i -= 1
mobile_driver.quit()
except Exception as err:
log_error(err, mobile_driver)
except Exception as e:
log_error(e, mobile_driver)
mobile_driver.quit()
def daily_routine(cred: UserCredentials, custom=False):
def daily_routine(custom=False):
try:
if not custom: # custom already is logged in
login(driver, cred)
if not custom: # custom already login
login(driver)
except Banned:
log_error("This account is locked.", driver)
return
log_error("This account is locked. Fix that. (-U ?)", driver)
return ()
except Identity:
log_error("This account has an issue.", driver)
return
log_error("This account has an issue. Fix that.", driver)
return ()
try:
all_cards()
except Banned:
log_error("banned", driver)
raise Banned
except Exception as err:
log_error(err)
return ("BANNED")
except Exception as e:
log_error(e)
try:
fidelity()
except Exception as err:
log_error(err)
except Exception as e:
log_error(e)
try:
bing_pc_search()
except Exception as err:
log_error(err)
except Exception as e:
log_error(e)
try:
bing_mobile_search(cred)
except Exception as err:
log_error(err)
bing_mobile_search()
except Exception as e:
log_error(e)
try:
log_points(cred.get_mail())
except Exception as err:
log_error(err)
log_points(g._mail)
except Exception as e:
log_error(e)
def json_start(json_entry, cred: UserCredentials):
def dev():
pass
def CustomStart():
if not g.islinux:
raise NameError(
'You need to be on linux to do that, due to the utilisation of a module named enquieries, sorry.')
global driver
system("clear") # clear from previous command to allow a clean choice
actions = ["tout", "daily", "pc", "mobile", "log_points", "fidelity", "dev"]
Actions = enquiries.choose("quels Actions ?", actions, multi=True)
liste = select_accounts()
g.start_time = time() # Reset timer to the start of the actions
for cred in liste:
g._mail = cred[0]
g._password = cred[1]
if len(cred) == 3:
g._otp = TOTP(cred[2])
driver = create_driver()
driver.implicitly_wait(3)
if login(driver) != "STOP":
if "tout" in Actions:
daily_routine(True)
if "daily" in Actions:
# input("0")
try:
all_cards()
except Exception as e:
log_error(e)
if "pc" in Actions:
try:
bing_pc_search()
except Exception as e:
log_error(e)
if "mobile" in Actions:
try:
bing_mobile_search()
except Exception as e:
log_error(e)
if "fidelity" in Actions:
try:
fidelity()
except Exception as e:
log_error(e)
if "dev" in Actions:
try:
dev()
except Exception as e:
printf(e)
break
if not "tout" in Actions:
try:
log_points(g._mail)
except Exception as e:
printf(f"CustomStart {e}")
driver.quit()
def very_custom_start(json):
global driver
display = SmartDisplay(backend="xvnc", size=(1920, 1080), rfbport=2345, color_depth=24)
display.start()
for i in range(5):
cred = g._cred[i]
g._mail = cred[0]
g._password = cred[1]
if cred[2] != '':
g._otp = TOTP(cred[2])
start = False
for j in ["unban", "tout", "pc", "mobile", "daily"]:
try:
if str(i) in json_entry[j]:
if str(i) in json[j]:
start = True
info(f"{cred.get_mail()} : {j}")
print(f"{g._mail} : {j}")
except KeyError:
pass
# print(f"none is set to \"{j}\"")
if start:
driver = create_driver()
try:
if str(i) in json_entry["unban"]:
login_part_1(driver, cred)
if str(i) in json["unban"]:
pwd_login(driver) # TODO : define only the first part of login
print("\nGO TO example.com TO PROCEED or wait 1200 secs.")
for _ in range(1200):
sleep(1)
@ -716,18 +860,18 @@ def json_start(json_entry, cred: UserCredentials):
print("proceeding")
break
else:
login(driver, cred)
login(driver)
except KeyError:
login(driver, cred)
login(driver)
try:
if str(i) in json_entry["tout"]:
daily_routine(cred)
if str(i) in json["tout"]:
daily_routine(True)
except KeyError:
pass
# print("none is set to \"tout\"")
else:
try:
if str(i) in json_entry["daily"]:
if str(i) in json["daily"]:
try:
all_cards()
except Exception as e:
@ -736,71 +880,87 @@ def json_start(json_entry, cred: UserCredentials):
pass
# print("none is set to \"daily\"")
try:
if str(i) in json_entry["pc"]:
if str(i) in json["pc"]:
try:
bing_pc_search()
except Exception as e:
log_error(e)
except KeyError:
pass
# print("none is set to \"pc\"")
try:
if str(i) in json_entry["mobile"]:
if str(i) in json["mobile"]:
try:
bing_mobile_search(cred)
bing_mobile_search()
except Exception as e:
log_error(e)
except KeyError:
pass
# print("none is set to \"mobile\"")
try:
log_points(g._mail)
except Exception as e:
printf(f"CustomStart {e}")
driver.close()
cred.next_account()
display.stop()
c = Config(args)
if g.json_start:
dict_data = json.loads(g.json_start.replace("'", "\""))
json_start(dict_data, c.UserCredentials)
if g.very_custom:
dict_data = json.loads(g.very_custom.replace("'", "\""))
very_custom_start(dict_data)
else:
if g.vnc_enabled or g.dev:
display = SmartDisplay(backend="xvnc", size=(1920, 1080), rfbport=g.vnc_port, color_depth=24)
else:
display = SmartDisplay(size=(1920, 1080))
display.start()
if g.update_version != "None":
if g.discord_enabled_error:
webhookFailure.send(f"Updated to {g.update_version}", username="UPDATE",
avatar_url="https://cdn-icons-png.flaticon.com/512/1688/1688988.png")
while c.UserCredentials.is_valid():
custom_sleep(1)
info("Starting driver.")
if g.dev:
driver = create_driver()
# input("kill ?")
driver.quit()
elif g.custom_start:
CustomStart()
elif g.unban:
g._mail, g._password = select_accounts(False)[0]
driver = create_driver()
info("Driver started.")
driver.implicitly_wait(3)
try:
wait_time = uniform(1200, 3600)
info(f"Waiting for {round(wait_time / 60)}min before starting")
daily_routine(c.UserCredentials)
driver.quit()
custom_sleep(wait_time)
except KeyboardInterrupt:
critical("Canceled by user. Closing driver and display.")
driver.quit()
display.stop()
break
except Exception as e:
log_error(f"Error not caught. Skipping this account. " + format_error(e), driver)
critical(f"Error not caught. Skipping this account. {e}")
driver.quit()
login(driver)
except Banned:
unban()
driver.quit()
else:
if g.update_version != "None":
if g.discord_enabled_error:
webhookFailure.send(f"Updated to {g.update_version}", username="UPDATE",
avatar_url="https://cdn-icons-png.flaticon.com/512/1688/1688988.png")
for cred in g._cred:
if cred[0] != "":
g._mail = cred[0]
g._password = cred[1]
if cred[2] != '':
g._otp = TOTP(cred[2])
custom_sleep(1)
printf("Début du driver.")
driver = create_driver()
printf("Driver demarré.")
driver.implicitly_wait(3)
try:
attente = uniform(1200, 3600)
printf(f"Attente de {round(attente / 60)} min avant de démarrer")
daily_routine()
driver.quit()
custom_sleep(attente)
except KeyboardInterrupt:
printf("Canceled. Closing driver and display.")
driver.quit()
display.stop()
break
except Exception as e:
log_error(f"Error not catched. Skipping this account. " + format_error(e), driver)
printf(f"Error not catched. Skipping this account. {e}")
driver.quit()
c.UserCredentials.next_account()
display.stop()
display.stop()

43
V7/V7.py Normal file
View File

@ -0,0 +1,43 @@
from playwright.sync_api import sync_playwright, expect, Page, BrowserContext
from playwright_stealth import stealth_sync
from pyvirtualdisplay.smartdisplay import SmartDisplay
from actions.cards import daily_cards, more_cards, all_cards
from actions.login import login
from actions.websearch import pc_search
from tools.browser_setup import create_display, start_browser
from tools.config import load_parameters, check_config
from tools.logger import *
import sys
import json
def routine(mail: str, pwd: str, vnc: bool = False) -> None:
name = mail.split("@")[0]
# display = create_display(vnc)
# display.start()
page, browser = start_browser(name)
login(page, mail, pwd)
all_cards(page)
pc_search(page)
# display.stop()
browser.close()
def main():
data = load_parameters()
check_config(data)
# routine("EMAIL", "PWD", True)
# parse_file({"accounts": {"nb": 1, 0: {"mail": "piair338@gmail.com", "pwd": "<PASSWORD>"}}})
"""
TODO :
Fidelity management.
Daily search mobile
start -> with json config
-> allow claiming
-> default mode
--vnc-- Should work, but not tested with WSL.
remove unused imports
"""

61
V7/actions/cards.py Normal file
View File

@ -0,0 +1,61 @@
from playwright.sync_api import sync_playwright, expect, Page, BrowserContext
from playwright_stealth import stealth_sync
from actions.quiz import play
from tools.logger import *
from tools.logger import *
def all_cards(page: Page):
# input("1")
page.goto("https://rewards.bing.com")
page.wait_for_load_state('load')
daily_cards(page)
more_cards(page)
def daily_cards(page: Page):
for i in range(1, 4):
card = page.locator(f'#daily-sets > mee-card-group:nth-child(7) > div > mee-card:nth-child({i}) > div')
if "mee-icon-AddMedium" not in card.inner_html():
info(f'Daily Card {i} : {"Validated" if "mee-icon-AddMedium" not in card.inner_html() else "Failure"}')
continue
with page.expect_popup() as page1_info:
card.click()
page1 = page1_info.value
stealth_sync(page1)
page1.wait_for_load_state('load')
try:
selector = page1.get_by_role("button", name="Accepter")
expect(selector).to_be_visible(timeout=10_000)
selector.click()
except AssertionError as e:
pass # The RGPD button should not be there in the first place
play(page1)
page1.close()
page.reload()
info(f'Daily Card {i} : {"Validated" if "mee-icon-AddMedium" not in card.inner_html() else "Failure"}')
def more_cards(page: Page):
c = 1
while c < 15: # todo I don't really know why it stops without raising any errors, but I guess it's fine
card = page.locator(f"#more-activities > div > mee-card:nth-child({c})")
try:
expect(card).to_be_visible(timeout=10_000)
except Exception as e: # The card probably does not exist
debug(e)
break
if "mee-icon-AddMedium" in card.inner_html():
info(f"Playing more cards {c}.")
with page.expect_popup() as page1_info:
card.click()
page1 = page1_info.value
stealth_sync(page1)
page1.wait_for_load_state('load')
play(page1)
page1.close()
page.reload()
info(f'More Card {c} : {"Validated" if "mee-icon-AddMedium" not in card.inner_html() else "Failure"}')
c += 1

10
V7/actions/checks.py Normal file
View File

@ -0,0 +1,10 @@
from playwright.sync_api import Playwright, sync_playwright, expect, Page
from playwright_stealth import stealth_sync
from logging import *
def check_logged_in(page: Page):
if "pas connecté à Microsoft Rewards" in page.content():
page.locator('css=[onclick="setsrchusr()"]').click()
page.wait_for_load_state('load')
info('Fixed logging in in card.')

30
V7/actions/login.py Normal file
View File

@ -0,0 +1,30 @@
from playwright.sync_api import Playwright, sync_playwright, expect, Page
from logging import *
def login(page: Page, mail: str, pwd: str) -> None:
page.goto("https://rewards.bing.com")
page.wait_for_load_state('load')
if page.url == "https://rewards.bing.com/":
info("Logged in via cookies")
return
else:
info("logging in")
page.get_by_placeholder("Email, phone, or Skype").click()
page.get_by_placeholder("Email, phone, or Skype").fill(mail)
page.get_by_placeholder("Email, phone, or Skype").press("Enter")
page.get_by_placeholder("Password").click()
page.get_by_placeholder("Password").fill(pwd)
page.get_by_placeholder("Password").press("Enter")
page.get_by_label("Don't show this again").check()
page.get_by_role("button", name="Yes").click()
page.wait_for_url("https://rewards.bing.com/")
page.wait_for_load_state('load')
page.goto("https://bing.com")
page.get_by_role("link", name="Sign in").click()
page.get_by_role("link", name="Accept").click()
page.wait_for_load_state('load')
page.wait_for_timeout(3000)
page.goto("https://rewards.bing.com")
page.wait_for_load_state('load')
info("Logged in via password.")

120
V7/actions/quiz.py Normal file
View File

@ -0,0 +1,120 @@
from random import choice, shuffle
from playwright.sync_api import Playwright, sync_playwright, expect, Page
from playwright_stealth import stealth_sync
from logging import *
from re import findall, search
from actions.checks import check_logged_in
def play(page: Page):
check_logged_in(page)
quiz_type = detect_quiz_type(page)
match quiz_type:
case 8 | 9:
info('Detected quiz_8.')
play_quiz_8(page)
case 5 | 4:
info('Detected quiz_4.')
play_quiz_4(page)
case 3 | 2:
info('Detected quiz_2.')
play_quiz_2(page)
case 1:
info('Detected poll.')
play_poll(page)
case 0:
info('Detected empty page.')
def detect_quiz_type(page: Page) -> int:
info("Detecting quiz type.")
if "bt_PollRadio" in page.content():
return 1
else:
try:
# RGPD
selector = page.locator("#rqStartQuiz")
expect(selector).to_be_visible(timeout=10_000)
selector.click()
return page.content().count("rqAnswerOption") - 1
except AssertionError as e:
if "rqQuestionState" in page.content():
# The quiz is already started
warning("Detected via recovery mode.")
return page.content().count("rqAnswerOption") - 1
return 0
def play_quiz_8(page: Page):
info("Playing quiz 8.")
num_question = len(findall("<span id=\"rqQuestionState.\" class=\"emptyCircle\"></span>", page.content())) + 1
info(f"Looping on {num_question} questions.")
counter = 0
# rgpd_popup(driver)
for _ in range(num_question):
correct_answers = []
for i in range(1, 9):
element = page.locator(f'#rqAnswerOption{i - 1}')
# todo can probably be optimised using filter and has_text
if 'iscorrectoption="True"' in element.evaluate("el => el.outerHTML"):
correct_answers.append(f'#rqAnswerOption{i - 1}')
shuffle(correct_answers)
for answer_id in correct_answers:
page.locator(answer_id).click()
page.wait_for_timeout(1000)
page.wait_for_timeout(3000) # todo check if there is a better method than hardcoded timout
page.wait_for_timeout(3000) # todo check if there is a better method than hardcoded timout
def play_quiz_4(page: Page):
info("Playing quiz 4.")
num_question = len(findall("<span id=\"rqQuestionState.\" class=\"emptyCircle\"></span>", page.content())) + 1
info(f"Looping on {num_question} questions.")
for i in range(num_question):
txt = page.content()
answer_option = search('correctAnswer":"([^"]+)', txt)[1]
answer_option = answer_option.replace("\\u0027", "'") # replace Unicode weird symbols
page.locator(f'css=[data-option="{answer_option}"]').click()
info(f'Validated answer n°{i + 1}.')
page.wait_for_load_state('load')
debug(f'Next page loaded.')
info("Quiz 4 successful.")
def play_quiz_2(page: Page):
info("Playing quiz 2.")
for j in range(10): # todo de-hardcode the value
js_function = """
function get_correct_answer(){
function br(n) { for (var r, t = 0, i = 0; i < n.length; i++)t += n.charCodeAt(i); return r = parseInt(_G.IG.substr(_G.IG.length - 2), 16), t += r, t.toString() } // Ms check function
function namedRAValue() { //allow calls to getRAvalue
return _w.getRAValue()
};
if (br(document.getElementById("rqAnswerOption0").attributes["data-option"].value) == namedRAValue()){
return(0);
}
else {
return(1);
}
};
return(get_correct_answer())
"""
correct_answer_value = page.evaluate(js_function)
page.locator(f"#rqAnswerOption{correct_answer_value}").click()
page.wait_for_timeout(2000)
info("Quiz 2 successful.")
page.wait_for_timeout(3000) # todo check if there is a better method than hardcoded timout
def play_poll(page: Page):
info("Playing poll.")
answer_elem = page.locator(f"#btoption{choice([0, 1])}")
answer_elem.click()
info("Poll successful.")
page.wait_for_timeout(3000) # todo check if there is a better method than hardcoded timout

25
V7/actions/websearch.py Normal file
View File

@ -0,0 +1,25 @@
from random import choice
from playwright.sync_api import sync_playwright, expect, Page
from playwright_stealth import stealth_sync
h = open("../user_data/french", "r", encoding="utf-8")
lines = h.readlines()
if len(lines) < 3:
Liste_de_mot = list(lines[0].split(","))
else:
Liste_de_mot = [x.replace('\n', "") for x in lines]
h.close()
def pc_search(page: Page) -> None:
mot = choice(Liste_de_mot).replace(" ", "+")
page.goto(f"https://www.bing.com/search?q={mot}")
for _ in range(35): # todo de-hardcode this variable
word = choice(Liste_de_mot)
page.get_by_label("Enter your search here -").click()
page.get_by_label("Enter your search here -").fill(word)
page.get_by_label("Enter your search here -").press("Enter")
page.wait_for_load_state("load")
page.wait_for_timeout(3000) # todo check if there is a better method than hardcoded timout

14
V7/config.example.json Normal file
View File

@ -0,0 +1,14 @@
{
"accounts": {
"nb": 1,
"0": {
"mail": "piair338@gmail.com",
"pwd": "uéuéué"
}
},
"config": {
"vnc_enabled": "False",
"vnc_port": 2345,
"claim": "False"
}
}

4
V7/requirement.txt Normal file
View File

@ -0,0 +1,4 @@
playwright
pyvirtualdisplay
playwright_stealth
pillow

18
V7/tools/browser_setup.py Normal file
View File

@ -0,0 +1,18 @@
from playwright.sync_api import sync_playwright, expect, Page, BrowserContext
from playwright_stealth import stealth_sync
from pyvirtualdisplay.smartdisplay import SmartDisplay
def create_display(vnc=False) -> SmartDisplay:
if vnc:
return SmartDisplay(backend="xvnc", size=(1920, 1080), rfbport=2345, color_depth=24)
return SmartDisplay(size=(1920, 1080))
def start_browser(name: str) -> (Page, BrowserContext):
p = sync_playwright().start()
browser = p.firefox.launch_persistent_context(f"./data/{name}/", headless=False, args=["--start-maximised"],
no_viewport=True)
page = browser.new_page()
stealth_sync(page)
return page, browser

47
V7/tools/config.py Normal file
View File

@ -0,0 +1,47 @@
from playwright.sync_api import sync_playwright, expect, Page, BrowserContext
from playwright_stealth import stealth_sync
from pyvirtualdisplay.smartdisplay import SmartDisplay
from actions.cards import daily_cards, more_cards, all_cards
from actions.login import login
from actions.websearch import pc_search
from tools.browser_setup import create_display, start_browser
from tools.logger import *
import sys
import json
def check_config(data: dict) -> None:
info("Checking config file.")
try:
assert data["accounts"]["nb"] != 0, "No accounts found in the file"
for i in range(data["accounts"]["nb"]):
assert data["accounts"][str(i)]["mail"] != "", f"Account {i} has no mail."
assert data["accounts"][str(i)]["pwd"] != "", f"Account {i} has no password."
assert data["config"]["vnc_enabled"] in ["True", "False"], f"vnc_enable should be either True or False."
assert data["config"]["claim"] in ["True", "False"], f"claim should be either True or False."
assert 1024 < data["config"]["vnc_port"] < 65535, f"vnc_port should be a valid port."
assert data["config"]["vnc_port"] not in [1234], f"vnc_port should be different than the webserver's ports."
except AssertionError as e:
critical(e)
exit(1)
except KeyError as e:
critical(f"Malformed config file. Missing {e}. Refer to the example config file.")
exit(1)
info("Successfully Checked config.")
def load_parameters() -> dict:
parameters = sys.argv
if len(parameters) == 1:
critical("Started with no parameters. Please provide at least a config file.")
exit(1)
if len(parameters) > 2:
warning("Started with multiple parameters. Some will be ignored")
try:
with open(parameters[1], "r") as f:
data = json.load(f)
return data
except FileNotFoundError:
critical("Could not open the config file.")
exit(1)

View File

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

View File

@ -1,15 +0,0 @@
import json
from modules.Classes.UserCredentials import UserCredentials
class Config:
def __init__(self, args):
self.args = args
self.UserCredentials = UserCredentials()
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
configs = json.load(inFile)
for i in configs[str(args.config)]["accounts"]:
d = configs[str(args.config)]["accounts"][i]
self.UserCredentials.add(d["mail"], d["pwd"], d["2fa"])

View File

@ -1,39 +0,0 @@
import json
from modules.Tools.logger import debug, warning
class UserCredentials:
def __init__(self):
self.data = {}
self.current = 0
self.total = 0
def add(self, username: str, password: str, tfa: str = None):
debug(f"adding account with data : Username: {username}, Password: {password}, 2FA: {'None' if tfa == '' else tfa}")
self.data[self.total] = {
"username": username,
"password": password,
"2fa": None if tfa == '' else tfa
}
self.total += 1
def tfa_enable(self):
return self.data[self.current]["2fa"] is not None
def get_mail(self):
return self.data[self.current]["username"]
def get_password(self):
return self.data[self.current]["password"]
def get_tfa(self):
if not self.tfa_enable():
warning("Warning: TFA is not enabled. Calling get_tfa is an expected behaviour.")
return self.data[self.current]["tfa"]
def next_account(self):
self.current += 1
debug(f"New credentials: {self.data[self.current]}")
def is_valid(self):
return self.current < self.total

View File

@ -4,29 +4,61 @@ from modules.imports import *
import modules.globals as g
import json
class FakeWebHook:
def send(self, text="", username='', avatar_url='', embed="", file=""):
def send(self, text = "", username='', avatar_url='', embed = "", file =""):
print(text)
parser = argparse.ArgumentParser()
parser.add_argument(
"-c",
"--config",
help="Choose a specific config file",
default=""
"-o",
"--override",
help="override",
dest="override",
action="store_true"
)
parser.add_argument(
"-v",
"-u",
"--unban",
help="unban an account",
dest="unban",
action="store_true"
)
parser.add_argument(
"-fl",
"--fulllog",
dest="fulllog",
help="enable full logging in discord",
action="store_true",
)
parser.add_argument(
"-d",
"--dev",
dest="dev",
help="enable dev mode",
action="store_true",
)
parser.add_argument(
"-c",
"--config",
help="Choose a specific config file",
default=""
)
parser.add_argument(
"-v",
"--vnc",
help="enable VNC",
dest="vnc"
)
parser.add_argument(
parser.add_argument(
"--version",
help="display a message on discord to tell that the bot have been updated",
dest="update_version",
@ -34,8 +66,8 @@ parser.add_argument(
)
parser.add_argument(
"--json",
help="input json to start the bot with custom parameters",
"--very-custom",
help="Choose a specific config file",
default=""
)
@ -48,21 +80,31 @@ with open("/app/MsRewards-Reborn/user_data/proxy.json", "r") as inFile:
with open("/app/MsRewards-Reborn/user_data/configs.json", "r") as inFile:
config = json.load(inFile)
args = parser.parse_args()
g.json_start = args.json
g.custom_start = args.override
g.unban = args.unban
g.full_log = args.fulllog
g.dev = args.dev
g.very_custom = args.very_custom
if g.custom_start :
g.log = True
g.vnc_enabled = args.vnc != "None"
g.vnc_port = args.vnc
g.update_version = args.update_version
# global variables used later in the code
g.islinux = platform == "linux" # if the computer running this program is Linux, it allow more things
g.start_time = time()
# path configurations
g.mot_path = "/usr/share/dict/french"
g.credential_path = "/app/MsRewards-Reborn/user_data/login.csv"
discord_conf = config[args.config]["discord"]
# discord configuration
@ -73,35 +115,39 @@ g.discord_enabled_success = discord[discord_conf]["successT"] == "True"
g.avatar_url = settings["avatarlink"]
if not g.json_start:
if not g.very_custom :
if g.discord_enabled_error:
webhookFailure = Webhook.from_url(g.discord_error_link, adapter=RequestsWebhookAdapter())
if g.discord_enabled_success:
webhookSuccess = Webhook.from_url(g.discord_success_link, adapter=RequestsWebhookAdapter())
else:
else :
webhookFailure = FakeWebHook()
webhookSuccess = FakeWebHook()
# base settings
g.discord_embed = False # send new point value in an embed, fixed for now
g.discord_embed = False # send new point value in an embed, fixed for now
g.headless = False
# proxy settings
g.proxy_enabled = config[args.config]["proxy"] != "-1"
if g.proxy_enabled:
if g.proxy_enabled :
g.proxy_address = proxy[config[args.config]["proxy"]]["address"]
g.proxy_port = proxy[config[args.config]["proxy"]]["port"]
g.fast = False
# list of words
with open(g.mot_path, "r", encoding="utf-8") as h:
lines = h.readlines()
if len(lines) < 3:
Liste_de_mot = list(lines[0].split(","))
else:
Liste_de_mot = [x.replace('\n', "") for x in lines]
h = open(g.mot_path, "r", encoding="utf-8")
lines = h.readlines()
if len(lines) < 3 :
Liste_de_mot = list(lines[0].split(","))
else :
Liste_de_mot = [x.replace('\n', "") for x in lines]
h.close()
if g.proxy_enabled:
setup_proxy(g.proxy_address, g.proxy_port)
Credentials = [ (config[args.config]['accounts'][x]["mail"],config[args.config]['accounts'][x]["pwd"],config[args.config]['accounts'][x]["2fa"]) for x in config[args.config]['accounts']]
g._cred = Credentials
if g.proxy_enabled :
setup_proxy(g.proxy_address,g.proxy_port)

View File

@ -8,7 +8,7 @@ def set_language(ldriver):
ldriver.get("chrome://settings/languages")
action = ActionChains(ldriver)
action.reset_actions()
# select language
# select langage
x_coord = 1200
y_coord = 150
action.move_by_offset(x_coord, y_coord).click().perform()

View File

@ -1,11 +1,18 @@
driver = None
_mail = '_mail temp'
_password = '_password temp'
_otp = '_otp temp'
display = None
log = False
_cred = []
custom_start = False
unban = False
log = False
full_log = False
vnc_enabled = False
vnc_port = 2345
points_file = "/"
update_version = False
islinux = True
start_time = 0
mot_path = "/"
credential_path = "/"
@ -25,6 +32,8 @@ sql_usr = "None"
sql_pwd = "azerty"
sql_host = "https://example.com"
sql_database = "MsRewards"
dev = False
norvege = False
database_error_override = False
json_start = ""
fast = False
very_custom=""

View File

@ -1 +1 @@
v6.8.0
v6.7.4