GramCore/gram_core/services/cookies/services.py

160 lines
7.6 KiB
Python
Raw Normal View History

from typing import List, Optional
from simnet import StarRailClient, Region, Game
from simnet.errors import InvalidCookies, BadRequest as SimnetBadRequest, TooManyRequests
from gram_core.base_service import BaseService
from gram_core.basemodel import RegionEnum
from gram_core.services.cookies.cache import PublicCookiesCache
from gram_core.services.cookies.error import CookieServiceError, TooManyRequestPublicCookies
from gram_core.services.cookies.models import CookiesDataBase as Cookies, CookiesStatusEnum
from gram_core.services.cookies.repositories import CookiesRepository
from utils.log import logger
__all__ = ("CookiesService", "PublicCookiesService")
class CookiesService(BaseService):
def __init__(self, cookies_repository: CookiesRepository) -> None:
self._repository: CookiesRepository = cookies_repository
async def update(self, cookies: Cookies):
await self._repository.update(cookies)
async def add(self, cookies: Cookies):
await self._repository.add(cookies)
async def get(
self,
user_id: int,
account_id: Optional[int] = None,
region: Optional[RegionEnum] = None,
) -> Optional[Cookies]:
return await self._repository.get(user_id, account_id, region)
async def delete(self, cookies: Cookies) -> None:
return await self._repository.delete(cookies)
class PublicCookiesService(BaseService):
def __init__(self, cookies_repository: CookiesRepository, public_cookies_cache: PublicCookiesCache):
self._cache = public_cookies_cache
self._repository: CookiesRepository = cookies_repository
self.count: int = 0
self.user_times_limiter = 3 * 3
async def initialize(self) -> None:
logger.info("正在初始化公共Cookies池")
await self.refresh()
logger.success("刷新公共Cookies池成功")
async def refresh(self):
"""刷新公共Cookies 定时任务
:return:
"""
user_list: List[int] = []
cookies_list = await self._repository.get_all_by_region(RegionEnum.HYPERION) # 从数据库获取2
for cookies in cookies_list:
if cookies.status is None or cookies.status == CookiesStatusEnum.STATUS_SUCCESS:
user_list.append(cookies.user_id)
if len(user_list) > 0:
add, count = await self._cache.add_public_cookies(user_list, RegionEnum.HYPERION)
logger.info("国服公共Cookies池已经添加[%s]个 当前成员数为[%s]", add, count)
user_list.clear()
cookies_list = await self._repository.get_all_by_region(RegionEnum.HOYOLAB)
for cookies in cookies_list:
if cookies.status is None or cookies.status == CookiesStatusEnum.STATUS_SUCCESS:
user_list.append(cookies.user_id)
if len(user_list) > 0:
add, count = await self._cache.add_public_cookies(user_list, RegionEnum.HOYOLAB)
logger.info("国际服公共Cookies池已经添加[%s]个 当前成员数为[%s]", add, count)
async def get_cookies(self, user_id: int, region: RegionEnum = RegionEnum.NULL):
"""获取公共Cookies
:param user_id: 用户ID
:param region: 注册的服务器
:return:
"""
user_times = await self._cache.incr_by_user_times(user_id)
if int(user_times) > self.user_times_limiter:
logger.warning("用户 %s 使用公共Cookies次数已经到达上限", user_id)
raise TooManyRequestPublicCookies(user_id)
while True:
public_id, count = await self._cache.get_public_cookies(region)
cookies = await self._repository.get(public_id, region=region)
if cookies is None:
await self._cache.delete_public_cookies(public_id, region)
continue
if region == RegionEnum.HYPERION:
client = StarRailClient(cookies=cookies.data, region=Region.CHINESE)
elif region == RegionEnum.HOYOLAB:
client = StarRailClient(cookies=cookies.data, region=Region.OVERSEAS, lang="zh-cn")
else:
raise CookieServiceError
try:
if client.account_id is None:
raise RuntimeError("account_id not found")
record_cards = await client.get_record_cards()
for record_card in record_cards:
if record_card.game == Game.STARRAIL:
await client.get_starrail_user(record_card.uid)
break
else:
accounts = await client.get_game_accounts()
for account in accounts:
if account.game == Game.STARRAIL:
await client.get_starrail_user(account.uid)
break
except InvalidCookies as exc:
if exc.ret_code in (10001, -100):
logger.warning("用户 [%s] Cookies无效", public_id)
elif exc.ret_code == 10103:
logger.warning("用户 [%s] Cookies有效但没有绑定到游戏帐户", public_id)
else:
logger.warning("Cookies无效 ")
logger.exception(exc)
cookies.status = CookiesStatusEnum.INVALID_COOKIES
await self._repository.update(cookies)
await self._cache.delete_public_cookies(cookies.user_id, region)
continue
except TooManyRequests:
logger.warning("用户 [%s] 查询次数太多或操作频繁", public_id)
cookies.status = CookiesStatusEnum.TOO_MANY_REQUESTS
await self._repository.update(cookies)
await self._cache.delete_public_cookies(cookies.user_id, region)
continue
except SimnetBadRequest as exc:
if "invalid content type" in exc.message:
raise exc
if exc.ret_code == 1034:
logger.warning("用户 [%s] 触发验证", public_id)
else:
logger.warning("用户 [%s] 获取账号信息发生错误,错误信息为", public_id)
logger.exception(exc)
await self._cache.delete_public_cookies(cookies.user_id, region)
continue
except RuntimeError as exc:
if "account_id not found" in str(exc):
cookies.status = CookiesStatusEnum.INVALID_COOKIES
await self._repository.update(cookies)
await self._cache.delete_public_cookies(cookies.user_id, region)
continue
raise exc
except Exception as exc:
await self._cache.delete_public_cookies(cookies.user_id, region)
raise exc
finally:
await client.shutdown()
logger.info("用户 user_id[%s] 请求用户 user_id[%s] 的公共Cookies 该Cookies使用次数为%s", user_id, public_id, count)
return cookies
async def undo(self, user_id: int, cookies: Optional[Cookies] = None, status: Optional[CookiesStatusEnum] = None):
await self._cache.incr_by_user_times(user_id, -1)
if cookies is not None and status is not None:
cookies.status = status
await self._repository.update(cookies)
await self._cache.delete_public_cookies(cookies.user_id, cookies.region)
logger.info("用户 user_id[%s] 反馈用户 user_id[%s] 的Cookies状态为 %s", user_id, cookies.user_id, status.name)
else:
logger.info("用户 user_id[%s] 撤销一次公共Cookies计数", user_id)