diff --git a/nonebot_plugin_tetris_stats/config/migrations/2ff388a8c486_add_verify_field.py b/nonebot_plugin_tetris_stats/config/migrations/2ff388a8c486_add_verify_field.py new file mode 100644 index 0000000..3bd50bc --- /dev/null +++ b/nonebot_plugin_tetris_stats/config/migrations/2ff388a8c486_add_verify_field.py @@ -0,0 +1,42 @@ +"""add verify field + +迁移 ID: 2ff388a8c486 +父迁移: 3588702dd3a4 +创建时间: 2025-07-22 18:09:09.734164 + +""" + +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 = '2ff388a8c486' +down_revision: str | Sequence[str] | None = '3588702dd3a4' +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('nb_t_bind', schema=None) as batch_op: + batch_op.add_column(sa.Column('verify', sa.Boolean(), nullable=False, server_default='false')) + + # ### end Alembic commands ### + + +def downgrade(name: str = '') -> None: + if name: + return + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('nb_t_bind', schema=None) as batch_op: + batch_op.drop_column('verify') + + # ### end Alembic commands ### diff --git a/nonebot_plugin_tetris_stats/db/__init__.py b/nonebot_plugin_tetris_stats/db/__init__.py index 2f56be9..d739b36 100644 --- a/nonebot_plugin_tetris_stats/db/__init__.py +++ b/nonebot_plugin_tetris_stats/db/__init__.py @@ -42,6 +42,8 @@ async def create_or_update_bind( user: User, game_platform: GameType, game_account: str, + *, + verify: bool = False, ) -> BindStatus: bind = await query_bind_info( session=session, @@ -53,6 +55,7 @@ async def create_or_update_bind( user_id=user.id, game_platform=game_platform, game_account=game_account, + verify=verify, ) session.add(bind) status = BindStatus.SUCCESS diff --git a/nonebot_plugin_tetris_stats/db/models.py b/nonebot_plugin_tetris_stats/db/models.py index 6e43b4c..40ff98e 100644 --- a/nonebot_plugin_tetris_stats/db/models.py +++ b/nonebot_plugin_tetris_stats/db/models.py @@ -71,6 +71,7 @@ class Bind(MappedAsDataclass, Model): user_id: Mapped[int] = mapped_column(index=True) game_platform: Mapped[GameType] = mapped_column(String(32)) game_account: Mapped[str] + verify: Mapped[bool] class TriggerHistoricalDataV2(MappedAsDataclass, Model): diff --git a/nonebot_plugin_tetris_stats/games/tetrio/bind.py b/nonebot_plugin_tetris_stats/games/tetrio/bind.py index 1642741..c5e6c48 100644 --- a/nonebot_plugin_tetris_stats/games/tetrio/bind.py +++ b/nonebot_plugin_tetris_stats/games/tetrio/bind.py @@ -1,3 +1,4 @@ +from asyncio import gather from hashlib import md5 from secrets import choice @@ -44,6 +45,42 @@ alc.shortcut( humanized='io绑定', ) +try: + from nonebot.adapters.discord import MessageCreateEvent + + @alc.assign('TETRIO.bind') + async def _(_: MessageCreateEvent, nb_user: User, account: Player, event_session: Uninfo, interface: QryItrface): + async with trigger( + session_persist_id=await get_session_persist_id(event_session), + game_platform=GAME_TYPE, + command_type='bind', + command_args=[], + ): + user, user_info = await gather(account.user, account.get_info()) + verify = ( + user_info.data.connections.discord is not None + and user_info.data.connections.discord.id == event_session.user.id + ) + async with get_session() as session: + bind_status = await create_or_update_bind( + session=session, + user=nb_user, + game_platform=GAME_TYPE, + game_account=user.unique_identifier, + verify=verify, + ) + if bind_status in (BindStatus.SUCCESS, BindStatus.UPDATE): + await UniMessage.image( + raw=await make_bind_image( + player=account, + event_session=event_session, + interface=interface, + verify=verify, + ) + ).finish() +except ImportError: + pass + @alc.assign('TETRIO.bind') async def _(nb_user: User, account: Player, event_session: Uninfo, interface: QryItrface): @@ -62,36 +99,49 @@ async def _(nb_user: User, account: Player, event_session: Uninfo, interface: Qr game_account=user.unique_identifier, ) if bind_status in (BindStatus.SUCCESS, BindStatus.UPDATE): - netloc = get_self_netloc() - async with HostPage( - await render( - 'v1/binding', - Bind( - platform='TETR.IO', - type='unknown', - user=People( - avatar=str( - URL(f'http://{netloc}/host/resource/tetrio/avatars/{user.ID}') - % {'revision': avatar_revision} - ) - if (avatar_revision := (await account.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_user := await interface.get_user(event_session.self_id) - or UninfoUser(id=event_session.self_id) - ), - 'Data URI', - '../../static/logo/logo.svg', - ), - name=bot_user.nick or bot_user.name or choice(list(global_config.nickname) or ['bot']), - ), - prompt='io查我', - lang=get_lang(), - ), + await UniMessage.image( + raw=await make_bind_image( + player=account, + event_session=event_session, + interface=interface, + verify=None, ) - ) as page_hash: - await UniMessage.image(raw=await screenshot(f'http://{netloc}/host/{page_hash}.html')).finish() + ).finish() + + +async def make_bind_image( + player: Player, event_session: Uninfo, interface: QryItrface, *, verify: bool | None = None +) -> bytes: + (user, avatar_revision) = await gather(player.user, player.avatar_revision) + netloc = get_self_netloc() + async with HostPage( + await render( + 'v1/binding', + Bind( + platform='TETR.IO', + type='unknown' if verify is None else 'success' if verify else 'unverified', + user=People( + avatar=str( + URL(f'http://{netloc}/host/resource/tetrio/avatars/{user.ID}') % {'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 + name=user.name.upper(), + ), + bot=People( + avatar=await get_avatar( + ( + bot_user := await interface.get_user(event_session.self_id) + or UninfoUser(id=event_session.self_id) + ), + 'Data URI', + '../../static/logo/logo.svg', + ), + name=bot_user.nick or bot_user.name or choice(list(global_config.nickname) or ['bot']), + ), + prompt='io查我', + lang=get_lang(), + ), + ) + ) as page_hash: + return await screenshot(f'http://{netloc}/host/{page_hash}.html')