From 02e703ea91fb91569f4bc3cbdde8e666bd62cb09 Mon Sep 17 00:00:00 2001 From: shoucandanghehe Date: Fri, 9 Aug 2024 16:23:12 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E9=80=82=E9=85=8D=E6=96=B0=20recor?= =?UTF-8?q?ds=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../games/tetrio/api/models.py | 4 +- .../games/tetrio/api/player.py | 71 ++++++++++++++++--- .../games/tetrio/api/schemas/records/solo.py | 9 ++- .../tetrio/api/schemas/summaries/__init__.py | 5 +- .../tetrio/api/schemas/summaries/solo.py | 3 +- .../games/tetrio/api/typing.py | 9 +++ 6 files changed, 84 insertions(+), 17 deletions(-) diff --git a/nonebot_plugin_tetris_stats/games/tetrio/api/models.py b/nonebot_plugin_tetris_stats/games/tetrio/api/models.py index 0dc12ed..8bc9ee4 100644 --- a/nonebot_plugin_tetris_stats/games/tetrio/api/models.py +++ b/nonebot_plugin_tetris_stats/games/tetrio/api/models.py @@ -7,12 +7,12 @@ from sqlalchemy.orm import Mapped, MappedAsDataclass, mapped_column from ....db.models import PydanticType from .schemas.base import SuccessModel -from .typing import Summaries +from .typing import Records, Summaries class TETRIOHistoricalData(MappedAsDataclass, Model): id: Mapped[int] = mapped_column(init=False, primary_key=True) user_unique_identifier: Mapped[str] = mapped_column(String(24), index=True) - api_type: Mapped[Literal['User Info', Summaries]] = mapped_column(String(32), index=True) + api_type: Mapped[Literal['User Info', Records, Summaries]] = mapped_column(String(32), index=True) data: Mapped[SuccessModel] = mapped_column(PydanticType(get_model=[SuccessModel.__subclasses__], models=set())) update_time: Mapped[datetime] = mapped_column(DateTime, index=True) diff --git a/nonebot_plugin_tetris_stats/games/tetrio/api/player.py b/nonebot_plugin_tetris_stats/games/tetrio/api/player.py index 2b46a66..2f292be 100644 --- a/nonebot_plugin_tetris_stats/games/tetrio/api/player.py +++ b/nonebot_plugin_tetris_stats/games/tetrio/api/player.py @@ -1,5 +1,6 @@ +from enum import Enum from types import MappingProxyType -from typing import Literal, overload +from typing import Literal, NamedTuple, cast, overload from async_lru import alru_cache from nonebot.compat import type_validate_json @@ -11,24 +12,47 @@ from ..constant import BASE_URL, USER_ID, USER_NAME from .cache import Cache from .models import TETRIOHistoricalData from .schemas.base import FailedModel +from .schemas.records.solo import Solo as SoloRecord +from .schemas.records.solo import SoloSuccessModel as RecordsSoloSuccessModel from .schemas.summaries import ( AchievementsSuccessModel, - SoloSuccessModel, SummariesModel, ZenithSuccessModel, ZenSuccessModel, ) +from .schemas.summaries import ( + SoloSuccessModel as SummariesSoloSuccessModel, +) from .schemas.summaries.base import User as SummariesUser from .schemas.user import User from .schemas.user_info import UserInfo, UserInfoSuccess -from .typing import Summaries +from .typing import Records, Summaries + + +class RecordModeType(str, Enum): + Sprint = '40l' + Blitz = 'blitz' + + +class RecordType(str, Enum): + Top = 'top' + Recent = 'recent' + Progression = 'progression' + + +class RecordKey(NamedTuple): + mode_type: RecordModeType + record_type: RecordType + + def to_records(self) -> Records: + return cast(Records, f'{self.mode_type.value}_{self.record_type.value}') class Player: __SUMMARIES_MAPPING: MappingProxyType[Summaries, type[SummariesModel]] = MappingProxyType( { - '40l': SoloSuccessModel, - 'blitz': SoloSuccessModel, + '40l': SummariesSoloSuccessModel, + 'blitz': SummariesSoloSuccessModel, 'zenith': ZenithSuccessModel, 'zenithex': ZenithSuccessModel, 'zen': ZenSuccessModel, @@ -58,6 +82,7 @@ class Player: self.__user: User | None = None self._user_info: UserInfoSuccess | None = None self._summaries: dict[Summaries, SummariesModel] = {} + self._records: dict[RecordKey, RecordsSoloSuccessModel] = {} @property def _request_user_parameter(self) -> str: @@ -108,7 +133,7 @@ class Player: return self._user_info @overload - async def get_summaries(self, summaries_type: Literal['40l', 'blitz']) -> SoloSuccessModel: ... + async def get_summaries(self, summaries_type: Literal['40l', 'blitz']) -> SummariesSoloSuccessModel: ... @overload async def get_summaries(self, summaries_type: Literal['zenith', 'zenithex']) -> ZenithSuccessModel: ... @overload @@ -142,12 +167,12 @@ class Player: @property @alru_cache - async def sprint(self) -> SoloSuccessModel: + async def sprint(self) -> SummariesSoloSuccessModel: return await self.get_summaries('40l') @property @alru_cache - async def blitz(self) -> SoloSuccessModel: + async def blitz(self) -> SummariesSoloSuccessModel: return await self.get_summaries('blitz') @property @@ -185,3 +210,33 @@ class Player: if (user := (await self._get_local_summaries_user())) is not None: return user.banner_revision return (await self.get_info()).data.banner_revision + + async def get_records(self, mode_type: RecordModeType, records_type: RecordType) -> RecordsSoloSuccessModel: + if (record_key := RecordKey(mode_type, records_type)) not in self._records: + raw_records = await Cache.get( + splice_url( + [ + BASE_URL, + 'users/', + f'{self._request_user_parameter}/', + 'records/', + f'{mode_type}/', + records_type, + ] + ) + ) + records: RecordsSoloSuccessModel | FailedModel = type_validate_json(SoloRecord, raw_records) # type: ignore[arg-type] + if isinstance(records, FailedModel): + msg = f'用户Summaries数据请求错误:\n{records.error}' + raise RequestError(msg) + self._records[record_key] = records + await anti_duplicate_add( + TETRIOHistoricalData, + TETRIOHistoricalData( + user_unique_identifier=(await self.user).unique_identifier, + api_type=record_key.to_records(), + data=records, + update_time=records.cache.cached_at, + ), + ) + return self._records[record_key] diff --git a/nonebot_plugin_tetris_stats/games/tetrio/api/schemas/records/solo.py b/nonebot_plugin_tetris_stats/games/tetrio/api/schemas/records/solo.py index 52903b3..ef3242d 100644 --- a/nonebot_plugin_tetris_stats/games/tetrio/api/schemas/records/solo.py +++ b/nonebot_plugin_tetris_stats/games/tetrio/api/schemas/records/solo.py @@ -1,6 +1,8 @@ +from typing import TypeAlias + from pydantic import BaseModel -from ..base import SuccessModel +from ..base import FailedModel, SuccessModel from ..base.solo import Record @@ -8,5 +10,8 @@ class Data(BaseModel): entries: list[Record] -class Model(SuccessModel): +class SoloSuccessModel(SuccessModel): data: Data + + +Solo: TypeAlias = SoloSuccessModel | FailedModel diff --git a/nonebot_plugin_tetris_stats/games/tetrio/api/schemas/summaries/__init__.py b/nonebot_plugin_tetris_stats/games/tetrio/api/schemas/summaries/__init__.py index 7507ffd..1f05cec 100644 --- a/nonebot_plugin_tetris_stats/games/tetrio/api/schemas/summaries/__init__.py +++ b/nonebot_plugin_tetris_stats/games/tetrio/api/schemas/summaries/__init__.py @@ -1,5 +1,5 @@ from .achievements import Achievements, AchievementsSuccessModel -from .solo import Blitz, SoloSuccessModel, Sprint +from .solo import Solo, SoloSuccessModel from .zen import Zen, ZenSuccessModel from .zenith import Zenith, ZenithEx, ZenithSuccessModel @@ -8,8 +8,7 @@ SummariesModel = AchievementsSuccessModel | SoloSuccessModel | ZenSuccessModel | __all__ = [ 'Achievements', 'AchievementsSuccessModel', - 'Blitz', - 'Sprint', + 'Solo', 'SoloSuccessModel', 'Zen', 'ZenSuccessModel', diff --git a/nonebot_plugin_tetris_stats/games/tetrio/api/schemas/summaries/solo.py b/nonebot_plugin_tetris_stats/games/tetrio/api/schemas/summaries/solo.py index 6335726..26b1f2d 100644 --- a/nonebot_plugin_tetris_stats/games/tetrio/api/schemas/summaries/solo.py +++ b/nonebot_plugin_tetris_stats/games/tetrio/api/schemas/summaries/solo.py @@ -21,5 +21,4 @@ class SoloSuccessModel(SuccessModel): data: Data -Sprint: TypeAlias = SoloSuccessModel | FailedModel -Blitz: TypeAlias = SoloSuccessModel | FailedModel +Solo: TypeAlias = SoloSuccessModel | FailedModel diff --git a/nonebot_plugin_tetris_stats/games/tetrio/api/typing.py b/nonebot_plugin_tetris_stats/games/tetrio/api/typing.py index 125f10c..23cdc3b 100644 --- a/nonebot_plugin_tetris_stats/games/tetrio/api/typing.py +++ b/nonebot_plugin_tetris_stats/games/tetrio/api/typing.py @@ -31,3 +31,12 @@ Summaries = Literal[ 'zen', 'achievements', ] + +Records = Literal[ + '40l_top', + '40l_recent', + '40l_progression', + 'blitz_top', + 'blitz_recent', + 'blitz_progression', +]