Compare commits

...

17 Commits

Author SHA1 Message Date
fbe018e56a 🔖 1.2.11 2024-06-08 12:04:44 +08:00
ab046fe786 使用 nonebot-plugin-user 进行身份绑定 close #63 2024-06-08 12:03:07 +08:00
ce95d8f977 完善 retry 装饰器 2024-06-08 11:57:45 +08:00
fa05b80e61 修改截图方式 2024-06-08 11:57:21 +08:00
0ab0d11a98 添加依赖 nonebot-plugin-user 2024-06-08 11:55:47 +08:00
7f469540b2 ️ 删除一个不必要的async 2024-06-08 00:55:42 +08:00
21bee29146 适配 v2 模板 2024-06-07 21:18:12 +08:00
dependabot[bot]
c2dd9c5d86 ⬆️ Bump nonebot-plugin-alconna from 0.46.3 to 0.46.4 (#333)
Bumps [nonebot-plugin-alconna](https://github.com/nonebot/plugin-alconna) from 0.46.3 to 0.46.4.
- [Release notes](https://github.com/nonebot/plugin-alconna/releases)
- [Commits](https://github.com/nonebot/plugin-alconna/compare/v0.46.3...v0.46.4)

---
updated-dependencies:
- dependency-name: nonebot-plugin-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-06-06 21:33:51 +08:00
dependabot[bot]
5927cb2bb5 ⬆️ Bump pandas-stubs from 2.2.2.240514 to 2.2.2.240603 (#334)
Bumps [pandas-stubs](https://github.com/pandas-dev/pandas-stubs) from 2.2.2.240514 to 2.2.2.240603.
- [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.240514...v2.2.2.240603)

---
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-06-06 21:33:35 +08:00
dependabot[bot]
4a4a215b61 ⬆️ Bump ruff from 0.4.6 to 0.4.8 (#336)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.6 to 0.4.8.
- [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/v0.4.6...v0.4.8)

---
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-06-06 21:33:15 +08:00
bfe931d3bf 为截图添加自动重试 2024-06-04 20:15:41 +08:00
b7b152d84d 完善 retry 装饰器的类型,并添加消息提示功能 2024-06-04 20:14:19 +08:00
b6f6eb1170 sprint 成绩保留三位小数 2024-06-04 20:12:56 +08:00
934800aae0 🐛 修正 UniMessage 的使用方式 2024-06-04 20:10:57 +08:00
dependabot[bot]
d19c37e99a ⬆️ Bump nonebot-plugin-alconna from 0.46.1 to 0.46.3 (#331)
Bumps [nonebot-plugin-alconna](https://github.com/nonebot/plugin-alconna) from 0.46.1 to 0.46.3.
- [Release notes](https://github.com/nonebot/plugin-alconna/releases)
- [Commits](https://github.com/nonebot/plugin-alconna/compare/v0.46.1...v0.46.3)

---
updated-dependencies:
- dependency-name: nonebot-plugin-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-06-01 14:50:10 +08:00
dependabot[bot]
43167fe9bd ⬆️ Bump ruff from 0.4.4 to 0.4.6 (#330)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.4 to 0.4.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/v0.4.4...v0.4.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-06-01 14:50:00 +08:00
dependabot[bot]
db8de88667 ⬆️ Bump nonebot-plugin-orm from 0.7.2 to 0.7.3 (#327)
updated-dependencies:
- dependency-name: nonebot-plugin-orm
  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-06-01 14:49:44 +08:00
16 changed files with 338 additions and 133 deletions

View File

@@ -5,8 +5,9 @@ require('nonebot_plugin_alconna')
require('nonebot_plugin_apscheduler') require('nonebot_plugin_apscheduler')
require('nonebot_plugin_localstore') require('nonebot_plugin_localstore')
require('nonebot_plugin_orm') require('nonebot_plugin_orm')
require('nonebot_plugin_session')
require('nonebot_plugin_session_orm') require('nonebot_plugin_session_orm')
require('nonebot_plugin_session')
require('nonebot_plugin_user')
from nonebot_plugin_alconna import namespace # noqa: E402 from nonebot_plugin_alconna import namespace # noqa: E402

View File

@@ -0,0 +1,71 @@
"""Migrate to nonobot-plugin-user
迁移 ID: b15844837693
父迁移: 3c25a5a8c050
创建时间: 2024-06-08 02:27:35.227596
"""
from __future__ import annotations
from typing import TYPE_CHECKING
import sqlalchemy as sa
from alembic import op
if TYPE_CHECKING:
from collections.abc import Sequence
revision: str = 'b15844837693'
down_revision: str | Sequence[str] | None = '3c25a5a8c050'
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade(name: str = '') -> None:
if name:
return
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('nonebot_plugin_tetris_stats_bind', schema=None) as batch_op:
batch_op.drop_index('ix_nonebot_plugin_tetris_stats_bind_chat_account')
batch_op.drop_index('ix_nonebot_plugin_tetris_stats_bind_chat_platform')
op.drop_table('nonebot_plugin_tetris_stats_bind')
op.create_table(
'nonebot_plugin_tetris_stats_bind',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('game_platform', sa.String(length=32), nullable=False),
sa.Column('game_account', sa.String(), nullable=False),
sa.PrimaryKeyConstraint('id', name=op.f('pk_nonebot_plugin_tetris_stats_bind')),
info={'bind_key': 'nonebot_plugin_tetris_stats'},
)
with op.batch_alter_table('nonebot_plugin_tetris_stats_bind', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_nonebot_plugin_tetris_stats_bind_user_id'), ['user_id'], unique=False)
# ### end Alembic commands ###
def downgrade(name: str = '') -> None:
if name:
return
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('nonebot_plugin_tetris_stats_bind', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_nonebot_plugin_tetris_stats_bind_user_id'))
op.drop_table('nonebot_plugin_tetris_stats_bind')
op.create_table(
'nonebot_plugin_tetris_stats_bind',
sa.Column('id', sa.INTEGER(), nullable=False),
sa.Column('chat_platform', sa.VARCHAR(length=32), nullable=False),
sa.Column('chat_account', sa.VARCHAR(), nullable=False),
sa.Column('game_platform', sa.VARCHAR(length=32), nullable=False),
sa.Column('game_account', sa.VARCHAR(), nullable=False),
sa.PrimaryKeyConstraint('id', name='pk_nonebot_plugin_tetris_stats_bind'),
)
with op.batch_alter_table('nonebot_plugin_tetris_stats_bind', schema=None) as batch_op:
batch_op.create_index('ix_nonebot_plugin_tetris_stats_bind_chat_platform', ['chat_platform'], unique=False)
batch_op.create_index('ix_nonebot_plugin_tetris_stats_bind_chat_account', ['chat_account'], unique=False)
# ### end Alembic commands ###

View File

@@ -8,6 +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 sqlalchemy import select from sqlalchemy import select
from ..utils.typing import CommandType, GameType from ..utils.typing import CommandType, GameType
@@ -28,37 +29,28 @@ class BindStatus(Enum):
async def query_bind_info( async def query_bind_info(
session: AsyncSession, session: AsyncSession,
chat_platform: str, user: User,
chat_account: str,
game_platform: GameType, game_platform: GameType,
) -> Bind | None: ) -> Bind | None:
return ( return (
await session.scalars( await session.scalars(select(Bind).where(Bind.user_id == user.id).where(Bind.game_platform == game_platform))
select(Bind)
.where(Bind.chat_platform == chat_platform)
.where(Bind.chat_account == chat_account)
.where(Bind.game_platform == game_platform)
)
).one_or_none() ).one_or_none()
async def create_or_update_bind( async def create_or_update_bind(
session: AsyncSession, session: AsyncSession,
chat_platform: str, user: User,
chat_account: str,
game_platform: GameType, game_platform: GameType,
game_account: str, game_account: str,
) -> BindStatus: ) -> BindStatus:
bind = await query_bind_info( bind = await query_bind_info(
session=session, session=session,
chat_platform=chat_platform, user=user,
chat_account=chat_account,
game_platform=game_platform, game_platform=game_platform,
) )
if bind is None: if bind is None:
bind = Bind( bind = Bind(
chat_platform=chat_platform, user_id=user.id,
chat_account=chat_account,
game_platform=game_platform, game_platform=game_platform,
game_account=game_account, game_account=game_account,
) )

View File

@@ -66,8 +66,7 @@ class PydanticType(TypeDecorator):
class Bind(MappedAsDataclass, Model): class Bind(MappedAsDataclass, Model):
id: Mapped[int] = mapped_column(init=False, primary_key=True) id: Mapped[int] = mapped_column(init=False, primary_key=True)
chat_platform: Mapped[str] = mapped_column(String(32), index=True) user_id: Mapped[int] = mapped_column(index=True)
chat_account: Mapped[str] = mapped_column(index=True)
game_platform: Mapped[GameType] = mapped_column(String(32)) game_platform: Mapped[GameType] = mapped_column(String(32))
game_account: Mapped[str] game_account: Mapped[str]

View File

@@ -1,17 +1,16 @@
from hashlib import md5 from hashlib import md5
from urllib.parse import urlunparse from urllib.parse import urlunparse
from nonebot.adapters import Bot, Event
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 # type: ignore[import-untyped]
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_userinfo import BotUserInfo, UserInfo # type: ignore[import-untyped] from nonebot_plugin_userinfo import BotUserInfo, UserInfo # type: ignore[import-untyped]
from ...db import BindStatus, create_or_update_bind, trigger from ...db import BindStatus, create_or_update_bind, trigger
from ...utils.avatar import get_avatar from ...utils.avatar import get_avatar
from ...utils.host import HostPage, get_self_netloc from ...utils.host import HostPage, get_self_netloc
from ...utils.platform import get_platform
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
@@ -21,7 +20,7 @@ from .constant import GAME_TYPE
@alc.assign('bind') @alc.assign('bind')
async def _(bot: Bot, event: Event, 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
async with trigger( async with trigger(
session_persist_id=await get_session_persist_id(event_session), session_persist_id=await get_session_persist_id(event_session),
game_platform=GAME_TYPE, game_platform=GAME_TYPE,
@@ -32,8 +31,7 @@ async def _(bot: Bot, event: Event, account: Player, event_session: EventSession
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,
chat_platform=get_platform(bot), user=nb_user,
chat_account=event.get_user_id(),
game_platform=GAME_TYPE, game_platform=GAME_TYPE,
game_account=user.unique_identifier, game_account=user.unique_identifier,
) )
@@ -41,7 +39,7 @@ async def _(bot: Bot, event: Event, account: Player, event_session: EventSession
if bind_status in (BindStatus.SUCCESS, BindStatus.UPDATE): if bind_status in (BindStatus.SUCCESS, BindStatus.UPDATE):
async with HostPage( async with HostPage(
await render( await render(
'binding', 'v1/binding',
Bind( Bind(
platform='TETR.IO', platform='TETR.IO',
status='unknown', status='unknown',

View File

@@ -1,6 +1,6 @@
import contextlib
from asyncio import gather from asyncio import gather
from collections import defaultdict from collections import defaultdict
from contextlib import suppress
from datetime import date, datetime, timedelta, timezone from datetime import date, datetime, timedelta, timezone
from hashlib import md5 from hashlib import md5
from math import ceil, floor from math import ceil, floor
@@ -10,7 +10,7 @@ from zoneinfo import ZoneInfo
from aiofiles import open from aiofiles import open
from nonebot import get_driver from nonebot import get_driver
from nonebot.adapters import Bot, Event from nonebot.adapters import Event
from nonebot.compat import type_validate_json from nonebot.compat import type_validate_json
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot_plugin_alconna import At from nonebot_plugin_alconna import At
@@ -20,12 +20,12 @@ from nonebot_plugin_localstore import get_data_file # type: ignore[import-untyp
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 # type: ignore[import-untyped]
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 sqlalchemy import select from sqlalchemy import select
from zstandard import ZstdDecompressor from zstandard import ZstdDecompressor
from ...db import query_bind_info, trigger from ...db import query_bind_info, trigger
from ...utils.host import HostPage, get_self_netloc from ...utils.host import HostPage, get_self_netloc
from ...utils.platform import get_platform
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, Info, Radar, TetraLeague, TetraLeagueHistory from ...utils.render.schemas.tetrio_info import Data, Info, Radar, TetraLeague, TetraLeagueHistory
@@ -48,7 +48,7 @@ driver = get_driver()
@alc.assign('query') @alc.assign('query')
async def _(bot: Bot, event: Event, matcher: Matcher, target: At | Me, event_session: EventSession): async def _(event: Event, matcher: Matcher, target: At | Me, event_session: EventSession):
async with trigger( async with trigger(
session_persist_id=await get_session_persist_id(event_session), session_persist_id=await get_session_persist_id(event_session),
game_platform=GAME_TYPE, game_platform=GAME_TYPE,
@@ -58,8 +58,9 @@ async def _(bot: Bot, event: Event, matcher: Matcher, target: At | Me, event_ses
async with get_session() as session: async with get_session() as session:
bind = await query_bind_info( bind = await query_bind_info(
session=session, session=session,
chat_platform=get_platform(bot), user=await get_user(
chat_account=(target.target if isinstance(target, At) else event.get_user_id()), event_session.platform, target.target if isinstance(target, At) else event.get_user_id()
),
game_platform=GAME_TYPE, game_platform=GAME_TYPE,
) )
if bind is None: if bind is None:
@@ -69,8 +70,8 @@ async def _(bot: Bot, event: Event, matcher: Matcher, target: At | Me, event_ses
user, user_info, user_records = await gather(player.user, player.get_info(), player.get_records()) user, user_info, user_records = await gather(player.user, player.get_info(), player.get_records())
sprint = user_records.data.records.sprint sprint = user_records.data.records.sprint
blitz = user_records.data.records.blitz blitz = user_records.data.records.blitz
with contextlib.suppress(TypeError): with suppress(TypeError):
message += UniMessage.image(raw=await make_query_image(user, user_info, sprint.record, blitz.record)) message.image(raw=await make_query_image(user, user_info, sprint.record, blitz.record))
await message.finish() await message.finish()
message += make_query_text(user_info, sprint, blitz) message += make_query_text(user_info, sprint, blitz)
await message.finish() await message.finish()
@@ -87,7 +88,7 @@ async def _(account: Player, event_session: EventSession):
user, user_info, user_records = await gather(account.user, account.get_info(), account.get_records()) user, user_info, user_records = await gather(account.user, account.get_info(), account.get_records())
sprint = user_records.data.records.sprint sprint = user_records.data.records.sprint
blitz = user_records.data.records.blitz blitz = user_records.data.records.blitz
with contextlib.suppress(TypeError): with suppress(TypeError):
await UniMessage.image(raw=await make_query_image(user, user_info, sprint.record, blitz.record)).finish() await UniMessage.image(raw=await make_query_image(user, user_info, sprint.record, blitz.record)).finish()
await make_query_text(user_info, sprint, blitz).finish() await make_query_text(user_info, sprint, blitz).finish()
@@ -223,13 +224,13 @@ async def make_query_image(
split_value, offset = get_split(value_max, value_min) split_value, offset = get_split(value_max, value_min)
if sprint is not None: if sprint is not None:
duration = timedelta(milliseconds=sprint.endcontext.final_time).total_seconds() duration = timedelta(milliseconds=sprint.endcontext.final_time).total_seconds()
sprint_value = f'{duration:.1f}s' if duration < 60 else f'{duration // 60:.0f}m {duration % 60:.1f}s' # noqa: PLR2004 sprint_value = f'{duration:.3f}s' if duration < 60 else f'{duration // 60:.0f}m {duration % 60:.3f}s' # noqa: PLR2004
else: else:
sprint_value = 'N/A' sprint_value = 'N/A'
blitz_value = f'{blitz.endcontext.score:,}' if blitz is not None else 'N/A' blitz_value = f'{blitz.endcontext.score:,}' if blitz is not None else 'N/A'
async with HostPage( async with HostPage(
await render( await render(
'tetrio/info', 'v1/tetrio/info',
Info( Info(
user=TemplateUser( user=TemplateUser(
avatar=f'https://tetr.io/user-content/avatars/{user_info.data.user.id}.jpg?rv={user_info.data.user.avatar_revision}' avatar=f'https://tetr.io/user-content/avatars/{user_info.data.user.id}.jpg?rv={user_info.data.user.avatar_revision}'

View File

@@ -1,16 +1,15 @@
from urllib.parse import urlunparse from urllib.parse import urlunparse
from nonebot.adapters import Bot
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 # type: ignore[import-untyped]
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_userinfo import BotUserInfo, EventUserInfo, UserInfo # type: ignore[import-untyped] from nonebot_plugin_userinfo import BotUserInfo, EventUserInfo, UserInfo # type: ignore[import-untyped]
from ...db import BindStatus, create_or_update_bind, trigger from ...db import BindStatus, create_or_update_bind, trigger
from ...utils.avatar import get_avatar from ...utils.avatar import get_avatar
from ...utils.host import HostPage, get_self_netloc from ...utils.host import HostPage, get_self_netloc
from ...utils.platform import get_platform
from ...utils.render import Bind, render from ...utils.render import Bind, render
from ...utils.render.schemas.base import People from ...utils.render.schemas.base import People
from ...utils.screenshot import screenshot from ...utils.screenshot import screenshot
@@ -21,11 +20,11 @@ from .constant import GAME_TYPE
@alc.assign('bind') @alc.assign('bind')
async def _( async def _(
bot: Bot, nb_user: User,
account: Player, account: Player,
event_session: EventSession, event_session: EventSession,
bot_info: UserInfo = BotUserInfo(), # noqa: B008
event_user_info: UserInfo = EventUserInfo(), # noqa: B008 event_user_info: UserInfo = EventUserInfo(), # noqa: B008
bot_info: UserInfo = BotUserInfo(), # noqa: B008
): ):
async with trigger( async with trigger(
session_persist_id=await get_session_persist_id(event_session), session_persist_id=await get_session_persist_id(event_session),
@@ -37,15 +36,14 @@ async def _(
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,
chat_platform=get_platform(bot), user=nb_user,
chat_account=event_user_info.user_id,
game_platform=GAME_TYPE, game_platform=GAME_TYPE,
game_account=user.unique_identifier, game_account=user.unique_identifier,
) )
if bind_status in (BindStatus.SUCCESS, BindStatus.UPDATE): if bind_status in (BindStatus.SUCCESS, BindStatus.UPDATE):
async with HostPage( async with HostPage(
await render( await render(
'binding', 'v1/binding',
Bind( Bind(
platform=GAME_TYPE, platform=GAME_TYPE,
status='unknown', status='unknown',

View File

@@ -1,14 +1,14 @@
from nonebot.adapters import Bot, 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
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 # type: ignore[import-untyped]
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 ...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
from ...utils.platform import get_platform
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
@@ -18,7 +18,7 @@ from .constant import GAME_TYPE
@alc.assign('query') @alc.assign('query')
async def _(bot: Bot, event: Event, matcher: Matcher, target: At | Me, event_session: EventSession): async def _(event: Event, matcher: Matcher, target: At | Me, event_session: EventSession):
async with trigger( async with trigger(
session_persist_id=await get_session_persist_id(event_session), session_persist_id=await get_session_persist_id(event_session),
game_platform=GAME_TYPE, game_platform=GAME_TYPE,
@@ -28,8 +28,9 @@ async def _(bot: Bot, event: Event, matcher: Matcher, target: At | Me, event_ses
async with get_session() as session: async with get_session() as session:
bind = await query_bind_info( bind = await query_bind_info(
session=session, session=session,
chat_platform=get_platform(bot), user=await get_user(
chat_account=(target.target if isinstance(target, At) else event.get_user_id()), event_session.platform, target.target if isinstance(target, At) else event.get_user_id()
),
game_platform=GAME_TYPE, game_platform=GAME_TYPE,
) )
if bind is None: if bind is None:

View File

@@ -1,16 +1,15 @@
from urllib.parse import urlunparse from urllib.parse import urlunparse
from nonebot.adapters import Bot
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 # type: ignore[import-untyped]
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_userinfo import BotUserInfo, EventUserInfo, UserInfo # type: ignore[import-untyped] from nonebot_plugin_userinfo import BotUserInfo, EventUserInfo, UserInfo # type: ignore[import-untyped]
from ...db import BindStatus, create_or_update_bind, trigger from ...db import BindStatus, create_or_update_bind, trigger
from ...utils.avatar import get_avatar from ...utils.avatar import get_avatar
from ...utils.host import HostPage, get_self_netloc from ...utils.host import HostPage, get_self_netloc
from ...utils.platform import get_platform
from ...utils.render import Bind, render from ...utils.render import Bind, render
from ...utils.render.schemas.base import People from ...utils.render.schemas.base import People
from ...utils.screenshot import screenshot from ...utils.screenshot import screenshot
@@ -21,11 +20,11 @@ from .constant import GAME_TYPE
@alc.assign('bind') @alc.assign('bind')
async def _( async def _(
bot: Bot, nb_user: User,
account: Player, account: Player,
event_session: EventSession, event_session: EventSession,
bot_info: UserInfo = BotUserInfo(), # noqa: B008
event_user_info: UserInfo = EventUserInfo(), # noqa: B008 event_user_info: UserInfo = EventUserInfo(), # noqa: B008
bot_info: UserInfo = BotUserInfo(), # noqa: B008
): ):
async with trigger( async with trigger(
session_persist_id=await get_session_persist_id(event_session), session_persist_id=await get_session_persist_id(event_session),
@@ -37,8 +36,7 @@ async def _(
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,
chat_platform=get_platform(bot), user=nb_user,
chat_account=event_user_info.user_id,
game_platform=GAME_TYPE, game_platform=GAME_TYPE,
game_account=user.unique_identifier, game_account=user.unique_identifier,
) )
@@ -46,7 +44,7 @@ async def _(
if bind_status in (BindStatus.SUCCESS, BindStatus.UPDATE): if bind_status in (BindStatus.SUCCESS, BindStatus.UPDATE):
async with HostPage( async with HostPage(
await render( await render(
'binding', 'v1/binding',
Bind( Bind(
platform=GAME_TYPE, platform=GAME_TYPE,
status='unknown', status='unknown',

View File

@@ -4,13 +4,14 @@ from http import HTTPStatus
from typing import Literal, NamedTuple from typing import Literal, NamedTuple
from urllib.parse import urlunparse from urllib.parse import urlunparse
from nonebot.adapters import Bot, 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
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] # type: ignore[import-untyped] from nonebot_plugin_session import EventSession # type: ignore[import-untyped]
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_userinfo import EventUserInfo, UserInfo # type: ignore[import-untyped] from nonebot_plugin_userinfo import EventUserInfo, UserInfo # type: ignore[import-untyped]
from ...db import query_bind_info, trigger from ...db import query_bind_info, trigger
@@ -18,7 +19,6 @@ from ...utils.avatar import get_avatar
from ...utils.exception import RequestError from ...utils.exception import RequestError
from ...utils.host import HostPage, get_self_netloc from ...utils.host import HostPage, get_self_netloc
from ...utils.metrics import TetrisMetricsProWithLPMADPM, get_metrics from ...utils.metrics import TetrisMetricsProWithLPMADPM, get_metrics
from ...utils.platform import get_platform
from ...utils.render import render from ...utils.render import render
from ...utils.render.schemas.base import People, Ranking from ...utils.render.schemas.base import People, Ranking
from ...utils.render.schemas.tos_info import Info, Multiplayer, Radar from ...utils.render.schemas.tos_info import Info, Multiplayer, Radar
@@ -60,7 +60,7 @@ def add_special_handlers(
await UniMessage.image( await UniMessage.image(
raw=await make_query_image(user_info, game_data, event_user_info) raw=await make_query_image(user_info, game_data, event_user_info)
).finish() ).finish()
await (await make_query_text(user_info, game_data)).finish() await make_query_text(user_info, game_data).finish()
except RequestError as e: except RequestError as e:
if e.status_code == HTTPStatus.BAD_REQUEST and '未找到此用户' in e.message: if e.status_code == HTTPStatus.BAD_REQUEST and '未找到此用户' in e.message:
return return
@@ -99,8 +99,7 @@ except ImportError:
@alc.assign('query') @alc.assign('query')
async def _( # noqa: PLR0913 async def _(
bot: Bot,
event: Event, event: Event,
matcher: Matcher, matcher: Matcher,
target: At | Me, target: At | Me,
@@ -116,8 +115,9 @@ async def _( # noqa: PLR0913
async with get_session() as session: async with get_session() as session:
bind = await query_bind_info( bind = await query_bind_info(
session=session, session=session,
chat_platform=get_platform(bot), user=await get_user(
chat_account=(target.target if isinstance(target, At) else event.get_user_id()), event_session.platform, target.target if isinstance(target, At) else event.get_user_id()
),
game_platform=GAME_TYPE, game_platform=GAME_TYPE,
) )
if bind is None: if bind is None:
@@ -129,7 +129,7 @@ async def _( # noqa: PLR0913
await ( await (
message + UniMessage.image(raw=await make_query_image(user_info, game_data, event_user_info)) message + UniMessage.image(raw=await make_query_image(user_info, game_data, event_user_info))
).finish() ).finish()
await (message + await make_query_text(user_info, game_data)).finish() await (message + make_query_text(user_info, game_data)).finish()
@alc.assign('query') @alc.assign('query')
@@ -143,7 +143,7 @@ async def _(account: Player, event_session: EventSession, event_user_info: UserI
user_info, game_data = await gather(account.get_info(), get_game_data(account)) user_info, game_data = await gather(account.get_info(), get_game_data(account))
if game_data is not None: if game_data is not None:
await UniMessage.image(raw=await make_query_image(user_info, game_data, event_user_info)).finish() await UniMessage.image(raw=await make_query_image(user_info, game_data, event_user_info)).finish()
await (await make_query_text(user_info, game_data)).finish() await make_query_text(user_info, game_data).finish()
class GameData(NamedTuple): class GameData(NamedTuple):
@@ -201,10 +201,10 @@ async def get_game_data(player: Player, query_num: int = 50) -> GameData | None:
async def make_query_image(user_info: UserInfoSuccess, game_data: GameData, event_user_info: UserInfo) -> bytes: async def make_query_image(user_info: UserInfoSuccess, game_data: GameData, event_user_info: UserInfo) -> bytes:
metrics = game_data.metrics metrics = game_data.metrics
duration = timedelta(milliseconds=float(user_info.data.pb_sprint)).total_seconds() duration = timedelta(milliseconds=float(user_info.data.pb_sprint)).total_seconds()
sprint_value = f'{duration:.1f}s' if duration < 60 else f'{duration // 60:.0f}m {duration % 60:.1f}s' # noqa: PLR2004 sprint_value = f'{duration:.3f}s' if duration < 60 else f'{duration // 60:.0f}m {duration % 60:.3f}s' # noqa: PLR2004
async with HostPage( async with HostPage(
await render( await render(
'tos/info', 'v1/tos/info',
Info( Info(
user=People(avatar=await get_avatar(event_user_info, 'Data URI', None), name=user_info.data.name), user=People(avatar=await get_avatar(event_user_info, 'Data URI', None), name=user_info.data.name),
ranking=Ranking(rating=float(user_info.data.ranking), rd=round(float(user_info.data.rd_now), 2)), ranking=Ranking(rating=float(user_info.data.ranking), rd=round(float(user_info.data.rd_now), 2)),
@@ -233,7 +233,7 @@ async def make_query_image(user_info: UserInfoSuccess, game_data: GameData, even
return await screenshot(urlunparse(('http', get_self_netloc(), f'/host/{page_hash}.html', '', '', ''))) return await screenshot(urlunparse(('http', get_self_netloc(), f'/host/{page_hash}.html', '', '', '')))
async def make_query_text(user_info: UserInfoSuccess, game_data: GameData | None) -> UniMessage: def make_query_text(user_info: UserInfoSuccess, game_data: GameData | None) -> UniMessage:
user_data = user_info.data user_data = user_info.data
message = f'用户 {user_data.name} ({user_data.teaid}) ' message = f'用户 {user_data.name} ({user_data.teaid}) '
if user_data.ranked_games == '0': if user_data.ranked_games == '0':

View File

@@ -6,6 +6,7 @@ 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_info import Info as TETRIOInfo
from .schemas.tetrio_info_v2 import Info as TETRIOInfoV2
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
@@ -15,23 +16,28 @@ env = Environment(
@overload @overload
async def render(render_type: Literal['binding'], data: Bind) -> str: ... async def render(render_type: Literal['v1/binding'], data: Bind) -> str: ...
@overload @overload
async def render(render_type: Literal['tetrio/info'], data: TETRIOInfo) -> str: ... async def render(render_type: Literal['v1/tetrio/info'], data: TETRIOInfo) -> str: ...
@overload @overload
async def render(render_type: Literal['top/info'], data: TOPInfo) -> str: ... async def render(render_type: Literal['v2/tetrio/info'], data: TETRIOInfoV2) -> str: ...
@overload @overload
async def render(render_type: Literal['tos/info'], data: TOSInfo) -> str: ... async def render(render_type: Literal['v1/top/info'], data: TOPInfo) -> str: ...
@overload
async def render(render_type: Literal['v1/tos/info'], data: TOSInfo) -> str: ...
async def render( async def render(
render_type: Literal['binding', 'tetrio/info', 'top/info', 'tos/info'], data: Bind | TETRIOInfo | TOPInfo | TOSInfo render_type: Literal['v1/binding', 'v1/tetrio/info', 'v2/tetrio/info', 'v1/top/info', 'v1/tos/info'],
data: Bind | TETRIOInfo | TETRIOInfoV2 | TOPInfo | TOSInfo,
) -> 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(

View File

@@ -0,0 +1,84 @@
from datetime import datetime
from pydantic import BaseModel
from ....games.tetrio.api.typing import Rank
from ...typing import Number
from .base import Avatar
class User(BaseModel):
id: str
name: str
country: str | None
avatar: str | Avatar
banner: str | None
bio: str | None
friend_count: int
supporter_tier: int
verified: bool
bad_standing: bool
badges: list[str]
xp: Number
playtime: str
join_at: datetime | None
class Statistic(BaseModel):
total: int
wins: int
class TetraLeague(BaseModel):
rank: Rank
highest_rank: Rank
tr: Number
glicko: Number
rd: Number
global_rank: int
country_rank: int
pps: Number
apm: Number
adpm: Number
vs: Number
adpl: Number
statistic: Statistic
class Sprint(BaseModel):
time: str
global_rank: int | None
play_at: datetime
class Blitz(BaseModel):
score: int
global_rank: int | None
play_at: datetime
class Zen(BaseModel):
score: int
level: int
class Info(BaseModel):
user: User
tetra_league: TetraLeague | None
statistic: Statistic | None
sprint: Sprint | None
blitz: Blitz | None
zen: Zen

View File

@@ -1,38 +1,44 @@
from asyncio import sleep from asyncio import sleep
from collections.abc import Awaitable, Callable from collections.abc import Callable, Coroutine
from contextlib import suppress
from datetime import timedelta from datetime import timedelta
from functools import wraps from functools import wraps
from typing import TypeVar, cast from typing import Any, ParamSpec, TypeVar
from nonebot.log import logger from nonebot.log import logger
from nonebot_plugin_alconna.uniseg import SerializeFailed, UniMessage
T = TypeVar('T') T = TypeVar('T')
P = ParamSpec('P')
def retry( def retry(
max_attempts: int = 3, max_attempts: int = 3,
exception_type: type[BaseException] | tuple[type[BaseException], ...] = Exception, exception_type: type[BaseException] | tuple[type[BaseException], ...] = Exception,
delay: timedelta | None = None, delay: timedelta | None = None,
) -> Callable[[Callable[..., Awaitable[T]]], Callable[..., Awaitable[T]]]: reply: str | UniMessage | None = None,
def decorator(func: Callable[..., Awaitable[T]]) -> Callable[..., Awaitable[T]]: ) -> Callable[[Callable[P, Coroutine[Any, Any, T]]], Callable[P, Coroutine[Any, Any, T]]]:
def decorator(func: Callable[P, Coroutine[Any, Any, T]]) -> Callable[P, Coroutine[Any, Any, T]]:
@wraps(func) @wraps(func)
async def wrapper(*args, **kwargs) -> T: # noqa: ANN002, ANN003 async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
attempts = 0 for i in range(max_attempts + 1):
while attempts < max_attempts + 1: if i > 0:
message = f'Retrying: {func.__name__} ({i}/{max_attempts})'
logger.debug(message)
with suppress(SerializeFailed):
await UniMessage(reply or message).send()
if i == max_attempts:
break
try: try:
return await func(*args, **kwargs) return await func(*args, **kwargs)
except exception_type as e: # noqa: PERF203 except exception_type as e:
if i == max_attempts:
raise
logger.exception(e) logger.exception(e)
attempts += 1 if delay is not None:
if attempts <= max_attempts: await sleep(delay.total_seconds())
if delay is not None: return await func(*args, **kwargs)
await sleep(delay.total_seconds())
logger.debug(f'Retrying: {func.__name__} ({attempts}/{max_attempts})')
continue
raise
msg = 'Unexpectedly reached the end of the retry loop'
raise RuntimeError(msg)
return cast(Callable[..., Awaitable[T]], wrapper) return wrapper
return decorator return decorator

View File

@@ -1,11 +1,15 @@
from playwright.async_api import TimeoutError
from .browser import BrowserManager from .browser import BrowserManager
from .retry import retry
@retry(exception_type=TimeoutError, reply='截图失败, 重试中')
async def screenshot(url: str) -> bytes: async def screenshot(url: str) -> bytes:
browser = await BrowserManager.get_browser() browser = await BrowserManager.get_browser()
async with ( async with (
await browser.new_page(no_viewport=True, viewport={'width': 0, 'height': 0}) as page, await browser.new_page(viewport={'width': 3000, 'height': 3000}) as page,
): ):
await page.goto(url) await page.goto(url)
await page.wait_for_load_state('networkidle') await page.wait_for_load_state('networkidle', timeout=5000)
return await page.screenshot(full_page=True, type='png') return await page.locator('id=content').screenshot(timeout=5000, type='png')

124
poetry.lock generated
View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]] [[package]]
name = "aiocache" name = "aiocache"
@@ -254,13 +254,13 @@ zookeeper = ["kazoo"]
[[package]] [[package]]
name = "arclet-alconna" name = "arclet-alconna"
version = "1.8.13" version = "1.8.14"
description = "A High-performance, Generality, Humane Command Line Arguments Parser Library." description = "A High-performance, Generality, Humane Command Line Arguments Parser Library."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "arclet_alconna-1.8.13-py3-none-any.whl", hash = "sha256:7a85d51aa588c40e525f5df3a2e23db757cd051e06dd266401ddb6346cc857db"}, {file = "arclet_alconna-1.8.14-py3-none-any.whl", hash = "sha256:e74ec1d0ca6829592a15768f48c6af9e5d9bb75a4476a763550d71594f7019d7"},
{file = "arclet_alconna-1.8.13.tar.gz", hash = "sha256:19aba65958578b3287af9afcf051da903104553460c6198d3f9398de4b8371f7"}, {file = "arclet_alconna-1.8.14.tar.gz", hash = "sha256:77b4b15b1c6b9873554b0990310f587016d9982b086680150febc21fa17390d6"},
] ]
[package.dependencies] [package.dependencies]
@@ -643,6 +643,20 @@ files = [
[package.extras] [package.extras]
test = ["pytest (>=6)"] test = ["pytest (>=6)"]
[[package]]
name = "expiringdict"
version = "1.2.2"
description = "Dictionary with auto-expiring values for caching purposes"
optional = false
python-versions = "*"
files = [
{file = "expiringdict-1.2.2-py3-none-any.whl", hash = "sha256:09a5d20bc361163e6432a874edd3179676e935eb81b925eccef48d409a8a45e8"},
{file = "expiringdict-1.2.2.tar.gz", hash = "sha256:300fb92a7e98f15b05cf9a856c1415b3bc4f2e132be07daa326da6414c23ee09"},
]
[package.extras]
tests = ["coverage", "coveralls", "dill", "mock", "nose"]
[[package]] [[package]]
name = "fastapi" name = "fastapi"
version = "0.111.0" version = "0.111.0"
@@ -1204,13 +1218,9 @@ files = [
{file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"}, {file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"},
{file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"}, {file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"},
{file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"},
{file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b590b39ef90c6b22ec0be925b211298e810b4856909c8ca60d27ffbca6c12e6"},
{file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"},
{file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:c2faf60c583af0d135e853c86ac2735ce178f0e338a3c7f9ae8f622fd2eb788c"},
{file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"},
{file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7ff762670cada8e05b32bf1e4dc50b140790909caa8303cfddc4d702b71ea184"},
{file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"},
{file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:a6d2092797b388342c1bc932077ad232f914351932353e2e8706851c870bca1f"},
{file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"},
{file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"}, {file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"},
{file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"}, {file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"},
@@ -1734,20 +1744,21 @@ nonebot2 = ">=2.2.0"
[[package]] [[package]]
name = "nonebot-plugin-alconna" name = "nonebot-plugin-alconna"
version = "0.46.1" version = "0.46.4"
description = "Alconna Adapter for Nonebot" description = "Alconna Adapter for Nonebot"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
files = [ files = [
{file = "nonebot_plugin_alconna-0.46.1-py3-none-any.whl", hash = "sha256:4737e2036929f012181c832823a9d6d8e9992b939a0116ab5a81b49763565292"}, {file = "nonebot_plugin_alconna-0.46.4-py3-none-any.whl", hash = "sha256:332aa98056f02b63aa194536cfac42387496d8ece29f354d5499418ca33fe83d"},
{file = "nonebot_plugin_alconna-0.46.1.tar.gz", hash = "sha256:e5e7b8b61cb472e51546c72a29a254a5152b55ddbf1e1b8a249382b9d2db13a1"}, {file = "nonebot_plugin_alconna-0.46.4.tar.gz", hash = "sha256:60f6bf93d82117cca52948cfaa5f4563ee8ed6c942d1993634d88a14ada8c27f"},
] ]
[package.dependencies] [package.dependencies]
arclet-alconna = ">=1.8.12" arclet-alconna = ">=1.8.14"
arclet-alconna-tools = ">=0.7.4" arclet-alconna-tools = ">=0.7.4"
importlib-metadata = ">=4.13.0" importlib-metadata = ">=4.13.0"
nepattern = ">=0.7.4" nepattern = ">=0.7.4"
nonebot-plugin-waiter = ">=0.6.0"
nonebot2 = ">=2.3.0" nonebot2 = ">=2.3.0"
[[package]] [[package]]
@@ -1784,13 +1795,13 @@ typing-extensions = ">=4.0.0"
[[package]] [[package]]
name = "nonebot-plugin-orm" name = "nonebot-plugin-orm"
version = "0.7.2" version = "0.7.3"
description = "SQLAlchemy ORM support for nonebot" description = "SQLAlchemy ORM support for nonebot"
optional = false optional = false
python-versions = "<4.0,>=3.8" python-versions = "<4.0,>=3.8"
files = [ files = [
{file = "nonebot_plugin_orm-0.7.2-py3-none-any.whl", hash = "sha256:a9fce44d7d80a98b7e07125abe26529995fde967e1f9dd390180d70734f9c596"}, {file = "nonebot_plugin_orm-0.7.3-py3-none-any.whl", hash = "sha256:9b0d114a4e7b2e452cb333e7147ae4216dff2f9685df616e5fa1f3f892d8795b"},
{file = "nonebot_plugin_orm-0.7.2.tar.gz", hash = "sha256:12f12e8b1be5d5f75bba0a630e5112ad4041b0ef67469dbcd7ac6557faec0953"}, {file = "nonebot_plugin_orm-0.7.3.tar.gz", hash = "sha256:cbb573598d0ecef2d0e75b5bdebd05297c68a2b368029a8763660f2a45381a2c"},
] ]
[package.dependencies] [package.dependencies]
@@ -1847,6 +1858,24 @@ nonebot-plugin-orm = ">=0.7.0,<1.0.0"
nonebot-plugin-session = ">=0.3.0,<0.4.0" nonebot-plugin-session = ">=0.3.0,<0.4.0"
nonebot2 = {version = ">=2.2.0,<3.0.0", extras = ["fastapi"]} nonebot2 = {version = ">=2.2.0,<3.0.0", extras = ["fastapi"]}
[[package]]
name = "nonebot-plugin-user"
version = "0.2.0"
description = "适用于 Nonebot2 的用户插件"
optional = false
python-versions = ">=3.8,<4.0"
files = [
{file = "nonebot_plugin_user-0.2.0-py3-none-any.whl", hash = "sha256:9b052551b13fd8f8fab39023a8088637b0447b0ef42d87f54a0e7e2c3c371740"},
{file = "nonebot_plugin_user-0.2.0.tar.gz", hash = "sha256:f2f559e3381deb20a067fb2004a92f3625b1777da076ecfb956e334bdbe0f7d5"},
]
[package.dependencies]
expiringdict = ">=1.2.2,<2.0.0"
nonebot-plugin-alconna = ">=0.37.1"
nonebot-plugin-orm = ">=0.7.0"
nonebot-plugin-session = ">=0.3.0,<0.4.0"
nonebot2 = ">=2.2.0,<3.0.0"
[[package]] [[package]]
name = "nonebot-plugin-userinfo" name = "nonebot-plugin-userinfo"
version = "0.2.4" version = "0.2.4"
@@ -1865,6 +1894,20 @@ httpx = ">=0.20.0,<1.0.0"
nonebot2 = ">=2.0.0,<3.0.0" nonebot2 = ">=2.0.0,<3.0.0"
strenum = ">=0.4.8,<0.5.0" strenum = ">=0.4.8,<0.5.0"
[[package]]
name = "nonebot-plugin-waiter"
version = "0.6.1"
description = "An alternative for got-and-reject in Nonebot"
optional = false
python-versions = ">=3.9"
files = [
{file = "nonebot_plugin_waiter-0.6.1-py3-none-any.whl", hash = "sha256:0abd678ff7671a2f5cee53b8c96fdbb700a78bbff921e8f37ab1e9388bdc8649"},
{file = "nonebot_plugin_waiter-0.6.1.tar.gz", hash = "sha256:ba4da15694ef4eeb23329e7ecde7e9e56df5cbf8e0dc03b8f98747658bbc138f"},
]
[package.dependencies]
nonebot2 = ">=2.3.0"
[[package]] [[package]]
name = "nonebot2" name = "nonebot2"
version = "2.3.1" version = "2.3.1"
@@ -2018,7 +2061,6 @@ optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
files = [ files = [
{file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"},
{file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"},
{file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"},
{file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"},
{file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"},
@@ -2039,7 +2081,6 @@ files = [
{file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"},
{file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"},
{file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"},
{file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"},
{file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"},
{file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"},
{file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"},
@@ -2085,17 +2126,20 @@ xml = ["lxml (>=4.9.2)"]
[[package]] [[package]]
name = "pandas-stubs" name = "pandas-stubs"
version = "2.2.2.240514" version = "2.2.2.240603"
description = "Type annotations for pandas" description = "Type annotations for pandas"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
files = [ files = [
{file = "pandas_stubs-2.2.2.240514-py3-none-any.whl", hash = "sha256:5d6f64d45a98bc94152a0f76fa648e598cd2b9ba72302fd34602479f0c391a53"}, {file = "pandas_stubs-2.2.2.240603-py3-none-any.whl", hash = "sha256:e08ce7f602a4da2bff5a67475ba881c39f2a4d4f7fccc1cba57c6f35a379c6c0"},
{file = "pandas_stubs-2.2.2.240514.tar.gz", hash = "sha256:85b20da44a62c80eb8389bcf4cbfe31cce1cafa8cca4bf1fc75ec45892e72ce8"}, {file = "pandas_stubs-2.2.2.240603.tar.gz", hash = "sha256:2dcc86e8fa6ea41535a4561c1f08b3942ba5267b464eff2e99caeee66f9e4cd1"},
] ]
[package.dependencies] [package.dependencies]
numpy = {version = ">=1.26.0", markers = "python_version < \"3.13\""} numpy = [
{version = ">=1.23.5", markers = "python_version >= \"3.9\" and python_version < \"3.12\""},
{version = ">=1.26.0", markers = "python_version >= \"3.12\" and python_version < \"3.13\""},
]
types-pytz = ">=2022.1.1" types-pytz = ">=2022.1.1"
[[package]] [[package]]
@@ -2631,28 +2675,28 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.4.4" version = "0.4.8"
description = "An extremely fast Python linter and code formatter, written in Rust." description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6"}, {file = "ruff-0.4.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7663a6d78f6adb0eab270fa9cf1ff2d28618ca3a652b60f2a234d92b9ec89066"},
{file = "ruff-0.4.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891"}, {file = "ruff-0.4.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eeceb78da8afb6de0ddada93112869852d04f1cd0f6b80fe464fd4e35c330913"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15"}, {file = "ruff-0.4.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aad360893e92486662ef3be0a339c5ca3c1b109e0134fcd37d534d4be9fb8de3"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab"}, {file = "ruff-0.4.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:284c2e3f3396fb05f5f803c9fffb53ebbe09a3ebe7dda2929ed8d73ded736deb"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85"}, {file = "ruff-0.4.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7354f921e3fbe04d2a62d46707e569f9315e1a613307f7311a935743c51a764"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef"}, {file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:72584676164e15a68a15778fd1b17c28a519e7a0622161eb2debdcdabdc71883"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd"}, {file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9678d5c9b43315f323af2233a04d747409d1e3aa6789620083a82d1066a35199"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9"}, {file = "ruff-0.4.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704977a658131651a22b5ebeb28b717ef42ac6ee3b11e91dc87b633b5d83142b"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36"}, {file = "ruff-0.4.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05f8d6f0c3cce5026cecd83b7a143dcad503045857bc49662f736437380ad45"},
{file = "ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595"}, {file = "ruff-0.4.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ea874950daca5697309d976c9afba830d3bf0ed66887481d6bca1673fc5b66a"},
{file = "ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768"}, {file = "ruff-0.4.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fc95aac2943ddf360376be9aa3107c8cf9640083940a8c5bd824be692d2216dc"},
{file = "ruff-0.4.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e"}, {file = "ruff-0.4.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:384154a1c3f4bf537bac69f33720957ee49ac8d484bfc91720cc94172026ceed"},
{file = "ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95"}, {file = "ruff-0.4.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e9d5ce97cacc99878aa0d084c626a15cd21e6b3d53fd6f9112b7fc485918e1fa"},
{file = "ruff-0.4.4-py3-none-win32.whl", hash = "sha256:cb53473849f011bca6e754f2cdf47cafc9c4f4ff4570003a0dad0b9b6890e876"}, {file = "ruff-0.4.8-py3-none-win32.whl", hash = "sha256:6d795d7639212c2dfd01991259460101c22aabf420d9b943f153ab9d9706e6a9"},
{file = "ruff-0.4.4-py3-none-win_amd64.whl", hash = "sha256:424e5b72597482543b684c11def82669cc6b395aa8cc69acc1858b5ef3e5daae"}, {file = "ruff-0.4.8-py3-none-win_amd64.whl", hash = "sha256:e14a3a095d07560a9d6769a72f781d73259655919d9b396c650fc98a8157555d"},
{file = "ruff-0.4.4-py3-none-win_arm64.whl", hash = "sha256:39df0537b47d3b597293edbb95baf54ff5b49589eb7ff41926d8243caa995ea6"}, {file = "ruff-0.4.8-py3-none-win_arm64.whl", hash = "sha256:14019a06dbe29b608f6b7cbcec300e3170a8d86efaddb7b23405cb7f7dcaf780"},
{file = "ruff-0.4.4.tar.gz", hash = "sha256:f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af"}, {file = "ruff-0.4.8.tar.gz", hash = "sha256:16d717b1d57b2e2fd68bd0bf80fb43931b79d05a7131aa477d66fc40fbd86268"},
] ]
[[package]] [[package]]
@@ -3638,4 +3682,4 @@ cffi = ["cffi (>=1.11)"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "f073f9fbc693de09d1db3f326451e3fa1b1233af5b04965d658cae297886cdd9" content-hash = "7eeac60a63bedc7ebff1466657b02fca9e6c4dcc6b525feca23c5bd664f92bdd"

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = 'nonebot-plugin-tetris-stats' name = 'nonebot-plugin-tetris-stats'
version = '1.2.10' version = '1.2.11'
description = '一款基于 NoneBot2 的用于查询 Tetris 相关游戏数据的插件' description = '一款基于 NoneBot2 的用于查询 Tetris 相关游戏数据的插件'
authors = ['scdhh <wallfjjd@gmail.com>'] authors = ['scdhh <wallfjjd@gmail.com>']
readme = 'README.md' readme = 'README.md'
@@ -17,6 +17,7 @@ nonebot-plugin-localstore = "^0.6.0"
nonebot-plugin-orm = ">=0.1.1,<0.8.0" nonebot-plugin-orm = ">=0.1.1,<0.8.0"
nonebot-plugin-session = "^0.3.1" nonebot-plugin-session = "^0.3.1"
nonebot-plugin-session-orm = "^0.2.0" nonebot-plugin-session-orm = "^0.2.0"
nonebot-plugin-user = "^0.2.0"
nonebot-plugin-userinfo = "^0.2.4" nonebot-plugin-userinfo = "^0.2.4"
aiocache = "^0.12.2" aiocache = "^0.12.2"
aiofiles = "^23.2.1" aiofiles = "^23.2.1"
@@ -128,3 +129,4 @@ quote-style = 'single'
[tool.nonebot] [tool.nonebot]
plugins = ['nonebot_plugin_tetris_stats'] plugins = ['nonebot_plugin_tetris_stats']
# plugins = ['test_user']