mirror of
https://github.com/User0332/rewards-farmer.git
synced 2026-09-16 01:31:36 +00:00
run in docker, and work through more than one account
Both taken from TheNetsky/Microsoft-Rewards-Script, which packages a container and handles several accounts. Approach only: that project is GPL-3.0 and this one MIT, so no code crosses over. **Accounts.** Rewards is per Microsoft account and the browser profile holds the sign-in, so an account here is a profile directory. REWARDS_ACCOUNTS takes a comma separated list and gives each its own directory under the configured one. They run in sequence, and a profile that will not start is reported and skipped rather than ending the run. Left unset, a run uses the single profile exactly as before. Names are validated rather than trusted: they become directory names, so "../escape" is refused instead of quietly writing outside data-dir. **Docker.** The image carries only what main.py actually reaches, selenium and numpy. pygetwindow, keyboard, matplotlib and pygame are used solely by the recording and visualisation scripts, and two of those are Windows-only, so none of them belong in a container. msedgedriver is pinned at build time to the Edge the image installed rather than to latest, which drifts from it between releases. QUERY_SOURCE defaults to trends in the image, so a container needs no Ollama account and no model download at all. That default turned out to require a fix. queries.py imported llm_utils at module scope, which imports ollama, so a trends-only install still needed the ollama package: exactly what running in a minimal image is good at exposing. The import is now made inside the llm branch, and the wordlist fallback reads nouns.txt directly rather than borrowing llm_utils.get_random_noun. REWARDS_HEADLESS drives the headless flags. The window size is set explicitly because the pointer code works in viewport coordinates and the default headless window is small enough to put cards out of reach, which is the MoveTargetOutOfBoundsException from #19. Verified on the host that move_to_element and human_like_click both work headless before relying on it. Verified in the built image: Edge 151.0.4129.107 with a driver of exactly the same build, the trends feed reachable from inside, Edge driven to bing.com and rewards.bing.com at 1920x1080, and REWARDS_ACCOUNTS producing separate profile directories with traversal refused.
This commit is contained in:
+80
-14
@@ -1,26 +1,92 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
import accounts
|
||||
import rewards_tasks
|
||||
import mouse_trajectory
|
||||
import mimic_typing
|
||||
from selenium import webdriver
|
||||
from constants import USER_DATA_DIR, PROFILE_NAME
|
||||
from selenium.common.exceptions import SessionNotCreatedException
|
||||
|
||||
options = webdriver.EdgeOptions()
|
||||
HEADLESS = os.environ.get("REWARDS_HEADLESS", "").strip().lower() in ("1", "true", "yes")
|
||||
|
||||
options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
||||
options.add_experimental_option('useAutomationExtension', False)
|
||||
options.add_argument("--disable-blink-features=AutomationControlled")
|
||||
options.add_argument(f"--user-data-dir={USER_DATA_DIR}")
|
||||
options.add_argument(f"--profile-directory={PROFILE_NAME}")
|
||||
|
||||
driver = webdriver.Edge(options=options)
|
||||
def build_options(account: accounts.Account) -> webdriver.EdgeOptions:
|
||||
options = webdriver.EdgeOptions()
|
||||
|
||||
mouse = mouse_trajectory.MouseUtils(driver)
|
||||
keyboard = mimic_typing.KeyboardUtils(driver)
|
||||
options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
||||
options.add_experimental_option('useAutomationExtension', False)
|
||||
options.add_argument("--disable-blink-features=AutomationControlled")
|
||||
options.add_argument(f"--user-data-dir={account.user_data_dir}")
|
||||
options.add_argument(f"--profile-directory={account.profile_name}")
|
||||
|
||||
rewards = rewards_tasks.RewardsTaskUtils(driver)
|
||||
if HEADLESS:
|
||||
# A container has no display. The window size is set explicitly because
|
||||
# the pointer code works in viewport coordinates, and the default
|
||||
# headless window is small enough to put cards out of reach.
|
||||
options.add_argument("--headless=new")
|
||||
options.add_argument("--window-size=1920,1080")
|
||||
options.add_argument("--no-sandbox")
|
||||
options.add_argument("--disable-dev-shm-usage")
|
||||
|
||||
rewards.complete_all_tasks()
|
||||
return options
|
||||
|
||||
input("Press Enter to exit...")
|
||||
|
||||
driver.quit()
|
||||
def run_account(account: accounts.Account) -> bool:
|
||||
"""Work one account. Returns whether the browser started."""
|
||||
try:
|
||||
driver = webdriver.Edge(options=build_options(account))
|
||||
except SessionNotCreatedException as exc:
|
||||
# Chromium allows one process per user data directory. When the profile
|
||||
# is already open the driver's copy exits during startup, and selenium
|
||||
# reports it as the browser crashing with a message that names neither
|
||||
# the profile nor the other window.
|
||||
print(f"[FAIL] {account.name}: could not start Edge with this profile.")
|
||||
print(f" profile directory: {account.user_data_dir}")
|
||||
print(" The usual cause is that this profile is already open in another")
|
||||
print(" Edge window, including one left over from a previous run.")
|
||||
print(f" driver said: {str(exc).strip().splitlines()[0]}")
|
||||
|
||||
return False
|
||||
|
||||
try:
|
||||
mouse = mouse_trajectory.MouseUtils(driver)
|
||||
keyboard = mimic_typing.KeyboardUtils(driver)
|
||||
|
||||
rewards = rewards_tasks.RewardsTaskUtils(driver)
|
||||
rewards.complete_all_tasks()
|
||||
finally:
|
||||
driver.quit()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def main() -> int:
|
||||
try:
|
||||
configured = accounts.configured()
|
||||
except ValueError as exc:
|
||||
print(f"[FAIL] {exc}")
|
||||
|
||||
return 2
|
||||
|
||||
started = 0
|
||||
|
||||
for account in configured:
|
||||
if len(configured) > 1:
|
||||
print(f"\n=== account: {account.name} ===")
|
||||
|
||||
if run_account(account):
|
||||
started += 1
|
||||
|
||||
if len(configured) > 1:
|
||||
print(f"\n{started}/{len(configured)} accounts ran")
|
||||
|
||||
# Nothing is watching a container, and stdin is not a terminal there.
|
||||
if not HEADLESS:
|
||||
input("Press Enter to exit...")
|
||||
|
||||
return 0 if started else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
||||
Reference in New Issue
Block a user