添加公共Cookies服务模块

This commit is contained in:
洛水居室 2022-08-04 21:19:17 +08:00
parent 9e7637203a
commit 0b898d4868
No known key found for this signature in database
GPG Key ID: C9DE87DA724B88FC
4 changed files with 192 additions and 6 deletions

View File

@ -1,7 +1,9 @@
from app.cookies.repositories import CookiesRepository
from app.cookies.service import CookiesService
from utils.app.manager import listener_service
from utils.mysql import MySQL
from utils.redisdb import RedisDB
from .cache import PublicCookiesCache
from .repositories import CookiesRepository
from .service import CookiesService, PublicCookiesService
@listener_service()
@ -9,3 +11,11 @@ def create_cookie_service(mysql: MySQL):
_repository = CookiesRepository(mysql)
_service = CookiesService(_repository)
return _service
@listener_service()
def create_public_cookie_service(mysql: MySQL, redis: RedisDB):
_repository = CookiesRepository(mysql)
_cache = PublicCookiesCache(redis)
_service = PublicCookiesService(_repository, _cache)
return _service

92
app/cookies/cache.py Normal file
View File

@ -0,0 +1,92 @@
from typing import List, Union
from model.base import RegionEnum
from utils.error import RegionNotFoundError
from utils.redisdb import RedisDB
from .error import CookiesCachePoolExhausted
class PublicCookiesCache:
"""使用优先级(score)进行排序对使用次数最少的Cookies进行审核"""
def __init__(self, redis: RedisDB):
self.client = redis.client
self.score_qname = "cookie:public"
self.end = 20
def get_queue_name(self, region: RegionEnum):
if region == RegionEnum.HYPERION:
return self.score_qname + ":yuanshen"
elif region == RegionEnum.HOYOLAB:
return self.score_qname + ":genshin"
else:
raise RegionNotFoundError(region.name)
async def putback(self, uid: int, region: RegionEnum):
"""重新添加单个到缓存列表
:param uid:
:param region:
:return:
"""
qname = self.get_queue_name(region)
score_maps = {f"{uid}": 0}
result = await self.client.zrem(qname, f"{uid}")
if result == 1:
await self.client.zadd(qname, score_maps)
return result
async def add(self, uid: Union[List[int], int], region: RegionEnum):
"""单个或批量添加到缓存列表
:param uid:
:param region:
:return: 成功返回列表大小
"""
qname = self.get_queue_name(region)
if isinstance(uid, int):
score_maps = {f"{uid}": 0}
elif isinstance(uid, list):
score_maps = {}
for i in uid:
score_maps[f"{i}"] = 0
else:
raise TypeError(f"uid variable type error")
async with self.client.pipeline(transaction=True) as pipe:
# nx:只添加新元素。不要更新已经存在的元素
await pipe.zadd(qname, score_maps, nx=True)
await pipe.zcard(qname)
add, count = await pipe.execute()
return int(add), count
async def get(self, region: RegionEnum):
"""从缓存列表获取
:param region:
:return:
"""
qname = self.get_queue_name(region)
scores = await self.client.zrevrange(qname, 0, self.end, withscores=True, score_cast_func=int)
if len(scores) > 0:
def take_score(elem):
return elem[1]
scores.sort(key=take_score)
key = scores[0][0]
score = scores[0][1]
else:
raise CookiesCachePoolExhausted
async with self.client.pipeline(transaction=True) as pipe:
await pipe.zincrby(qname, 1, key)
await pipe.execute()
return int(key), score + 1
async def delete(self, uid: int, region: RegionEnum):
qname = self.get_queue_name(region)
async with self.client.pipeline(transaction=True) as pipe:
await pipe.zrem(qname, uid)
return await pipe.execute()
async def count(self, limit: bool = True):
async with self.client.pipeline(transaction=True) as pipe:
if limit:
await pipe.zcount(0, self.end)
else:
await pipe.zcard(self.score_qname)
return await pipe.execute()

3
app/cookies/error.py Normal file
View File

@ -0,0 +1,3 @@
class CookiesCachePoolExhausted(Exception):
def __init__(self):
super().__init__("Cookies cache pool is exhausted")

View File

@ -1,5 +1,13 @@
from app.cookies.repositories import CookiesRepository
from model.base import ServiceEnum
from typing import List
import genshin
from genshin import types, InvalidCookies, TooManyRequests, GenshinException
from logger import Log
from model.base import RegionEnum
from .cache import PublicCookiesCache
from .models import CookiesStatusEnum
from .repositories import CookiesRepository, CookiesNotFoundError
class CookiesService:
@ -15,5 +23,78 @@ class CookiesService:
async def get_cookies(self, user_id: int, region: RegionEnum):
return await self._repository.get_cookies(user_id, region)
async def read_cookies(self, user_id: int, default_service: ServiceEnum):
return await self._repository.read_cookies(user_id, default_service)
class PublicCookiesService:
def __init__(self, cookies_repository: CookiesRepository, public_cookies_cache: PublicCookiesCache):
self._cache = public_cookies_cache
self._repository: CookiesRepository = cookies_repository
self.count: int = 0
async def refresh(self):
"""刷新公共Cookies 定时任务
:return:
"""
Log.info("正在初始化公共Cookies")
user_list: List[int] = []
cookies_list = await self._repository.get_all_cookies(RegionEnum.HYPERION) # 从数据库获取2
for cookies in cookies_list:
if cookies.status is not None and cookies.status != CookiesStatusEnum.STATUS_SUCCESS:
continue
user_list.append(cookies.user_id)
add, count = await self._cache.add(user_list, RegionEnum.HYPERION)
Log.info(f"国服公共Cookies池已经添加[{add}]个 当前成员数为[{count}]")
user_list.clear()
cookies_list = await self._repository.get_all_cookies(RegionEnum.HOYOLAB)
for cookies in cookies_list:
user_list.append(cookies.user_id)
add, count = await self._cache.add(user_list, RegionEnum.HOYOLAB)
Log.info(f"国际服公共Cookies池已经添加[{add}]个 当前成员数为[{count}]")
async def get_cookies(self, user_id: int, region: RegionEnum = RegionEnum.NULL):
"""获取公共Cookies
:param user_id: 用户ID
:param region: 注册的服务器
:return:
"""
while True:
public_id, count = await self._cache.get(region)
try:
cookies = await self._repository.get_cookies(public_id, region)
except CookiesNotFoundError:
await self._cache.delete(public_id, region)
continue
if region == RegionEnum.HYPERION:
client = genshin.Client(cookies=cookies.cookies, game=types.Game.GENSHIN, region=types.Region.CHINESE)
elif region == RegionEnum.HOYOLAB:
client = genshin.Client(cookies=cookies.cookies, game=types.Game.GENSHIN, region=types.Region.OVERSEAS,
lang="zh-cn")
else:
return None
try:
await client.get_record_card()
except InvalidCookies as exc:
if "[10001]" in str(exc):
Log.warning(f"用户 [{public_id}] Cookies无效")
elif "[-100]" in str(exc):
Log.warning(f"用户 [{public_id}] Cookies无效")
elif "[10103]" in str(exc):
Log.warning(f"用户 [{public_id}] Cookie有效但没有绑定到游戏帐户")
else:
Log.warning("Cookies无效具体原因未知", exc)
cookies.status = CookiesStatusEnum.INVALID_COOKIES
await self._repository.update_cookies_ex(cookies, region)
await self._cache.delete(cookies.user_id, region)
continue
except TooManyRequests as exc:
Log.warning(f"用户 [{public_id}] 查询次数太多(操作频繁)")
cookies.status = CookiesStatusEnum.TOO_MANY_REQUESTS
await self._repository.update_cookies_ex(cookies, region)
await self._cache.delete(cookies.user_id, region)
continue
except GenshinException as exc:
Log.warning(f"用户 [{public_id}] 获取账号信息发生错误,错误信息为", exc)
continue
Log.info(f"用户 user_id[{user_id}] 请求"
f"用户 user_id[{public_id}] 的公共Cookies 该Cookie使用次数为[{count}]次 ")
return cookies