diff --git a/alas.py b/alas.py index 29f53c104..4429055fb 100644 --- a/alas.py +++ b/alas.py @@ -606,13 +606,15 @@ class AzurLaneAutoScript: def loop(self): gl.gl_set("g_config", self.config) - GGH=GGHandler(config=self.config, device=self.device) + if self.__class__.__name__ == "AzurLaneAutoScript": + GGH=GGHandler(config=self.config, device=self.device) self.gg_check() logger.set_file_logger(self.config_name) logger.info(f'Start scheduler loop: {self.config_name}') # Try forced task_call restart to reset GG status self.checker.wait_until_available() - GGH.handle_restart_before_tasks() + if self.__class__.__name__ == "AzurLaneAutoScript": + GGH.handle_restart_before_tasks() check_fail = 0 while 1: # Check update event from GUI @@ -651,9 +653,11 @@ class AzurLaneAutoScript: continue # Check GG config before a task begins (to reset temporary config), and decide to enable it. - GGH.check_config() + if self.__class__.__name__ == "AzurLaneAutoScript": + GGH.check_config() try: - GGH.check_then_set_gg_status(inflection.underscore(task)) + if self.__class__.__name__ == "AzurLaneAutoScript": + GGH.check_then_set_gg_status(inflection.underscore(task)) check_fail = 0 except GameStuckError: del_cached_property(self, 'config') diff --git a/deploy/logger.py b/deploy/logger.py new file mode 100644 index 000000000..f5d764cdc --- /dev/null +++ b/deploy/logger.py @@ -0,0 +1,41 @@ +import logging +import os +import sys + +os.chdir(os.path.join(os.path.dirname(__file__), '../')) + +logger = logging.getLogger("deploy") +_logger = logger + +formatter = logging.Formatter(fmt="%(message)s") +hdlr = logging.StreamHandler(stream=sys.stdout) +hdlr.setFormatter(formatter) +logger.addHandler(hdlr) +logger.setLevel(logging.INFO) + + +def hr(title, level=3): + if logger is not _logger: + return logger.hr(title, level) + + title = str(title).upper() + if level == 0: + middle = "|" + " " * 20 + title + " " * 20 + "|" + border = "+" + "-" * (len(middle) - 2) + "+" + logger.info(border) + logger.info(middle) + logger.info(border) + if level == 1: + logger.info("=" * 20 + " " + title + " " + "=" * 20) + if level == 2: + logger.info("-" * 20 + " " + title + " " + "-" * 20) + if level == 3: + logger.info(f"<<< {title} >>>") + + +def attr(name, text): + print(f'[{name}] {text}') + + +logger.hr = hr +logger.attr = attr diff --git a/deploy/template b/deploy/template new file mode 100644 index 000000000..1f6355c99 --- /dev/null +++ b/deploy/template @@ -0,0 +1,159 @@ +Deploy: + Git: + # URL of AzurLaneAutoScript repository + # [CN user] Use 'git://git.lyoko.io/AzurLaneAutoScript' for faster and more stable download + # [Other] Use 'https://github.com/LmeSzinc/AzurLaneAutoScript' + Repository: 'https://github.com/LmeSzinc/AzurLaneAutoScript' + # Branch of Alas + # [Developer] Use 'dev', 'app', etc, to try new features + # [Other] Use 'master', the stable branch + Branch: 'master' + # Filepath of git executable `git.exe` + # [Easy installer] Use './toolkit/Git/mingw64/bin/git.exe' + # [Other] Use you own git + GitExecutable: './toolkit/Git/mingw64/bin/git.exe' + # Set git proxy + # [CN user] Use your local http proxy (http://127.0.0.1:{port}) or socks5 proxy (socks5://127.0.0.1:{port}) + # [Other] Use null + GitProxy: null + # Set SSL Verify + # [In most cases] Use true + # [Other] Use false to when connected to an untrusted network + SSLVerify: true + # Update Alas at startup + # [In most cases] Use true + AutoUpdate: true + # Whether to keep local changes during update + # User settings, logs and screenshots will be kept, no mather this is true or false + # [Developer] Use true, if you modified the code + # [Other] Use false + KeepLocalChanges: false + + Python: + # Filepath of python executable `python.exe` + # [Easy installer] Use './toolkit/python.exe' + # [Other] Use you own python, and its version should be 3.7.6 64bit + PythonExecutable: './toolkit/python.exe' + # URL of pypi mirror + # [CN user] Use 'https://pypi.tuna.tsinghua.edu.cn/simple' for faster and more stable download + # [Other] Use null + PypiMirror: null + # Install dependencies at startup + # [In most cases] Use true + InstallDependencies: true + # Path to requirements.txt + # [In most cases] Use 'requirements.txt' + # [In AidLux] Use './deploy/AidLux/{version}/requirements.txt', version is default to 0.92 + RequirementsFile: 'requirements.txt' + + Adb: + # Filepath of ADB executable `adb.exe` + # [Easy installer] Use './toolkit/Lib/site-packages/adbutils/binaries/adb.exe' + # [Other] Use you own latest ADB, but not the ADB in your emulator + AdbExecutable: './toolkit/Lib/site-packages/adbutils/binaries/adb.exe' + # Whether to replace ADB + # Chinese emulators (NoxPlayer, LDPlayer, MemuPlayer, MuMuPlayer) use their own ADB, instead of the latest. + # Different ADB servers will terminate each other at startup, resulting in disconnection. + # For compatibility, we have to replace them all. + # This will do: + # 1. Terminate current ADB server + # 2. Rename ADB from all emulators to *.bak and replace them by the AdbExecutable set above + # 3. Brute-force connect to all available emulator instances + # [In most cases] Use true + # [In few cases] Use false, if you have other programs using ADB. + ReplaceAdb: true + # Brute-force connect to all available emulator instances + # [In most cases] Use true + AutoConnect: true + # Re-install uiautomator2 + # [In most cases] Use true + InstallUiautomator2: true + + Ocr: + # Run Ocr as a service, can reduce memory usage by not import mxnet everytime you start an alas instance + + # Whether to use ocr server + # [Default] false + UseOcrServer: false + # Whether to start ocr server when start GUI + # [Default] false + StartOcrServer: false + # Port of ocr server runs by GUI + # [Default] 22268 + OcrServerPort: 22268 + # Address of ocr server for alas instance to connect + # [Default] 127.0.0.1:22268 + OcrClientAddress: 127.0.0.1:22268 + + Update: + # Use auto update and builtin updater feature + # This may cause problem https://github.com/LmeSzinc/AzurLaneAutoScript/issues/876 + EnableReload: true + # Check update every X minute + # [Disable] 0 + # [Default] 5 + CheckUpdateInterval: 5 + # Scheduled restart time + # If there are updates, Alas will automatically restart and update at this time every day + # and run all alas instances that running before restarted + # [Disable] null + # [Default] 03:50 + AutoRestartTime: 03:50 + + Misc: + # Enable discord rich presence + DiscordRichPresence: false + + RemoteAccess: + # Enable remote access (using ssh reverse tunnel serve by https://github.com/wang0618/localshare) + # ! You need to set Password below to enable remote access since everyone can access to your alas if they have your url. + # See here (http://app.azurlane.cloud/en.html) for more infomation. + EnableRemoteAccess: false + # Username when login into ssh server + # [Default] null (will generate a random one when startup) + SSHUser: null + # Server to connect + # [Default] null + # [Format] host:port + SSHServer: null + # Filepath of SSH executable `ssh.exe` + # [Default] ssh (find ssh in system PATH) + # If you don't have one, install OpenSSH or download it here (https://github.com/PowerShell/Win32-OpenSSH/releases) + SSHExecutable: ssh + + Webui: + # --host. Host to listen + # [Use IPv6] '::' + # [In most cases] Default to '0.0.0.0' + WebuiHost: 0.0.0.0 + # --port. Port to listen + # You will be able to access webui via `http://{host}:{port}` + # [In most cases] Default to 22267 + WebuiPort: 22267 + # Language to use on web ui + # 'zh-CN' for Chinese simplified + # 'en-US' for English + # 'ja-JP' for Japanese + # 'zh-TW' for Chinese traditional + Language: en-US + # Theme of web ui + # 'default' for light theme + # 'dark' for dark theme + Theme: default + # Follow system DPI scaling + # [In most cases] true + # [In few cases] false to make Alas smaller, if you have a low resolution but high DPI scaling. + DpiScaling: true + # --key. Password of web ui + # Useful when expose Alas to the public network + Password: null + # --cdn. Use jsdelivr cdn for pywebio static files (css, js). + # 'true' for jsdelivr cdn + # 'false' for self host cdn (automatically) + # 'https://path.to.your/cdn' to use custom cdn + CDN: false + # --run. Auto-run specified config when startup + # 'null' default no specified config + # '["alas"]' specified "alas" config + # '["alas","alas2"]' specified "alas" "alas2" configs + Run: null diff --git a/deploy/utils.py b/deploy/utils.py new file mode 100644 index 000000000..f7741b9f2 --- /dev/null +++ b/deploy/utils.py @@ -0,0 +1,113 @@ +import os +import re +from typing import Callable, Generic, TypeVar + +T = TypeVar("T") + +DEPLOY_CONFIG = './config/deploy.yaml' +DEPLOY_TEMPLATE = './deploy/template' + + +class cached_property(Generic[T]): + """ + cached-property from https://github.com/pydanny/cached-property + Add typing support + + A property that is only computed once per instance and then replaces itself + with an ordinary attribute. Deleting the attribute resets the property. + Source: https://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76 + """ + + def __init__(self, func: Callable[..., T]): + self.func = func + + def __get__(self, obj, cls) -> T: + if obj is None: + return self + + value = obj.__dict__[self.func.__name__] = self.func(obj) + return value + + +def iter_folder(folder, is_dir=False, ext=None): + """ + Args: + folder (str): + is_dir (bool): True to iter directories only + ext (str): File extension, such as `.yaml` + + Yields: + str: Absolute path of files + """ + for file in os.listdir(folder): + sub = os.path.join(folder, file) + if is_dir: + if os.path.isdir(sub): + yield sub.replace('\\\\', '/').replace('\\', '/') + elif ext is not None: + if not os.path.isdir(sub): + _, extension = os.path.splitext(file) + if extension == ext: + yield os.path.join(folder, file).replace('\\\\', '/').replace('\\', '/') + else: + yield os.path.join(folder, file).replace('\\\\', '/').replace('\\', '/') + + +def poor_yaml_read(file): + """ + Poor implementation to load yaml without pyyaml dependency, but with re + + Args: + file (str): + + Returns: + dict: + """ + if not os.path.exists(file): + return {} + + data = {} + regex = re.compile(r'^(.*?):(.*?)$') + with open(file, 'r', encoding='utf-8') as f: + for line in f.readlines(): + line = line.strip('\n\r\t ').replace('\\', '/') + if line.startswith('#'): + continue + result = re.match(regex, line) + if result: + k, v = result.group(1), result.group(2).strip('\n\r\t\' ') + if v: + if v.lower() == 'null': + v = None + elif v.lower() == 'false': + v = False + elif v.lower() == 'true': + v = True + elif v.isdigit(): + v = int(v) + data[k] = v + + return data + + +def poor_yaml_write(data, file, template_file=DEPLOY_TEMPLATE): + """ + Args: + data (dict): + file (str): + template_file (str): + """ + with open(template_file, 'r', encoding='utf-8') as f: + text = f.read().replace('\\', '/') + + for key, value in data.items(): + if value is None: + value = 'null' + elif value is True: + value = "true" + elif value is False: + value = "false" + text = re.sub(f'{key}:.*?\n', f'{key}: {value}\n', text) + + with open(file, 'w', encoding='utf-8', newline='') as f: + f.write(text) diff --git a/submodule/AlasMaaBridge/module/handler/handler.py b/submodule/AlasMaaBridge/module/handler/handler.py index 6c502be5c..712d49d5e 100644 --- a/submodule/AlasMaaBridge/module/handler/handler.py +++ b/submodule/AlasMaaBridge/module/handler/handler.py @@ -2,13 +2,15 @@ import os import re import json import time + +import adbutils import requests import datetime from typing import Any from cached_property import cached_property -from deploy.Windows.config import DeployConfig +from deploy.config import DeployConfig from module.base.timer import Timer from module.config.utils import read_file, deep_get, get_server_last_update from module.device.connection_attr import ConnectionAttr @@ -193,6 +195,8 @@ class AssistantHandler: def connect(self): adb = os.path.abspath(DeployConfig().AdbExecutable) + if not os.path.exists(adb): + adb = adbutils.adb_path() self.serial = self.config.MaaEmulator_Serial self.serial_check()