From f945711ceac51608be334524b4a1efecfec6b67c Mon Sep 17 00:00:00 2001 From: Karako Date: Sat, 15 Apr 2023 13:09:56 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20building...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- models/__init__.py | 0 models/_base.py | 8 ++ models/avatar.py | 70 +++++++++++ models/enums.py | 88 +++++++++++++ models/item.py | 90 ++++++++++++++ poetry.lock | 304 ++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 6 + run.py | 90 ++++++++++++++ utils/__init__.py | 0 utils/const.py | 5 + utils/context.py | 46 +++++++ utils/manager.py | 78 ++++++++++++ utils/model.py | 35 ++++++ utils/single.py | 55 ++++++++ utils/text.py | 20 +++ utils/typedefs.py | 19 +++ 17 files changed, 914 insertions(+), 2 deletions(-) create mode 100644 models/__init__.py create mode 100644 models/_base.py create mode 100644 models/avatar.py create mode 100644 models/enums.py create mode 100644 models/item.py create mode 100644 run.py create mode 100644 utils/__init__.py create mode 100644 utils/const.py create mode 100644 utils/context.py create mode 100644 utils/manager.py create mode 100644 utils/model.py create mode 100644 utils/single.py create mode 100644 utils/text.py create mode 100644 utils/typedefs.py diff --git a/.gitignore b/.gitignore index b6e4761..c2127a1 100644 --- a/.gitignore +++ b/.gitignore @@ -125,5 +125,5 @@ venv.bak/ .dmypy.json dmypy.json -# Pyre type checker +# Pyre item_type checker .pyre/ diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/_base.py b/models/_base.py new file mode 100644 index 0000000..1a43e21 --- /dev/null +++ b/models/_base.py @@ -0,0 +1,8 @@ +from humps import camelize + +from utils.model import BaseConfig, BaseModel + + +class DataModel(BaseModel): + class Config(BaseConfig): + alias_generator = lambda x: camelize(x.removesuffix("TextHashMap")) diff --git a/models/avatar.py b/models/avatar.py new file mode 100644 index 0000000..db7231e --- /dev/null +++ b/models/avatar.py @@ -0,0 +1,70 @@ +from typing import TYPE_CHECKING + +from models.enums import AvatarQuality, WeaponType +from utils.model import BaseModel + +if TYPE_CHECKING: + from models.item import Item + + +class AvatarBirth(BaseModel): + month: int + """月""" + day: int + """日""" + + +class AvatarInfo(BaseModel): + title: str + """称号""" + birth: AvatarBirth + """生日""" + occupation: str + """所属""" + vision: str + """神之眼""" + constellation: str + """星座""" + description: str + """描述""" + + cn_cv: str + jp_cv: str + en_cv: str + kr_cv: str + + +class AvatarItem(BaseModel): + item: "Item" + """物品""" + count: int + """数量""" + + +class AvatarPromote(BaseModel): + required_level: int = 0 + """突破所需等级""" + promote_level: int = 0 + """突破等级""" + max_level: int + """解锁的等级上限""" + + coin: int = 0 + """摩拉""" + items: list[AvatarItem] + """突破所需材料""" + + +class Avatar(BaseModel): + id: int + """角色ID""" + name: str + """名称""" + quality: AvatarQuality + """品质""" + weapon: WeaponType + """武器类型""" + information: AvatarInfo + """角色信息""" + promote: AvatarPromote + """角色突破数据""" diff --git a/models/enums.py b/models/enums.py new file mode 100644 index 0000000..46ccef7 --- /dev/null +++ b/models/enums.py @@ -0,0 +1,88 @@ +from enum import StrEnum + + +class AvatarQuality(StrEnum): + Orange = "ORANGE" + """五星""" + Purple = "PURPLE" + """四星""" + Special = "SPECIAL" + """特殊""" + + +class WeaponType(StrEnum): + Sword = "SWORD" + """单手剑""" + Claymore = "CLAYMORE" + """双手剑""" + Bow = "BOW" + """弓""" + Catalyst = "CATALYST" + """法器""" + Polearm = "POLEARM" + """长柄武器""" + + +class Element(StrEnum): + Pyro = "PYRO" + """火""" + Hydro = "HYDRO" + """水""" + Anemo = "ANEMO" + """风""" + Electro = "ELECTRO" + """雷""" + Dendro = "DENDRO" + """草""" + Cryo = "CRYO" + """冰""" + Geo = "GEO" + """岩""" + Null = "NULL" + """无""" + + +class PropType(StrEnum): + HP = "HP" + """生命值""" + HP_P = "HPPercent" + """生命值百分比""" + + Defense = "Defense" + """防御力""" + Defense_P = "DefensePercent" + """防御力百分比""" + + Attack = "Attack" + """攻击力""" + Attack_P = "AttackPercent" + """攻击力百分比""" + + Critical = "Critical" + """暴击率""" + CriticalHurt = "CriticalHurt" + """暴击伤害""" + + Heal = "HealAdd" + """治疗加成""" + Element = "ElementMastery" + """元素精通""" + Charge = "ChargeEfficiency" + """元素充能效率""" + + Physical = "PhysicalAddHurt" + """物理伤害加成""" + Pyro = "PyroAddHurt" + """火元素伤害加成""" + Hydro = "HydroAddHurt" + """水元素伤害加成""" + Anemo = "AnemoAddHurt" + """风元素伤害加成""" + Electro = "ElectroAddHurt" + """雷元素伤害加成""" + Dendro = "DendroAddHurt" + """草元素伤害加成""" + Cryo = "CryoAddHurt" + """冰元素伤害加成""" + Geo = "GeoAddHurt" + """岩元素伤害加成""" diff --git a/models/item.py b/models/item.py new file mode 100644 index 0000000..f8a074e --- /dev/null +++ b/models/item.py @@ -0,0 +1,90 @@ +from enum import StrEnum + +from utils.model import BaseModel + + +class ItemType(StrEnum): + ... + + +class Item(BaseModel): + id: int + """ID""" + name: str + """名称""" + family: str + """种类""" + type: str | None + """类型""" + icon: str + """图标名""" + rarity: int | None + """星级""" + description: str + """描述""" + special_description: str | None + """特殊描述""" + + +class MaterialType(StrEnum): + ADSORBATE = "Adsorbate" + FAKE_ABSORBATE = "Fake_Absorbate" + + CONSUME = "消费物" + TALENT = "天赋" + AVATAR = "角色" + CHEST = "宝箱" + NOTICE_ADD_HP = "NOTICE_ADD_HP" + EXCHANGE = "交换物" + WOOD = "木材" + QUEST = "任务" + CRICKET = "蟋蟀" + WIDGET = "Widget" + ELEM_CRYSTAL = "Elem_Crystal" + SPICE_FOOD = "Spice_Food" + ACTIVITY_GEAR = "Activity_Gear" + ACTIVITY_ROBOT = "Activity_Robot" + ACTIVITY_JIGSAW = "Activity_Jigsaw" + FOOD = "Food" + EXP_FRUIT = "Exp_Fruit" + WEAPON_EXP_STONE = "Weapon_Exp_Stone" + AVATAR_MATERIAL = "Avatar_Material" + RELIQUARY_MATERIAL = "Reliquary_Material" + CONSUME_BATCH_USE = "Consume_Batch_Use" + FISH_BAIT = "Fish_Bait" + CHEST_BATCH_USE = "Chest_Batch_Use" + SELECTABLE_CHEST = "Selectable_Chest" + HOME_SEED = "Home_Seed" + FLYCLOAK = "Flycloak" + BGM = "Bgm" + SEA_LAMP = "Sea_Lamp" + CHANNELLER_SLAB_BUFF = "Channeller_Slab_Buff" + FISH_ROD = "Fish_Rod" + NAMECARD = "Namecard" + ARANARA = "Aranara" + DESHRET_MANUAL = "Deshret_Manual" + FIREWORKS = "Fireworks" + COSTUME = "Costume" + FURNITURE_SUITE_FORMULA = "Furniture_Suite_Formula" + FURNITURE_FORMULA = "Furniture_Formula" + + +class Material(Item): + material_type: MaterialType + """材料类型""" + + +class FoodQuality(StrEnum): + STRANGE = "Strange" + """奇怪的""" + ORDINARY = "Ordinary" + """普通的""" + DELICIOUS = "Delicious" + """美味的""" + + +class Food(Item): + quality: FoodQuality | None + """食物质量""" + effect: str + """效果""" diff --git a/poetry.lock b/poetry.lock index cb000c2..6819f9a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -15,6 +15,18 @@ files = [ [package.dependencies] pycares = ">=4.0.0" +[[package]] +name = "aiofiles" +version = "23.1.0" +description = "File support for asyncio." +category = "main" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "aiofiles-23.1.0-py3-none-any.whl", hash = "sha256:9312414ae06472eb6f1d163f555e466a23aed1c8f60c30cccf7121dba2e53eb2"}, + {file = "aiofiles-23.1.0.tar.gz", hash = "sha256:edd247df9a19e0db16534d4baaf536d6609a43e1de5401d7a4c1c148753a1635"}, +] + [[package]] name = "aiohttp" version = "3.8.4" @@ -141,6 +153,45 @@ files = [ [package.dependencies] frozenlist = ">=1.1.0" +[[package]] +name = "anyio" +version = "3.6.2" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +category = "main" +optional = false +python-versions = ">=3.6.2" +files = [ + {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, + {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] +trio = ["trio (>=0.16,<0.22)"] + +[[package]] +name = "arko-wrapper" +version = "0.2.8" +description = "给你的Python迭代器加上魔法" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "arko-wrapper-0.2.8.tar.gz", hash = "sha256:85167bc6f1dd48e3415a23a7f2f193c1544a450fd6d219ce28043af796c9b4c3"}, + {file = "arko_wrapper-0.2.8-py3-none-any.whl", hash = "sha256:c56b8cdbbd273cc1b7737667374ee600766e9e7f9f9546871b20912024aa0fb2"}, +] + +[package.dependencies] +typing-extensions = "*" + +[package.extras] +test = ["pytest", "pytest-rerunfailures"] + [[package]] name = "async-timeout" version = "4.0.2" @@ -264,6 +315,18 @@ files = [ {file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"}, ] +[[package]] +name = "certifi" +version = "2022.12.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] + [[package]] name = "cffi" version = "1.15.1" @@ -547,6 +610,105 @@ files = [ {file = "frozenlist-1.3.3.tar.gz", hash = "sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a"}, ] +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "h2" +version = "4.1.0" +description = "HTTP/2 State-Machine based protocol implementation" +category = "main" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, + {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, +] + +[package.dependencies] +hpack = ">=4.0,<5" +hyperframe = ">=6.0,<7" + +[[package]] +name = "hpack" +version = "4.0.0" +description = "Pure-Python HPACK header compression" +category = "main" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, + {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, +] + +[[package]] +name = "httpcore" +version = "0.16.3" +description = "A minimal low-level HTTP client." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, + {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, +] + +[package.dependencies] +anyio = ">=3.0,<5.0" +certifi = "*" +h11 = ">=0.13,<0.15" +sniffio = ">=1.0.0,<2.0.0" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + +[[package]] +name = "httpx" +version = "0.23.3" +description = "The next generation HTTP client." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, + {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, +] + +[package.dependencies] +certifi = "*" +h2 = {version = ">=3,<5", optional = true, markers = "extra == \"http2\""} +httpcore = ">=0.15.0,<0.17.0" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + +[[package]] +name = "hyperframe" +version = "6.0.1" +description = "HTTP/2 framing layer for Python" +category = "main" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, + {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, +] + [[package]] name = "idna" version = "3.4" @@ -837,6 +999,18 @@ typing-extensions = ">=4.2.0" dotenv = ["python-dotenv (>=0.10.4)"] email = ["email-validator (>=1.0.3)"] +[[package]] +name = "pyhumps" +version = "3.8.0" +description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, + {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, +] + [[package]] name = "python-dotenv" version = "1.0.0" @@ -852,6 +1026,134 @@ files = [ [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "regex" +version = "2022.10.31" +description = "Alternative regular expression module, to replace re." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "regex-2022.10.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a8ff454ef0bb061e37df03557afda9d785c905dab15584860f982e88be73015f"}, + {file = "regex-2022.10.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1eba476b1b242620c266edf6325b443a2e22b633217a9835a52d8da2b5c051f9"}, + {file = "regex-2022.10.31-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0e5af9a9effb88535a472e19169e09ce750c3d442fb222254a276d77808620b"}, + {file = "regex-2022.10.31-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d03fe67b2325cb3f09be029fd5da8df9e6974f0cde2c2ac6a79d2634e791dd57"}, + {file = "regex-2022.10.31-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9d0b68ac1743964755ae2d89772c7e6fb0118acd4d0b7464eaf3921c6b49dd4"}, + {file = "regex-2022.10.31-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a45b6514861916c429e6059a55cf7db74670eaed2052a648e3e4d04f070e001"}, + {file = "regex-2022.10.31-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8b0886885f7323beea6f552c28bff62cbe0983b9fbb94126531693ea6c5ebb90"}, + {file = "regex-2022.10.31-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5aefb84a301327ad115e9d346c8e2760009131d9d4b4c6b213648d02e2abe144"}, + {file = "regex-2022.10.31-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:702d8fc6f25bbf412ee706bd73019da5e44a8400861dfff7ff31eb5b4a1276dc"}, + {file = "regex-2022.10.31-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a3c1ebd4ed8e76e886507c9eddb1a891673686c813adf889b864a17fafcf6d66"}, + {file = "regex-2022.10.31-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:50921c140561d3db2ab9f5b11c5184846cde686bb5a9dc64cae442926e86f3af"}, + {file = "regex-2022.10.31-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:7db345956ecce0c99b97b042b4ca7326feeec6b75facd8390af73b18e2650ffc"}, + {file = "regex-2022.10.31-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:763b64853b0a8f4f9cfb41a76a4a85a9bcda7fdda5cb057016e7706fde928e66"}, + {file = "regex-2022.10.31-cp310-cp310-win32.whl", hash = "sha256:44136355e2f5e06bf6b23d337a75386371ba742ffa771440b85bed367c1318d1"}, + {file = "regex-2022.10.31-cp310-cp310-win_amd64.whl", hash = "sha256:bfff48c7bd23c6e2aec6454aaf6edc44444b229e94743b34bdcdda2e35126cf5"}, + {file = "regex-2022.10.31-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b4b1fe58cd102d75ef0552cf17242705ce0759f9695334a56644ad2d83903fe"}, + {file = "regex-2022.10.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:542e3e306d1669b25936b64917285cdffcd4f5c6f0247636fec037187bd93542"}, + {file = "regex-2022.10.31-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c27cc1e4b197092e50ddbf0118c788d9977f3f8f35bfbbd3e76c1846a3443df7"}, + {file = "regex-2022.10.31-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8e38472739028e5f2c3a4aded0ab7eadc447f0d84f310c7a8bb697ec417229e"}, + {file = "regex-2022.10.31-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76c598ca73ec73a2f568e2a72ba46c3b6c8690ad9a07092b18e48ceb936e9f0c"}, + {file = "regex-2022.10.31-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c28d3309ebd6d6b2cf82969b5179bed5fefe6142c70f354ece94324fa11bf6a1"}, + {file = "regex-2022.10.31-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9af69f6746120998cd9c355e9c3c6aec7dff70d47247188feb4f829502be8ab4"}, + {file = "regex-2022.10.31-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a5f9505efd574d1e5b4a76ac9dd92a12acb2b309551e9aa874c13c11caefbe4f"}, + {file = "regex-2022.10.31-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5ff525698de226c0ca743bfa71fc6b378cda2ddcf0d22d7c37b1cc925c9650a5"}, + {file = "regex-2022.10.31-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4fe7fda2fe7c8890d454f2cbc91d6c01baf206fbc96d89a80241a02985118c0c"}, + {file = "regex-2022.10.31-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2cdc55ca07b4e70dda898d2ab7150ecf17c990076d3acd7a5f3b25cb23a69f1c"}, + {file = "regex-2022.10.31-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:44a6c2f6374e0033873e9ed577a54a3602b4f609867794c1a3ebba65e4c93ee7"}, + {file = "regex-2022.10.31-cp311-cp311-win32.whl", hash = "sha256:d8716f82502997b3d0895d1c64c3b834181b1eaca28f3f6336a71777e437c2af"}, + {file = "regex-2022.10.31-cp311-cp311-win_amd64.whl", hash = "sha256:61edbca89aa3f5ef7ecac8c23d975fe7261c12665f1d90a6b1af527bba86ce61"}, + {file = "regex-2022.10.31-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a069c8483466806ab94ea9068c34b200b8bfc66b6762f45a831c4baaa9e8cdd"}, + {file = "regex-2022.10.31-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d26166acf62f731f50bdd885b04b38828436d74e8e362bfcb8df221d868b5d9b"}, + {file = "regex-2022.10.31-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac741bf78b9bb432e2d314439275235f41656e189856b11fb4e774d9f7246d81"}, + {file = "regex-2022.10.31-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75f591b2055523fc02a4bbe598aa867df9e953255f0b7f7715d2a36a9c30065c"}, + {file = "regex-2022.10.31-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b30bddd61d2a3261f025ad0f9ee2586988c6a00c780a2fb0a92cea2aa702c54"}, + {file = "regex-2022.10.31-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef4163770525257876f10e8ece1cf25b71468316f61451ded1a6f44273eedeb5"}, + {file = "regex-2022.10.31-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7b280948d00bd3973c1998f92e22aa3ecb76682e3a4255f33e1020bd32adf443"}, + {file = "regex-2022.10.31-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:d0213671691e341f6849bf33cd9fad21f7b1cb88b89e024f33370733fec58742"}, + {file = "regex-2022.10.31-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:22e7ebc231d28393dfdc19b185d97e14a0f178bedd78e85aad660e93b646604e"}, + {file = "regex-2022.10.31-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:8ad241da7fac963d7573cc67a064c57c58766b62a9a20c452ca1f21050868dfa"}, + {file = "regex-2022.10.31-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:586b36ebda81e6c1a9c5a5d0bfdc236399ba6595e1397842fd4a45648c30f35e"}, + {file = "regex-2022.10.31-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:0653d012b3bf45f194e5e6a41df9258811ac8fc395579fa82958a8b76286bea4"}, + {file = "regex-2022.10.31-cp36-cp36m-win32.whl", hash = "sha256:144486e029793a733e43b2e37df16a16df4ceb62102636ff3db6033994711066"}, + {file = "regex-2022.10.31-cp36-cp36m-win_amd64.whl", hash = "sha256:c14b63c9d7bab795d17392c7c1f9aaabbffd4cf4387725a0ac69109fb3b550c6"}, + {file = "regex-2022.10.31-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4cac3405d8dda8bc6ed499557625585544dd5cbf32072dcc72b5a176cb1271c8"}, + {file = "regex-2022.10.31-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23cbb932cc53a86ebde0fb72e7e645f9a5eec1a5af7aa9ce333e46286caef783"}, + {file = "regex-2022.10.31-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74bcab50a13960f2a610cdcd066e25f1fd59e23b69637c92ad470784a51b1347"}, + {file = "regex-2022.10.31-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78d680ef3e4d405f36f0d6d1ea54e740366f061645930072d39bca16a10d8c93"}, + {file = "regex-2022.10.31-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce6910b56b700bea7be82c54ddf2e0ed792a577dfaa4a76b9af07d550af435c6"}, + {file = "regex-2022.10.31-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:659175b2144d199560d99a8d13b2228b85e6019b6e09e556209dfb8c37b78a11"}, + {file = "regex-2022.10.31-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1ddf14031a3882f684b8642cb74eea3af93a2be68893901b2b387c5fd92a03ec"}, + {file = "regex-2022.10.31-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b683e5fd7f74fb66e89a1ed16076dbab3f8e9f34c18b1979ded614fe10cdc4d9"}, + {file = "regex-2022.10.31-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2bde29cc44fa81c0a0c8686992c3080b37c488df167a371500b2a43ce9f026d1"}, + {file = "regex-2022.10.31-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4919899577ba37f505aaebdf6e7dc812d55e8f097331312db7f1aab18767cce8"}, + {file = "regex-2022.10.31-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:9c94f7cc91ab16b36ba5ce476f1904c91d6c92441f01cd61a8e2729442d6fcf5"}, + {file = "regex-2022.10.31-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ae1e96785696b543394a4e3f15f3f225d44f3c55dafe3f206493031419fedf95"}, + {file = "regex-2022.10.31-cp37-cp37m-win32.whl", hash = "sha256:c670f4773f2f6f1957ff8a3962c7dd12e4be54d05839b216cb7fd70b5a1df394"}, + {file = "regex-2022.10.31-cp37-cp37m-win_amd64.whl", hash = "sha256:8e0caeff18b96ea90fc0eb6e3bdb2b10ab5b01a95128dfeccb64a7238decf5f0"}, + {file = "regex-2022.10.31-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:131d4be09bea7ce2577f9623e415cab287a3c8e0624f778c1d955ec7c281bd4d"}, + {file = "regex-2022.10.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e613a98ead2005c4ce037c7b061f2409a1a4e45099edb0ef3200ee26ed2a69a8"}, + {file = "regex-2022.10.31-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052b670fafbe30966bbe5d025e90b2a491f85dfe5b2583a163b5e60a85a321ad"}, + {file = "regex-2022.10.31-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa62a07ac93b7cb6b7d0389d8ef57ffc321d78f60c037b19dfa78d6b17c928ee"}, + {file = "regex-2022.10.31-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5352bea8a8f84b89d45ccc503f390a6be77917932b1c98c4cdc3565137acc714"}, + {file = "regex-2022.10.31-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20f61c9944f0be2dc2b75689ba409938c14876c19d02f7585af4460b6a21403e"}, + {file = "regex-2022.10.31-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29c04741b9ae13d1e94cf93fca257730b97ce6ea64cfe1eba11cf9ac4e85afb6"}, + {file = "regex-2022.10.31-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:543883e3496c8b6d58bd036c99486c3c8387c2fc01f7a342b760c1ea3158a318"}, + {file = "regex-2022.10.31-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7a8b43ee64ca8f4befa2bea4083f7c52c92864d8518244bfa6e88c751fa8fff"}, + {file = "regex-2022.10.31-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6a9a19bea8495bb419dc5d38c4519567781cd8d571c72efc6aa959473d10221a"}, + {file = "regex-2022.10.31-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6ffd55b5aedc6f25fd8d9f905c9376ca44fcf768673ffb9d160dd6f409bfda73"}, + {file = "regex-2022.10.31-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4bdd56ee719a8f751cf5a593476a441c4e56c9b64dc1f0f30902858c4ef8771d"}, + {file = "regex-2022.10.31-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ca88da1bd78990b536c4a7765f719803eb4f8f9971cc22d6ca965c10a7f2c4c"}, + {file = "regex-2022.10.31-cp38-cp38-win32.whl", hash = "sha256:5a260758454580f11dd8743fa98319bb046037dfab4f7828008909d0aa5292bc"}, + {file = "regex-2022.10.31-cp38-cp38-win_amd64.whl", hash = "sha256:5e6a5567078b3eaed93558842346c9d678e116ab0135e22eb72db8325e90b453"}, + {file = "regex-2022.10.31-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5217c25229b6a85049416a5c1e6451e9060a1edcf988641e309dbe3ab26d3e49"}, + {file = "regex-2022.10.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4bf41b8b0a80708f7e0384519795e80dcb44d7199a35d52c15cc674d10b3081b"}, + {file = "regex-2022.10.31-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf0da36a212978be2c2e2e2d04bdff46f850108fccc1851332bcae51c8907cc"}, + {file = "regex-2022.10.31-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d403d781b0e06d2922435ce3b8d2376579f0c217ae491e273bab8d092727d244"}, + {file = "regex-2022.10.31-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a37d51fa9a00d265cf73f3de3930fa9c41548177ba4f0faf76e61d512c774690"}, + {file = "regex-2022.10.31-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4f781ffedd17b0b834c8731b75cce2639d5a8afe961c1e58ee7f1f20b3af185"}, + {file = "regex-2022.10.31-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d243b36fbf3d73c25e48014961e83c19c9cc92530516ce3c43050ea6276a2ab7"}, + {file = "regex-2022.10.31-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:370f6e97d02bf2dd20d7468ce4f38e173a124e769762d00beadec3bc2f4b3bc4"}, + {file = "regex-2022.10.31-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:597f899f4ed42a38df7b0e46714880fb4e19a25c2f66e5c908805466721760f5"}, + {file = "regex-2022.10.31-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7dbdce0c534bbf52274b94768b3498abdf675a691fec5f751b6057b3030f34c1"}, + {file = "regex-2022.10.31-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:22960019a842777a9fa5134c2364efaed5fbf9610ddc5c904bd3a400973b0eb8"}, + {file = "regex-2022.10.31-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7f5a3ffc731494f1a57bd91c47dc483a1e10048131ffb52d901bfe2beb6102e8"}, + {file = "regex-2022.10.31-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7ef6b5942e6bfc5706301a18a62300c60db9af7f6368042227ccb7eeb22d0892"}, + {file = "regex-2022.10.31-cp39-cp39-win32.whl", hash = "sha256:395161bbdbd04a8333b9ff9763a05e9ceb4fe210e3c7690f5e68cedd3d65d8e1"}, + {file = "regex-2022.10.31-cp39-cp39-win_amd64.whl", hash = "sha256:957403a978e10fb3ca42572a23e6f7badff39aa1ce2f4ade68ee452dc6807692"}, + {file = "regex-2022.10.31.tar.gz", hash = "sha256:a3a98921da9a1bf8457aeee6a551948a83601689e5ecdd736894ea9bbec77e83"}, +] + +[[package]] +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] + +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + [[package]] name = "typing-extensions" version = "4.5.0" @@ -1030,4 +1332,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "043085936cf3935b690f67dd9a0350953a4f7e9e7aed7b23a40d74f4208bd55f" +content-hash = "7b099d3f7d122dc03ac646e52c3680b9cf29c2e96a41ea937716a3452b76d689" diff --git a/pyproject.toml b/pyproject.toml index 6c12771..87982a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,12 @@ ujson = "^5.7.0" orjson = "^3.8.8" aiohttp = {extras = ["speedups"], version = "^3.8.4"} pydantic = {extras = ["dotenv", "email"], version = "^1.10.6"} +aiofiles = "^23.1.0" +regex = "^2022.10.31" +pyhumps = "^3.8.0" +httpx = {extras = ["http2"], version = "^0.23.3"} +arko-wrapper = "^0.2.8" + [build-system] diff --git a/run.py b/run.py new file mode 100644 index 0000000..fd86097 --- /dev/null +++ b/run.py @@ -0,0 +1,90 @@ +import asyncio + +from utils.const import PROJECT_ROOT +from utils.context import ContextManager +from utils.manager import ResourceManager +from utils.text import Text +from utils.typedefs import Lang +import ujson as json + +OUTPUT_DIR = PROJECT_ROOT / "data" +OUTPUT_DIR.mkdir(parents=True, exist_ok=True) + + +# noinspection PyShadowingBuiltins +async def parse_item_data(resource: ResourceManager): + from models.item import Food, FoodQuality, Item + + json_data = resource.fetch("MaterialExcelConfigData") + data_list = [] + + for item_data in json_data: + id = item_data["id"] + name = Text(item_data["nameTextMapHash"]) + family = item_data.get("materialType", "") + rarity = item_data.get("rankLevel") + type = Text(item_data["typeDescTextMapHash"]) + icon = item_data["icon"] + description = Text(item_data["descTextMapHash"]) + special = Text(item_data["specialDescTextMapHash"]) or None + + base_kwargs = { + "id": id, + "name": name, + "family": family, + "rarity": rarity, + "type": type, + "icon": icon, + "description": description, + } + if special is not None: + base_kwargs["special_description"] = special + + if "materialType" in item_data: # 材料 + material_type = item_data["materialType"] + + elif "foodQuality" in item_data: # 食物 + quality = FoodQuality( + item_data["foodQuality"].removeprefix("FOOD_QUALITY_").title() + ) + effect = Text(item_data["effectDescTextMapHash"]) + item = Food(quality=quality, effect=effect, **base_kwargs) + else: + item = Item(**base_kwargs) + data_list.append(item.dict(exclude_none=True)) + + item_data_file = OUTPUT_DIR / "item.json" + with open(item_data_file, "w", encoding="utf-8") as f: + f.write(json.dumps(data_list, ensure_ascii=False)) + breakpoint() + + +async def fetch_parse_data(lang: Lang): + with ContextManager().with_context( + "resource_manager", ResourceManager(lang=lang) + ) as resource_manager: + await parse_item_data(resource_manager) + + +async def main(): + task_list = [] + for lang in Lang.__args__: + task = asyncio.create_task(fetch_parse_data(lang=lang)) + task_list.append(task) + await asyncio.gather(*task_list) + + +def __main__(): + import asyncio + + import sys + + if (3, 10) >= sys.version_info >= (3, 8) and sys.platform.startswith("win"): + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + loop = asyncio.new_event_loop() + loop.run_until_complete(main()) + + +if __name__ == "__main__": + __main__() diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/const.py b/utils/const.py new file mode 100644 index 0000000..3897065 --- /dev/null +++ b/utils/const.py @@ -0,0 +1,5 @@ +from pathlib import Path + +PROJECT_ROOT = Path(__file__).joinpath("../../").resolve() +DATA_DIR = PROJECT_ROOT.joinpath("data") +DATA_DIR.mkdir(parents=True, exist_ok=True) diff --git a/utils/context.py b/utils/context.py new file mode 100644 index 0000000..38a4633 --- /dev/null +++ b/utils/context.py @@ -0,0 +1,46 @@ +from contextlib import contextmanager +from contextvars import ContextVar, Token +from typing import TypeVar + +from utils.single import Singleton + +__all__ = ("ContextManager",) + +T = TypeVar("T") + +_values: dict[str, ContextVar] = {} + + +class ContextManager(Singleton): + _values: dict[str, ContextVar] = {} + + def get_context(self, name: str) -> ContextVar[T]: + if name in self._values: + return self._values[name] + else: + result = ContextVar(name) + self._values[name] = result + return result + + def get_value(self, name: str) -> T: + return self.get_context(name).get() + + def set_context(self, name: str, value: T) -> Token: + context: ContextVar[T] = self.get_context(name) + return context.set(value) + + def reset_context(self, name: str, token: Token) -> None: + context: ContextVar[T] = self.get_context(name) + context.reset(token) + + @contextmanager + def with_context(self, name: str, value: T) -> T: + context: ContextVar[T] = self.get_context(name) + token: Token = context.set(value) + try: + yield value + finally: + context.reset(token) + + def __getitem__(self, item: str) -> ContextVar[T]: + return self.get_context(item) diff --git a/utils/manager.py b/utils/manager.py new file mode 100644 index 0000000..2c60b10 --- /dev/null +++ b/utils/manager.py @@ -0,0 +1,78 @@ +import os +import ssl +from functools import lru_cache + +import ujson as json +from httpx import Client +from pydantic import Json +from yarl import URL + +from utils.const import DATA_DIR +from utils.single import Singleton +from utils.typedefs import Lang + +__all__ = "ResourceManager" + +ssl_context = ssl.SSLContext() + + +class ResourceManager(Singleton): + _lang: Lang + _base_url: URL + + _client: Client | None = None + _lang_data: dict[str, str] | None = None + + def __init__(self, base_url: str | None = None, lang: Lang | None = None): + self._base_url = URL( + base_url + or "https://git.neeemooo.com/githubbackup/GenshinData/-/raw/master/" + ) + self._lang = lang or "chs" + + @property + def lang(self) -> Lang: + return self._lang + + @property + def client(self) -> Client: + with self._lock: + if self._client is None or self._client.is_closed: + self._client = Client(verify=ssl_context) + return self._client + + def refresh(self) -> None: + """删除缓存数据的文件夹,需要数据时会重新从网络上下载,达到刷新缓存数据的目的""" + if self._client is not None: + if not self._client.is_closed: + self._client.close() + self._client = None + if DATA_DIR.exists(): + os.remove(DATA_DIR) + DATA_DIR.mkdir(parents=True, exist_ok=True) + + @lru_cache(128, typed=True) + def get_text(self, text_id: int | str | None) -> str | None: + if text_id is None: + return None + if self._lang_data is None: + self._lang_data = self.fetch("TextMap" + self.lang.upper(), "TextMap") + result = self._lang_data.get(str(text_id), None) + if result is not None: + return result.replace("\\n", "\n") + return result + + @lru_cache() + def fetch(self, name: str, file_dir: str = "ExcelBinOutput") -> Json: + file_path = DATA_DIR.joinpath(file_dir).joinpath(name).with_suffix(".json") + file_path.parent.mkdir(exist_ok=True, parents=True) + + if not file_path.exists() or os.stat(file_path) == 0: + response = self.client.get(str(self._base_url / file_dir / file_path.name)) + response.raise_for_status() + with open(file_path, encoding="utf-8", mode="w") as file: + file.write(content := response.text) + return json.loads(content) + + with open(file_path, encoding="utf-8", mode="r") as file: + return json.loads(file.read()) diff --git a/utils/model.py b/utils/model.py new file mode 100644 index 0000000..553c702 --- /dev/null +++ b/utils/model.py @@ -0,0 +1,35 @@ +from typing import Type, TypeVar + +import ujson as json +from pydantic import ( + BaseConfig as PydanticBaseConfig, + BaseModel as PydanticBaseModel, + BaseSettings as PydanticBaseSettings, +) + +__all__ = ("BaseConfig", "BaseSettings", "BaseModel") + +T = TypeVar("T") + + +class BaseConfig(PydanticBaseConfig): + json_dumps = json.dumps + json_loads = json.loads + + +class BaseSettings(PydanticBaseSettings): + def __new__(cls: Type[T], *args, **kwargs) -> T: + cls.update_forward_refs() + return super(PydanticBaseSettings, cls).__new__(cls) + + class Config(BaseConfig): + pass + + +class BaseModel(PydanticBaseModel): + def __new__(cls: Type[T], *args, **kwargs) -> T: + cls.update_forward_refs() + return super(PydanticBaseModel, cls).__new__(cls) + + class Config(BaseConfig): + pass diff --git a/utils/single.py b/utils/single.py new file mode 100644 index 0000000..c17fc72 --- /dev/null +++ b/utils/single.py @@ -0,0 +1,55 @@ +from multiprocessing import RLock as Lock +from typing import ( + ClassVar, + Generic, + Optional, + TYPE_CHECKING, + Type, + TypeVar, +) + +from typing_extensions import Self + +if TYPE_CHECKING: + from multiprocessing.synchronize import RLock as LockType + +__all__ = ["singleton", "Singleton"] + +T = TypeVar("T") + + +class _Singleton(Generic[T]): + lock: ClassVar["LockType"] = Lock() + + __slots__ = "cls", "instance" + + cls: Type[T] + instance: Optional[T] + + def __init__(self, cls: Type[T]): + self.cls = cls + self.instance = None + + def __call__(self, *args, **kwargs) -> T: + with self.lock: + if self.instance is None or args or kwargs: + self.instance = self.cls(*args, **kwargs) + return self.instance + + +def singleton(cls: Optional[Type[T]] = None) -> Type[T]: + def wrap(_cls: Type[T]) -> _Singleton[T]: + return _Singleton(_cls) + + return wrap if cls is None else wrap(cls) + + +class Singleton(object): + _lock: ClassVar["LockType"] = Lock() + _instance: ClassVar[Optional[Self]] = None + + def __new__(cls: Type[T], *args, **kwargs) -> T: + with cls._lock: + if cls._instance is None: + cls._instance = object.__new__(cls) + return cls._instance diff --git a/utils/text.py b/utils/text.py new file mode 100644 index 0000000..53739dd --- /dev/null +++ b/utils/text.py @@ -0,0 +1,20 @@ +from utils.context import ContextManager + +__all__ = ("Text",) + + +class Text(str): + def __new__(cls, string: str | int) -> "Text": + _id = None + if isinstance(string, str): + try: + _id = int(string) + except ValueError: + _id = None + elif isinstance(string, int): + _id = string + if _id is not None: + string = ( + ContextManager().get_value("resource_manager").get_text(_id) or "" + ).replace("\\n", "\n") + return str.__new__(cls, string) diff --git a/utils/typedefs.py b/utils/typedefs.py new file mode 100644 index 0000000..932b89e --- /dev/null +++ b/utils/typedefs.py @@ -0,0 +1,19 @@ +from typing import Literal + +Lang = Literal[ + "chs", + "cht", + "de", + "en", + "es", + "fr", + "id", + "it", + "jp", + "kr", + "pt", + "ru", + "th", + "tr", + "vi", +]