from datetime import datetime, timezone from typing import overload from urllib.parse import urlencode from httpx import TimeoutException from nonebot.compat import type_validate_json from ....db import anti_duplicate_add from ....utils.exception import RequestError from ....utils.request import Request, splice_url from ..constant import BASE_URL, USER_NAME from .models import TOSHistoricalData from .schemas.user import User from .schemas.user_info import UserInfo, UserInfoSuccess from .schemas.user_profile import UserProfile UTC = timezone.utc class Player: @overload def __init__(self, *, teaid: str, trust: bool = False): ... @overload def __init__(self, *, user_name: str, trust: bool = False): ... def __init__(self, *, teaid: str | None = None, user_name: str | None = None, trust: bool = False): self.teaid = teaid self.user_name = user_name if not trust: if self.teaid is not None: if ( not self.teaid.startswith(('onebot-', 'qqguild-', 'kook-', 'discord-')) or not self.teaid.split('-', maxsplit=1)[1].isdigit() ): msg = 'Invalid teaid' raise ValueError(msg) elif self.user_name is not None: if not USER_NAME.match(self.user_name) or self.user_name.isdigit() or 2 > len(self.user_name) > 18: # noqa: PLR2004 msg = 'Invalid user name' raise ValueError(msg) else: msg = 'Invalid user' raise ValueError(msg) self.__user: User | None = None self._user_info: UserInfoSuccess | None = None self._user_profile: dict[str, UserProfile] = {} @property async def user(self) -> User: if self.__user is None: user_info = await self.get_info() self.__user = User(teaid=user_info.data.teaid, name=user_info.data.name) self.teaid = user_info.data.teaid self.user_name = user_info.data.name return self.__user async def get_info(self) -> UserInfoSuccess: """获取用户信息""" if self._user_info is None: if self.teaid is not None: url = [ splice_url( [ i, 'getTeaIdInfo', f'?{urlencode({"teaId":self.teaid})}', ] ) for i in BASE_URL ] else: url = [ splice_url( [ i, 'getUsernameInfo', f'?{urlencode({"username":self.user_name})}', ] ) for i in BASE_URL ] raw_user_info = await Request.failover_request(url, failover_code=[502], failover_exc=(TimeoutException,)) user_info: UserInfo = type_validate_json(UserInfo, raw_user_info) # type: ignore[arg-type] if not isinstance(user_info, UserInfoSuccess): msg = f'用户信息请求错误:\n{user_info.error}' raise RequestError(msg) self._user_info = user_info await anti_duplicate_add( TOSHistoricalData, TOSHistoricalData( user_unique_identifier=(await self.user).unique_identifier, api_type='User Info', data=user_info, update_time=datetime.now(UTC), ), ) return self._user_info async def get_profile(self, other_parameter: dict[str, str | bytes] | None = None) -> UserProfile: """获取用户数据""" if other_parameter is None: other_parameter = {} params = urlencode(dict(sorted(other_parameter.items()))) if self._user_profile.get(params) is None: raw_user_profile = await Request.failover_request( [ splice_url( [ i, 'getProfile', f'?{urlencode({"id":self.teaid or self.user_name,**other_parameter})}', ] ) for i in BASE_URL ], failover_code=[502], failover_exc=(TimeoutException,), ) self._user_profile[params] = type_validate_json(UserProfile, raw_user_profile) await anti_duplicate_add( TOSHistoricalData, TOSHistoricalData( user_unique_identifier=(await self.user).unique_identifier, api_type='User Profile', data=self._user_profile[params], update_time=datetime.now(UTC), ), ) return self._user_profile[params]