from argparse import ArgumentParser from pathlib import Path from platform import system from shutil import rmtree, copytree, copyfileobj from tempfile import TemporaryDirectory, NamedTemporaryFile from zipfile import ZipFile from urllib.request import Request, urlopen from urllib.parse import quote import logging import re HEADERS = { "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:48.0) Gecko/20100101 Firefox/48.0" } def live_to_esoui(*, path: Path, esoui_uris: list): live_name, live_version, live_path = live_parse(path) if not live_path: return esoui_name, esoui_version, esoui_uri = None, None, None for _name, _version, _uri in esoui_uris: if _name in live_name: esoui_name, esoui_version, esoui_uri = _name, _version, _uri break if live_name in _name: esoui_name, esoui_version, esoui_uri = _name, _version, _uri break if not esoui_name: rmtree(live_path) logging.info(f"{live_name} addon removed from: {live_path}") return if esoui_version == live_version: logging.info(f"{live_name} is already up to date.") return request = Request(esoui_uri, headers=HEADERS) response = urlopen(request) temp_zip = NamedTemporaryFile() copyfileobj(response, temp_zip) temp_dir = TemporaryDirectory() temp_path = Path(temp_dir.name) zip_file = ZipFile(temp_zip) zip_file.extractall(temp_path) rmtree(live_path) for each in temp_path.iterdir(): copytree(each, live_path) logging.info( f"{live_name} updated from {live_version} to {esoui_version} at {live_path}" ) def esoui_to_live(*, esoui_uris: list, live_path: Path): for addon_name, addon_version, esoui_dowload_uri in esoui_uris: match = None for each in live_path.iterdir(): if addon_name in each.name: match = each break if each.name in addon_name: match = each break if match: logging.debug(f"{addon_name} already installed.") continue request = Request(esoui_dowload_uri, headers=HEADERS) response = urlopen(request) temp_zip = NamedTemporaryFile() copyfileobj(response, temp_zip) temp_dir = TemporaryDirectory() temp_path = Path(temp_dir.name) zip_file = ZipFile(temp_zip) zip_file.extractall(temp_path) for each in temp_path.iterdir(): live_dest = live_path.joinpath(each.name) if live_dest.exists(): continue copytree(each, live_dest) logging.info(f"{addon_name} installed {addon_version} at {live_dest}") esoui_prefix = re.compile("https://www.esoui.com/downloads/info[0-9]+\-") esoui_version_html = re.compile('Version:\s+[^<]+') esoui_version_split = re.compile('Version:\s+') esoui_download = re.compile('https://cdn.esoui.com/downloads/file[^"]*') live_version = re.compile("##\s+Version:\s+.*") live_version_split = re.compile("##\s+Version:\s+") def esoui_parse(url: str): addon_name = esoui_prefix.split(url)[1] addon_name = addon_name.split(".html")[0] request = Request(url, headers=HEADERS) response = urlopen(request) response_data = response.read() # writworthy has some garbage characters on it's page response_text = response_data[:110000].decode("unicode_escape") version_line = esoui_version_html.search(response_text).group(0) version = esoui_version_split.split(version_line)[1] esoui_page_url = url.replace("info", "download").replace(".html", "") request = Request(esoui_page_url, headers=HEADERS) response = urlopen(request) response_text = response.read().decode("unicode_escape") esoui_dowload_uri = esoui_download.search(response_text).group(0) esoui_dowload_uri = esoui_dowload_uri.split("?")[0] esoui_dowload_uri = esoui_dowload_uri.split("https://")[1] esoui_dowload_uri = quote(esoui_dowload_uri) esoui_dowload_uri = f"https://{esoui_dowload_uri}" head_request = Request(esoui_dowload_uri, method="HEAD", headers=HEADERS) response = urlopen(head_request) response_text = response.read().decode("unicode_escape") return addon_name, version, esoui_dowload_uri def live_parse(path: Path): if not path.is_dir(): logging.error(f"unexpected file object {path}, ignoring") return meta_file = path.joinpath(f"{path.stem}.txt") if not meta_file.exists(): for meta_file in path.glob("*.txt"): if not meta_file.stem in path.stem: continue try: with meta_file.open("r") as file_open: meta_data = file_open.read() except UnicodeDecodeError: with meta_file.open("r", encoding="latin-1") as file_open: meta_data = file_open.read() addon_name = meta_file.stem result = live_version.search(meta_data) version = "0" if result: version = result.group(0) version = live_version_split.split(version)[1] return addon_name, version, path config_template = """https://www.esoui.com/downloads/info7-LibAddonMenu.html https://www.esoui.com/downloads/info1245-TamrielTradeCentre.html https://www.esoui.com/downloads/info1146-LibCustomMenu.html """ def config_new(path: Path): path.touch(exist_ok=True) with path.open("w") as file_open: file_open.write(config_template) def periodical_script(): parser = ArgumentParser( description="Visit https://www.esoui.com/ to search for addons and their dependencies URLs. Edit addons.text in the ESO live path and add the URL for each addon for installation. " ) parser.add_argument("-v", "--verbose", action="count", help="verbose logging") parser.add_argument("-l", "--log", action="store_true") parser.add_argument("-p", "--eso_live_path") args = parser.parse_args() if args.eso_live_path: args.eso_live_path = Path(args.eso_live_path) else: if system() == "Windows": args.eso_live_path = Path.home().joinpath( "Documents\Elder Scrolls Online\live" ) else: args.eso_live_path = Path.home().joinpath( ".steam/steam/steamapps/compatdata/306130/pfx/drive_c/users/steamuser/Documents/Elder Scrolls Online/live/" ) if args.verbose: level = logging.DEBUG format = "%(asctime)s %(filename)s:%(lineno)d %(message)s" else: level = logging.INFO format = "%(asctime)s %(message)s" if args.log: logging.basicConfig( level=level, format=format, filename=args.eso_live_path.joinpath("banana.log"), ) else: logging.basicConfig( level=level, format=format, ) logging.info(args) config_path = Path(args.eso_live_path).joinpath("addons.text") if not config_path.exists(): config_new(config_path) logging.info(f'addons list created at "{config_path}"') with config_path.open("r") as file_open: config_current = file_open.readlines() config_current = filter(None, config_current) live_path = args.eso_live_path.joinpath("AddOns") live_path.mkdir(parents=True, exist_ok=True) esoui_uris = list() for url in config_current: esoui = esoui_parse(url) esoui_uris.append(esoui) for child in live_path.iterdir(): live_to_esoui(path=child, esoui_uris=esoui_uris) esoui_to_live(esoui_uris=esoui_uris, live_path=live_path) ttc_update(live_path=live_path) def ttc(): parser = ArgumentParser(description="Tamriel Trade Centre price table updater.") parser.add_argument("-v", "--verbose", action="count", help="verbose logging") parser.add_argument("-l", "--log", action="store_true") parser.add_argument("-p", "--eso_live_path") args = parser.parse_args() if args.eso_live_path: args.eso_live_path = Path(args.eso_live_path) else: if system() == "Windows": args.eso_live_path = Path.home().joinpath( "Documents\Elder Scrolls Online\live" ) else: args.eso_live_path = Path.home().joinpath( ".steam/steam/steamapps/compatdata/306130/pfx/drive_c/users/steamuser/Documents/Elder Scrolls Online/live/" ) if args.verbose: level = logging.DEBUG format = "%(asctime)s %(filename)s:%(lineno)d %(message)s" else: level = logging.INFO format = "%(asctime)s %(message)s" if args.log: logging.basicConfig( level=level, format=format, filename=args.eso_live_path.joinpath("banana.log"), ) else: logging.basicConfig( level=level, format=format, ) logging.info(args) live_path = Path(args.eso_live_path).joinpath("AddOns") if not live_path.is_dir(): logging.error(f"eso_live_path_invalid_dir {live_path}") return ttc_update(live_path=live_path) price_table_uri = "https://us.tamrieltradecentre.com/download/PriceTable" price_table_name = "TamrielTradeCentre" def ttc_update(live_path: Path): request = Request(price_table_uri, headers=HEADERS) response = urlopen(request) temp_zip = NamedTemporaryFile() copyfileobj(response, temp_zip) temp_dir = TemporaryDirectory() temp_path = Path(temp_dir.name) zip_file = ZipFile(temp_zip) zip_file.extractall(temp_path) live_tamriel_trade_centre = live_path.joinpath("TamrielTradeCentre") copytree(temp_path, live_tamriel_trade_centre, dirs_exist_ok=True) logging.info( f"tamriel trade centre price table updated: {live_tamriel_trade_centre}" ) if __name__ == "__main__": periodical_script()