import io import logging import os.path import subprocess import urllib.request from shutil import which from tkinter.messagebox import askyesnocancel from typing import Any, Optional from zipfile import ZipFile from Utils import open_file import requests from Utils import is_windows, messagebox, tuplize_version MOD_URL = "https://api.github.com/repos/alwaysintreble/TheMessengerRandomizerModAP/releases/latest" def launch_game(url: Optional[str] = None) -> None: """Check the game installation, then launch it""" def courier_installed() -> bool: """Check if Courier is installed""" return os.path.exists(os.path.join(game_folder, "TheMessenger_Data", "Managed", "Assembly-CSharp.Courier.mm.dll")) def mod_installed() -> bool: """Check if the mod is installed""" return os.path.exists(os.path.join(game_folder, "Mods", "TheMessengerRandomizerAP", "courier.toml")) def request_data(request_url: str) -> Any: """Fetches json response from given url""" logging.info(f"requesting {request_url}") response = requests.get(request_url) if response.status_code == 200: # success try: data = response.json() except requests.exceptions.JSONDecodeError: raise RuntimeError(f"Unable to fetch data. (status code {response.status_code})") else: raise RuntimeError(f"Unable to fetch data. (status code {response.status_code})") return data def install_courier() -> None: """Installs latest version of Courier""" # can't use latest since courier uses pre-release tags courier_url = "https://api.github.com/repos/Brokemia/Courier/releases" latest_download = request_data(courier_url)[0]["assets"][-1]["browser_download_url"] with urllib.request.urlopen(latest_download) as download: with ZipFile(io.BytesIO(download.read()), "r") as zf: for member in zf.infolist(): zf.extract(member, path=game_folder) os.chdir(game_folder) # linux and mac handling if not is_windows: mono_exe = which("mono") if not mono_exe: # steam deck support but doesn't currently work messagebox("Failure", "Failed to install Courier", True) raise RuntimeError("Failed to install Courier") # # download and use mono kickstart # # this allows steam deck support # mono_kick_url = "https://github.com/flibitijibibo/MonoKickstart/archive/refs/heads/master.zip" # target = os.path.join(folder, "monoKickstart") # os.makedirs(target, exist_ok=True) # with urllib.request.urlopen(mono_kick_url) as download: # with ZipFile(io.BytesIO(download.read()), "r") as zf: # for member in zf.infolist(): # zf.extract(member, path=target) # installer = subprocess.Popen([os.path.join(target, "precompiled"), # os.path.join(folder, "MiniInstaller.exe")], shell=False) # os.remove(target) else: installer = subprocess.Popen([mono_exe, os.path.join(game_folder, "MiniInstaller.exe")], shell=False) else: installer = subprocess.Popen(os.path.join(game_folder, "MiniInstaller.exe"), shell=False) failure = installer.wait() if failure: messagebox("Failure", "Failed to install Courier", True) os.chdir(working_directory) raise RuntimeError("Failed to install Courier") os.chdir(working_directory) if courier_installed(): messagebox("Success!", "Courier successfully installed!") return messagebox("Failure", "Failed to install Courier", True) raise RuntimeError("Failed to install Courier") def install_mod() -> None: """Installs latest version of the mod""" assets = request_data(MOD_URL)["assets"] if len(assets) == 1: release_url = assets[0]["browser_download_url"] else: for asset in assets: if "TheMessengerRandomizerAP" in asset["name"]: release_url = asset["browser_download_url"] break else: messagebox("Failure", "Failed to find latest mod download", True) raise RuntimeError("Failed to install Mod") mod_folder = os.path.join(game_folder, "Mods") os.makedirs(mod_folder, exist_ok=True) with urllib.request.urlopen(release_url) as download: with ZipFile(io.BytesIO(download.read()), "r") as zf: for member in zf.infolist(): zf.extract(member, path=mod_folder) messagebox("Success!", "Latest mod successfully installed!") def available_mod_update(latest_version: str) -> bool: """Check if there's an available update""" latest_version = latest_version.lstrip("v") toml_path = os.path.join(game_folder, "Mods", "TheMessengerRandomizerAP", "courier.toml") with open(toml_path, "r") as f: installed_version = f.read().splitlines()[1].strip("version = \"") logging.info(f"Installed version: {installed_version}. Latest version: {latest_version}") # one of the alpha builds return "alpha" in latest_version or tuplize_version(latest_version) > tuplize_version(installed_version) from . import MessengerWorld game_folder = os.path.dirname(MessengerWorld.settings.game_path) working_directory = os.getcwd() if not courier_installed(): should_install = askyesnocancel("Install Courier", "No Courier installation detected. Would you like to install now?") if not should_install: return logging.info("Installing Courier") install_courier() if not mod_installed(): should_install = askyesnocancel("Install Mod", "No randomizer mod detected. Would you like to install now?") if not should_install: return logging.info("Installing Mod") install_mod() else: latest = request_data(MOD_URL)["tag_name"] if available_mod_update(latest): should_update = askyesnocancel("Update Mod", f"New mod version detected. Would you like to update to {latest} now?") if should_update: logging.info("Updating mod") install_mod() elif should_update is None: return if not is_windows: if url: open_file(f"steam://rungameid/764790//{url}/") else: open_file("steam://rungameid/764790") else: os.chdir(game_folder) if url: subprocess.Popen([MessengerWorld.settings.game_path, str(url)]) else: subprocess.Popen(MessengerWorld.settings.game_path) os.chdir(working_directory)