diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml new file mode 100644 index 0000000..ee0940d --- /dev/null +++ b/.github/workflows/python.yml @@ -0,0 +1,38 @@ +name: Fetch Wiki + +on: + push: + branches: + - 'main' + workflow_dispatch: ~ + +jobs: + Fetch-Wiki: + runs-on: ubuntu-latest + steps: + - name: Checkout source + uses: actions/checkout@master + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.12 + + - name: Install Dependencies + run: | + python -m pip install -r requirements.txt + + - name: Fetch Remote Files + run: | + python main.py + + - name: Commit changes + uses: EndBug/add-and-commit@v9 + with: + author_name: github-actions[bot] + author_email: github-actions[bot]@users.noreply.github.com + push: 'origin remote --force' + new_branch: 'remote' + message: ':sparkles: Fetch Wiki Changes' + add: | + 'data' diff --git a/.gitignore b/.gitignore index 68bc17f..2dc53ca 100644 --- a/.gitignore +++ b/.gitignore @@ -157,4 +157,4 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ diff --git a/func/__init__.py b/func/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/func/base.py b/func/base.py new file mode 100644 index 0000000..016eea2 --- /dev/null +++ b/func/base.py @@ -0,0 +1,27 @@ +import json + +from typing import Type, List + +from httpx import AsyncClient +from pydantic import BaseModel +from pathlib import Path + +BASE_URL = "https://api.encore.moe/zh-Hans" +client = AsyncClient(timeout=60.0) +data_path = Path("data") +data_path.mkdir(exist_ok=True) + + +async def get_item_base(item_path: str, item_type: Type[BaseModel], fix_func=None): + req = await client.get(f"{BASE_URL}/{item_path}") + req.raise_for_status() + data = fix_func(req.json()) if fix_func else req.json() + item_data = item_type(**data) + return item_data + + +def save_data(item_path: str, item_datas: List[BaseModel]): + path = data_path / f"{item_path}.json" + item_data = [i.dict(by_alias=True) for i in item_datas] + with path.open("w", encoding="utf-8") as f: + json.dump(item_data, f, ensure_ascii=False, indent=4) diff --git a/func/character.py b/func/character.py new file mode 100644 index 0000000..8d2bbd9 --- /dev/null +++ b/func/character.py @@ -0,0 +1,23 @@ +from typing import Dict, Any + +from .base import get_item_base, save_data +from models.character import EncoreAvatar, EncoreAvatars + + +async def get_characters(): + return await get_item_base("character", EncoreAvatars) + + +def fix_character(data: Dict[str, Any]) -> Dict[str, Any]: + data["Name"] = data["Name"]["Content"] + return data + + +async def get_character(character_id: int): + return await get_item_base(f"character/{character_id}", EncoreAvatar, fix_func=fix_character) + + +async def main(): + characters = await get_characters() + data = [await get_character(character.Id) for character in characters.roleList] + save_data("character", data) diff --git a/func/namecard.py b/func/namecard.py new file mode 100644 index 0000000..77ae217 --- /dev/null +++ b/func/namecard.py @@ -0,0 +1,16 @@ +from .base import get_item_base, save_data +from models.namecard import EncoreNameCard, EncoreNameCards + + +async def get_namecards(): + return await get_item_base("namecard", EncoreNameCards) + + +async def get_namecard(namecard_id: int): + return await get_item_base(f"namecard/{namecard_id}", EncoreNameCard) + + +async def main(): + namecards = await get_namecards() + data = [await get_namecard(namecard.Id) for namecard in namecards.namecardList] + save_data("namecard", data) diff --git a/func/weapon.py b/func/weapon.py new file mode 100644 index 0000000..dfadac8 --- /dev/null +++ b/func/weapon.py @@ -0,0 +1,16 @@ +from .base import get_item_base, save_data +from models.weapon import EncoreWeapon, EncoreWeapons + + +async def get_weapons(): + return await get_item_base("weapon", EncoreWeapons) + + +async def get_weapon(weapon_id: int): + return await get_item_base(f"weapon/{weapon_id}", EncoreWeapon) + + +async def main(): + weapons = await get_weapons() + data = [await get_weapon(weapon.Id) for weapon in weapons.weapons] + save_data("weapon", data) diff --git a/main.py b/main.py new file mode 100644 index 0000000..42855b3 --- /dev/null +++ b/main.py @@ -0,0 +1,17 @@ +from func.character import main as main_character +from func.weapon import main as main_weapon +from func.namecard import main as main_namecard + + +async def main(): + print("======== 请求角色数据 ========") + await main_character() + print("======== 请求武器数据 ========") + await main_weapon() + print("======== 请求名片数据 ========") + await main_namecard() + + +if __name__ == "__main__": + import asyncio + asyncio.run(main()) diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/character.py b/models/character.py new file mode 100644 index 0000000..aa11760 --- /dev/null +++ b/models/character.py @@ -0,0 +1,50 @@ +from typing import List + +from pydantic import BaseModel, Field + +from .enums import Element + + +class RoleListItem(BaseModel): + Id: int + RoleHeadIcon: str + Name: str + + +class EncoreAvatars(BaseModel): + roleList: List[RoleListItem] + + +class EncoreAvatar(BaseModel): + id: int = Field(alias="Id") + """ 角色ID """ + name: str = Field(alias="Name") + """ 名称 """ + rank: int = Field(alias="Priority") + """ 星级 """ + element: Element = Field(alias="ElementName") + """ 属性 """ + RoleHeadIconCircle: str + RoleHeadIconLarge: str + RoleHeadIconBig: str + Card: str + RoleHeadIcon: str + FormationRoleCard: str + RoleStand: str + RolePortrait: str + + @property + def big_gacha(self) -> str: + return self.RoleStand + + @property + def gacha(self) -> str: + return self.FormationRoleCard + + @property + def square(self) -> str: + return self.Card + + @property + def normal(self) -> str: + return self.RoleHeadIconLarge diff --git a/models/enums.py b/models/enums.py new file mode 100644 index 0000000..27262b1 --- /dev/null +++ b/models/enums.py @@ -0,0 +1,12 @@ +from enum import Enum + + +class Element(str, Enum): + """属性""" + + Wind = "气动" + Fire = "热熔" + Light = "衍射" + Ice = "冷凝" + Dark = "湮灭" + Thunder = "导电" diff --git a/models/namecard.py b/models/namecard.py new file mode 100644 index 0000000..9c0669d --- /dev/null +++ b/models/namecard.py @@ -0,0 +1,38 @@ +from typing import List + +from pydantic import BaseModel, Field + + +class NamecardListItem(BaseModel): + Id: int + CardPath: str + Icon: str + IconMiddle: str + IconSmall: str + Name: str + + +class EncoreNameCards(BaseModel): + namecardList: List[NamecardListItem] + + +class EncoreNameCard(BaseModel): + Id: int + QualityId: int + SortIndex: int + LongCardPath: str + CardPath: str + FunctionViewCardPath: str + Icon: str + IconMiddle: str + IconSmall: str + + name: str = Field(alias="Title") + desc: str = Field(alias="AttributesDescription") + + ObtainedShowDescription: str + TypeDescription: str + BgDescription: str + Tips: str + ShowInBag: bool + RedDotDisableRule: int diff --git a/models/weapon.py b/models/weapon.py new file mode 100644 index 0000000..c28f2b5 --- /dev/null +++ b/models/weapon.py @@ -0,0 +1,36 @@ +from typing import List + +from pydantic import BaseModel, Field + + +class Weapon(BaseModel): + Id: int + Name: str + Icon: str + + +class EncoreWeapons(BaseModel): + weapons: List[Weapon] + + +class EncoreWeapon(BaseModel): + id: int = Field(alias="ItemId") + """"武器ID""" + name: str = Field(alias="WeaponName") + """名称""" + description: str = Field(alias="BgDescription") + """描述""" + rank: int = Field(alias="QualityId") + """稀有度""" + IconBig: str = Field(alias="Icon") + IconMiddle: str + IconSmall: str + Mesh: str + + @property + def icon(self): + return self.IconBig + + @property + def big_pic(self): + return self.IconMiddle diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..574bd6b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +httpx +pydantic<2.0.0