95 lines
3.2 KiB
Python
95 lines
3.2 KiB
Python
"""Пользователи: создание из внешней личности, ник, активная группа."""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
from sqlmodel import Session, select
|
|
|
|
from app.auth.provider import ExternalIdentity
|
|
from app.core.errors import NicknameTakenError, NotFoundError, ValidationError
|
|
from app.models import AuthIdentity, GroupMember, User
|
|
|
|
_NICK_RE = re.compile(r"^[\w .\-]{2,64}$", re.UNICODE)
|
|
|
|
|
|
def get_user(session: Session, user_id: int) -> User:
|
|
user = session.get(User, user_id)
|
|
if user is None:
|
|
raise NotFoundError("Пользователь не найден.")
|
|
return user
|
|
|
|
|
|
def nickname_available(session: Session, nickname: str, exclude_user_id: int | None = None) -> bool:
|
|
stmt = select(User).where(User.nickname == nickname)
|
|
existing = session.exec(stmt).first()
|
|
return existing is None or existing.id == exclude_user_id
|
|
|
|
|
|
def _unique_nickname(session: Session, base: str) -> str:
|
|
base = (base or "Игрок").strip()[:60] or "Игрок"
|
|
candidate = base
|
|
suffix = 1
|
|
while session.exec(select(User).where(User.nickname == candidate)).first() is not None:
|
|
suffix += 1
|
|
candidate = f"{base} {suffix}"
|
|
return candidate
|
|
|
|
|
|
def get_or_create_from_identity(session: Session, identity: ExternalIdentity) -> User:
|
|
link = session.exec(
|
|
select(AuthIdentity).where(
|
|
AuthIdentity.provider == identity.provider,
|
|
AuthIdentity.external_id == identity.external_id,
|
|
)
|
|
).first()
|
|
if link is not None:
|
|
return get_user(session, link.user_id)
|
|
|
|
user = User(
|
|
nickname=_unique_nickname(session, identity.suggested_nickname or identity.external_id),
|
|
role="player",
|
|
auth_provider=identity.provider,
|
|
telegram_id=identity.telegram_id,
|
|
)
|
|
session.add(user)
|
|
session.flush()
|
|
session.add(
|
|
AuthIdentity(
|
|
user_id=user.id, # type: ignore[arg-type]
|
|
provider=identity.provider,
|
|
external_id=identity.external_id,
|
|
)
|
|
)
|
|
session.commit()
|
|
session.refresh(user)
|
|
return user
|
|
|
|
|
|
def update_nickname(session: Session, user: User, new_nickname: str) -> User:
|
|
new_nickname = (new_nickname or "").strip()
|
|
if not _NICK_RE.match(new_nickname):
|
|
raise ValidationError("Ник: 2–64 символа, буквы/цифры/пробел/.-_")
|
|
if not nickname_available(session, new_nickname, exclude_user_id=user.id):
|
|
raise NicknameTakenError()
|
|
user.nickname = new_nickname
|
|
session.add(user)
|
|
session.commit()
|
|
session.refresh(user)
|
|
return user
|
|
|
|
|
|
def set_active_group(session: Session, user: User, group_id: int | None) -> User:
|
|
if group_id is not None:
|
|
member = session.exec(
|
|
select(GroupMember).where(
|
|
GroupMember.group_id == group_id, GroupMember.user_id == user.id
|
|
)
|
|
).first()
|
|
if member is None:
|
|
raise ValidationError("Нельзя сделать активной группу, в которой вы не состоите.")
|
|
user.active_group_id = group_id
|
|
session.add(user)
|
|
session.commit()
|
|
session.refresh(user)
|
|
return user
|