diff --git a/_trial_temp/_trial_marker b/_trial_temp/_trial_marker new file mode 100755 index 0000000..e69de29 diff --git a/src/__main__.py b/src/__main__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/app.py b/src/app.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/launcher_api_test.py b/src/tests/launcher_api_test.py new file mode 100644 index 0000000..3af4139 --- /dev/null +++ b/src/tests/launcher_api_test.py @@ -0,0 +1,28 @@ +import unittest +import asyncio +import worthless +client = worthless.Launcher() + + +class LauncherTest(unittest.TestCase): + def test_get_version_info(self): + version_info = asyncio.run(client.get_version_info()) + print("get_version_info test.") + print("version_info: ", version_info) + self.assertIsInstance(version_info, dict) + + def test_get_launcher_info(self): + launcher_info = asyncio.run(client.get_launcher_info()) + print("get_launcher_info test.") + print("launcher_info: ", launcher_info) + self.assertIsInstance(launcher_info, dict) + + def test_get_launcher_full_info(self): + launcher_info = asyncio.run(client.get_launcher_full_info()) + print("get_launcher_full_info test.") + print("launcher_full_info: ", launcher_info) + self.assertIsInstance(launcher_info, dict) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/worthless/__init__.py b/src/worthless/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/worthless/constants.py b/src/worthless/constants.py new file mode 100644 index 0000000..e69de29 diff --git a/src/worthless/launcher.py b/src/worthless/launcher.py new file mode 100644 index 0000000..b7aca88 --- /dev/null +++ b/src/worthless/launcher.py @@ -0,0 +1,26 @@ +import aiohttp +from worthless import constants + + +class Launcher: + def __init__(self): + self._api = constants.LAUNCHER_API_URL + + # Workaround because miHoYo uses retcode for their API + @staticmethod + async def _get(url, **kwargs): + async with aiohttp.ClientSession() as session: + rsp = await session.get(url, **kwargs) + rsp_json = await rsp.json() + # Why retcode seriously? They can just make the page not returning 200 status code. + if rsp_json["retcode"] != 0: + raise aiohttp.ClientResponseError(code=rsp_json["retcode"], + message=rsp_json["message"], + history=rsp.history, + request_info=rsp.request_info) + return rsp_json + + async def get_version_info(self): + rsp = await self._get(self._api + "/resource", params={"key": "gcStgarh", + "launcher_id": "10"}) + return rsp diff --git a/tests/_trial_temp/_trial_marker b/tests/_trial_temp/_trial_marker new file mode 100755 index 0000000..e69de29 diff --git a/tests/launcher_api_test.py b/tests/launcher_api_test.py new file mode 100644 index 0000000..e898b79 --- /dev/null +++ b/tests/launcher_api_test.py @@ -0,0 +1,35 @@ +import unittest +import asyncio +import worthless +client = worthless.Launcher() + + +class LauncherTest(unittest.TestCase): + def test_get_version_info(self): + version_info = asyncio.run(client.get_version_info()) + print("get_version_info test.") + print("get_version_info: ", version_info) + self.assertIsInstance(version_info, dict) + + def test_get_launcher_info(self): + launcher_info = asyncio.run(client.get_launcher_info()) + print("get_launcher_info test.") + print("get_launcher_info: ", launcher_info) + self.assertIsInstance(launcher_info, dict) + + def test_get_launcher_full_info(self): + launcher_info = asyncio.run(client.get_launcher_full_info()) + print("get_launcher_full_info test.") + print("get_launcher_full_info: ", launcher_info) + self.assertIsInstance(launcher_info, dict) + + def test_get_launcher_background_url(self): + bg_url = asyncio.run(client.get_launcher_background_url()) + print("get_launcher_background_url test.") + print("get_launcher_background_url: ", bg_url) + self.assertIsInstance(bg_url, str) + self.assertTrue(bg_url) + + +if __name__ == '__main__': + unittest.main() diff --git a/worthless/__init__.py b/worthless/__init__.py new file mode 100644 index 0000000..28ad165 --- /dev/null +++ b/worthless/__init__.py @@ -0,0 +1,3 @@ +from worthless import launcher + +Launcher = launcher.Launcher diff --git a/worthless/__main__.py b/worthless/__main__.py new file mode 100755 index 0000000..e4a0f2c --- /dev/null +++ b/worthless/__main__.py @@ -0,0 +1,6 @@ +#!/usr/bin/python3 + +from app import main + +if __name__ == '__main__': + main() diff --git a/worthless/constants.py b/worthless/constants.py new file mode 100644 index 0000000..e1a5ca0 --- /dev/null +++ b/worthless/constants.py @@ -0,0 +1,6 @@ +LAUNCHER_API_URL = "https://sdk-os-static.mihoyo.com/hk4e_global/mdk/launcher/api" +PATCH_GIT_URL = "https://notabug.org/Krock/dawn" +TELEMETRY_URL_LIST = [ + "log-upload-os.mihoyo.com", + "overseauspider.yuanshen.com" +] diff --git a/worthless/gui.py b/worthless/gui.py new file mode 100755 index 0000000..f7864a5 --- /dev/null +++ b/worthless/gui.py @@ -0,0 +1,44 @@ +#!/usr/bin/python3 + +import argparse +from pathlib import Path + + +def interactive_ui(gamedir=Path.cwd()): + raise NotImplementedError("Interactive UI is not implemented") + + +def update_game(gamedir=Path.cwd(), noconfirm=False): + print("Checking for current game version...") + # Call check_game_version() + print("Updating game...") + # Call update_game(fromver) + raise NotImplementedError("Update game is not implemented") + + +def main(): + parser = argparse.ArgumentParser(prog="worthless-launcher", description="A worthless launcher written in Python.") + parser.add_argument("-D", "-d", "--dir", action="store", type=Path, default=Path.cwd(), + help="Specify the game directory (default current working directory)") + parser.add_argument("-I", "-i", "--install", action="store_true", + help="Install the game (if not already installed, else do nothing)") + parser.add_argument("-U", "-u", "--update", action="store_true", help="Update the game (if not updated)") + parser.add_argument("--noconfirm", action="store_true", + help="Do not ask any questions. (Ignored in interactive mode)") + args = parser.parse_args() + print(args) + if args.install and args.update: + raise ValueError("Cannot specify both --install and --update arguments.") + + if args.install: + raise NotImplementedError("Install game is not implemented") + + if args.update: + update_game(args.dir, args.noconfirm) + return + + interactive_ui(args.dir) + + +if __name__ == "__main__": + main() diff --git a/worthless/launcher.py b/worthless/launcher.py new file mode 100644 index 0000000..9d2e6ce --- /dev/null +++ b/worthless/launcher.py @@ -0,0 +1,82 @@ +import aiohttp +import locale +from worthless import constants + + +class Launcher: + def __init__(self): + self._api = constants.LAUNCHER_API_URL + self._lang = self._get_system_language() + + # Workaround because miHoYo uses retcode for their API + @staticmethod + async def _get(url, **kwargs) -> dict: + async with aiohttp.ClientSession() as session: + rsp = await session.get(url, **kwargs) + rsp_json = await rsp.json() + # Why retcode seriously? They can just make the page not returning 200 status code. + if rsp_json["retcode"] != 0: + raise aiohttp.ClientResponseError(code=rsp_json["retcode"], + message=rsp_json["message"], + history=rsp.history, + request_info=rsp.request_info) + return rsp_json + + async def _get_launcher_info(self, adv=True) -> dict: + params = {"key": "gcStgarh", + "filter_adv": str(adv).lower(), + "launcher_id": "10", + "language": self._lang} + rsp = await self._get(self._api + "/content", params=params) + if rsp["data"]["adv"] is None: + params["language"] = "en-us" + rsp = await self._get(self._api + "/content", params=params) + return rsp + + @staticmethod + def _get_system_language() -> str: + """ + Get system language compatible with server parameters. + """ + try: + lang = locale.getdefaultlocale()[0] + lowercase_lang = lang.lower().replace("_", "-") + return lowercase_lang + except ValueError: + return "en-us" + + async def override_language(self, language: str) -> None: + """ + Override system detected language with another language. + :param language: The language to override with. + :return: None + """ + self._lang = language.lower().replace("_", "-") + + async def get_version_info(self) -> dict: + rsp = await self._get(self._api + "/resource", params={"key": "gcStgarh", + "launcher_id": "10"}) + return rsp + + async def get_launcher_info(self) -> dict: + """ + This function will get short launcher info from the server (only contains background image and FAQ) + :return: dict: Launcher info from the server. + """ + return await self._get_launcher_info(adv=True) + + async def get_launcher_full_info(self) -> dict: + """ + This function will get full launcher info from the server. + Since the server content is very long for a short explanation, you should see it manually. + :return: dict: Launcher info from the server. + """ + return await self._get_launcher_info(adv=False) + + async def get_launcher_background_url(self) -> str: + """ + This function will get launcher background image from the server. + :return: str: Background image URL. + """ + rsp = await self.get_launcher_info() + return rsp["data"]["adv"]["background"]