mirror of
https://github.com/A-Minos/nonebot-plugin-tetris-stats.git
synced 2026-03-05 05:36:54 +08:00
✨ 通过io账号绑定的discord验证归属权 #64 (#552)
Some checks are pending
Code Coverage / Test (macos-latest, 3.10) (push) Waiting to run
Code Coverage / Test (macos-latest, 3.11) (push) Waiting to run
Code Coverage / Test (macos-latest, 3.12) (push) Waiting to run
Code Coverage / Test (ubuntu-latest, 3.10) (push) Waiting to run
Code Coverage / Test (ubuntu-latest, 3.11) (push) Waiting to run
Code Coverage / Test (ubuntu-latest, 3.12) (push) Waiting to run
Code Coverage / Test (windows-latest, 3.10) (push) Waiting to run
Code Coverage / Test (windows-latest, 3.11) (push) Waiting to run
Code Coverage / Test (windows-latest, 3.12) (push) Waiting to run
Code Coverage / check (push) Blocked by required conditions
TypeCheck / TypeCheck (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Some checks are pending
Code Coverage / Test (macos-latest, 3.10) (push) Waiting to run
Code Coverage / Test (macos-latest, 3.11) (push) Waiting to run
Code Coverage / Test (macos-latest, 3.12) (push) Waiting to run
Code Coverage / Test (ubuntu-latest, 3.10) (push) Waiting to run
Code Coverage / Test (ubuntu-latest, 3.11) (push) Waiting to run
Code Coverage / Test (ubuntu-latest, 3.12) (push) Waiting to run
Code Coverage / Test (windows-latest, 3.10) (push) Waiting to run
Code Coverage / Test (windows-latest, 3.11) (push) Waiting to run
Code Coverage / Test (windows-latest, 3.12) (push) Waiting to run
Code Coverage / check (push) Blocked by required conditions
TypeCheck / TypeCheck (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
* 🗃️ 添加 verify 字段到 Bind 模型,并在创建或更新绑定时支持该字段 * ✨ 通过io账号绑定的discord验证归属权
This commit is contained in:
@@ -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 ###
|
||||||
@@ -42,6 +42,8 @@ async def create_or_update_bind(
|
|||||||
user: User,
|
user: User,
|
||||||
game_platform: GameType,
|
game_platform: GameType,
|
||||||
game_account: str,
|
game_account: str,
|
||||||
|
*,
|
||||||
|
verify: bool = False,
|
||||||
) -> BindStatus:
|
) -> BindStatus:
|
||||||
bind = await query_bind_info(
|
bind = await query_bind_info(
|
||||||
session=session,
|
session=session,
|
||||||
@@ -53,6 +55,7 @@ async def create_or_update_bind(
|
|||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
game_platform=game_platform,
|
game_platform=game_platform,
|
||||||
game_account=game_account,
|
game_account=game_account,
|
||||||
|
verify=verify,
|
||||||
)
|
)
|
||||||
session.add(bind)
|
session.add(bind)
|
||||||
status = BindStatus.SUCCESS
|
status = BindStatus.SUCCESS
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class Bind(MappedAsDataclass, Model):
|
|||||||
user_id: Mapped[int] = mapped_column(index=True)
|
user_id: Mapped[int] = 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]
|
||||||
|
verify: Mapped[bool]
|
||||||
|
|
||||||
|
|
||||||
class TriggerHistoricalDataV2(MappedAsDataclass, Model):
|
class TriggerHistoricalDataV2(MappedAsDataclass, Model):
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from asyncio import gather
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from secrets import choice
|
from secrets import choice
|
||||||
|
|
||||||
@@ -44,6 +45,42 @@ alc.shortcut(
|
|||||||
humanized='io绑定',
|
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')
|
@alc.assign('TETRIO.bind')
|
||||||
async def _(nb_user: User, account: Player, event_session: Uninfo, interface: QryItrface):
|
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,
|
game_account=user.unique_identifier,
|
||||||
)
|
)
|
||||||
if bind_status in (BindStatus.SUCCESS, BindStatus.UPDATE):
|
if bind_status in (BindStatus.SUCCESS, BindStatus.UPDATE):
|
||||||
netloc = get_self_netloc()
|
await UniMessage.image(
|
||||||
async with HostPage(
|
raw=await make_bind_image(
|
||||||
await render(
|
player=account,
|
||||||
'v1/binding',
|
event_session=event_session,
|
||||||
Bind(
|
interface=interface,
|
||||||
platform='TETR.IO',
|
verify=None,
|
||||||
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(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
) as page_hash:
|
).finish()
|
||||||
await UniMessage.image(raw=await screenshot(f'http://{netloc}/host/{page_hash}.html')).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')
|
||||||
|
|||||||
Reference in New Issue
Block a user