mirror of
https://github.com/PaiGramTeam/PaiGram.git
synced 2024-11-22 07:07:46 +00:00
✨ 添加公共Cookies服务模块
This commit is contained in:
parent
9e7637203a
commit
0b898d4868
@ -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
92
app/cookies/cache.py
Normal 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
3
app/cookies/error.py
Normal file
@ -0,0 +1,3 @@
|
||||
class CookiesCachePoolExhausted(Exception):
|
||||
def __init__(self):
|
||||
super().__init__("Cookies cache pool is exhausted")
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user