mirror of
https://github.com/A-Minos/nonebot-plugin-tetris-stats.git
synced 2026-03-05 05:36:54 +08:00
✨ 适配新 records API
This commit is contained in:
@@ -7,12 +7,12 @@ from sqlalchemy.orm import Mapped, MappedAsDataclass, mapped_column
|
|||||||
|
|
||||||
from ....db.models import PydanticType
|
from ....db.models import PydanticType
|
||||||
from .schemas.base import SuccessModel
|
from .schemas.base import SuccessModel
|
||||||
from .typing import Summaries
|
from .typing import Records, Summaries
|
||||||
|
|
||||||
|
|
||||||
class TETRIOHistoricalData(MappedAsDataclass, Model):
|
class TETRIOHistoricalData(MappedAsDataclass, Model):
|
||||||
id: Mapped[int] = mapped_column(init=False, primary_key=True)
|
id: Mapped[int] = mapped_column(init=False, primary_key=True)
|
||||||
user_unique_identifier: Mapped[str] = mapped_column(String(24), index=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()))
|
data: Mapped[SuccessModel] = mapped_column(PydanticType(get_model=[SuccessModel.__subclasses__], models=set()))
|
||||||
update_time: Mapped[datetime] = mapped_column(DateTime, index=True)
|
update_time: Mapped[datetime] = mapped_column(DateTime, index=True)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
from enum import Enum
|
||||||
from types import MappingProxyType
|
from types import MappingProxyType
|
||||||
from typing import Literal, overload
|
from typing import Literal, NamedTuple, cast, overload
|
||||||
|
|
||||||
from async_lru import alru_cache
|
from async_lru import alru_cache
|
||||||
from nonebot.compat import type_validate_json
|
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 .cache import Cache
|
||||||
from .models import TETRIOHistoricalData
|
from .models import TETRIOHistoricalData
|
||||||
from .schemas.base import FailedModel
|
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 (
|
from .schemas.summaries import (
|
||||||
AchievementsSuccessModel,
|
AchievementsSuccessModel,
|
||||||
SoloSuccessModel,
|
|
||||||
SummariesModel,
|
SummariesModel,
|
||||||
ZenithSuccessModel,
|
ZenithSuccessModel,
|
||||||
ZenSuccessModel,
|
ZenSuccessModel,
|
||||||
)
|
)
|
||||||
|
from .schemas.summaries import (
|
||||||
|
SoloSuccessModel as SummariesSoloSuccessModel,
|
||||||
|
)
|
||||||
from .schemas.summaries.base import User as SummariesUser
|
from .schemas.summaries.base import User as SummariesUser
|
||||||
from .schemas.user import User
|
from .schemas.user import User
|
||||||
from .schemas.user_info import UserInfo, UserInfoSuccess
|
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:
|
class Player:
|
||||||
__SUMMARIES_MAPPING: MappingProxyType[Summaries, type[SummariesModel]] = MappingProxyType(
|
__SUMMARIES_MAPPING: MappingProxyType[Summaries, type[SummariesModel]] = MappingProxyType(
|
||||||
{
|
{
|
||||||
'40l': SoloSuccessModel,
|
'40l': SummariesSoloSuccessModel,
|
||||||
'blitz': SoloSuccessModel,
|
'blitz': SummariesSoloSuccessModel,
|
||||||
'zenith': ZenithSuccessModel,
|
'zenith': ZenithSuccessModel,
|
||||||
'zenithex': ZenithSuccessModel,
|
'zenithex': ZenithSuccessModel,
|
||||||
'zen': ZenSuccessModel,
|
'zen': ZenSuccessModel,
|
||||||
@@ -58,6 +82,7 @@ class Player:
|
|||||||
self.__user: User | None = None
|
self.__user: User | None = None
|
||||||
self._user_info: UserInfoSuccess | None = None
|
self._user_info: UserInfoSuccess | None = None
|
||||||
self._summaries: dict[Summaries, SummariesModel] = {}
|
self._summaries: dict[Summaries, SummariesModel] = {}
|
||||||
|
self._records: dict[RecordKey, RecordsSoloSuccessModel] = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _request_user_parameter(self) -> str:
|
def _request_user_parameter(self) -> str:
|
||||||
@@ -108,7 +133,7 @@ class Player:
|
|||||||
return self._user_info
|
return self._user_info
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
async def get_summaries(self, summaries_type: Literal['40l', 'blitz']) -> SoloSuccessModel: ...
|
async def get_summaries(self, summaries_type: Literal['40l', 'blitz']) -> SummariesSoloSuccessModel: ...
|
||||||
@overload
|
@overload
|
||||||
async def get_summaries(self, summaries_type: Literal['zenith', 'zenithex']) -> ZenithSuccessModel: ...
|
async def get_summaries(self, summaries_type: Literal['zenith', 'zenithex']) -> ZenithSuccessModel: ...
|
||||||
@overload
|
@overload
|
||||||
@@ -142,12 +167,12 @@ class Player:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
@alru_cache
|
@alru_cache
|
||||||
async def sprint(self) -> SoloSuccessModel:
|
async def sprint(self) -> SummariesSoloSuccessModel:
|
||||||
return await self.get_summaries('40l')
|
return await self.get_summaries('40l')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@alru_cache
|
@alru_cache
|
||||||
async def blitz(self) -> SoloSuccessModel:
|
async def blitz(self) -> SummariesSoloSuccessModel:
|
||||||
return await self.get_summaries('blitz')
|
return await self.get_summaries('blitz')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -185,3 +210,33 @@ class Player:
|
|||||||
if (user := (await self._get_local_summaries_user())) is not None:
|
if (user := (await self._get_local_summaries_user())) is not None:
|
||||||
return user.banner_revision
|
return user.banner_revision
|
||||||
return (await self.get_info()).data.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]
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
from typing import TypeAlias
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from ..base import SuccessModel
|
from ..base import FailedModel, SuccessModel
|
||||||
from ..base.solo import Record
|
from ..base.solo import Record
|
||||||
|
|
||||||
|
|
||||||
@@ -8,5 +10,8 @@ class Data(BaseModel):
|
|||||||
entries: list[Record]
|
entries: list[Record]
|
||||||
|
|
||||||
|
|
||||||
class Model(SuccessModel):
|
class SoloSuccessModel(SuccessModel):
|
||||||
data: Data
|
data: Data
|
||||||
|
|
||||||
|
|
||||||
|
Solo: TypeAlias = SoloSuccessModel | FailedModel
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from .achievements import Achievements, AchievementsSuccessModel
|
from .achievements import Achievements, AchievementsSuccessModel
|
||||||
from .solo import Blitz, SoloSuccessModel, Sprint
|
from .solo import Solo, SoloSuccessModel
|
||||||
from .zen import Zen, ZenSuccessModel
|
from .zen import Zen, ZenSuccessModel
|
||||||
from .zenith import Zenith, ZenithEx, ZenithSuccessModel
|
from .zenith import Zenith, ZenithEx, ZenithSuccessModel
|
||||||
|
|
||||||
@@ -8,8 +8,7 @@ SummariesModel = AchievementsSuccessModel | SoloSuccessModel | ZenSuccessModel |
|
|||||||
__all__ = [
|
__all__ = [
|
||||||
'Achievements',
|
'Achievements',
|
||||||
'AchievementsSuccessModel',
|
'AchievementsSuccessModel',
|
||||||
'Blitz',
|
'Solo',
|
||||||
'Sprint',
|
|
||||||
'SoloSuccessModel',
|
'SoloSuccessModel',
|
||||||
'Zen',
|
'Zen',
|
||||||
'ZenSuccessModel',
|
'ZenSuccessModel',
|
||||||
|
|||||||
@@ -21,5 +21,4 @@ class SoloSuccessModel(SuccessModel):
|
|||||||
data: Data
|
data: Data
|
||||||
|
|
||||||
|
|
||||||
Sprint: TypeAlias = SoloSuccessModel | FailedModel
|
Solo: TypeAlias = SoloSuccessModel | FailedModel
|
||||||
Blitz: TypeAlias = SoloSuccessModel | FailedModel
|
|
||||||
|
|||||||
@@ -31,3 +31,12 @@ Summaries = Literal[
|
|||||||
'zen',
|
'zen',
|
||||||
'achievements',
|
'achievements',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Records = Literal[
|
||||||
|
'40l_top',
|
||||||
|
'40l_recent',
|
||||||
|
'40l_progression',
|
||||||
|
'blitz_top',
|
||||||
|
'blitz_recent',
|
||||||
|
'blitz_progression',
|
||||||
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user