From b3677e61be392f35f965bcd9a83393256fc08889 Mon Sep 17 00:00:00 2001 From: shoucandanghehe Date: Sun, 24 Nov 2024 16:42:04 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9E=95=20=E6=B7=BB=E5=8A=A0=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=20nonebot-plugin-waiter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 1 + uv.lock | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d4b4a86..ab38d8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ dependencies = [ "nonebot-plugin-session-orm>=0.2.0", "nonebot-plugin-user>=0.4.4", "nonebot-plugin-userinfo>=0.2.6", + "nonebot-plugin-waiter>=0.8.0", "nonebot2[fastapi]>=2.3.3", "pandas>=2.2.3", "pillow>=11.0.0", diff --git a/uv.lock b/uv.lock index 329757e..a21ad5a 100644 --- a/uv.lock +++ b/uv.lock @@ -1818,7 +1818,7 @@ wheels = [ [[package]] name = "nonebot-plugin-tetris-stats" -version = "1.6.2" +version = "1.6.3" source = { editable = "." } dependencies = [ { name = "aiocache" }, @@ -1837,6 +1837,7 @@ dependencies = [ { name = "nonebot-plugin-session-orm" }, { name = "nonebot-plugin-user" }, { name = "nonebot-plugin-userinfo" }, + { name = "nonebot-plugin-waiter" }, { name = "nonebot2", extra = ["fastapi"] }, { name = "pandas" }, { name = "pillow" }, @@ -1845,7 +1846,7 @@ dependencies = [ { name = "yarl" }, ] -[package.dependency-groups] +[package.dev-dependencies] debug = [ { name = "matplotlib" }, { name = "memory-profiler" }, @@ -1898,6 +1899,7 @@ requires-dist = [ { name = "nonebot-plugin-session-orm", specifier = ">=0.2.0" }, { name = "nonebot-plugin-user", specifier = ">=0.4.4" }, { name = "nonebot-plugin-userinfo", specifier = ">=0.2.6" }, + { name = "nonebot-plugin-waiter", specifier = ">=0.8.0" }, { name = "nonebot2", extras = ["fastapi"], specifier = ">=2.3.3" }, { name = "pandas", specifier = ">=2.2.3" }, { name = "pillow", specifier = ">=11.0.0" }, @@ -1906,7 +1908,7 @@ requires-dist = [ { name = "yarl", specifier = ">=1.16.0" }, ] -[package.metadata.dependency-groups] +[package.metadata.requires-dev] debug = [ { name = "matplotlib", specifier = ">=3.9.2" }, { name = "memory-profiler", specifier = ">=0.61.0" }, From 793c38bfb811085074f85701d1ddf64c8436962b Mon Sep 17 00:00:00 2001 From: shoucandanghehe Date: Thu, 19 Dec 2024 03:52:05 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9C=A8=20=E6=B7=BB=E5=8A=A0=20unbind=20?= =?UTF-8?q?=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_tetris_stats/__init__.py | 1 + nonebot_plugin_tetris_stats/db/__init__.py | 17 +++++ .../games/tetrio/__init__.py | 3 +- .../games/tetrio/unbind.py | 75 +++++++++++++++++++ .../games/top/__init__.py | 23 +++++- .../games/top/unbind.py | 63 ++++++++++++++++ .../games/tos/__init__.py | 23 +++++- .../games/tos/unbind.py | 66 ++++++++++++++++ nonebot_plugin_tetris_stats/utils/typing.py | 2 +- 9 files changed, 265 insertions(+), 8 deletions(-) create mode 100644 nonebot_plugin_tetris_stats/games/tetrio/unbind.py create mode 100644 nonebot_plugin_tetris_stats/games/top/unbind.py create mode 100644 nonebot_plugin_tetris_stats/games/tos/unbind.py diff --git a/nonebot_plugin_tetris_stats/__init__.py b/nonebot_plugin_tetris_stats/__init__.py index 30a652d..2dc5c79 100644 --- a/nonebot_plugin_tetris_stats/__init__.py +++ b/nonebot_plugin_tetris_stats/__init__.py @@ -10,6 +10,7 @@ 'nonebot_plugin_session', 'nonebot_plugin_user', 'nonebot_plugin_userinfo', + 'nonebot_plugin_waiter', } for i in require_plugins: diff --git a/nonebot_plugin_tetris_stats/db/__init__.py b/nonebot_plugin_tetris_stats/db/__init__.py index c4ee595..17a4ea7 100644 --- a/nonebot_plugin_tetris_stats/db/__init__.py +++ b/nonebot_plugin_tetris_stats/db/__init__.py @@ -63,6 +63,23 @@ async def create_or_update_bind( return status +async def remove_bind( + session: AsyncSession, + user: User, + game_platform: GameType, +) -> bool: + bind = await query_bind_info( + session=session, + user=user, + game_platform=game_platform, + ) + if bind is not None: + await session.delete(bind) + await session.commit() + return True + return False + + T = TypeVar('T', 'TETRIOHistoricalData', 'TOPHistoricalData', 'TOSHistoricalData') lock = Lock() diff --git a/nonebot_plugin_tetris_stats/games/tetrio/__init__.py b/nonebot_plugin_tetris_stats/games/tetrio/__init__.py index 079b81e..e5de361 100644 --- a/nonebot_plugin_tetris_stats/games/tetrio/__init__.py +++ b/nonebot_plugin_tetris_stats/games/tetrio/__init__.py @@ -23,7 +23,7 @@ def get_player(user_id_or_name: str) -> Player | MessageFormatError: ) -from . import bind, config, list, query, rank, record # noqa: E402 +from . import bind, config, list, query, rank, record, unbind # noqa: E402 main_command.add(command) @@ -35,4 +35,5 @@ def get_player(user_id_or_name: str) -> Player | MessageFormatError: 'query', 'rank', 'record', + 'unbind', ] diff --git a/nonebot_plugin_tetris_stats/games/tetrio/unbind.py b/nonebot_plugin_tetris_stats/games/tetrio/unbind.py new file mode 100644 index 0000000..c03cdb0 --- /dev/null +++ b/nonebot_plugin_tetris_stats/games/tetrio/unbind.py @@ -0,0 +1,75 @@ +from hashlib import md5 + +from nonebot_plugin_alconna import 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 +from nonebot_plugin_userinfo import BotUserInfo, UserInfo +from nonebot_plugin_waiter import suggest # type: ignore[import-untyped] +from yarl import URL + +from ...db import query_bind_info, remove_bind, trigger +from ...utils.host import HostPage, get_self_netloc +from ...utils.image import get_avatar +from ...utils.render import Bind, render +from ...utils.render.schemas.base import Avatar, People +from ...utils.screenshot import screenshot +from . import alc, command +from .api import Player +from .constant import GAME_TYPE + +command.add(Subcommand('unbind', help_text='解除绑定 TETR.IO 账号')) + +alc.shortcut( + '(?i:io)(?i:解除绑定|解绑|unbind)', + command='tstats TETR.IO unbind', + humanized='io解绑', +) + + +@alc.assign('TETRIO.unbind') +async def _(nb_user: User, event_session: EventSession, bot_info: UserInfo = BotUserInfo()): # noqa: B008 + async with ( + trigger( + session_persist_id=await get_session_persist_id(event_session), + game_platform=GAME_TYPE, + command_type='unbind', + command_args=[], + ), + get_session() as session, + ): + if (bind := await query_bind_info(session=session, user=nb_user, game_platform=GAME_TYPE)) is None: + await UniMessage('您还未绑定 TETR.IO 账号').finish() + resp = await suggest('您确定要解绑吗?', ['是', '否']) + if resp is None or resp.extract_plain_text() == '否': + return + player = Player(user_id=bind.game_account, trust=True) + user = await player.user + netloc = get_self_netloc() + async with HostPage( + await render( + 'v1/binding', + Bind( + platform='TETR.IO', + status='unlink', + user=People( + avatar=str( + URL(f'http://{netloc}/host/resource/tetrio/avatars/{user.ID}') + % {'revision': avatar_revision} + ) + if (avatar_revision := (await player.avatar_revision)) is not None and avatar_revision != 0 + else Avatar(type='identicon', hash=md5(user.ID.encode()).hexdigest()), # noqa: S324 + name=user.name.upper(), + ), + bot=People( + avatar=await get_avatar(bot_info, 'Data URI', '../../static/logo/logo.svg'), + name=bot_info.user_name, + ), + command='io绑定{游戏ID}', + ), + ) + ) as page_hash: + await UniMessage.image(raw=await screenshot(f'http://{netloc}/host/{page_hash}.html')).send() + await remove_bind(session=session, user=nb_user, game_platform=GAME_TYPE) diff --git a/nonebot_plugin_tetris_stats/games/top/__init__.py b/nonebot_plugin_tetris_stats/games/top/__init__.py index 806611e..8f84fe1 100644 --- a/nonebot_plugin_tetris_stats/games/top/__init__.py +++ b/nonebot_plugin_tetris_stats/games/top/__init__.py @@ -29,6 +29,10 @@ def get_player(name: str) -> Player | MessageFormatError: ), help_text='绑定 TOP 账号', ), + Subcommand( + 'unbind', + help_text='解除绑定 TOP 账号', + ), Subcommand( 'query', Args( @@ -51,9 +55,22 @@ def get_player(name: str) -> Player | MessageFormatError: ) ) -alc.shortcut('(?i:top)(?i:绑定|绑|bind)', {'command': 'tstats TOP bind', 'humanized': 'top绑定'}) -alc.shortcut('(?i:top)(?i:查询|查|query|stats)', {'command': 'tstats TOP query', 'humanized': 'top查'}) +alc.shortcut( + '(?i:top)(?i:绑定|绑|bind)', + command='tstats TOP bind', + humanized='top绑定', +) +alc.shortcut( + '(?i:top)(?i:解除绑定|解绑|unbind)', + command='tstats TOP unbind', + humanized='top解绑', +) +alc.shortcut( + '(?i:top)(?i:查询|查|query|stats)', + command='tstats TOP query', + humanized='top查', +) add_block_handlers(alc.assign('TOP.query')) -from . import bind, query # noqa: E402, F401 +from . import bind, query, unbind # noqa: E402, F401 diff --git a/nonebot_plugin_tetris_stats/games/top/unbind.py b/nonebot_plugin_tetris_stats/games/top/unbind.py new file mode 100644 index 0000000..01b8b09 --- /dev/null +++ b/nonebot_plugin_tetris_stats/games/top/unbind.py @@ -0,0 +1,63 @@ +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 +from nonebot_plugin_userinfo import BotUserInfo, EventUserInfo, UserInfo +from nonebot_plugin_waiter import suggest # type: ignore[import-untyped] + +from ...db import query_bind_info, remove_bind, trigger +from ...utils.host import HostPage, get_self_netloc +from ...utils.image import get_avatar +from ...utils.render import Bind, render +from ...utils.render.schemas.base import People +from ...utils.screenshot import screenshot +from . import alc +from .api import Player +from .constant import GAME_TYPE + + +@alc.assign('TOP.unbind') +async def _( + nb_user: User, + event_session: EventSession, + event_user_info: UserInfo = EventUserInfo(), # noqa: B008 + bot_info: UserInfo = BotUserInfo(), # noqa: B008 +): + async with ( + trigger( + session_persist_id=await get_session_persist_id(event_session), + game_platform=GAME_TYPE, + command_type='unbind', + command_args=[], + ), + get_session() as session, + ): + if (bind := await query_bind_info(session=session, user=nb_user, game_platform=GAME_TYPE)) is None: + await UniMessage('您还未绑定 TOP 账号').finish() + resp = await suggest('您确定要解绑吗?', ['是', '否']) + if resp is None or resp.extract_plain_text() == '否': + return + player = Player(user_name=bind.game_account, trust=True) + user = await player.user + netloc = get_self_netloc() + async with HostPage( + await render( + 'v1/binding', + Bind( + platform='TOP', + status='unlink', + user=People( + avatar=await get_avatar(event_user_info, 'Data URI', None), + name=user.user_name, + ), + bot=People( + avatar=await get_avatar(bot_info, 'Data URI', '../../static/logo/logo.svg'), + name=bot_info.user_name, + ), + command='top绑定{游戏ID}', + ), + ) + ) as page_hash: + await UniMessage.image(raw=await screenshot(f'http://{netloc}/host/{page_hash}.html')).send() + await remove_bind(session=session, user=nb_user, game_platform=GAME_TYPE) diff --git a/nonebot_plugin_tetris_stats/games/tos/__init__.py b/nonebot_plugin_tetris_stats/games/tos/__init__.py index 14538eb..359fd35 100644 --- a/nonebot_plugin_tetris_stats/games/tos/__init__.py +++ b/nonebot_plugin_tetris_stats/games/tos/__init__.py @@ -34,6 +34,10 @@ def get_player(teaid_or_name: str) -> Player | MessageFormatError: ), help_text='绑定 茶服 账号', ), + Subcommand( + 'unbind', + help_text='解除绑定 TOS 账号', + ), Subcommand( 'query', Args( @@ -56,9 +60,22 @@ def get_player(teaid_or_name: str) -> Player | MessageFormatError: ) ) -alc.shortcut('(?i:tos|茶服)(?i:绑定|绑|bind)', {'command': 'tstats TOS bind', 'humanized': '茶服绑定'}) -alc.shortcut('(?i:tos|茶服)(?i:查询|查|query|stats)', {'command': 'tstats TOS query', 'humanized': '茶服查'}) +alc.shortcut( + '(?i:tos|茶服)(?i:绑定|绑|bind)', + command='tstats TOS bind', + humanized='茶服绑定', +) +alc.shortcut( + '(?i:tos|茶服)(?i:解除绑定|解绑|unbind)', + command='tstats TOS unbind', + humanized='茶服解绑', +) +alc.shortcut( + '(?i:tos|茶服)(?i:查询|查|query|stats)', + command='tstats TOS query', + humanized='茶服查', +) add_block_handlers(alc.assign('TOS.query')) -from . import bind, query # noqa: E402, F401 +from . import bind, query, unbind # noqa: E402, F401 diff --git a/nonebot_plugin_tetris_stats/games/tos/unbind.py b/nonebot_plugin_tetris_stats/games/tos/unbind.py new file mode 100644 index 0000000..8c34e81 --- /dev/null +++ b/nonebot_plugin_tetris_stats/games/tos/unbind.py @@ -0,0 +1,66 @@ +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 +from nonebot_plugin_userinfo import BotUserInfo, EventUserInfo, UserInfo +from nonebot_plugin_waiter import suggest # type: ignore[import-untyped] + +from ...db import query_bind_info, remove_bind, trigger +from ...utils.host import HostPage, get_self_netloc +from ...utils.image import get_avatar +from ...utils.render import Bind, render +from ...utils.render.avatar import get_avatar as get_random_avatar +from ...utils.render.schemas.base import People +from ...utils.screenshot import screenshot +from . import alc +from .api import Player +from .constant import GAME_TYPE + + +@alc.assign('TOP.unbind') +async def _( + nb_user: User, + event_session: EventSession, + event_user_info: UserInfo = EventUserInfo(), # noqa: B008 + bot_info: UserInfo = BotUserInfo(), # noqa: B008 +): + async with ( + trigger( + session_persist_id=await get_session_persist_id(event_session), + game_platform=GAME_TYPE, + command_type='unbind', + command_args=[], + ), + get_session() as session, + ): + if (bind := await query_bind_info(session=session, user=nb_user, game_platform=GAME_TYPE)) is None: + await UniMessage('您还未绑定 TOP 账号').finish() + resp = await suggest('您确定要解绑吗?', ['是', '否']) + if resp is None or resp.extract_plain_text() == '否': + return + player = Player(user_name=bind.game_account, trust=True) + user = await player.user + netloc = get_self_netloc() + async with HostPage( + await render( + 'v1/binding', + Bind( + platform='TOS', + status='unlink', + user=People( + avatar=await get_avatar(event_user_info, 'Data URI', None) + if event_user_info is not None + else get_random_avatar(user.teaid), + name=user.name, + ), + bot=People( + avatar=await get_avatar(bot_info, 'Data URI', '../../static/logo/logo.svg'), + name=bot_info.user_name, + ), + command='茶服绑定{游戏ID}', + ), + ) + ) as page_hash: + await UniMessage.image(raw=await screenshot(f'http://{netloc}/host/{page_hash}.html')).send() + await remove_bind(session=session, user=nb_user, game_platform=GAME_TYPE) diff --git a/nonebot_plugin_tetris_stats/utils/typing.py b/nonebot_plugin_tetris_stats/utils/typing.py index b0abc4c..7edb3ae 100644 --- a/nonebot_plugin_tetris_stats/utils/typing.py +++ b/nonebot_plugin_tetris_stats/utils/typing.py @@ -2,7 +2,7 @@ Number: TypeAlias = float | int GameType: TypeAlias = Literal['IO', 'TOP', 'TOS'] -BaseCommandType: TypeAlias = Literal['bind', 'query'] +BaseCommandType: TypeAlias = Literal['bind', 'unbind', 'query'] TETRIOCommandType: TypeAlias = BaseCommandType | Literal['rank', 'config', 'list', 'record'] AllCommandType: TypeAlias = BaseCommandType | TETRIOCommandType Me: TypeAlias = Literal[