Compare commits

...

17 Commits
1.4.2 ... 1.4.3

Author SHA1 Message Date
41bbcdb66c 🔖 1.4.3 2024-08-06 15:46:11 +08:00
160d81476a 🔥 删除不需要的 type: ignore 2024-08-06 15:35:53 +08:00
1e5b00a280 初步重新适配 TETR.IO query 2024-08-06 15:29:32 +08:00
ee53b92559 🔥 删除不需要的函数调用 2024-08-06 15:28:26 +08:00
cd9d29b748 🚨 修复 pyright 类型报错 2024-08-06 15:27:42 +08:00
214ebc5073 移除对 arclet-alconna 的显式依赖声明 2024-08-06 13:41:13 +08:00
485706267e 🐛 更新 TETR.IO summaries solo 模型 2024-08-06 01:34:07 +08:00
12cb5193b3 🎨 优化模板模型路径
~~真的是优化吗~~
2024-08-06 00:03:02 +08:00
dependabot[bot]
461d3450d6 ⬆️ Bump ruff from 0.5.5 to 0.5.6 (#386)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.5.5 to 0.5.6.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.5.5...0.5.6)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 15:55:56 +00:00
dependabot[bot]
64d77dbff2 ⬆️ Bump pandas-stubs from 2.2.2.240603 to 2.2.2.240805 (#385)
Bumps [pandas-stubs](https://github.com/pandas-dev/pandas-stubs) from 2.2.2.240603 to 2.2.2.240805.
- [Changelog](https://github.com/pandas-dev/pandas-stubs/blob/main/docs/release_procedure.md)
- [Commits](https://github.com/pandas-dev/pandas-stubs/compare/v2.2.2.240603...v2.2.2.240805)

---
updated-dependencies:
- dependency-name: pandas-stubs
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 15:52:59 +00:00
dependabot[bot]
e5b4d3bc08 ⬆️ Bump arclet-alconna from 1.8.19 to 1.8.21 (#387)
Bumps [arclet-alconna](https://github.com/ArcletProject/Alconna) from 1.8.19 to 1.8.21.
- [Release notes](https://github.com/ArcletProject/Alconna/releases)
- [Changelog](https://github.com/ArcletProject/Alconna/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/ArcletProject/Alconna/compare/v1.8.19...v1.8.21)

---
updated-dependencies:
- dependency-name: arclet-alconna
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 15:49:32 +00:00
dependabot[bot]
4208018caf ⬆️ Bump nonebot-plugin-localstore from 0.7.0 to 0.7.1 (#384)
Bumps [nonebot-plugin-localstore](https://github.com/nonebot/plugin-localstore) from 0.7.0 to 0.7.1.
- [Release notes](https://github.com/nonebot/plugin-localstore/releases)
- [Commits](https://github.com/nonebot/plugin-localstore/compare/v0.7.0...v0.7.1)

---
updated-dependencies:
- dependency-name: nonebot-plugin-localstore
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 15:45:53 +00:00
dependabot[bot]
5032a3eb9a ⬆️ Bump nonebot-plugin-session from 0.3.1 to 0.3.2 (#383)
Bumps [nonebot-plugin-session](https://github.com/noneplugin/nonebot-plugin-session) from 0.3.1 to 0.3.2.
- [Release notes](https://github.com/noneplugin/nonebot-plugin-session/releases)
- [Commits](https://github.com/noneplugin/nonebot-plugin-session/compare/v0.3.1...v0.3.2)

---
updated-dependencies:
- dependency-name: nonebot-plugin-session
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 23:42:23 +08:00
dependabot[bot]
bf9a9953dd ⬆️ Bump nonebot-adapter-qq from 1.4.4 to 1.5.0 (#381)
Bumps [nonebot-adapter-qq](https://github.com/nonebot/adapter-qq) from 1.4.4 to 1.5.0.
- [Release notes](https://github.com/nonebot/adapter-qq/releases)
- [Commits](https://github.com/nonebot/adapter-qq/compare/v1.4.4...v1.5.0)

---
updated-dependencies:
- dependency-name: nonebot-adapter-qq
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 04:10:49 +00:00
dependabot[bot]
85feb9cb41 ⬆️ Bump mypy from 1.11.0 to 1.11.1 (#382)
Bumps [mypy](https://github.com/python/mypy) from 1.11.0 to 1.11.1.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.11...v1.11.1)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 04:07:14 +00:00
5a7c54528c 🐛 修正 record 使用的 type 2024-08-05 12:02:50 +08:00
afce74afe8 修改命令注册逻辑 2024-08-05 11:56:48 +08:00
31 changed files with 879 additions and 770 deletions

View File

@@ -1,6 +1,6 @@
from pathlib import Path from pathlib import Path
from nonebot_plugin_localstore import get_cache_dir # type: ignore[import-untyped] from nonebot_plugin_localstore import get_cache_dir
from pydantic import BaseModel from pydantic import BaseModel
CACHE_PATH: Path = get_cache_dir('nonebot_plugin_tetris_stats') CACHE_PATH: Path = get_cache_dir('nonebot_plugin_tetris_stats')

View File

@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Literal, TypeVar, overload
from nonebot.exception import FinishedException from nonebot.exception import FinishedException
from nonebot.log import logger from nonebot.log import logger
from nonebot_plugin_orm import AsyncSession, get_session from nonebot_plugin_orm import AsyncSession, get_session
from nonebot_plugin_user import User # type: ignore[import-untyped] from nonebot_plugin_user import User
from sqlalchemy import select from sqlalchemy import select
from ..utils.typing import AllCommandType, BaseCommandType, GameType, TETRIOCommandType from ..utils.typing import AllCommandType, BaseCommandType, GameType, TETRIOCommandType

View File

@@ -1,16 +1,10 @@
from arclet.alconna import Arg, ArgFlag, Args, Option, Subcommand from nonebot_plugin_alconna import Subcommand
from nonebot_plugin_alconna import At
from ...utils.exception import MessageFormatError from ...utils.exception import MessageFormatError
from ...utils.typing import Me from .. import alc
from .. import command as main_command
# from .. import add_block_handlers, alc, command
from .. import alc, command
from .api import Player from .api import Player
# from .api.typing import ValidRank
from .constant import USER_ID, USER_NAME from .constant import USER_ID, USER_NAME
from .typing import Template
def get_player(user_id_or_name: str) -> Player | MessageFormatError: def get_player(user_id_or_name: str) -> Player | MessageFormatError:
@@ -21,171 +15,22 @@ def get_player(user_id_or_name: str) -> Player | MessageFormatError:
return MessageFormatError('用户名/ID不合法') return MessageFormatError('用户名/ID不合法')
command.add( command = Subcommand(
Subcommand( 'TETR.IO',
'TETR.IO', alias=['TETRIO', 'tetr.io', 'tetrio', 'io'],
Subcommand( dest='TETRIO',
'bind', help_text='TETR.IO 游戏相关指令',
Args(
Arg(
'account',
get_player,
notice='TETR.IO 用户名 / ID',
flags=[ArgFlag.HIDDEN],
)
),
help_text='绑定 TETR.IO 账号',
),
# Subcommand(
# 'query',
# Args(
# Arg(
# 'target',
# At | Me,
# notice='@想要查询的人 / 自己',
# flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL],
# ),
# Arg(
# 'account',
# get_player,
# notice='TETR.IO 用户名 / ID',
# flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL],
# ),
# ),
# Option(
# '--template',
# Arg('template', Template),
# alias=['-T'],
# help_text='要使用的查询模板',
# ),
# help_text='查询 TETR.IO 游戏信息',
# ),
Subcommand(
'record',
Option(
'--40l',
dest='sprint',
),
Option(
'--blitz',
dest='blitz',
),
Args(
Arg(
'target',
At | Me,
notice='@想要查询的人 / 自己',
flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL],
),
Arg(
'account',
get_player,
notice='TETR.IO 用户名 / ID',
flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL],
),
),
),
# Subcommand(
# 'list',
# Option('--max-tr', Arg('max_tr', float), help_text='TR的上限'),
# Option('--min-tr', Arg('min_tr', float), help_text='TR的下限'),
# Option('--limit', Arg('limit', int), help_text='查询数量'),
# Option('--country', Arg('country', str), help_text='国家代码'),
# help_text='查询 TETR.IO 段位排行榜',
# ),
# Subcommand(
# 'rank',
# Subcommand(
# '--all',
# Option(
# '--template',
# Arg('template', Template),
# alias=['-T'],
# help_text='要使用的查询模板',
# ),
# dest='all',
# ),
# Option(
# '--detail',
# Arg('rank', ValidRank),
# alias=['-D'],
# ),
# help_text='查询 TETR.IO 段位信息',
# ),
Subcommand(
'config',
Option(
'--default-template',
Arg('template', Template),
alias=['-DT', 'DefaultTemplate'],
),
),
alias=['TETRIO', 'tetr.io', 'tetrio', 'io'],
dest='TETRIO',
help_text='TETR.IO 游戏相关指令',
)
) )
# def rank_wrapper(slot: int | str, content: str | None): from . import bind, config, query, record # noqa: E402
# if slot == 'rank' and not content:
# return '--all'
# if content is not None:
# return f'--detail {content.lower()}'
# return content
main_command.add(command)
alc.shortcut(
'(?i:io)(?i:绑定|绑|bind)',
command='tstats TETR.IO bind',
humanized='io绑定',
)
# alc.shortcut(
# '(?i:io)(?i:查询|查|query|stats)',
# command='tstats TETR.IO query',
# humanized='io查',
# )
alc.shortcut(
'(?i:io)(?i:记录|record)(?i:40l)',
command='tstats TETR.IO record --40l',
humanized='io记录40l',
)
alc.shortcut(
'(?i:io)(?i:记录|record)(?i:blitz)',
command='tstats TETR.IO record --blitz',
humanized='io记录blitz',
)
# alc.shortcut(
# r'(?i:io)(?i:段位|段|rank)\s*(?P<rank>[a-zA-Z+-]{0,2})',
# command='tstats TETR.IO rank {rank}',
# humanized='iorank',
# fuzzy=False,
# wrapper=rank_wrapper,
# )
alc.shortcut(
'(?i:io)(?i:配置|配|config)',
command='tstats TETR.IO config',
humanized='io配置',
)
# alc.shortcut(
# 'fkosk',
# command='tstats TETR.IO query',
# arguments=['我'],
# fuzzy=False,
# humanized='An Easter egg!',
# )
# add_block_handlers(alc.assign('TETRIO.query'))
# from . import bind, config, list, query, rank, record
from . import bind, config, record # noqa: E402
__all__ = [ __all__ = [
'alc',
'bind', 'bind',
'config', 'config',
# 'list', 'query',
# 'query',
# 'rank',
'record', 'record',
] ]

View File

@@ -39,13 +39,13 @@ class Garbage(BaseModel):
class Stats(BaseModel): class Stats(BaseModel):
seed: int seed: int | None = None # ?: 不知道是之后都没有了还是还会有
lines: int lines: int
level_lines: int level_lines: int
level_lines_needed: int level_lines_needed: int
inputs: int inputs: int
holds: int holds: int
time: Time time: Time | None = None # ?: 不知道是之后都没有了还是还会有
score: int score: int
zenlevel: int zenlevel: int
zenprogress: int zenprogress: int

View File

@@ -1,13 +1,15 @@
from asyncio import gather
from hashlib import md5 from hashlib import md5
from urllib.parse import urlencode from urllib.parse import urlencode
from arclet.alconna import Arg, ArgFlag
from nepattern import parser # type: ignore[import-untyped]
from nonebot_plugin_alconna import Args, Subcommand
from nonebot_plugin_alconna.uniseg import UniMessage from nonebot_plugin_alconna.uniseg import UniMessage
from nonebot_plugin_orm import get_session from nonebot_plugin_orm import get_session
from nonebot_plugin_session import EventSession # type: ignore[import-untyped] from nonebot_plugin_session import EventSession
from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped] from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped]
from nonebot_plugin_user import User # type: ignore[import-untyped] from nonebot_plugin_user import User
from nonebot_plugin_userinfo import BotUserInfo, UserInfo # type: ignore[import-untyped] from nonebot_plugin_userinfo import BotUserInfo, UserInfo
from ...db import BindStatus, create_or_update_bind, trigger from ...db import BindStatus, create_or_update_bind, trigger
from ...utils.host import HostPage, get_self_netloc from ...utils.host import HostPage, get_self_netloc
@@ -15,10 +17,31 @@ from ...utils.image import get_avatar
from ...utils.render import Bind, render from ...utils.render import Bind, render
from ...utils.render.schemas.base import Avatar, People from ...utils.render.schemas.base import Avatar, People
from ...utils.screenshot import screenshot from ...utils.screenshot import screenshot
from . import alc from . import alc, command, get_player
from .api import Player from .api import Player
from .constant import GAME_TYPE from .constant import GAME_TYPE
command.add(
Subcommand(
'bind',
Args(
Arg(
'account',
parser(get_player),
notice='TETR.IO 用户名 / ID',
flags=[ArgFlag.HIDDEN],
)
),
help_text='绑定 TETR.IO 账号',
)
)
alc.shortcut(
'(?i:io)(?i:绑定|绑|bind)',
command='tstats TETR.IO bind',
humanized='io绑定',
)
@alc.assign('TETRIO.bind') @alc.assign('TETRIO.bind')
async def _(nb_user: User, account: Player, event_session: EventSession, bot_info: UserInfo = BotUserInfo()): # noqa: B008 async def _(nb_user: User, account: Player, event_session: EventSession, bot_info: UserInfo = BotUserInfo()): # noqa: B008
@@ -28,7 +51,7 @@ async def _(nb_user: User, account: Player, event_session: EventSession, bot_inf
command_type='bind', command_type='bind',
command_args=[], command_args=[],
): ):
user, user_info = await gather(account.user, account.get_info()) user = await account.user
async with get_session() as session: async with get_session() as session:
bind_status = await create_or_update_bind( bind_status = await create_or_update_bind(
session=session, session=session,

View File

@@ -1,16 +1,38 @@
from arclet.alconna import Arg
from nepattern import parser # type: ignore[import-untyped]
from nonebot_plugin_alconna import Option, Subcommand
from nonebot_plugin_alconna.uniseg import UniMessage from nonebot_plugin_alconna.uniseg import UniMessage
from nonebot_plugin_orm import async_scoped_session from nonebot_plugin_orm import async_scoped_session
from nonebot_plugin_session import EventSession # type: ignore[import-untyped] from nonebot_plugin_session import EventSession
from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped] from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped]
from nonebot_plugin_user import User # type: ignore[import-untyped] from nonebot_plugin_user import User
from sqlalchemy import select from sqlalchemy import select
from ...db import trigger from ...db import trigger
from . import alc from . import alc, command
from .constant import GAME_TYPE from .constant import GAME_TYPE
from .models import TETRIOUserConfig from .models import TETRIOUserConfig
from .typing import Template from .typing import Template
command.add(
Subcommand(
'config',
Option(
'--default-template',
Arg('template', parser(Template), notice='模板版本'),
alias=['-DT', 'DefaultTemplate'],
help_text='设置默认查询模板',
),
help_text='TETR.IO 查询个性化配置',
),
)
alc.shortcut(
'(?i:io)(?i:配置|配|config)',
command='tstats TETR.IO config',
humanized='io配置',
)
@alc.assign('TETRIO.config') @alc.assign('TETRIO.config')
async def _(user: User, session: async_scoped_session, event_session: EventSession, template: Template): async def _(user: User, session: async_scoped_session, event_session: EventSession, template: Template):

View File

@@ -0,0 +1,242 @@
from asyncio import gather
from datetime import datetime, timedelta, timezone
from hashlib import md5
from typing import TYPE_CHECKING, TypeVar
from urllib.parse import urlencode
from arclet.alconna import Arg, ArgFlag
from nepattern import parser # type: ignore[import-untyped]
from nonebot import get_driver
from nonebot.adapters import Event
from nonebot.matcher import Matcher
from nonebot_plugin_alconna import Args, At, Option, Subcommand
from nonebot_plugin_alconna.uniseg import UniMessage
from nonebot_plugin_orm import get_session
from nonebot_plugin_session import EventSession
from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped]
from nonebot_plugin_user import User as NBUser
from nonebot_plugin_user import get_user
from sqlalchemy import select
from ...db import query_bind_info, trigger
from ...utils.host import HostPage, get_self_netloc
from ...utils.render import render
from ...utils.render.schemas.base import Avatar
from ...utils.render.schemas.tetrio.user.info_v2 import Badge, Blitz, Sprint, Statistic, Zen
from ...utils.render.schemas.tetrio.user.info_v2 import Info as V2TemplateInfo
from ...utils.render.schemas.tetrio.user.info_v2 import User as V2TemplateUser
from ...utils.screenshot import screenshot
from ...utils.typing import Me
from .. import add_block_handlers, alc
from ..constant import CANT_VERIFY_MESSAGE
from . import command, get_player
from .api import Player
from .constant import GAME_TYPE
from .models import TETRIOUserConfig
from .typing import Template
if TYPE_CHECKING:
from .api.schemas.summaries import SoloSuccessModel, ZenSuccessModel
from .api.schemas.user import User
from .api.schemas.user_info import UserInfoSuccess
UTC = timezone.utc
driver = get_driver()
command.add(
Subcommand(
'query',
Args(
Arg(
'target',
parser(At | Me),
notice='@想要查询的人 / 自己',
flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL],
),
Arg(
'account',
parser(get_player),
notice='TETR.IO 用户名 / ID',
flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL],
),
),
Option(
'--template',
Arg('template', parser(Template)),
alias=['-T'],
help_text='要使用的查询模板',
),
help_text='查询 TETR.IO 游戏信息',
),
)
alc.shortcut(
'(?i:io)(?i:查询|查|query|stats)',
command='tstats TETR.IO query',
humanized='io查',
)
alc.shortcut(
'fkosk',
command='tstats TETR.IO query',
arguments=[''],
fuzzy=False,
humanized='An Easter egg!',
)
add_block_handlers(alc.assign('TETRIO.query'))
@alc.assign('TETRIO.query')
async def _( # noqa: PLR0913
user: NBUser,
event: Event,
matcher: Matcher,
target: At | Me,
event_session: EventSession,
template: Template | None = None,
):
async with trigger(
session_persist_id=await get_session_persist_id(event_session),
game_platform=GAME_TYPE,
command_type='query',
command_args=[f'--default-template {template}'] if template is not None else [],
):
async with get_session() as session:
bind = await query_bind_info(
session=session,
user=await get_user(
event_session.platform, target.target if isinstance(target, At) else event.get_user_id()
),
game_platform=GAME_TYPE,
)
if template is None:
template = await session.scalar(
select(TETRIOUserConfig.query_template).where(TETRIOUserConfig.id == user.id)
)
if bind is None:
await matcher.finish('未查询到绑定信息')
message = UniMessage(CANT_VERIFY_MESSAGE)
player = Player(user_id=bind.game_account, trust=True)
await (message + UniMessage.image(raw=await make_query_image_v2(player))).finish()
@alc.assign('TETRIO.query')
async def _(user: NBUser, account: Player, event_session: EventSession, template: Template | None = None):
async with trigger(
session_persist_id=await get_session_persist_id(event_session),
game_platform=GAME_TYPE,
command_type='query',
command_args=[f'--default-template {template}'] if template is not None else [],
):
async with get_session() as session:
if template is None:
template = await session.scalar(
select(TETRIOUserConfig.query_template).where(TETRIOUserConfig.id == user.id)
)
await (UniMessage.image(raw=await make_query_image_v2(account))).finish()
N = TypeVar('N', int, float)
def handling_special_value(value: N) -> N | None:
return value if value != -1 else None
async def make_query_image_v2(player: Player) -> bytes:
user: User
user_info: UserInfoSuccess
sprint: SoloSuccessModel
blitz: SoloSuccessModel
zen: ZenSuccessModel
avatar_revision: int | None
banner_revision: int | None
# [todo) 有没有什么办法能让这类型推导成功)
user, user_info, sprint, blitz, zen, avatar_revision, banner_revision = await gather( # type: ignore[assignment]
player.user,
player.get_info(),
player.sprint,
player.blitz,
player.zen,
player.avatar_revision,
player.banner_revision,
)
if sprint.data.record is not None:
duration = timedelta(milliseconds=sprint.data.record.results.stats.finaltime).total_seconds()
sprint_value = f'{duration:.3f}s' if duration < 60 else f'{duration // 60:.0f}m {duration % 60:.3f}s' # noqa: PLR2004
else:
sprint_value = 'N/A'
play_time: str | None
if (game_time := handling_special_value(user_info.data.gametime)) is not None:
if game_time // 3600 > 0:
play_time = f'{game_time//3600:.0f}h {game_time % 3600 // 60:.0f}m {game_time % 60:.0f}s'
elif game_time // 60 > 0:
play_time = f'{game_time//60:.0f}m {game_time % 60:.0f}s'
else:
play_time = f'{game_time:.0f}s'
else:
play_time = game_time
netloc = get_self_netloc()
async with HostPage(
await render(
'v2/tetrio/user/info',
V2TemplateInfo(
user=V2TemplateUser(
id=user.ID,
name=user.name.upper(),
bio=user_info.data.bio,
banner=f'http://{netloc}/host/resource/tetrio/banners/{user.ID}?{urlencode({"revision": banner_revision})}'
if banner_revision is not None and banner_revision != 0
else None,
avatar=f'http://{netloc}/host/resource/tetrio/avatars/{user.ID}?{urlencode({"revision": avatar_revision})}'
if avatar_revision is not None and avatar_revision != 0
else Avatar(
type='identicon',
hash=md5(user.ID.encode()).hexdigest(), # noqa: S324
),
badges=[
Badge(
id=i.id,
description=i.label,
group=i.group,
receive_at=i.ts if isinstance(i.ts, datetime) else None,
)
for i in user_info.data.badges
],
country=user_info.data.country,
role=user_info.data.role,
xp=user_info.data.xp,
friend_count=user_info.data.friend_count,
supporter_tier=user_info.data.supporter_tier,
bad_standing=user_info.data.badstanding or False,
verified=user_info.data.verified,
playtime=play_time,
join_at=user_info.data.ts,
),
tetra_league=None,
statistic=Statistic(
total=handling_special_value(user_info.data.gamesplayed),
wins=handling_special_value(user_info.data.gameswon),
),
sprint=Sprint(
time=sprint_value,
global_rank=sprint.data.rank,
play_at=sprint.data.record.ts,
)
if sprint.data.record is not None
else None,
blitz=Blitz(
score=blitz.data.record.results.stats.score,
global_rank=blitz.data.rank,
play_at=blitz.data.record.ts,
)
if blitz.data.record is not None
else None,
zen=Zen(level=zen.data.level, score=zen.data.score),
),
),
) as page_hash:
return await screenshot(f'http://{netloc}/host/{page_hash}.html')

View File

@@ -1,4 +1,31 @@
from . import blitz, sprint from arclet.alconna import Arg, ArgFlag
from nonebot_plugin_alconna import Args, At, Subcommand
from ....utils.typing import Me
from .. import command as base_command
from .. import get_player
command = Subcommand(
'record',
Args(
Arg(
'target',
At | Me,
notice='@想要查询的人 / 自己',
flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL],
),
Arg(
'account',
get_player,
notice='TETR.IO 用户名 / ID',
flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL],
),
),
)
from . import blitz, sprint # noqa: E402
base_command.add(command)
__all__ = [ __all__ = [
'blitz', 'blitz',

View File

@@ -5,12 +5,12 @@ from urllib.parse import urlencode
from nonebot.adapters import Event from nonebot.adapters import Event
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot_plugin_alconna import At from nonebot_plugin_alconna import At, Option
from nonebot_plugin_alconna.uniseg import UniMessage from nonebot_plugin_alconna.uniseg import UniMessage
from nonebot_plugin_orm import get_session from nonebot_plugin_orm import get_session
from nonebot_plugin_session import EventSession # type: ignore[import-untyped] from nonebot_plugin_session import EventSession
from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped] from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped]
from nonebot_plugin_user import get_user # type: ignore[import-untyped] from nonebot_plugin_user import get_user
from ....db import query_bind_info, trigger from ....db import query_bind_info, trigger
from ....utils.exception import RecordNotFoundError from ....utils.exception import RecordNotFoundError
@@ -18,14 +18,23 @@ 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.tetrio_record_base import Finesse, Max, Mini, Tspins, User from ....utils.render.schemas.tetrio.record.base import Finesse, Max, Mini, Tspins, User
from ....utils.render.schemas.tetrio.tetrio_record_blitz import Record, Statistic from ....utils.render.schemas.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
from .. import alc from .. import alc
from ..api.player import Player from ..api.player import Player
from ..constant import GAME_TYPE from ..constant import GAME_TYPE
from . import command
command.add(Option('--blitz', dest='blitz'))
alc.shortcut(
'(?i:io)(?i:记录|record)(?i:blitz)',
command='tstats TETR.IO record --blitz',
humanized='io记录blitz',
)
@alc.assign('TETRIO.record.blitz') @alc.assign('TETRIO.record.blitz')
@@ -81,7 +90,7 @@ async def make_blitz_image(player: Player) -> bytes:
page=await render( page=await render(
'v2/tetrio/record/blitz', 'v2/tetrio/record/blitz',
Record( Record(
type='personal_best', type='best',
user=User( user=User(
id=user.ID, id=user.ID,
name=user.name.upper(), name=user.name.upper(),

View File

@@ -5,12 +5,12 @@ from urllib.parse import urlencode
from nonebot.adapters import Event from nonebot.adapters import Event
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot_plugin_alconna import At from nonebot_plugin_alconna import At, Option
from nonebot_plugin_alconna.uniseg import UniMessage from nonebot_plugin_alconna.uniseg import UniMessage
from nonebot_plugin_orm import get_session from nonebot_plugin_orm import get_session
from nonebot_plugin_session import EventSession # type: ignore[import-untyped] from nonebot_plugin_session import EventSession
from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped] from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped]
from nonebot_plugin_user import get_user # type: ignore[import-untyped] from nonebot_plugin_user import get_user
from ....db import query_bind_info, trigger from ....db import query_bind_info, trigger
from ....utils.exception import RecordNotFoundError from ....utils.exception import RecordNotFoundError
@@ -18,14 +18,23 @@ 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.tetrio_record_base import Finesse, Max, Mini, Statistic, Tspins, User from ....utils.render.schemas.tetrio.record.base import Finesse, Max, Mini, Statistic, Tspins, User
from ....utils.render.schemas.tetrio.tetrio_record_sprint import Record from ....utils.render.schemas.tetrio.record.sprint import Record
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
from .. import alc from .. import alc
from ..api.player import Player from ..api.player import Player
from ..constant import GAME_TYPE from ..constant import GAME_TYPE
from . import command
command.add(Option('--40l', dest='sprint'))
alc.shortcut(
'(?i:io)(?i:记录|record)(?i:40l)',
command='tstats TETR.IO record --40l',
humanized='io记录40l',
)
@alc.assign('TETRIO.record.sprint') @alc.assign('TETRIO.record.sprint')
@@ -82,7 +91,7 @@ async def make_sprint_image(player: Player) -> bytes:
page=await render( page=await render(
'v2/tetrio/record/40l', 'v2/tetrio/record/40l',
Record( Record(
type='personal_best', type='best',
user=User( user=User(
id=user.ID, id=user.ID,
name=user.name.upper(), name=user.name.upper(),

View File

@@ -1,5 +1,6 @@
from arclet.alconna import Arg, ArgFlag, Args, Subcommand from arclet.alconna import Arg, ArgFlag
from nonebot_plugin_alconna import At from nepattern import parser # type: ignore[import-untyped]
from nonebot_plugin_alconna import Args, At, Subcommand
from ...utils.exception import MessageFormatError from ...utils.exception import MessageFormatError
from ...utils.typing import Me from ...utils.typing import Me
@@ -22,7 +23,7 @@ command.add(
Args( Args(
Arg( Arg(
'account', 'account',
get_player, parser(get_player),
notice='TOP 用户名 / ID', notice='TOP 用户名 / ID',
flags=[ArgFlag.HIDDEN], flags=[ArgFlag.HIDDEN],
) )
@@ -34,13 +35,13 @@ command.add(
Args( Args(
Arg( Arg(
'target', 'target',
At | Me, parser(At | Me),
notice='@想要查询的人 / 自己', notice='@想要查询的人 / 自己',
flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL], flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL],
), ),
Arg( Arg(
'account', 'account',
get_player, parser(get_player),
notice='TOP 用户名', notice='TOP 用户名',
flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL], flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL],
), ),

View File

@@ -1,9 +1,9 @@
from nonebot_plugin_alconna.uniseg import UniMessage from nonebot_plugin_alconna.uniseg import UniMessage
from nonebot_plugin_orm import get_session from nonebot_plugin_orm import get_session
from nonebot_plugin_session import EventSession # type: ignore[import-untyped] from nonebot_plugin_session import EventSession
from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped] from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped]
from nonebot_plugin_user import User # type: ignore[import-untyped] from nonebot_plugin_user import User
from nonebot_plugin_userinfo import BotUserInfo, EventUserInfo, UserInfo # type: ignore[import-untyped] from nonebot_plugin_userinfo import BotUserInfo, EventUserInfo, UserInfo
from ...db import BindStatus, create_or_update_bind, trigger from ...db import BindStatus, create_or_update_bind, trigger
from ...utils.host import HostPage, get_self_netloc from ...utils.host import HostPage, get_self_netloc

View File

@@ -3,9 +3,9 @@ from nonebot.matcher import Matcher
from nonebot_plugin_alconna import At from nonebot_plugin_alconna import At
from nonebot_plugin_alconna.uniseg import UniMessage from nonebot_plugin_alconna.uniseg import UniMessage
from nonebot_plugin_orm import get_session from nonebot_plugin_orm import get_session
from nonebot_plugin_session import EventSession # type: ignore[import-untyped] from nonebot_plugin_session import EventSession
from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped] from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped]
from nonebot_plugin_user import get_user # type: ignore[import-untyped] from nonebot_plugin_user import get_user
from ...db import query_bind_info, trigger from ...db import query_bind_info, trigger
from ...utils.metrics import get_metrics from ...utils.metrics import get_metrics

View File

@@ -1,5 +1,6 @@
from arclet.alconna import Arg, ArgFlag, Args, Subcommand from arclet.alconna import Arg, ArgFlag
from nonebot_plugin_alconna import At from nepattern import parser # type: ignore[import-untyped]
from nonebot_plugin_alconna import Args, At, Subcommand
from ...utils.exception import MessageFormatError from ...utils.exception import MessageFormatError
from ...utils.typing import Me from ...utils.typing import Me
@@ -27,7 +28,7 @@ command.add(
Args( Args(
Arg( Arg(
'account', 'account',
get_player, parser(get_player),
notice='茶服 用户名 / ID', notice='茶服 用户名 / ID',
flags=[ArgFlag.HIDDEN], flags=[ArgFlag.HIDDEN],
) )
@@ -39,13 +40,13 @@ command.add(
Args( Args(
Arg( Arg(
'target', 'target',
At | Me, parser(At | Me),
notice='@想要查询的人 / 自己', notice='@想要查询的人 / 自己',
flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL], flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL],
), ),
Arg( Arg(
'account', 'account',
get_player, parser(get_player),
notice='茶服 用户名 / TeaID', notice='茶服 用户名 / TeaID',
flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL], flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL],
), ),

View File

@@ -1,9 +1,9 @@
from nonebot_plugin_alconna.uniseg import UniMessage from nonebot_plugin_alconna.uniseg import UniMessage
from nonebot_plugin_orm import get_session from nonebot_plugin_orm import get_session
from nonebot_plugin_session import EventSession # type: ignore[import-untyped] from nonebot_plugin_session import EventSession
from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped] from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped]
from nonebot_plugin_user import User # type: ignore[import-untyped] from nonebot_plugin_user import User
from nonebot_plugin_userinfo import BotUserInfo, EventUserInfo, UserInfo # type: ignore[import-untyped] from nonebot_plugin_userinfo import BotUserInfo, EventUserInfo, UserInfo
from ...db import BindStatus, create_or_update_bind, trigger from ...db import BindStatus, create_or_update_bind, trigger
from ...utils.host import HostPage, get_self_netloc from ...utils.host import HostPage, get_self_netloc

View File

@@ -8,10 +8,10 @@ from nonebot.matcher import Matcher
from nonebot_plugin_alconna import At from nonebot_plugin_alconna import At
from nonebot_plugin_alconna.uniseg import UniMessage from nonebot_plugin_alconna.uniseg import UniMessage
from nonebot_plugin_orm import get_session from nonebot_plugin_orm import get_session
from nonebot_plugin_session import EventSession # type: ignore[import-untyped] from nonebot_plugin_session import EventSession
from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped] from nonebot_plugin_session_orm import get_session_persist_id # type: ignore[import-untyped]
from nonebot_plugin_user import get_user # type: ignore[import-untyped] from nonebot_plugin_user import get_user
from nonebot_plugin_userinfo import EventUserInfo, UserInfo # type: ignore[import-untyped] from nonebot_plugin_userinfo import EventUserInfo, UserInfo
from ...db import query_bind_info, trigger from ...db import query_bind_info, trigger
from ...utils.exception import RequestError from ...utils.exception import RequestError

View File

@@ -2,7 +2,7 @@ from base64 import b64encode
from io import BytesIO from io import BytesIO
from typing import Literal, overload from typing import Literal, overload
from nonebot_plugin_userinfo import UserInfo # type: ignore[import-untyped] from nonebot_plugin_userinfo import UserInfo
from PIL import Image from PIL import Image

View File

@@ -5,14 +5,14 @@ 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.tetrio_info import Info as TETRIOInfo from .schemas.tetrio.rank.detail import Data as TETRIORankDetailData
from .schemas.tetrio.tetrio_rank_detail import Data as TETRIORankDetailData from .schemas.tetrio.rank.v1 import Data as TETRIORankDataV1
from .schemas.tetrio.tetrio_rank_v1 import Data as TETRIORankDataV1 from .schemas.tetrio.rank.v2 import Data as TETRIORankDataV2
from .schemas.tetrio.tetrio_rank_v2 import Data as TETRIORankDataV2 from .schemas.tetrio.record.blitz import Record as TETRIORecordBlitz
from .schemas.tetrio.tetrio_record_blitz import Record as TETRIORecordBlitz from .schemas.tetrio.record.sprint import Record as TETRIORecordSprint
from .schemas.tetrio.tetrio_record_sprint import Record as TETRIORecordSprint from .schemas.tetrio.user.info_v1 import Info as TETRIOUserInfoV1
from .schemas.tetrio.tetrio_user_info_v2 import Info as TETRIOUserInfoV2 from .schemas.tetrio.user.info_v2 import Info as TETRIOUserInfoV2
from .schemas.tetrio.tetrio_user_list_v2 import List as TETRIOUserListV2 from .schemas.tetrio.user.list_v2 import List as TETRIOUserListV2
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
@@ -24,7 +24,7 @@ env = Environment(
@overload @overload
async def render(render_type: Literal['v1/binding'], data: Bind) -> str: ... async def render(render_type: Literal['v1/binding'], data: Bind) -> str: ...
@overload @overload
async def render(render_type: Literal['v1/tetrio/info'], data: TETRIOInfo) -> str: ... async def render(render_type: Literal['v1/tetrio/info'], data: TETRIOUserInfoV1) -> str: ...
@overload @overload
async def render(render_type: Literal['v1/tetrio/rank'], data: TETRIORankDataV1) -> str: ... async def render(render_type: Literal['v1/tetrio/rank'], data: TETRIORankDataV1) -> str: ...
@overload @overload
@@ -60,7 +60,7 @@ async def render(
'v2/tetrio/rank/detail', 'v2/tetrio/rank/detail',
], ],
data: Bind data: Bind
| TETRIOInfo | TETRIOUserInfoV1
| TETRIORankDataV1 | TETRIORankDataV1
| TOPInfo | TOPInfo
| TOSInfo | TOSInfo

View File

@@ -2,7 +2,7 @@ from datetime import datetime
from pydantic import BaseModel from pydantic import BaseModel
from .....games.tetrio.api.typing import ValidRank from ......games.tetrio.api.typing import ValidRank
class SpecialData(BaseModel): class SpecialData(BaseModel):

View File

@@ -2,7 +2,7 @@ from datetime import datetime
from pydantic import BaseModel from pydantic import BaseModel
from .....games.tetrio.api.typing import ValidRank from ......games.tetrio.api.typing import ValidRank
class ItemData(BaseModel): class ItemData(BaseModel):

View File

@@ -2,7 +2,7 @@ from datetime import datetime
from pydantic import BaseModel from pydantic import BaseModel
from .....games.tetrio.api.typing import ValidRank from ......games.tetrio.api.typing import ValidRank
class AverageData(BaseModel): class AverageData(BaseModel):

View File

@@ -3,7 +3,7 @@ from typing import Literal
from pydantic import BaseModel from pydantic import BaseModel
from ..base import People from ...base import People
class User(People): class User(People):

View File

@@ -1,5 +1,5 @@
from .tetrio_record_base import Record as BaseRecord from .base import Record as BaseRecord
from .tetrio_record_base import Statistic as BaseStatistic from .base import Statistic as BaseStatistic
class Statistic(BaseStatistic): class Statistic(BaseStatistic):

View File

@@ -0,0 +1,5 @@
from .base import Record as BaseRecord
class Record(BaseRecord):
time: str

View File

@@ -1,5 +0,0 @@
from .tetrio_record_base import Record as BaseRecord
class Record(BaseRecord):
time: str

View File

@@ -2,7 +2,7 @@ from datetime import datetime
from pydantic import BaseModel from pydantic import BaseModel
from ....typing import Number from .....typing import Number
class TetraLeagueHistoryData(BaseModel): class TetraLeagueHistoryData(BaseModel):

View File

@@ -1,8 +1,8 @@
from pydantic import BaseModel from pydantic import BaseModel
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 People, Ranking from ...base import People, Ranking
from .base import TetraLeagueHistoryData from .base import TetraLeagueHistoryData

View File

@@ -3,10 +3,10 @@ from typing import Literal
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, ValidRank
from ....typing import Number from .....typing import Number
from ..base import Avatar from ...base import Avatar
from .base import TetraLeagueHistoryData from .base import TetraLeagueHistoryData
@@ -54,20 +54,20 @@ class TetraLeagueStatistic(BaseModel):
class TetraLeague(BaseModel): class TetraLeague(BaseModel):
rank: Rank rank: Rank
highest_rank: Rank highest_rank: ValidRank
tr: Number tr: Number
glicko: Number glicko: Number | None
rd: Number rd: Number | None
global_rank: int | None global_rank: int | None
country_rank: int | None country_rank: int | None
pps: Number pps: Number | None
apm: Number apm: Number | None
apl: Number apl: Number | None
vs: Number | None vs: Number | None
adpl: Number | None adpl: Number | None
@@ -76,7 +76,7 @@ class TetraLeague(BaseModel):
decaying: bool decaying: bool
history: list[TetraLeagueHistoryData] history: list[TetraLeagueHistoryData] | None
class Sprint(BaseModel): class Sprint(BaseModel):
@@ -97,4 +97,4 @@ class Info(BaseModel):
statistic: Statistic | None statistic: Statistic | None
sprint: Sprint | None sprint: Sprint | None
blitz: Blitz | None blitz: Blitz | None
zen: Zen zen: Zen | None

View File

@@ -2,9 +2,9 @@ from datetime import datetime
from pydantic import BaseModel from pydantic import BaseModel
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
class TetraLeague(BaseModel): class TetraLeague(BaseModel):

956
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = 'nonebot-plugin-tetris-stats' name = 'nonebot-plugin-tetris-stats'
version = '1.4.2' version = '1.4.3'
description = '一款基于 NoneBot2 的用于查询 Tetris 相关游戏数据的插件' description = '一款基于 NoneBot2 的用于查询 Tetris 相关游戏数据的插件'
authors = ['scdhh <wallfjjd@gmail.com>'] authors = ['scdhh <wallfjjd@gmail.com>']
readme = 'README.md' readme = 'README.md'
@@ -21,7 +21,6 @@ nonebot-plugin-user = ">=0.2,<0.4"
nonebot-plugin-userinfo = "^0.2.4" nonebot-plugin-userinfo = "^0.2.4"
aiocache = "^0.12.2" aiocache = "^0.12.2"
aiofiles = ">=23.2.1,<25.0.0" aiofiles = ">=23.2.1,<25.0.0"
arclet-alconna = "^1.8.19"
async-lru = "^2.0.4" async-lru = "^2.0.4"
httpx = "^0.27.0" httpx = "^0.27.0"
jinja2 = "^3.1.3" jinja2 = "^3.1.3"
@@ -131,3 +130,4 @@ quote-style = 'single'
[tool.nonebot] [tool.nonebot]
plugins = ['nonebot_plugin_tetris_stats'] plugins = ['nonebot_plugin_tetris_stats']
# plugins = ['test_datetime']