diff --git a/.gitignore b/.gitignore index 7302770d..03073b38 100644 --- a/.gitignore +++ b/.gitignore @@ -187,3 +187,4 @@ runbot.bat /google_trends.dir /google_trends.bak /config-private.yaml +mypy.ini diff --git a/activities.py b/activities.py new file mode 100644 index 00000000..bebb0a8c --- /dev/null +++ b/activities.py @@ -0,0 +1,141 @@ +import contextlib +import random +import time + +from selenium.common import TimeoutException +from selenium.webdriver.common.by import By +from selenium.webdriver.remote.webelement import WebElement + +from src.browser import Browser + + +class Activities: + def __init__(self, browser: Browser): + self.browser = browser + self.webdriver = browser.webdriver + + def openDailySetActivity(self, cardId: int): + # Open the Daily Set activity for the given cardId + element = self.webdriver.find_element(By.XPATH, + f'//*[@id="daily-sets"]/mee-card-group[1]/div/mee-card[{cardId}]/div/card-content/mee-rewards-daily-set-item-content/div/a', ) + self.browser.utils.click(element) + self.browser.utils.switchToNewTab(timeToWait=8) + + def openMorePromotionsActivity(self, cardId: int): + # Open the More Promotions activity for the given cardId + element = self.webdriver.find_element(By.CSS_SELECTOR, + f"#more-activities > .m-card-group > .ng-scope:nth-child({cardId + 1}) .ds-card-sec") + self.browser.utils.click(element) + self.browser.utils.switchToNewTab(timeToWait=5) + + def completeSearch(self): + # Simulate completing a search activity + time.sleep(random.randint(10, 15)) + self.browser.utils.closeCurrentTab() + + def completeSurvey(self): + # Simulate completing a survey activity + # noinspection SpellCheckingInspection + self.webdriver.find_element(By.ID, f"btoption{random.randint(0, 1)}").click() + time.sleep(random.randint(10, 15)) + self.browser.utils.closeCurrentTab() + + def completeQuiz(self): + # Simulate completing a quiz activity + with contextlib.suppress(TimeoutException): + startQuiz = self.browser.utils.waitUntilQuizLoads() + self.browser.utils.click(startQuiz) + self.browser.utils.waitUntilVisible( + By.ID, "overlayPanel", 5 + ) + currentQuestionNumber: int = self.webdriver.execute_script( + "return _w.rewardsQuizRenderInfo.currentQuestionNumber" + ) + maxQuestions = self.webdriver.execute_script( + "return _w.rewardsQuizRenderInfo.maxQuestions" + ) + numberOfOptions = self.webdriver.execute_script( + "return _w.rewardsQuizRenderInfo.numberOfOptions" + ) + for _ in range(currentQuestionNumber, maxQuestions + 1): + if numberOfOptions == 8: + answers = [] + for i in range(numberOfOptions): + isCorrectOption = self.webdriver.find_element( + By.ID, f"rqAnswerOption{i}" + ).get_attribute("iscorrectoption") + if isCorrectOption and isCorrectOption.lower() == "true": + answers.append(f"rqAnswerOption{i}") + for answer in answers: + element = self.webdriver.find_element(By.ID, answer) + self.browser.utils.click(element) + self.browser.utils.waitUntilQuestionRefresh() + elif numberOfOptions in [2, 3, 4]: + correctOption = self.webdriver.execute_script( + "return _w.rewardsQuizRenderInfo.correctAnswer" + ) + for i in range(numberOfOptions): + if ( + self.webdriver.find_element( + By.ID, f"rqAnswerOption{i}" + ).get_attribute("data-option") + == correctOption + ): + element = self.webdriver.find_element(By.ID, f"rqAnswerOption{i}") + self.browser.utils.click(element) + + self.browser.utils.waitUntilQuestionRefresh() + break + self.browser.utils.closeCurrentTab() + + def completeABC(self): + # Simulate completing an ABC activity + counter = self.webdriver.find_element( + By.XPATH, '//*[@id="QuestionPane0"]/div[2]' + ).text[:-1][1:] + numberOfQuestions = max(int(s) for s in counter.split() if s.isdigit()) + for question in range(numberOfQuestions): + element = self.webdriver.find_element(By.ID, f"questionOptionChoice{question}{random.randint(0, 2)}") + self.browser.utils.click(element) + time.sleep(random.randint(10, 15)) + element = self.webdriver.find_element(By.ID, f"nextQuestionbtn{question}") + self.browser.utils.click(element) + time.sleep(random.randint(10, 15)) + time.sleep(random.randint(1, 7)) + self.browser.utils.closeCurrentTab() + + def completeThisOrThat(self): + # Simulate completing a This or That activity + startQuiz = self.browser.utils.waitUntilQuizLoads() + self.browser.utils.click(startQuiz) + self.browser.utils.waitUntilVisible( + By.XPATH, '//*[@id="currentQuestionContainer"]/div/div[1]', 10 + ) + time.sleep(random.randint(10, 15)) + for _ in range(10): + correctAnswerCode = self.webdriver.execute_script( + "return _w.rewardsQuizRenderInfo.correctAnswer" + ) + answer1, answer1Code = self.getAnswerAndCode("rqAnswerOption0") + answer2, answer2Code = self.getAnswerAndCode("rqAnswerOption1") + answerToClick: WebElement + if answer1Code == correctAnswerCode: + answerToClick = answer1 + elif answer2Code == correctAnswerCode: + answerToClick = answer2 + + self.browser.utils.click(answerToClick) + time.sleep(random.randint(10, 15)) + + time.sleep(random.randint(10, 15)) + self.browser.utils.closeCurrentTab() + + def getAnswerAndCode(self, answerId: str) -> tuple[WebElement, str]: + # Helper function to get answer element and its code + answerEncodeKey = self.webdriver.execute_script("return _G.IG") + answer = self.webdriver.find_element(By.ID, answerId) + answerTitle = answer.get_attribute("data-option") + return ( + answer, + self.browser.utils.getAnswerCode(answerEncodeKey, answerTitle), + ) diff --git a/config.yaml b/config.yaml index b835582b..9bc98b96 100644 --- a/config.yaml +++ b/config.yaml @@ -11,6 +11,7 @@ apprise: summary: ON_ERROR default: geolocation: US # Replace with your country code https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 + language: en # Replace with your language code https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes logging: level: INFO # See https://docs.python.org/3/library/logging.html#logging-levels retries: diff --git a/src/browser.py b/src/browser.py index cc332b21..0ee94e3c 100644 --- a/src/browser.py +++ b/src/browser.py @@ -19,7 +19,7 @@ from src import Account, RemainingSearches from src.userAgentGenerator import GenerateUserAgent -from src.utils import Utils, CONFIG, saveBrowserConfig, getProjectRoot, getBrowserConfig +from src.utils import CONFIG, Utils, getBrowserConfig, getProjectRoot, saveBrowserConfig class Browser: @@ -102,7 +102,7 @@ def browserSetup( options.add_argument("--disable-features=PrivacySandboxSettings4") options.add_argument("--disable-http2") options.add_argument("--disable-search-engine-choice-screen") # 153 - options.page_load_strategy = 'eager' + options.page_load_strategy = "eager" seleniumwireOptions: dict[str, Any] = {"verify_ssl": False} @@ -223,19 +223,22 @@ def setupProfiles(self) -> Path: @staticmethod def getLanguageCountry(language: str, country: str) -> tuple[str, str]: if not country: - country = CONFIG.get("default").get("location") + country = CONFIG.get("default").get("geolocation") + + if not language: + country = CONFIG.get("default").get("language") if not language or not country: currentLocale = locale.getlocale() if not language: with contextlib.suppress(ValueError): language = pycountry.languages.get( - name=currentLocale[0].split("_")[0] + alpha_2=currentLocale[0].split("_")[0] ).alpha_2 if not country: with contextlib.suppress(ValueError): country = pycountry.countries.get( - name=currentLocale[0].split("_")[1] + alpha_2=currentLocale[0].split("_")[1] ).alpha_2 if not language or not country: diff --git a/src/userAgentGenerator.py b/src/userAgentGenerator.py index 0f8a44a2..1696657a 100644 --- a/src/userAgentGenerator.py +++ b/src/userAgentGenerator.py @@ -139,29 +139,45 @@ def getEdgeVersions(self) -> tuple[str, str]: response = self.getWebdriverPage( "https://edgeupdates.microsoft.com/api/products" ) + + def get_value_ignore_case(data: dict, key: str) -> Any: + """Get the value from a dictionary ignoring the case of the first letter of the key.""" + for k, v in data.items(): + if k.lower() == key.lower(): + return v + return None + data = response.json() if stableProduct := next( - (product for product in data if product["product"] == "Stable"), + ( + product + for product in data + if get_value_ignore_case(product, "product") == "Stable" + ), None, ): - releases = stableProduct["releases"] + releases = get_value_ignore_case(stableProduct, "releases") androidRelease = next( - (release for release in releases if release["platform"] == "Android"), + ( + release + for release in releases + if get_value_ignore_case(release, "platform") == "Android" + ), None, ) windowsRelease = next( ( release for release in releases - if release["platform"] == "Windows" - and release["architecture"] == "x64" + if get_value_ignore_case(release, "platform") == "Windows" + and get_value_ignore_case(release, "architecture") == "x64" ), None, ) if androidRelease and windowsRelease: return ( - windowsRelease["productVersion"], - androidRelease["productVersion"], + get_value_ignore_case(windowsRelease, "productVersion"), + get_value_ignore_case(androidRelease, "productVersion"), ) raise HTTPError("Failed to get Edge versions.") diff --git a/src/utils.py b/src/utils.py index 87eceac7..6b8781a1 100644 --- a/src/utils.py +++ b/src/utils.py @@ -16,10 +16,10 @@ from requests import Session from requests.adapters import HTTPAdapter from selenium.common import ( - NoSuchElementException, - TimeoutException, ElementClickInterceptedException, ElementNotInteractableException, + NoSuchElementException, + TimeoutException, ) from selenium.webdriver.chrome.webdriver import WebDriver from selenium.webdriver.common.by import By @@ -28,8 +28,7 @@ from selenium.webdriver.support.wait import WebDriverWait from urllib3 import Retry -from .constants import REWARDS_URL -from .constants import SEARCH_URL +from .constants import REWARDS_URL, SEARCH_URL DEFAULT_CONFIG: MappingProxyType = MappingProxyType( { @@ -271,7 +270,7 @@ def sendNotification(title: str, body: str, e: Exception = None) -> None: return for url in urls: apprise.add(url) - assert apprise.notify(title=str(title), body=str(body)) + # assert apprise.notify(title=str(title), body=str(body)) # not work for telegram def getAnswerCode(key: str, string: str) -> str: