mirror of
https://gitea.augustin64.fr/piair/MsRewards-Reborn.git
synced 2025-07-11 15:36:36 +02:00
Compare commits
101 Commits
80286b6a05
...
v5.2.0
Author | SHA1 | Date | |
---|---|---|---|
eb2b9dc2d3 | |||
3385540350 | |||
0588180dda | |||
9995bc8e25 | |||
98ff0a183a | |||
d579a2c160 | |||
ccf284f6e2 | |||
f30832d8cd | |||
799d3d67d5 | |||
f10cd8d226 | |||
e41d28c142 | |||
b0c6a93951 | |||
012e923ab5 | |||
0cb0521da6 | |||
c5beafe036 | |||
098b934e96 | |||
339775bdf4 | |||
a2b07b9fcd | |||
d5bacd99a1 | |||
43035e115d | |||
c1bbb26c26 | |||
95156bacd8 | |||
f7c6d3f65e | |||
4ab2530f98 | |||
3ca16d1f37 | |||
db4ab3bf90 | |||
6fefaca00a | |||
a2328c2ca7 | |||
37f002049a | |||
8f655a04fb | |||
860c7b536a | |||
e93d4f0baf | |||
a72c74ec05 | |||
5282b4b434 | |||
0263f2e4c1 | |||
3324fa478d | |||
d14e0efad9 | |||
16ddd7aae9 | |||
66de4dbbd2 | |||
27237354b2 | |||
484a9692cf | |||
ebb1847a51 | |||
80f6cbc919 | |||
dff47887bc | |||
d0c78d7db1 | |||
ebd22102ef | |||
6ce85286dd | |||
db557c2e3c | |||
2888f1d761 | |||
84898cee76 | |||
cef0204868 | |||
93600dd78a | |||
7e64604e9f | |||
c28c2c573d | |||
1a4cd03ae1 | |||
87195de1e5 | |||
4631a6608c | |||
904ad83f36 | |||
cd5ce0f6c1 | |||
e28660ea7d | |||
bad8be5d1f | |||
87b47e97fd | |||
442dbb08d9 | |||
05b88945ed | |||
3770fa6451 | |||
cfbe5d7af8 | |||
510c7f7251 | |||
5b95893bf0 | |||
5e3282f873 | |||
deb77cd3d3 | |||
dda61eaef9 | |||
8943c0e15d | |||
90bcd29d36 | |||
bb5dbb3cbd | |||
ab8ac50fb2 | |||
994302d28d | |||
4723049615 | |||
dd7b62dc92 | |||
992465c562 | |||
fbc8dac0e9 | |||
ecfda92584 | |||
e4f6677da8 | |||
9f3ccafee8 | |||
5d64ebdb29 | |||
45cd381c3a | |||
ed47bae6b6 | |||
e7f375650d | |||
ad513bfe9c | |||
fe772048de | |||
08c069ca1e | |||
141e896cab | |||
05013cea75 | |||
26c83c38c6 | |||
347ba86a87 | |||
24b2af9d10 | |||
dd38060253 | |||
b0cdc9b779 | |||
31ec4a1200 | |||
48b4128632 | |||
b9adb9c59c | |||
ca0ae8917a |
4
.gitignore
vendored
4
.gitignore
vendored
@ -6,7 +6,9 @@ update.sh
|
||||
page.html
|
||||
screenshot.png
|
||||
login.csv
|
||||
requirements.txt
|
||||
data
|
||||
**/__pycache__
|
||||
/user_data
|
||||
install.sh
|
||||
nohup.out
|
||||
test.py
|
||||
|
13
Dockerfile
13
Dockerfile
@ -11,21 +11,26 @@ RUN set -x \
|
||||
&& apt install -y \
|
||||
wfrench \
|
||||
git \
|
||||
&& git clone https://github.com/piair338/MsRewards \
|
||||
&& pip install -r MsRewards/requirements.txt \
|
||||
&& apt install -y \
|
||||
libx11-xcb1 \
|
||||
libdbus-glib-1-2 \
|
||||
libasound2 \
|
||||
libgtk-3-0 \
|
||||
xvfb \
|
||||
nano \
|
||||
tigervnc-standalone-server \
|
||||
&& git clone https://github.com/piair338/MsRewards \
|
||||
&& pip install -r MsRewards/requirements.txt \
|
||||
&& curl -sSLO https://download-installer.cdn.mozilla.net/pub/firefox/releases/91.9.1esr/linux-x86_64/en-US/firefox-91.9.1esr.tar.bz2 \
|
||||
&& tar -jxf firefox-* \
|
||||
&& mv firefox /opt/ \
|
||||
&& chmod 755 /opt/firefox \
|
||||
&& chmod 755 /opt/firefox/firefox \
|
||||
&& ln -s /opt/firefox/firefox /usr/bin/firefox \
|
||||
&& curl -sSLO https://github.com/mozilla/geckodriver/releases/download/${GECKODRIVER_VER}/geckodriver-${GECKODRIVER_VER}-linux64.tar.gz \
|
||||
&& tar zxf geckodriver-*.tar.gz \
|
||||
&& mv geckodriver /usr/bin/
|
||||
|
||||
|
||||
WORKDIR /app/MsReward
|
||||
WORKDIR /app/MsRewards
|
||||
CMD python main.py
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# MsReward
|
||||
|
||||
|
||||
A Microsoft reward automator, designed to work headless on a raspberry pi. Tested with a pi 3b+ and a pi 4 2Gb .
|
||||
Using a discord webhook or SQL to log points everydays.
|
||||
A Microsoft reward automator, designed to work headless on any server.
|
||||
Using a discord webhook or SQL to log points everyday.
|
||||
Using Selenium and geckodriver.
|
||||
|
||||
## If you're using docker (way easier)
|
||||
@ -33,6 +33,8 @@ You should limit to 6 account per IP, and DON'T USE outlook account, they are ba
|
||||
|
||||
installation recommandation :
|
||||
```
|
||||
sudo apt-get install xdg-utils libdbus-glib-1-2 bzip2 wfrench tigervnc-standalone-server -y
|
||||
|
||||
curl -sSLO https://download-installer.cdn.mozilla.net/pub/firefox/releases/91.9.1esr/linux-x86_64/en-US/firefox-91.9.1esr.tar.bz2
|
||||
tar -xjf firefox-91.9.1esr.tar.bz2
|
||||
sudo mv firefox /opt/
|
||||
@ -42,4 +44,7 @@ sudo ln -s /opt/firefox/firefox /usr/bin/firefox
|
||||
curl -sSLO https://github.com/mozilla/geckodriver/releases/download/v0.31.0/geckodriver-v0.31.0-linux64.tar.gz
|
||||
tar zxf geckodriver-v0.31.0-linux64.tar.gz
|
||||
sudo mv geckodriver /usr/bin/
|
||||
|
||||
rm geckodriver-v0.31.0-linux64.tar.gz
|
||||
rm firefox-91.9.1esr.tar.bz2
|
||||
```
|
||||
|
986
V4.py
986
V4.py
@ -1,986 +0,0 @@
|
||||
#!/usr/bin/python3.10
|
||||
import asyncio
|
||||
import csv
|
||||
from os import sys, system, path
|
||||
from random import choice, randint, shuffle, uniform
|
||||
from re import findall, search
|
||||
from sys import platform
|
||||
from time import sleep
|
||||
from requests import get
|
||||
from selenium import webdriver
|
||||
from selenium.common import exceptions
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.firefox.options import Options
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.ui import Select
|
||||
from rich.progress import BarColumn, Progress, TextColumn, Progress, TimeElapsedColumn, TaskProgressColumn, TimeRemainingColumn
|
||||
|
||||
from modules.db import add_to_database
|
||||
from modules.config import *
|
||||
from modules.tools import *
|
||||
from modules.error import *
|
||||
import modules.progress
|
||||
global driver
|
||||
|
||||
|
||||
def printf(x):
|
||||
printf2(x, _mail)
|
||||
|
||||
|
||||
def WaitUntilVisible(by, id, to = 20, browser = driver):
|
||||
try :
|
||||
WebDriverWait(browser, to).until(EC.visibility_of_element_located((by,id)), "element not found")
|
||||
except TimeoutException as e:
|
||||
print(f"element not found after {to}s")
|
||||
|
||||
|
||||
def claim_amazon():
|
||||
try :
|
||||
driver.get("https://rewards.microsoft.com/redeem/000803000031")
|
||||
try :
|
||||
driver.find_element(By.XPATH, "//span[contains( text( ), 'ÉCHANGER UNE RÉCOMPENSE')]").click()
|
||||
except :
|
||||
driver.find_element(By.XPATH, "//span[contains( text( ), 'REDEEM REWARD')]").click()
|
||||
sleep(5)
|
||||
try :
|
||||
driver.find_element(By.XPATH, "//span[contains( text( ), 'CONFIRMER LA RÉCOMPENSE')]").click()
|
||||
except :
|
||||
driver.find_element(By.XPATH, "//span[contains( text( ), 'CONFIRM REWARD')]").click()
|
||||
|
||||
sleep(5)
|
||||
|
||||
if ("/rewards/redeem/orderhistory" in driver.page_source) :
|
||||
driver.get("https://rewards.microsoft.com/redeem/orderhistory")
|
||||
try :
|
||||
driver.find_element(By.XPATH, "//span[contains( text( ), 'Détails de la commande')]").click()
|
||||
except :
|
||||
driver.find_element(By.XPATH, "//span[contains( text( ), 'Get code')]").click()
|
||||
sleep(5)
|
||||
code = driver.find_element(By.CLASS_NAME, "tango-credential-value").get_attribute('innerHTML')
|
||||
lien = driver.find_elements(By.CLASS_NAME, "tango-credential-key")[1].get_attribute('innerHTML')
|
||||
lien = search('\"([^\"]+)\"',lien)[1]
|
||||
driver.get(lien)
|
||||
sleep(10)
|
||||
box = driver.find_element(By.ID, "input-45")
|
||||
box.click()
|
||||
box.send_keys(code)
|
||||
driver.find_element(By.XPATH, "//span[contains( text( ), 'Déverrouillez votre récompense')]").click()
|
||||
sleep(5)
|
||||
#amazon = search("> ([^ ]+) <", fcode)[1]
|
||||
fcode = driver.find_element(By.XPATH, "/html/body/div[1]/div[1]/main/div/div/div/div/div[1]/div/div[1]/div[2]/div[2]/div/div/div/div/div/div[2]/span").get_attribute("innerHTML")
|
||||
if fcode :
|
||||
webhookFailure.send(_mail)
|
||||
webhookFailure.send(fcode)
|
||||
return(1)
|
||||
else :
|
||||
LogError("impossible de localiser le code ", driver, _mail)
|
||||
return(1)
|
||||
|
||||
else :
|
||||
LogError("la recuperation ne peux pas être automatique", driver, _mail)
|
||||
return(0)
|
||||
except Exception as e :
|
||||
LogError(f'problème dans la recuperation : {str(e)}', driver, _mail)
|
||||
|
||||
|
||||
def setup_proxy(ip, port, socks=False) :
|
||||
PROXY = f"{ip}:{port}"
|
||||
if socks :
|
||||
options.set_preference('network.proxy.type', 1)
|
||||
options.set_preference('network.proxy.socks', ip)
|
||||
options.set_preference('network.proxy.socks_port', int(port))
|
||||
options.set_preference("browser.link.open_newwindow", 3)
|
||||
else :
|
||||
webdriver.DesiredCapabilities.FIREFOX['proxy'] = {
|
||||
"httpProxy": PROXY,
|
||||
"sslProxy": PROXY,
|
||||
"proxyType": "MANUAL",
|
||||
}
|
||||
|
||||
|
||||
def FirefoxDriver(mobile=False, Headless=Headless):
|
||||
if proxy_enabled :
|
||||
setup_proxy(proxy_address,proxy_port)
|
||||
PC_USER_AGENT = (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko)"
|
||||
"Chrome/104.0.5112.102 Safari/537.36 Edg/104.0.1293.70"
|
||||
)
|
||||
MOBILE_USER_AGENT = (
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 15_5 like Mac OS X)"
|
||||
"AppleWebKit/605.1.15 (KHTML, like Gecko)"
|
||||
"CriOS/103.0.5060.63 Mobile/15E148 Safari/604.1"
|
||||
)
|
||||
|
||||
options = Options()
|
||||
options.set_preference("browser.link.open_newwindow", 3)
|
||||
if FAST :
|
||||
options.set_preference("permissions.default.image", 2) #disable image loading. May add this without the fast option soon
|
||||
if Headless:
|
||||
options.add_argument("-headless")
|
||||
if mobile :
|
||||
options.set_preference("general.useragent.override", MOBILE_USER_AGENT)
|
||||
else :
|
||||
options.set_preference("general.useragent.override", PC_USER_AGENT)
|
||||
driver = webdriver.Firefox(options=options)
|
||||
driver.set_window_size(1900 + hash(_mail)%20 , 1070 + hash(_password + "salt")%10)
|
||||
return driver
|
||||
|
||||
|
||||
def Close(fenetre, SwitchTo=0):
|
||||
driver.switch_to.window(fenetre)
|
||||
driver.close()
|
||||
driver.switch_to.window(driver.window_handles[SwitchTo])
|
||||
|
||||
|
||||
#Deal with RGPD popup as well as some random popup like 'are you satisfied' one
|
||||
def RGPD():
|
||||
for i in ["bnp_btn_accept", "bnp_hfly_cta2", "bnp_hfly_close"] :
|
||||
try:
|
||||
driver.find_element(By.ID, i).click()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
"""
|
||||
PlayQuiz[N]([int : override]) make the quizz 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 quizz. Can be usefull in some case, where the programm crashes before finishing the quizz
|
||||
"""
|
||||
def PlayQuiz2(override=10):
|
||||
printf("début de PlayQuiz2")
|
||||
for j in range(override):
|
||||
try:
|
||||
RGPD()
|
||||
CustomSleep(uniform(3, 5))
|
||||
txt = driver.page_source
|
||||
secret = search('IG:"([^"]+)"', txt)[1] # variable dans la page, pour calculer le offset
|
||||
reponse1 = search('data-option="([^"]+)"', txt)[1]
|
||||
offset = int(secret[-2:], 16) # la conversion ec decimal des deux dernier caracteres de IG
|
||||
reponse = search('correctAnswer":"([0-9]+)', txt)[1]
|
||||
somme = 0
|
||||
|
||||
for i in reponse1:
|
||||
somme += ord(i)
|
||||
|
||||
if somme + offset == int(reponse):
|
||||
elem = driver.find_element(By.ID, "rqAnswerOption0")
|
||||
elem.click()
|
||||
progressBar(j, 10, name="quiz 2")
|
||||
|
||||
else:
|
||||
elem = driver.find_element(By.ID, "rqAnswerOption1")
|
||||
elem.click()
|
||||
progressBar(j, 10, name="quiz 2")
|
||||
|
||||
except exceptions.ElementNotInteractableException as e:
|
||||
driver.execute_script("arguments[0].click();", elem)
|
||||
|
||||
except Exception as e:
|
||||
LogError("PlayQuiz2" + str(e), driver, _mail)
|
||||
break
|
||||
printf("PlayQuiz2 finis")
|
||||
|
||||
|
||||
def PlayQuiz8(task = None):
|
||||
override = len(findall("<span id=\"rqQuestionState.\" class=\"emptyCircle\"></span>", driver.page_source))+1
|
||||
printf(f"PlayQuiz8 : start, override : {override}")
|
||||
try:
|
||||
c = 0
|
||||
RGPD()
|
||||
for i in range(override):
|
||||
CustomSleep(uniform(3, 5))
|
||||
AnswerOptions = [ (driver.find_element(By.ID, f"rqAnswerOption{i-1}"),f'rqAnswerOption{i-1}') for i in range(1,9)]
|
||||
isCorrect = [x[1] for x in AnswerOptions if 'iscorrectoption="True" ' in x[0].get_attribute("outerHTML") ]
|
||||
shuffle(isCorrect)
|
||||
|
||||
for i in isCorrect:
|
||||
WaitUntilVisible(By.ID, i, to = 20, browser=driver)
|
||||
c += 1
|
||||
progressBar(c, 16, name="Quiz 8 ")
|
||||
try:
|
||||
elem = driver.find_element(By.ID, i)
|
||||
elem.click()
|
||||
if not task is None:
|
||||
AdvanceTask(task, 1/override / len(isCorrect) * 100)
|
||||
except exceptions.NoSuchElementException :
|
||||
driver.refresh()
|
||||
CustomSleep(10)
|
||||
elem = driver.find_element(By.ID, i)
|
||||
elem.click()
|
||||
except ElementClickInterceptedException :
|
||||
RGPD()
|
||||
isCorrect.append(i)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
LogError(f"PlayQuiz8 - 4 - {e} \n ListOfGood : {str(isCorrect)}", driver, _mail)
|
||||
|
||||
printf("PlayQuiz8 : fin ")
|
||||
|
||||
|
||||
def PlayQuiz4(override=None):
|
||||
printf("PlayQuiz4 : start")
|
||||
if not override:
|
||||
try: # permet de gerer les truc de fidélité, qui sont plus long
|
||||
override = int(findall('rqQuestionState([\d]{1,2})"', driver.page_source)[-1])
|
||||
printf(f"Override : {override}")
|
||||
except:
|
||||
override = 3
|
||||
|
||||
try:
|
||||
for i in range(override):
|
||||
CustomSleep(uniform(3, 5))
|
||||
txt = driver.page_source
|
||||
RGPD()
|
||||
reponse = search('correctAnswer":"([^"]+)', txt)[1] # je suis pas sur qu'il y ait un espace
|
||||
reponse = reponse.replace("\\u0027", "'") # il faut cancel l'unicode avec un double \ (on replacer les caracteres en unicode en caracteres utf-8)
|
||||
printf(f"validation de la reponse ")
|
||||
printf(f"validation de la reponse {i+1}/{override} {reponse}")
|
||||
try:
|
||||
elem = driver.find_element(
|
||||
By.CSS_SELECTOR, f'[data-option="{reponse}"]'
|
||||
)
|
||||
elem.click()
|
||||
except exceptions.ElementNotInteractableException:
|
||||
driver.execute_script("arguments[0].click();", elem)
|
||||
|
||||
except Exception as e:
|
||||
LogError(f"PlayQuiz4 {str(e)}", driver, _mail)
|
||||
raise ValueError(e)
|
||||
printf("PlayQuiz4 : end")
|
||||
|
||||
|
||||
"""
|
||||
PlayPoll() reply a random thing to poll, on of daily activities
|
||||
"""
|
||||
def PlayPoll():
|
||||
printf("PlayPoll : start")
|
||||
try:
|
||||
try:
|
||||
elem = driver.find_element(By.ID, f"btoption{choice([0,1])}")
|
||||
elem.click()
|
||||
except exceptions.ElementNotInteractableException as e:
|
||||
driver.execute_script("arguments[0].click();", elem)
|
||||
CustomSleep(uniform(2, 2.5))
|
||||
except Exception as e:
|
||||
LogError("PlayPoll" + str(e), driver, _mail)
|
||||
raise ValueError(e)
|
||||
printf("PlayPoll : end")
|
||||
|
||||
|
||||
def AllCard(): # fonction qui clique sur les cartes
|
||||
def reset(Partie2=False): # retourne sur la page de depart apres avoir finis
|
||||
if len(driver.window_handles) == 1:
|
||||
driver.get("https://www.bing.com/rewardsapp/flyout")
|
||||
if Partie2:
|
||||
driver.find_element(
|
||||
By.XPATH, "/html/body/div/div/div[3]/div[2]/div[2]/div[2]/div[1]"
|
||||
).click()
|
||||
else:
|
||||
driver.switch_to.window(driver.window_handles[1])
|
||||
printf(f"fermeture : {driver.current_url}")
|
||||
driver.close()
|
||||
driver.switch_to.window(driver.window_handles[0])
|
||||
reset(Partie2)
|
||||
|
||||
def dailyCards():
|
||||
try:
|
||||
StartTask(task["daily"][f"all"])
|
||||
for i in range(3):
|
||||
StartTask(task["daily"][f"carte{i}"])
|
||||
CustomSleep(uniform(3, 5))
|
||||
try:
|
||||
titre = "erreur"
|
||||
driver.find_element(
|
||||
By.XPATH,f"/html/body/div/div/div[3]/div[2]/div[1]/div[2]/div/div[{i+1}]/a/div/div[2]",
|
||||
).click()
|
||||
sleep(1)
|
||||
titre = driver.title
|
||||
TryPlay(titre, task=task["daily"][f"carte{i}"])
|
||||
AdvanceTask(task["daily"][f"carte{i}"], 100)
|
||||
ChangeColor(task["daily"][f"carte{i}"], "green")
|
||||
sleep(1)
|
||||
reset()
|
||||
printf(f"DailyCard {titre} ok ")
|
||||
except Exception as e:
|
||||
printf(f"Allcard card {titre} error ({e})")
|
||||
except Exception as e:
|
||||
LogError(f"Dailycards {e}", driver, _mail)
|
||||
|
||||
try:
|
||||
dailyCards()
|
||||
except Exception as e:
|
||||
printf(f"erreur dans les quetes de la semaine {e}")
|
||||
|
||||
def weekly_cards():
|
||||
try:
|
||||
driver.find_element(
|
||||
By.XPATH, "/html/body/div/div/div[3]/div[2]/div[2]/div[2]/div[1]"
|
||||
).click() # declenche la premiere partie ?
|
||||
except:
|
||||
reset()
|
||||
try:
|
||||
driver.find_element(
|
||||
By.XPATH, "/html/body/div/div/div[3]/div[2]/div[2]/div[2]/div[1]"
|
||||
).click() # declenche la deuxieme partie ?
|
||||
except:
|
||||
pass
|
||||
|
||||
for i in range(20):
|
||||
printf("début de l'une des cartes")
|
||||
driver.find_element(
|
||||
By.XPATH,
|
||||
"/html/body/div/div/div[3]/div[2]/div[2]/div[3]/div/div[1]/a/div/div[2]",
|
||||
).click()
|
||||
printf("carte cliquée")
|
||||
driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1])
|
||||
sleep(1)
|
||||
titre = driver.title
|
||||
print(f"carte {titre} en cours")
|
||||
TryPlay(titre)
|
||||
reset(True)
|
||||
sleep(1)
|
||||
try:
|
||||
link = findall('href="([^<]+)" title=""', driver.page_source)[
|
||||
3
|
||||
] # verifie si on a toujours des cartes
|
||||
except:
|
||||
break
|
||||
for i in range(2): # don't seem useful for fixing error
|
||||
try :
|
||||
weekly_cards()
|
||||
break
|
||||
except Exception as e:
|
||||
LogError(f"weekly_cards, try n°{i+1} \n {e}", driver, _mail)
|
||||
if i == 0 :
|
||||
driver.refresh()
|
||||
else :
|
||||
CustomSleep(1800)
|
||||
driver.refresh()
|
||||
|
||||
|
||||
"""
|
||||
login() tries to login to your micrososft account.
|
||||
it uses global variable _mail and _password to login
|
||||
"""
|
||||
def login():
|
||||
global driver
|
||||
def sub_login():
|
||||
printf("sublogin : start")
|
||||
driver.get("https://www.bing.com/rewardsapp/flyout")
|
||||
for i in [f'[title="Rejoindre"]', f'[title="Join now"]', f'[title="Rejoindre maintenant"]'] :
|
||||
try:
|
||||
driver.find_element(By.CSS_SELECTOR, i).click() # depend of the language of the page
|
||||
break
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
WaitUntilVisible(By.ID, "i0116", browser = driver)
|
||||
mail = driver.find_element(By.ID, "i0116")
|
||||
send_keys_wait(mail, _mail)
|
||||
mail.send_keys(Keys.ENTER)
|
||||
WaitUntilVisible(By.ID, "i0118", browser = driver)
|
||||
pwd = driver.find_element(By.ID, "i0118")
|
||||
send_keys_wait(pwd, _password)
|
||||
pwd.send_keys(Keys.ENTER)
|
||||
CustomSleep(5)
|
||||
|
||||
if ('Abuse' in driver.current_url) :
|
||||
LogError("account suspended", driver, _mail)
|
||||
raise Banned()
|
||||
|
||||
|
||||
for i in ["KmsiCheckboxField","iLooksGood", "idSIButton9", "iCancel"]:
|
||||
try:
|
||||
driver.find_element(By.ID, i).click()
|
||||
except Exception as e:
|
||||
pass
|
||||
try :
|
||||
elm = driver.find_element(By.TAG_NAME, "body")
|
||||
elm.send_keys(Keys.ENTER)
|
||||
except :
|
||||
pass
|
||||
printf("login completed")
|
||||
RGPD()
|
||||
CustomSleep(uniform(3,5))
|
||||
driver.get("https://www.bing.com/rewardsapp/flyout")
|
||||
CustomSleep(uniform(3,5))
|
||||
|
||||
for i in range(3) :
|
||||
try :
|
||||
sub_login()
|
||||
return (driver.current_window_handle)
|
||||
except Banned:
|
||||
raise Banned()
|
||||
except Exception as e:
|
||||
LogError("login - 3 - " + str(e), driver, _mail)
|
||||
driver.close()
|
||||
CustomSleep(1200)
|
||||
driver = FirefoxDriver()
|
||||
return("STOP")
|
||||
|
||||
|
||||
def BingPcSearch(override=randint(35, 40)):
|
||||
StartTask(task["PC"])
|
||||
driver.get(f"https://www.bing.com/search?q=test") # {choice(Liste_de_mot)}')
|
||||
CustomSleep(uniform(1, 2))
|
||||
RGPD()
|
||||
send_keys_wait(
|
||||
driver.find_element(By.ID, "sb_form_q"),
|
||||
Keys.BACKSPACE
|
||||
+ Keys.BACKSPACE
|
||||
+ Keys.BACKSPACE
|
||||
+ Keys.BACKSPACE
|
||||
+ Keys.BACKSPACE
|
||||
+ Keys.BACKSPACE,
|
||||
)
|
||||
|
||||
for i in range(override):
|
||||
mot = choice(Liste_de_mot)
|
||||
try:
|
||||
send_keys_wait(driver.find_element(By.ID, "sb_form_q"), mot)
|
||||
driver.find_element(By.ID, "sb_form_q").send_keys(Keys.ENTER)
|
||||
except Exception as e :
|
||||
printf(e)
|
||||
sleep(10)
|
||||
driver.get('https://www.bing.com/search?q=pls')
|
||||
sleep(3)
|
||||
send_keys_wait(driver.find_element(By.ID, "sb_form_q"), mot)
|
||||
driver.find_element(By.ID, "sb_form_q").send_keys(Keys.ENTER)
|
||||
|
||||
AdvanceTask(task["PC"], 1/override * 100 )
|
||||
CustomSleep(uniform(5, 20))
|
||||
|
||||
try:
|
||||
driver.find_element(By.ID, "sb_form_q").clear()
|
||||
except Exception as e:
|
||||
printf(e)
|
||||
try:
|
||||
driver.get('https://www.bing.com/search?q=pls')
|
||||
driver.find_element(By.ID, "sb_form_q").clear()
|
||||
except Exception as e:
|
||||
LogError(f"BingPcSearch - clear la barre de recherche - {e}", driver, _mail)
|
||||
|
||||
ChangeColor(task["PC"], "green")
|
||||
|
||||
|
||||
def unban():
|
||||
driver.find_element(By.ID, "StartAction").click()
|
||||
CustomSleep(10)
|
||||
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]
|
||||
sel = Select(driver.find_element(By.ID, "wlspispHIPCountrySelect" + uuid0))
|
||||
CC = input("enter Contry code (FR, ...) ")
|
||||
sel.select_by_value(CC)
|
||||
WaitUntilVisible(By.ID, "wlspispHIPPhoneInput" + uuid1, browser=driver)
|
||||
phone = input("entrez le numero de téléphone : +33")
|
||||
phone_box = driver.find_element(By.ID, "wlspispHIPPhoneInput" + uuid1)
|
||||
phone_box.send_keys(phone)
|
||||
WaitUntilVisible(By.ID, "wlspispHipSendCode" + uuid2, browser=driver)
|
||||
send_link = driver.find_element(By.ID, "wlspispHipSendCode" + uuid2)
|
||||
send_link.click()
|
||||
WaitUntilVisible(By.ID, "wlspispSolutionElement" + uuid3, browser=driver)
|
||||
LogError("test", driver,"phone test")
|
||||
answer_box = driver.find_element(By.ID, "wlspispSolutionElement" + uuid3)
|
||||
answer = input("entrez le contenu du msg : ")
|
||||
answer_box.send_keys(answer)
|
||||
send_box = driver.find_element(By.ID, "ProofAction")
|
||||
send_box.click()
|
||||
WaitUntilVisible(By.ID, "FinishAction", browser=driver)
|
||||
continue_box = driver.find_element(By.ID, "FinishAction")
|
||||
continue_box.click()
|
||||
|
||||
|
||||
def TryPlay(nom="inconnu", task = None):
|
||||
RGPD()
|
||||
printf("TryPlay en cours")
|
||||
|
||||
def play(number):
|
||||
if number == 8 or number == 9:
|
||||
try:
|
||||
printf(f"\033[96m Quiz 8 détecté sur la page {nom} \033[0m")
|
||||
PlayQuiz8(task=task)
|
||||
printf(f"\033[92m Quiz 8 reussit sur {nom} \033[0m")
|
||||
except Exception as e:
|
||||
printf(f"echec de PlayQuiz 8. Aborted {e} \033[0m")
|
||||
|
||||
elif number == 5 or number == 4:
|
||||
try:
|
||||
printf(f"\033[96m Quiz 4 détecté sur la page {nom} \033[0m")
|
||||
PlayQuiz4()
|
||||
printf(f"\033[92m Quiz 4 reussit sur {nom} \033[0m")
|
||||
except Exception as e:
|
||||
printf(f"echec de PlayQuiz 4. Aborted {e} \033[0m")
|
||||
|
||||
elif number == 3 or number == 2:
|
||||
try:
|
||||
printf(f"\033[96m Quiz 2 détecté sur la page {nom}\033[0m")
|
||||
PlayQuiz2()
|
||||
printf(f"\033[92m Quiz 2 reussit sur la page {nom}\033[0m")
|
||||
except Exception as e:
|
||||
printf(f"echec de PlayQuiz 2. Aborted {e}")
|
||||
else:
|
||||
LogError("probleme dans la carte : il y a un bouton play et aucun quiz detecté", driver, _mail)
|
||||
|
||||
try:
|
||||
driver.find_element(By.ID, "rqStartQuiz").click() # start the quiz
|
||||
number = driver.page_source.count("rqAnswerOption")
|
||||
play(number)
|
||||
|
||||
except Exception as e:
|
||||
# printf(e) normal error here
|
||||
if "bt_PollRadio" in driver.page_source:
|
||||
try:
|
||||
printf("Poll détected")
|
||||
RGPD()
|
||||
PlayPoll()
|
||||
printf("Poll reussit ")
|
||||
except Exception as e:
|
||||
printf(f"TryPlay - 1 - Poll aborted {e}")
|
||||
|
||||
elif "rqQuestionState" in driver.page_source:
|
||||
try:
|
||||
number = driver.page_source.count("rqAnswerOption")
|
||||
printf(f"recovery détecté. quiz : {number}")
|
||||
play(number-1, override=restant + 1)
|
||||
except Exception as e:
|
||||
printf("TryPlay - 2 - " + e)
|
||||
|
||||
elif search("([0-9]) de ([0-9]) finalisée", driver.page_source):
|
||||
print("fidélité")
|
||||
RGPD()
|
||||
Fidelite()
|
||||
|
||||
else:
|
||||
printf(f"rien à faire sur la page {nom}")
|
||||
RGPD()
|
||||
CustomSleep(uniform(3, 5))
|
||||
|
||||
|
||||
def LogPoint(account="unknown"): # log des points sur discord
|
||||
def get_points():
|
||||
driver.get("https://www.bing.com/rewardsapp/flyout")
|
||||
if not LINUX_HOST:
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
else:
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
|
||||
regex1 = '<a href="https://rewards\.bing\.com/" title="((.{1,3}),(.{1,3})) points" target="_blank"'
|
||||
try:
|
||||
point = search(regex1, driver.page_source)[1].replace(",", "")
|
||||
|
||||
except Exception as e:
|
||||
elem = driver.find_element(By.CSS_SELECTOR, '[title="Microsoft Rewards"]')
|
||||
elem.click()
|
||||
CustomSleep(5)
|
||||
driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1])
|
||||
CustomSleep(uniform(5,7))
|
||||
|
||||
point = search('availablePoints":([\d]+)', driver.page_source)[1]
|
||||
return(point)
|
||||
for i in range (3):
|
||||
try :
|
||||
points = get_points()
|
||||
break
|
||||
except Exception as e:
|
||||
CustomSleep(300)
|
||||
printf(f"LogPoints : {e}")
|
||||
points = None
|
||||
|
||||
if not points :
|
||||
LogError(f"impossible d'avoir les points : {e}", driver, _mail)
|
||||
CustomSleep(uniform(3, 20))
|
||||
|
||||
account = account.split("@")[0]
|
||||
|
||||
if DISCORD_ENABLED_SUCCESS:
|
||||
|
||||
if DISCORD_EMBED:
|
||||
embed = discord.Embed(
|
||||
title=f"{account} actuellement à {str(points)} points", colour=Colour.green()
|
||||
)
|
||||
embed.set_footer(text=account)
|
||||
webhookSuccess.send(embed=embed)
|
||||
else:
|
||||
webhookSuccess.send(f"{account} actuellement à {str(points)} points")
|
||||
|
||||
if CLAIM_AMAZON and int(points) >= 7500:
|
||||
if (claim_amazon() == 1) :
|
||||
points = str( int(points) - 7500)
|
||||
|
||||
if sql_enabled :
|
||||
add_to_database(account, points, sql_host, sql_usr, sql_pwd, sql_database)
|
||||
|
||||
|
||||
def Fidelite():
|
||||
try:
|
||||
while 1: #close all tabs
|
||||
try:
|
||||
Close(1)
|
||||
except:
|
||||
break
|
||||
try :
|
||||
result = get(FidelityLink) #get the url of fidelity page
|
||||
except Exception as e :
|
||||
printf(e)
|
||||
result = False
|
||||
|
||||
if result :
|
||||
lien = result.content.decode("UTF-8")
|
||||
printf(lien)
|
||||
|
||||
if (lien.split(":")[0] == "https") or (lien.split(":")[0] == "http") :
|
||||
|
||||
driver.get(lien)
|
||||
|
||||
WaitUntilVisible(By.CSS_SELECTOR, 'div[class="pull-left spacer-48-bottom punchcard-row"]', browser=driver)
|
||||
choix = driver.find_element(By.CSS_SELECTOR, 'div[class="pull-left spacer-48-bottom punchcard-row"]') # pull-left spacer-48-bottom punchcard-row? USELESS ?
|
||||
|
||||
nb = search("([0-9]) of ([0-9]) completed", driver.page_source)
|
||||
if not nb:
|
||||
nb = search("([0-9]) de ([0-9]) finalisé", driver.page_source)
|
||||
for i in range(int(nb[2]) - int(nb[1])):
|
||||
driver.refresh()
|
||||
CustomSleep(2)
|
||||
choix = driver.find_element(By.CLASS_NAME, "spacer-48-bottom")
|
||||
try :
|
||||
ButtonText = search('<span class="pull-left margin-right-15">([^<^>]+)</span>',choix.get_attribute("innerHTML"))[1]
|
||||
bouton = driver.find_element(By.XPATH, f'//span[text()="{ButtonText}"]')
|
||||
bouton.click()
|
||||
except Exception as e1 :
|
||||
try :
|
||||
t = driver.find_element(By.XPATH,'/html/body/div[1]/div[2]/main/div[2]/div[2]/div[7]/div[3]/div[1]')
|
||||
t.click()
|
||||
except Exception as e2 :
|
||||
LogError(f"fidélité - double erreur - e1 : {e1} - e2 {e2}", driver, _mail)
|
||||
break
|
||||
CustomSleep(uniform(3, 5))
|
||||
driver.switch_to.window(driver.window_handles[1])
|
||||
TryPlay(driver.title)
|
||||
driver.get(lien) # USELESS ?
|
||||
CustomSleep(uniform(3, 5))
|
||||
try:
|
||||
Close(driver.window_handles[1])
|
||||
except Exception as e:
|
||||
printf(e)
|
||||
printf("fidelité - done")
|
||||
else :
|
||||
printf("lien invalide")
|
||||
except Exception as e:
|
||||
LogError("Fidélité" + str(e), driver, _mail)
|
||||
|
||||
|
||||
def Mlogin(echec):
|
||||
try:
|
||||
MobileDriver.get("https://www.bing.com/search?q=test+speed")
|
||||
MRGPD()
|
||||
printf("start of Mobile login")
|
||||
MobileDriver.find_element(By.ID, "mHamburger").click()
|
||||
WaitUntilVisible(By.ID, "hb_s", browser=MobileDriver)
|
||||
MobileDriver.find_element(By.ID, "hb_s").click()
|
||||
WaitUntilVisible(By.ID, "i0116", browser=MobileDriver)
|
||||
mail = MobileDriver.find_element(By.ID, "i0116")
|
||||
send_keys_wait(mail, _mail)
|
||||
mail.send_keys(Keys.ENTER)
|
||||
WaitUntilVisible(By.ID, "i0118", browser=MobileDriver)
|
||||
pwd = MobileDriver.find_element(By.ID, "i0118")
|
||||
send_keys_wait(pwd, _password)
|
||||
pwd.send_keys(Keys.ENTER)
|
||||
CustomSleep(uniform(1, 2))
|
||||
for i in ["KmsiCheckboxField", "iLooksGood", "idSIButton9"]:
|
||||
try:
|
||||
MobileDriver.find_element(By.ID,i ).click()
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
printf("end of Mobile login")
|
||||
|
||||
except Exception as e:
|
||||
echec += 1
|
||||
if echec <= 3:
|
||||
printf(f"echec du login sur la version mobile. on reesaye ({echec}/3), {e}")
|
||||
CustomSleep(uniform(5, 10))
|
||||
Mlogin(echec)
|
||||
else:
|
||||
LogError(
|
||||
f"login impossible 3 fois de suite. {e}", MobileDriver, _mail
|
||||
)
|
||||
MobileDriver.quit()
|
||||
return True
|
||||
|
||||
|
||||
def MRGPD():
|
||||
try:
|
||||
MobileDriver.find_element(By.ID, "bnp_btn_accept").click()
|
||||
except Exception as e:
|
||||
pass
|
||||
try:
|
||||
MobileDriver.find_element(By.ID, "bnp_hfly_cta2").click()
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
def Alerte():
|
||||
try:
|
||||
alert = MobileDriver.switch_to.alert
|
||||
alert.dismiss()
|
||||
except exceptions.NoAlertPresentException as e:
|
||||
pass
|
||||
except Exception as e:
|
||||
LogError(f"mobile.py -> Alerte : {e}", MobileDriver, _mail)
|
||||
|
||||
|
||||
def BingMobileSearch(override=randint(22, 25)):
|
||||
global MobileDriver
|
||||
MobileDriver = "unable to start"
|
||||
try:
|
||||
try:
|
||||
MobileDriver = FirefoxDriver(mobile=True)
|
||||
MobileDriver.implicitly_wait(15)
|
||||
except Exception as e:
|
||||
LogError("BingMobileSearch - 1 - echec de la creation du driver mobile", MobileDriver, _mail)
|
||||
ChangeColor(task["Mobile"], "red")
|
||||
echec = 0
|
||||
|
||||
if not Mlogin(echec):
|
||||
StartTask(task["Mobile"])
|
||||
CustomSleep(uniform(1, 2))
|
||||
MRGPD()
|
||||
CustomSleep(uniform(1, 1.5))
|
||||
|
||||
for i in range(override): # 20
|
||||
try :
|
||||
mot = choice(Liste_de_mot)
|
||||
send_keys_wait(MobileDriver.find_element(By.ID, "sb_form_q"), mot)
|
||||
MobileDriver.find_element(By.ID, "sb_form_q").send_keys(Keys.ENTER)
|
||||
AdvanceTask(task["Mobile"], 1/override * 100)
|
||||
#printf(MobileDriver.current_url)
|
||||
CustomSleep(uniform(5, 20))
|
||||
Alerte() # verifie si il y a des alertes (demande de positions ....)
|
||||
MobileDriver.find_element(By.ID, "sb_form_q").clear()
|
||||
except :
|
||||
driver.refresh()
|
||||
CustomSleep(30)
|
||||
i -= 1
|
||||
MobileDriver.quit()
|
||||
ChangeColor(task["Mobile"], "green")
|
||||
|
||||
except Exception as e:
|
||||
LogError("BingMobileSearch - 4 - " + str(e), MobileDriver, _mail)
|
||||
MobileDriver.quit()
|
||||
|
||||
|
||||
def DailyRoutine(custom = False):
|
||||
|
||||
ShowDefaultTask()
|
||||
try :
|
||||
if not custom: # custom already login
|
||||
login()
|
||||
except Banned :
|
||||
LogError("THIS ACCOUND IS BANNED. FIX THIS ISSUE WITH -U", driver, _mail)
|
||||
return()
|
||||
|
||||
try:
|
||||
AllCard()
|
||||
except Exception as e:
|
||||
LogError(f"DailyRoutine - AllCard - \n{e}", driver, _mail)
|
||||
|
||||
try:
|
||||
BingPcSearch()
|
||||
except Exception as e:
|
||||
LogError(f"DailyRoutine - BingPcSearch - \n{e}", driver, _mail)
|
||||
|
||||
try:
|
||||
BingMobileSearch()
|
||||
except Exception as e:
|
||||
LogError(f"DailyRoutine - BingMobileSearch - \n{e}", driver, _mail)
|
||||
|
||||
try:
|
||||
Fidelite()
|
||||
except Exception as e:
|
||||
LogError(f"DailyRoutine - Fidelité - \n{e}", driver, _mail)
|
||||
|
||||
try:
|
||||
LogPoint(_mail)
|
||||
except Exception as e:
|
||||
LogError(f"DailyRoutine - LogPoint - \n{e}", driver, _mail)
|
||||
|
||||
|
||||
def close():
|
||||
driver.quit()
|
||||
quit()
|
||||
|
||||
|
||||
def dev():
|
||||
pass
|
||||
|
||||
|
||||
def CustomStart(Credentials):
|
||||
global START_TIME
|
||||
if not LINUX_HOST :
|
||||
raise NameError('You need to be on linux to do that, due to the utilisation of a module named enquieries, sorry.')
|
||||
global driver, _mail, _password, p, task
|
||||
|
||||
system("clear") # clear from previous command to allow a clean choice
|
||||
actions = ["tout", "daily", "pc", "mobile", "LogPoint","Fidelite", "dev"]
|
||||
Actions = enquiries.choose("quels Actions ?", actions, multi=True)
|
||||
liste = SelectAccount()
|
||||
START_TIME = time() # Reset timer to the start of the actions
|
||||
with Progress(
|
||||
TextColumn("[progress.description]{task.description}"),
|
||||
BarColumn(),
|
||||
TaskProgressColumn(),
|
||||
TimeRemainingColumn(),
|
||||
TimeElapsedColumn(),
|
||||
) as p:
|
||||
task = modules.progress.dico(p)
|
||||
for _mail, _password in liste:
|
||||
|
||||
driver = FirefoxDriver()
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
if login() != "STOP":
|
||||
if "tout" in Actions:
|
||||
DailyRoutine(True)
|
||||
|
||||
if "daily" in Actions:
|
||||
try:
|
||||
AllCard()
|
||||
except Exception as e:
|
||||
LogError(f"AllCards - {e} -- override", driver, _mail)
|
||||
|
||||
if "pc" in Actions:
|
||||
try:
|
||||
ShowTask(task["PC"])
|
||||
BingPcSearch()
|
||||
except Exception as e:
|
||||
LogError(f"il y a eu une erreur dans BingPcSearch, {e} -- override", driver, _mail)
|
||||
|
||||
if "mobile" in Actions:
|
||||
try:
|
||||
ShowTask(task["Mobile"])
|
||||
BingMobileSearch()
|
||||
except Exception as e:
|
||||
LogError(f"BingMobileSearch - {e} -- override", driver, _mail)
|
||||
|
||||
if "Fidelite" in Actions:
|
||||
try :
|
||||
Fidelite()
|
||||
except Exception as e :
|
||||
LogError(f"Fidelite - {e} -- override", driver, _mail)
|
||||
|
||||
if "dev" in Actions:
|
||||
try:
|
||||
dev()
|
||||
except Exception as e:
|
||||
printf(e)
|
||||
break
|
||||
|
||||
if not "tout" in Actions:
|
||||
try:
|
||||
LogPoint(_mail)
|
||||
except Exception as e:
|
||||
print("CustomStart " + str(e))
|
||||
driver.close()
|
||||
|
||||
|
||||
def SelectAccount(multiple = True):
|
||||
system("clear") # clear from previous command to allow a clean choice
|
||||
emails = [x[0] for x in Credentials] # list of all email adresses
|
||||
emailsSelected = enquiries.choose("quels comptes ?", emails, multi=multiple)
|
||||
return([x for x in Credentials if x[0] in emailsSelected])
|
||||
|
||||
|
||||
def unban2():
|
||||
global _mail, _password
|
||||
_mail, _password = SelectAccount(False)[0]
|
||||
try :
|
||||
driver = FirefoxDriver()
|
||||
login()
|
||||
raise NotBanned
|
||||
except Banned :
|
||||
unban()
|
||||
except NotBanned :
|
||||
printf("you are not currently banned on this account")
|
||||
|
||||
def SavePointsFromFile(file):
|
||||
with open(file) as f:
|
||||
reader = csv.reader(f)
|
||||
points_list = list(reader)
|
||||
|
||||
for item in points_list:
|
||||
compte, points = item[0], item[1]
|
||||
add_to_database(compte, points, sql_host,sql_usr,sql_pwd,sql_database, save_if_fail=False)
|
||||
|
||||
with open(file, "w") as f:
|
||||
f.write("")
|
||||
|
||||
|
||||
def StartTask(task):
|
||||
ChangeColor(task, "blue")
|
||||
p.start_task(task)
|
||||
p.update(task, advance=0) # Reset the Task if it was already filled to 100%
|
||||
|
||||
def ShowTask(task):
|
||||
p.update(task, visible=True)
|
||||
|
||||
def AdvanceTask(task, pourcentage):
|
||||
p.update(task, advance=pourcentage)
|
||||
|
||||
def ChangeColor(task, newcolor):
|
||||
old = p.tasks[task].description
|
||||
old = old.split(']')
|
||||
old[0] = f"[{newcolor}"
|
||||
new = "]".join(old)
|
||||
p.update(task,description=new)
|
||||
|
||||
def ShowDefaultTask():
|
||||
for i in ["all", "carte1", "carte2", "carte0"]:
|
||||
ShowTask(task["daily"][i])
|
||||
|
||||
for i in ["PC", "Mobile"]:
|
||||
ShowTask(task[i])
|
||||
|
||||
if CUSTOM_START:
|
||||
CustomStart(Credentials)
|
||||
elif UNBAN:
|
||||
unban2()
|
||||
elif POINTS_FILE != "":
|
||||
SavePointsFromFile(POINTS_FILE)
|
||||
else:
|
||||
|
||||
with Progress(
|
||||
TextColumn("[progress.description]{task.description}"),
|
||||
BarColumn(),
|
||||
TaskProgressColumn(),
|
||||
TimeRemainingColumn(),
|
||||
TimeElapsedColumn(),
|
||||
) as p:
|
||||
task = modules.progress.dico(p)
|
||||
|
||||
for _mail, _password in Credentials:
|
||||
#system("pkill -9 firefox")
|
||||
print("\n\n")
|
||||
print(_mail)
|
||||
CustomSleep(1)
|
||||
printf("début du driver")
|
||||
driver = FirefoxDriver()
|
||||
printf("driver demarré")
|
||||
driver.implicitly_wait(7)
|
||||
|
||||
try:
|
||||
DailyRoutine()
|
||||
driver.quit()
|
||||
attente = uniform(1200, 3600)
|
||||
printf(f"finis. attente de {round(attente/60)}min")
|
||||
CustomSleep(attente)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("canceled")
|
||||
close()
|
954
V5.py
Executable file
954
V5.py
Executable file
@ -0,0 +1,954 @@
|
||||
#!/usr/bin/python3.10
|
||||
from modules.imports import *
|
||||
from modules.db import add_to_database
|
||||
from modules.config import *
|
||||
from modules.tools import *
|
||||
from modules.error import *
|
||||
from modules.driver_tools import *
|
||||
|
||||
global driver
|
||||
driver = None
|
||||
global _mail, _password, _otp, display
|
||||
|
||||
# TODO : replace by a better print (with logging, cf https://realpython.com/python-logging/)
|
||||
def printf(e):
|
||||
printf2(str(e), _mail)
|
||||
|
||||
# TODO
|
||||
# handle "panda"'s error: error while logging in preventing some task to be done
|
||||
# check that each card worked (lot of misses lately) -- test that -- don't crash at least
|
||||
# Fix l'affichage du compteur de custom_sleep
|
||||
|
||||
|
||||
# create a webdriver
|
||||
def firefox_driver(mobile=False, headless=False):
|
||||
PC_USER_AGENT = (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko)"
|
||||
"Chrome/112.0.0.0 Safari/537.36 Edg/110.0.1587.56")
|
||||
MOBILE_USER_AGENT = (
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 15_5 like Mac OS X)"
|
||||
"AppleWebKit/606.0.2 (KHTML, like Gecko)"
|
||||
"CriOS/107.0.5060.64 Mobile/15E148 Safari/604.1"
|
||||
)
|
||||
options = Options()
|
||||
options.set_preference('intl.accept_languages', 'fr-FR, fr')
|
||||
if proxy_enabled :
|
||||
setup_proxy(proxy_address,proxy_port, options)
|
||||
options.set_preference("browser.link.open_newwindow", 3)
|
||||
options.set_preference("dom.confirm_repost.testing.always_accept", True)
|
||||
if FAST :
|
||||
options.set_preference("permissions.default.image", 2) #disable image loading. You shouldn't use it except if really nessecary
|
||||
if headless:
|
||||
options.add_argument("-headless")
|
||||
if mobile :
|
||||
options.set_preference("general.useragent.override", MOBILE_USER_AGENT)
|
||||
driver = webdriver.Firefox(options=options)
|
||||
driver.set_window_size(1070 + hash(_mail)%20 , 1900 + hash(_password + "salt")%10) # mobile resolution are crazy high now, right ?
|
||||
else :
|
||||
options.set_preference("general.useragent.override", PC_USER_AGENT)
|
||||
driver = webdriver.Firefox(options=options)
|
||||
driver.set_window_size(1900 + hash(_mail)%20 , 1070 + hash(_password + "salt")%10)
|
||||
return(driver)
|
||||
|
||||
|
||||
def log_error(error, ldriver=driver, log=FULL_LOG):
|
||||
global driver
|
||||
if ldriver is None:
|
||||
ldriver = driver
|
||||
if type(error) != str :
|
||||
error = format_error(error)
|
||||
printf(f"\n\n\033[93m Erreur : {str(error)} \033[0m\n\n")
|
||||
if DISCORD_ENABLED_ERROR:
|
||||
with open("page.html", "w") as f:
|
||||
try :
|
||||
f.write(ldriver.page_source)
|
||||
except :
|
||||
f.write("the driver has closed or crashed. Can't access page content")
|
||||
try :
|
||||
img = display.waitgrab()
|
||||
img.save("screenshot.png")
|
||||
except :
|
||||
ldriver.save_screenshot("screenshot.png")
|
||||
if not log:
|
||||
embed = Embed(
|
||||
title="An Error has occured",
|
||||
description=str(error),
|
||||
colour=Colour.red(),
|
||||
)
|
||||
else:
|
||||
embed = Embed(
|
||||
title="Full log is enabled",
|
||||
description=str(error),
|
||||
colour=Colour.blue(),
|
||||
)
|
||||
file = File("screenshot.png")
|
||||
embed.set_image(url="attachment://screenshot.png")
|
||||
embed.set_footer(text=_mail)
|
||||
webhookFailure.send(embed=embed, file=file)
|
||||
webhookFailure.send(file=File("page.html"))
|
||||
|
||||
|
||||
|
||||
# close the tab currently on and go back to the one first, or the one specified
|
||||
def close_tab(tab, SwitchTo=0) -> None:
|
||||
driver.switch_to.window(tab)
|
||||
driver.close()
|
||||
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
|
||||
def play_quiz2(override=10) -> None:
|
||||
printf("starting play_quiz2")
|
||||
for j in range(override):
|
||||
try:
|
||||
rgpd_popup(driver)
|
||||
custom_sleep(uniform(3, 5))
|
||||
page_html = driver.page_source
|
||||
secret_answer = search('IG:"([^"]+)"', page_html)[1] # variable used to calculate offset
|
||||
answers_values = search('data-option="([^"]+)"', page_html)[1]
|
||||
offset = int(secret_answer[-2:], 16) # the last two character converted to int are the offset
|
||||
correct_answer_value = search('correctAnswer":"([0-9]+)', page_html)[1]
|
||||
|
||||
somme = 0
|
||||
for answer in answers_values:
|
||||
somme += ord(answer)
|
||||
|
||||
if somme + offset == int(correct_answer_value):
|
||||
answer_elem = driver.find_element(By.ID, "rqAnswerOption0")
|
||||
answer_elem.click()
|
||||
progressBar(j, 10, name="quiz 2")
|
||||
else:
|
||||
answer_elem = driver.find_element(By.ID, "rqAnswerOption1")
|
||||
answer_elem.click()
|
||||
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
|
||||
printf("play_quiz2 done")
|
||||
|
||||
|
||||
def play_quiz8():
|
||||
override = len(findall("<span id=\"rqQuestionState.\" class=\"emptyCircle\"></span>", driver.page_source))+1
|
||||
printf(f"play_quiz8 : start, override : {override}")
|
||||
try:
|
||||
counter = 0
|
||||
rgpd_popup(driver)
|
||||
for _ in range(override):
|
||||
custom_sleep(uniform(3, 5))
|
||||
correct_answers = []
|
||||
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 :
|
||||
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
|
||||
progressBar(counter, 16, name="Quiz 8")
|
||||
try:
|
||||
answer_elem = driver.find_element(By.ID, answer_id)
|
||||
answer_elem.click()
|
||||
custom_sleep(1)
|
||||
except exceptions.NoSuchElementException :
|
||||
driver.refresh()
|
||||
custom_sleep(10)
|
||||
answer_elem = driver.find_element(By.ID, answer_id)
|
||||
answer_elem.click()
|
||||
except ElementClickInterceptedException :
|
||||
rgpd_popup(driver)
|
||||
correct_answers.append(answer_id)
|
||||
|
||||
except Exception as e:
|
||||
log_error(f"{format_error(e)} \n Good answers : {' '.join(correct_answers)}")
|
||||
printf("play_quiz8 : fin ")
|
||||
|
||||
|
||||
def play_quiz4(override=None):
|
||||
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
|
||||
|
||||
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
|
||||
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)
|
||||
|
||||
except Exception as e:
|
||||
log_error(e)
|
||||
raise ValueError(e)
|
||||
printf("play_quiz4 : end")
|
||||
|
||||
|
||||
# do_poll() answer a random thing to poll, on of daily activities
|
||||
def do_poll():
|
||||
printf("do_poll : start")
|
||||
try:
|
||||
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 error:
|
||||
log_error(error)
|
||||
raise ValueError(error)
|
||||
printf("do_poll : end")
|
||||
|
||||
|
||||
# finds all task to do, and launch them
|
||||
def all_cards(): # return to the main page and closes all other tabs
|
||||
def reset(part2=False):
|
||||
if len(driver.window_handles) == 1:
|
||||
driver.get("https://www.bing.com/rewardsapp/flyout")
|
||||
if part2:
|
||||
driver.find_element(
|
||||
By.XPATH, "/html/body/div/div/div[3]/div[2]/div[2]/div[2]/div[1]"
|
||||
).click()
|
||||
else:
|
||||
driver.switch_to.window(driver.window_handles[1])
|
||||
printf(f"fermeture : {driver.current_url}")
|
||||
driver.close()
|
||||
driver.switch_to.window(driver.window_handles[0])
|
||||
if driver.current_url != "https://www.bing.com/rewardsapp/flyout":
|
||||
driver.get("https://www.bing.com/rewardsapp/flyout")
|
||||
reset(part2)
|
||||
|
||||
def daily_cards(): # cartes de la premiere partie (renouvelées chaque jours).
|
||||
try:
|
||||
# make sure that the daily area is expanded
|
||||
row_element = driver.find_element(By.XPATH, "/html/body/div/div/div[3]/div[2]/div[1]/div[1]")
|
||||
expanded = row_element.get_attribute("aria-expanded")
|
||||
if expanded != "true":
|
||||
row_element.click()
|
||||
for i in range(3):
|
||||
custom_sleep(uniform(3, 5))
|
||||
try:
|
||||
titre = "Placeholder"
|
||||
driver.find_element(
|
||||
By.XPATH,f"/html/body/div/div/div[3]/div[2]/div[1]/div[2]/div/div[{i+1}]/a",
|
||||
).click()
|
||||
sleep(1)
|
||||
titre = driver.title
|
||||
try_play(titre)
|
||||
sleep(1)
|
||||
reset()
|
||||
printf(f"DailyCard {titre} ok")
|
||||
except Exception as e:
|
||||
log_error(f"all_cards card {titre} error ({format_error(e)})")
|
||||
|
||||
try : # devrait renvoyer vrai si la carte i est faite ou pas, a l'aide su symbole en haut a droite de la carte
|
||||
elm = driver.find_element(By.XPATH, f"/html/body/div/div/div[3]/div[2]/div[1]/div[2]/div/div[{i+1}]/a/div/div[2]/div[1]/div[2]/div")
|
||||
if not ("correctCircle" in elm.get_attribute("innerHTML")):
|
||||
printf(f"missed card {i}")
|
||||
try_play(titre)
|
||||
sleep(3)
|
||||
reset()
|
||||
except Exception as e :
|
||||
printf(format_error(e) + "probablement ok - check card")
|
||||
# if it fail, it's probably okay -> when all three card are done, the pannel fold
|
||||
|
||||
except Exception as e:
|
||||
log_error(e)
|
||||
|
||||
def weekly_cards():
|
||||
# make sure that the weekly area is expanded
|
||||
row_element = driver.find_element(By.XPATH, "/html/body/div/div/div[3]/div[2]/div[2]/div[2]")
|
||||
expanded = row_element.get_attribute("aria-expanded")
|
||||
if expanded != "true":
|
||||
row_element.click()
|
||||
|
||||
for i in range(20): # Should raise an error whene there is no card left
|
||||
printf("début de l'une des cartes")
|
||||
driver.find_element(
|
||||
By.XPATH,
|
||||
"/html/body/div/div/div[3]/div[2]/div[2]/div[3]/div/div[1]/a/div/div[2]",
|
||||
).click()
|
||||
driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1])
|
||||
sleep(1)
|
||||
titre = driver.title
|
||||
printf(f"carte {titre} en cours")
|
||||
try_play(titre)
|
||||
reset(True)
|
||||
sleep(1)
|
||||
try:
|
||||
findall('href="([^<]+)" title=""', driver.page_source)[3] # return error if there is no cards left to do
|
||||
except:
|
||||
break
|
||||
|
||||
def top_cards():
|
||||
for _ in range(10):
|
||||
try :
|
||||
driver.find_element(By.XPATH, "/html/body/div/div/div[3]/div[1]/div/div[1]/div[2]").click()
|
||||
reset()
|
||||
except Exception as e:
|
||||
printf(format_error(e))
|
||||
break
|
||||
|
||||
try :
|
||||
top_cards()
|
||||
except Exception as e:
|
||||
log_error(e)
|
||||
|
||||
try:
|
||||
daily_cards()
|
||||
except Exception as e:
|
||||
log_error(e)
|
||||
|
||||
try :
|
||||
weekly_cards()
|
||||
except Exception as e:
|
||||
log_error(e)
|
||||
|
||||
|
||||
# Find out which type of action to do
|
||||
def try_play(nom="inconnu"):
|
||||
rgpd_popup(driver)
|
||||
printf("try_play en cours")
|
||||
|
||||
def play(number):
|
||||
if number == 8 or number == 9:
|
||||
try:
|
||||
printf(f"\033[96m Quiz 8 detected on {nom} \033[0m")
|
||||
play_quiz8()
|
||||
printf(f"\033[92m Quiz 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 == 5 or number == 4:
|
||||
try:
|
||||
printf(f"\033[96m Quiz 4 detected on {nom} \033[0m")
|
||||
play_quiz4()
|
||||
printf(f"\033[92m Quiz 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 == 3 or number == 2:
|
||||
try:
|
||||
printf(f"\033[96m Quiz 2 detected on {nom}\033[0m")
|
||||
play_quiz2()
|
||||
printf(f"\033[92m Quiz 2 succeeded on {nom}\033[0m")
|
||||
except Exception as e:
|
||||
printf(f"fail of PlayQuiz 2. Aborted {e}")
|
||||
else:
|
||||
log_error("There is an error. rqAnswerOption present in page but no action to do. skipping.")
|
||||
|
||||
try:
|
||||
driver.find_element(By.ID, "rqStartQuiz").click() # start the quiz
|
||||
answer_number = driver.page_source.count("rqAnswerOption")
|
||||
play(answer_number)
|
||||
|
||||
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")
|
||||
rgpd_popup(driver)
|
||||
do_poll()
|
||||
printf("Poll succeeded")
|
||||
except Exception as e:
|
||||
printf(f"try_play - 1 - Poll aborted {e}")
|
||||
|
||||
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 search("([0-9]) de ([0-9]) finalisée", driver.page_source):
|
||||
printf("fidélité")
|
||||
rgpd_popup(driver)
|
||||
fidelity()
|
||||
|
||||
else:
|
||||
printf(f"rien à faire sur la page {nom}")
|
||||
rgpd_popup(driver)
|
||||
custom_sleep(uniform(3, 5))
|
||||
|
||||
|
||||
# login() tries to login to your Microsoft account.
|
||||
# it uses global variable _mail and _password to login
|
||||
def login(ldriver):
|
||||
def pwd_login():
|
||||
printf("pwd_login : start")
|
||||
ldriver.get("https://login.live.com")
|
||||
custom_sleep(2)
|
||||
wait_until_visible(By.ID, "i0116", browser = ldriver)
|
||||
mail_elem = ldriver.find_element(By.ID, "i0116")
|
||||
send_keys_wait(mail_elem, _mail)
|
||||
mail_elem.send_keys(Keys.ENTER)
|
||||
custom_sleep(2)
|
||||
wait_until_visible(By.ID, "i0118", browser = ldriver)
|
||||
pwd_elem = ldriver.find_element(By.ID, "i0118")
|
||||
send_keys_wait(pwd_elem, _password)
|
||||
pwd_elem.send_keys(Keys.ENTER)
|
||||
custom_sleep(2)
|
||||
if "Entrez le code de sécurité" in ldriver.page_source :
|
||||
try :
|
||||
a2f_elem = ldriver.find_element(By.ID, "idTxtBx_SAOTCC_OTC")
|
||||
a2f_elem.send_keys(_otp.now())
|
||||
a2f_elem.send_keys(Keys.ENTER)
|
||||
except Exception as e :
|
||||
log_error(e)
|
||||
custom_sleep(5)
|
||||
|
||||
if ('Abuse' in ldriver.current_url) :
|
||||
log_error("account suspended")
|
||||
raise Banned()
|
||||
save_cookies(driver, _mail)
|
||||
for id in ["KmsiCheckboxField","iLooksGood", "idSIButton9", "iCancel"]:
|
||||
try:
|
||||
ldriver.find_element(By.ID, id).click()
|
||||
restart = True
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
try :
|
||||
body_elem = ldriver.find_element(By.TAG_NAME, "body") # in case of any random popup
|
||||
body_elem.send_keys(Keys.ENTER)
|
||||
except :
|
||||
pass
|
||||
printf("login completed - going to MsRewards")
|
||||
custom_sleep(uniform(3,5))
|
||||
ldriver.get("https://www.bing.com/rewardsapp/flyout")
|
||||
custom_sleep(uniform(3,5))
|
||||
for i in [f'[title="Rejoindre maintenant"]', f'[title="Rejoindre"]', f'[title="Join now"]'] :
|
||||
try:
|
||||
ldriver.find_element(By.CSS_SELECTOR, i).click() # depend of the language of the page
|
||||
except:
|
||||
printf(f"element {i} not found")
|
||||
rgpd_popup(ldriver)
|
||||
custom_sleep(uniform(3,5))
|
||||
ldriver.get("https://www.bing.com/rewardsapp/flyout")
|
||||
try:
|
||||
ldriver.find_element(By.CSS_SELECTOR, '[title="Rejoindre maintenant"]').click() # depend of the language of the page
|
||||
except:
|
||||
printf(f"unlock test: fail, probably normal")
|
||||
|
||||
printf('on MsRewards')
|
||||
|
||||
def cookie_login():
|
||||
ldriver.get("https://login.live.com")
|
||||
try :
|
||||
load_cookies(ldriver, _mail)
|
||||
except FileNotFoundError :
|
||||
printf("Creating cookies file")
|
||||
return(False)
|
||||
try :
|
||||
ldriver.refresh()
|
||||
except WebDriverException as e: # This error occurs at random time. Don't really know why
|
||||
if "Reached error page: about:neterror?e=netTimeout" in str(e):
|
||||
printf("Timeout error occurred. \"normal\"....., maybe because of mismatch date ? ")
|
||||
log_error("Timeout error occurred. \"normal\"....., maybe because of mismatch date ?", ldriver, True) # TODO check this hypothesis
|
||||
else:
|
||||
log_error(e)
|
||||
custom_sleep(20) # TODO : remplacer par un wait_element
|
||||
if ("account.microsoft.com" in ldriver.current_url) :
|
||||
ldriver.get("https://bing.com")
|
||||
custom_sleep(5)
|
||||
ldriver.refresh()
|
||||
rgpd_popup(ldriver)
|
||||
ldriver.get("https://www.bing.com/rewardsapp/flyout")
|
||||
if not('>Tableau de bord' in ldriver.page_source):
|
||||
try :
|
||||
ldriver.find_element(By.CSS_SELECTOR, "[h='ID=RewardsFlyout,2.1']").click()
|
||||
custom_sleep(5)
|
||||
if "bing.com" in ldriver.current_url :
|
||||
rgpd_popup(ldriver)
|
||||
ldriver.get("https://www.bing.com/rewardsapp/flyout")
|
||||
if ('>Tableau de bord' in ldriver.page_source) :
|
||||
return(True)
|
||||
else :
|
||||
printf("error during the connection. Trying something else")
|
||||
except Exception as e:
|
||||
log_error(f"not connected 5 - error {e}", ldriver)
|
||||
if not('>Tableau de bord' in ldriver.page_source):
|
||||
try :
|
||||
ldriver.find_element(By.XPATH, "/html/body/div/div/div/div/div[2]/a").click()
|
||||
custom_sleep(5)
|
||||
except Exception as e:
|
||||
log_error(f"erreur not connected 6{e}", ldriver)
|
||||
return(False)
|
||||
if "bing.com" in ldriver.current_url :
|
||||
rgpd_popup(ldriver)
|
||||
ldriver.get("https://www.bing.com/rewardsapp/flyout")
|
||||
if ('>Tableau de bord' in ldriver.page_source) :
|
||||
return(True)
|
||||
else :
|
||||
log_error("not connected 6", ldriver)
|
||||
return(False)
|
||||
return(True)
|
||||
|
||||
if ('account.live.com' in ldriver.current_url):
|
||||
log_error("error 1", ldriver, True)
|
||||
ldriver.refresh()
|
||||
log_error("error 2", ldriver, True)
|
||||
ldriver.get("https://bing.com")
|
||||
ldriver.refresh()
|
||||
rgpd_popup(ldriver)
|
||||
log_error("error 3", ldriver, True)
|
||||
sleep(5)
|
||||
return(True)
|
||||
|
||||
printf("cookies plus valides ?")
|
||||
return(False)
|
||||
|
||||
try :
|
||||
if cookie_login():
|
||||
return (ldriver.current_window_handle)
|
||||
pwd_login() #mobile login in never called. TODO : check if it's bad.
|
||||
return(ldriver.current_window_handle)
|
||||
except Banned:
|
||||
raise Banned()
|
||||
except Exception as e:
|
||||
log_error(e)
|
||||
ldriver.quit()
|
||||
|
||||
|
||||
|
||||
|
||||
# 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}") # {choice(Liste_de_mot)}')
|
||||
custom_sleep(uniform(1, 2))
|
||||
rgpd_popup(driver)
|
||||
send_keys_wait(
|
||||
driver.find_element(By.ID, "sb_form_q"),
|
||||
Keys.BACKSPACE + Keys.BACKSPACE + Keys.BACKSPACE + Keys.BACKSPACE + Keys.BACKSPACE + Keys.BACKSPACE
|
||||
)
|
||||
|
||||
for _ in range(override):
|
||||
word = choice(Liste_de_mot)
|
||||
try:
|
||||
send_keys_wait(driver.find_element(By.ID, "sb_form_q"), word)
|
||||
driver.find_element(By.ID, "sb_form_q").send_keys(Keys.ENTER)
|
||||
except Exception as e :
|
||||
printf(e)
|
||||
sleep(10)
|
||||
driver.get(f'https://www.bing.com/search?q={word}')
|
||||
sleep(3)
|
||||
send_keys_wait(driver.find_element(By.ID, "sb_form_q"), word)
|
||||
driver.find_element(By.ID, "sb_form_q").send_keys(Keys.ENTER)
|
||||
|
||||
custom_sleep(uniform(5, 20))
|
||||
|
||||
try:
|
||||
driver.find_element(By.ID, "sb_form_q").clear()
|
||||
except Exception as 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)}")
|
||||
|
||||
|
||||
|
||||
# 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
|
||||
def log_points(account="unknown"):
|
||||
def get_points():
|
||||
driver.get("https://www.bing.com/rewardsapp/flyout")
|
||||
regex1 = '<a href="https://rewards\.bing\.com/" title="((.{1,3}),(.{1,3})) points" target="_blank"'
|
||||
try:
|
||||
point = search(regex1, driver.page_source)[1].replace(",", "")
|
||||
|
||||
except Exception as e:
|
||||
elem = driver.find_element(By.CSS_SELECTOR, '[title="Microsoft Rewards"]')
|
||||
elem.click()
|
||||
custom_sleep(5)
|
||||
driver.switch_to.window(driver.window_handles[len(driver.window_handles) - 1])
|
||||
custom_sleep(uniform(5,7))
|
||||
try :
|
||||
point = search('availablePoints":([\d]+)', driver.page_source)[1]
|
||||
except :
|
||||
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")
|
||||
|
||||
custom_sleep(uniform(3, 20))
|
||||
account_name = account.split("@")[0]
|
||||
|
||||
if DISCORD_ENABLED_SUCCESS:
|
||||
if DISCORD_EMBED:
|
||||
embed = Embed(
|
||||
title=f"{account_name} actuellement à {str(points)} points", colour=Colour.green()
|
||||
)
|
||||
embed.set_footer(text=account_name)
|
||||
webhookSuccess.send(embed=embed)
|
||||
else:
|
||||
webhookSuccess.send(f"{account_name} actuellement à {str(points)} points")
|
||||
|
||||
if sql_enabled :
|
||||
add_to_database(account_name, points, sql_host, sql_usr, sql_pwd, sql_database)
|
||||
|
||||
|
||||
def fidelity():
|
||||
try:
|
||||
while 1: #close all tabs
|
||||
try:
|
||||
close_tab(1)
|
||||
except:
|
||||
break
|
||||
try :
|
||||
fidelity_link_page = get(FidelityLink) #get the url of fidelity page
|
||||
except Exception as e :
|
||||
printf(e)
|
||||
fidelity_link_page = False
|
||||
|
||||
if fidelity_link_page :
|
||||
fidelity_link = fidelity_link_page.content.decode("UTF-8")
|
||||
|
||||
if (fidelity_link.split(":")[0] == "https") or (fidelity_link.split(":")[0] == "http") :
|
||||
|
||||
driver.get(fidelity_link)
|
||||
wait_until_visible(By.CSS_SELECTOR, 'div[class="pull-left spacer-48-bottom punchcard-row"]', browser=driver)
|
||||
try :
|
||||
choix = driver.find_element(By.CSS_SELECTOR, 'div[class="pull-left spacer-48-bottom punchcard-row"]') # pull-left spacer-48-bottom punchcard-row? USELESS ?
|
||||
except : # tentative de fix
|
||||
driver.refresh()
|
||||
choix = driver.find_element(By.CSS_SELECTOR, 'div[class="pull-left spacer-48-bottom punchcard-row"]')
|
||||
answer_number = search("([0-9]) of ([0-9]) completed", driver.page_source)
|
||||
if answer_number is None:
|
||||
answer_number = search("([0-9]) de ([0-9]) finalisé", driver.page_source)
|
||||
if answer_number is None :
|
||||
answer_number = search("([0-9]) licence\(s\) sur ([0-9]) disponible\(s\)", driver.page_source)
|
||||
if answer_number is None :
|
||||
answer_number = [0,0,0]
|
||||
|
||||
for _ in range(int(answer_number[2]) - int(answer_number[1])):
|
||||
driver.refresh()
|
||||
custom_sleep(2)
|
||||
card_elem = driver.find_element(By.CLASS_NAME, "spacer-48-bottom")
|
||||
try :
|
||||
button_text = search('<span class="pull-left margin-right-15">([^<^>]+)</span>',card_elem.get_attribute("innerHTML"))[1]
|
||||
bouton_card = driver.find_element(By.XPATH, f'//span[text()="{button_text}"]')
|
||||
bouton_card.click()
|
||||
except Exception as e1 :
|
||||
try :
|
||||
recover_elem = driver.find_element(By.XPATH,'/html/body/div[1]/div[2]/main/div[2]/div[2]/div[7]/div[3]/div[1]/a')
|
||||
recover_elem.click()
|
||||
except Exception as e2 :
|
||||
log_error(f"fidélité - double erreur - e1 : {format_error(e1)} - e2 {format_error(e2)}")
|
||||
break
|
||||
custom_sleep(uniform(3, 5))
|
||||
driver.switch_to.window(driver.window_handles[1])
|
||||
try_play(driver.title)
|
||||
driver.get(fidelity_link) # USELESS ?
|
||||
custom_sleep(uniform(3, 5))
|
||||
try:
|
||||
close_tab(driver.window_handles[1])
|
||||
except Exception as e:
|
||||
printf(e)
|
||||
printf("fidelity - done")
|
||||
else :
|
||||
printf("invalid fidelity link.")
|
||||
except Exception as e:
|
||||
log_error(e)
|
||||
|
||||
|
||||
def mobile_login_pwd(error):
|
||||
try:
|
||||
# TODO
|
||||
# seems fine, check if there are no issues NO
|
||||
mot = choice(Liste_de_mot).replace(" ","+")
|
||||
mobile_driver.get(f"https://www.bing.com/search?q={mot}")
|
||||
rgpd_popup(mobile_driver)
|
||||
printf("start of Mobile login")
|
||||
try :
|
||||
mobile_driver.find_element(By.ID, "mHamburger").click()
|
||||
except Exception as e :
|
||||
elm = mobile_driver.find_element(By.ID, "mHamburger")
|
||||
mobile_driver.execute_script("arguments[0].scrollIntoView();", elm)
|
||||
mobile_driver.find_element(By.ID, "mHamburger").click()
|
||||
|
||||
wait_until_visible(By.ID, "hb_s", browser=mobile_driver)
|
||||
mobile_driver.find_element(By.ID, "hb_s").click()
|
||||
wait_until_visible(By.ID, "i0116", browser=mobile_driver)
|
||||
mail_elem = mobile_driver.find_element(By.ID, "i0116")
|
||||
send_keys_wait(mail_elem, _mail)
|
||||
mail_elem.send_keys(Keys.ENTER)
|
||||
wait_until_visible(By.ID, "i0118", browser=mobile_driver)
|
||||
pwd_elem = mobile_driver.find_element(By.ID, "i0118")
|
||||
send_keys_wait(pwd_elem, _password)
|
||||
pwd_elem.send_keys(Keys.ENTER)
|
||||
custom_sleep(uniform(1, 2))
|
||||
if "Entrez le code de sécurité" in driver.page_source :
|
||||
try :
|
||||
a2f_elem = mobile_driver.find_element(By.ID, "idTxtBx_SAOTCC_OTC")
|
||||
a2f_elem.send_keys(_otp.now())
|
||||
a2f_elem.send_keys(Keys.ENTER)
|
||||
except Exception as e :
|
||||
log_error(e)
|
||||
custom_sleep(uniform(1, 2))
|
||||
for i in ["KmsiCheckboxField", "iLooksGood", "idSIButton9"]:
|
||||
try:
|
||||
mobile_driver.find_element(By.ID,i ).click()
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
printf("end of Mobile login")
|
||||
|
||||
except Exception as e:
|
||||
error += 1
|
||||
if error <= 3:
|
||||
printf(f"failure on mobile_login. Retrying({error}/3), {e}")
|
||||
custom_sleep(uniform(5, 10))
|
||||
mobile_login_pwd(error)
|
||||
else:
|
||||
log_error(f"login impossible 3 fois de suite. {e}", mobile_driver)
|
||||
mobile_driver.quit()
|
||||
return(True)
|
||||
|
||||
|
||||
def mobile_alert_popup():
|
||||
try:
|
||||
alert = mobile_driver.switch_to.alert
|
||||
alert.dismiss()
|
||||
except exceptions.NoAlertPresentException as e:
|
||||
pass
|
||||
except Exception as e:
|
||||
log_error(e, mobile_driver)
|
||||
|
||||
|
||||
def bing_mobile_search(override=randint(22, 25)):
|
||||
global mobile_driver
|
||||
mobile_driver = firefox_driver(mobile=True)
|
||||
try:
|
||||
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))
|
||||
rgpd_popup(mobile_driver)
|
||||
custom_sleep(uniform(1, 1.5))
|
||||
for i in range(override): # 20
|
||||
try :
|
||||
mot = choice(Liste_de_mot)
|
||||
send_keys_wait(mobile_driver.find_element(By.ID, "sb_form_q"), mot)
|
||||
mobile_driver.find_element(By.ID, "sb_form_q").send_keys(Keys.ENTER)
|
||||
custom_sleep(uniform(5, 20))
|
||||
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 e:
|
||||
printf(e)
|
||||
mobile_driver.refresh()
|
||||
custom_sleep(30)
|
||||
i -= 1
|
||||
mobile_driver.quit()
|
||||
|
||||
except Exception as e:
|
||||
log_error(e, mobile_driver)
|
||||
mobile_driver.quit()
|
||||
|
||||
|
||||
def daily_routine(custom = False):
|
||||
try :
|
||||
if not custom: # custom already login
|
||||
login(driver)
|
||||
except Banned :
|
||||
log_error("THIS ACCOUNT IS BANNED. FIX THIS ISSUE WITH -U")
|
||||
return()
|
||||
|
||||
try:
|
||||
all_cards()
|
||||
except Exception as e:
|
||||
log_error(e)
|
||||
|
||||
try:
|
||||
fidelity()
|
||||
except Exception as e:
|
||||
log_error(e)
|
||||
|
||||
try:
|
||||
bing_pc_search()
|
||||
except Exception as e:
|
||||
log_error(e)
|
||||
|
||||
try:
|
||||
bing_mobile_search()
|
||||
except Exception as e:
|
||||
log_error(e)
|
||||
|
||||
|
||||
try:
|
||||
log_points(_mail)
|
||||
except Exception as e:
|
||||
log_error(e)
|
||||
|
||||
|
||||
def dev():
|
||||
pass
|
||||
|
||||
|
||||
def CustomStart(Credentials):
|
||||
global START_TIME
|
||||
if not LINUX_HOST :
|
||||
raise NameError('You need to be on linux to do that, due to the utilisation of a module named enquieries, sorry.')
|
||||
global driver, _mail, _password, p, _otp
|
||||
|
||||
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()
|
||||
START_TIME = time() # Reset timer to the start of the actions
|
||||
|
||||
for cred in liste:
|
||||
_mail = cred[0]
|
||||
_password = cred[1]
|
||||
if len(cred) == 3:
|
||||
_otp = TOTP(cred[2])
|
||||
|
||||
driver = firefox_driver()
|
||||
driver.implicitly_wait(3)
|
||||
|
||||
if login(driver) != "STOP":
|
||||
if "tout" in Actions:
|
||||
daily_routine(True)
|
||||
if "daily" in Actions:
|
||||
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(_mail)
|
||||
except Exception as e:
|
||||
printf(f"CustomStart {e}")
|
||||
driver.close()
|
||||
|
||||
|
||||
|
||||
if VNC_ENABLED :
|
||||
display = SmartDisplay(backend="xvnc", size=(2160, 2160), rfbport=VNC_PORT, color_depth=24)
|
||||
else :
|
||||
display = SmartDisplay(size=(2160, 2160))
|
||||
display.start()
|
||||
|
||||
|
||||
if CUSTOM_START:
|
||||
CustomStart(Credentials)
|
||||
elif UNBAN:
|
||||
_mail, _password = select_accounts(False)[0]
|
||||
driver = firefox_driver()
|
||||
try :
|
||||
login(driver)
|
||||
except Banned:
|
||||
unban()
|
||||
|
||||
driver.quit()
|
||||
elif POINTS_FILE != "":
|
||||
save_points_from_file(POINTS_FILE)
|
||||
else:
|
||||
for cred in Credentials:
|
||||
_mail = cred[0]
|
||||
_password = cred[1]
|
||||
if len(cred) == 3:
|
||||
_otp = TOTP(cred[2])
|
||||
printf("\n\n")
|
||||
printf(_mail)
|
||||
custom_sleep(1)
|
||||
printf("début du driver")
|
||||
driver = firefox_driver()
|
||||
printf("driver demarré")
|
||||
driver.implicitly_wait(3)
|
||||
try:
|
||||
daily_routine()
|
||||
driver.quit()
|
||||
attente = uniform(1200, 3600)
|
||||
printf(f"finis. attente de {round(attente/60)}min")
|
||||
custom_sleep(attente)
|
||||
except KeyboardInterrupt:
|
||||
printf("canceled. Closing driver and display.")
|
||||
driver.quit()
|
||||
display.stop()
|
||||
except Exception as e:
|
||||
printf(f"error not catched. skipping this account. {e}")
|
||||
driver.quit()
|
||||
|
||||
display.stop()
|
72
database.py
Normal file
72
database.py
Normal file
@ -0,0 +1,72 @@
|
||||
import mysql.connector
|
||||
import configparser
|
||||
from os import path
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--file",
|
||||
help="Choose a file",
|
||||
type=argparse.FileType('r')
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
config_path = "./user_data/config.cfg"
|
||||
config = configparser.ConfigParser()
|
||||
config.read(config_path)
|
||||
|
||||
sql_usr = config["SQL"]["usr"]
|
||||
sql_pwd = config["SQL"]["pwd"]
|
||||
sql_host = config["SQL"]["host"]
|
||||
sql_database = config["SQL"]["database"]
|
||||
|
||||
|
||||
mydb = mysql.connector.connect(
|
||||
host=sql_host,
|
||||
user=sql_usr,
|
||||
password=sql_pwd,
|
||||
database = sql_database
|
||||
)
|
||||
mycursor = mydb.cursor()
|
||||
|
||||
def add_account(name: str, endroit: str, proprio: str):
|
||||
command = f'INSERT INTO comptes (compte, proprio, endroit, last_pts) VALUES ("{name}", "{proprio}", "{endroit}",0);'
|
||||
mycursor.execute(command)
|
||||
|
||||
|
||||
def ban_account(name: str, pts = 0):
|
||||
|
||||
command1 = f"INSERT INTO banned (nom, total) VALUES ('{name}', {pts});"
|
||||
command2 = f'DELETE FROM comptes WHERE compte = "{name}";'
|
||||
mycursor.execute(command1)
|
||||
mycursor.execute(command2)
|
||||
|
||||
def update_pts(name: str, pts = 0):
|
||||
pass
|
||||
|
||||
|
||||
print("ajouter un compte : 1\nban un compte : 2")
|
||||
i = input()
|
||||
if i == "1":
|
||||
if args.file :
|
||||
l =[x.split(",")[0].split("@")[0] for x in args.file.readlines()]
|
||||
endroit = input("ou est le bot ? ")
|
||||
proprio = input("qui est le proprio ? ")
|
||||
for name in l :
|
||||
add_account(name, endroit, proprio)
|
||||
else :
|
||||
name = input("quel est le nom ? ").split("@")[0]
|
||||
endroit = input("ou est le bot ? ")
|
||||
proprio = input("qui est le proprio ? ")
|
||||
add_account(name, endroit, proprio)
|
||||
elif i == '2':
|
||||
name = input("quel est le compte qui a été ban ? ")
|
||||
pts = input("il avait combien de points ? ")
|
||||
ban_account(name, pts)
|
||||
|
||||
mydb.commit()
|
||||
mycursor.close()
|
||||
mydb.close()
|
52
main.py
52
main.py
@ -1,8 +1,11 @@
|
||||
#/usr/bin/python3.10
|
||||
|
||||
from packaging.version import parse as parse_version
|
||||
import configparser
|
||||
import os
|
||||
import shutil
|
||||
import requests
|
||||
import sys
|
||||
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
@ -16,8 +19,6 @@ except :
|
||||
config.read(config_path)
|
||||
|
||||
|
||||
|
||||
|
||||
def confirm(texte, default = False):
|
||||
if default :
|
||||
txt = '[Y/n]'
|
||||
@ -41,12 +42,12 @@ text = {"fr" : {
|
||||
"next" : "voulez vous ajouter un compte ? ",
|
||||
"finc" : "comptes en cours d'ajout ",
|
||||
"ajout" : "comptes ajouté ",
|
||||
"fidelity" : "avez vous un lien sur lequel le lien vers la page fidelité du mois est le seul contenu de la page ? ",
|
||||
"fidelity" : "avez vous un lien sur lequel le lien vers la page fidélité du mois est le seul contenu de la page ? ",
|
||||
"lien" : "entrez le lien ",
|
||||
"discorde" : "voulez vous envoyer les erreurs sur discord ? ",
|
||||
"w1" : "entrez le lien du WebHook pour envoyer les points ",
|
||||
"w2" : "entrez le lien du WebHook pour envoyer les erreurs ",
|
||||
"msqle" : "voulez vous untiliser une base de donnée ",
|
||||
"msqle" : "voulez vous utiliser une base de donnée ",
|
||||
"msqll" : "entrez le lien de la base de donnée ",
|
||||
"msqlu" : "entrez l'utilisateur de la base de donnée ",
|
||||
"msqlp" : "entrez le mot de passe de la base de donnée ",
|
||||
@ -73,17 +74,16 @@ def setup_comptes():
|
||||
if confirm(t["next"], default = True):
|
||||
compte = input(t["compte"])
|
||||
mdp = input(t["mdp"])
|
||||
lc.append(f"{compte},{mdp}\n")
|
||||
lc.append(f"{compte},{mdp}")
|
||||
else:
|
||||
print(t["finc"])
|
||||
break
|
||||
f = open('./user_data/login.csv', "w")
|
||||
for i in lc :
|
||||
f.write(i)
|
||||
f.write("\n")
|
||||
f.close()
|
||||
print(t["ajout"])
|
||||
|
||||
#modifie le fichier de configuration
|
||||
edit_config_txt("logpath",f'{os.getcwd()}/user_data/login.csv')
|
||||
|
||||
|
||||
@ -110,13 +110,14 @@ def setup_settings():
|
||||
discord()
|
||||
proxy()
|
||||
sql()
|
||||
amazon()
|
||||
|
||||
|
||||
|
||||
def general():
|
||||
if confirm(t["fidelity"]):
|
||||
lien = input(t["lien"])
|
||||
edit_config_txt('FidelityLink',lien)
|
||||
|
||||
|
||||
def discord():
|
||||
enabled = confirm(t["discorde"], default = True)
|
||||
if enabled :
|
||||
@ -128,6 +129,7 @@ def discord():
|
||||
w2 = input(t["w2"])
|
||||
edit_config_txt("errorlink",w2)
|
||||
|
||||
|
||||
def sql() :
|
||||
enabled = confirm(t["msqle"], default = False)
|
||||
if enabled :
|
||||
@ -140,7 +142,8 @@ def sql() :
|
||||
edit_config_txt("usr",user)
|
||||
pwd = input(t["msqlp"])
|
||||
edit_config_txt("pwd",pwd)
|
||||
|
||||
|
||||
|
||||
def proxy() :
|
||||
enabled = confirm(t["proxye"], default = False)
|
||||
if enabled :
|
||||
@ -149,14 +152,31 @@ def proxy() :
|
||||
edit_config_txt("url",lien)
|
||||
port = input(t["proxyp"])
|
||||
edit_config_txt("port",port)
|
||||
|
||||
def amazon():
|
||||
enabled = confirm("claim les recompenses automatiquement sur amazon ?", default = False)
|
||||
edit_config_txt("claim_amazon",enabled)
|
||||
|
||||
|
||||
def check_update():
|
||||
try :
|
||||
latest = requests.get("https://api.github.com/repos/piair338/MsRewards/releases").json()[0]["tag_name"]
|
||||
latest = parse_version(latest)
|
||||
except Exception as e :
|
||||
print(e)
|
||||
return ()
|
||||
f = open("./version", 'r')
|
||||
txt = f.readlines()[0].replace("\n","")
|
||||
f.close()
|
||||
cur = parse_version(txt)
|
||||
if not (cur < latest) :
|
||||
print("Already up to date.")
|
||||
else :
|
||||
print(f"updating to {latest}")
|
||||
os.system("git pull")
|
||||
print("updated")
|
||||
|
||||
|
||||
LogPath = config["PATH"]["logpath"]
|
||||
if LogPath == "/your/path/to/loginandpass.csv" :
|
||||
setup()
|
||||
else :
|
||||
os.system("python3.10 V4.py")
|
||||
args = " ".join(sys.argv[1::])
|
||||
check_update()
|
||||
os.system("python3 V5.py " + args)
|
||||
|
69
manual_claim.py
Normal file
69
manual_claim.py
Normal file
@ -0,0 +1,69 @@
|
||||
from easyprocess import EasyProcess
|
||||
from pyvirtualdisplay import Display
|
||||
from modules.config import *
|
||||
from selenium import webdriver
|
||||
from selenium.common import exceptions
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.firefox.options import Options
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.ui import Select
|
||||
|
||||
|
||||
def setup_proxy(ip, port, options, socks=False) :
|
||||
PROXY = f"{ip}:{port}"
|
||||
if socks :
|
||||
options.set_preference('network.proxy.type', 1)
|
||||
options.set_preference('network.proxy.socks', ip)
|
||||
options.set_preference('network.proxy.socks_port', int(port))
|
||||
options.set_preference("browser.link.open_newwindow", 3)
|
||||
else :
|
||||
webdriver.DesiredCapabilities.FIREFOX['proxy'] = {
|
||||
"httpProxy": PROXY,
|
||||
"sslProxy": PROXY,
|
||||
"proxyType": "MANUAL",
|
||||
}
|
||||
|
||||
def firefox_driver(mobile=False, Headless=False):
|
||||
PC_USER_AGENT = (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko)"
|
||||
"Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.56")
|
||||
MOBILE_USER_AGENT = (
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 15_5 like Mac OS X)"
|
||||
"AppleWebKit/605.1.15 (KHTML, like Gecko)"
|
||||
"CriOS/103.0.5060.63 Mobile/15E148 Safari/604.1"
|
||||
)
|
||||
|
||||
options = Options()
|
||||
options.set_preference('intl.accept_languages', 'fr-FR, fr')
|
||||
if proxy_enabled :
|
||||
setup_proxy(proxy_address,proxy_port, options)
|
||||
options.set_preference("browser.link.open_newwindow", 3)
|
||||
if FAST :
|
||||
options.set_preference("permissions.default.image", 2) #disable image loading. May add this without the fast option soon
|
||||
if Headless:
|
||||
options.add_argument("-headless")
|
||||
if mobile :
|
||||
options.set_preference("general.useragent.override", MOBILE_USER_AGENT)
|
||||
else :
|
||||
options.set_preference("general.useragent.override", PC_USER_AGENT)
|
||||
driver = webdriver.Firefox(options=options)
|
||||
driver.set_window_size(1900 + hash(_mail)%20 , 1070 + hash(_password + "salt")%10)
|
||||
return(driver)
|
||||
|
||||
def select_accounts(multiple = True):
|
||||
system("clear") # clear from previous command to allow a clean choice
|
||||
emails = [x[0] for x in Credentials] # list of all email adresses
|
||||
emails_selected = enquiries.choose(f"quel{'s' if multiple else ''} compte{'s' if multiple else ''} ?", emails, multi=multiple)
|
||||
return([x for x in Credentials if x[0] in emails_selected])
|
||||
|
||||
|
||||
|
||||
with Display(backend="xvnc", size=(2000, 1000), rfbport=5904) as disp:
|
||||
_mail, _password = select_accounts(False)[0]
|
||||
driver = firefox_driver()
|
||||
print(f"connect via VNC to port 5904. \nID: {_mail}\npwd : {_password}")
|
||||
i = input('stop ? ')
|
||||
driver.close()
|
@ -7,7 +7,9 @@ import argparse
|
||||
from discord import ( # Importing discord.Webhook and discord.RequestsWebhookAdapter
|
||||
RequestsWebhookAdapter,
|
||||
Webhook,
|
||||
Colour,
|
||||
)
|
||||
|
||||
from time import time
|
||||
from random import shuffle
|
||||
|
||||
@ -33,7 +35,6 @@ parser.add_argument(
|
||||
action="store_true"
|
||||
)
|
||||
|
||||
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--log",
|
||||
@ -72,7 +73,16 @@ parser.add_argument(
|
||||
default=""
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--vnc",
|
||||
help="enable VNC",
|
||||
dest="vnc",
|
||||
default="None"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
CUSTOM_START = args.override
|
||||
UNBAN = args.unban
|
||||
LOG = args.log
|
||||
@ -80,13 +90,13 @@ FULL_LOG = args.fulllog
|
||||
FAST = args.fast
|
||||
if CUSTOM_START :
|
||||
LOG = True
|
||||
|
||||
VNC_ENABLED = args.vnc != "None"
|
||||
VNC_PORT = args.vnc
|
||||
POINTS_FILE = args.points_file
|
||||
|
||||
# global variables used later in the code
|
||||
LINUX_HOST = platform == "linux" # if the computer running this programm is linux, it allow more things
|
||||
LINUX_HOST = platform == "linux" # if the computer running this program is Linux, it allow more things
|
||||
START_TIME = time()
|
||||
driver = None
|
||||
|
||||
|
||||
if LINUX_HOST:
|
||||
@ -138,9 +148,8 @@ sql_pwd = config["SQL"]["pwd"]
|
||||
sql_host = config["SQL"]["host"]
|
||||
sql_database = config["SQL"]["database"]
|
||||
|
||||
# Other seetings
|
||||
# Other settings
|
||||
IPV6_CHECKED = config["OTHER"]["ipv6"]
|
||||
CLAIM_AMAZON = config["OTHER"]["claim_amazon"] == "True"
|
||||
|
||||
|
||||
g = open(MotPath, "r", encoding="utf-8")
|
||||
|
@ -33,35 +33,38 @@ def get_row(compte, points, mycursor, same_points = True): #return if there is a
|
||||
|
||||
|
||||
def add_to_database(compte, points, sql_host,sql_usr,sql_pwd,sql_database, save_if_fail=True):
|
||||
try:
|
||||
mydb = mysql.connector.connect(
|
||||
host=sql_host,
|
||||
user=sql_usr,
|
||||
password=sql_pwd,
|
||||
database = sql_database
|
||||
)
|
||||
mycursor = mydb.cursor()
|
||||
if points is None:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
mydb = mysql.connector.connect(
|
||||
host=sql_host,
|
||||
user=sql_usr,
|
||||
password=sql_pwd,
|
||||
database = sql_database
|
||||
)
|
||||
mycursor = mydb.cursor()
|
||||
|
||||
if get_row(compte, points,mycursor, True): #check if the row exist with the same ammount of points and do nothind if it does
|
||||
#printf("les points sont deja bon")
|
||||
#return(0)
|
||||
pass
|
||||
elif get_row(compte, points,mycursor, False) : #check if the row exist, but without the same ammount of points and update the point account then
|
||||
update_row(compte, points,mycursor,mydb)
|
||||
#printf("row updated")
|
||||
#return(1)
|
||||
else : # if the row don't exist, create it with the good ammount of points
|
||||
add_row(compte, points,mycursor,mydb)
|
||||
#return(2) #printf("row added")
|
||||
if int(points) > 10 :
|
||||
update_last(compte, points, mycursor, mydb)
|
||||
if get_row(compte, points,mycursor, True): #check if the row exist with the same ammount of points and do nothind if it does
|
||||
#printf("les points sont deja bon")
|
||||
#return(0)
|
||||
pass
|
||||
elif get_row(compte, points,mycursor, False) : #check if the row exist, but without the same ammount of points and update the point account then
|
||||
update_row(compte, points,mycursor,mydb)
|
||||
#printf("row updated")
|
||||
#return(1)
|
||||
else : # if the row don't exist, create it with the good ammount of points
|
||||
add_row(compte, points,mycursor,mydb)
|
||||
#return(2) #printf("row added")
|
||||
if int(points) > 10 :
|
||||
update_last(compte, points, mycursor, mydb)
|
||||
|
||||
mycursor.close()
|
||||
mydb.close()
|
||||
except BaseException as e:
|
||||
if save_if_fail:
|
||||
print("\nLes points n'ont pas pu être ajoutés, enregistrement dans le fichier 'points.csv'\n")
|
||||
with open("points.csv", "a") as file:
|
||||
file.write(f"{compte},{points}\n")
|
||||
raise e
|
||||
mycursor.close()
|
||||
mydb.close()
|
||||
except BaseException as e:
|
||||
if save_if_fail:
|
||||
print("\nLes points n'ont pas pu être ajoutés, enregistrement dans le fichier 'points.csv'\n")
|
||||
with open("points.csv", "a") as file:
|
||||
file.write(f"{compte},{points}\n")
|
||||
raise e
|
||||
|
||||
|
57
modules/driver_tools.py
Normal file
57
modules/driver_tools.py
Normal file
@ -0,0 +1,57 @@
|
||||
from modules.imports import *
|
||||
from modules.config import *
|
||||
|
||||
|
||||
def setup_proxy(ip, port, options, socks=False) :
|
||||
PROXY = f"{ip}:{port}"
|
||||
if socks :
|
||||
options.set_preference('network.proxy.type', 1)
|
||||
options.set_preference('network.proxy.socks', ip)
|
||||
options.set_preference('network.proxy.socks_port', int(port))
|
||||
options.set_preference("browser.link.open_newwindow", 3)
|
||||
else :
|
||||
webdriver.DesiredCapabilities.FIREFOX['proxy'] = {
|
||||
"httpProxy": PROXY,
|
||||
"sslProxy": PROXY,
|
||||
"proxyType": "MANUAL",
|
||||
}
|
||||
|
||||
#Deal with rgpd popup as well as some random popup like 'are you satisfied' one
|
||||
def rgpd_popup(driver) -> None:
|
||||
for i in ["bnp_btn_accept", "bnp_hfly_cta2", "bnp_hfly_close"] :
|
||||
try:
|
||||
driver.find_element(By.ID, i).click()
|
||||
except:
|
||||
pass
|
||||
|
||||
# save webdriver cookies
|
||||
def save_cookies(driver, _mail):
|
||||
pickle.dump(driver.get_cookies(), open(f"{'/'.join(__file__.split('/')[:-2])}/user_data/cookies/{_mail}.pkl", "wb"))
|
||||
|
||||
# load cookies previously saved to the driver
|
||||
def load_cookies(driver, _mail):
|
||||
cookies = pickle.load(open(f"{'/'.join(__file__.split('/')[:-2])}/user_data/cookies/{_mail}.pkl", "rb"))
|
||||
for cookie in cookies:
|
||||
driver.add_cookie(cookie)
|
||||
|
||||
"""
|
||||
send_keys_wait([selenium element:element, str:keys]) send the different keys to the field element, with a random time between each press to simulate human action.
|
||||
keys can be an string, but also selenium keys
|
||||
"""
|
||||
def send_keys_wait(element, keys):
|
||||
for i in keys:
|
||||
element.send_keys(i)
|
||||
if FAST :
|
||||
pass
|
||||
else :
|
||||
sleep(uniform(0.1, 0.3))
|
||||
|
||||
|
||||
|
||||
# Wait for the presence of the element identifier or [timeout]s
|
||||
def wait_until_visible(search_by: str, identifier: str, timeout = 20, browser = None) -> None:
|
||||
try :
|
||||
WebDriverWait(browser, timeout).until(EC.visibility_of_element_located((search_by,identifier)), "element not found")
|
||||
except TimeoutException as e:
|
||||
print(f"element not found after {timeout}s")
|
||||
|
@ -1,5 +1,3 @@
|
||||
from selenium.common.exceptions import TimeoutException, NoSuchElementException, ElementClickInterceptedException
|
||||
|
||||
class Banned(Exception):
|
||||
pass
|
||||
|
||||
|
24
modules/imports.py
Normal file
24
modules/imports.py
Normal file
@ -0,0 +1,24 @@
|
||||
import asyncio
|
||||
import csv
|
||||
from os import sys, system, path
|
||||
from random import choice, randint, shuffle, uniform
|
||||
from re import findall, search
|
||||
from sys import platform
|
||||
from time import sleep
|
||||
from requests import get
|
||||
from selenium import webdriver
|
||||
from selenium.common import exceptions
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.firefox.options import Options
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.ui import Select
|
||||
from selenium.common.exceptions import WebDriverException, TimeoutException, NoSuchElementException, ElementClickInterceptedException
|
||||
|
||||
from pyotp import TOTP
|
||||
from pyvirtualdisplay import Display
|
||||
from pyvirtualdisplay.smartdisplay import SmartDisplay
|
||||
import pickle
|
||||
from datetime import timedelta, datetime
|
||||
from discord import Embed, Colour, File
|
@ -1,32 +0,0 @@
|
||||
#add return a string witx tabs
|
||||
def tabs(x):
|
||||
return(x*4*" ")
|
||||
|
||||
|
||||
#create dictionnary with all progress bars
|
||||
def dico(progress):
|
||||
dico_task = {
|
||||
"daily" : {
|
||||
"all" : progress.add_task("[yellow]daily", total=100, start=False, visible=False),
|
||||
"carte0" : progress.add_task(f"[yellow]{tabs(1)}carte 1", total=100, start=False, visible = False),
|
||||
"carte1" : progress.add_task(f"[yellow]{tabs(1)}carte 2", total=100, start=False, visible = False),
|
||||
"carte2" : progress.add_task(f"[yellow]{tabs(1)}carte 3", total=100, start=False, visible = False)
|
||||
},
|
||||
"weekly" : {
|
||||
"all" : progress.add_task("[yellow]weekly", total=100, start=False, visible=False),
|
||||
"carte1" : progress.add_task(f"[yellow]{tabs(1)}carte 1", total=100, start=False, visible = False),
|
||||
"carte2" : progress.add_task(f"[yellow]{tabs(1)}carte 2", total=100, start=False, visible = False),
|
||||
"carte3" : progress.add_task(f"[yellow]{tabs(1)}carte 3", total=100, start=False, visible = False),
|
||||
"carte3" : progress.add_task(f"[yellow]{tabs(1)}carte 4", total=100, start=False, visible = False),
|
||||
"carte3" : progress.add_task(f"[yellow]{tabs(1)}carte 5", total=100, start=False, visible = False),
|
||||
"carte3" : progress.add_task(f"[yellow]{tabs(1)}carte 6", total=100, start=False, visible = False),
|
||||
"carte3" : progress.add_task(f"[yellow]{tabs(1)}carte 7", total=100, start=False, visible = False),
|
||||
"carte3" : progress.add_task(f"[yellow]{tabs(1)}carte 8", total=100, start=False, visible = False),
|
||||
"carte3" : progress.add_task(f"[yellow]{tabs(1)}carte 9", total=100, start=False, visible = False),
|
||||
},
|
||||
"PC" : progress.add_task(f"[yellow]PC", total=100, start=False, visible = False),
|
||||
"Mobile" : progress.add_task(f"[yellow]Mobile", total=100, start=False, visible = False),
|
||||
|
||||
|
||||
}
|
||||
return(dico_task)
|
@ -1,72 +1,23 @@
|
||||
from time import sleep
|
||||
from datetime import timedelta
|
||||
from random import uniform
|
||||
import discord
|
||||
from discord import ( # Importing discord.Webhook and discord.RequestsWebhookAdapter
|
||||
Colour,
|
||||
Webhook,
|
||||
)
|
||||
|
||||
from modules.imports import *
|
||||
from modules.config import *
|
||||
|
||||
"""
|
||||
send_keys_wait([selenium element:element, str:keys]) send the different keys to the field element, with a random time between each press to simulate human action.
|
||||
keys can be an string, but alos selenium keys
|
||||
"""
|
||||
def send_keys_wait(element, keys):
|
||||
for i in keys:
|
||||
element.send_keys(i)
|
||||
if FAST :
|
||||
pass
|
||||
else :
|
||||
sleep(uniform(0.1, 0.3))
|
||||
|
||||
|
||||
def LogError(message, driver, mail, log=FULL_LOG):
|
||||
print(f"\n\n\033[93m Erreur : {str(message)} \033[0m\n\n")
|
||||
if DISCORD_ENABLED_ERROR:
|
||||
with open("page.html", "w") as f:
|
||||
f.write(driver.page_source)
|
||||
|
||||
driver.save_screenshot("screenshot.png")
|
||||
if not log:
|
||||
embed = discord.Embed(
|
||||
title="An Error has occured",
|
||||
description=str(message),
|
||||
colour=Colour.red(),
|
||||
)
|
||||
else:
|
||||
embed = discord.Embed(
|
||||
title="Full log is enabled",
|
||||
description=str(message),
|
||||
colour=Colour.blue(),
|
||||
)
|
||||
|
||||
file = discord.File("screenshot.png")
|
||||
embed.set_image(url="attachment://screenshot.png")
|
||||
embed.set_footer(text=mail)
|
||||
webhookFailure.send(embed=embed, file=file)
|
||||
webhookFailure.send(file=discord.File("page.html"))
|
||||
|
||||
|
||||
|
||||
# add the time arround the text given in [text]
|
||||
# [text] : string
|
||||
def Timer(text, mail):
|
||||
return(f"[{mail} - {timedelta(seconds = round(float(time() - START_TIME)))}] " + str(text))
|
||||
# add the time arround the text given in [text]&
|
||||
def Timer(text: str, mail: str) -> str:
|
||||
return(f"[{mail} - {datetime.today().strftime('%d-%m-%Y')} - {timedelta(seconds = round(float(time() - START_TIME)))}] " + str(text))
|
||||
|
||||
|
||||
# replace the function print, with more options
|
||||
# [txt] : string, [driver] : selenium wbdriver
|
||||
# [txt] : string, [driver] : selenium webdriver
|
||||
def printf2(txt, mail, LOG = LOG):
|
||||
if LOG:
|
||||
print(Timer(txt, mail))
|
||||
print(Timer(txt, mail))
|
||||
|
||||
|
||||
|
||||
# check if the user is using IPV4 using ipify.org
|
||||
# [driver] : selenium webdriver
|
||||
|
||||
# never used here
|
||||
# can be useful as Ms had issues with IPV6 at some point
|
||||
def check_ipv4(driver):
|
||||
driver.get("https://api64.ipify.org")
|
||||
elm = driver.find_element(By.TAG_NAME, "body")
|
||||
@ -76,12 +27,11 @@ def check_ipv4(driver):
|
||||
|
||||
|
||||
|
||||
def CustomSleep(temps):
|
||||
def custom_sleep(temps):
|
||||
try :
|
||||
if FAST and temps > 50:
|
||||
sleep(temps/10)
|
||||
return()
|
||||
if not LOG or not LINUX_HOST: #only print sleep when user see it
|
||||
elif LOG: #only print sleep when user see it
|
||||
points = ["⢿", "⣻", "⣽", "⣾", "⣷", "⣯", "⣟", "⡿"]
|
||||
passe = 0
|
||||
for i in range(int(temps)):
|
||||
@ -96,7 +46,13 @@ def CustomSleep(temps):
|
||||
print("attente annulée")
|
||||
|
||||
|
||||
|
||||
def format_error(e) -> str:
|
||||
tb = e.__traceback__
|
||||
txt = ""
|
||||
while tb != None :
|
||||
txt = txt + f" -> {tb.tb_frame.f_code.co_name} ({tb.tb_lineno}) "
|
||||
tb = tb.tb_next
|
||||
return(txt + "\n" + str(e))
|
||||
|
||||
|
||||
def progressBar(current, total=30, barLength=20, name="Progress"):
|
||||
@ -104,3 +60,24 @@ def progressBar(current, total=30, barLength=20, name="Progress"):
|
||||
arrow = "-" * int(percent / 100 * barLength - 1) + ">"
|
||||
spaces = " " * (barLength - len(arrow))
|
||||
print(name + ": [%s%s] %d %%" % (arrow, spaces, percent), end="\r")
|
||||
|
||||
|
||||
def save_points_from_file(file):
|
||||
with open(file) as f:
|
||||
reader = csv.reader(f)
|
||||
points_list = list(reader)
|
||||
|
||||
for item in points_list:
|
||||
compte, points = item[0], item[1]
|
||||
add_to_database(compte, points, sql_host,sql_usr,sql_pwd,sql_database, save_if_fail=False)
|
||||
|
||||
with open(file, "w") as f:
|
||||
f.write("")
|
||||
|
||||
|
||||
def select_accounts(multiple = True):
|
||||
system("clear") # clear from previous command to allow a clean choice
|
||||
emails = [x[0] for x in Credentials] # list of all email adresses
|
||||
emails_selected = enquiries.choose(f"quel{'s' if multiple else ''} compte{'s' if multiple else ''} ?", emails, multi=multiple)
|
||||
return([x for x in Credentials if x[0] in emails_selected])
|
||||
|
||||
|
@ -3,4 +3,10 @@ argparse
|
||||
discord.py==1.7.3
|
||||
selenium
|
||||
enquiries
|
||||
rich
|
||||
rich
|
||||
requests
|
||||
pyvirtualdisplay
|
||||
pillow
|
||||
EasyProcess
|
||||
pyotp
|
||||
packaging
|
||||
|
@ -14,7 +14,7 @@ Headless = True
|
||||
DiscordErrorEnabled = True
|
||||
DiscordSuccessEnabled = True
|
||||
successlink = https://discord.com/api/webhooks/[put your webhook here]
|
||||
errorlink =https://discord.com/api/webhooks/[put your webhook here]
|
||||
errorlink = https://discord.com/api/webhooks/[put your webhook here]
|
||||
|
||||
[PROXY]
|
||||
|
||||
@ -32,5 +32,4 @@ pwd = password
|
||||
|
||||
[OTHER]
|
||||
|
||||
ipv6 = False
|
||||
claim_amazon = False
|
||||
ipv6 = False
|
4
user_data/cookies/.gitignore
vendored
Normal file
4
user_data/cookies/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
Reference in New Issue
Block a user