mirror of
https://github.com/PaiGramTeam/PamGram.git
synced 2024-11-24 23:29:40 +00:00
✨ Support starrail warp log
This commit is contained in:
parent
c07f0ea8fd
commit
088e9ac315
@ -1,47 +1,25 @@
|
|||||||
"""用于下载和管理角色、武器、材料等的图标"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import re
|
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
from functools import cached_property, lru_cache, partial
|
|
||||||
from multiprocessing import RLock as Lock
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from ssl import SSLZeroReturnError
|
from ssl import SSLZeroReturnError
|
||||||
from typing import AsyncIterator, Awaitable, Callable, ClassVar, Dict, Optional, TYPE_CHECKING, TypeVar, Union
|
from typing import Optional, List, Dict
|
||||||
|
|
||||||
from aiofiles import open as async_open
|
from aiofiles import open as async_open
|
||||||
from aiofiles.os import remove as async_remove
|
from httpx import AsyncClient, HTTPError
|
||||||
from enkanetwork import Assets as EnkaAssets
|
|
||||||
from enkanetwork.model.assets import CharacterAsset as EnkaCharacterAsset
|
|
||||||
from httpx import AsyncClient, HTTPError, HTTPStatusError, TransportError, URL
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from core.base_service import BaseService
|
from core.base_service import BaseService
|
||||||
from core.config import config
|
from modules.wiki.base import WikiModel
|
||||||
from metadata.genshin import AVATAR_DATA, HONEY_DATA, MATERIAL_DATA, NAMECARD_DATA, WEAPON_DATA
|
from modules.wiki.models.avatar_config import AvatarIcon
|
||||||
from metadata.scripts.honey import update_honey_metadata
|
from modules.wiki.models.light_cone_config import LightConeIcon
|
||||||
from metadata.scripts.metadatas import update_metadata_from_ambr, update_metadata_from_github
|
from utils.const import PROJECT_ROOT
|
||||||
from metadata.shortname import roleToId, weaponToId
|
|
||||||
from utils.const import AMBR_HOST, ENKA_HOST, PROJECT_ROOT
|
|
||||||
from utils.log import logger
|
from utils.log import logger
|
||||||
from utils.typedefs import StrOrInt, StrOrURL
|
from utils.typedefs import StrOrURL, StrOrInt
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from httpx import Response
|
|
||||||
from multiprocessing.synchronize import RLock
|
|
||||||
|
|
||||||
__all__ = ("AssetsServiceType", "AssetsService", "AssetsServiceError", "AssetsCouldNotFound", "DEFAULT_EnkaAssets")
|
|
||||||
|
|
||||||
ICON_TYPE = Union[Callable[[bool], Awaitable[Optional[Path]]], Callable[..., Awaitable[Optional[Path]]]]
|
|
||||||
NAME_MAP_TYPE = Dict[str, StrOrURL]
|
|
||||||
|
|
||||||
ASSETS_PATH = PROJECT_ROOT.joinpath("resources/assets")
|
ASSETS_PATH = PROJECT_ROOT.joinpath("resources/assets")
|
||||||
ASSETS_PATH.mkdir(exist_ok=True, parents=True)
|
ASSETS_PATH.mkdir(exist_ok=True, parents=True)
|
||||||
HONEY_HOST = ""
|
DATA_MAP = {
|
||||||
DATA_MAP = {"avatar": AVATAR_DATA, "weapon": WEAPON_DATA, "material": MATERIAL_DATA}
|
"avatar": WikiModel.BASE_URL + "avatar_icons.json",
|
||||||
|
"light_cone": WikiModel.BASE_URL + "light_cone_icons.json",
|
||||||
DEFAULT_EnkaAssets = EnkaAssets(lang="chs")
|
}
|
||||||
|
|
||||||
|
|
||||||
class AssetsServiceError(Exception):
|
class AssetsServiceError(Exception):
|
||||||
@ -55,84 +33,16 @@ class AssetsCouldNotFound(AssetsServiceError):
|
|||||||
super().__init__(f"{message}: target={message}")
|
super().__init__(f"{message}: target={message}")
|
||||||
|
|
||||||
|
|
||||||
class _AssetsService(ABC):
|
class _AssetsService:
|
||||||
_lock: ClassVar["RLock"] = Lock()
|
client: Optional[AsyncClient] = None
|
||||||
_dir: ClassVar[Path]
|
|
||||||
icon_types: ClassVar[list[str]]
|
|
||||||
|
|
||||||
_client: Optional[AsyncClient] = None
|
|
||||||
_links: dict[str, str] = {}
|
|
||||||
|
|
||||||
id: int
|
|
||||||
type: str
|
|
||||||
|
|
||||||
icon: ICON_TYPE
|
|
||||||
"""图标"""
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@cached_property
|
|
||||||
def game_name(self) -> str:
|
|
||||||
"""游戏数据中的名称"""
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def honey_id(self) -> str:
|
|
||||||
"""当前资源在 Honey Impact 所对应的 ID"""
|
|
||||||
return HONEY_DATA[self.type].get(str(self.id), [""])[0]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def path(self) -> Path:
|
|
||||||
"""当前资源的文件夹"""
|
|
||||||
result = self._dir.joinpath(str(self.id)).resolve()
|
|
||||||
result.mkdir(exist_ok=True, parents=True)
|
|
||||||
return result
|
|
||||||
|
|
||||||
@property
|
|
||||||
def client(self) -> AsyncClient:
|
|
||||||
with self._lock:
|
|
||||||
if self._client is None or self._client.is_closed:
|
|
||||||
self._client = AsyncClient()
|
|
||||||
return self._client
|
|
||||||
|
|
||||||
def __init__(self, client: Optional[AsyncClient] = None) -> None:
|
def __init__(self, client: Optional[AsyncClient] = None) -> None:
|
||||||
self._client = client
|
self.client = client
|
||||||
|
|
||||||
def __call__(self, target: int) -> Self:
|
async def _download(self, url: StrOrURL, path: Path, retry: int = 5) -> Optional[Path]:
|
||||||
"""用于生成与 target 对应的 assets"""
|
|
||||||
result = self.__class__(self.client)
|
|
||||||
result.id = target
|
|
||||||
return result
|
|
||||||
|
|
||||||
def __init_subclass__(cls, **kwargs) -> None:
|
|
||||||
"""初始化一些类变量"""
|
|
||||||
from itertools import chain
|
|
||||||
|
|
||||||
cls.icon_types = [ # 支持的图标类型
|
|
||||||
k
|
|
||||||
for k, v in chain(cls.__annotations__.items(), *map(lambda x: x.__annotations__.items(), cls.__bases__))
|
|
||||||
if v in [ICON_TYPE, "ICON_TYPE"]
|
|
||||||
]
|
|
||||||
cls.type = cls.__name__.lstrip("_").split("Assets")[0].lower() # 当前 assert 的类型
|
|
||||||
cls._dir = ASSETS_PATH.joinpath(cls.type) # 图标保存的文件夹
|
|
||||||
cls._dir.mkdir(exist_ok=True, parents=True)
|
|
||||||
|
|
||||||
async def _request(self, url: str, interval: float = 0.2) -> "Response":
|
|
||||||
error = None
|
|
||||||
for _ in range(5):
|
|
||||||
try:
|
|
||||||
return await self.client.get(url, follow_redirects=False)
|
|
||||||
except (TransportError, SSLZeroReturnError) as e:
|
|
||||||
error = e
|
|
||||||
await asyncio.sleep(interval)
|
|
||||||
continue
|
|
||||||
if error is not None:
|
|
||||||
raise error
|
|
||||||
|
|
||||||
async def _download(self, url: StrOrURL, path: Path, retry: int = 5) -> Path | None:
|
|
||||||
"""从 url 下载图标至 path"""
|
"""从 url 下载图标至 path"""
|
||||||
logger.debug("正在从 %s 下载图标至 %s", url, path)
|
logger.debug("正在从 %s 下载图标至 %s", url, path)
|
||||||
headers = None
|
headers = None
|
||||||
if config.enka_network_api_agent is not None and URL(url).host == "enka.network":
|
|
||||||
headers = {"user-agent": config.enka_network_api_agent}
|
|
||||||
for time in range(retry):
|
for time in range(retry):
|
||||||
try:
|
try:
|
||||||
response = await self.client.get(url, follow_redirects=False, headers=headers)
|
response = await self.client.get(url, follow_redirects=False, headers=headers)
|
||||||
@ -150,356 +60,144 @@ class _AssetsService(ABC):
|
|||||||
await file.write(response.content) # 保存图标
|
await file.write(response.content) # 保存图标
|
||||||
return path.resolve()
|
return path.resolve()
|
||||||
|
|
||||||
async def _get_from_ambr(self, item: str) -> AsyncIterator[str | None]: # pylint: disable=W0613,R0201
|
|
||||||
"""从 ambr.top 上获取目标链接"""
|
|
||||||
yield None
|
|
||||||
|
|
||||||
async def _get_from_enka(self, item: str) -> AsyncIterator[str | None]: # pylint: disable=W0613,R0201
|
|
||||||
"""从 enke.network 上获取目标链接"""
|
|
||||||
yield None
|
|
||||||
|
|
||||||
async def _get_from_honey(self, item: str) -> AsyncIterator[str | None]:
|
|
||||||
"""从 honey 上获取目标链接"""
|
|
||||||
if (honey_name := self.honey_name_map.get(item, None)) is not None:
|
|
||||||
yield HONEY_HOST.join(f"img/{honey_name}.png")
|
|
||||||
yield HONEY_HOST.join(f"img/{honey_name}.webp")
|
|
||||||
|
|
||||||
async def _download_url_generator(self, item: str) -> AsyncIterator[str]:
|
|
||||||
# 获取当前 `AssetsService` 的所有爬虫
|
|
||||||
for func in map(lambda x: getattr(self, x), sorted(filter(lambda x: x.startswith("_get_from_"), dir(self)))):
|
|
||||||
async for url in func(item):
|
|
||||||
if url is not None:
|
|
||||||
try:
|
|
||||||
response = await self._request(url := str(url))
|
|
||||||
response.raise_for_status()
|
|
||||||
yield url
|
|
||||||
except HTTPStatusError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
async def _get_download_url(self, item: str) -> str | None:
|
|
||||||
"""获取图标的下载链接"""
|
|
||||||
async for url in self._download_url_generator(item):
|
|
||||||
if url is not None:
|
|
||||||
return url
|
|
||||||
|
|
||||||
async def _get_img(self, overwrite: bool = False, *, item: str) -> Path | None:
|
|
||||||
"""获取图标"""
|
|
||||||
path = next(filter(lambda x: x.stem == item, self.path.iterdir()), None)
|
|
||||||
if not overwrite and path: # 如果需要下载的图标存在且不覆盖( overwrite )
|
|
||||||
return path.resolve()
|
|
||||||
if path is not None and path.exists():
|
|
||||||
if overwrite: # 如果覆盖
|
|
||||||
await async_remove(path) # 删除已存在的图标
|
|
||||||
else:
|
|
||||||
return path
|
|
||||||
# 依次从使用当前 assets class 中的爬虫下载图标,顺序为爬虫名的字母顺序
|
|
||||||
async for url in self._download_url_generator(item):
|
|
||||||
if url is not None:
|
|
||||||
path = self.path.joinpath(f"{item}{Path(url).suffix}")
|
|
||||||
if (result := await self._download(url, path)) is not None:
|
|
||||||
return result
|
|
||||||
|
|
||||||
@lru_cache
|
|
||||||
async def get_link(self, item: str) -> str | None:
|
|
||||||
"""获取相应图标链接"""
|
|
||||||
return await self._get_download_url(item)
|
|
||||||
|
|
||||||
def __getattr__(self, item: str):
|
|
||||||
"""魔法"""
|
|
||||||
if item in self.icon_types:
|
|
||||||
return partial(self._get_img, item=item)
|
|
||||||
object.__getattribute__(self, item)
|
|
||||||
return None
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@cached_property
|
|
||||||
def game_name_map(self) -> dict[str, str]:
|
|
||||||
"""游戏中的图标名"""
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@cached_property
|
|
||||||
def honey_name_map(self) -> dict[str, str]:
|
|
||||||
"""来自honey的图标名"""
|
|
||||||
|
|
||||||
|
|
||||||
class _AvatarAssets(_AssetsService):
|
class _AvatarAssets(_AssetsService):
|
||||||
enka: EnkaCharacterAsset | None
|
path: Path
|
||||||
|
data: List[AvatarIcon]
|
||||||
|
name_map: Dict[str, AvatarIcon]
|
||||||
|
id_map: Dict[int, AvatarIcon]
|
||||||
|
|
||||||
side: ICON_TYPE
|
def __init__(self, client: Optional[AsyncClient] = None) -> None:
|
||||||
"""侧视图图标"""
|
|
||||||
|
|
||||||
card: ICON_TYPE
|
|
||||||
"""卡片图标"""
|
|
||||||
|
|
||||||
gacha: ICON_TYPE
|
|
||||||
"""抽卡立绘"""
|
|
||||||
|
|
||||||
gacha_card: ICON_TYPE
|
|
||||||
"""抽卡卡片"""
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def game_name(self) -> str:
|
|
||||||
icon = "UI_AvatarIcon_"
|
|
||||||
if (avatar := AVATAR_DATA.get(str(self.id), None)) is not None:
|
|
||||||
icon = avatar["icon"]
|
|
||||||
else:
|
|
||||||
for aid, avatar in AVATAR_DATA.items():
|
|
||||||
if aid.startswith(str(self.id)):
|
|
||||||
icon = avatar["icon"]
|
|
||||||
return re.findall(r"UI_AvatarIcon_(.*)", icon)[0]
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def honey_id(self) -> str:
|
|
||||||
return HONEY_DATA["avatar"].get(str(self.id), "")[0]
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def enka(self) -> Optional[EnkaCharacterAsset]:
|
|
||||||
api = getattr(self, "_enka_api", None)
|
|
||||||
cid = getattr(self, "id", None)
|
|
||||||
return None if api is None or cid is None else api.character(cid)
|
|
||||||
|
|
||||||
def __init__(self, client: Optional[AsyncClient] = None, enka: Optional[EnkaAssets] = None):
|
|
||||||
super().__init__(client)
|
super().__init__(client)
|
||||||
self._enka_api = enka or DEFAULT_EnkaAssets
|
self.path = ASSETS_PATH.joinpath("avatar")
|
||||||
|
self.path.mkdir(exist_ok=True, parents=True)
|
||||||
|
|
||||||
def __call__(self, target: StrOrInt) -> "_AvatarAssets":
|
async def initialize(self):
|
||||||
temp = target
|
logger.info("正在初始化角色素材图标")
|
||||||
result = _AvatarAssets(self.client)
|
html = await self.client.get(DATA_MAP["avatar"])
|
||||||
if isinstance(target, str):
|
self.data = [AvatarIcon(**data) for data in html.json()]
|
||||||
try:
|
self.name_map = {icon.name: icon for icon in self.data}
|
||||||
target = int(target)
|
self.id_map = {icon.id: icon for icon in self.data}
|
||||||
except ValueError:
|
tasks = []
|
||||||
target = roleToId(target)
|
for icon in self.data:
|
||||||
if isinstance(target, str) or target is None:
|
base_path = self.path / f"{icon.id}"
|
||||||
raise AssetsCouldNotFound("找不到对应的角色", temp)
|
base_path.mkdir(exist_ok=True, parents=True)
|
||||||
result.id = target
|
gacha_path = base_path / "gacha.webp"
|
||||||
result._enka_api = self._enka_api
|
icon_path = base_path / "icon.webp"
|
||||||
return result
|
normal_path = base_path / "normal.webp"
|
||||||
|
if not gacha_path.exists():
|
||||||
|
tasks.append(self._download(icon.gacha, gacha_path))
|
||||||
|
if not icon_path.exists():
|
||||||
|
tasks.append(self._download(icon.icon_, icon_path))
|
||||||
|
if not normal_path.exists():
|
||||||
|
tasks.append(self._download(icon.normal, normal_path))
|
||||||
|
if len(tasks) >= 100:
|
||||||
|
await asyncio.gather(*tasks)
|
||||||
|
tasks = []
|
||||||
|
if tasks:
|
||||||
|
await asyncio.gather(*tasks)
|
||||||
|
logger.info("角色素材图标初始化完成")
|
||||||
|
|
||||||
async def _get_from_ambr(self, item: str) -> AsyncIterator[str | None]:
|
def get_path(self, icon: AvatarIcon, name: str) -> Path:
|
||||||
if item in {"icon", "side", "gacha"}:
|
path = self.path / f"{icon.id}"
|
||||||
yield str(AMBR_HOST.join(f"assets/UI/{self.game_name_map[item]}.png"))
|
path.mkdir(exist_ok=True, parents=True)
|
||||||
|
return path / f"{name}.webp"
|
||||||
|
|
||||||
async def _get_from_enka(self, item: str) -> AsyncIterator[str | None]:
|
def get_by_id(self, id_: int) -> Optional[AvatarIcon]:
|
||||||
if (item_id := self.game_name_map.get(item)) is not None:
|
return self.id_map.get(id_, None)
|
||||||
yield str(ENKA_HOST.join(f"ui/{item_id}.png"))
|
|
||||||
|
|
||||||
@cached_property
|
def get_by_name(self, name: str) -> Optional[AvatarIcon]:
|
||||||
def honey_name_map(self) -> dict[str, str]:
|
return self.name_map.get(name, None)
|
||||||
return {
|
|
||||||
"icon": f"{self.honey_id}_icon",
|
|
||||||
"side": f"{self.honey_id}_side_icon",
|
|
||||||
"gacha": f"{self.honey_id}_gacha_splash",
|
|
||||||
"gacha_card": f"{self.honey_id}_gacha_card",
|
|
||||||
}
|
|
||||||
|
|
||||||
@cached_property
|
def get_target(self, target: StrOrInt) -> Optional[AvatarIcon]:
|
||||||
def game_name_map(self) -> dict[str, str]:
|
if isinstance(target, int):
|
||||||
return {
|
return self.get_by_id(target)
|
||||||
"icon": f"UI_AvatarIcon_{self.game_name}",
|
elif isinstance(target, str):
|
||||||
"card": f"UI_AvatarIcon_{self.game_name}_Card",
|
return self.get_by_name(target)
|
||||||
"side": f"UI_AvatarIcon_Side_{self.game_name}",
|
return None
|
||||||
"gacha": f"UI_Gacha_AvatarImg_{self.game_name}",
|
|
||||||
}
|
def gacha(self, target: StrOrInt) -> Path:
|
||||||
|
icon = self.get_target(target)
|
||||||
|
if icon is None:
|
||||||
|
raise AssetsCouldNotFound("角色素材图标不存在", target)
|
||||||
|
return self.get_path(icon, "gacha")
|
||||||
|
|
||||||
|
def icon(self, target: StrOrInt) -> Path:
|
||||||
|
icon = self.get_target(target)
|
||||||
|
if icon is None:
|
||||||
|
raise AssetsCouldNotFound("角色素材图标不存在", target)
|
||||||
|
return self.get_path(icon, "icon")
|
||||||
|
|
||||||
|
def normal(self, target: StrOrInt) -> Path:
|
||||||
|
icon = self.get_target(target)
|
||||||
|
if icon is None:
|
||||||
|
raise AssetsCouldNotFound("角色素材图标不存在", target)
|
||||||
|
return self.get_path(icon, "normal")
|
||||||
|
|
||||||
|
|
||||||
class _WeaponAssets(_AssetsService):
|
class _LightConeAssets(_AssetsService):
|
||||||
awaken: ICON_TYPE
|
path: Path
|
||||||
"""突破后图标"""
|
data: List[LightConeIcon]
|
||||||
|
name_map: Dict[str, LightConeIcon]
|
||||||
|
id_map: Dict[int, LightConeIcon]
|
||||||
|
|
||||||
gacha: ICON_TYPE
|
def __init__(self, client: Optional[AsyncClient] = None) -> None:
|
||||||
"""抽卡立绘"""
|
super().__init__(client)
|
||||||
|
self.path = ASSETS_PATH.joinpath("light_cone")
|
||||||
|
self.path.mkdir(exist_ok=True, parents=True)
|
||||||
|
|
||||||
@cached_property
|
async def initialize(self):
|
||||||
def game_name(self) -> str:
|
logger.info("正在初始化光锥素材图标")
|
||||||
return re.findall(r"UI_EquipIcon_(.*)", WEAPON_DATA[str(self.id)]["icon"])[0]
|
html = await self.client.get(DATA_MAP["light_cone"])
|
||||||
|
self.data = [LightConeIcon(**data) for data in html.json()]
|
||||||
|
self.name_map = {icon.name: icon for icon in self.data}
|
||||||
|
self.id_map = {icon.id: icon for icon in self.data}
|
||||||
|
tasks = []
|
||||||
|
for icon in self.data:
|
||||||
|
base_path = self.path / f"{icon.id}"
|
||||||
|
base_path.mkdir(exist_ok=True, parents=True)
|
||||||
|
gacha_path = base_path / "gacha.webp"
|
||||||
|
icon_path = base_path / "icon.webp"
|
||||||
|
if not gacha_path.exists():
|
||||||
|
tasks.append(self._download(icon.gacha, gacha_path))
|
||||||
|
if not icon_path.exists():
|
||||||
|
tasks.append(self._download(icon.icon_, icon_path))
|
||||||
|
if len(tasks) >= 100:
|
||||||
|
await asyncio.gather(*tasks)
|
||||||
|
tasks = []
|
||||||
|
if tasks:
|
||||||
|
await asyncio.gather(*tasks)
|
||||||
|
logger.info("光锥素材图标初始化完成")
|
||||||
|
|
||||||
@cached_property
|
def get_path(self, icon: LightConeIcon, name: str) -> Path:
|
||||||
def game_name_map(self) -> dict[str, str]:
|
path = self.path / f"{icon.id}"
|
||||||
return {
|
path.mkdir(exist_ok=True, parents=True)
|
||||||
"icon": f"UI_EquipIcon_{self.game_name}",
|
return path / f"{name}.webp"
|
||||||
"awaken": f"UI_EquipIcon_{self.game_name}_Awaken",
|
|
||||||
"gacha": f"UI_Gacha_EquipIcon_{self.game_name}",
|
|
||||||
}
|
|
||||||
|
|
||||||
@cached_property
|
def get_by_id(self, id_: int) -> Optional[LightConeIcon]:
|
||||||
def honey_id(self) -> str:
|
return self.id_map.get(id_, None)
|
||||||
return f"i_n{self.id}"
|
|
||||||
|
|
||||||
def __call__(self, target: StrOrInt) -> Self:
|
def get_by_name(self, name: str) -> Optional[LightConeIcon]:
|
||||||
temp = target
|
return self.name_map.get(name, None)
|
||||||
result = _WeaponAssets(self.client)
|
|
||||||
if isinstance(target, str):
|
|
||||||
target = int(target) if target.isnumeric() else weaponToId(target)
|
|
||||||
if isinstance(target, str) or target is None:
|
|
||||||
raise AssetsCouldNotFound("找不到对应的武器", temp)
|
|
||||||
result.id = target
|
|
||||||
return result
|
|
||||||
|
|
||||||
async def _get_from_ambr(self, item: str) -> AsyncIterator[str | None]:
|
def get_target(self, target: StrOrInt) -> Optional[LightConeIcon]:
|
||||||
if item == "icon":
|
if isinstance(target, int):
|
||||||
yield str(AMBR_HOST.join(f"assets/UI/{self.game_name_map.get(item)}.png"))
|
return self.get_by_id(target)
|
||||||
|
elif isinstance(target, str):
|
||||||
|
return self.get_by_name(target)
|
||||||
|
return None
|
||||||
|
|
||||||
async def _get_from_enka(self, item: str) -> AsyncIterator[str | None]:
|
def gacha(self, target: StrOrInt) -> Path:
|
||||||
if item in self.game_name_map:
|
icon = self.get_target(target)
|
||||||
yield str(ENKA_HOST.join(f"ui/{self.game_name_map.get(item)}.png"))
|
if icon is None:
|
||||||
|
raise AssetsCouldNotFound("光锥素材图标不存在", target)
|
||||||
|
return self.get_path(icon, "gacha")
|
||||||
|
|
||||||
@cached_property
|
def icon(self, target: StrOrInt) -> Path:
|
||||||
def honey_name_map(self) -> dict[str, str]:
|
icon = self.get_target(target)
|
||||||
return {
|
if icon is None:
|
||||||
"icon": f"{self.honey_id}",
|
raise AssetsCouldNotFound("光锥素材图标不存在", target)
|
||||||
"awaken": f"{self.honey_id}_awaken_icon",
|
return self.get_path(icon, "icon")
|
||||||
"gacha": f"{self.honey_id}_gacha_icon",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class _MaterialAssets(_AssetsService):
|
|
||||||
@cached_property
|
|
||||||
def game_name(self) -> str:
|
|
||||||
return str(self.id)
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def game_name_map(self) -> dict[str, str]:
|
|
||||||
return {"icon": f"UI_ItemIcon_{self.game_name}"}
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def honey_name_map(self) -> dict[str, str]:
|
|
||||||
return {"icon": self.honey_id}
|
|
||||||
|
|
||||||
def __call__(self, target: StrOrInt) -> Self:
|
|
||||||
temp = target
|
|
||||||
result = _MaterialAssets(self.client)
|
|
||||||
if isinstance(target, str):
|
|
||||||
if target.isnumeric():
|
|
||||||
target = int(target)
|
|
||||||
else:
|
|
||||||
target = {v["name"]: int(k) for k, v in MATERIAL_DATA.items()}.get(target)
|
|
||||||
if isinstance(target, str) or target is None:
|
|
||||||
raise AssetsCouldNotFound("找不到对应的素材", temp)
|
|
||||||
result.id = target
|
|
||||||
return result
|
|
||||||
|
|
||||||
async def _get_from_ambr(self, item: str) -> AsyncIterator[str | None]:
|
|
||||||
if item == "icon":
|
|
||||||
yield str(AMBR_HOST.join(f"assets/UI/{self.game_name_map.get(item)}.png"))
|
|
||||||
|
|
||||||
async def _get_from_honey(self, item: str) -> AsyncIterator[str | None]:
|
|
||||||
yield HONEY_HOST.join(f"/img/{self.honey_name_map.get(item)}.png")
|
|
||||||
yield HONEY_HOST.join(f"/img/{self.honey_name_map.get(item)}.webp")
|
|
||||||
|
|
||||||
|
|
||||||
class _ArtifactAssets(_AssetsService):
|
|
||||||
flower: ICON_TYPE
|
|
||||||
"""生之花"""
|
|
||||||
|
|
||||||
plume: ICON_TYPE
|
|
||||||
"""死之羽"""
|
|
||||||
|
|
||||||
sands: ICON_TYPE
|
|
||||||
"""时之沙"""
|
|
||||||
|
|
||||||
goblet: ICON_TYPE
|
|
||||||
"""空之杯"""
|
|
||||||
|
|
||||||
circlet: ICON_TYPE
|
|
||||||
"""理之冠"""
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def honey_id(self) -> str:
|
|
||||||
return HONEY_DATA["artifact"][str(self.id)][0]
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def game_name(self) -> str:
|
|
||||||
return f"UI_RelicIcon_{self.id}"
|
|
||||||
|
|
||||||
async def _get_from_enka(self, item: str) -> AsyncIterator[str | None]:
|
|
||||||
if item in self.game_name_map:
|
|
||||||
yield str(ENKA_HOST.join(f"ui/{self.game_name_map.get(item)}.png"))
|
|
||||||
|
|
||||||
async def _get_from_ambr(self, item: str) -> AsyncIterator[str | None]:
|
|
||||||
if item in self.game_name_map:
|
|
||||||
yield str(AMBR_HOST.join(f"assets/UI/reliquary/{self.game_name_map[item]}.png"))
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def game_name_map(self) -> dict[str, str]:
|
|
||||||
return {
|
|
||||||
"icon": f"UI_RelicIcon_{self.id}_4",
|
|
||||||
"flower": f"UI_RelicIcon_{self.id}_4",
|
|
||||||
"plume": f"UI_RelicIcon_{self.id}_2",
|
|
||||||
"sands": f"UI_RelicIcon_{self.id}_5",
|
|
||||||
"goblet": f"UI_RelicIcon_{self.id}_1",
|
|
||||||
"circlet": f"UI_RelicIcon_{self.id}_3",
|
|
||||||
}
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def honey_name_map(self) -> dict[str, str]:
|
|
||||||
first_id = int(re.findall(r"\d+", HONEY_DATA["artifact"][str(self.id)][-1])[0])
|
|
||||||
return {
|
|
||||||
"icon": f"i_n{first_id + 30}",
|
|
||||||
"flower": f"i_n{first_id + 30}",
|
|
||||||
"plume": f"i_n{first_id + 10}",
|
|
||||||
"sands": f"i_n{first_id + 40}",
|
|
||||||
"goblet": f"i_n{first_id}",
|
|
||||||
"circlet": f"i_n{first_id + 20}",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class _NamecardAssets(_AssetsService):
|
|
||||||
enka: EnkaCharacterAsset | None
|
|
||||||
|
|
||||||
navbar: ICON_TYPE
|
|
||||||
"""好友名片背景"""
|
|
||||||
|
|
||||||
profile: ICON_TYPE
|
|
||||||
"""个人资料名片背景"""
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def honey_id(self) -> str:
|
|
||||||
return HONEY_DATA["namecard"][str(self.id)][0]
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def game_name(self) -> str:
|
|
||||||
return NAMECARD_DATA[str(self.id)]["icon"]
|
|
||||||
|
|
||||||
@lru_cache
|
|
||||||
def _get_id_from_avatar_id(self, avatar_id: Union[int, str]) -> int:
|
|
||||||
avatar_icon_name = AVATAR_DATA[str(avatar_id)]["icon"].replace("AvatarIcon", "NameCardIcon")
|
|
||||||
for namecard_id, namecard_data in NAMECARD_DATA.items():
|
|
||||||
if namecard_data["icon"] == avatar_icon_name:
|
|
||||||
return int(namecard_id)
|
|
||||||
raise ValueError(avatar_id)
|
|
||||||
|
|
||||||
def __call__(self, target: int) -> "_NamecardAssets":
|
|
||||||
result = _NamecardAssets(self.client)
|
|
||||||
if target > 10000000:
|
|
||||||
target = self._get_id_from_avatar_id(target)
|
|
||||||
result.id = target
|
|
||||||
result.enka = DEFAULT_EnkaAssets.namecards(target)
|
|
||||||
return result
|
|
||||||
|
|
||||||
async def _get_from_ambr(self, item: str) -> AsyncIterator[str | None]:
|
|
||||||
if item == "profile":
|
|
||||||
yield AMBR_HOST.join(f"assets/UI/namecard/{self.game_name_map[item]}.png.png")
|
|
||||||
|
|
||||||
async def _get_from_enka(self, item: str) -> AsyncIterator[str | None]:
|
|
||||||
if (url := getattr(self.enka, {"profile": "banner"}.get(item, item), None)) is not None:
|
|
||||||
yield url.url
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def game_name_map(self) -> dict[str, str]:
|
|
||||||
return {
|
|
||||||
"icon": self.game_name,
|
|
||||||
"navbar": NAMECARD_DATA[str(self.id)]["navbar"],
|
|
||||||
"profile": NAMECARD_DATA[str(self.id)]["profile"],
|
|
||||||
}
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def honey_name_map(self) -> dict[str, str]:
|
|
||||||
return {
|
|
||||||
"icon": self.honey_id,
|
|
||||||
"navbar": f"{self.honey_id}_back",
|
|
||||||
"profile": f"{self.honey_id}_profile",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class AssetsService(BaseService.Dependence):
|
class AssetsService(BaseService.Dependence):
|
||||||
@ -510,26 +208,19 @@ class AssetsService(BaseService.Dependence):
|
|||||||
若本地不存在,则从网络上下载;若存在,则返回其路径
|
若本地不存在,则从网络上下载;若存在,则返回其路径
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
client: Optional[AsyncClient] = None
|
||||||
|
|
||||||
avatar: _AvatarAssets
|
avatar: _AvatarAssets
|
||||||
"""角色"""
|
"""角色"""
|
||||||
|
|
||||||
weapon: _WeaponAssets
|
light_cone: _LightConeAssets
|
||||||
"""武器"""
|
"""光锥"""
|
||||||
|
|
||||||
material: _MaterialAssets
|
|
||||||
"""素材"""
|
|
||||||
|
|
||||||
artifact: _ArtifactAssets
|
|
||||||
"""圣遗物"""
|
|
||||||
|
|
||||||
namecard: _NamecardAssets
|
|
||||||
"""名片"""
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
for attr, assets_type_name in filter(
|
self.client = AsyncClient(timeout=60.0)
|
||||||
lambda x: (not x[0].startswith("_")) and x[1].endswith("Assets"), self.__annotations__.items()
|
self.avatar = _AvatarAssets(self.client)
|
||||||
):
|
self.light_cone = _LightConeAssets(self.client)
|
||||||
setattr(self, attr, globals()[assets_type_name]())
|
|
||||||
|
|
||||||
|
async def initialize(self): # pylint: disable=W0221
|
||||||
AssetsServiceType = TypeVar("AssetsServiceType", bound=_AssetsService)
|
await self.avatar.initialize()
|
||||||
|
await self.light_cone.initialize()
|
||||||
|
@ -1,167 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
from functools import partial
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Awaitable, Callable, ClassVar, TypeVar
|
|
||||||
|
|
||||||
from enkanetwork import Assets as EnkaAssets
|
|
||||||
from enkanetwork.model.assets import CharacterAsset as EnkaCharacterAsset
|
|
||||||
from httpx import AsyncClient
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from core.base_service import BaseService
|
|
||||||
from utils.typedefs import StrOrInt
|
|
||||||
|
|
||||||
__all__ = ("AssetsServiceType", "AssetsService", "AssetsServiceError", "AssetsCouldNotFound", "DEFAULT_EnkaAssets")
|
|
||||||
|
|
||||||
ICON_TYPE = Callable[[bool], Awaitable[Path | None]] | Callable[..., Awaitable[Path | None]]
|
|
||||||
DEFAULT_EnkaAssets: EnkaAssets
|
|
||||||
_GET_TYPE = partial | list[str] | int | str | ICON_TYPE | Path | AsyncClient | None | Self | dict[str, str]
|
|
||||||
|
|
||||||
class AssetsServiceError(Exception): ...
|
|
||||||
|
|
||||||
class AssetsCouldNotFound(AssetsServiceError):
|
|
||||||
message: str
|
|
||||||
target: str
|
|
||||||
def __init__(self, message: str, target: str): ...
|
|
||||||
|
|
||||||
class _AssetsService(ABC):
|
|
||||||
icon_types: ClassVar[list[str]]
|
|
||||||
id: int
|
|
||||||
type: str
|
|
||||||
|
|
||||||
icon: ICON_TYPE
|
|
||||||
"""图标"""
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
@property
|
|
||||||
def game_name(self) -> str:
|
|
||||||
"""游戏数据中的名称"""
|
|
||||||
@property
|
|
||||||
def honey_id(self) -> str:
|
|
||||||
"""当前资源在 Honey Impact 所对应的 ID"""
|
|
||||||
@property
|
|
||||||
def path(self) -> Path:
|
|
||||||
"""当前资源的文件夹"""
|
|
||||||
@property
|
|
||||||
def client(self) -> AsyncClient:
|
|
||||||
"""当前的 http client"""
|
|
||||||
def __init__(self, client: AsyncClient | None = None) -> None: ...
|
|
||||||
def __call__(self, target: int) -> Self:
|
|
||||||
"""用于生成与 target 对应的 assets"""
|
|
||||||
def __getattr__(self, item: str) -> _GET_TYPE:
|
|
||||||
"""魔法"""
|
|
||||||
async def get_link(self, item: str) -> str | None:
|
|
||||||
"""获取相应图标链接"""
|
|
||||||
@abstractmethod
|
|
||||||
@property
|
|
||||||
def game_name_map(self) -> dict[str, str]:
|
|
||||||
"""游戏中的图标名"""
|
|
||||||
@abstractmethod
|
|
||||||
@property
|
|
||||||
def honey_name_map(self) -> dict[str, str]:
|
|
||||||
"""来自honey的图标名"""
|
|
||||||
|
|
||||||
class _AvatarAssets(_AssetsService):
|
|
||||||
enka: EnkaCharacterAsset | None
|
|
||||||
|
|
||||||
side: ICON_TYPE
|
|
||||||
"""侧视图图标"""
|
|
||||||
|
|
||||||
card: ICON_TYPE
|
|
||||||
"""卡片图标"""
|
|
||||||
|
|
||||||
gacha: ICON_TYPE
|
|
||||||
"""抽卡立绘"""
|
|
||||||
|
|
||||||
gacha_card: ICON_TYPE
|
|
||||||
"""抽卡卡片"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def honey_name_map(self) -> dict[str, str]: ...
|
|
||||||
@property
|
|
||||||
def game_name_map(self) -> dict[str, str]: ...
|
|
||||||
@property
|
|
||||||
def enka(self) -> EnkaCharacterAsset | None: ...
|
|
||||||
def __init__(self, client: AsyncClient | None = None, enka: EnkaAssets | None = None) -> None: ...
|
|
||||||
def __call__(self, target: StrOrInt) -> Self: ...
|
|
||||||
def __getitem__(self, item: str) -> _GET_TYPE | EnkaCharacterAsset: ...
|
|
||||||
def game_name(self) -> str: ...
|
|
||||||
|
|
||||||
class _WeaponAssets(_AssetsService):
|
|
||||||
awaken: ICON_TYPE
|
|
||||||
"""突破后图标"""
|
|
||||||
|
|
||||||
gacha: ICON_TYPE
|
|
||||||
"""抽卡立绘"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def honey_name_map(self) -> dict[str, str]: ...
|
|
||||||
@property
|
|
||||||
def game_name_map(self) -> dict[str, str]: ...
|
|
||||||
def __call__(self, target: StrOrInt) -> Self: ...
|
|
||||||
def game_name(self) -> str: ...
|
|
||||||
|
|
||||||
class _MaterialAssets(_AssetsService):
|
|
||||||
@property
|
|
||||||
def honey_name_map(self) -> dict[str, str]: ...
|
|
||||||
@property
|
|
||||||
def game_name_map(self) -> dict[str, str]: ...
|
|
||||||
def __call__(self, target: StrOrInt) -> Self: ...
|
|
||||||
def game_name(self) -> str: ...
|
|
||||||
|
|
||||||
class _ArtifactAssets(_AssetsService):
|
|
||||||
flower: ICON_TYPE
|
|
||||||
"""生之花"""
|
|
||||||
|
|
||||||
plume: ICON_TYPE
|
|
||||||
"""死之羽"""
|
|
||||||
|
|
||||||
sands: ICON_TYPE
|
|
||||||
"""时之沙"""
|
|
||||||
|
|
||||||
goblet: ICON_TYPE
|
|
||||||
"""空之杯"""
|
|
||||||
|
|
||||||
circlet: ICON_TYPE
|
|
||||||
"""理之冠"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def honey_name_map(self) -> dict[str, str]: ...
|
|
||||||
@property
|
|
||||||
def game_name_map(self) -> dict[str, str]: ...
|
|
||||||
def game_name(self) -> str: ...
|
|
||||||
|
|
||||||
class _NamecardAssets(_AssetsService):
|
|
||||||
enka: EnkaCharacterAsset | None
|
|
||||||
|
|
||||||
navbar: ICON_TYPE
|
|
||||||
"""好友名片背景"""
|
|
||||||
|
|
||||||
profile: ICON_TYPE
|
|
||||||
"""个人资料名片背景"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def honey_name_map(self) -> dict[str, str]: ...
|
|
||||||
@property
|
|
||||||
def game_name_map(self) -> dict[str, str]: ...
|
|
||||||
def game_name(self) -> str: ...
|
|
||||||
|
|
||||||
class AssetsService(BaseService.Dependence):
|
|
||||||
avatar: _AvatarAssets
|
|
||||||
"""角色"""
|
|
||||||
|
|
||||||
weapon: _WeaponAssets
|
|
||||||
"""武器"""
|
|
||||||
|
|
||||||
material: _MaterialAssets
|
|
||||||
"""素材"""
|
|
||||||
|
|
||||||
artifact: _ArtifactAssets
|
|
||||||
"""圣遗物"""
|
|
||||||
|
|
||||||
namecard: _NamecardAssets
|
|
||||||
"""名片"""
|
|
||||||
|
|
||||||
AssetsServiceType = TypeVar("AssetsServiceType", bound=_AssetsService)
|
|
@ -23,12 +23,15 @@ class WikiService(BaseService):
|
|||||||
|
|
||||||
async def initialize(self) -> None:
|
async def initialize(self) -> None:
|
||||||
logger.info("正在加载 Wiki 数据")
|
logger.info("正在加载 Wiki 数据")
|
||||||
|
try:
|
||||||
await self.character.read()
|
await self.character.read()
|
||||||
await self.material.read()
|
await self.material.read()
|
||||||
await self.monster.read()
|
await self.monster.read()
|
||||||
await self.relic.read()
|
await self.relic.read()
|
||||||
await self.light_cone.read()
|
await self.light_cone.read()
|
||||||
await self.raider.read()
|
await self.raider.read()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("加载 Wiki 数据失败", exc_info=e)
|
||||||
logger.info("加载 Wiki 数据完成")
|
logger.info("加载 Wiki 数据完成")
|
||||||
|
|
||||||
async def refresh_wiki(self) -> NoReturn:
|
async def refresh_wiki(self) -> NoReturn:
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
from metadata.pool.pool_200 import POOL_200
|
from metadata.pool.pool_1 import POOL_1
|
||||||
from metadata.pool.pool_301 import POOL_301
|
from metadata.pool.pool_2 import POOL_2
|
||||||
from metadata.pool.pool_302 import POOL_302
|
from metadata.pool.pool_11 import POOL_11
|
||||||
|
from metadata.pool.pool_12 import POOL_12
|
||||||
|
|
||||||
|
|
||||||
def get_pool_by_id(pool_type):
|
def get_pool_by_id(pool_type):
|
||||||
if pool_type == 200:
|
if pool_type == 1:
|
||||||
return POOL_200
|
return POOL_1
|
||||||
if pool_type == 301:
|
elif pool_type == 2:
|
||||||
return POOL_301
|
return POOL_2
|
||||||
if pool_type == 302:
|
if pool_type == 11:
|
||||||
return POOL_302
|
return POOL_11
|
||||||
|
if pool_type == 12:
|
||||||
|
return POOL_12
|
||||||
return None
|
return None
|
||||||
|
1
metadata/pool/pool_1.py
Normal file
1
metadata/pool/pool_1.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
POOL_1 = [{"five": ["常驻池"], "four": [], "from": "2023-04-26 06:00:00", "name": "常驻池", "to": "2050-09-15 17:59:59"}]
|
9
metadata/pool/pool_11.py
Normal file
9
metadata/pool/pool_11.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
POOL_11 = [
|
||||||
|
{
|
||||||
|
"five": ["希尔"],
|
||||||
|
"four": ["娜塔莎", "佩拉", "虎克"],
|
||||||
|
"from": "2023-04-26 06:00:00",
|
||||||
|
"name": "蝶立锋锷",
|
||||||
|
"to": "2023-05-17 17:59:59",
|
||||||
|
},
|
||||||
|
]
|
9
metadata/pool/pool_12.py
Normal file
9
metadata/pool/pool_12.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
POOL_12 = [
|
||||||
|
{
|
||||||
|
"five": ["于夜色中"],
|
||||||
|
"four": ["一场术后对话", "晚安与睡颜", "鼹鼠党欢迎你"],
|
||||||
|
"from": "2023-04-26 06:00:00",
|
||||||
|
"name": "流光定影",
|
||||||
|
"to": "2023-05-17 17:59:59",
|
||||||
|
},
|
||||||
|
]
|
1
metadata/pool/pool_2.py
Normal file
1
metadata/pool/pool_2.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
POOL_2 = [{"five": ["新手池"], "four": [], "from": "2023-04-26 06:00:00", "name": "新手池", "to": "2050-09-15 17:59:59"}]
|
@ -1 +0,0 @@
|
|||||||
POOL_200 = [{"five": ["常驻池"], "four": [], "from": "2020-09-15 06:00:00", "name": "常驻池", "to": "2050-09-15 17:59:59"}]
|
|
@ -1,324 +0,0 @@
|
|||||||
POOL_301 = [
|
|
||||||
{
|
|
||||||
"five": ["纳西妲", "妮露"],
|
|
||||||
"four": ["久岐忍", "多莉", "莱依拉"],
|
|
||||||
"from": "2023-04-12 06:00:00",
|
|
||||||
"name": "月草的赐慧|翩舞歈莲",
|
|
||||||
"to": "2023-05-02 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["申鹤", "神里绫华"],
|
|
||||||
"four": ["米卡", "砂糖", "迪奥娜"],
|
|
||||||
"from": "2023-03-21 18:00:00",
|
|
||||||
"name": "孤辰茕怀|白鹭霜华",
|
|
||||||
"to": "2023-04-11 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["迪希雅", "赛诺"],
|
|
||||||
"four": ["班尼特", "芭芭拉", "柯莱"],
|
|
||||||
"from": "2023-03-01 06:00:00",
|
|
||||||
"name": "烈阳烁金|雳裁冥昭",
|
|
||||||
"to": "2023-03-21 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["胡桃", "夜兰"],
|
|
||||||
"four": ["行秋", "凝光", "北斗"],
|
|
||||||
"from": "2023-02-07 18:00:00",
|
|
||||||
"name": "赤团开时|素霓伣天",
|
|
||||||
"to": "2023-02-28 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["艾尔海森", "魈"],
|
|
||||||
"four": ["瑶瑶", "云堇", "辛焱"],
|
|
||||||
"from": "2023-01-18 06:00:00",
|
|
||||||
"name": "敕诫枢谋|烟火之邀",
|
|
||||||
"to": "2023-02-07 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["雷电将军", "神里绫人"],
|
|
||||||
"four": ["罗莎莉亚", "早柚", "九条裟罗"],
|
|
||||||
"from": "2022-12-27 18:00:00",
|
|
||||||
"name": "影寂天下人|苍流踏花",
|
|
||||||
"to": "2023-01-17 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["流浪者", "荒泷一斗"],
|
|
||||||
"four": ["珐露珊", "五郎", "烟绯"],
|
|
||||||
"from": "2022-12-07 06:00:00",
|
|
||||||
"name": "余火变相|鬼门斗宴",
|
|
||||||
"to": "2022-12-27 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["八重神子", "达达利亚"],
|
|
||||||
"four": ["莱依拉", "托马", "鹿野院平藏"],
|
|
||||||
"from": "2022-11-18 18:00:00",
|
|
||||||
"name": "华紫樱绯|暂别冬都",
|
|
||||||
"to": "2022-12-06 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["纳西妲", "宵宫"],
|
|
||||||
"four": ["雷泽", "诺艾尔", "班尼特"],
|
|
||||||
"from": "2022-11-2 06:00:00",
|
|
||||||
"name": "月草的赐慧|焰色天河",
|
|
||||||
"to": "2022-11-18 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["妮露", "阿贝多"],
|
|
||||||
"four": ["北斗", "芭芭拉", "香菱"],
|
|
||||||
"from": "2022-10-14 18:00:00",
|
|
||||||
"name": "翩舞歈莲|深秘之息",
|
|
||||||
"to": "2022-11-01 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["赛诺", "温迪"],
|
|
||||||
"four": ["久岐忍", "早柚", "坎蒂丝"],
|
|
||||||
"from": "2022-09-28 06:00:00",
|
|
||||||
"name": "雳裁冥昭|杯装之诗",
|
|
||||||
"to": "2022-10-14 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["甘雨", "心海"],
|
|
||||||
"four": ["行秋", "砂糖", "多莉"],
|
|
||||||
"from": "2022-09-09 18:00:00",
|
|
||||||
"name": "浮生孰来|浮岳虹珠",
|
|
||||||
"to": "2022-09-27 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["提纳里", "钟离"],
|
|
||||||
"four": ["云堇", "辛焱", "班尼特"],
|
|
||||||
"from": "2022-08-24 06:00:00",
|
|
||||||
"name": "巡御蘙荟|陵薮市朝",
|
|
||||||
"to": "2022-09-09 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["宵宫"],
|
|
||||||
"four": ["云堇", "辛焱", "班尼特"],
|
|
||||||
"from": "2022-08-02 18:00:00",
|
|
||||||
"name": "焰色天河",
|
|
||||||
"to": "2022-08-23 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["枫原万叶", "可莉"],
|
|
||||||
"four": ["凝光", "鹿野院平藏", "托马"],
|
|
||||||
"from": "2022-07-13 06:00:00",
|
|
||||||
"name": "红叶逐荒波",
|
|
||||||
"to": "2022-08-02 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["荒泷一斗"],
|
|
||||||
"four": ["烟绯", "芭芭拉", "诺艾尔"],
|
|
||||||
"from": "2022-06-21 18:00:00",
|
|
||||||
"name": "鬼门斗宴",
|
|
||||||
"to": "2022-07-12 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["夜兰", "魈"],
|
|
||||||
"four": ["烟绯", "芭芭拉", "诺艾尔"],
|
|
||||||
"from": "2022-05-31 06:00:00",
|
|
||||||
"name": "素霓伣天|烟火之邀",
|
|
||||||
"to": "2022-06-21 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["神里绫华"],
|
|
||||||
"four": ["罗莎莉亚", "早柚", "雷泽"],
|
|
||||||
"from": "2022-04-19 17:59:59",
|
|
||||||
"name": "白鹭之庭",
|
|
||||||
"to": "2022-05-31 05:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["神里绫人", "温迪"],
|
|
||||||
"four": ["香菱", "砂糖", "云堇"],
|
|
||||||
"from": "2022-03-30 06:00:00",
|
|
||||||
"name": "苍流踏花|杯装之诗",
|
|
||||||
"to": "2022-04-19 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["雷电将军", "珊瑚宫心海"],
|
|
||||||
"four": ["辛焱", "九条裟罗", "班尼特"],
|
|
||||||
"from": "2022-03-08 18:00:00",
|
|
||||||
"name": "影寂天下人|浮岳虹珠",
|
|
||||||
"to": "2022-03-29 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["八重神子"],
|
|
||||||
"four": ["菲谢尔", "迪奥娜", "托马"],
|
|
||||||
"from": "2022-02-16 06:00:00",
|
|
||||||
"name": "华紫樱绯",
|
|
||||||
"to": "2022-03-08 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["甘雨", "钟离"],
|
|
||||||
"four": ["行秋", "北斗", "烟绯"],
|
|
||||||
"from": "2022-01-25 18:00:00",
|
|
||||||
"name": "浮生孰来|陵薮市朝",
|
|
||||||
"to": "2022-02-15 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["申鹤", "魈"],
|
|
||||||
"four": ["云堇", "凝光", "重云"],
|
|
||||||
"from": "2022-01-05 06:00:00",
|
|
||||||
"name": "出尘入世|烟火之邀",
|
|
||||||
"to": "2022-01-25 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["荒泷一斗"],
|
|
||||||
"four": ["五郎", "芭芭拉", "香菱"],
|
|
||||||
"from": "2021-12-14 18:00:00",
|
|
||||||
"name": "鬼门斗宴",
|
|
||||||
"to": "2022-01-04 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["阿贝多", "优菈"],
|
|
||||||
"four": ["班尼特", "诺艾尔", "罗莎莉亚"],
|
|
||||||
"from": "2021-11-24 06:00:00",
|
|
||||||
"name": "深秘之息|浪涌之瞬",
|
|
||||||
"to": "2021-12-14 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["胡桃"],
|
|
||||||
"four": ["托马", "迪奥娜", "早柚"],
|
|
||||||
"from": "2021-11-02 18:00:00",
|
|
||||||
"name": "赤团开时",
|
|
||||||
"to": "2021-11-23 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["达达利亚"],
|
|
||||||
"four": ["凝光", "重云", "烟绯"],
|
|
||||||
"from": "2021-10-13 06:00:00",
|
|
||||||
"name": "暂别冬都",
|
|
||||||
"to": "2021-11-02 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["珊瑚宫心海"],
|
|
||||||
"four": ["罗莎莉亚", "北斗", "行秋"],
|
|
||||||
"from": "2021-09-21 18:00:00",
|
|
||||||
"name": "浮岳虹珠",
|
|
||||||
"to": "2021-10-12 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["雷电将军"],
|
|
||||||
"four": ["九条裟罗", "香菱", "砂糖"],
|
|
||||||
"from": "2021-09-01 06:00:00",
|
|
||||||
"name": "影寂天下人",
|
|
||||||
"to": "2021-09-21 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["宵宫"],
|
|
||||||
"four": ["早柚", "迪奥娜", "辛焱"],
|
|
||||||
"from": "2021-08-10 18:00:00",
|
|
||||||
"name": "焰色天河",
|
|
||||||
"to": "2021-08-31 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["神里绫华"],
|
|
||||||
"four": ["凝光", "重云", "烟绯"],
|
|
||||||
"from": "2021-07-21 06:00:00",
|
|
||||||
"name": "白鹭之庭",
|
|
||||||
"to": "2021-08-10 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["枫原万叶"],
|
|
||||||
"four": ["罗莎莉亚", "班尼特", "雷泽"],
|
|
||||||
"from": "2021-06-29 18:00:00",
|
|
||||||
"name": "红叶逐荒波",
|
|
||||||
"to": "2021-07-20 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["可莉"],
|
|
||||||
"four": ["芭芭拉", "砂糖", "菲谢尔"],
|
|
||||||
"from": "2021-06-09 06:00:00",
|
|
||||||
"name": "逃跑的太阳",
|
|
||||||
"to": "2021-06-29 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["优菈"],
|
|
||||||
"four": ["辛焱", "行秋", "北斗"],
|
|
||||||
"from": "2021-05-18 18:00:00",
|
|
||||||
"name": "浪沫的旋舞",
|
|
||||||
"to": "2021-06-08 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["钟离"],
|
|
||||||
"four": ["烟绯", "诺艾尔", "迪奥娜"],
|
|
||||||
"from": "2021-04-28 06:00:00",
|
|
||||||
"name": "陵薮市朝",
|
|
||||||
"to": "2021-05-18 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["达达利亚"],
|
|
||||||
"four": ["罗莎莉亚", "芭芭拉", "菲谢尔"],
|
|
||||||
"from": "2021-04-06 18:00:00",
|
|
||||||
"name": "暂别冬都",
|
|
||||||
"to": "2021-04-27 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["温迪"],
|
|
||||||
"four": ["砂糖", "雷泽", "诺艾尔"],
|
|
||||||
"from": "2021-03-17 06:00:00",
|
|
||||||
"name": "杯装之诗",
|
|
||||||
"to": "2021-04-06 15:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["胡桃"],
|
|
||||||
"four": ["行秋", "香菱", "重云"],
|
|
||||||
"from": "2021-03-02 18:00:00",
|
|
||||||
"name": "赤团开时",
|
|
||||||
"to": "2021-03-16 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["刻晴"],
|
|
||||||
"four": ["凝光", "班尼特", "芭芭拉"],
|
|
||||||
"from": "2021-02-17 18:00:00",
|
|
||||||
"name": "鱼龙灯昼",
|
|
||||||
"to": "2021-03-02 15:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["魈"],
|
|
||||||
"four": ["迪奥娜", "北斗", "辛焱"],
|
|
||||||
"from": "2021-02-03 06:00:00",
|
|
||||||
"name": "烟火之邀",
|
|
||||||
"to": "2021-02-17 15:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["甘雨"],
|
|
||||||
"four": ["香菱", "行秋", "诺艾尔"],
|
|
||||||
"from": "2021-01-12 18:00:00",
|
|
||||||
"name": "浮生孰来",
|
|
||||||
"to": "2021-02-02 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["阿贝多"],
|
|
||||||
"four": ["菲谢尔", "砂糖", "班尼特"],
|
|
||||||
"from": "2020-12-23 06:00:00",
|
|
||||||
"name": "深秘之息",
|
|
||||||
"to": "2021-01-12 15:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["钟离"],
|
|
||||||
"four": ["辛焱", "雷泽", "重云"],
|
|
||||||
"from": "2020-12-01 18:00:00",
|
|
||||||
"name": "陵薮市朝",
|
|
||||||
"to": "2020-12-22 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["达达利亚"],
|
|
||||||
"four": ["迪奥娜", "北斗", "凝光"],
|
|
||||||
"from": "2020-11-11 06:00:00",
|
|
||||||
"name": "暂别冬都",
|
|
||||||
"to": "2020-12-01 15:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["可莉"],
|
|
||||||
"four": ["行秋", "诺艾尔", "砂糖"],
|
|
||||||
"from": "2020-10-20 18:00:00",
|
|
||||||
"name": "闪焰的驻足",
|
|
||||||
"to": "2020-11-10 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["温迪"],
|
|
||||||
"four": ["芭芭拉", "菲谢尔", "香菱"],
|
|
||||||
"from": "2020-9-28 06:00:00",
|
|
||||||
"name": "杯装之诗",
|
|
||||||
"to": "2020-10-18 17:59:59",
|
|
||||||
},
|
|
||||||
]
|
|
@ -1,317 +0,0 @@
|
|||||||
POOL_302 = [
|
|
||||||
{
|
|
||||||
"five": ["千夜浮梦", "圣显之钥"],
|
|
||||||
"four": ["西福斯的月光", "西风大剑", "匣里灭辰", "祭礼残章", "绝弦"],
|
|
||||||
"from": "2023-04-12 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2023-05-02 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["息灾", "雾切之回光"],
|
|
||||||
"four": ["暗巷的酒与诗", "祭礼剑", "钟剑", "西风长枪", "西风猎弓"],
|
|
||||||
"from": "2023-03-21 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2023-04-11 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["苇海信标", "赤沙之杖"],
|
|
||||||
"four": ["暗巷闪光", "暗巷猎手", "祭礼大剑", "匣里灭辰", "昭心"],
|
|
||||||
"from": "2023-03-01 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2023-03-21 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["护摩之杖", "若水"],
|
|
||||||
"four": ["千岩古剑", "西风剑", "匣里灭辰", "西风秘典", "弓藏"],
|
|
||||||
"from": "2023-02-07 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2023-02-28 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["裁叶萃光", "和璞鸢"],
|
|
||||||
"four": ["千岩长枪", "笛剑", "雨裁", "流浪乐章", "祭礼弓"],
|
|
||||||
"from": "2023-01-18 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2023-02-07 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["薙草之稻光", "波乱月白经津"],
|
|
||||||
"four": ["恶王丸", "曚云之月", "匣里龙吟", "西风长枪", "祭礼残章"],
|
|
||||||
"from": "2022-12-27 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2023-01-17 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["图莱杜拉的回忆", "赤角石溃杵"],
|
|
||||||
"four": ["祭礼剑", "西风大剑", "断浪长鳍", "昭心", "西风猎弓"],
|
|
||||||
"from": "2022-12-07 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-12-27 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["神乐之真意", "冬极白星"],
|
|
||||||
"four": ["西风剑", "钟剑", "匣里灭辰", "西风秘典", "绝弦"],
|
|
||||||
"from": "2022-11-18 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-12-06 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["千夜浮梦", "飞雷之弦振"],
|
|
||||||
"four": ["笛剑", "祭礼大剑", "西风长枪", "流浪乐章", "弓藏"],
|
|
||||||
"from": "2022-11-2 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-11-18 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["圣显之钥", "磐岩结绿"],
|
|
||||||
"four": ["西福斯的月光", "雨裁", "匣里灭辰", "流浪的晚星", "祭礼弓"],
|
|
||||||
"from": "2022-10-14 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-11-01 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["赤沙之杖", "终末嗟叹之诗"],
|
|
||||||
"four": ["匣里龙吟", "玛海菈的水色", "西风长枪", "祭礼残章", "西风猎弓"],
|
|
||||||
"from": "2022-09-28 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-10-14 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["阿莫斯之弓", "不灭月华"],
|
|
||||||
"four": ["祭礼剑", "西风大剑", "匣里灭辰", "昭心", "弓藏"],
|
|
||||||
"from": "2022-09-09 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-09-27 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["猎人之径", "贯虹之槊"],
|
|
||||||
"four": ["西风剑", "钟剑", "西风长枪", "西风秘典", "绝弦"],
|
|
||||||
"from": "2022-08-24 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-09-09 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["飞雷之弦振", "斫峰之刃"],
|
|
||||||
"four": ["暗巷的酒与诗", "暗巷猎手", "笛剑", "祭礼大剑", "匣里灭辰"],
|
|
||||||
"from": "2022-08-02 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-08-23 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["苍古自由之誓", "四风原典"],
|
|
||||||
"four": ["千岩古剑", "匣里龙吟", "匣里灭辰", "祭礼残章", "绝弦"],
|
|
||||||
"from": "2022-07-13 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-08-02 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["赤角石溃杵", "尘世之锁"],
|
|
||||||
"four": ["千岩古剑", "匣里龙吟", "匣里灭辰", "祭礼残章", "绝弦"],
|
|
||||||
"from": "2022-06-21 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-07-12 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["若水", "和璞鸢"],
|
|
||||||
"four": ["千岩长枪", "祭礼剑", "西风大剑", "昭心", "祭礼弓"],
|
|
||||||
"from": "2022-05-31 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-06-21 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["雾切之回光", "无工之剑"],
|
|
||||||
"four": ["西风剑", "钟剑", "西风长枪", "西风秘典", "西风猎弓"],
|
|
||||||
"from": "2022-04-19 17:59:59",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-05-31 05:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["波乱月白经津", "终末嗟叹之诗"],
|
|
||||||
"four": ["弓藏", "笛剑", "流浪乐章", "匣里灭辰", "祭礼大剑"],
|
|
||||||
"from": "2022-03-30 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-04-19 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["薙草之稻光", "不灭月华"],
|
|
||||||
"four": ["恶王丸", "曚云之月", "匣里龙吟", "西风长枪", "祭礼残章"],
|
|
||||||
"from": "2022-03-08 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-03-29 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["神乐之真意", "磐岩结绿"],
|
|
||||||
"four": ["祭礼剑", "雨裁", "断浪长鳍", "昭心", "绝弦"],
|
|
||||||
"from": "2022-02-16 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-03-08 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["贯虹之槊", "阿莫斯之弓"],
|
|
||||||
"four": ["西风剑", "千岩古剑", "匣里灭辰", "西风秘典", "祭礼弓"],
|
|
||||||
"from": "2022-01-25 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-02-15 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["息灾", "和璞鸢"],
|
|
||||||
"four": ["笛剑", "西风大剑", "千岩长枪", "流浪乐章", "西风猎弓"],
|
|
||||||
"from": "2022-01-05 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-01-25 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["赤角石溃杵", "天空之翼"],
|
|
||||||
"four": ["暗巷闪光", "钟剑", "西风长枪", "祭礼残章", "幽夜华尔兹"],
|
|
||||||
"from": "2021-12-14 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2022-01-04 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["苍古自由之誓", "松籁响起之时"],
|
|
||||||
"four": ["匣里龙吟", "祭礼大剑", "匣里灭辰", "暗巷的酒与诗", "暗巷猎手"],
|
|
||||||
"from": "2021-11-24 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-12-14 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["护摩之杖", "终末嗟叹之诗"],
|
|
||||||
"four": ["祭礼剑", "雨裁", "断浪长鳍", "流浪乐章", "曚云之月"],
|
|
||||||
"from": "2021-11-02 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-11-23 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["冬极白星", "尘世之锁"],
|
|
||||||
"four": ["西风剑", "恶王丸", "西风长枪", "昭心", "弓藏"],
|
|
||||||
"from": "2021-10-13 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-11-02 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["不灭月华", "磐岩结绿"],
|
|
||||||
"four": ["笛剑", "西风大剑", "匣里灭辰", "西风秘典", "绝弦"],
|
|
||||||
"from": "2021-09-21 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-10-12 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["薙草之稻光", "无工之剑"],
|
|
||||||
"four": ["匣里龙吟", "钟剑", "西风长枪", "流浪乐章", "祭礼弓"],
|
|
||||||
"from": "2021-09-01 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-09-21 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["飞雷之弦振", "天空之刃"],
|
|
||||||
"four": ["祭礼剑", "雨裁", "匣里灭辰", "祭礼残章", "西风猎弓"],
|
|
||||||
"from": "2021-08-10 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-08-31 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["雾切之回光", "天空之脊"],
|
|
||||||
"four": ["西风剑", "祭礼大剑", "西风长枪", "西风秘典", "绝弦"],
|
|
||||||
"from": "2021-07-21 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-08-10 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["苍古自由之誓", "天空之卷"],
|
|
||||||
"four": ["暗巷闪光", "西风大剑", "匣里灭辰", "暗巷的酒与诗", "暗巷猎手"],
|
|
||||||
"from": "2021-06-29 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-07-20 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["天空之傲", "四风原典"],
|
|
||||||
"four": ["匣里龙吟", "钟剑", "西风长枪", "流浪乐章", "幽夜华尔兹"],
|
|
||||||
"from": "2021-06-09 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-06-29 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["松籁响起之时", "风鹰剑"],
|
|
||||||
"four": ["祭礼剑", "雨裁", "匣里灭辰", "祭礼残章", "弓藏"],
|
|
||||||
"from": "2021-05-18 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-06-08 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["斫峰之刃", "尘世之锁"],
|
|
||||||
"four": ["笛剑", "千岩古剑", "祭礼弓", "昭心", "千岩长枪"],
|
|
||||||
"from": "2021-04-28 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-05-18 17:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["天空之翼", "四风原典"],
|
|
||||||
"four": ["西风剑", "祭礼大剑", "暗巷猎手", "西风秘典", "西风长枪"],
|
|
||||||
"from": "2021-04-06 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-04-27 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["终末嗟叹之诗", "天空之刃"],
|
|
||||||
"four": ["暗巷闪光", "西风大剑", "西风猎弓", "暗巷的酒与诗", "匣里灭辰"],
|
|
||||||
"from": "2021-03-17 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-04-06 15:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["护摩之杖", "狼的末路"],
|
|
||||||
"four": ["匣里龙吟", "千岩古剑", "祭礼弓", "流浪乐章", "千岩长枪"],
|
|
||||||
"from": "2021-02-23 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-03-16 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["磐岩结绿", "和璞鸢"],
|
|
||||||
"four": ["笛剑", "祭礼大剑", "弓藏", "昭心", "西风长枪"],
|
|
||||||
"from": "2021-02-03 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-02-23 15:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["阿莫斯之弓", "天空之傲"],
|
|
||||||
"four": ["祭礼剑", "钟剑", "匣里灭辰", "昭心", "西风猎弓"],
|
|
||||||
"from": "2021-01-12 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-02-02 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["斫峰之刃", "天空之卷"],
|
|
||||||
"four": ["西风剑", "西风大剑", "西风长枪", "祭礼残章", "绝弦"],
|
|
||||||
"from": "2020-12-23 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2021-01-12 15:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["贯虹之槊", "无工之剑"],
|
|
||||||
"four": ["匣里龙吟", "钟剑", "西风秘典", "西风猎弓", "匣里灭辰"],
|
|
||||||
"from": "2020-12-01 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2020-12-22 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["天空之翼", "尘世之锁"],
|
|
||||||
"four": ["笛剑", "雨裁", "昭心", "弓藏", "西风长枪"],
|
|
||||||
"from": "2020-11-11 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2020-12-01 15:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["四风原典", "狼的末路"],
|
|
||||||
"four": ["祭礼剑", "祭礼大剑", "祭礼残章", "祭礼弓", "匣里灭辰"],
|
|
||||||
"from": "2020-10-20 18:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2020-11-10 14:59:59",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"five": ["风鹰剑", "阿莫斯之弓"],
|
|
||||||
"four": ["祭礼剑", "祭礼大剑", "祭礼残章", "祭礼弓", "匣里灭辰"],
|
|
||||||
"from": "2020-09-28 06:00:00",
|
|
||||||
"name": "神铸赋形",
|
|
||||||
"to": "2020-10-18 17:59:59",
|
|
||||||
},
|
|
||||||
]
|
|
@ -5,700 +5,107 @@ from typing import List
|
|||||||
|
|
||||||
from metadata.genshin import WEAPON_DATA
|
from metadata.genshin import WEAPON_DATA
|
||||||
|
|
||||||
__all__ = ["roles", "weapons", "roleToId", "roleToName", "weaponToName", "weaponToId", "not_real_roles", "roleToTag"]
|
__all__ = ["roles", "light_cones", "roleToId", "roleToName", "lightConeToName", "lightConeToId", "not_real_roles", "roleToTag"]
|
||||||
|
|
||||||
# noinspection SpellCheckingInspection
|
# noinspection SpellCheckingInspection
|
||||||
roles = {
|
roles = {
|
||||||
20000000: [
|
1001: ['三月七'],
|
||||||
"旅行者",
|
1002: ['丹恒'],
|
||||||
"主角",
|
1003: ['姬子'],
|
||||||
"卑鄙的外乡人",
|
1004: ['瓦尔特'],
|
||||||
"荣誉骑士",
|
1005: ['卡芙卡'],
|
||||||
"爷",
|
1006: ['银狼'],
|
||||||
"履刑者",
|
1008: ['阿兰'],
|
||||||
"人之子",
|
1009: ['艾丝妲'],
|
||||||
"命定之人",
|
1013: ['黑塔'],
|
||||||
"荣誉骑士",
|
1101: ['布洛妮娅'],
|
||||||
"小可爱", # 丽莎
|
1102: ['希儿'],
|
||||||
"小家伙", # 八重神子
|
1103: ['希露瓦'],
|
||||||
"金发异乡人",
|
1104: ['杰帕德'],
|
||||||
"大黄金钓鱼手", # 派蒙
|
1105: ['娜塔莎'],
|
||||||
"黄毛阿姨",
|
1106: ['佩拉'],
|
||||||
"黄毛叔叔",
|
1107: ['克拉拉'],
|
||||||
"大黄倭瓜那菈",
|
1108: ['桑博'],
|
||||||
],
|
1109: ['虎克'],
|
||||||
10000002: ["神里绫华", "ayaka", "kamisato ayaka", "神里", "绫华", "神里凌华", "凌华", "白鹭公主", "神里大小姐", "冰骗骗花", "龟龟"],
|
1201: ['青雀'],
|
||||||
10000003: ["琴", "jean", "团长", "代理团长", "琴团长", "蒲公英骑士", "蒙德砍王", "骑士团的魂"],
|
1202: ['停云'],
|
||||||
10000005: ["空", "aether", "男主", "男主角", "龙哥", "空哥", "王子"],
|
1203: ['罗刹'],
|
||||||
10000006: ["丽莎", "lisa", "图书管理员", "图书馆管理员", "蔷薇魔女"],
|
1204: ['景元'],
|
||||||
10000007: ["荧", "lumine", "女主", "女主角", "莹", "萤", "黄毛阿姨", "荧妹", "公主殿下"],
|
1206: ['素裳'],
|
||||||
10000014: ["芭芭拉", "barbara", "巴巴拉", "拉粑粑", "拉巴巴", "内鬼", "加湿器", "闪耀偶像", "偶像", "蒙德辣王"],
|
1209: ['彦卿'],
|
||||||
10000015: ["凯亚", "kaeya", "盖亚", "凯子哥", "凯鸭", "矿工", "矿工头子", "骑兵队长", "凯子", "凝冰渡海真君", "花脸猫"],
|
1211: ['白露'],
|
||||||
10000016: [
|
8004: ['开拓者'],
|
||||||
"迪卢克",
|
|
||||||
"diluc",
|
|
||||||
"卢姥爷",
|
|
||||||
"姥爷",
|
|
||||||
"卢老爷",
|
|
||||||
"卢锅巴",
|
|
||||||
"正义人",
|
|
||||||
"正e人",
|
|
||||||
"正E人",
|
|
||||||
"卢本伟",
|
|
||||||
"暗夜英雄",
|
|
||||||
"卢卢伯爵",
|
|
||||||
"落魄了",
|
|
||||||
"落魄了家人们",
|
|
||||||
"哦哦哦",
|
|
||||||
"前夫哥",
|
|
||||||
"在此烧鸟真君",
|
|
||||||
"E键三连真君",
|
|
||||||
],
|
|
||||||
10000020: [
|
|
||||||
"雷泽",
|
|
||||||
"razor",
|
|
||||||
"狼少年",
|
|
||||||
"狼崽子",
|
|
||||||
"狼崽",
|
|
||||||
"卢皮卡",
|
|
||||||
"小狼",
|
|
||||||
"小狼狼",
|
|
||||||
"小狼狗",
|
|
||||||
"小赛诺",
|
|
||||||
"替身使者",
|
|
||||||
"须佐狼乎",
|
|
||||||
"蒙德砍王",
|
|
||||||
"炸矿之星",
|
|
||||||
],
|
|
||||||
10000021: [
|
|
||||||
"安柏",
|
|
||||||
"amber",
|
|
||||||
"安伯",
|
|
||||||
"兔兔伯爵",
|
|
||||||
"飞行冠军",
|
|
||||||
"侦查骑士",
|
|
||||||
"侦察骑士",
|
|
||||||
"点火姬",
|
|
||||||
"点火机",
|
|
||||||
"打火机",
|
|
||||||
"打火姬",
|
|
||||||
"燃炬焚棘真君",
|
|
||||||
"初代目提瓦特第一火弓",
|
|
||||||
],
|
|
||||||
10000022: [
|
|
||||||
"温迪",
|
|
||||||
"venti",
|
|
||||||
"barbatos",
|
|
||||||
"温蒂",
|
|
||||||
"风神",
|
|
||||||
"卖唱的",
|
|
||||||
"巴巴托斯",
|
|
||||||
"巴巴脱丝",
|
|
||||||
"芭芭托斯",
|
|
||||||
"芭芭脱丝",
|
|
||||||
"干点正事",
|
|
||||||
"不干正事",
|
|
||||||
"吟游诗人",
|
|
||||||
"诶嘿",
|
|
||||||
"唉嘿",
|
|
||||||
"摸鱼",
|
|
||||||
"最弱最丢人的七神",
|
|
||||||
"卖唱的大哥哥",
|
|
||||||
"巴巴托斯大人",
|
|
||||||
"欸嘿聚怪真君",
|
|
||||||
"荻花洲的吹笛人",
|
|
||||||
"直升机",
|
|
||||||
],
|
|
||||||
10000023: [
|
|
||||||
"香菱",
|
|
||||||
"xiangling",
|
|
||||||
"香玲",
|
|
||||||
"锅巴",
|
|
||||||
"厨师",
|
|
||||||
"万民堂厨师",
|
|
||||||
"香师傅",
|
|
||||||
"哪吒",
|
|
||||||
"锅巴发射器",
|
|
||||||
"无敌风火轮真君",
|
|
||||||
"舌尖上的璃月",
|
|
||||||
"提瓦特枪王",
|
|
||||||
],
|
|
||||||
10000024: ["北斗", "beidou", "大姐头", "大姐", "无冕的龙王", "稻妻人形继电石"],
|
|
||||||
10000025: ["行秋", "xingqiu", "秋秋人", "秋妹妹", "书呆子", "水神", "飞云商会二少爷", "秋秋人", "6星水神", "枕玉老师"],
|
|
||||||
10000026: [
|
|
||||||
"魈",
|
|
||||||
"xiao",
|
|
||||||
"杏仁豆腐",
|
|
||||||
"打桩机",
|
|
||||||
"插秧",
|
|
||||||
"三眼五显仙人",
|
|
||||||
"三眼五显真人",
|
|
||||||
"降魔大圣",
|
|
||||||
"护法夜叉",
|
|
||||||
"快乐风男",
|
|
||||||
"无聊",
|
|
||||||
"靖妖傩舞",
|
|
||||||
"矮子仙人",
|
|
||||||
"三点五尺仙人",
|
|
||||||
"跳跳虎",
|
|
||||||
"护法夜叉大将",
|
|
||||||
"金鹏大将",
|
|
||||||
"这里无能真君",
|
|
||||||
"抬头不见低头见真君",
|
|
||||||
"跳跳虎",
|
|
||||||
"随叫随到真君",
|
|
||||||
"成天冷着脸的帅气小哥",
|
|
||||||
],
|
|
||||||
10000027: ["凝光", "ningguang", "富婆", "天权", "天权星", "寻山见矿真君"],
|
|
||||||
10000029: [
|
|
||||||
"可莉",
|
|
||||||
"klee",
|
|
||||||
"嘟嘟可",
|
|
||||||
"火花骑士",
|
|
||||||
"蹦蹦炸弹",
|
|
||||||
"炸鱼",
|
|
||||||
"放火烧山",
|
|
||||||
"放火烧山真君",
|
|
||||||
"蒙德最强战力",
|
|
||||||
"逃跑的太阳",
|
|
||||||
"啦啦啦",
|
|
||||||
"哒哒哒",
|
|
||||||
"炸弹人",
|
|
||||||
"禁闭室",
|
|
||||||
"艾莉丝的女儿",
|
|
||||||
"阿贝多的义妹",
|
|
||||||
"火化骑士",
|
|
||||||
"炸鱼禁闭真君",
|
|
||||||
"蒙德小坦克",
|
|
||||||
"骑士团团宠",
|
|
||||||
],
|
|
||||||
10000030: [
|
|
||||||
"钟离",
|
|
||||||
"zhongli",
|
|
||||||
"morax",
|
|
||||||
"摩拉克斯",
|
|
||||||
"岩王爷",
|
|
||||||
"岩神",
|
|
||||||
"钟师傅",
|
|
||||||
"天动万象",
|
|
||||||
"岩王帝君",
|
|
||||||
"未来可期",
|
|
||||||
"帝君",
|
|
||||||
"契约之神",
|
|
||||||
"社会废人",
|
|
||||||
"未来可期真君",
|
|
||||||
"废人养成器",
|
|
||||||
"听书人",
|
|
||||||
],
|
|
||||||
10000031: ["菲谢尔", "fischl", "皇女", "小艾米", "小艾咪", "奥兹", "断罪皇女", "中二病", "中二少女", "中二皇女", "奥兹发射器"],
|
|
||||||
10000032: ["班尼特", "bennett", "点赞哥", "点赞", "倒霉少年", "倒霉蛋", "霹雳闪雷真君", "班神", "班爷", "倒霉", "火神", "六星真神"],
|
|
||||||
10000033: [
|
|
||||||
"达达利亚",
|
|
||||||
"tartaglia",
|
|
||||||
"childe",
|
|
||||||
"ajax",
|
|
||||||
"达达鸭",
|
|
||||||
"达达利鸭",
|
|
||||||
"公子",
|
|
||||||
"玩具销售员",
|
|
||||||
"玩具推销员",
|
|
||||||
"钱包",
|
|
||||||
"鸭鸭",
|
|
||||||
"愚人众末席",
|
|
||||||
"至冬国驻璃月港玩具推销员主管",
|
|
||||||
"钟离的钱包",
|
|
||||||
"近战弓兵",
|
|
||||||
"在蒙德认识的冒险家",
|
|
||||||
"永别冬都",
|
|
||||||
"汤达人",
|
|
||||||
"大貉妖处理专家",
|
|
||||||
],
|
|
||||||
10000034: ["诺艾尔", "noelle", "女仆", "高达", "岩王帝姬", "山吹", "冰萤术士", "岩王帝姬"],
|
|
||||||
10000035: ["七七", "qiqi", "僵尸", "肚饿真君", "度厄真君", "77", "起死回骸童子", "救苦度厄真君", "椰羊创始人", "不卜庐砍王", "不卜庐剑圣"],
|
|
||||||
10000036: ["重云", "chongyun", "纯阳之体", "冰棍", "驱邪世家", "大外甥"],
|
|
||||||
10000037: ["甘雨", "ganyu", "椰羊", "椰奶", "鸡腿猎人", "咕噜咕噜滚下山真君", "肝雨", "走路上山真君"],
|
|
||||||
10000038: [
|
|
||||||
"阿贝多",
|
|
||||||
"albedo",
|
|
||||||
"可莉哥哥",
|
|
||||||
"升降机",
|
|
||||||
"升降台",
|
|
||||||
"电梯",
|
|
||||||
"白垩之子",
|
|
||||||
"贝爷",
|
|
||||||
"白垩",
|
|
||||||
"阿贝少",
|
|
||||||
"花呗多",
|
|
||||||
"阿贝夕",
|
|
||||||
"abd",
|
|
||||||
"阿师傅",
|
|
||||||
"小王子",
|
|
||||||
"调查小队队长",
|
|
||||||
"西风骑士团首席炼金术师",
|
|
||||||
"白垩老师",
|
|
||||||
"电梯人",
|
|
||||||
"蒙德岩神",
|
|
||||||
"平平无奇",
|
|
||||||
"蒙德NPC",
|
|
||||||
],
|
|
||||||
10000039: ["迪奥娜", "diona", "迪欧娜", "dio", "dio娜", "冰猫", "猫猫", "猫娘", "喵喵", "调酒师"],
|
|
||||||
10000041: [
|
|
||||||
"莫娜",
|
|
||||||
"mona",
|
|
||||||
"穷鬼",
|
|
||||||
"穷光蛋",
|
|
||||||
"穷",
|
|
||||||
"莫纳",
|
|
||||||
"占星术士",
|
|
||||||
"占星师",
|
|
||||||
"讨龙真君",
|
|
||||||
"半部讨龙真君",
|
|
||||||
"阿斯托洛吉斯·莫娜·梅姬斯图斯",
|
|
||||||
"astrologist mona megistus",
|
|
||||||
"梅姬斯图斯",
|
|
||||||
"梅姬斯图斯卿",
|
|
||||||
"梅姬",
|
|
||||||
"半部讨龙真君",
|
|
||||||
],
|
|
||||||
10000042: [
|
|
||||||
"刻晴",
|
|
||||||
"keqing",
|
|
||||||
"刻情",
|
|
||||||
"氪晴",
|
|
||||||
"刻师傅",
|
|
||||||
"刻师父",
|
|
||||||
"牛杂",
|
|
||||||
"牛杂师傅",
|
|
||||||
"斩尽牛杂",
|
|
||||||
"免疫",
|
|
||||||
"免疫免疫",
|
|
||||||
"屁斜剑法",
|
|
||||||
"玉衡星",
|
|
||||||
"阿晴",
|
|
||||||
"啊晴",
|
|
||||||
"得不到的女人",
|
|
||||||
"金丝虾球真君",
|
|
||||||
"璃月雷神",
|
|
||||||
"刻猫猫",
|
|
||||||
],
|
|
||||||
10000043: ["砂糖", "sucrose", "雷莹术士", "雷萤术士", "雷荧术士"],
|
|
||||||
10000044: ["辛焱", "xinyan", "辛炎", "黑妹", "摇滚"],
|
|
||||||
10000045: [
|
|
||||||
"罗莎莉亚",
|
|
||||||
"rosaria",
|
|
||||||
"罗莎莉娅",
|
|
||||||
"白色史莱姆",
|
|
||||||
"白史莱姆",
|
|
||||||
"修女",
|
|
||||||
"罗莎利亚",
|
|
||||||
"罗莎利娅",
|
|
||||||
"罗沙莉亚",
|
|
||||||
"罗沙莉娅",
|
|
||||||
"罗沙利亚",
|
|
||||||
"罗沙利娅",
|
|
||||||
"萝莎莉亚",
|
|
||||||
"萝莎莉娅",
|
|
||||||
"萝莎利亚",
|
|
||||||
"萝莎利娅",
|
|
||||||
"萝沙莉亚",
|
|
||||||
"萝沙莉娅",
|
|
||||||
"萝沙利亚",
|
|
||||||
"萝沙利娅",
|
|
||||||
],
|
|
||||||
10000046: [
|
|
||||||
"胡桃",
|
|
||||||
"hutao",
|
|
||||||
"hu tao",
|
|
||||||
"胡淘",
|
|
||||||
"往生堂堂主",
|
|
||||||
"火化",
|
|
||||||
"抬棺的",
|
|
||||||
"蝴蝶",
|
|
||||||
"核桃",
|
|
||||||
"堂主",
|
|
||||||
"胡堂主",
|
|
||||||
"雪霁梅香",
|
|
||||||
"赤团开时",
|
|
||||||
"黑无常",
|
|
||||||
"嘘嘘鬼王",
|
|
||||||
"琪亚娜",
|
|
||||||
"薪炎之律者",
|
|
||||||
],
|
|
||||||
10000047: ["枫原万叶", "kazuha", "kaedehara kazuha", "万叶", "叶天帝", "天帝", "人型气象观测台", "浪人武士"],
|
|
||||||
10000048: ["烟绯", "yanfei", "烟老师", "律师", "罗翔", "璃月港的知名律法咨询师", "璃月罗翔", "铁人三项真君"],
|
|
||||||
10000049: [
|
|
||||||
"宵宫",
|
|
||||||
"yoimiya",
|
|
||||||
"霄宫",
|
|
||||||
"烟花",
|
|
||||||
"肖宫",
|
|
||||||
"肖工",
|
|
||||||
"绷带女孩",
|
|
||||||
"夏祭的女王",
|
|
||||||
"地对鸽导弹",
|
|
||||||
"打火姬二代目",
|
|
||||||
"长野原加特林",
|
|
||||||
"花见坂军火商",
|
|
||||||
],
|
|
||||||
10000050: ["托马", "thoma", "家政官", "太郎丸", "地头蛇", "男仆", "男妈妈"],
|
|
||||||
10000051: ["优菈", "eula", "优拉", "尤拉", "尤菈", "浪花骑士", "记仇", "喷嚏记仇真君"],
|
|
||||||
10000052: [
|
|
||||||
"雷电将军",
|
|
||||||
"shougun",
|
|
||||||
"raiden shogun",
|
|
||||||
"raiden",
|
|
||||||
"ei",
|
|
||||||
"raiden ei",
|
|
||||||
"baal",
|
|
||||||
"雷神",
|
|
||||||
"将军",
|
|
||||||
"雷军",
|
|
||||||
"巴尔",
|
|
||||||
"阿影",
|
|
||||||
"影",
|
|
||||||
"巴尔泽布",
|
|
||||||
"煮饭婆",
|
|
||||||
"奶香一刀",
|
|
||||||
"无想一刀",
|
|
||||||
"宅女",
|
|
||||||
"大御所大人",
|
|
||||||
"鸣神",
|
|
||||||
"永恒之神",
|
|
||||||
"姐控",
|
|
||||||
"不会做饭真君",
|
|
||||||
"宅女程序员",
|
|
||||||
"奶香一刀真君",
|
|
||||||
"雷电芽衣",
|
|
||||||
"又哭又闹真君",
|
|
||||||
"御建鸣神主尊大御所大人",
|
|
||||||
],
|
|
||||||
10000053: ["早柚", "sayu", "小狸猫", "狸猫", "咕噜咕噜赶路真君", "柚岩龙蜥", "善于潜行的矮子", "专业人士"],
|
|
||||||
10000054: [
|
|
||||||
"珊瑚宫心海",
|
|
||||||
"kokomi",
|
|
||||||
"sangonomiya kokomi",
|
|
||||||
"心海",
|
|
||||||
"军师",
|
|
||||||
"珊瑚宫",
|
|
||||||
"书记",
|
|
||||||
"观赏鱼",
|
|
||||||
"水母",
|
|
||||||
"鱼",
|
|
||||||
"现人神巫女",
|
|
||||||
"宅家派节能军师",
|
|
||||||
"藤原千花",
|
|
||||||
"能量管理大师",
|
|
||||||
"五星观赏鱼",
|
|
||||||
"海天后",
|
|
||||||
"深海舌鲆鱼小姐",
|
|
||||||
],
|
|
||||||
10000055: ["五郎", "gorou", "柴犬", "土狗", "希娜", "希娜小姐", "海祇岛的小狗大将", "修勾", "五郎大将的朋友", "小狗勾"],
|
|
||||||
10000056: [
|
|
||||||
"九条裟罗",
|
|
||||||
"sara",
|
|
||||||
"kujou sara",
|
|
||||||
"九条",
|
|
||||||
"九条沙罗",
|
|
||||||
"裟罗",
|
|
||||||
"条家的养子",
|
|
||||||
"雷系班尼特",
|
|
||||||
"雷神单推头子",
|
|
||||||
"珊瑚宫心海的冤家",
|
|
||||||
"荒泷一斗的冤家",
|
|
||||||
"外置暴伤",
|
|
||||||
"维密天使",
|
|
||||||
],
|
|
||||||
10000057: [
|
|
||||||
"荒泷一斗",
|
|
||||||
"itto",
|
|
||||||
"arataki itto",
|
|
||||||
"荒龙一斗",
|
|
||||||
"荒泷天下第一斗",
|
|
||||||
"一斗",
|
|
||||||
"一抖",
|
|
||||||
"荒泷",
|
|
||||||
"1斗",
|
|
||||||
"牛牛",
|
|
||||||
"斗子哥",
|
|
||||||
"牛子哥",
|
|
||||||
"牛子",
|
|
||||||
"孩子王",
|
|
||||||
"斗虫",
|
|
||||||
"巧乐兹",
|
|
||||||
"放牛的",
|
|
||||||
"岩丘丘萨满",
|
|
||||||
"伐伐伐伐伐木工",
|
|
||||||
"希娜小姐的榜一大哥",
|
|
||||||
],
|
|
||||||
10000058: [
|
|
||||||
"八重神子",
|
|
||||||
"miko",
|
|
||||||
"yae miko",
|
|
||||||
"八重",
|
|
||||||
"神子",
|
|
||||||
"狐狸",
|
|
||||||
"想得美哦",
|
|
||||||
"巫女",
|
|
||||||
"屑狐狸",
|
|
||||||
"骚狐狸",
|
|
||||||
"八重宫司",
|
|
||||||
"婶子",
|
|
||||||
"小八",
|
|
||||||
"白辰血脉的后裔",
|
|
||||||
"兼具智慧和美貌的八重神子大人",
|
|
||||||
"稻妻老八",
|
|
||||||
"雷丘丘萨满",
|
|
||||||
"八重樱",
|
|
||||||
"嗑瓜子",
|
|
||||||
"小奥兹",
|
|
||||||
"玲珑油豆腐小姐",
|
|
||||||
],
|
|
||||||
10000059: [
|
|
||||||
"鹿野院平藏",
|
|
||||||
"heizou",
|
|
||||||
"shikanoin heizou",
|
|
||||||
"heizo",
|
|
||||||
"鹿野苑",
|
|
||||||
"鹿野院",
|
|
||||||
"平藏",
|
|
||||||
"鹿野苑平藏",
|
|
||||||
"鹿野",
|
|
||||||
"小鹿",
|
|
||||||
"天领奉行侦探",
|
|
||||||
"鹿野奈奈的表弟",
|
|
||||||
"风拳前锋军",
|
|
||||||
"拳师",
|
|
||||||
"名侦探柯南",
|
|
||||||
"捕快展昭",
|
|
||||||
],
|
|
||||||
10000060: ["夜兰", "yelan", "夜阑", "叶澜", "腋兰", "夜天后", "自称就职于总务司的神秘人士", "岩上茶室老板", "夜上海", "胸怀大痣"],
|
|
||||||
10000062: ["埃洛伊", "aloy", "异界的救世主"],
|
|
||||||
10000063: ["申鹤", "shenhe", "神鹤", "小姨", "小姨子", "审鹤", "仙家弟子", "驱邪世家旁", "药材杀手"],
|
|
||||||
10000064: ["云堇", "yunjin", "yun jin", "云瑾", "云先生", "云锦", "神女劈观", "岩北斗", "五更琉璃"],
|
|
||||||
10000065: [
|
|
||||||
"久岐忍",
|
|
||||||
"kuki",
|
|
||||||
"kuki shinobu",
|
|
||||||
"shinobu",
|
|
||||||
"97忍",
|
|
||||||
"小忍",
|
|
||||||
"久歧忍",
|
|
||||||
"97",
|
|
||||||
"茄忍",
|
|
||||||
"阿忍",
|
|
||||||
"忍姐",
|
|
||||||
"鬼之副手",
|
|
||||||
"不是忍者的忍者",
|
|
||||||
"医疗忍者",
|
|
||||||
"考证专家",
|
|
||||||
],
|
|
||||||
10000066: [
|
|
||||||
"神里绫人",
|
|
||||||
"ayato",
|
|
||||||
"kamisato ayato",
|
|
||||||
"绫人",
|
|
||||||
"神里凌人",
|
|
||||||
"凌人",
|
|
||||||
"0人",
|
|
||||||
"神人",
|
|
||||||
"零人",
|
|
||||||
"大舅哥",
|
|
||||||
"神里绫华的兄长",
|
|
||||||
"荒泷一斗的虫友",
|
|
||||||
"奥托",
|
|
||||||
"奥托·阿波卡利斯",
|
|
||||||
"奥托主教",
|
|
||||||
"藏镜仕男",
|
|
||||||
"袖藏奶茶真君",
|
|
||||||
"真正的甘雨",
|
|
||||||
"可莉的爷爷",
|
|
||||||
],
|
|
||||||
10000067: [
|
|
||||||
"柯莱",
|
|
||||||
"collei",
|
|
||||||
"柯来",
|
|
||||||
"科莱",
|
|
||||||
"科来",
|
|
||||||
"小天使",
|
|
||||||
"须弥安柏",
|
|
||||||
"须弥飞行冠军",
|
|
||||||
"见习巡林员",
|
|
||||||
"克莱",
|
|
||||||
"草安伯",
|
|
||||||
"道成林见习巡林员",
|
|
||||||
"提纳里的学徒",
|
|
||||||
"安柏的挚友",
|
|
||||||
"兰那罗奶奶",
|
|
||||||
],
|
|
||||||
10000068: ["多莉", "dori", "多利", "多力", "多丽", "奸商", "须弥百货商人", "歌玛哈巴依老爷", "艾尔卡萨扎莱宫之主"],
|
|
||||||
10000069: [
|
|
||||||
"提纳里",
|
|
||||||
"tighnari",
|
|
||||||
"小提",
|
|
||||||
"提那里",
|
|
||||||
"缇娜里",
|
|
||||||
"提哪里",
|
|
||||||
"驴",
|
|
||||||
"柯莱老师",
|
|
||||||
"柯莱师傅",
|
|
||||||
"巡林官",
|
|
||||||
"提那里",
|
|
||||||
"耳朵很好摸",
|
|
||||||
"道成林巡林官",
|
|
||||||
"柯莱的师父",
|
|
||||||
],
|
|
||||||
10000070: ["妮露", "nilou", "尼露", "祖拜尔剧场之星", "红牛"],
|
|
||||||
10000071: ["赛诺", "cyno", "赛洛", "大风纪官", "大风机关", "胡狼头大人", "夹击妹抖", "游戏王", "冷笑话爱好者", "牌佬", "沙漠死神", "胡狼"],
|
|
||||||
10000072: ["坎蒂丝", "candace", "坎迪斯", "水北斗", "赤王后裔", "阿如村守护者"],
|
|
||||||
10000073: [
|
|
||||||
"纳西妲",
|
|
||||||
"nahida",
|
|
||||||
"buer",
|
|
||||||
"草王",
|
|
||||||
"草神",
|
|
||||||
"小吉祥草王",
|
|
||||||
"草萝莉",
|
|
||||||
"艹萝莉",
|
|
||||||
"羽毛球",
|
|
||||||
"布耶尔",
|
|
||||||
"纳西坦",
|
|
||||||
"摩诃善法大吉祥智慧主",
|
|
||||||
"智慧之神",
|
|
||||||
"草木之主",
|
|
||||||
"草神大人",
|
|
||||||
],
|
|
||||||
10000074: ["莱依拉", "layla", "拉一拉", "莱伊拉", "莫娜的同行", "西琳", "黑塔"],
|
|
||||||
10000075: [
|
|
||||||
"流浪者",
|
|
||||||
"wanderer",
|
|
||||||
"散兵",
|
|
||||||
"伞兵",
|
|
||||||
"伞兵一号",
|
|
||||||
"雷电国崩",
|
|
||||||
"国崩",
|
|
||||||
"卢本伟",
|
|
||||||
"雷电大炮",
|
|
||||||
"雷大炮",
|
|
||||||
"大炮",
|
|
||||||
"sb",
|
|
||||||
"斯卡拉姆齐",
|
|
||||||
"倾奇者",
|
|
||||||
"黑主",
|
|
||||||
"崩崩小圆帽",
|
|
||||||
"七叶寂照秘密主",
|
|
||||||
"七彩阳光秘密主",
|
|
||||||
"正机之神",
|
|
||||||
"伪神",
|
|
||||||
],
|
|
||||||
10000076: ["珐露珊", "faruzan", "法露珊", "珐妹", "初音", "初音未来", "miku", "发露姗", "发姐", "法姐", "百岁珊", "百岁山", "童姥", "知论派名宿"],
|
|
||||||
10000077: ["瑶瑶", "yaoyao", "遥遥", "遥遥无期", "香菱师妹", "萝卜", "四星草奶"],
|
|
||||||
10000078: ["艾尔海森", "alhaitham", "爱尔海森", "艾尔海参", "艾尔", "海森", "海参", "海神", "埃尔海森", "草刻晴", "书记官", "代理大贤者"],
|
|
||||||
10000079: ["迪希雅", "dehya", "狮女", "狮子", "腕豪", "女拳"],
|
|
||||||
10000080: ["米卡", "mika", "镜音连", "咪卡", "小米"],
|
|
||||||
10000081: ["白术", "baizhuer"],
|
|
||||||
10000082: ["卡维", "kaveh"],
|
|
||||||
}
|
}
|
||||||
not_real_roles = [10000081, 10000082]
|
not_real_roles = []
|
||||||
weapons = {
|
light_cones = {
|
||||||
# 1.x
|
20000: ['锋镝'],
|
||||||
"决斗之枪": ["决斗枪", "决斗", "月卡枪"],
|
20001: ['物穰'],
|
||||||
"螭骨剑": ["螭骨", "丈育剑", "离骨剑", "月卡大剑"],
|
20002: ['天倾'],
|
||||||
"黑剑": ["月卡剑"],
|
20003: ['琥珀'],
|
||||||
"苍翠猎弓": ["绿弓", "月卡弓"],
|
20004: ['幽邃'],
|
||||||
"匣里日月": ["日月"],
|
20005: ['齐颂'],
|
||||||
"匣里灭辰": ["灭辰"],
|
20006: ['智库'],
|
||||||
"匣里龙吟": ["龙吟"],
|
20007: ['离弦'],
|
||||||
"流月针": ["针"],
|
20008: ['嘉果'],
|
||||||
"流浪乐章": ["赌狗书", "赌狗乐章", "赌狗"],
|
20009: ['乐圮'],
|
||||||
"昭心": ["糟心"],
|
20010: ['戍御'],
|
||||||
"讨龙英杰谭": ["讨龙"],
|
20011: ['渊环'],
|
||||||
"神射手之誓": ["脚气弓", "神射手"],
|
20012: ['轮契'],
|
||||||
"黑缨枪": ["史莱姆枪"],
|
20013: ['灵钥'],
|
||||||
"黑岩刺枪": ["黑岩枪"],
|
20014: ['相抗'],
|
||||||
"黑岩战弓": ["黑岩弓"],
|
20015: ['蕃息'],
|
||||||
"天空之刃": ["天空剑"],
|
20016: ['俱殁'],
|
||||||
"天空之傲": ["天空大剑"],
|
20017: ['开疆'],
|
||||||
"天空之脊": ["天空枪", "薄荷枪", "薄荷"],
|
20018: ['匿影'],
|
||||||
"天空之卷": ["天空书", "厕纸"],
|
20019: ['调和'],
|
||||||
"天空之翼": ["天空弓"],
|
20020: ['睿见'],
|
||||||
"四风原典": ["四风", "可莉专武"],
|
21000: ['一场术后对话'],
|
||||||
"阿莫斯之弓": ["阿莫斯", "ams", "痛苦弓", "甘雨专武"],
|
21001: ['晚安与睡颜'],
|
||||||
"狼的末路": ["狼末"],
|
21002: ['余生的第一天'],
|
||||||
"和璞鸢": ["鸟枪", "绿枪", "魈专武"],
|
21003: ['唯有沉默'],
|
||||||
"风鹰剑": ["风鹰"],
|
21004: ['记忆中的模样'],
|
||||||
"试作斩岩": ["斩岩"],
|
21005: ['鼹鼠党欢迎你'],
|
||||||
"试作星镰": ["星镰"],
|
21006: ['「我」的诞生'],
|
||||||
"试作金珀": ["金珀"],
|
21007: ['同一种心情'],
|
||||||
"试作古华": ["古华"],
|
21008: ['猎物的视线'],
|
||||||
"试作澹月": ["澹月"],
|
21009: ['朗道的选择'],
|
||||||
"万国诸海图谱": ["万国", "万国诸海"],
|
21010: ['论剑'],
|
||||||
"尘世之锁": ["尘世锁", "尘世", "盾书", "锁"],
|
21011: ['与行星相会'],
|
||||||
"无工之剑": ["蜈蚣", "蜈蚣大剑", "无工大剑", "盾大剑", "无工"],
|
21012: ['秘密誓心'],
|
||||||
"贯虹之槊": ["贯虹", "岩枪", "盾枪", "钟离专武"],
|
21013: ['别让世界静下来'],
|
||||||
"斫峰之刃": ["斫峰", "盾剑"],
|
21014: ['此时恰好'],
|
||||||
"腐殖之剑": ["腐殖", "腐殖剑"],
|
21015: ['决心如汗珠般闪耀'],
|
||||||
"雪葬的星银": ["雪葬", "星银", "雪葬星银", "雪山大剑"],
|
21016: ['宇宙市场趋势'],
|
||||||
"磐岩结绿": ["绿箭", "绿剑"],
|
21017: ['点个关注吧!'],
|
||||||
"护摩之杖": ["护摩", "护摩枪", "护膜", "胡桃专武"],
|
21018: ['舞!舞!舞!'],
|
||||||
"千岩长枪": ["千岩枪"],
|
21019: ['在蓝天下'],
|
||||||
"千岩古剑": ["千岩剑", "千岩大剑"],
|
21020: ['天才们的休憩'],
|
||||||
"西风长枪": ["西风枪"],
|
21021: ['等价交换'],
|
||||||
"西风猎弓": ["西风弓"],
|
21022: ['延长记号'],
|
||||||
"西风秘典": ["西风书"],
|
21023: ['我们是地火'],
|
||||||
"暗巷闪光": ["暗巷剑", "暗巷小剑", "暗巷"],
|
21024: ['春水初生'],
|
||||||
"暗巷猎手": ["暗巷弓"],
|
21025: ['过往未来'],
|
||||||
"暗巷的酒与诗": ["暗巷法器", "暗巷书"],
|
21026: ['汪!散步时间!'],
|
||||||
"风花之颂": ["风花弓"],
|
21027: ['早餐的仪式感'],
|
||||||
"终末嗟叹之诗": ["终末", "终末弓", "叹气弓", "乐团弓", "温迪专武"],
|
21028: ['暖夜不会漫长'],
|
||||||
"松籁响起之时": ["松籁", "乐团大剑", "松剑", "优菈专武"],
|
21029: ['后会有期'],
|
||||||
"苍古自由之誓": ["苍古", "乐团剑", "枫原万叶专武"],
|
21030: ['这就是我啦!'],
|
||||||
"幽夜华尔兹": ["幽夜", "幽夜弓", "华尔兹", "皇女弓"],
|
21031: ['重返幽冥'],
|
||||||
"嘟嘟可故事集": ["嘟嘟可"],
|
21032: ['镂月裁云之意'],
|
||||||
# 2.x
|
21033: ['无处可逃'],
|
||||||
"天目影打刀": ["天目刀", "天目"],
|
21034: ['今日亦是和平的一日'],
|
||||||
"桂木斩长正": ["桂木", "斩长正"],
|
23000: ['银河铁道之夜'],
|
||||||
"喜多院十文字": ["喜多院", "十文字"],
|
23001: ['于夜色中'],
|
||||||
"破魔之弓": ["破魔弓", "破魔"],
|
23002: ['无可取代的东西'],
|
||||||
"白辰之环": ["白辰", "白辰环"],
|
23003: ['但战斗还未结束'],
|
||||||
"雾切之回光": ["雾切", "神里绫华专武"],
|
23004: ['以世界之名'],
|
||||||
"飞雷之弦振": ["飞雷", "飞雷弓", "宵宫专武"],
|
23005: ['制胜的瞬间'],
|
||||||
"薙草之稻光": ["薙草", "稻光", "薙草稻光", "马尾枪", "马尾", "薙刀", "雷电将军专武"],
|
23010: ['拂晓之前'],
|
||||||
"不灭月华": ["月华", "珊瑚宫心海专武"],
|
23012: ['如泥酣眠'],
|
||||||
"「渔获」": ["鱼叉", "渔叉", "渔获"],
|
23013: ['时节不居'],
|
||||||
"衔珠海皇": ["海皇", "咸鱼剑", "咸鱼大剑"],
|
24000: ['记一位星神的陨落'],
|
||||||
"冬极白星": ["冬极", "达达利亚专武"],
|
24001: ['星海巡航'],
|
||||||
"曚云之月": ["曚云弓", "曚云"],
|
24002: ['记忆的质料']
|
||||||
"恶王丸": ["断浪大剑"],
|
|
||||||
"断浪长鳍": ["断浪", "断浪长枪", "断浪枪"],
|
|
||||||
"辰砂之纺锤": ["辰砂", "辰砂纺锤", "纺锤", "阿贝多专武"],
|
|
||||||
"赤角石溃杵": ["赤角", "石溃杵", "荒泷一斗专武", "巧乐兹"],
|
|
||||||
"息灾": ["申鹤专武"],
|
|
||||||
"神乐之真意": ["神乐", "真意", "八重神子专武"],
|
|
||||||
"证誓之明瞳": ["证誓", "明瞳", "证誓明瞳"],
|
|
||||||
"波乱月白经津": ["波乱", "月白", "波乱月白", "经津", "波波津", "神里绫人专武", "钵钵鸡"],
|
|
||||||
"若水": ["麒麟弓", "夜兰专武"],
|
|
||||||
"笼钓瓶一心": ["万叶刀", "一心传名刀", "妖刀"],
|
|
||||||
# 3.x
|
|
||||||
"猎人之径": ["草弓", "提纳里专武"],
|
|
||||||
"竭泽": ["鱼弓"],
|
|
||||||
"原木刀": ["须弥锻造单手剑"],
|
|
||||||
"森林王器": ["须弥锻造大剑", "原木大剑"],
|
|
||||||
"贯月矢": ["须弥锻造长枪", "原木枪"],
|
|
||||||
"盈满之实": ["须弥锻造法器"],
|
|
||||||
"王下近侍": ["须弥锻造弓", "原木弓"],
|
|
||||||
"赤沙之杖": ["赤沙", "赛诺专武", "船桨"],
|
|
||||||
"圣显之钥": ["圣显之钥", "圣显", "不灭剑华", "妮露专武", "板砖"],
|
|
||||||
"风信之锋": ["风信", "风信锋"],
|
|
||||||
"西福斯的月光": ["西福斯", "月光", "月光小剑", "月光剑"],
|
|
||||||
"玛海菈的水色": ["玛海菈", "水色"],
|
|
||||||
"流浪的晚星": ["晚星"],
|
|
||||||
"千夜浮梦": ["千夜", "神灯", "茶壶", "夜壶"],
|
|
||||||
"图莱杜拉的回忆": ["图莱杜拉", "铃铛", "流浪者专武"],
|
|
||||||
"东花坊时雨": ["东花坊", "时雨", "伞"],
|
|
||||||
"裁叶萃光": ["萃光", "韭菜刀", "裁叶", "菜叶"],
|
|
||||||
"饰铁之花": ["饰铁", "铁花"],
|
|
||||||
"苇海信标": ["苇海", "信标"],
|
|
||||||
"碧落之珑": ["碧落", "白术专武"],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -720,16 +127,18 @@ def roleToId(name: str) -> int | None:
|
|||||||
|
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
@functools.lru_cache()
|
@functools.lru_cache()
|
||||||
def weaponToName(shortname: str) -> str:
|
def lightConeToName(shortname: str) -> str:
|
||||||
"""将武器昵称转为正式名"""
|
"""将光锥昵称转为正式名"""
|
||||||
return next((key for key, value in weapons.items() if shortname == key or shortname in value), shortname)
|
shortname = str.casefold(shortname) # 忽略大小写
|
||||||
|
return next((value[0] for value in light_cones.values() for name in value if name == shortname), shortname)
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
@functools.lru_cache()
|
@functools.lru_cache()
|
||||||
def weaponToId(name: str) -> int | None:
|
def lightConeToId(name: str) -> int | None:
|
||||||
"""获取武器ID"""
|
"""获取光锥ID"""
|
||||||
return next((int(key) for key, value in WEAPON_DATA.items() if weaponToName(name) in value["name"]), None)
|
name = str.casefold(name)
|
||||||
|
return next((key for key, value in light_cones.items() for n in value if n == name), None)
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
from genshin.models import BannerType
|
from genshin.models import StarRailBannerType
|
||||||
|
|
||||||
PAIMONMOE_VERSION = 3
|
UIWF_VERSION = "v1.0"
|
||||||
UIGF_VERSION = "v2.2"
|
|
||||||
|
|
||||||
|
|
||||||
GACHA_TYPE_LIST = {
|
GACHA_TYPE_LIST = {
|
||||||
BannerType.NOVICE: "新手祈愿",
|
StarRailBannerType.NOVICE: "新手跃迁",
|
||||||
BannerType.PERMANENT: "常驻祈愿",
|
StarRailBannerType.PERMANENT: "常驻跃迁",
|
||||||
BannerType.WEAPON: "武器祈愿",
|
StarRailBannerType.CHARACTER: "角色跃迁",
|
||||||
BannerType.CHARACTER1: "角色祈愿",
|
StarRailBannerType.WEAPON: "光锥跃迁",
|
||||||
BannerType.CHARACTER2: "角色祈愿",
|
|
||||||
}
|
}
|
||||||
|
@ -3,19 +3,16 @@ import contextlib
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from os import PathLike
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, IO, List, Optional, Tuple, Union
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
import aiofiles
|
import aiofiles
|
||||||
from genshin import AuthkeyTimeout, Client, InvalidAuthkey
|
from genshin import AuthkeyTimeout, Client, InvalidAuthkey
|
||||||
from genshin.models import BannerType
|
from genshin.models import StarRailBannerType
|
||||||
from openpyxl import load_workbook
|
|
||||||
|
|
||||||
from core.dependence.assets import AssetsService
|
from core.dependence.assets import AssetsService
|
||||||
from metadata.pool.pool import get_pool_by_id
|
from metadata.pool.pool import get_pool_by_id
|
||||||
from metadata.shortname import roleToId, weaponToId
|
from modules.gacha_log.const import GACHA_TYPE_LIST
|
||||||
from modules.gacha_log.const import GACHA_TYPE_LIST, PAIMONMOE_VERSION
|
|
||||||
from modules.gacha_log.error import (
|
from modules.gacha_log.error import (
|
||||||
GachaLogAccountNotFound,
|
GachaLogAccountNotFound,
|
||||||
GachaLogAuthkeyTimeout,
|
GachaLogAuthkeyTimeout,
|
||||||
@ -24,7 +21,6 @@ from modules.gacha_log.error import (
|
|||||||
GachaLogInvalidAuthkey,
|
GachaLogInvalidAuthkey,
|
||||||
GachaLogMixedProvider,
|
GachaLogMixedProvider,
|
||||||
GachaLogNotFound,
|
GachaLogNotFound,
|
||||||
PaimonMoeGachaLogFileError,
|
|
||||||
)
|
)
|
||||||
from modules.gacha_log.models import (
|
from modules.gacha_log.models import (
|
||||||
FiveStarItem,
|
FiveStarItem,
|
||||||
@ -32,16 +28,14 @@ from modules.gacha_log.models import (
|
|||||||
GachaItem,
|
GachaItem,
|
||||||
GachaLogInfo,
|
GachaLogInfo,
|
||||||
ImportType,
|
ImportType,
|
||||||
ItemType,
|
|
||||||
Pool,
|
Pool,
|
||||||
UIGFGachaType,
|
UIWFInfo,
|
||||||
UIGFInfo,
|
UIWFItem,
|
||||||
UIGFItem,
|
UIWFModel,
|
||||||
UIGFModel,
|
|
||||||
)
|
)
|
||||||
from utils.const import PROJECT_ROOT
|
from utils.const import PROJECT_ROOT
|
||||||
|
|
||||||
GACHA_LOG_PATH = PROJECT_ROOT.joinpath("data", "apihelper", "gacha_log")
|
GACHA_LOG_PATH = PROJECT_ROOT.joinpath("data", "apihelper", "warp_log")
|
||||||
GACHA_LOG_PATH.mkdir(parents=True, exist_ok=True)
|
GACHA_LOG_PATH.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
@ -64,11 +58,11 @@ class GachaLog:
|
|||||||
async def load_history_info(
|
async def load_history_info(
|
||||||
self, user_id: str, uid: str, only_status: bool = False
|
self, user_id: str, uid: str, only_status: bool = False
|
||||||
) -> Tuple[Optional[GachaLogInfo], bool]:
|
) -> Tuple[Optional[GachaLogInfo], bool]:
|
||||||
"""读取历史抽卡记录数据
|
"""读取历史跃迁记录数据
|
||||||
:param user_id: 用户id
|
:param user_id: 用户id
|
||||||
:param uid: 原神uid
|
:param uid: 原神uid
|
||||||
:param only_status: 是否只读取状态
|
:param only_status: 是否只读取状态
|
||||||
:return: 抽卡记录数据
|
:return: 跃迁记录数据
|
||||||
"""
|
"""
|
||||||
file_path = self.gacha_log_path / f"{user_id}-{uid}.json"
|
file_path = self.gacha_log_path / f"{user_id}-{uid}.json"
|
||||||
if only_status:
|
if only_status:
|
||||||
@ -81,7 +75,7 @@ class GachaLog:
|
|||||||
return GachaLogInfo(user_id=user_id, uid=uid, update_time=datetime.datetime.now()), False
|
return GachaLogInfo(user_id=user_id, uid=uid, update_time=datetime.datetime.now()), False
|
||||||
|
|
||||||
async def remove_history_info(self, user_id: str, uid: str) -> bool:
|
async def remove_history_info(self, user_id: str, uid: str) -> bool:
|
||||||
"""删除历史抽卡记录数据
|
"""删除历史跃迁记录数据
|
||||||
:param user_id: 用户id
|
:param user_id: 用户id
|
||||||
:param uid: 原神uid
|
:param uid: 原神uid
|
||||||
:return: 是否删除成功
|
:return: 是否删除成功
|
||||||
@ -102,10 +96,10 @@ class GachaLog:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
async def save_gacha_log_info(self, user_id: str, uid: str, info: GachaLogInfo):
|
async def save_gacha_log_info(self, user_id: str, uid: str, info: GachaLogInfo):
|
||||||
"""保存抽卡记录数据
|
"""保存跃迁记录数据
|
||||||
:param user_id: 用户id
|
:param user_id: 用户id
|
||||||
:param uid: 原神uid
|
:param uid: 原神uid
|
||||||
:param info: 抽卡记录数据
|
:param info: 跃迁记录数据
|
||||||
"""
|
"""
|
||||||
save_path = self.gacha_log_path / f"{user_id}-{uid}.json"
|
save_path = self.gacha_log_path / f"{user_id}-{uid}.json"
|
||||||
save_path_bak = self.gacha_log_path / f"{user_id}-{uid}.json.bak"
|
save_path_bak = self.gacha_log_path / f"{user_id}-{uid}.json.bak"
|
||||||
@ -118,21 +112,21 @@ class GachaLog:
|
|||||||
# 写入数据
|
# 写入数据
|
||||||
await self.save_json(save_path, info.json())
|
await self.save_json(save_path, info.json())
|
||||||
|
|
||||||
async def gacha_log_to_uigf(self, user_id: str, uid: str) -> Optional[Path]:
|
async def gacha_log_to_uiwf(self, user_id: str, uid: str) -> Optional[Path]:
|
||||||
"""抽卡日记转换为 UIGF 格式
|
"""跃迁日记转换为 UIWF 格式
|
||||||
:param user_id: 用户ID
|
:param user_id: 用户ID
|
||||||
:param uid: 游戏UID
|
:param uid: 游戏UID
|
||||||
:return: 转换是否成功、转换信息、UIGF文件目录
|
:return: 转换是否成功、转换信息、UIWF文件目录
|
||||||
"""
|
"""
|
||||||
data, state = await self.load_history_info(user_id, uid)
|
data, state = await self.load_history_info(user_id, uid)
|
||||||
if not state:
|
if not state:
|
||||||
raise GachaLogNotFound
|
raise GachaLogNotFound
|
||||||
save_path = self.gacha_log_path / f"{user_id}-{uid}-uigf.json"
|
save_path = self.gacha_log_path / f"{user_id}-{uid}-uiwf.json"
|
||||||
info = UIGFModel(info=UIGFInfo(uid=uid, export_app=ImportType.PaiGram.value, export_app_version="v3"), list=[])
|
info = UIWFModel(info=UIWFInfo(uid=uid, export_app=ImportType.PaiGram.value, export_app_version="v3"), list=[])
|
||||||
for items in data.item_list.values():
|
for items in data.item_list.values():
|
||||||
for item in items:
|
for item in items:
|
||||||
info.list.append(
|
info.list.append(
|
||||||
UIGFItem(
|
UIWFItem(
|
||||||
id=item.id,
|
id=item.id,
|
||||||
name=item.name,
|
name=item.name,
|
||||||
gacha_type=item.gacha_type,
|
gacha_type=item.gacha_type,
|
||||||
@ -153,9 +147,9 @@ class GachaLog:
|
|||||||
four_star = len([i for i in data if i.rank_type == "4"])
|
four_star = len([i for i in data if i.rank_type == "4"])
|
||||||
if total > 50:
|
if total > 50:
|
||||||
if total <= five_star * 15:
|
if total <= five_star * 15:
|
||||||
raise GachaLogFileError("检测到您将要导入的抽卡记录中五星数量过多,可能是由于文件错误导致的,请检查后重新导入。")
|
raise GachaLogFileError("检测到您将要导入的跃迁记录中五星数量过多,可能是由于文件错误导致的,请检查后重新导入。")
|
||||||
if four_star < five_star:
|
if four_star < five_star:
|
||||||
raise GachaLogFileError("检测到您将要导入的抽卡记录中五星数量过多,可能是由于文件错误导致的,请检查后重新导入。")
|
raise GachaLogFileError("检测到您将要导入的跃迁记录中五星数量过多,可能是由于文件错误导致的,请检查后重新导入。")
|
||||||
return True
|
return True
|
||||||
except Exception as exc: # pylint: disable=W0703
|
except Exception as exc: # pylint: disable=W0703
|
||||||
raise GachaLogFileError from exc
|
raise GachaLogFileError from exc
|
||||||
@ -164,7 +158,7 @@ class GachaLog:
|
|||||||
def import_data_backend(all_items: List[GachaItem], gacha_log: GachaLogInfo, temp_id_data: Dict) -> int:
|
def import_data_backend(all_items: List[GachaItem], gacha_log: GachaLogInfo, temp_id_data: Dict) -> int:
|
||||||
new_num = 0
|
new_num = 0
|
||||||
for item_info in all_items:
|
for item_info in all_items:
|
||||||
pool_name = GACHA_TYPE_LIST[BannerType(int(item_info.gacha_type))]
|
pool_name = GACHA_TYPE_LIST[StarRailBannerType(int(item_info.gacha_type))]
|
||||||
if item_info.id not in temp_id_data[pool_name]:
|
if item_info.id not in temp_id_data[pool_name]:
|
||||||
gacha_log.item_list[pool_name].append(item_info)
|
gacha_log.item_list[pool_name].append(item_info)
|
||||||
temp_id_data[pool_name].append(item_info.id)
|
temp_id_data[pool_name].append(item_info.id)
|
||||||
@ -187,11 +181,6 @@ class GachaLog:
|
|||||||
all_items = [GachaItem(**i) for i in data["list"]]
|
all_items = [GachaItem(**i) for i in data["list"]]
|
||||||
await self.verify_data(all_items)
|
await self.verify_data(all_items)
|
||||||
gacha_log, status = await self.load_history_info(str(user_id), uid)
|
gacha_log, status = await self.load_history_info(str(user_id), uid)
|
||||||
if import_type == ImportType.PAIMONMOE:
|
|
||||||
if status and gacha_log.get_import_type != ImportType.PAIMONMOE:
|
|
||||||
raise GachaLogMixedProvider
|
|
||||||
elif status and gacha_log.get_import_type == ImportType.PAIMONMOE:
|
|
||||||
raise GachaLogMixedProvider
|
|
||||||
# 将唯一 id 放入临时数据中,加快查找速度
|
# 将唯一 id 放入临时数据中,加快查找速度
|
||||||
temp_id_data = {
|
temp_id_data = {
|
||||||
pool_name: [i.id for i in pool_data] for pool_name, pool_data in gacha_log.item_list.items()
|
pool_name: [i.id for i in pool_data] for pool_name, pool_data in gacha_log.item_list.items()
|
||||||
@ -216,10 +205,11 @@ class GachaLog:
|
|||||||
except GachaLogMixedProvider as e:
|
except GachaLogMixedProvider as e:
|
||||||
raise GachaLogMixedProvider from e
|
raise GachaLogMixedProvider from e
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
breakpoint()
|
||||||
raise GachaLogException from exc
|
raise GachaLogException from exc
|
||||||
|
|
||||||
async def get_gacha_log_data(self, user_id: int, client: Client, authkey: str) -> int:
|
async def get_gacha_log_data(self, user_id: int, client: Client, authkey: str) -> int:
|
||||||
"""使用authkey获取抽卡记录数据,并合并旧数据
|
"""使用authkey获取跃迁记录数据,并合并旧数据
|
||||||
:param user_id: 用户id
|
:param user_id: 用户id
|
||||||
:param client: genshin client
|
:param client: genshin client
|
||||||
:param authkey: authkey
|
:param authkey: authkey
|
||||||
@ -227,8 +217,6 @@ class GachaLog:
|
|||||||
"""
|
"""
|
||||||
new_num = 0
|
new_num = 0
|
||||||
gacha_log, _ = await self.load_history_info(str(user_id), str(client.uid))
|
gacha_log, _ = await self.load_history_info(str(user_id), str(client.uid))
|
||||||
if gacha_log.get_import_type == ImportType.PAIMONMOE:
|
|
||||||
raise GachaLogMixedProvider
|
|
||||||
# 将唯一 id 放入临时数据中,加快查找速度
|
# 将唯一 id 放入临时数据中,加快查找速度
|
||||||
temp_id_data = {pool_name: [i.id for i in pool_data] for pool_name, pool_data in gacha_log.item_list.items()}
|
temp_id_data = {pool_name: [i.id for i in pool_data] for pool_name, pool_data in gacha_log.item_list.items()}
|
||||||
try:
|
try:
|
||||||
@ -261,30 +249,20 @@ class GachaLog:
|
|||||||
for i in gacha_log.item_list.values():
|
for i in gacha_log.item_list.values():
|
||||||
i.sort(key=lambda x: (x.time, x.id))
|
i.sort(key=lambda x: (x.time, x.id))
|
||||||
gacha_log.update_time = datetime.datetime.now()
|
gacha_log.update_time = datetime.datetime.now()
|
||||||
gacha_log.import_type = ImportType.UIGF.value
|
gacha_log.import_type = ImportType.UIWF.value
|
||||||
await self.save_gacha_log_info(str(user_id), str(client.uid), gacha_log)
|
await self.save_gacha_log_info(str(user_id), str(client.uid), gacha_log)
|
||||||
return new_num
|
return new_num
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check_avatar_up(name: str, gacha_time: datetime.datetime) -> bool:
|
def check_avatar_up(name: str, gacha_time: datetime.datetime) -> bool:
|
||||||
if name in {"莫娜", "七七", "迪卢克", "琴"}:
|
if name in {"姬子", "瓦尔特", "布洛妮娅", "杰帕德", "克拉拉", "彦卿", "白露"}:
|
||||||
return False
|
|
||||||
if name == "刻晴":
|
|
||||||
start_time = datetime.datetime.strptime("2021-02-17 18:00:00", "%Y-%m-%d %H:%M:%S")
|
|
||||||
end_time = datetime.datetime.strptime("2021-03-02 15:59:59", "%Y-%m-%d %H:%M:%S")
|
|
||||||
if not start_time < gacha_time < end_time:
|
|
||||||
return False
|
|
||||||
elif name == "提纳里":
|
|
||||||
start_time = datetime.datetime.strptime("2022-08-24 06:00:00", "%Y-%m-%d %H:%M:%S")
|
|
||||||
end_time = datetime.datetime.strptime("2022-09-09 17:59:59", "%Y-%m-%d %H:%M:%S")
|
|
||||||
if not start_time < gacha_time < end_time:
|
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def get_all_5_star_items(self, data: List[GachaItem], assets: AssetsService, pool_name: str = "角色祈愿"):
|
async def get_all_5_star_items(self, data: List[GachaItem], assets: AssetsService, pool_name: str = "角色跃迁"):
|
||||||
"""
|
"""
|
||||||
获取所有5星角色
|
获取所有5星角色
|
||||||
:param data: 抽卡记录
|
:param data: 跃迁记录
|
||||||
:param assets: 资源服务
|
:param assets: 资源服务
|
||||||
:param pool_name: 池子名称
|
:param pool_name: 池子名称
|
||||||
:return: 5星角色列表
|
:return: 5星角色列表
|
||||||
@ -294,21 +272,27 @@ class GachaLog:
|
|||||||
for item in data:
|
for item in data:
|
||||||
count += 1
|
count += 1
|
||||||
if item.rank_type == "5":
|
if item.rank_type == "5":
|
||||||
if item.item_type == "角色" and pool_name in {"角色祈愿", "常驻祈愿"}:
|
if item.item_type == "角色" and pool_name in {"角色跃迁", "常驻跃迁", "新手跃迁"}:
|
||||||
|
if pool_name == "新手跃迁":
|
||||||
|
isUp, isBig = True, False
|
||||||
|
elif pool_name == "角色跃迁":
|
||||||
|
isUp, isBig = self.check_avatar_up(item.name, item.time), (not result[-1].isUp) if result else False
|
||||||
|
else:
|
||||||
|
isUp, isBig = False, False
|
||||||
data = {
|
data = {
|
||||||
"name": item.name,
|
"name": item.name,
|
||||||
"icon": (await assets.avatar(roleToId(item.name)).icon()).as_uri(),
|
"icon": assets.avatar.icon(item.name).as_uri(),
|
||||||
"count": count,
|
"count": count,
|
||||||
"type": "角色",
|
"type": "角色",
|
||||||
"isUp": self.check_avatar_up(item.name, item.time) if pool_name == "角色祈愿" else False,
|
"isUp": isUp,
|
||||||
"isBig": (not result[-1].isUp) if result and pool_name == "角色祈愿" else False,
|
"isBig": isBig,
|
||||||
"time": item.time,
|
"time": item.time,
|
||||||
}
|
}
|
||||||
result.append(FiveStarItem.construct(**data))
|
result.append(FiveStarItem.construct(**data))
|
||||||
elif item.item_type == "武器" and pool_name in {"武器祈愿", "常驻祈愿"}:
|
elif item.item_type == "光锥" and pool_name in {"光锥跃迁", "常驻跃迁"}:
|
||||||
data = {
|
data = {
|
||||||
"name": item.name,
|
"name": item.name,
|
||||||
"icon": (await assets.weapon(weaponToId(item.name)).icon()).as_uri(),
|
"icon": assets.light_cone.icon(item.name).as_uri(),
|
||||||
"count": count,
|
"count": count,
|
||||||
"type": "武器",
|
"type": "武器",
|
||||||
"isUp": False,
|
"isUp": False,
|
||||||
@ -324,7 +308,7 @@ class GachaLog:
|
|||||||
async def get_all_4_star_items(data: List[GachaItem], assets: AssetsService):
|
async def get_all_4_star_items(data: List[GachaItem], assets: AssetsService):
|
||||||
"""
|
"""
|
||||||
获取 no_fout_star
|
获取 no_fout_star
|
||||||
:param data: 抽卡记录
|
:param data: 跃迁记录
|
||||||
:param assets: 资源服务
|
:param assets: 资源服务
|
||||||
:return: no_fout_star
|
:return: no_fout_star
|
||||||
"""
|
"""
|
||||||
@ -336,18 +320,18 @@ class GachaLog:
|
|||||||
if item.item_type == "角色":
|
if item.item_type == "角色":
|
||||||
data = {
|
data = {
|
||||||
"name": item.name,
|
"name": item.name,
|
||||||
"icon": (await assets.avatar(roleToId(item.name)).icon()).as_uri(),
|
"icon": assets.avatar.icon(item.name).as_uri(),
|
||||||
"count": count,
|
"count": count,
|
||||||
"type": "角色",
|
"type": "角色",
|
||||||
"time": item.time,
|
"time": item.time,
|
||||||
}
|
}
|
||||||
result.append(FourStarItem.construct(**data))
|
result.append(FourStarItem.construct(**data))
|
||||||
elif item.item_type == "武器":
|
elif item.item_type == "光锥":
|
||||||
data = {
|
data = {
|
||||||
"name": item.name,
|
"name": item.name,
|
||||||
"icon": (await assets.weapon(weaponToId(item.name)).icon()).as_uri(),
|
"icon": assets.light_cone.icon(item.name).as_uri(),
|
||||||
"count": count,
|
"count": count,
|
||||||
"type": "武器",
|
"type": "光锥",
|
||||||
"time": item.time,
|
"time": item.time,
|
||||||
}
|
}
|
||||||
result.append(FourStarItem.construct(**data))
|
result.append(FourStarItem.construct(**data))
|
||||||
@ -387,7 +371,7 @@ class GachaLog:
|
|||||||
{"num": no_four_star, "unit": "抽", "lable": "未出四星"},
|
{"num": no_four_star, "unit": "抽", "lable": "未出四星"},
|
||||||
{"num": five_star_const, "unit": "个", "lable": "五星常驻"},
|
{"num": five_star_const, "unit": "个", "lable": "五星常驻"},
|
||||||
{"num": up_avg, "unit": "抽", "lable": "UP平均"},
|
{"num": up_avg, "unit": "抽", "lable": "UP平均"},
|
||||||
{"num": up_cost, "unit": "", "lable": "UP花费原石"},
|
{"num": up_cost, "unit": "", "lable": "UP花费星琼"},
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -400,7 +384,7 @@ class GachaLog:
|
|||||||
# 五星平均
|
# 五星平均
|
||||||
five_star_avg = round((total - no_five_star) / five_star, 2) if five_star != 0 else 0
|
five_star_avg = round((total - no_five_star) / five_star, 2) if five_star != 0 else 0
|
||||||
# 五星武器
|
# 五星武器
|
||||||
five_star_weapon = len([i for i in all_five if i.type == "武器"])
|
five_star_weapon = len([i for i in all_five if i.type == "光锥"])
|
||||||
# 总共四星
|
# 总共四星
|
||||||
four_star = len(all_four)
|
four_star = len(all_four)
|
||||||
# 四星平均
|
# 四星平均
|
||||||
@ -414,7 +398,7 @@ class GachaLog:
|
|||||||
{"num": no_five_star, "unit": "抽", "lable": "未出五星"},
|
{"num": no_five_star, "unit": "抽", "lable": "未出五星"},
|
||||||
{"num": five_star, "unit": "个", "lable": "五星"},
|
{"num": five_star, "unit": "个", "lable": "五星"},
|
||||||
{"num": five_star_avg, "unit": "抽", "lable": "五星平均"},
|
{"num": five_star_avg, "unit": "抽", "lable": "五星平均"},
|
||||||
{"num": five_star_weapon, "unit": "个", "lable": "五星武器"},
|
{"num": five_star_weapon, "unit": "个", "lable": "五星光锥"},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{"num": no_four_star, "unit": "抽", "lable": "未出四星"},
|
{"num": no_four_star, "unit": "抽", "lable": "未出四星"},
|
||||||
@ -433,7 +417,7 @@ class GachaLog:
|
|||||||
# 五星平均
|
# 五星平均
|
||||||
five_star_avg = round((total - no_five_star) / five_star, 2) if five_star != 0 else 0
|
five_star_avg = round((total - no_five_star) / five_star, 2) if five_star != 0 else 0
|
||||||
# 四星武器
|
# 四星武器
|
||||||
four_star_weapon = len([i for i in all_four if i.type == "武器"])
|
four_star_weapon = len([i for i in all_four if i.type == "光锥"])
|
||||||
# 总共四星
|
# 总共四星
|
||||||
four_star = len(all_four)
|
four_star = len(all_four)
|
||||||
# 四星平均
|
# 四星平均
|
||||||
@ -447,7 +431,7 @@ class GachaLog:
|
|||||||
{"num": no_five_star, "unit": "抽", "lable": "未出五星"},
|
{"num": no_five_star, "unit": "抽", "lable": "未出五星"},
|
||||||
{"num": five_star, "unit": "个", "lable": "五星"},
|
{"num": five_star, "unit": "个", "lable": "五星"},
|
||||||
{"num": five_star_avg, "unit": "抽", "lable": "五星平均"},
|
{"num": five_star_avg, "unit": "抽", "lable": "五星平均"},
|
||||||
{"num": four_star_weapon, "unit": "个", "lable": "四星武器"},
|
{"num": four_star_weapon, "unit": "个", "lable": "四星光锥"},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{"num": no_four_star, "unit": "抽", "lable": "未出四星"},
|
{"num": no_four_star, "unit": "抽", "lable": "未出四星"},
|
||||||
@ -460,7 +444,7 @@ class GachaLog:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def count_fortune(pool_name: str, summon_data, weapon: bool = False):
|
def count_fortune(pool_name: str, summon_data, weapon: bool = False):
|
||||||
"""
|
"""
|
||||||
角色 武器
|
角色 光锥
|
||||||
欧 50以下 45以下
|
欧 50以下 45以下
|
||||||
吉 50-60 45-55
|
吉 50-60 45-55
|
||||||
中 60-70 55-65
|
中 60-70 55-65
|
||||||
@ -482,9 +466,9 @@ class GachaLog:
|
|||||||
return f"{pool_name} · 非"
|
return f"{pool_name} · 非"
|
||||||
return pool_name
|
return pool_name
|
||||||
|
|
||||||
async def get_analysis(self, user_id: int, client: Client, pool: BannerType, assets: AssetsService):
|
async def get_analysis(self, user_id: int, client: Client, pool: StarRailBannerType, assets: AssetsService):
|
||||||
"""
|
"""
|
||||||
获取抽卡记录分析数据
|
获取跃迁记录分析数据
|
||||||
:param user_id: 用户id
|
:param user_id: 用户id
|
||||||
:param client: genshin client
|
:param client: genshin client
|
||||||
:param pool: 池子类型
|
:param pool: 池子类型
|
||||||
@ -502,13 +486,13 @@ class GachaLog:
|
|||||||
all_five, no_five_star = await self.get_all_5_star_items(data, assets, pool_name)
|
all_five, no_five_star = await self.get_all_5_star_items(data, assets, pool_name)
|
||||||
all_four, no_four_star = await self.get_all_4_star_items(data, assets)
|
all_four, no_four_star = await self.get_all_4_star_items(data, assets)
|
||||||
summon_data = None
|
summon_data = None
|
||||||
if pool == BannerType.CHARACTER1:
|
if pool in [StarRailBannerType.CHARACTER, StarRailBannerType.NOVICE]:
|
||||||
summon_data = self.get_301_pool_data(total, all_five, no_five_star, no_four_star)
|
summon_data = self.get_301_pool_data(total, all_five, no_five_star, no_four_star)
|
||||||
pool_name = self.count_fortune(pool_name, summon_data)
|
pool_name = self.count_fortune(pool_name, summon_data)
|
||||||
elif pool == BannerType.WEAPON:
|
elif pool == StarRailBannerType.WEAPON:
|
||||||
summon_data = self.get_302_pool_data(total, all_five, all_four, no_five_star, no_four_star)
|
summon_data = self.get_302_pool_data(total, all_five, all_four, no_five_star, no_four_star)
|
||||||
pool_name = self.count_fortune(pool_name, summon_data, True)
|
pool_name = self.count_fortune(pool_name, summon_data, True)
|
||||||
elif pool == BannerType.PERMANENT:
|
elif pool == StarRailBannerType.PERMANENT:
|
||||||
summon_data = self.get_200_pool_data(total, all_five, all_four, no_five_star, no_four_star)
|
summon_data = self.get_200_pool_data(total, all_five, all_four, no_five_star, no_four_star)
|
||||||
pool_name = self.count_fortune(pool_name, summon_data)
|
pool_name = self.count_fortune(pool_name, summon_data)
|
||||||
last_time = data[0].time.strftime("%Y-%m-%d %H:%M")
|
last_time = data[0].time.strftime("%Y-%m-%d %H:%M")
|
||||||
@ -526,9 +510,9 @@ class GachaLog:
|
|||||||
}
|
}
|
||||||
|
|
||||||
async def get_pool_analysis(
|
async def get_pool_analysis(
|
||||||
self, user_id: int, client: Client, pool: BannerType, assets: AssetsService, group: bool
|
self, user_id: int, client: Client, pool: StarRailBannerType, assets: AssetsService, group: bool
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""获取抽卡记录分析数据
|
"""获取跃迁记录分析数据
|
||||||
:param user_id: 用户id
|
:param user_id: 用户id
|
||||||
:param client: genshin client
|
:param client: genshin client
|
||||||
:param pool: 池子类型
|
:param pool: 池子类型
|
||||||
@ -573,7 +557,7 @@ class GachaLog:
|
|||||||
}
|
}
|
||||||
|
|
||||||
async def get_all_five_analysis(self, user_id: int, client: Client, assets: AssetsService) -> dict:
|
async def get_all_five_analysis(self, user_id: int, client: Client, assets: AssetsService) -> dict:
|
||||||
"""获取五星抽卡记录分析数据
|
"""获取五星跃迁记录分析数据
|
||||||
:param user_id: 用户id
|
:param user_id: 用户id
|
||||||
:param client: genshin client
|
:param client: genshin client
|
||||||
:param assets: 资源服务
|
:param assets: 资源服务
|
||||||
@ -612,128 +596,3 @@ class GachaLog:
|
|||||||
"pool": pool_data,
|
"pool": pool_data,
|
||||||
"hasMore": False,
|
"hasMore": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def convert_xlsx_to_uigf(file: Union[str, PathLike, IO[bytes]], zh_dict: Dict) -> Dict:
|
|
||||||
"""转换 paimone.moe 或 非小酋 导出 xlsx 数据为 UIGF 格式
|
|
||||||
:param file: 导出的 xlsx 文件
|
|
||||||
:param zh_dict:
|
|
||||||
:return: UIGF 格式数据
|
|
||||||
"""
|
|
||||||
|
|
||||||
def from_paimon_moe(
|
|
||||||
uigf_gacha_type: UIGFGachaType, item_type: str, name: str, date_string: str, rank_type: int, _id: int
|
|
||||||
) -> UIGFItem:
|
|
||||||
item_type = ItemType.CHARACTER if item_type == "Character" else ItemType.WEAPON
|
|
||||||
return UIGFItem(
|
|
||||||
id=str(_id),
|
|
||||||
name=zh_dict[name],
|
|
||||||
gacha_type=uigf_gacha_type,
|
|
||||||
item_type=item_type,
|
|
||||||
rank_type=str(rank_type),
|
|
||||||
time=date_string,
|
|
||||||
uigf_gacha_type=uigf_gacha_type,
|
|
||||||
)
|
|
||||||
|
|
||||||
def from_uigf(
|
|
||||||
uigf_gacha_type: str,
|
|
||||||
gacha__type: str,
|
|
||||||
item_type: str,
|
|
||||||
name: str,
|
|
||||||
date_string: str,
|
|
||||||
rank_type: str,
|
|
||||||
_id: str,
|
|
||||||
) -> UIGFItem:
|
|
||||||
return UIGFItem(
|
|
||||||
id=_id,
|
|
||||||
name=name,
|
|
||||||
gacha_type=gacha__type,
|
|
||||||
item_type=item_type,
|
|
||||||
rank_type=rank_type,
|
|
||||||
time=date_string,
|
|
||||||
uigf_gacha_type=uigf_gacha_type,
|
|
||||||
)
|
|
||||||
|
|
||||||
def from_fxq(
|
|
||||||
uigf_gacha_type: UIGFGachaType, item_type: str, name: str, date_string: str, rank_type: int, _id: int
|
|
||||||
) -> UIGFItem:
|
|
||||||
item_type = ItemType.CHARACTER if item_type == "角色" else ItemType.WEAPON
|
|
||||||
return UIGFItem(
|
|
||||||
id=str(_id),
|
|
||||||
name=name,
|
|
||||||
gacha_type=uigf_gacha_type,
|
|
||||||
item_type=item_type,
|
|
||||||
rank_type=str(rank_type),
|
|
||||||
time=date_string,
|
|
||||||
uigf_gacha_type=uigf_gacha_type,
|
|
||||||
)
|
|
||||||
|
|
||||||
wb = load_workbook(file)
|
|
||||||
wb_len = len(wb.worksheets)
|
|
||||||
|
|
||||||
if wb_len == 6:
|
|
||||||
import_type = ImportType.PAIMONMOE
|
|
||||||
elif wb_len == 5:
|
|
||||||
import_type = ImportType.UIGF
|
|
||||||
elif wb_len == 4:
|
|
||||||
import_type = ImportType.FXQ
|
|
||||||
else:
|
|
||||||
raise GachaLogFileError("xlsx 格式错误")
|
|
||||||
|
|
||||||
paimonmoe_sheets = {
|
|
||||||
UIGFGachaType.BEGINNER: "Beginners' Wish",
|
|
||||||
UIGFGachaType.STANDARD: "Standard",
|
|
||||||
UIGFGachaType.CHARACTER: "Character Event",
|
|
||||||
UIGFGachaType.WEAPON: "Weapon Event",
|
|
||||||
}
|
|
||||||
fxq_sheets = {
|
|
||||||
UIGFGachaType.BEGINNER: "新手祈愿",
|
|
||||||
UIGFGachaType.STANDARD: "常驻祈愿",
|
|
||||||
UIGFGachaType.CHARACTER: "角色活动祈愿",
|
|
||||||
UIGFGachaType.WEAPON: "武器活动祈愿",
|
|
||||||
}
|
|
||||||
data = UIGFModel(info=UIGFInfo(export_app=import_type.value), list=[])
|
|
||||||
if import_type == ImportType.PAIMONMOE:
|
|
||||||
ws = wb["Information"]
|
|
||||||
if ws["B2"].value != PAIMONMOE_VERSION:
|
|
||||||
raise PaimonMoeGachaLogFileError(file_version=ws["B2"].value, support_version=PAIMONMOE_VERSION)
|
|
||||||
count = 1
|
|
||||||
for gacha_type in paimonmoe_sheets:
|
|
||||||
ws = wb[paimonmoe_sheets[gacha_type]]
|
|
||||||
for row in ws.iter_rows(min_row=2, values_only=True):
|
|
||||||
if row[0] is None:
|
|
||||||
break
|
|
||||||
data.list.append(from_paimon_moe(gacha_type, row[0], row[1], row[2], row[3], count))
|
|
||||||
count += 1
|
|
||||||
elif import_type == ImportType.UIGF:
|
|
||||||
ws = wb["原始数据"]
|
|
||||||
type_map = {}
|
|
||||||
count = 0
|
|
||||||
for row in ws["1"]:
|
|
||||||
if row.value is None:
|
|
||||||
break
|
|
||||||
type_map[row.value] = count
|
|
||||||
count += 1
|
|
||||||
for row in ws.iter_rows(min_row=2, values_only=True):
|
|
||||||
if row[0] is None:
|
|
||||||
break
|
|
||||||
data.list.append(
|
|
||||||
from_uigf(
|
|
||||||
row[type_map["uigf_gacha_type"]],
|
|
||||||
row[type_map["gacha_type"]],
|
|
||||||
row[type_map["item_type"]],
|
|
||||||
row[type_map["name"]],
|
|
||||||
row[type_map["time"]],
|
|
||||||
row[type_map["rank_type"]],
|
|
||||||
row[type_map["id"]],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for gacha_type in fxq_sheets:
|
|
||||||
ws = wb[fxq_sheets[gacha_type]]
|
|
||||||
for row in ws.iter_rows(min_row=2, values_only=True):
|
|
||||||
if row[0] is None:
|
|
||||||
break
|
|
||||||
data.list.append(from_fxq(gacha_type, row[2], row[1], row[0], row[3], row[6]))
|
|
||||||
|
|
||||||
return json.loads(data.json())
|
|
||||||
|
@ -4,15 +4,13 @@ from typing import Any, Dict, List, Union
|
|||||||
|
|
||||||
from pydantic import BaseModel, validator
|
from pydantic import BaseModel, validator
|
||||||
|
|
||||||
from metadata.shortname import not_real_roles, roleToId, weaponToId
|
from metadata.shortname import not_real_roles, roleToId, lightConeToId
|
||||||
from modules.gacha_log.const import UIGF_VERSION
|
from modules.gacha_log.const import UIWF_VERSION
|
||||||
|
|
||||||
|
|
||||||
class ImportType(Enum):
|
class ImportType(Enum):
|
||||||
PaiGram = "PaiGram"
|
PaiGram = "PaiGram"
|
||||||
PAIMONMOE = "PAIMONMOE"
|
UIWF = "UIWF"
|
||||||
FXQ = "FXQ"
|
|
||||||
UIGF = "UIGF"
|
|
||||||
UNKNOWN = "UNKNOWN"
|
UNKNOWN = "UNKNOWN"
|
||||||
|
|
||||||
|
|
||||||
@ -44,20 +42,20 @@ class GachaItem(BaseModel):
|
|||||||
|
|
||||||
@validator("name")
|
@validator("name")
|
||||||
def name_validator(cls, v):
|
def name_validator(cls, v):
|
||||||
if item_id := (roleToId(v) or weaponToId(v)):
|
if item_id := (roleToId(v) or lightConeToId(v)):
|
||||||
if item_id not in not_real_roles:
|
if item_id not in not_real_roles:
|
||||||
return v
|
return v
|
||||||
raise ValueError("Invalid name")
|
raise ValueError("Invalid name")
|
||||||
|
|
||||||
@validator("gacha_type")
|
@validator("gacha_type")
|
||||||
def check_gacha_type(cls, v):
|
def check_gacha_type(cls, v):
|
||||||
if v not in {"100", "200", "301", "302", "400"}:
|
if v not in {"1", "2", "11", "12"}:
|
||||||
raise ValueError("gacha_type must be 200, 301, 302 or 400")
|
raise ValueError("gacha_type must be 1, 2, 11 or 12")
|
||||||
return v
|
return v
|
||||||
|
|
||||||
@validator("item_type")
|
@validator("item_type")
|
||||||
def check_item_type(cls, item):
|
def check_item_type(cls, item):
|
||||||
if item not in {"角色", "武器"}:
|
if item not in {"角色", "光锥"}:
|
||||||
raise ValueError("error item type")
|
raise ValueError("error item type")
|
||||||
return item
|
return item
|
||||||
|
|
||||||
@ -74,10 +72,10 @@ class GachaLogInfo(BaseModel):
|
|||||||
update_time: datetime.datetime
|
update_time: datetime.datetime
|
||||||
import_type: str = ""
|
import_type: str = ""
|
||||||
item_list: Dict[str, List[GachaItem]] = {
|
item_list: Dict[str, List[GachaItem]] = {
|
||||||
"角色祈愿": [],
|
"角色跃迁": [],
|
||||||
"武器祈愿": [],
|
"光锥跃迁": [],
|
||||||
"常驻祈愿": [],
|
"常驻跃迁": [],
|
||||||
"新手祈愿": [],
|
"新手跃迁": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -131,37 +129,36 @@ class Pool:
|
|||||||
|
|
||||||
class ItemType(Enum):
|
class ItemType(Enum):
|
||||||
CHARACTER = "角色"
|
CHARACTER = "角色"
|
||||||
WEAPON = "武器"
|
LIGHTCONE = "光锥"
|
||||||
|
|
||||||
|
|
||||||
class UIGFGachaType(Enum):
|
class UIWFGachaType(Enum):
|
||||||
BEGINNER = "100"
|
BEGINNER = "2"
|
||||||
STANDARD = "200"
|
STANDARD = "1"
|
||||||
CHARACTER = "301"
|
CHARACTER = "11"
|
||||||
WEAPON = "302"
|
LIGHTCONE = "12"
|
||||||
CHARACTER2 = "400"
|
|
||||||
|
|
||||||
|
|
||||||
class UIGFItem(BaseModel):
|
class UIWFItem(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
name: str
|
name: str
|
||||||
count: str = "1"
|
count: str = "1"
|
||||||
gacha_type: UIGFGachaType
|
gacha_type: UIWFGachaType
|
||||||
item_id: str = ""
|
item_id: str = ""
|
||||||
item_type: ItemType
|
item_type: ItemType
|
||||||
rank_type: str
|
rank_type: str
|
||||||
time: str
|
time: str
|
||||||
uigf_gacha_type: UIGFGachaType
|
uigf_gacha_type: UIWFGachaType
|
||||||
|
|
||||||
|
|
||||||
class UIGFInfo(BaseModel):
|
class UIWFInfo(BaseModel):
|
||||||
uid: str = "0"
|
uid: str = "0"
|
||||||
lang: str = "zh-cn"
|
lang: str = "zh-cn"
|
||||||
export_time: str = ""
|
export_time: str = ""
|
||||||
export_timestamp: int = 0
|
export_timestamp: int = 0
|
||||||
export_app: str = ""
|
export_app: str = ""
|
||||||
export_app_version: str = ""
|
export_app_version: str = ""
|
||||||
uigf_version: str = UIGF_VERSION
|
uigf_version: str = UIWF_VERSION
|
||||||
|
|
||||||
def __init__(self, **data: Any):
|
def __init__(self, **data: Any):
|
||||||
super().__init__(**data)
|
super().__init__(**data)
|
||||||
@ -170,6 +167,6 @@ class UIGFInfo(BaseModel):
|
|||||||
self.export_timestamp = int(datetime.datetime.now().timestamp())
|
self.export_timestamp = int(datetime.datetime.now().timestamp())
|
||||||
|
|
||||||
|
|
||||||
class UIGFModel(BaseModel):
|
class UIWFModel(BaseModel):
|
||||||
info: UIGFInfo
|
info: UIWFInfo
|
||||||
list: List[UIGFItem]
|
list: List[UIWFItem]
|
||||||
|
@ -44,8 +44,6 @@ class Avatar(BaseModel):
|
|||||||
"""角色ID"""
|
"""角色ID"""
|
||||||
name: str
|
name: str
|
||||||
"""名称"""
|
"""名称"""
|
||||||
icon: str
|
|
||||||
"""图标"""
|
|
||||||
quality: Quality
|
quality: Quality
|
||||||
"""品质"""
|
"""品质"""
|
||||||
destiny: Destiny
|
destiny: Destiny
|
||||||
|
36
modules/wiki/models/avatar_config.py
Normal file
36
modules/wiki/models/avatar_config.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class AvatarName(BaseModel):
|
||||||
|
Hash: int
|
||||||
|
|
||||||
|
|
||||||
|
class AvatarConfig(BaseModel):
|
||||||
|
name: str = ""
|
||||||
|
AvatarID: int
|
||||||
|
AvatarName: AvatarName
|
||||||
|
AvatarVOTag: str
|
||||||
|
Release: bool
|
||||||
|
|
||||||
|
|
||||||
|
class AvatarIcon(BaseModel):
|
||||||
|
id: int
|
||||||
|
"""角色ID"""
|
||||||
|
name: str
|
||||||
|
"""名称"""
|
||||||
|
icon: List[str]
|
||||||
|
"""图标(从小到大)"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def gacha(self) -> str:
|
||||||
|
return self.icon[2]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon_(self) -> str:
|
||||||
|
return self.icon[0]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def normal(self) -> str:
|
||||||
|
return self.icon[1]
|
20
modules/wiki/models/light_cone_config.py
Normal file
20
modules/wiki/models/light_cone_config.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class LightConeIcon(BaseModel):
|
||||||
|
id: int
|
||||||
|
"""光锥ID"""
|
||||||
|
name: str
|
||||||
|
"""名称"""
|
||||||
|
icon: List[str]
|
||||||
|
"""图标(从小到大)"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def gacha(self) -> str:
|
||||||
|
return self.icon[1]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon_(self) -> str:
|
||||||
|
return self.icon[0]
|
@ -23,4 +23,3 @@ class Monster(BaseModel):
|
|||||||
"""抗性"""
|
"""抗性"""
|
||||||
find_area: str
|
find_area: str
|
||||||
"""发现地点"""
|
"""发现地点"""
|
||||||
|
|
||||||
|
34
modules/wiki/models/wiki.py
Normal file
34
modules/wiki/models/wiki.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
import ujson
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class Content(BaseModel):
|
||||||
|
content_id: int
|
||||||
|
"""内容ID"""
|
||||||
|
ext: str
|
||||||
|
"""扩展信息"""
|
||||||
|
icon: str
|
||||||
|
"""图标"""
|
||||||
|
summary: str
|
||||||
|
"""摘要"""
|
||||||
|
title: str
|
||||||
|
"""标题"""
|
||||||
|
article_user_name: str = ""
|
||||||
|
"""作者"""
|
||||||
|
bbs_url: str = ""
|
||||||
|
"""BBS对应地址"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self) -> Dict:
|
||||||
|
return ujson.loads(self.ext)
|
||||||
|
|
||||||
|
|
||||||
|
class Children(BaseModel):
|
||||||
|
id: int
|
||||||
|
"""分类ID"""
|
||||||
|
name: str
|
||||||
|
"""分类名称"""
|
||||||
|
list: List[Content]
|
||||||
|
"""内容列表"""
|
395
plugins/starrail/wish_log.py
Normal file
395
plugins/starrail/wish_log.py
Normal file
@ -0,0 +1,395 @@
|
|||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
from genshin.models import StarRailBannerType
|
||||||
|
from telegram import Document, InlineKeyboardButton, InlineKeyboardMarkup, Message, Update, User
|
||||||
|
from telegram.constants import ChatAction
|
||||||
|
from telegram.ext import CallbackContext, CommandHandler, ConversationHandler, MessageHandler, filters
|
||||||
|
from telegram.helpers import create_deep_linked_url
|
||||||
|
|
||||||
|
from core.dependence.assets import AssetsService
|
||||||
|
from core.plugin import Plugin, conversation, handler
|
||||||
|
from core.services.cookies import CookiesService
|
||||||
|
from core.services.players import PlayersService
|
||||||
|
from core.services.template.models import FileType
|
||||||
|
from core.services.template.services import TemplateService
|
||||||
|
from modules.gacha_log.error import (
|
||||||
|
GachaLogAccountNotFound,
|
||||||
|
GachaLogAuthkeyTimeout,
|
||||||
|
GachaLogFileError,
|
||||||
|
GachaLogInvalidAuthkey,
|
||||||
|
GachaLogMixedProvider,
|
||||||
|
GachaLogNotFound,
|
||||||
|
)
|
||||||
|
from modules.gacha_log.helpers import from_url_get_authkey
|
||||||
|
from modules.gacha_log.log import GachaLog
|
||||||
|
from plugins.tools.genshin import PlayerNotFoundError, GenshinHelper
|
||||||
|
from utils.log import logger
|
||||||
|
|
||||||
|
try:
|
||||||
|
import ujson as jsonlib
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
import json as jsonlib
|
||||||
|
|
||||||
|
INPUT_URL, INPUT_FILE, CONFIRM_DELETE = range(10100, 10103)
|
||||||
|
|
||||||
|
|
||||||
|
class WishLogPlugin(Plugin.Conversation):
|
||||||
|
"""跃迁记录导入/导出/分析"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
template_service: TemplateService,
|
||||||
|
players_service: PlayersService,
|
||||||
|
assets: AssetsService,
|
||||||
|
cookie_service: CookiesService,
|
||||||
|
helper: GenshinHelper,
|
||||||
|
):
|
||||||
|
self.template_service = template_service
|
||||||
|
self.players_service = players_service
|
||||||
|
self.assets_service = assets
|
||||||
|
self.cookie_service = cookie_service
|
||||||
|
self.gacha_log = GachaLog()
|
||||||
|
self.helper = helper
|
||||||
|
|
||||||
|
async def _refresh_user_data(
|
||||||
|
self, user: User, data: dict = None, authkey: str = None, verify_uid: bool = True
|
||||||
|
) -> str:
|
||||||
|
"""刷新用户数据
|
||||||
|
:param user: 用户
|
||||||
|
:param data: 数据
|
||||||
|
:param authkey: 认证密钥
|
||||||
|
:return: 返回信息
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
logger.debug("尝试获取已绑定的星穹铁道账号")
|
||||||
|
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
|
||||||
|
if authkey:
|
||||||
|
new_num = await self.gacha_log.get_gacha_log_data(user.id, client, authkey)
|
||||||
|
return "更新完成,本次没有新增数据" if new_num == 0 else f"更新完成,本次共新增{new_num}条跃迁记录"
|
||||||
|
if data:
|
||||||
|
new_num = await self.gacha_log.import_gacha_log_data(user.id, client, data, verify_uid)
|
||||||
|
return "更新完成,本次没有新增数据" if new_num == 0 else f"更新完成,本次共新增{new_num}条跃迁记录"
|
||||||
|
except GachaLogNotFound:
|
||||||
|
return "彦卿没有找到你的跃迁记录,快来私聊彦卿导入吧~"
|
||||||
|
except GachaLogAccountNotFound:
|
||||||
|
return "导入失败,可能文件包含的跃迁记录所属 uid 与你当前绑定的 uid 不同"
|
||||||
|
except GachaLogFileError:
|
||||||
|
return "导入失败,数据格式错误"
|
||||||
|
except GachaLogInvalidAuthkey:
|
||||||
|
return "更新数据失败,authkey 无效"
|
||||||
|
except GachaLogAuthkeyTimeout:
|
||||||
|
return "更新数据失败,authkey 已经过期"
|
||||||
|
except GachaLogMixedProvider:
|
||||||
|
return "导入失败,你已经通过其他方式导入过跃迁记录了,本次无法导入"
|
||||||
|
except PlayerNotFoundError:
|
||||||
|
logger.info("未查询到用户 %s[%s] 所绑定的账号信息", user.full_name, user.id)
|
||||||
|
return "彦卿没有找到您所绑定的账号信息,请先私聊彦卿绑定账号"
|
||||||
|
|
||||||
|
async def import_from_file(self, user: User, message: Message, document: Document = None) -> None:
|
||||||
|
if not document:
|
||||||
|
document = message.document
|
||||||
|
# TODO: 使用 mimetype 判断文件类型
|
||||||
|
if document.file_name.endswith(".json"):
|
||||||
|
file_type = "json"
|
||||||
|
else:
|
||||||
|
await message.reply_text("文件格式错误,请发送符合 UIWF 标准的跃迁记录文件")
|
||||||
|
return
|
||||||
|
if document.file_size > 5 * 1024 * 1024:
|
||||||
|
await message.reply_text("文件过大,请发送小于 5 MB 的文件")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
out = BytesIO()
|
||||||
|
await (await document.get_file()).download_to_memory(out=out)
|
||||||
|
if file_type == "json":
|
||||||
|
# bytesio to json
|
||||||
|
data = jsonlib.loads(out.getvalue().decode("utf-8"))
|
||||||
|
else:
|
||||||
|
await message.reply_text("文件解析失败,请检查文件")
|
||||||
|
return
|
||||||
|
except GachaLogFileError:
|
||||||
|
await message.reply_text("文件解析失败,请检查文件是否符合 UIWF 标准")
|
||||||
|
return
|
||||||
|
except (KeyError, IndexError, ValueError):
|
||||||
|
await message.reply_text("文件解析失败,请检查文件编码是否正确或符合 UIWF 标准")
|
||||||
|
return
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error("文件解析失败 %s", repr(exc))
|
||||||
|
await message.reply_text("文件解析失败,请检查文件是否符合 UIWF 标准")
|
||||||
|
return
|
||||||
|
await message.reply_chat_action(ChatAction.TYPING)
|
||||||
|
reply = await message.reply_text("文件解析成功,正在导入数据")
|
||||||
|
await message.reply_chat_action(ChatAction.TYPING)
|
||||||
|
try:
|
||||||
|
text = await self._refresh_user_data(user, data=data, verify_uid=file_type == "json")
|
||||||
|
except Exception as exc: # pylint: disable=W0703
|
||||||
|
logger.error("文件解析失败 %s", repr(exc))
|
||||||
|
text = "文件解析失败,请检查文件是否符合 UIWF 标准"
|
||||||
|
await reply.edit_text(text)
|
||||||
|
|
||||||
|
@conversation.entry_point
|
||||||
|
@handler.command(command="warp_log_import", filters=filters.ChatType.PRIVATE, block=False)
|
||||||
|
@handler.message(filters=filters.Regex("^导入跃迁记录(.*)") & filters.ChatType.PRIVATE, block=False)
|
||||||
|
async def command_start(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
message = update.effective_message
|
||||||
|
user = update.effective_user
|
||||||
|
args = self.get_args(context)
|
||||||
|
logger.info("用户 %s[%s] 导入跃迁记录命令请求", user.full_name, user.id)
|
||||||
|
authkey = from_url_get_authkey(args[0] if args else "")
|
||||||
|
# if not args:
|
||||||
|
# player_info = await self.players_service.get_player(user.id, region=RegionEnum.HYPERION)
|
||||||
|
# if player_info is not None:
|
||||||
|
# cookies = await self.cookie_service.get(user.id, account_id=player_info.account_id)
|
||||||
|
# if cookies is not None and cookies.data and "stoken" in cookies.data:
|
||||||
|
# if stuid := next(
|
||||||
|
# (value for key, value in cookies.data.items() if key in ["ltuid", "login_uid"]), None
|
||||||
|
# ):
|
||||||
|
# cookies.data["stuid"] = stuid
|
||||||
|
# client = genshin.Client(
|
||||||
|
# cookies=cookies.data,
|
||||||
|
# game=genshin.types.Game.STARRAIL,
|
||||||
|
# region=genshin.Region.CHINESE,
|
||||||
|
# lang="zh-cn",
|
||||||
|
# uid=player_info.player_id,
|
||||||
|
# )
|
||||||
|
# authkey = await get_authkey_by_stoken(client)
|
||||||
|
if not authkey:
|
||||||
|
await message.reply_text(
|
||||||
|
"<b>开始导入跃迁历史记录:请通过 https://starrailstation.com/cn/warp#import 获取跃迁记录链接后发送给我"
|
||||||
|
"(非 starrailstation.com 导出的文件数据)</b>\n\n"
|
||||||
|
"> 你还可以向彦卿发送从其他工具导出的 UIWF 标准的记录文件\n"
|
||||||
|
# "> 在绑定 Cookie 时添加 stoken 可能有特殊效果哦(仅限国服)\n"
|
||||||
|
"<b>注意:导入的数据将会与旧数据进行合并。</b>",
|
||||||
|
parse_mode="html",
|
||||||
|
)
|
||||||
|
return INPUT_URL
|
||||||
|
text = "小彦卿正在从服务器获取数据,请稍后"
|
||||||
|
if not args:
|
||||||
|
text += "\n\n> 由于你绑定的 Cookie 中存在 stoken ,本次通过 stoken 自动刷新数据"
|
||||||
|
reply = await message.reply_text(text)
|
||||||
|
await message.reply_chat_action(ChatAction.TYPING)
|
||||||
|
data = await self._refresh_user_data(user, authkey=authkey)
|
||||||
|
await reply.edit_text(data)
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
@conversation.state(state=INPUT_URL)
|
||||||
|
@handler.message(filters=~filters.COMMAND, block=False)
|
||||||
|
async def import_data_from_message(self, update: Update, _: CallbackContext) -> int:
|
||||||
|
message = update.effective_message
|
||||||
|
user = update.effective_user
|
||||||
|
if message.document:
|
||||||
|
await self.import_from_file(user, message)
|
||||||
|
return ConversationHandler.END
|
||||||
|
if not message.text:
|
||||||
|
await message.reply_text("请发送文件或链接")
|
||||||
|
return INPUT_URL
|
||||||
|
authkey = from_url_get_authkey(message.text)
|
||||||
|
reply = await message.reply_text("小彦卿正在从服务器获取数据,请稍后")
|
||||||
|
await message.reply_chat_action(ChatAction.TYPING)
|
||||||
|
text = await self._refresh_user_data(user, authkey=authkey)
|
||||||
|
await reply.edit_text(text)
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
@conversation.entry_point
|
||||||
|
@handler.command(command="warp_log_delete", filters=filters.ChatType.PRIVATE, block=False)
|
||||||
|
@handler.message(filters=filters.Regex("^删除跃迁记录(.*)") & filters.ChatType.PRIVATE, block=False)
|
||||||
|
async def command_start_delete(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
message = update.effective_message
|
||||||
|
user = update.effective_user
|
||||||
|
logger.info("用户 %s[%s] 删除跃迁记录命令请求", user.full_name, user.id)
|
||||||
|
try:
|
||||||
|
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
|
||||||
|
context.chat_data["uid"] = client.uid
|
||||||
|
except PlayerNotFoundError:
|
||||||
|
logger.info("未查询到用户 %s[%s] 所绑定的账号信息", user.full_name, user.id)
|
||||||
|
await message.reply_text("未查询到您所绑定的账号信息,请先绑定账号")
|
||||||
|
return ConversationHandler.END
|
||||||
|
_, status = await self.gacha_log.load_history_info(str(user.id), str(client.uid), only_status=True)
|
||||||
|
if not status:
|
||||||
|
await message.reply_text("你还没有导入跃迁记录哦~")
|
||||||
|
return ConversationHandler.END
|
||||||
|
await message.reply_text("你确定要删除跃迁记录吗?(此项操作无法恢复),如果确定请发送 ”确定“,发送其他内容取消")
|
||||||
|
return CONFIRM_DELETE
|
||||||
|
|
||||||
|
@conversation.state(state=CONFIRM_DELETE)
|
||||||
|
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=False)
|
||||||
|
async def command_confirm_delete(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
message = update.effective_message
|
||||||
|
user = update.effective_user
|
||||||
|
if message.text == "确定":
|
||||||
|
status = await self.gacha_log.remove_history_info(str(user.id), str(context.chat_data["uid"]))
|
||||||
|
await message.reply_text("跃迁记录已删除" if status else "跃迁记录删除失败")
|
||||||
|
return ConversationHandler.END
|
||||||
|
await message.reply_text("已取消")
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
@handler(CommandHandler, command="warp_log_force_delete", block=False, admin=True)
|
||||||
|
async def command_warp_log_force_delete(self, update: Update, context: CallbackContext):
|
||||||
|
message = update.effective_message
|
||||||
|
args = self.get_args(context)
|
||||||
|
if not args:
|
||||||
|
await message.reply_text("请指定用户ID")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
cid = int(args[0])
|
||||||
|
if cid < 0:
|
||||||
|
raise ValueError("Invalid cid")
|
||||||
|
client = await self.helper.get_genshin_client(cid, need_cookie=False)
|
||||||
|
_, status = await self.gacha_log.load_history_info(str(cid), str(client.uid), only_status=True)
|
||||||
|
if not status:
|
||||||
|
await message.reply_text("该用户还没有导入跃迁记录")
|
||||||
|
return
|
||||||
|
status = await self.gacha_log.remove_history_info(str(cid), str(client.uid))
|
||||||
|
await message.reply_text("跃迁记录已强制删除" if status else "跃迁记录删除失败")
|
||||||
|
except GachaLogNotFound:
|
||||||
|
await message.reply_text("该用户还没有导入跃迁记录")
|
||||||
|
except PlayerNotFoundError:
|
||||||
|
await message.reply_text("该用户暂未绑定账号")
|
||||||
|
except (ValueError, IndexError):
|
||||||
|
await message.reply_text("用户ID 不合法")
|
||||||
|
|
||||||
|
@handler(CommandHandler, command="warp_log_export", filters=filters.ChatType.PRIVATE, block=False)
|
||||||
|
@handler(MessageHandler, filters=filters.Regex("^导出跃迁记录(.*)") & filters.ChatType.PRIVATE, block=False)
|
||||||
|
async def command_start_export(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
message = update.effective_message
|
||||||
|
user = update.effective_user
|
||||||
|
logger.info("用户 %s[%s] 导出跃迁记录命令请求", user.full_name, user.id)
|
||||||
|
try:
|
||||||
|
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
|
||||||
|
await message.reply_chat_action(ChatAction.TYPING)
|
||||||
|
path = await self.gacha_log.gacha_log_to_uiwf(str(user.id), str(client.uid))
|
||||||
|
await message.reply_chat_action(ChatAction.UPLOAD_DOCUMENT)
|
||||||
|
await message.reply_document(document=open(path, "rb+"), caption="跃迁记录导出文件 - UIWF V1.0")
|
||||||
|
except GachaLogNotFound:
|
||||||
|
logger.info("未找到用户 %s[%s] 的跃迁记录", user.full_name, user.id)
|
||||||
|
buttons = [
|
||||||
|
[InlineKeyboardButton("点我导入", url=create_deep_linked_url(context.bot.username, "warp_log_import"))]
|
||||||
|
]
|
||||||
|
await message.reply_text("彦卿没有找到你的跃迁记录,快来私聊彦卿导入吧~", reply_markup=InlineKeyboardMarkup(buttons))
|
||||||
|
except GachaLogAccountNotFound:
|
||||||
|
await message.reply_text("导入失败,可能文件包含的跃迁记录所属 uid 与你当前绑定的 uid 不同")
|
||||||
|
except GachaLogFileError:
|
||||||
|
await message.reply_text("导入失败,数据格式错误")
|
||||||
|
except PlayerNotFoundError:
|
||||||
|
logger.info("未查询到用户 %s[%s] 所绑定的账号信息", user.full_name, user.id)
|
||||||
|
await message.reply_text("未查询到您所绑定的账号信息,请先绑定账号")
|
||||||
|
|
||||||
|
@handler(CommandHandler, command="warp_log", block=False)
|
||||||
|
@handler(MessageHandler, filters=filters.Regex("^跃迁记录?(光锥|角色|常驻|新手)$"), block=False)
|
||||||
|
async def command_start_analysis(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
message = update.effective_message
|
||||||
|
user = update.effective_user
|
||||||
|
pool_type = StarRailBannerType.CHARACTER
|
||||||
|
if args := self.get_args(context):
|
||||||
|
if "光锥" in args:
|
||||||
|
pool_type = StarRailBannerType.WEAPON
|
||||||
|
elif "常驻" in args:
|
||||||
|
pool_type = StarRailBannerType.STANDARD
|
||||||
|
elif "新手" in args:
|
||||||
|
pool_type = StarRailBannerType.NOVICE
|
||||||
|
logger.info("用户 %s[%s] 跃迁记录命令请求 || 参数 %s", user.full_name, user.id, pool_type.name)
|
||||||
|
try:
|
||||||
|
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
|
||||||
|
await message.reply_chat_action(ChatAction.TYPING)
|
||||||
|
data = await self.gacha_log.get_analysis(user.id, client, pool_type, self.assets_service)
|
||||||
|
if isinstance(data, str):
|
||||||
|
reply_message = await message.reply_text(data)
|
||||||
|
if filters.ChatType.GROUPS.filter(message):
|
||||||
|
self.add_delete_message_job(reply_message, delay=300)
|
||||||
|
self.add_delete_message_job(message, delay=300)
|
||||||
|
else:
|
||||||
|
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
|
||||||
|
png_data = await self.template_service.render(
|
||||||
|
"starrail/gacha_log/gacha_log.html",
|
||||||
|
data,
|
||||||
|
full_page=True,
|
||||||
|
file_type=FileType.DOCUMENT if len(data.get("fiveLog")) > 36 else FileType.PHOTO,
|
||||||
|
query_selector=".body_box",
|
||||||
|
)
|
||||||
|
if png_data.file_type == FileType.DOCUMENT:
|
||||||
|
await png_data.reply_document(message, filename="跃迁记录.png")
|
||||||
|
else:
|
||||||
|
await png_data.reply_photo(message)
|
||||||
|
except GachaLogNotFound:
|
||||||
|
logger.info("未找到用户 %s[%s] 的跃迁记录", user.full_name, user.id)
|
||||||
|
buttons = [
|
||||||
|
[InlineKeyboardButton("点我导入", url=create_deep_linked_url(context.bot.username, "warp_log_import"))]
|
||||||
|
]
|
||||||
|
await message.reply_text("彦卿没有找到你此卡池的跃迁记录,快来点击按钮私聊彦卿导入吧~", reply_markup=InlineKeyboardMarkup(buttons))
|
||||||
|
except PlayerNotFoundError:
|
||||||
|
logger.info("未查询到用户 %s[%s] 所绑定的账号信息", user.full_name, user.id)
|
||||||
|
buttons = [[InlineKeyboardButton("点我绑定账号", url=create_deep_linked_url(context.bot.username, "set_uid"))]]
|
||||||
|
if filters.ChatType.GROUPS.filter(message):
|
||||||
|
reply_message = await message.reply_text(
|
||||||
|
"未查询到您所绑定的账号信息,请先私聊彦卿绑定账号", reply_markup=InlineKeyboardMarkup(buttons)
|
||||||
|
)
|
||||||
|
self.add_delete_message_job(reply_message, delay=30)
|
||||||
|
self.add_delete_message_job(message, delay=30)
|
||||||
|
else:
|
||||||
|
await message.reply_text("未查询到您所绑定的账号信息,请先绑定账号", reply_markup=InlineKeyboardMarkup(buttons))
|
||||||
|
|
||||||
|
@handler(CommandHandler, command="warp_count", block=False)
|
||||||
|
@handler(MessageHandler, filters=filters.Regex("^跃迁统计?(光锥|角色|常驻|新手)$"), block=False)
|
||||||
|
async def command_start_count(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
message = update.effective_message
|
||||||
|
user = update.effective_user
|
||||||
|
pool_type = StarRailBannerType.CHARACTER
|
||||||
|
all_five = False
|
||||||
|
if args := self.get_args(context):
|
||||||
|
if "光锥" in args:
|
||||||
|
pool_type = StarRailBannerType.WEAPON
|
||||||
|
elif "常驻" in args:
|
||||||
|
pool_type = StarRailBannerType.STANDARD
|
||||||
|
elif "新手" in args:
|
||||||
|
pool_type = StarRailBannerType.NOVICE
|
||||||
|
if "仅五星" in args:
|
||||||
|
all_five = True
|
||||||
|
logger.info("用户 %s[%s] 跃迁统计命令请求 || 参数 %s || 仅五星 %s", user.full_name, user.id, pool_type.name, all_five)
|
||||||
|
try:
|
||||||
|
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
|
||||||
|
group = filters.ChatType.GROUPS.filter(message)
|
||||||
|
await message.reply_chat_action(ChatAction.TYPING)
|
||||||
|
if all_five:
|
||||||
|
data = await self.gacha_log.get_all_five_analysis(user.id, client, self.assets_service)
|
||||||
|
else:
|
||||||
|
data = await self.gacha_log.get_pool_analysis(user.id, client, pool_type, self.assets_service, group)
|
||||||
|
if isinstance(data, str):
|
||||||
|
reply_message = await message.reply_text(data)
|
||||||
|
if filters.ChatType.GROUPS.filter(message):
|
||||||
|
self.add_delete_message_job(reply_message)
|
||||||
|
self.add_delete_message_job(message)
|
||||||
|
else:
|
||||||
|
document = False
|
||||||
|
if data["hasMore"] and not group:
|
||||||
|
document = True
|
||||||
|
data["hasMore"] = False
|
||||||
|
await message.reply_chat_action(ChatAction.UPLOAD_DOCUMENT if document else ChatAction.UPLOAD_PHOTO)
|
||||||
|
png_data = await self.template_service.render(
|
||||||
|
"starrail/gacha_count/gacha_count.html",
|
||||||
|
data,
|
||||||
|
full_page=True,
|
||||||
|
query_selector=".body_box",
|
||||||
|
file_type=FileType.DOCUMENT if document else FileType.PHOTO,
|
||||||
|
)
|
||||||
|
if document:
|
||||||
|
await png_data.reply_document(message, filename="跃迁统计.png")
|
||||||
|
else:
|
||||||
|
await png_data.reply_photo(message)
|
||||||
|
except GachaLogNotFound:
|
||||||
|
logger.info("未找到用户 %s[%s] 的跃迁记录", user.full_name, user.id)
|
||||||
|
buttons = [
|
||||||
|
[InlineKeyboardButton("点我导入", url=create_deep_linked_url(context.bot.username, "warp_log_import"))]
|
||||||
|
]
|
||||||
|
await message.reply_text("彦卿没有找到你此卡池的跃迁记录,快来私聊彦卿导入吧~", reply_markup=InlineKeyboardMarkup(buttons))
|
||||||
|
except PlayerNotFoundError:
|
||||||
|
logger.info("未查询到用户 %s[%s] 所绑定的账号信息", user.full_name, user.id)
|
||||||
|
buttons = [[InlineKeyboardButton("点我绑定账号", url=create_deep_linked_url(context.bot.username, "set_uid"))]]
|
||||||
|
if filters.ChatType.GROUPS.filter(message):
|
||||||
|
reply_message = await message.reply_text(
|
||||||
|
"未查询到您所绑定的账号信息,请先私聊彦卿绑定账号", reply_markup=InlineKeyboardMarkup(buttons)
|
||||||
|
)
|
||||||
|
self.add_delete_message_job(reply_message, delay=30)
|
||||||
|
|
||||||
|
self.add_delete_message_job(message, delay=30)
|
||||||
|
else:
|
||||||
|
await message.reply_text("未查询到您所绑定的账号信息,请先绑定账号", reply_markup=InlineKeyboardMarkup(buttons))
|
@ -60,14 +60,14 @@
|
|||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- <div class="command-description">角色卡片</div>-->
|
<!-- <div class="command-description">角色卡片</div>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- <!– 最高查询类 –>-->
|
<!-- 最高查询类 -->
|
||||||
<!-- <div class="command">-->
|
<div class="command">
|
||||||
<!-- <div class="command-name">-->
|
<div class="command-name">
|
||||||
<!-- /dailynote-->
|
/dailynote
|
||||||
<!-- <i class="fa fa-id-card-o ml-2"></i>-->
|
<i class="fa fa-id-card-o ml-2"></i>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<!-- <div class="command-description">查询实时便笺</div>-->
|
<div class="command-description">查询实时便笺</div>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<div class="command">
|
<div class="command">
|
||||||
<div class="command-name">
|
<div class="command-name">
|
||||||
/ledger
|
/ledger
|
||||||
@ -103,21 +103,21 @@
|
|||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- <div class="command-description">原神账号注册时间</div>-->
|
<!-- <div class="command-description">原神账号注册时间</div>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- <!–! gacha_log 相关 –>-->
|
<!--! warp_log 相关 -->
|
||||||
<!-- <div class="command">-->
|
<div class="command">
|
||||||
<!-- <div class="command-name">-->
|
<div class="command-name">
|
||||||
<!-- /gacha_log-->
|
/warp_log
|
||||||
<!-- <i class="fa fa-user-circle-o ml-2"></i>-->
|
<i class="fa fa-user-circle-o ml-2"></i>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<!-- <div class="command-description">抽卡记录</div>-->
|
<div class="command-description">跃迁记录</div>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<!-- <div class="command">-->
|
<div class="command">
|
||||||
<!-- <div class="command-name">-->
|
<div class="command-name">
|
||||||
<!-- /gacha_count-->
|
/warp_count
|
||||||
<!-- <i class="fa fa-user-circle-o ml-2"></i>-->
|
<i class="fa fa-user-circle-o ml-2"></i>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<!-- <div class="command-description">抽卡统计</div>-->
|
<div class="command-description">跃迁统计</div>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<!-- <div class="command">-->
|
<!-- <div class="command">-->
|
||||||
<!-- <div class="command-name">-->
|
<!-- <div class="command-name">-->
|
||||||
<!-- /pay_log-->
|
<!-- /pay_log-->
|
||||||
@ -181,19 +181,19 @@
|
|||||||
<!-- 派蒙的十万个为什么-->
|
<!-- 派蒙的十万个为什么-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- <!–! gacha_log 相关 –>-->
|
<!--! warp_log 相关 -->
|
||||||
<!-- <div class="command">-->
|
<div class="command">
|
||||||
<!-- <div class="command-name">/gacha_log_import</div>-->
|
<div class="command-name">/warp_log_import</div>
|
||||||
<!-- <div class="command-description">导入抽卡记录</div>-->
|
<div class="command-description">导入跃迁记录</div>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<!-- <div class="command">-->
|
<div class="command">
|
||||||
<!-- <div class="command-name">/gacha_log_export</div>-->
|
<div class="command-name">/warp_log_export</div>
|
||||||
<!-- <div class="command-description">导出抽卡记录</div>-->
|
<div class="command-description">导出跃迁记录</div>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<!-- <div class="command">-->
|
<div class="command">
|
||||||
<!-- <div class="command-name">/gacha_log_delete</div>-->
|
<div class="command-name">/warp_log_delete</div>
|
||||||
<!-- <div class="command-description">删除抽卡记录</div>-->
|
<div class="command-description">删除跃迁记录</div>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<!-- <!–! pay_log 相关 –>-->
|
<!-- <!–! pay_log 相关 –>-->
|
||||||
<!-- <div class="command">-->
|
<!-- <div class="command">-->
|
||||||
<!-- <div class="command-name">/pay_log_import</div>-->
|
<!-- <div class="command-name">/pay_log_import</div>-->
|
||||||
@ -207,7 +207,7 @@
|
|||||||
<!-- <div class="command-name">/pay_log_delete</div>-->
|
<!-- <div class="command-name">/pay_log_delete</div>-->
|
||||||
<!-- <div class="command-description">删除充值记录</div>-->
|
<!-- <div class="command-description">删除充值记录</div>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!--! user 相关 -->
|
<!-- ! user 相关-->
|
||||||
<!-- <div class="command">-->
|
<!-- <div class="command">-->
|
||||||
<!-- <div class="command-name">/setuid</div>-->
|
<!-- <div class="command-name">/setuid</div>-->
|
||||||
<!-- <div class="command-description">添加/重设UID(请私聊BOT)</div>-->
|
<!-- <div class="command-description">添加/重设UID(请私聊BOT)</div>-->
|
||||||
|
47
resources/starrail/gacha_count/example.html
Normal file
47
resources/starrail/gacha_count/example.html
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
|
||||||
|
<link rel="shortcut icon" href="#"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="gacha_count.css"/>
|
||||||
|
<link rel="preload" href="../../fonts/HYWenHei-85W.ttf" as="font">
|
||||||
|
<link rel="preload" href="./../../fonts/tttgbnumber.ttf" as="font">
|
||||||
|
<link rel="preload" href="./../abyss/background/roleStarBg5.png" as="image">
|
||||||
|
<link rel="preload" href="./../abyss/background/roleStarBg4.png" as="image">
|
||||||
|
<style>
|
||||||
|
.head_box {
|
||||||
|
background-position-x: 42px;
|
||||||
|
background: #fff url(../gacha_log/img/提纳里.png) no-repeat;
|
||||||
|
background-size: auto 101%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body id="container" class="body_box">
|
||||||
|
<div class="container">
|
||||||
|
<div class="head_box">
|
||||||
|
<div class="id_text">ID: 10001</div>
|
||||||
|
<h2 class="day_text">抽卡统计-角色祈愿</h2>
|
||||||
|
<img class="genshin_logo" src="./../../bot/help/background/genshin.png"/>
|
||||||
|
</div>
|
||||||
|
<div class="pool_box">
|
||||||
|
<div class="title_box">
|
||||||
|
<div class="name_box">
|
||||||
|
<div class="title"><h2>「枫原万叶、可莉」</h2></div>
|
||||||
|
<span class="label label_301">98抽</span>
|
||||||
|
</div>
|
||||||
|
<span class="date">2022-08-02 - 2022-08-02</span>
|
||||||
|
</div>
|
||||||
|
<div class="list_box">
|
||||||
|
<div class="item">
|
||||||
|
<div class="bg5"></div>
|
||||||
|
<span class="num life5">20</span>
|
||||||
|
<img class="role_img" src=""/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hasMore">*完整数据请私聊查看</div>
|
||||||
|
<div class="logo">Template By Yunzai-Bot</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
214
resources/starrail/gacha_count/gacha_count.css
Normal file
214
resources/starrail/gacha_count/gacha_count.css
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "tttgbnumber";
|
||||||
|
src: url("./../../fonts/tttgbnumber.ttf");
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "HYWenHei-55W";
|
||||||
|
src: url("../../fonts/HYWenHei-85W.ttf");
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-size: 16px;
|
||||||
|
width: 530px;
|
||||||
|
color: #1E1F20;
|
||||||
|
transform: scale(1.5);
|
||||||
|
transform-origin: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 530px;
|
||||||
|
padding: 20px 15px 10px 15px;
|
||||||
|
background-color: #F5F6FB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head_box {
|
||||||
|
border-radius: 15px;
|
||||||
|
font-family: tttgbnumber, serif;
|
||||||
|
padding: 10px 20px;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 5px 10px 0 rgb(0 0 0 / 15%);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.head_box .id_text {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head_box .day_text {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head_box .genshin_logo {
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: 15px;
|
||||||
|
width: 97px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base_info {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uid {
|
||||||
|
font-family: tttgbnumber, serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pool_box {
|
||||||
|
font-family: HYWenHei-55W, serif;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 10px 5px 5px 5px;
|
||||||
|
background: #FFF;
|
||||||
|
box-shadow: 0 5px 10px 0 rgb(0 0 0 / 15%);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title_box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 210px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name_box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title_box .date {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list_box {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
margin: 0 0 10px 10px;
|
||||||
|
border-radius: 7px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2px 6px 0 rgb(132 93 90 / 30%);
|
||||||
|
height: 70px;
|
||||||
|
width: 70px;
|
||||||
|
background: #E9E5DC;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item .role_img {
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background-size: 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
/* filter: contrast(95%); */
|
||||||
|
}
|
||||||
|
|
||||||
|
.item .num {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 9;
|
||||||
|
font-size: 18px;
|
||||||
|
text-align: center;
|
||||||
|
color: #FFF;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 1px 5px;
|
||||||
|
background: rgb(0 0 0 / 50%);
|
||||||
|
font-family: "tttgbnumber", serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label_301 {
|
||||||
|
background-color: rgb(235 106 75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label_302 {
|
||||||
|
background-color: #E69449;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label_200 {
|
||||||
|
background-color: #757CC8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: #FFF;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 2px 7px;
|
||||||
|
vertical-align: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg5 {
|
||||||
|
background-image: url(./../../genshin/abyss/background/roleStarBg5.png);
|
||||||
|
width: 100%;
|
||||||
|
height: 70px;
|
||||||
|
/* filter: brightness(1.1); */
|
||||||
|
background-size: 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg4 {
|
||||||
|
width: 100%;
|
||||||
|
height: 70px;
|
||||||
|
background-image: url(./../../genshin/abyss/background/roleStarBg4.png);
|
||||||
|
background-size: 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list_box .item .life1 {
|
||||||
|
background-color: #62A8EA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list_box .item .life2 {
|
||||||
|
background-color: #62A8EA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list_box .item .life3 {
|
||||||
|
background-color: #45B97C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list_box .item .life4 {
|
||||||
|
background-color: #45B97C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list_box .item .life5 {
|
||||||
|
background-color: #FF5722;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list_box .item .life6 {
|
||||||
|
background-color: #FF5722;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: "tttgbnumber", serif;
|
||||||
|
text-align: center;
|
||||||
|
color: #7994A7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hasMore {
|
||||||
|
font-size: 12px;
|
||||||
|
margin: -6px 0 10px 6px;
|
||||||
|
color: #7F858A;
|
||||||
|
}
|
55
resources/starrail/gacha_count/gacha_count.html
Normal file
55
resources/starrail/gacha_count/gacha_count.html
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
|
||||||
|
<link rel="shortcut icon" href="#"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="gacha_count.css"/>
|
||||||
|
<link rel="preload" href="../../fonts/HYWenHei-85W.ttf" as="font">
|
||||||
|
<link rel="preload" href="./../../fonts/tttgbnumber.ttf" as="font">
|
||||||
|
<link rel="preload" href="./../abyss/background/roleStarBg5.png" as="image">
|
||||||
|
<link rel="preload" href="./../abyss/background/roleStarBg4.png" as="image">
|
||||||
|
<style>
|
||||||
|
.head_box {
|
||||||
|
background-position-x: 42px;
|
||||||
|
background: #fff url(../gacha_log/img/提纳里.png) no-repeat;
|
||||||
|
background-size: auto 101%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body id="container" class="body_box">
|
||||||
|
<div class="container">
|
||||||
|
<div class="head_box">
|
||||||
|
<div class="id_text">ID: {{ uid }}</div>
|
||||||
|
<h2 class="day_text">跃迁统计-{{ typeName }}</h2>
|
||||||
|
<img class="genshin_logo" src="./../../bot/help/background/genshin.png" alt=""/>
|
||||||
|
</div>
|
||||||
|
{% for val in pool %}
|
||||||
|
<div class="pool_box">
|
||||||
|
<div class="title_box">
|
||||||
|
<div class="name_box">
|
||||||
|
<div class="title"><h2>「{{ val.name }}」</h2></div>
|
||||||
|
<span class="label label_301">{{ val.count }}抽</span>
|
||||||
|
</div>
|
||||||
|
{% if typeName != "常驻跃迁" %}
|
||||||
|
<span class="date">{{ val.start }} - {{ val.end }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="list_box">
|
||||||
|
{% for v in val.list %}
|
||||||
|
<div class="item">
|
||||||
|
<div class="bg{{ v.rank_type }}"></div>
|
||||||
|
<span class="num {% if v.count>=5 and v.rank_type == 5 %}life5{% endif %}">{{ v.count }}</span>
|
||||||
|
<img class="role_img" src="{{ v.icon }}" alt=""/>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% if hasMore %}
|
||||||
|
<div class="hasMore">*完整数据请私聊查看</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="logo">Template By Yunzai-Bot</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
77
resources/starrail/gacha_log/example.html
Normal file
77
resources/starrail/gacha_log/example.html
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
|
||||||
|
<link rel="shortcut icon" href="#"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="gacha_log.css"/>
|
||||||
|
<link rel="preload" href="./../../fonts/tttgbnumber.ttf" as="font">
|
||||||
|
<link rel="preload" href="./img/提纳里.png" as="image">
|
||||||
|
<link rel="preload" href="./../abyss/background/roleStarBg5.png" as="image">
|
||||||
|
<style>
|
||||||
|
.head_box {
|
||||||
|
background-position-x: 42px;
|
||||||
|
background: #fff url(./img/提纳里.png) no-repeat;
|
||||||
|
background-size: auto 101%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body id="container" class="body_box">
|
||||||
|
<div class="container">
|
||||||
|
<div class="info_box">
|
||||||
|
|
||||||
|
<div class="head_box">
|
||||||
|
<div class="id_text">
|
||||||
|
ID: 10001
|
||||||
|
</div>
|
||||||
|
<h2 class="day_text">
|
||||||
|
81抽
|
||||||
|
<span class="label label_301">角色祈愿池 · 欧</span>
|
||||||
|
</h2>
|
||||||
|
<img class="genshin_logo" src="./../../bot/help/background/genshin.png" alt=""/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="data_box">
|
||||||
|
<div class="tab_lable">数据总览</div>
|
||||||
|
<div class="data_line">
|
||||||
|
<div class="data_line_item">
|
||||||
|
<div class="num">1<span class="unit">抽</span></div>
|
||||||
|
<div class="lable">未出五星</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="line_box">
|
||||||
|
<span class="line"></span>
|
||||||
|
<span class="text">五星历史 2022-10-07 01:10 ~ 2022-10-07 23:10</span>
|
||||||
|
<span class="line"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="card_list">
|
||||||
|
<div class="item star5">
|
||||||
|
<span class="minimum">UP</span>
|
||||||
|
<img class="role" src="" alt=""/>
|
||||||
|
<!-- <div class="num">{{val.num}}</div>-->
|
||||||
|
<div class="num_name">80</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="line_box">
|
||||||
|
<span class="line"></span>
|
||||||
|
<span class="text">四星最近历史</span>
|
||||||
|
<span class="line"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card_list">
|
||||||
|
<div class="item star4">
|
||||||
|
<img class="role" src="" alt=""/>
|
||||||
|
<!-- <div class="num">{{val.num}}</div>-->
|
||||||
|
<div class="num_name">10</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="logo"> Template By Yunzai-Bot</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
341
resources/starrail/gacha_log/gacha_log.css
Normal file
341
resources/starrail/gacha_log/gacha_log.css
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "tttgbnumber";
|
||||||
|
src: url("./../../fonts/tttgbnumber.ttf");
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #1e1f20;
|
||||||
|
font-family: PingFangSC-Medium, PingFang SC, sans-serif;
|
||||||
|
transform: scale(1.5);
|
||||||
|
transform-origin: 0 0;
|
||||||
|
width: 510px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 510px;
|
||||||
|
padding: 20px 15px 10px 15px;
|
||||||
|
background-color: #f5f6fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head_box {
|
||||||
|
border-radius: 9999px;
|
||||||
|
font-family: tttgbnumber, sans-serif;
|
||||||
|
padding: 10px 20px;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 5px 10px 0 rgb(0 0 0 / 15%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.head_box .id_text {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head_box .day_text {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head_box .genshin_logo {
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: 15px;
|
||||||
|
width: 97px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: "tttgbnumber", serif;
|
||||||
|
text-align: center;
|
||||||
|
color: #7994a7;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data_box {
|
||||||
|
border-radius: 15px;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 20px 0 5px 10px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 5px 10px 0 rgb(0 0 0 / 15%);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab_lable {
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
left: -8px;
|
||||||
|
background: #d4b98c;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
border-radius: 15px 0 15px 15px;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data_line {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data_line_item {
|
||||||
|
width: 100px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
/* margin: 0 20px; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.num {
|
||||||
|
font-family: tttgbnumber, serif;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.num .unit {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data_box .lable {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #7f858a;
|
||||||
|
line-height: 1;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info_box_border {
|
||||||
|
border-radius: 15px;
|
||||||
|
|
||||||
|
/* margin-top: 20px; */
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 6px 0 5px 10px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 5px 10px 0 rgb(0 0 0 / 15%);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_list .item {
|
||||||
|
margin: 0 8px 10px 0;
|
||||||
|
border-radius: 7px;
|
||||||
|
box-shadow: 0 2px 6px 0 rgb(132 93 90 / 30%);
|
||||||
|
height: 90px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #e7e5d9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_list .item img {
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
border-radius: 7px 7px 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_list .item.star5 img {
|
||||||
|
background-image: url(./../../genshin/abyss/background/roleStarBg5.png);
|
||||||
|
width: 100%;
|
||||||
|
height: 70px;
|
||||||
|
/* filter: brightness(1.1); */
|
||||||
|
background-size: 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_list .item.star4 img {
|
||||||
|
width: 100%;
|
||||||
|
height: 70px;
|
||||||
|
background-image: url(./../../genshin/abyss/background/roleStarBg4.png);
|
||||||
|
background-size: 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_list .item .num {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 9;
|
||||||
|
font-size: 18px;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 1px 5px;
|
||||||
|
background: rgb(0 0 0 / 50%);
|
||||||
|
font-family: "tttgbnumber", serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_list .item .name,
|
||||||
|
.card_list .item .num_name {
|
||||||
|
position: absolute;
|
||||||
|
top: 71px;
|
||||||
|
left: 0;
|
||||||
|
z-index: 9;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 16px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_list .item .num_name {
|
||||||
|
font-family: "tttgbnumber", serif;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base_info {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uid::before {
|
||||||
|
content: " ";
|
||||||
|
position: absolute;
|
||||||
|
width: 5px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 1px;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
background: #d3bc8d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label_301 {
|
||||||
|
background-color: rgb(235 106 75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label_302 {
|
||||||
|
background-color: #e69449;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label_200 {
|
||||||
|
background-color: #757cc8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 2px 7px;
|
||||||
|
vertical-align: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ritem {
|
||||||
|
display: flex;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info_role {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: 0 0 5px 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ritem .role {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: #ffb285;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ritem .weapon_box {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ritem .weapon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: #ffb285;
|
||||||
|
border-radius: 100%;
|
||||||
|
transform: scale(1.5);
|
||||||
|
-webkit-transform: scale(1.5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.ritem .role_text {
|
||||||
|
margin: 2px 3px 0 2px;
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ritem .role_name {
|
||||||
|
width: 24px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ritem .role_num {
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line_box {
|
||||||
|
height: 32px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #7d7d7d;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line_box .line {
|
||||||
|
height: 2px;
|
||||||
|
flex-grow: 1;
|
||||||
|
background-color: #ebebeb;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.red {
|
||||||
|
color: #f21000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orange {
|
||||||
|
color: #ff8d00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green {
|
||||||
|
color: #12d88c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blue {
|
||||||
|
color: #4169e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.purple {
|
||||||
|
color: #7500ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minimum {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 9;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 1px 3px;
|
||||||
|
background-color: rgb(0 0 0 / 80%);
|
||||||
|
font-family: "tttgbnumber", serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hasMore {
|
||||||
|
font-size: 12px;
|
||||||
|
margin: 6px 0;
|
||||||
|
color: #7f858a;
|
||||||
|
}
|
87
resources/starrail/gacha_log/gacha_log.html
Normal file
87
resources/starrail/gacha_log/gacha_log.html
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
|
||||||
|
<link rel="shortcut icon" href="#"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="gacha_log.css"/>
|
||||||
|
<link rel="preload" href="./../../fonts/tttgbnumber.ttf" as="font">
|
||||||
|
<link rel="preload" href="./img/提纳里.png" as="image">
|
||||||
|
<link rel="preload" href="./../abyss/background/roleStarBg5.png" as="image">
|
||||||
|
<link rel="preload" href="./../abyss/background/roleStarBg4.png" as="image">
|
||||||
|
<style>
|
||||||
|
.head_box {
|
||||||
|
background-position: right center;
|
||||||
|
background: #fff url(./img/提纳里.png) no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body id="container" class="body_box">
|
||||||
|
<div class="container">
|
||||||
|
<div class="info_box">
|
||||||
|
|
||||||
|
<div class="head_box">
|
||||||
|
<div class="id_text">
|
||||||
|
ID: {{ uid }}
|
||||||
|
</div>
|
||||||
|
<h2 class="day_text">
|
||||||
|
{{ allNum }}抽
|
||||||
|
<span class="label label_{{type}}">{{ typeName }}</span>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="data_box">
|
||||||
|
<div class="tab_lable">数据总览</div>
|
||||||
|
{% for val in line %}
|
||||||
|
<div class="data_line">
|
||||||
|
{% for item in val %}
|
||||||
|
<div class="data_line_item">
|
||||||
|
<div class="num">{{item.num}}<span class="unit">{{item.unit}}</span></div>
|
||||||
|
<div class="lable">{{item.lable}}</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<div class="line_box">
|
||||||
|
<span class="line"></span>
|
||||||
|
<span class="text">五星历史 {{firstTime}} ~ {{lastTime}}</span>
|
||||||
|
<span class="line"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="card_list">
|
||||||
|
{% for val in fiveLog %}
|
||||||
|
<div class="item star5">
|
||||||
|
{% if val.isUp %}
|
||||||
|
<span class="minimum">UP</span>
|
||||||
|
{% endif %}
|
||||||
|
<img class="role" src="{{ val.icon }}" alt=""/>
|
||||||
|
<!-- <div class="num">{{val.num}}</div>-->
|
||||||
|
<div class="num_name">{{ val.count }}</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="line_box">
|
||||||
|
<span class="line"></span>
|
||||||
|
<span class="text">四星最近历史</span>
|
||||||
|
<span class="line"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card_list">
|
||||||
|
{% for val in fourLog %}
|
||||||
|
<div class="item star4">
|
||||||
|
<img class="role" src="{{ val.icon }}" alt=""/>
|
||||||
|
<!-- <div class="num">{{val.num}}</div>-->
|
||||||
|
<div class="num_name">{{ val.count }}</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="logo"> Template By Yunzai-Bot</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
resources/starrail/gacha_log/img/提纳里.png
Normal file
BIN
resources/starrail/gacha_log/img/提纳里.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
@ -2,7 +2,7 @@ from typing import Optional
|
|||||||
|
|
||||||
from genshin import Client
|
from genshin import Client
|
||||||
from genshin.client.routes import InternationalRoute # noqa F401
|
from genshin.client.routes import InternationalRoute # noqa F401
|
||||||
from genshin.utility import recognize_genshin_server
|
from genshin.utility import recognize_starrail_server
|
||||||
|
|
||||||
from modules.apihelper.utility.helpers import hex_digest, get_ds
|
from modules.apihelper.utility.helpers import hex_digest, get_ds
|
||||||
|
|
||||||
@ -23,8 +23,8 @@ GACHA_HEADERS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def recognize_genshin_game_biz(game_uid: int) -> str:
|
def recognize_starrail_game_biz(game_uid: int) -> str:
|
||||||
return "hk4e_cn" if game_uid < 600000000 else "hk4e_global"
|
return "hkrpg_cn" if game_uid < 600000000 else "hkrpg_global"
|
||||||
|
|
||||||
|
|
||||||
async def get_authkey_by_stoken(client: Client) -> Optional[str]:
|
async def get_authkey_by_stoken(client: Client) -> Optional[str]:
|
||||||
@ -32,9 +32,9 @@ async def get_authkey_by_stoken(client: Client) -> Optional[str]:
|
|||||||
headers = GACHA_HEADERS.copy()
|
headers = GACHA_HEADERS.copy()
|
||||||
json = {
|
json = {
|
||||||
"auth_appid": "webview_gacha",
|
"auth_appid": "webview_gacha",
|
||||||
"game_biz": recognize_genshin_game_biz(client.uid),
|
"game_biz": recognize_starrail_game_biz(client.uid),
|
||||||
"game_uid": client.uid,
|
"game_uid": client.uid,
|
||||||
"region": recognize_genshin_server(client.uid),
|
"region": recognize_starrail_server(client.uid),
|
||||||
}
|
}
|
||||||
device_id = hex_digest(str(client.uid))
|
device_id = hex_digest(str(client.uid))
|
||||||
headers["x-rpc-device_id"] = device_id
|
headers["x-rpc-device_id"] = device_id
|
||||||
@ -56,9 +56,9 @@ async def fetch_hk4e_token_by_cookie(client: Client) -> None:
|
|||||||
"Content-Type": "application/json;charset=UTF-8",
|
"Content-Type": "application/json;charset=UTF-8",
|
||||||
}
|
}
|
||||||
json = {
|
json = {
|
||||||
"game_biz": recognize_genshin_game_biz(client.uid),
|
"game_biz": recognize_starrail_game_biz(client.uid),
|
||||||
"lang": "zh-cn",
|
"lang": "zh-cn",
|
||||||
"uid": str(client.uid),
|
"uid": str(client.uid),
|
||||||
"region": recognize_genshin_server(client.uid),
|
"region": recognize_starrail_server(client.uid),
|
||||||
}
|
}
|
||||||
await client.cookie_manager.request(url, method="POST", json=json, headers=headers)
|
await client.cookie_manager.request(url, method="POST", json=json, headers=headers)
|
||||||
|
Loading…
Reference in New Issue
Block a user