-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
99 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,11 @@ | ||
NAME:=nexus_autodl | ||
|
||
ifeq ($(OS),Windows_NT) | ||
PATHSEP:=; | ||
else | ||
PATHSEP:=: | ||
endif | ||
|
||
all: yapf lint mypy build | ||
all: build | ||
|
||
build: $(NAME).py | ||
pyinstaller --clean -F --add-data 'templates$(PATHSEP)templates' $< | ||
pyinstaller --clean -F $< | ||
|
||
clean: | ||
$(RM) -r build dist *.spec | ||
|
||
lint: $(NAME).py | ||
pylint --max-line-length 120 $< | ||
|
||
mypy: $(NAME).py | ||
mypy $< | ||
|
||
yapf: $(NAME).py | ||
yapf -i --style style.yapf $< | ||
|
||
.PHONY: build clean lint mypy yapf | ||
.PHONY: build clean |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,99 +1,78 @@ | ||
#!/usr/bin/env python | ||
|
||
# pylint: disable=missing-module-docstring | ||
|
||
from typing import List, NamedTuple | ||
import os | ||
import logging | ||
import random | ||
import re | ||
import sys | ||
import time | ||
from pathlib import Path | ||
|
||
from numpy import ndarray as NDArray | ||
import click | ||
import cv2 as cv # type: ignore | ||
import numpy as np | ||
import PIL # type: ignore | ||
import PIL.ImageOps # type: ignore | ||
import pyautogui # type: ignore | ||
import pyautogui | ||
from PIL import UnidentifiedImageError | ||
from PIL.Image import Image, open as open_image | ||
from pyautogui import ImageNotFoundException | ||
from pyscreeze import Box | ||
|
||
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(message)s") | ||
|
||
|
||
@click.command() | ||
@click.option('--sleep_max', default=5.) | ||
@click.option('--sleep_min', default=0.) | ||
def run(sleep_max: float, sleep_min: float) -> None: # pylint: disable=missing-function-docstring | ||
logging.basicConfig( | ||
datefmt='%m/%d/%Y %I:%M:%S %p', | ||
format='%(asctime)s [%(levelname)s] %(message)s', | ||
level=logging.INFO, | ||
) | ||
templates = _get_templates() | ||
while True: | ||
sleep_seconds = random.uniform(sleep_min, sleep_max) | ||
logging.info('Sleeping for %f seconds', sleep_seconds) | ||
time.sleep(sleep_seconds) | ||
@click.option("--confidence", default=0.7, show_default=True) | ||
@click.option("--grayscale/--color", default=True, show_default=True) | ||
@click.option("--min-sleep-interval", default=1, show_default=True) | ||
@click.option("--max-sleep-interval", default=5, show_default=True) | ||
@click.option("--templates-path", default=Path.cwd() / "templates", show_default=True) | ||
def main( | ||
confidence: float, | ||
grayscale: bool, | ||
min_sleep_interval: int, | ||
max_sleep_interval: int, | ||
templates_path: str, | ||
) -> None: | ||
templates_path_ = Path(templates_path) | ||
templates: dict[Path, Image] = {} | ||
for template_path in templates_path_.rglob("*"): | ||
try: | ||
_find_and_click(templates) | ||
except cv.error: # pylint: disable=no-member | ||
logging.info('Ignoring OpenCV error') | ||
|
||
templates[template_path] = open_image(template_path) | ||
except UnidentifiedImageError: | ||
logging.info(f"{template_path} is not a valid image; skipping") | ||
|
||
class _Template(NamedTuple): | ||
array: NDArray | ||
name: str | ||
threshold: int | ||
if len(templates) == 0: | ||
logging.error( | ||
f"No images found in {templates_path_.absolute()}. " | ||
f"If this is your first time running, take a screenshot and crop " | ||
f"(WIN+S on Windows) the item on the screen you want to click on, " | ||
f"placing the result in the {templates_path_.absolute()} directory." | ||
) | ||
input("Press ENTER to exit.") | ||
sys.exit(1) | ||
|
||
while True: | ||
screenshot = pyautogui.screenshot() | ||
|
||
def _find_and_click(templates: List[_Template]) -> None: | ||
screenshot_image = pyautogui.screenshot() | ||
screenshot = _image_to_grayscale_array(screenshot_image) | ||
for template in templates: | ||
sift = cv.SIFT_create() # pylint: disable=no-member | ||
_, template_descriptors = sift.detectAndCompute(template.array, mask=None) | ||
screenshot_keypoints, screenshot_descriptors = sift.detectAndCompute(screenshot, mask=None) | ||
matcher = cv.BFMatcher() # pylint: disable=no-member | ||
matches = matcher.knnMatch(template_descriptors, screenshot_descriptors, k=2) | ||
points = np.array([screenshot_keypoints[m.trainIdx].pt for m, _ in matches if m.distance < template.threshold]) | ||
if points.shape[0] == 0: | ||
continue | ||
point = np.median(points, axis=0) | ||
current_mouse_pos = pyautogui.position() | ||
logging.info('Saving current mouse position at x=%f y=%f', *current_mouse_pos) | ||
pyautogui.click(*point) | ||
logging.info('Clicking on %s at coordinates x=%f y=%f', template.name, *point) | ||
pyautogui.moveTo(*current_mouse_pos) | ||
return | ||
logging.info('No matches found') | ||
|
||
|
||
def _get_templates() -> List[_Template]: # pylint: disable=too-many-locals | ||
templates = [] | ||
try: | ||
root_dir = sys._MEIPASS # type: ignore # pylint: disable=no-member,protected-access | ||
except AttributeError: | ||
root_dir = '.' | ||
templates_dir = os.path.join(root_dir, 'templates') | ||
pattern = re.compile(r'^([1-9][0-9]*)_([1-9][0-9]*)_(.+)\.png$') | ||
basenames = os.listdir(templates_dir) | ||
matches = (pattern.match(basename) for basename in basenames) | ||
filtered_matches = (match for match in matches if match is not None) | ||
groups = (match.groups() for match in filtered_matches) | ||
sorted_groups = sorted(groups, key=lambda t: int(t[0])) | ||
for index, threshold, name in sorted_groups: | ||
path = os.path.join(templates_dir, f'{index}_{threshold}_{name}.png') | ||
image = PIL.Image.open(path) # pylint: disable=no-member | ||
array = _image_to_grayscale_array(image) | ||
template = _Template(array=array, name=name, threshold=int(threshold)) | ||
templates.append(template) | ||
return templates | ||
|
||
for template_path, template_image in templates.items(): | ||
logging.info(f"Attempting to match {template_path}.") | ||
box: Box | None = None | ||
try: | ||
box = pyautogui.locate( | ||
template_image, | ||
screenshot, | ||
grayscale=grayscale, | ||
confidence=confidence, | ||
) | ||
except ImageNotFoundException: | ||
pass | ||
if not isinstance(box, Box): | ||
continue | ||
match_x, match_y = pyautogui.center(box) | ||
pyautogui.click(match_x, match_y) | ||
logging.info(f"Matched at ({match_x}, {match_y}).") | ||
break | ||
|
||
def _image_to_grayscale_array(image: PIL.Image.Image) -> NDArray: | ||
image = PIL.ImageOps.grayscale(image) | ||
array = np.array(image) | ||
return array | ||
sleep_interval = random.uniform(min_sleep_interval, max_sleep_interval) | ||
logging.info(f"Waiting for {sleep_interval:.2f} seconds.") | ||
time.sleep(sleep_interval) | ||
|
||
|
||
if __name__ == '__main__': | ||
run() # pylint: disable=no-value-for-parameter | ||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"venvPath": ".", | ||
"venv": "venv" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
pyautogui | ||
click | ||
pillow | ||
opencv-python | ||
|
This file was deleted.
Oops, something went wrong.
Binary file not shown.
Binary file not shown.
Binary file not shown.