mirror of
https://github.com/A-Minos/nonebot-plugin-tetris-stats.git
synced 2026-03-05 05:36:54 +08:00
✨ 更新 TETR.IO 模板 (#348)
* ✨ 更新路径以匹配模板 * ✨ 添加历史数据 * 🎨 优化模板模型代码结构 --------- Co-authored-by: shoucandanghehe <wallfjjd@gmail.com>
This commit is contained in:
@@ -30,13 +30,13 @@ from ...utils.host import HostPage, get_self_netloc
|
|||||||
from ...utils.metrics import TetrisMetricsProWithPPSVS, get_metrics
|
from ...utils.metrics import TetrisMetricsProWithPPSVS, get_metrics
|
||||||
from ...utils.render import render
|
from ...utils.render import render
|
||||||
from ...utils.render.schemas.base import Avatar, Ranking
|
from ...utils.render.schemas.base import Avatar, Ranking
|
||||||
from ...utils.render.schemas.tetrio_info import Data, Radar, TetraLeague, TetraLeagueHistory
|
from ...utils.render.schemas.tetrio.tetrio_info import Info as V1TemplateInfo
|
||||||
from ...utils.render.schemas.tetrio_info import Info as V1TemplateInfo
|
from ...utils.render.schemas.tetrio.tetrio_info import Radar, TetraLeague, TetraLeagueHistory, TetraLeagueHistoryData
|
||||||
from ...utils.render.schemas.tetrio_info import User as V1TemplateUser
|
from ...utils.render.schemas.tetrio.tetrio_info import User as V1TemplateUser
|
||||||
from ...utils.render.schemas.tetrio_info_v2 import Badge, Blitz, Sprint, Statistic, TetraLeagueStatistic
|
from ...utils.render.schemas.tetrio.tetrio_user_info_v2 import Badge, Blitz, Sprint, Statistic, TetraLeagueStatistic
|
||||||
from ...utils.render.schemas.tetrio_info_v2 import Info as V2TemplateInfo
|
from ...utils.render.schemas.tetrio.tetrio_user_info_v2 import Info as V2TemplateInfo
|
||||||
from ...utils.render.schemas.tetrio_info_v2 import TetraLeague as V2TemplateTetraLeague
|
from ...utils.render.schemas.tetrio.tetrio_user_info_v2 import TetraLeague as V2TemplateTetraLeague
|
||||||
from ...utils.render.schemas.tetrio_info_v2 import User as V2TemplateUser
|
from ...utils.render.schemas.tetrio.tetrio_user_info_v2 import User as V2TemplateUser
|
||||||
from ...utils.screenshot import screenshot
|
from ...utils.screenshot import screenshot
|
||||||
from ...utils.typing import Me, Number
|
from ...utils.typing import Me, Number
|
||||||
from ..constant import CANT_VERIFY_MESSAGE
|
from ..constant import CANT_VERIFY_MESSAGE
|
||||||
@@ -129,10 +129,10 @@ def get_split(value_max: int, value_min: int) -> tuple[int, int]:
|
|||||||
|
|
||||||
|
|
||||||
def get_specified_point(
|
def get_specified_point(
|
||||||
previous_point: Data,
|
previous_point: TetraLeagueHistoryData,
|
||||||
behind_point: Data,
|
behind_point: TetraLeagueHistoryData,
|
||||||
point_time: datetime,
|
point_time: datetime,
|
||||||
) -> Data:
|
) -> TetraLeagueHistoryData:
|
||||||
"""根据给出的 previous_point 和 behind_point, 推算 point_time 点处的数据
|
"""根据给出的 previous_point 和 behind_point, 推算 point_time 点处的数据
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -147,13 +147,13 @@ def get_specified_point(
|
|||||||
slope = (behind_point.tr - previous_point.tr) / (
|
slope = (behind_point.tr - previous_point.tr) / (
|
||||||
datetime.timestamp(behind_point.record_at) - datetime.timestamp(previous_point.record_at)
|
datetime.timestamp(behind_point.record_at) - datetime.timestamp(previous_point.record_at)
|
||||||
)
|
)
|
||||||
return Data(
|
return TetraLeagueHistoryData(
|
||||||
record_at=point_time,
|
record_at=point_time,
|
||||||
tr=previous_point.tr + slope * (datetime.timestamp(point_time) - datetime.timestamp(previous_point.record_at)),
|
tr=previous_point.tr + slope * (datetime.timestamp(point_time) - datetime.timestamp(previous_point.record_at)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def query_historical_data(user: User, user_info: UserInfoSuccess) -> list[Data]:
|
async def query_historical_data(user: User, user_info: UserInfoSuccess) -> list[TetraLeagueHistoryData]:
|
||||||
today = datetime.now(ZoneInfo('Asia/Shanghai')).replace(hour=0, minute=0, second=0, microsecond=0)
|
today = datetime.now(ZoneInfo('Asia/Shanghai')).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
forward = timedelta(days=9)
|
forward = timedelta(days=9)
|
||||||
start_time = (today - forward).astimezone(UTC)
|
start_time = (today - forward).astimezone(UTC)
|
||||||
@@ -183,11 +183,11 @@ async def query_historical_data(user: User, user_info: UserInfoSuccess) -> list[
|
|||||||
full_export_data = FullExport.get_data(user.unique_identifier)
|
full_export_data = FullExport.get_data(user.unique_identifier)
|
||||||
if not historical_data and not full_export_data:
|
if not historical_data and not full_export_data:
|
||||||
return [
|
return [
|
||||||
Data(record_at=today - forward, tr=user_info.data.user.league.rating),
|
TetraLeagueHistoryData(record_at=today - forward, tr=user_info.data.user.league.rating),
|
||||||
Data(record_at=today.replace(microsecond=1000), tr=user_info.data.user.league.rating),
|
TetraLeagueHistoryData(record_at=today.replace(microsecond=1000), tr=user_info.data.user.league.rating),
|
||||||
]
|
]
|
||||||
histories = [
|
histories = [
|
||||||
Data(
|
TetraLeagueHistoryData(
|
||||||
record_at=i.update_time.astimezone(ZoneInfo('Asia/Shanghai')),
|
record_at=i.update_time.astimezone(ZoneInfo('Asia/Shanghai')),
|
||||||
tr=i.data.data.user.league.rating,
|
tr=i.data.data.user.league.rating,
|
||||||
)
|
)
|
||||||
@@ -208,7 +208,7 @@ async def query_historical_data(user: User, user_info: UserInfoSuccess) -> list[
|
|||||||
histories.append(
|
histories.append(
|
||||||
get_specified_point(
|
get_specified_point(
|
||||||
histories[-1],
|
histories[-1],
|
||||||
Data(record_at=user_info.cache.cached_at, tr=user_info.data.user.league.rating),
|
TetraLeagueHistoryData(record_at=user_info.cache.cached_at, tr=user_info.data.user.league.rating),
|
||||||
today.replace(microsecond=1000),
|
today.replace(microsecond=1000),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -219,7 +219,7 @@ async def query_historical_data(user: User, user_info: UserInfoSuccess) -> list[
|
|||||||
today - forward,
|
today - forward,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
histories.insert(0, Data(record_at=today - forward, tr=histories[0].tr))
|
histories.insert(0, TetraLeagueHistoryData(record_at=today - forward, tr=histories[0].tr))
|
||||||
return histories
|
return histories
|
||||||
|
|
||||||
|
|
||||||
@@ -322,6 +322,7 @@ async def make_query_image_v2(player: Player) -> bytes:
|
|||||||
player.user, player.get_info(), player.sprint, player.blitz, player.zen
|
player.user, player.get_info(), player.sprint, player.blitz, player.zen
|
||||||
)
|
)
|
||||||
league = get_league(user_info)
|
league = get_league(user_info)
|
||||||
|
histories = await query_historical_data(user, user_info)
|
||||||
|
|
||||||
if sprint.record is not None:
|
if sprint.record is not None:
|
||||||
duration = timedelta(milliseconds=sprint.record.endcontext.final_time).total_seconds()
|
duration = timedelta(milliseconds=sprint.record.endcontext.final_time).total_seconds()
|
||||||
@@ -342,7 +343,7 @@ async def make_query_image_v2(player: Player) -> bytes:
|
|||||||
netloc = get_self_netloc()
|
netloc = get_self_netloc()
|
||||||
async with HostPage(
|
async with HostPage(
|
||||||
await render(
|
await render(
|
||||||
'v2/tetrio/info',
|
'v2/tetrio/user/info',
|
||||||
V2TemplateInfo(
|
V2TemplateInfo(
|
||||||
user=V2TemplateUser(
|
user=V2TemplateUser(
|
||||||
id=user.ID,
|
id=user.ID,
|
||||||
@@ -397,6 +398,7 @@ async def make_query_image_v2(player: Player) -> bytes:
|
|||||||
wins=league.gameswon,
|
wins=league.gameswon,
|
||||||
),
|
),
|
||||||
decaying=league.decaying,
|
decaying=league.decaying,
|
||||||
|
history=histories,
|
||||||
)
|
)
|
||||||
if isinstance(league, RatedLeague)
|
if isinstance(league, RatedLeague)
|
||||||
else None,
|
else None,
|
||||||
@@ -510,8 +512,8 @@ class FullExport:
|
|||||||
cls.latest_update = datetime.now(tz=ZoneInfo('Asia/Shanghai')).date()
|
cls.latest_update = datetime.now(tz=ZoneInfo('Asia/Shanghai')).date()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_data(cls, unique_identifier: str) -> list[Data]:
|
def get_data(cls, unique_identifier: str) -> list[TetraLeagueHistoryData]:
|
||||||
return [Data(record_at=i[0], tr=i[1]) for i in cls.cache[unique_identifier]]
|
return [TetraLeagueHistoryData(record_at=i[0], tr=i[1]) for i in cls.cache[unique_identifier]]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def start_time(cls) -> datetime:
|
def start_time(cls) -> datetime:
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ from ....utils.host import HostPage, get_self_netloc
|
|||||||
from ....utils.metrics import get_metrics
|
from ....utils.metrics import get_metrics
|
||||||
from ....utils.render import render
|
from ....utils.render import render
|
||||||
from ....utils.render.schemas.base import Avatar
|
from ....utils.render.schemas.base import Avatar
|
||||||
from ....utils.render.schemas.tetrio_record_base import Finesse, Max, Mini, Tspins, User
|
from ....utils.render.schemas.tetrio.tetrio_record_base import Finesse, Max, Mini, Tspins, User
|
||||||
from ....utils.render.schemas.tetrio_record_blitz import Record, Statistic
|
from ....utils.render.schemas.tetrio.tetrio_record_blitz import Record, Statistic
|
||||||
from ....utils.screenshot import screenshot
|
from ....utils.screenshot import screenshot
|
||||||
from ....utils.typing import Me
|
from ....utils.typing import Me
|
||||||
from ...constant import CANT_VERIFY_MESSAGE
|
from ...constant import CANT_VERIFY_MESSAGE
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ from ....utils.host import HostPage, get_self_netloc
|
|||||||
from ....utils.metrics import get_metrics
|
from ....utils.metrics import get_metrics
|
||||||
from ....utils.render import render
|
from ....utils.render import render
|
||||||
from ....utils.render.schemas.base import Avatar
|
from ....utils.render.schemas.base import Avatar
|
||||||
from ....utils.render.schemas.tetrio_record_base import Finesse, Max, Mini, Tspins, User
|
from ....utils.render.schemas.tetrio.tetrio_record_base import Finesse, Max, Mini, Tspins, User
|
||||||
from ....utils.render.schemas.tetrio_record_sprint import Record, Statistic
|
from ....utils.render.schemas.tetrio.tetrio_record_sprint import Record, Statistic
|
||||||
from ....utils.screenshot import screenshot
|
from ....utils.screenshot import screenshot
|
||||||
from ....utils.typing import Me
|
from ....utils.typing import Me
|
||||||
from ...constant import CANT_VERIFY_MESSAGE
|
from ...constant import CANT_VERIFY_MESSAGE
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ from nonebot.compat import PYDANTIC_V2
|
|||||||
|
|
||||||
from ..templates import templates_dir
|
from ..templates import templates_dir
|
||||||
from .schemas.bind import Bind
|
from .schemas.bind import Bind
|
||||||
from .schemas.tetrio_info import Info as TETRIOInfo
|
from .schemas.tetrio.tetrio_info import Info as TETRIOInfo
|
||||||
from .schemas.tetrio_info_v2 import Info as TETRIOInfoV2
|
from .schemas.tetrio.tetrio_record_blitz import Record as TETRIORecordBlitz
|
||||||
from .schemas.tetrio_record_blitz import Record as TETRIORecordBlitz
|
from .schemas.tetrio.tetrio_record_sprint import Record as TETRIORecordSprint
|
||||||
from .schemas.tetrio_record_sprint import Record as TETRIORecordSprint
|
from .schemas.tetrio.tetrio_user_info_v2 import Info as TETRIOUserInfoV2
|
||||||
from .schemas.top_info import Info as TOPInfo
|
from .schemas.top_info import Info as TOPInfo
|
||||||
from .schemas.tos_info import Info as TOSInfo
|
from .schemas.tos_info import Info as TOSInfo
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ async def render(render_type: Literal['v1/tos/info'], data: TOSInfo) -> str: ...
|
|||||||
|
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
async def render(render_type: Literal['v2/tetrio/info'], data: TETRIOInfoV2) -> str: ...
|
async def render(render_type: Literal['v2/tetrio/user/info'], data: TETRIOUserInfoV2) -> str: ...
|
||||||
|
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
@@ -51,11 +51,11 @@ async def render(
|
|||||||
'v1/tetrio/info',
|
'v1/tetrio/info',
|
||||||
'v1/top/info',
|
'v1/top/info',
|
||||||
'v1/tos/info',
|
'v1/tos/info',
|
||||||
'v2/tetrio/info',
|
'v2/tetrio/user/info',
|
||||||
'v2/tetrio/record/40l',
|
'v2/tetrio/record/40l',
|
||||||
'v2/tetrio/record/blitz',
|
'v2/tetrio/record/blitz',
|
||||||
],
|
],
|
||||||
data: Bind | TETRIOInfo | TOPInfo | TOSInfo | TETRIOInfoV2 | TETRIORecordSprint | TETRIORecordBlitz,
|
data: Bind | TETRIOInfo | TOPInfo | TOSInfo | TETRIOUserInfoV2 | TETRIORecordSprint | TETRIORecordBlitz,
|
||||||
) -> str:
|
) -> str:
|
||||||
if PYDANTIC_V2:
|
if PYDANTIC_V2:
|
||||||
return await env.get_template('index.html').render_async(
|
return await env.get_template('index.html').render_async(
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from ....typing import Number
|
||||||
|
|
||||||
|
|
||||||
|
class TetraLeagueHistoryData(BaseModel):
|
||||||
|
record_at: datetime
|
||||||
|
tr: Number
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from .....games.tetrio.api.typing import Rank
|
||||||
|
from ....typing import Number
|
||||||
|
from ..base import People, Ranking
|
||||||
|
from .base import TetraLeagueHistoryData
|
||||||
|
|
||||||
|
|
||||||
|
class User(People):
|
||||||
|
bio: str | None
|
||||||
|
|
||||||
|
|
||||||
|
class TetraLeague(BaseModel):
|
||||||
|
rank: Rank
|
||||||
|
tr: Number
|
||||||
|
global_rank: Number
|
||||||
|
pps: Number
|
||||||
|
lpm: Number
|
||||||
|
apm: Number
|
||||||
|
apl: Number
|
||||||
|
vs: Number
|
||||||
|
adpm: Number
|
||||||
|
adpl: Number
|
||||||
|
|
||||||
|
|
||||||
|
class TetraLeagueHistory(BaseModel):
|
||||||
|
data: list[TetraLeagueHistoryData]
|
||||||
|
split_interval: Number
|
||||||
|
min_tr: Number
|
||||||
|
max_tr: Number
|
||||||
|
offset: Number
|
||||||
|
|
||||||
|
|
||||||
|
class Radar(BaseModel):
|
||||||
|
app: Number
|
||||||
|
dsps: Number
|
||||||
|
dspp: Number
|
||||||
|
ci: Number
|
||||||
|
ge: Number
|
||||||
|
|
||||||
|
|
||||||
|
class Info(BaseModel):
|
||||||
|
user: User
|
||||||
|
ranking: Ranking
|
||||||
|
tetra_league: TetraLeague
|
||||||
|
tetra_league_history: TetraLeagueHistory
|
||||||
|
radar: Radar
|
||||||
|
sprint: str
|
||||||
|
blitz: str
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from .base import People
|
from ..base import People
|
||||||
|
|
||||||
|
|
||||||
class User(People):
|
class User(People):
|
||||||
@@ -2,10 +2,11 @@ from datetime import datetime
|
|||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from ....games.tetrio.api.schemas.user_records import Zen
|
from .....games.tetrio.api.schemas.user_records import Zen
|
||||||
from ....games.tetrio.api.typing import Rank
|
from .....games.tetrio.api.typing import Rank
|
||||||
from ...typing import Number
|
from ....typing import Number
|
||||||
from .base import Avatar
|
from ..base import Avatar
|
||||||
|
from .base import TetraLeagueHistoryData
|
||||||
|
|
||||||
|
|
||||||
class Badge(BaseModel):
|
class Badge(BaseModel):
|
||||||
@@ -72,6 +73,8 @@ class TetraLeague(BaseModel):
|
|||||||
|
|
||||||
decaying: bool
|
decaying: bool
|
||||||
|
|
||||||
|
history: list[TetraLeagueHistoryData]
|
||||||
|
|
||||||
|
|
||||||
class Sprint(BaseModel):
|
class Sprint(BaseModel):
|
||||||
time: str
|
time: str
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
from datetime import datetime
|
|
||||||
from typing import Annotated, ClassVar
|
|
||||||
|
|
||||||
from nonebot.compat import PYDANTIC_V2
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
from ....games.tetrio.api.typing import Rank
|
|
||||||
from ...typing import Number
|
|
||||||
from .base import People, Ranking
|
|
||||||
|
|
||||||
if PYDANTIC_V2:
|
|
||||||
from pydantic import PlainSerializer
|
|
||||||
|
|
||||||
|
|
||||||
def format_datetime_to_timestamp(dt: datetime) -> int:
|
|
||||||
return int(dt.timestamp() * 1000)
|
|
||||||
|
|
||||||
|
|
||||||
class User(People):
|
|
||||||
bio: str | None
|
|
||||||
|
|
||||||
|
|
||||||
class TetraLeague(BaseModel):
|
|
||||||
rank: Rank
|
|
||||||
tr: Number
|
|
||||||
global_rank: Number
|
|
||||||
pps: Number
|
|
||||||
lpm: Number
|
|
||||||
apm: Number
|
|
||||||
apl: Number
|
|
||||||
vs: Number
|
|
||||||
adpm: Number
|
|
||||||
adpl: Number
|
|
||||||
|
|
||||||
|
|
||||||
class Data(BaseModel):
|
|
||||||
if PYDANTIC_V2:
|
|
||||||
record_at: Annotated[datetime, PlainSerializer(format_datetime_to_timestamp, return_type=int)]
|
|
||||||
else:
|
|
||||||
record_at: datetime # type: ignore[no-redef]
|
|
||||||
tr: Number
|
|
||||||
|
|
||||||
|
|
||||||
class TetraLeagueHistory(BaseModel):
|
|
||||||
data: list[Data]
|
|
||||||
split_interval: Number
|
|
||||||
min_tr: Number
|
|
||||||
max_tr: Number
|
|
||||||
offset: Number
|
|
||||||
|
|
||||||
|
|
||||||
class Radar(BaseModel):
|
|
||||||
app: Number
|
|
||||||
dsps: Number
|
|
||||||
dspp: Number
|
|
||||||
ci: Number
|
|
||||||
ge: Number
|
|
||||||
|
|
||||||
|
|
||||||
class Info(BaseModel):
|
|
||||||
user: User
|
|
||||||
ranking: Ranking
|
|
||||||
tetra_league: TetraLeague
|
|
||||||
tetra_league_history: TetraLeagueHistory
|
|
||||||
radar: Radar
|
|
||||||
sprint: str
|
|
||||||
blitz: str
|
|
||||||
|
|
||||||
if not PYDANTIC_V2:
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
json_encoders: ClassVar[dict] = {datetime: format_datetime_to_timestamp}
|
|
||||||
Reference in New Issue
Block a user