|
import os |
|
import sys |
|
import time |
|
import signal |
|
from dataclasses import dataclass |
|
|
|
import pyautogui |
|
import keyboard |
|
|
|
|
|
# -------------------- CONFIG -------------------- |
|
# To use take a snippet of the download button and insert the path into the image_path of the config. |
|
# |
|
@dataclass |
|
class Config: |
|
image_path: str = r"absolute/path/to/file" |
|
confidence: float = 0.8 |
|
retry_attempts: int = 3 |
|
retry_delay: float = 0.5 |
|
loop_delay: float = 7.0 |
|
exit_hotkey: str = "ctrl+shift+e" |
|
|
|
|
|
CONFIG = Config() |
|
|
|
|
|
# -------------------- UTILITIES -------------------- |
|
|
|
def validate_image_path(path: str) -> None: |
|
"""Ensure the image file exists.""" |
|
if not os.path.isfile(path): |
|
print(f"[ERROR] Image file not found:\n{path}") |
|
sys.exit(1) |
|
|
|
|
|
def click_on_image(config: Config) -> bool: |
|
""" |
|
Search for an image on screen and click it if found. |
|
Returns True if clicked, False otherwise. |
|
""" |
|
for attempt in range(1, config.retry_attempts + 1): |
|
try: |
|
location = pyautogui.locateOnScreen( |
|
config.image_path, |
|
confidence=config.confidence |
|
) |
|
|
|
if location: |
|
center = pyautogui.center(location) |
|
original_position = pyautogui.position() |
|
|
|
print(f"[✓] Found button at {center} (attempt {attempt})") |
|
pyautogui.click(center) |
|
pyautogui.moveTo(original_position) |
|
|
|
return True |
|
|
|
print(f"[{attempt}/{config.retry_attempts}] Not found. Retrying...") |
|
time.sleep(config.retry_delay) |
|
|
|
except Exception as e: |
|
print(f"[ERROR] Screen search failed: {e}") |
|
time.sleep(config.retry_delay) |
|
|
|
print("[!] Button not found after retries.") |
|
return False |
|
|
|
|
|
# -------------------- MAIN LOOP -------------------- |
|
|
|
running = True |
|
|
|
|
|
def stop_program(): |
|
global running |
|
print("\n[INFO] Exit triggered. Shutting down...") |
|
running = False |
|
|
|
|
|
def main(): |
|
validate_image_path(CONFIG.image_path) |
|
|
|
print("Move this console window so it doesn't cover the button.") |
|
input("Press Enter to start...\n") |
|
|
|
keyboard.add_hotkey(CONFIG.exit_hotkey, stop_program) |
|
print(f"[INFO] Press {CONFIG.exit_hotkey.upper()} to stop.\n") |
|
|
|
while running: |
|
click_on_image(CONFIG) |
|
time.sleep(CONFIG.loop_delay) |
|
|
|
|
|
# -------------------- ENTRY POINT -------------------- |
|
|
|
if __name__ == "__main__": |
|
# Allow CTRL+C to exit cleanly |
|
signal.signal(signal.SIGINT, lambda s, f: stop_program()) |
|
main() |