Use SIMNet and Remove genshin.py

Co-authored-by: xtaodada <xtao@xtaolink.cn>
This commit is contained in:
luoshuijs 2023-07-18 17:29:31 +08:00 committed by GitHub
parent 23f08f5639
commit c1012d206b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1476 additions and 1905 deletions

View File

@ -1,7 +1,7 @@
from typing import List, Optional
import genshin
from genshin import GenshinException, InvalidCookies, TooManyRequests, types, Game
from simnet import GenshinClient, Region, Game
from simnet.errors import InvalidCookies, BadRequest as SimnetBadRequest, TooManyRequests
from core.base_service import BaseService
from core.basemodel import RegionEnum
@ -86,15 +86,13 @@ class PublicCookiesService(BaseService):
await self._cache.delete_public_cookies(public_id, region)
continue
if region == RegionEnum.HYPERION:
client = genshin.Client(cookies=cookies.data, game=types.Game.GENSHIN, region=types.Region.CHINESE)
client = GenshinClient(cookies=cookies.data, region=Region.CHINESE)
elif region == RegionEnum.HOYOLAB:
client = genshin.Client(
cookies=cookies.data, game=types.Game.GENSHIN, region=types.Region.OVERSEAS, lang="zh-cn"
)
client = GenshinClient(cookies=cookies.data, region=Region.OVERSEAS, lang="zh-cn")
else:
raise CookieServiceError
try:
if client.cookie_manager.user_id is None:
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:
@ -108,9 +106,9 @@ class PublicCookiesService(BaseService):
await client.get_partial_genshin_user(account.uid)
break
except InvalidCookies as exc:
if exc.retcode in (10001, -100):
if exc.ret_code in (10001, -100):
logger.warning("用户 [%s] Cookies无效", public_id)
elif exc.retcode == 10103:
elif exc.ret_code == 10103:
logger.warning("用户 [%s] Cookies有效但没有绑定到游戏帐户", public_id)
else:
logger.warning("Cookies无效 ")
@ -125,10 +123,10 @@ class PublicCookiesService(BaseService):
await self._repository.update(cookies)
await self._cache.delete_public_cookies(cookies.user_id, region)
continue
except GenshinException as exc:
if "invalid content type" in exc.msg:
except SimnetBadRequest as exc:
if "invalid content type" in exc.message:
raise exc
if exc.retcode == 1034:
if exc.ret_code == 1034:
logger.warning("用户 [%s] 触发验证", public_id)
else:
logger.warning("用户 [%s] 获取账号信息发生错误,错误信息为", public_id)
@ -145,6 +143,8 @@ class PublicCookiesService(BaseService):
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

View File

@ -1,7 +1,9 @@
import json
import re
import time
from typing import Dict, Optional
from typing import Dict, Optional, Union
from httpx import Cookies
from ..base.hyperionrequest import HyperionRequest
from ...utility.devices import devices_methods
@ -37,7 +39,7 @@ class Verify:
"Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
}
def __init__(self, account_id: int = None, cookies: Dict = None):
def __init__(self, account_id: int = None, cookies: Union[Dict, Cookies] = None):
self.account_id = account_id
self.client = HyperionRequest(headers=self.BBS_HEADERS, cookies=cookies)

View File

@ -1,13 +1,13 @@
from genshin.models import GenshinBannerType
from simnet.models.genshin.wish import BannerType
PAIMONMOE_VERSION = 3
UIGF_VERSION = "v2.2"
GACHA_TYPE_LIST = {
GenshinBannerType.NOVICE: "新手祈愿",
GenshinBannerType.PERMANENT: "常驻祈愿",
GenshinBannerType.WEAPON: "武器祈愿",
GenshinBannerType.CHARACTER1: "角色祈愿",
GenshinBannerType.CHARACTER2: "角色祈愿",
BannerType.NOVICE: "新手祈愿",
BannerType.PERMANENT: "常驻祈愿",
BannerType.WEAPON: "武器祈愿",
BannerType.CHARACTER1: "角色祈愿",
BannerType.CHARACTER2: "角色祈愿",
}

View File

@ -5,14 +5,14 @@ import json
from concurrent.futures import ThreadPoolExecutor
from os import PathLike
from pathlib import Path
from typing import Dict, IO, List, Optional, Tuple, Union
from typing import Dict, IO, List, Optional, Tuple, Union, TYPE_CHECKING
import aiofiles
from genshin import AuthkeyTimeout, Client, InvalidAuthkey
from genshin.models import GenshinBannerType
from openpyxl import load_workbook
from simnet import GenshinClient
from simnet.errors import AuthkeyTimeout, InvalidAuthkey
from simnet.models.genshin.wish import BannerType
from core.dependence.assets import AssetsService
from metadata.pool.pool import get_pool_by_id
from metadata.shortname import roleToId, weaponToId
from modules.gacha_log.const import GACHA_TYPE_LIST, PAIMONMOE_VERSION
@ -41,6 +41,10 @@ from modules.gacha_log.models import (
)
from utils.const import PROJECT_ROOT
if TYPE_CHECKING:
from core.dependence.assets import AssetsService
GACHA_LOG_PATH = PROJECT_ROOT.joinpath("data", "apihelper", "gacha_log")
GACHA_LOG_PATH.mkdir(parents=True, exist_ok=True)
@ -104,7 +108,7 @@ class GachaLog:
async def save_gacha_log_info(self, user_id: str, uid: str, info: GachaLogInfo):
"""保存抽卡记录数据
:param user_id: 用户id
:param uid: 原神uid
:param uid: 玩家uid
:param info: 抽卡记录数据
"""
save_path = self.gacha_log_path / f"{user_id}-{uid}.json"
@ -164,20 +168,20 @@ class GachaLog:
def import_data_backend(all_items: List[GachaItem], gacha_log: GachaLogInfo, temp_id_data: Dict) -> int:
new_num = 0
for item_info in all_items:
pool_name = GACHA_TYPE_LIST[GenshinBannerType(int(item_info.gacha_type))]
pool_name = GACHA_TYPE_LIST[BannerType(int(item_info.gacha_type))]
if item_info.id not in temp_id_data[pool_name]:
gacha_log.item_list[pool_name].append(item_info)
temp_id_data[pool_name].append(item_info.id)
new_num += 1
return new_num
async def import_gacha_log_data(self, user_id: int, client: Client, data: dict, verify_uid: bool = True) -> int:
async def import_gacha_log_data(self, user_id: int, player_id: int, data: dict, verify_uid: bool = True) -> int:
new_num = 0
try:
uid = data["info"]["uid"]
if not verify_uid:
uid = client.uid
elif int(uid) != client.uid:
uid = player_id
elif int(uid) != player_id:
raise GachaLogAccountNotFound
try:
import_type = ImportType(data["info"]["export_app"])
@ -218,22 +222,23 @@ class GachaLog:
except Exception as exc:
raise GachaLogException from exc
async def get_gacha_log_data(self, user_id: int, client: Client, authkey: str) -> int:
async def get_gacha_log_data(self, user_id: int, client: GenshinClient, authkey: str) -> int:
"""使用authkey获取抽卡记录数据并合并旧数据
:param user_id: 用户id
:param client: genshin client
:param client: GenshinClient
:param authkey: authkey
:return: 更新结果
"""
new_num = 0
gacha_log, _ = await self.load_history_info(str(user_id), str(client.uid))
gacha_log, _ = await self.load_history_info(str(user_id), str(client.player_id))
if gacha_log.get_import_type == ImportType.PAIMONMOE:
raise GachaLogMixedProvider
# 将唯一 id 放入临时数据中,加快查找速度
temp_id_data = {pool_name: [i.id for i in pool_data] for pool_name, pool_data in gacha_log.item_list.items()}
try:
for pool_id, pool_name in GACHA_TYPE_LIST.items():
async for data in client.wish_history(pool_id, authkey=authkey):
wish_history = await client.wish_history(pool_id.value, authkey=authkey)
for data in wish_history:
item = GachaItem(
id=str(data.id),
name=data.name,
@ -262,7 +267,7 @@ class GachaLog:
i.sort(key=lambda x: (x.time, x.id))
gacha_log.update_time = datetime.datetime.now()
gacha_log.import_type = ImportType.UIGF.value
await self.save_gacha_log_info(str(user_id), str(client.uid), gacha_log)
await self.save_gacha_log_info(str(user_id), str(client.player_id), gacha_log)
return new_num
@staticmethod
@ -281,7 +286,7 @@ class GachaLog:
return False
return True
async def get_all_5_star_items(self, data: List[GachaItem], assets: AssetsService, pool_name: str = "角色祈愿"):
async def get_all_5_star_items(self, data: List[GachaItem], assets: "AssetsService", pool_name: str = "角色祈愿"):
"""
获取所有5星角色
:param data: 抽卡记录
@ -321,7 +326,7 @@ class GachaLog:
return result, count
@staticmethod
async def get_all_4_star_items(data: List[GachaItem], assets: AssetsService):
async def get_all_4_star_items(data: List[GachaItem], assets: "AssetsService"):
"""
获取 no_fout_star
:param data: 抽卡记录
@ -482,16 +487,16 @@ class GachaLog:
return f"{pool_name} · 非"
return pool_name
async def get_analysis(self, user_id: int, client: Client, pool: GenshinBannerType, assets: AssetsService):
async def get_analysis(self, user_id: int, player_id: int, pool: BannerType, assets: "AssetsService"):
"""
获取抽卡记录分析数据
:param user_id: 用户id
:param client: genshin client
:param player_id: 玩家id
:param pool: 池子类型
:param assets: 资源服务
:return: 分析数据
"""
gacha_log, status = await self.load_history_info(str(user_id), str(client.uid))
gacha_log, status = await self.load_history_info(str(user_id), str(player_id))
if not status:
raise GachaLogNotFound
pool_name = GACHA_TYPE_LIST[pool]
@ -502,19 +507,19 @@ class GachaLog:
all_five, no_five_star = await self.get_all_5_star_items(data, assets, pool_name)
all_four, no_four_star = await self.get_all_4_star_items(data, assets)
summon_data = None
if pool == GenshinBannerType.CHARACTER1:
if pool == BannerType.CHARACTER1:
summon_data = self.get_301_pool_data(total, all_five, no_five_star, no_four_star)
pool_name = self.count_fortune(pool_name, summon_data)
elif pool == GenshinBannerType.WEAPON:
elif pool == BannerType.WEAPON:
summon_data = self.get_302_pool_data(total, all_five, all_four, no_five_star, no_four_star)
pool_name = self.count_fortune(pool_name, summon_data, True)
elif pool == GenshinBannerType.PERMANENT:
elif pool == BannerType.PERMANENT:
summon_data = self.get_200_pool_data(total, all_five, all_four, no_five_star, no_four_star)
pool_name = self.count_fortune(pool_name, summon_data)
last_time = data[0].time.strftime("%Y-%m-%d %H:%M")
first_time = data[-1].time.strftime("%Y-%m-%d %H:%M")
return {
"uid": client.uid,
"uid": player_id,
"allNum": total,
"type": pool.value,
"typeName": pool_name,
@ -526,17 +531,17 @@ class GachaLog:
}
async def get_pool_analysis(
self, user_id: int, client: Client, pool: GenshinBannerType, assets: AssetsService, group: bool
self, user_id: int, player_id: int, pool: BannerType, assets: "AssetsService", group: bool
) -> dict:
"""获取抽卡记录分析数据
:param user_id: 用户id
:param client: genshin client
:param player_id: 玩家id
:param pool: 池子类型
:param assets: 资源服务
:param group: 是否群组
:return: 分析数据
"""
gacha_log, status = await self.load_history_info(str(user_id), str(client.uid))
gacha_log, status = await self.load_history_info(str(user_id), str(player_id))
if not status:
raise GachaLogNotFound
pool_name = GACHA_TYPE_LIST[pool]
@ -566,20 +571,20 @@ class GachaLog:
)
pool_data = [i for i in pool_data if i["count"] > 0]
return {
"uid": client.uid,
"uid": player_id,
"typeName": pool_name,
"pool": pool_data[:6] if group else pool_data,
"hasMore": len(pool_data) > 6,
}
async def get_all_five_analysis(self, user_id: int, client: Client, assets: AssetsService) -> dict:
async def get_all_five_analysis(self, user_id: int, player_id: int, assets: "AssetsService") -> dict:
"""获取五星抽卡记录分析数据
:param user_id: 用户id
:param client: genshin client
:param player_id: 玩家id
:param assets: 资源服务
:return: 分析数据
"""
gacha_log, status = await self.load_history_info(str(user_id), str(client.uid))
gacha_log, status = await self.load_history_info(str(user_id), str(player_id))
if not status:
raise GachaLogNotFound
pools = []
@ -607,7 +612,7 @@ class GachaLog:
for up_pool in pools
]
return {
"uid": client.uid,
"uid": player_id,
"typeName": "五星列表",
"pool": pool_data,
"hasMore": False,

View File

@ -47,24 +47,24 @@ class GachaItem(BaseModel):
if item_id := (roleToId(v) or weaponToId(v)):
if item_id not in not_real_roles:
return v
raise ValueError("Invalid name")
raise ValueError(f"Invalid name {v}")
@validator("gacha_type")
def check_gacha_type(cls, v):
if v not in {"100", "200", "301", "302", "400"}:
raise ValueError("gacha_type must be 200, 301, 302 or 400")
raise ValueError(f"gacha_type must be 200, 301, 302 or 400, invalid value: {v}")
return v
@validator("item_type")
def check_item_type(cls, item):
if item not in {"角色", "武器"}:
raise ValueError("error item type")
raise ValueError(f"error item type {item}")
return item
@validator("rank_type")
def check_rank_type(cls, rank):
if rank not in {"5", "4", "3"}:
raise ValueError("error rank type")
raise ValueError(f"error rank type {rank}")
return rank

View File

@ -3,8 +3,9 @@ from pathlib import Path
from typing import Tuple, Optional, List, Dict
import aiofiles
from genshin import Client, AuthkeyTimeout, InvalidAuthkey
from genshin.models import TransactionKind, BaseTransaction
from simnet import GenshinClient
from simnet.errors import AuthkeyTimeout, InvalidAuthkey
from simnet.models.genshin.transaction import TransactionKind, BaseTransaction
from modules.pay_log.error import PayLogAuthkeyTimeout, PayLogInvalidAuthkey, PayLogNotFound
from modules.pay_log.models import PayLog as PayLogModel, BaseInfo
@ -16,6 +17,7 @@ try:
except ImportError:
import json as jsonlib
PAY_LOG_PATH = PROJECT_ROOT.joinpath("data", "apihelper", "pay_log")
PAY_LOG_PATH.mkdir(parents=True, exist_ok=True)
@ -32,7 +34,7 @@ class PayLog:
@staticmethod
async def save_json(path, data: PayLogModel):
async with aiofiles.open(path, "w", encoding="utf-8") as f:
return await f.write(data.json(ensure_ascii=False, indent=4))
return await f.write(data.json(ensure_ascii=False, indent=4, by_alias=True))
def get_file_path(
self,
@ -112,20 +114,21 @@ class PayLog:
async def get_log_data(
self,
user_id: int,
client: Client,
client: GenshinClient,
authkey: str,
) -> int:
"""使用 authkey 获取历史记录数据,并合并旧数据
:param user_id: 用户id
:param client: genshin client
:param client: GenshinClient
:param authkey: authkey
:return: 更新结果
"""
new_num = 0
pay_log, have_old = await self.load_history_info(str(user_id), str(client.uid))
pay_log, have_old = await self.load_history_info(str(user_id), str(client.player_id))
history_ids = [i.id for i in pay_log.list]
try:
async for data in client.transaction_log(TransactionKind.CRYSTAL, authkey=authkey):
transaction_log = await client.transaction_log(authkey=authkey, kind=TransactionKind.CRYSTAL.value)
for data in transaction_log:
if data.id not in history_ids:
pay_log.list.append(data)
new_num += 1
@ -136,7 +139,7 @@ class PayLog:
if new_num > 0 or have_old:
pay_log.list.sort(key=lambda x: (x.time, x.id), reverse=True)
pay_log.info.update_now()
await self.save_pay_log_info(str(user_id), str(client.uid), pay_log)
await self.save_pay_log_info(str(user_id), str(client.player_id), pay_log)
return new_num
@staticmethod
@ -180,13 +183,13 @@ class PayLog:
raise PayLogNotFound
return all_amount, month_datas
async def get_analysis(self, user_id: int, client: Client):
async def get_analysis(self, user_id: int, player_id: int):
"""获取分析数据
:param user_id: 用户id
:param client: genshin client
:param player_id: 玩家id
:return: 分析数据
"""
pay_log, status = await self.load_history_info(str(user_id), str(client.uid))
pay_log, status = await self.load_history_info(str(user_id), str(player_id))
if not status:
raise PayLogNotFound
# 单双倍结晶数
@ -227,7 +230,7 @@ class PayLog:
if price_data[i]["count"] > 0
]
return {
"uid": client.uid,
"uid": player_id,
"datas": datas,
"bar_data": month_datas,
"pie_data": pie_datas,

View File

@ -1,9 +1,8 @@
import datetime
from typing import Any, List
from genshin.models import BaseTransaction
from pydantic import BaseModel, BaseConfig
from simnet.models.genshin.transaction import BaseTransaction
try:
import ujson as jsonlib

View File

@ -1,8 +1,13 @@
from datetime import datetime
from typing import Optional, TYPE_CHECKING
import genshin
from genshin import DataNotPublic, GenshinException, types, AccountNotFound, InvalidCookies
from simnet import GenshinClient, Region
from simnet.errors import (
InvalidCookies,
BadRequest as SimnetBadRequest,
DataNotPublic,
AccountNotFound,
)
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, TelegramObject
from telegram.ext import ConversationHandler, filters
from telegram.helpers import escape_markdown
@ -16,7 +21,6 @@ from core.services.players.models import PlayersDataBase as Player, PlayerInfoSQ
from core.services.players.services import PlayersService, PlayerInfoService
from utils.log import logger
if TYPE_CHECKING:
from telegram import Update
from telegram.ext import ContextTypes
@ -133,27 +137,25 @@ class BindAccountPlugin(Plugin.Conversation):
await message.reply_text("用户查询次数过多,请稍后重试", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
if region == RegionEnum.HYPERION:
client = genshin.Client(cookies=cookies.data, game=types.Game.GENSHIN, region=types.Region.CHINESE)
client = GenshinClient(cookies=cookies.data, region=Region.CHINESE)
elif region == RegionEnum.HOYOLAB:
client = genshin.Client(
cookies=cookies.data, game=types.Game.GENSHIN, region=types.Region.OVERSEAS, lang="zh-cn"
)
client = GenshinClient(cookies=cookies.data, region=Region.OVERSEAS, lang="zh-cn")
else:
return ConversationHandler.END
try:
record_card = await client.get_record_card(account_id)
if record_card is None:
await message.reply_text("请在设置展示主界面添加原神", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
except DataNotPublic:
await message.reply_text("角色未公开", reply_markup=ReplyKeyboardRemove())
logger.warning("获取账号信息发生错误 %s 账户信息未公开", account_id)
return ConversationHandler.END
except GenshinException as exc:
except SimnetBadRequest as exc:
await message.reply_text("获取账号信息发生错误", reply_markup=ReplyKeyboardRemove())
logger.error("获取账号信息发生错误")
logger.exception(exc)
return ConversationHandler.END
if record_card.game != types.Game.GENSHIN:
await message.reply_text("角色信息查询返回非原神游戏信息,请设置展示主界面为原神", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
player_info = await self.players_service.get(
user.id, player_id=record_card.uid, region=bind_account_plugin_data.region
)
@ -197,11 +199,9 @@ class BindAccountPlugin(Plugin.Conversation):
await message.reply_text("用户查询次数过多,请稍后重试", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
if region == RegionEnum.HYPERION:
client = genshin.Client(cookies=cookies.data, game=types.Game.GENSHIN, region=types.Region.CHINESE)
client = GenshinClient(cookies=cookies.data, region=Region.CHINESE)
elif region == RegionEnum.HOYOLAB:
client = genshin.Client(
cookies=cookies.data, game=types.Game.GENSHIN, region=types.Region.OVERSEAS, lang="zh-cn"
)
client = GenshinClient(cookies=cookies.data, region=Region.OVERSEAS, lang="zh-cn")
else:
return ConversationHandler.END
try:
@ -218,8 +218,8 @@ class BindAccountPlugin(Plugin.Conversation):
await self.public_cookies_service.undo(user.id, cookies, CookiesStatusEnum.INVALID_COOKIES)
await message.reply_text("出错了呜呜呜 ~ 请稍后重试")
return ConversationHandler.END
except GenshinException as exc:
if exc.retcode == 1034:
except SimnetBadRequest as exc:
if exc.ret_code == 1034:
await self.public_cookies_service.undo(user.id)
await message.reply_text("出错了呜呜呜 ~ 请稍后重试")
return ConversationHandler.END
@ -230,6 +230,8 @@ class BindAccountPlugin(Plugin.Conversation):
except ValueError:
await message.reply_text("ID 格式有误,请检查", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
finally:
await client.shutdown()
player_info = await self.players_service.get(
user.id, player_id=player_id, region=bind_account_plugin_data.region
)

View File

@ -1,10 +1,10 @@
from datetime import datetime
from typing import Dict, Optional
import genshin
from arkowrapper import ArkoWrapper
from genshin import DataNotPublic, GenshinException, InvalidCookies, types
from genshin.models import GenshinAccount
from simnet import GenshinClient, Region
from simnet.errors import DataNotPublic, InvalidCookies, BadRequest as SimnetBadRequest
from simnet.models.lab.record import Account
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, TelegramObject, Update
from telegram.constants import ParseMode
from telegram.ext import CallbackContext, ConversationHandler, filters
@ -34,7 +34,7 @@ class AccountCookiesPluginData(TelegramObject):
cookies: dict = {}
account_id: int = 0
# player_id: int = 0
genshin_account: Optional[GenshinAccount] = None
genshin_account: Optional[Account] = None
def reset(self):
self.player = None
@ -214,76 +214,78 @@ class AccountCookiesPlugin(Plugin.Conversation):
account_cookies_plugin_data: AccountCookiesPluginData = context.chat_data.get("account_cookies_plugin_data")
cookies = CookiesModel(**account_cookies_plugin_data.cookies)
if account_cookies_plugin_data.region == RegionEnum.HYPERION:
client = genshin.Client(cookies=cookies.to_dict(), region=types.Region.CHINESE)
region = Region.CHINESE
elif account_cookies_plugin_data.region == RegionEnum.HOYOLAB:
client = genshin.Client(cookies=cookies.to_dict(), region=types.Region.OVERSEAS)
region = Region.OVERSEAS
else:
logger.error("用户 %s[%s] region 异常", user.full_name, user.id)
await message.reply_text("数据错误", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
if not cookies.check():
await message.reply_text("检测到Cookie不完整可能会出现问题。", reply_markup=ReplyKeyboardRemove())
try:
if client.cookie_manager.user_id is None and cookies.is_v2:
logger.info("检测到用户 %s[%s] 使用 V2 Cookie 正在尝试获取 account_id", user.full_name, user.id)
if client.region == types.Region.CHINESE:
account_info = await client.get_hoyolab_user()
account_id = account_info.hoyolab_id
account_cookies_plugin_data.account_id = account_id
cookies.set_v2_uid(account_id)
logger.success("获取用户 %s[%s] account_id[%s] 成功", user.full_name, user.id, account_id)
else:
logger.warning("用户 %s[%s] region[%s] 也许是不正确的", user.full_name, user.id, client.region.name)
else:
account_cookies_plugin_data.account_id = client.cookie_manager.user_id
genshin_accounts = await client.genshin_accounts()
except DataNotPublic:
logger.info("用户 %s[%s] 账号疑似被注销", user.full_name, user.id)
await message.reply_text("账号疑似被注销,请检查账号状态", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
except InvalidCookies:
logger.info("用户 %s[%s] Cookies已经过期", user.full_name, user.id)
await message.reply_text(
"获取账号信息失败返回Cookies已经过期请尝试在无痕浏览器中登录获取Cookies。", reply_markup=ReplyKeyboardRemove()
)
return ConversationHandler.END
except GenshinException as exc:
logger.info("用户 %s[%s] 获取账号信息发生错误 [%s]%s", user.full_name, user.id, exc.retcode, exc.original)
await message.reply_text(
f"获取账号信息发生错误,错误信息为 {exc.original}请检查Cookie或者账号是否正常", reply_markup=ReplyKeyboardRemove()
)
return ConversationHandler.END
except AccountIdNotFound:
logger.info("用户 %s[%s] 无法获取账号ID", user.full_name, user.id)
await message.reply_text("无法获取账号ID请检查Cookie是否正常", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
except (AttributeError, ValueError) as exc:
logger.warning("用户 %s[%s] Cookies错误", user.full_name, user.id)
logger.debug("用户 %s[%s] Cookies错误", user.full_name, user.id, exc_info=exc)
await message.reply_text("Cookies错误请检查是否正确", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
if cookies.login_ticket is not None:
async with GenshinClient(cookies=cookies.to_dict(), region=region) as client:
check_cookie = cookies.check()
if cookies.login_ticket is not None:
try:
cookies.cookie_token = await client.get_cookie_token_by_login_ticket()
cookies.account_id = cookies.account_id
cookies.ltuid = cookies.account_id
logger.success("用户 %s[%s] 绑定时获取 cookie_token 成功", user.full_name, user.id)
cookies.stoken = await client.get_stoken_by_login_ticket()
logger.success("用户 %s[%s] 绑定时获取 stoken 成功", user.full_name, user.id)
cookies.ltoken = await client.get_ltoken_by_stoken()
logger.success("用户 %s[%s] 绑定时获取 ltoken 成功", user.full_name, user.id)
check_cookie = True
except SimnetBadRequest as exc:
logger.warning("用户 %s[%s] 获取账号信息发生错误 [%s]%s", user.full_name, user.id, exc.ret_code, exc.original)
except Exception as exc:
logger.error("绑定时获取新Cookie失败 [%s]", (str(exc)))
finally:
cookies.login_ticket = None
cookies.login_uid = None
if not check_cookie:
await message.reply_text("检测到Cookie不完整可能会出现问题。", reply_markup=ReplyKeyboardRemove())
try:
if cookies.login_ticket is not None:
auth_client = AuthClient(cookies=cookies)
if await auth_client.get_stoken_by_login_ticket():
logger.success("用户 %s[%s] 绑定时获取 stoken 成功", user.full_name, user.id)
if await auth_client.get_cookie_token_by_stoken():
logger.success("用户 %s[%s] 绑定时获取 cookie_token 成功", user.full_name, user.id)
if await auth_client.get_ltoken_by_stoken():
logger.success("用户 %s[%s] 绑定时获取 ltoken 成功", user.full_name, user.id)
auth_client.cookies.remove_v2()
except Exception as exc: # pylint: disable=W0703
logger.error("绑定时获取新Cookie失败 [%s]", (str(exc)))
finally:
if cookies.user_id is not None:
account_cookies_plugin_data.account_id = cookies.user_id
cookies.login_ticket = None
cookies.login_uid = None
if client.account_id is None and cookies.is_v2:
logger.info("检测到用户 %s[%s] 使用 V2 Cookie 正在尝试获取 account_id", user.full_name, user.id)
if client.region == Region.CHINESE:
account_info = await client.get_user_info()
account_id = account_info.accident_id
account_cookies_plugin_data.account_id = account_id
cookies.set_v2_uid(account_id)
logger.success("获取用户 %s[%s] account_id[%s] 成功", user.full_name, user.id, account_id)
else:
logger.warning("用户 %s[%s] region[%s] 也许是不正确的", user.full_name, user.id, client.region.name)
else:
account_cookies_plugin_data.account_id = client.account_id
genshin_accounts = await client.get_genshin_accounts()
except DataNotPublic:
logger.info("用户 %s[%s] 账号疑似被注销", user.full_name, user.id)
await message.reply_text("账号疑似被注销,请检查账号状态", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
except InvalidCookies:
logger.info("用户 %s[%s] Cookies已经过期", user.full_name, user.id)
await message.reply_text(
"获取账号信息失败返回Cookies已经过期请尝试在无痕浏览器中登录获取Cookies。", reply_markup=ReplyKeyboardRemove()
)
return ConversationHandler.END
except SimnetBadRequest as exc:
logger.info("用户 %s[%s] 获取账号信息发生错误 [%s]%s", user.full_name, user.id, exc.ret_code, exc.original)
await message.reply_text(
f"获取账号信息发生错误,错误信息为 {exc.original}请检查Cookie或者账号是否正常", reply_markup=ReplyKeyboardRemove()
)
return ConversationHandler.END
except AccountIdNotFound:
logger.info("用户 %s[%s] 无法获取账号ID", user.full_name, user.id)
await message.reply_text("无法获取账号ID请检查Cookie是否正常", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
except (AttributeError, ValueError) as exc:
logger.warning("用户 %s[%s] Cookies错误", user.full_name, user.id)
logger.debug("用户 %s[%s] Cookies错误", user.full_name, user.id, exc_info=exc)
await message.reply_text("Cookies错误请检查是否正确", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
if account_cookies_plugin_data.account_id is None:
await message.reply_text("无法获取账号ID请检查Cookie是否正确或请稍后重试")
return ConversationHandler.END
genshin_account: Optional[GenshinAccount] = None
genshin_account: Optional[Account] = None
level: int = 0
# todo : 多账号绑定
for temp in genshin_accounts:

View File

@ -7,6 +7,7 @@ from telegram.helpers import escape_markdown
from core.config import config
from core.plugin import handler, Plugin
from core.services.players import PlayersService
from plugins.tools.challenge import ChallengeSystem, ChallengeSystemException
from plugins.tools.genshin import PlayerNotFoundError, CookiesNotFoundError, GenshinHelper
from plugins.tools.sign import SignSystem, NeedChallenge
@ -14,10 +15,17 @@ from utils.log import logger
class StartPlugin(Plugin):
def __init__(self, sign_system: SignSystem, challenge_system: ChallengeSystem, genshin_helper: GenshinHelper):
def __init__(
self,
player: PlayersService,
sign_system: SignSystem,
challenge_system: ChallengeSystem,
genshin_helper: GenshinHelper,
):
self.challenge_system = challenge_system
self.sign_system = sign_system
self.genshin_helper = genshin_helper
self.players_service = player
@handler.command("start", block=False)
async def start(self, update: Update, context: CallbackContext) -> None:
@ -77,14 +85,14 @@ class StartPlugin(Plugin):
async def process_sign_validate(self, message: Message, user: User, validate: str):
try:
client = await self.genshin_helper.get_genshin_client(user.id)
await message.reply_chat_action(ChatAction.TYPING)
_, challenge = await self.sign_system.get_challenge(client.uid)
if not challenge:
await message.reply_text("验证请求已过期。", allow_sending_without_reply=True)
return
sign_text = await self.sign_system.start_sign(client, challenge=challenge, validate=validate)
await message.reply_text(sign_text, allow_sending_without_reply=True)
async with self.genshin_helper.genshin(user.id) as client:
await message.reply_chat_action(ChatAction.TYPING)
_, challenge = await self.sign_system.get_challenge(client.player_id)
if not challenge:
await message.reply_text("验证请求已过期。", allow_sending_without_reply=True)
return
sign_text = await self.sign_system.start_sign(client, challenge=challenge, validate=validate)
await message.reply_text(sign_text, allow_sending_without_reply=True)
except (PlayerNotFoundError, CookiesNotFoundError):
logger.warning("用户 %s[%s] 账号信息未找到", user.full_name, user.id)
except NeedChallenge:
@ -119,13 +127,13 @@ class StartPlugin(Plugin):
)
async def get_sign_button(self, message: Message, user: User, bot_username: str):
try:
client = await self.genshin_helper.get_genshin_client(user.id)
await message.reply_chat_action(ChatAction.TYPING)
button = await self.sign_system.get_challenge_button(bot_username, client.uid, user.id, callback=False)
if not button:
await message.reply_text("验证请求已过期。", allow_sending_without_reply=True)
return
await message.reply_text("请尽快点击下方按钮进行验证。", allow_sending_without_reply=True, reply_markup=button)
except (PlayerNotFoundError, CookiesNotFoundError):
player = await self.players_service.get_player(user.id)
if player is None:
logger.warning("用户 %s[%s] 账号信息未找到", user.full_name, user.id)
return
await message.reply_chat_action(ChatAction.TYPING)
button = await self.sign_system.get_challenge_button(bot_username, player.player_id, user.id, callback=False)
if not button:
await message.reply_text("验证请求已过期。", allow_sending_without_reply=True)
return
await message.reply_text("请尽快点击下方按钮进行验证。", allow_sending_without_reply=True, reply_markup=button)

View File

@ -6,8 +6,9 @@ from functools import lru_cache, partial
from typing import Any, Coroutine, List, Match, Optional, Tuple, Union
from arkowrapper import ArkoWrapper
from genshin import Client, GenshinException
from pytz import timezone
from simnet import GenshinClient
from simnet.errors import BadRequest as SimnetBadRequest
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Message, Update
from telegram.constants import ChatAction, ParseMode
from telegram.ext import CallbackContext, filters
@ -19,7 +20,7 @@ from core.services.cookies.error import TooManyRequestPublicCookies
from core.services.template.models import RenderGroupResult, RenderResult
from core.services.template.services import TemplateService
from metadata.genshin import game_id_to_role_id
from plugins.tools.genshin import GenshinHelper, CookiesNotFoundError, PlayerNotFoundError
from plugins.tools.genshin import PlayerNotFoundError, CookiesNotFoundError, GenshinHelper
from utils.helpers import async_re_sub
from utils.log import logger
@ -29,7 +30,6 @@ try:
except ImportError:
import json as jsonlib
TZ = timezone("Asia/Shanghai")
cmd_pattern = r"(?i)^/abyss\s*((?:\d+)|(?:all))?\s*(pre)?"
msg_pattern = r"^深渊数据((?:查询)|(?:总览))(上期)?\D?(\d*)?.*?$"
@ -129,12 +129,38 @@ class AbyssPlugin(Plugin):
extra={"markup": True},
)
await message.reply_chat_action(ChatAction.TYPING)
reply_text: Optional[Message] = None
if total:
reply_text = await message.reply_text("派蒙需要时间整理深渊数据,还请耐心等待哦~")
try:
try:
client = await self.helper.get_genshin_client(user.id)
uid = client.uid
async with self.helper.genshin(user.id) as client:
images = await self.get_rendered_pic(client, client.player_id, floor, total, previous)
except CookiesNotFoundError:
client, uid = await self.helper.get_public_genshin_client(user.id)
# Cookie 不存在使用公开接口
async with self.helper.public_genshin(user.id) as client:
images = await self.get_rendered_pic(client, uid, floor, total, previous)
except SimnetBadRequest as exc:
if exc.ret_code == 1034 and client.player_id != uid:
await message.reply_text("出错了呜呜呜 ~ 请稍后重试")
return
raise exc
except AbyssUnlocked: # 若深渊未解锁
await message.reply_text("还未解锁深渊哦~")
return
except NoMostKills: # 若深渊还未挑战
await message.reply_text("还没有挑战本次深渊呢,咕咕咕~")
return
except AbyssNotFoundError:
await message.reply_text("无法查询玩家挑战队伍详情,只能查询统计详情哦~")
return
except IndexError: # 若深渊为挑战此层
await message.reply_text("还没有挑战本层呢,咕咕咕~")
return
except PlayerNotFoundError: # 若未找到账号
buttons = [[InlineKeyboardButton("点我绑定账号", url=create_deep_linked_url(context.bot.username, "set_uid"))]]
if filters.ChatType.GROUPS.filter(message):
@ -152,41 +178,12 @@ class AbyssPlugin(Plugin):
self.add_delete_message_job(reply_message)
self.add_delete_message_job(message)
return
finally:
if reply_text is not None:
await reply_text.delete()
async def reply_message_func(content: str) -> None:
_user = await client.get_genshin_user(uid)
_reply_msg = await message.reply_text(
f"旅行者 {_user.info.nickname}(<code>{uid}</code>) {content}", parse_mode=ParseMode.HTML
)
reply_text: Optional[Message] = None
if total:
reply_text = await message.reply_text("派蒙需要时间整理深渊数据,还请耐心等待哦~")
await message.reply_chat_action(ChatAction.TYPING)
try:
images = await self.get_rendered_pic(client, uid, floor, total, previous)
except GenshinException as exc:
if exc.retcode == 1034 and client.uid != uid:
await message.reply_text("出错了呜呜呜 ~ 请稍后重试")
return
raise exc
except AbyssUnlocked: # 若深渊未解锁
await reply_message_func("还未解锁深渊哦~")
return
except NoMostKills: # 若深渊还未挑战
await reply_message_func("还没有挑战本次深渊呢,咕咕咕~")
return
except AbyssNotFoundError:
await reply_message_func("无法查询玩家挑战队伍详情,只能查询统计详情哦~")
return
except IndexError: # 若深渊为挑战此层
await reply_message_func("还没有挑战本层呢,咕咕咕~")
return
if images is None:
await reply_message_func(f"还没有第 {floor} 层的挑战数据")
await message.reply_text(f"还没有第 {floor} 层的挑战数据")
return
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
@ -196,13 +193,10 @@ class AbyssPlugin(Plugin):
message, allow_sending_without_reply=True, write_timeout=60
)
if reply_text is not None:
await reply_text.delete()
logger.info("用户 %s[%s] [bold]深渊挑战数据[/bold]: 成功发送图片", user.full_name, user.id, extra={"markup": True})
async def get_rendered_pic(
self, client: Client, uid: int, floor: int, total: bool, previous: bool
self, client: GenshinClient, uid: int, floor: int, total: bool, previous: bool
) -> Union[
Tuple[
Union[BaseException, Any],
@ -233,7 +227,7 @@ class AbyssPlugin(Plugin):
return value.astimezone(TZ).strftime("%Y-%m-%d %H:%M:%S")
return value
abyss_data = await client.get_spiral_abyss(uid, previous=previous, lang="zh-cn")
abyss_data = await client.get_genshin_spiral_abyss(uid, previous=previous, lang="zh-cn")
if not abyss_data.unlocked:
raise AbyssUnlocked()

View File

@ -1,89 +0,0 @@
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.constants import ChatAction
from telegram.ext import CallbackContext, filters
from telegram.helpers import create_deep_linked_url
from core.dependence.assets import AssetsService
from core.plugin import Plugin, handler
from core.services.template.services import TemplateService
from metadata.shortname import roleToId
from modules.apihelper.client.components.abyss import AbyssTeam as AbyssTeamClient
from plugins.tools.genshin import GenshinHelper, CookiesNotFoundError, PlayerNotFoundError
from utils.log import logger
__all__ = ("AbyssTeamPlugin",)
class AbyssTeamPlugin(Plugin):
"""深境螺旋推荐配队查询"""
def __init__(
self,
template: TemplateService,
helper: GenshinHelper,
assets_service: AssetsService,
):
self.template_service = template
self.helper = helper
self.team_data = AbyssTeamClient()
self.assets_service = assets_service
@handler.command("abyss_team", block=False)
@handler.message(filters.Regex("^深渊推荐配队(.*)"), block=False)
async def command_start(self, update: Update, context: CallbackContext) -> None:
user = update.effective_user
message = update.effective_message
logger.info("用户 %s[%s] 查深渊推荐配队命令请求", user.full_name, user.id)
try:
client = await self.helper.get_genshin_client(user.id)
except (CookiesNotFoundError, PlayerNotFoundError):
buttons = [[InlineKeyboardButton("点我绑定账号", url=create_deep_linked_url(context.bot.username, "set_cookie"))]]
if filters.ChatType.GROUPS.filter(message):
reply_message = await message.reply_text(
"未查询到您所绑定的账号信息,请先私聊派蒙绑定账号", reply_markup=InlineKeyboardMarkup(buttons)
)
self.add_delete_message_job(reply_message, delay=30)
self.add_delete_message_job(message, delay=30)
else:
await message.reply_text("未查询到您所绑定的账号信息,请先绑定账号", reply_markup=InlineKeyboardMarkup(buttons))
return
await message.reply_chat_action(ChatAction.TYPING)
team_data = await self.team_data.get_data()
# 尝试获取用户已绑定的原神账号信息
characters = await client.get_genshin_characters(client.uid)
user_data = [character.name for character in characters]
team_data.sort(user_data)
random_team = team_data.random_team()
abyss_teams_data = {"uid": client.uid, "version": team_data.version, "teams": []}
for i in random_team:
team = {
"up": [],
"up_rate": f"{i.up.rate * 100: .2f}%",
"down": [],
"down_rate": f"{i.down.rate * 100: .2f}%",
}
for lane in ["up", "down"]:
for member in getattr(i, lane).formation:
name = member.name
temp = {
"icon": (await self.assets_service.avatar(roleToId(name.replace("旅行者", ""))).icon()).as_uri(),
"name": name,
"star": member.star,
"hava": (name in user_data) if user_data else True,
}
team[lane].append(temp)
abyss_teams_data["teams"].append(team)
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
render_result = await self.template_service.render(
"genshin/abyss_team/abyss_team.jinja2",
abyss_teams_data,
{"width": 785, "height": 800},
full_page=True,
query_selector=".bg-contain",
)
await render_result.reply_photo(message, filename=f"abyss_team_{user.id}.png", allow_sending_without_reply=True)

View File

@ -1,9 +1,11 @@
import asyncio
from typing import List, Optional, Sequence, TYPE_CHECKING
from genshin import Client, GenshinException, InvalidCookies
from genshin.models import CalculatorCharacterDetails, CalculatorTalent, Character
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, User
from simnet import GenshinClient
from simnet.errors import BadRequest as SimnetBadRequest, InvalidCookies
from simnet.models.genshin.calculator import CalculatorTalent, CalculatorCharacterDetails
from simnet.models.genshin.chronicle.characters import Character
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, User
from telegram.constants import ChatAction, ParseMode
from telegram.ext import filters
from telegram.helpers import create_deep_linked_url
@ -17,10 +19,11 @@ from core.services.template.models import FileType
from core.services.template.services import TemplateService
from metadata.genshin import AVATAR_DATA
from modules.wiki.base import Model
from plugins.tools.genshin import CharacterDetails, CookiesNotFoundError, GenshinHelper, PlayerNotFoundError
from plugins.tools.genshin import CharacterDetails, PlayerNotFoundError, CookiesNotFoundError, GenshinHelper
from utils.log import logger
if TYPE_CHECKING:
from telegram import Update
from telegram.ext import ContextTypes
@ -67,7 +70,9 @@ class AvatarListPlugin(Plugin):
self.player_service = player_service
self.player_info_service = player_info_service
async def get_user_client(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> Optional[Client]:
async def get_user_client(
self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"
) -> Optional["GenshinClient"]:
message = update.effective_message
user = update.effective_user
try:
@ -99,7 +104,7 @@ class AvatarListPlugin(Plugin):
reply_markup=InlineKeyboardMarkup(buttons),
)
async def get_avatar_data(self, character: Character, client: Client) -> Optional["AvatarData"]:
async def get_avatar_data(self, character: Character, client: "GenshinClient") -> Optional["AvatarData"]:
detail = await self.character_details.get_character_details(client, character)
if detail is None:
return None
@ -136,7 +141,7 @@ class AvatarListPlugin(Plugin):
)
async def get_avatars_data(
self, characters: Sequence[Character], client: Client, max_length: int = None
self, characters: Sequence[Character], client: "GenshinClient", max_length: int = None
) -> List["AvatarData"]:
async def _task(c):
return await self.get_avatar_data(c, client)
@ -201,31 +206,33 @@ class AvatarListPlugin(Plugin):
await message.reply_chat_action(ChatAction.TYPING)
try:
characters = await client.get_genshin_characters(client.uid)
characters = await client.get_genshin_characters(client.player_id)
avatar_datas: List[AvatarData] = await self.get_avatars_data(
characters, client, None if all_avatars else 20
)
except InvalidCookies as exc:
await notice.delete()
await client.get_genshin_user(client.uid)
logger.warning("用户 %s[%s] 无法请求角色数数据 API返回信息为 [%s]%s", user.full_name, user.id, exc.retcode, exc.original)
await client.get_genshin_user(client.player_id)
logger.warning("用户 %s[%s] 无法请求角色数数据 API返回信息为 [%s]%s", user.full_name, user.id, exc.ret_code, exc.original)
reply_message = await message.reply_text("出错了呜呜呜 ~ 当前访问令牌无法请求角色数数据请尝试重新获取Cookie。")
if filters.ChatType.GROUPS.filter(message):
self.add_delete_message_job(reply_message, delay=30)
self.add_delete_message_job(message, delay=30)
return
except GenshinException as e:
except SimnetBadRequest as e:
await notice.delete()
if e.retcode == -502002:
if e.ret_code == -502002:
reply_message = await message.reply_html("请先在米游社中使用一次<b>养成计算器</b>后再使用此功能~")
self.add_delete_message_job(reply_message, delay=20)
return
raise e
finally:
await client.shutdown()
name_card, avatar, nickname, rarity = await self.get_final_data(client.uid, user)
name_card, avatar, nickname, rarity = await self.get_final_data(client.player_id, user)
render_data = {
"uid": client.uid, # 玩家uid
"uid": client.player_id, # 玩家uid
"nickname": nickname, # 玩家昵称
"avatar": avatar, # 玩家头像
"rarity": rarity, # 玩家头像对应的角色星级

View File

@ -1,13 +1,13 @@
import re
from datetime import datetime
from typing import List, Optional
from typing import List, Optional, TYPE_CHECKING
from genshin import Client, GenshinException
from genshin.client.routes import Route
from genshin.utility import recognize_genshin_server
from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton
from simnet.client.routes import Route
from simnet.errors import BadRequest as SimnetBadRequest
from simnet.utils.player import recognize_genshin_server, recognize_genshin_game_biz
from telegram import InlineKeyboardMarkup, InlineKeyboardButton
from telegram.constants import ParseMode
from telegram.ext import filters, MessageHandler, CommandHandler, CallbackContext
from telegram.ext import filters, MessageHandler, CommandHandler
from telegram.helpers import create_deep_linked_url
from core.basemodel import RegionEnum
@ -17,10 +17,14 @@ from core.services.users.services import UserService
from metadata.genshin import AVATAR_DATA
from metadata.shortname import roleToId, roleToName
from modules.apihelper.client.components.calendar import Calendar
from plugins.tools.genshin import GenshinHelper, CookiesNotFoundError, PlayerNotFoundError
from utils.genshin import fetch_hk4e_token_by_cookie, recognize_genshin_game_biz
from plugins.tools.genshin import PlayerNotFoundError, CookiesNotFoundError, GenshinHelper
from utils.log import logger
if TYPE_CHECKING:
from simnet import GenshinClient
from telegram import Update
from telegram.ext import ContextTypes
BIRTHDAY_URL = Route(
"https://hk4e-api.mihoyo.com/event/birthdaystar/account/post_my_draw",
)
@ -61,7 +65,7 @@ class BirthdayPlugin(Plugin):
return (self.birthday_list.get(key, [])).copy()
@handler.command(command="birthday", block=False)
async def command_start(self, update: Update, context: CallbackContext) -> None:
async def command_start(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
message = update.effective_message
user = update.effective_user
key = (
@ -113,20 +117,20 @@ class BirthdayPlugin(Plugin):
self.add_delete_message_job(reply_message)
@staticmethod
async def get_card(client: Client, role_id: int) -> None:
async def get_card(client: "GenshinClient", role_id: int) -> None:
"""领取画片"""
url = BIRTHDAY_URL.get_url()
params = {
"game_biz": recognize_genshin_game_biz(client.uid),
"game_biz": recognize_genshin_game_biz(client.player_id),
"lang": "zh-cn",
"badge_uid": client.uid,
"badge_region": recognize_genshin_server(client.uid),
"badge_uid": client.player_id,
"badge_region": recognize_genshin_server(client.player_id),
"activity_id": "20220301153521",
}
json = {
"role_id": role_id,
}
await client.cookie_manager.request(url, method="POST", params=params, json=json)
await client.request_lab(url, method="POST", params=params, data=json)
@staticmethod
def role_to_id(name: str) -> Optional[int]:
@ -136,7 +140,7 @@ class BirthdayPlugin(Plugin):
@handler(CommandHandler, command="birthday_card", block=False)
@handler(MessageHandler, filters=filters.Regex("^领取角色生日画片$"), block=False)
async def command_birthday_card_start(self, update: Update, context: CallbackContext) -> None:
async def command_birthday_card_start(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
message = update.effective_message
user = update.effective_user
logger.info("用户 %s[%s] 领取生日画片命令请求", user.full_name, user.id)
@ -148,7 +152,28 @@ class BirthdayPlugin(Plugin):
self.add_delete_message_job(reply_message)
return
try:
client = await self.helper.get_genshin_client(user.id)
async with self.helper.genshin(user.id) as client:
if client.region == RegionEnum.HOYOLAB:
text = "此功能当前只支持国服账号哦~"
else:
game_biz = recognize_genshin_game_biz(client.player_id)
region = recognize_genshin_server(client.player_id)
await client.get_hk4e_token_by_cookie_token(game_biz, region)
for name in today_list.copy():
if role_id := self.role_to_id(name):
try:
await self.get_card(client, role_id)
except SimnetBadRequest as e:
if e.ret_code in {-512008, -512009}: # 未过生日、已领取过
today_list.remove(name)
if today_list:
text = f"成功领取了 {''.join(today_list)} 的生日画片~"
else:
text = "没有领取到生日画片哦 ~ 可能是已经领取过了"
reply_message = await message.reply_text(text)
if filters.ChatType.GROUPS.filter(reply_message):
self.add_delete_message_job(message)
self.add_delete_message_job(reply_message)
except (CookiesNotFoundError, PlayerNotFoundError):
buttons = [[InlineKeyboardButton("点我绑定账号", url=create_deep_linked_url(context.bot.username, "set_cookie"))]]
if filters.ChatType.GROUPS.filter(message):
@ -166,22 +191,3 @@ class BirthdayPlugin(Plugin):
reply_markup=InlineKeyboardMarkup(buttons),
)
return
if client.region == RegionEnum.HOYOLAB:
text = "此功能当前只支持国服账号哦~"
else:
await fetch_hk4e_token_by_cookie(client)
for name in today_list.copy():
if role_id := self.role_to_id(name):
try:
await self.get_card(client, role_id)
except GenshinException as e:
if e.retcode in {-512008, -512009}: # 未过生日、已领取过
today_list.remove(name)
if today_list:
text = f"成功领取了 {''.join(today_list)} 的生日画片~"
else:
text = "没有领取到生日画片哦 ~ 可能是已经领取过了"
reply_message = await message.reply_text(text)
if filters.ChatType.GROUPS.filter(reply_message):
self.add_delete_message_job(message)
self.add_delete_message_job(reply_message)

View File

@ -1,5 +1,4 @@
import asyncio
import contextlib
import os
import re
from asyncio import Lock
@ -9,29 +8,38 @@ from functools import partial
from multiprocessing import Value
from pathlib import Path
from ssl import SSLZeroReturnError
from typing import Any, Dict, Iterable, Iterator, List, Literal, Optional, Tuple
from typing import Any, Dict, Iterable, Iterator, List, Literal, Optional, Tuple, TYPE_CHECKING
import ujson as json
from aiofiles import open as async_open
from arkowrapper import ArkoWrapper
from bs4 import BeautifulSoup
from genshin import Client, GenshinException, InvalidCookies
from genshin.models import Character
from httpx import AsyncClient, HTTPError
from pydantic import BaseModel
from telegram import Message, Update, User
from simnet.errors import InvalidCookies, BadRequest as SimnetBadRequest
from simnet.models.genshin.chronicle.characters import Character
from telegram import Message, User
from telegram.constants import ChatAction, ParseMode
from telegram.error import RetryAfter, TimedOut
from telegram.ext import CallbackContext
from core.dependence.assets import AssetsCouldNotFound, AssetsService, AssetsServiceType
from core.plugin import Plugin, handler
from core.services.template.models import FileType, RenderGroupResult
from core.services.template.services import TemplateService
from metadata.genshin import AVATAR_DATA, HONEY_DATA
from plugins.tools.genshin import GenshinHelper, PlayerNotFoundError, CookiesNotFoundError, CharacterDetails
from plugins.tools.genshin import CharacterDetails, PlayerNotFoundError, CookiesNotFoundError, GenshinHelper
from utils.log import logger
try:
import ujson as jsonlib
except ImportError:
import json as jsonlib
if TYPE_CHECKING:
from telegram import Update
from telegram.ext import ContextTypes
from simnet import GenshinClient
INTERVAL = 1
DATA_TYPE = Dict[str, List[List[str]]]
@ -126,24 +134,24 @@ class DailyMaterial(Plugin):
asyncio.create_task(task_daily()) # 创建后台任务
if not data and DATA_FILE_PATH.exists(): # 若存在,则读取至内存中
async with async_open(DATA_FILE_PATH) as file:
data = json.loads(await file.read())
data = jsonlib.loads(await file.read())
self.data = data
async def _get_skills_data(self, client: Client, character: Character) -> Optional[List[int]]:
async def _get_skills_data(self, client: "GenshinClient", character: Character) -> Optional[List[int]]:
detail = await self.character_details.get_character_details(client, character)
if detail is None:
return None
talents = [t for t in detail.talents if t.type in ["attack", "skill", "burst"]]
return [t.level for t in talents]
async def _get_data_from_user(self, user: User) -> Tuple[Optional[Client], Dict[str, List[Any]]]:
async def _get_data_from_user(self, user: User) -> Tuple[Optional["GenshinClient"], Dict[str, List[Any]]]:
"""获取已经绑定的账号的角色、武器信息"""
user_data = {"avatar": [], "weapon": []}
try:
logger.debug("尝试获取已绑定的原神账号")
client = await self.helper.get_genshin_client(user.id)
logger.debug("获取账号数据成功: UID=%s", client.uid)
characters = await client.get_genshin_characters(client.uid)
logger.debug("获取账号数据成功: UID=%s", client.player_id)
characters = await client.get_genshin_characters(client.player_id)
for character in characters:
if character.name == "旅行者": # 跳过主角
continue
@ -153,7 +161,7 @@ class DailyMaterial(Plugin):
ItemData(
id=cid,
name=character.name,
rarity=character.rarity,
rarity=int(character.rarity),
level=character.level,
constellation=character.constellation,
gid=character.id,
@ -187,7 +195,7 @@ class DailyMaterial(Plugin):
return None, user_data
@handler.command("daily_material", block=False)
async def daily_material(self, update: Update, context: CallbackContext):
async def daily_material(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"):
user = update.effective_user
message = update.effective_message
args = self.get_args(context)
@ -240,7 +248,7 @@ class DailyMaterial(Plugin):
client, user_data = await self._get_data_from_user(user)
await message.reply_chat_action(ChatAction.TYPING)
render_data = RenderData(title=title, time=time, uid=client.uid if client else client)
render_data = RenderData(title=title, time=time, uid=client.player_id if client else client)
calculator_sync: bool = True # 默认养成计算器同步为开启
for type_ in ["avatar", "weapon"]:
@ -258,8 +266,8 @@ class DailyMaterial(Plugin):
i.skills = skills
except InvalidCookies:
calculator_sync = False
except GenshinException as e:
if e.retcode == -502002:
except SimnetBadRequest as e:
if e.ret_code == -502002:
calculator_sync = False # 发现角色养成计算器没启用 设置状态为 False 并防止下次继续获取
self.add_delete_message_job(notice, delay=5)
await notice.edit_text(
@ -341,7 +349,7 @@ class DailyMaterial(Plugin):
logger.debug("角色、武器培养素材图发送成功")
@handler.command("refresh_daily_material", admin=True, block=False)
async def refresh(self, update: Update, context: CallbackContext):
async def refresh(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"):
user = update.effective_user
message = update.effective_message
@ -408,7 +416,7 @@ class DailyMaterial(Plugin):
# noinspection PyUnresolvedReferences
result[stage][day][1] = list(set(result[stage][day][1])) # 去重
async with async_open(DATA_FILE_PATH, "w", encoding="utf-8") as file:
await file.write(json.dumps(result)) # skipcq: PY-W0079
await file.write(jsonlib.dumps(result)) # skipcq: PY-W0079
logger.info("每日素材刷新成功")
break
except (HTTPError, SSLZeroReturnError):
@ -437,11 +445,13 @@ class DailyMaterial(Plugin):
"""修改提示消息"""
async with lock:
if message is not None and time_() >= (the_time.value + INTERVAL):
with contextlib.suppress(TimedOut, RetryAfter):
try:
await message.edit_text(
"\n".join(message.text_html.split("\n")[:2] + [text]), parse_mode=ParseMode.HTML
)
the_time.value = time_()
except (TimedOut, RetryAfter):
pass
async def task(item_id, name, item_type):
logger.debug("正在开始下载 %s 的图标素材", name)

View File

@ -1,20 +1,24 @@
import datetime
from datetime import datetime
from typing import Optional
from typing import Optional, TYPE_CHECKING
import genshin
from genshin import DataNotPublic
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from simnet.errors import DataNotPublic
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ChatAction
from telegram.ext import ConversationHandler, filters, CallbackContext
from telegram.ext import ConversationHandler, filters
from telegram.helpers import create_deep_linked_url
from core.plugin import Plugin, handler
from core.services.template.models import RenderResult
from core.services.template.services import TemplateService
from plugins.tools.genshin import GenshinHelper, CookiesNotFoundError, PlayerNotFoundError
from plugins.tools.genshin import PlayerNotFoundError, CookiesNotFoundError, GenshinHelper
from utils.log import logger
if TYPE_CHECKING:
from simnet import GenshinClient
from telegram import Update
from telegram.ext import ContextTypes
__all__ = ("DailyNotePlugin",)
@ -29,8 +33,8 @@ class DailyNotePlugin(Plugin):
self.template_service = template
self.helper = helper
async def _get_daily_note(self, client: genshin.Client) -> RenderResult:
daily_info = await client.get_genshin_notes(client.uid)
async def _get_daily_note(self, client: "GenshinClient") -> RenderResult:
daily_info = await client.get_genshin_notes(client.player_id)
day = datetime.now().strftime("%m-%d %H:%M") + " 星期" + "一二三四五六日"[datetime.now().weekday()]
resin_recovery_time = (
@ -60,7 +64,7 @@ class DailyNotePlugin(Plugin):
transformer_recovery_time = daily_info.transformer_recovery_time.strftime("%m-%d %H:%M")
render_data = {
"uid": client.uid,
"uid": client.player_id,
"day": day,
"resin_recovery_time": resin_recovery_time,
"current_resin": daily_info.current_resin,
@ -92,16 +96,15 @@ class DailyNotePlugin(Plugin):
@handler.command("dailynote", block=False)
@handler.message(filters.Regex("^当前状态(.*)"), block=False)
async def command_start(self, update: Update, _: CallbackContext) -> Optional[int]:
async def command_start(self, update: "Update", _: "ContextTypes.DEFAULT_TYPE") -> Optional[int]:
message = update.effective_message
user = update.effective_user
logger.info("用户 %s[%s] 每日便签命令请求", user.full_name, user.id)
try:
# 获取当前用户的 genshin.Client
client = await self.helper.get_genshin_client(user.id)
# 渲染
render_result = await self._get_daily_note(client)
async with self.helper.genshin(user.id) as client:
render_result = await self._get_daily_note(client)
except (CookiesNotFoundError, PlayerNotFoundError):
buttons = [
[
@ -127,4 +130,4 @@ class DailyNotePlugin(Plugin):
return ConversationHandler.END
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
await render_result.reply_photo(message, filename=f"{client.uid}.png", allow_sending_without_reply=True)
await render_result.reply_photo(message, filename=f"{client.player_id}.png", allow_sending_without_reply=True)

View File

@ -1,20 +1,26 @@
import os
import re
from datetime import datetime, timedelta
from typing import TYPE_CHECKING
from genshin import DataNotPublic, InvalidCookies, GenshinException
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from simnet.errors import DataNotPublic, BadRequest as SimnetBadRequest, InvalidCookies
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ChatAction
from telegram.ext import filters, CallbackContext
from telegram.ext import filters
from telegram.helpers import create_deep_linked_url
from core.plugin import Plugin, handler
from core.services.cookies import CookiesService
from core.services.template.models import RenderResult
from core.services.template.services import TemplateService
from plugins.tools.genshin import CookiesNotFoundError, GenshinHelper, PlayerNotFoundError
from plugins.tools.genshin import PlayerNotFoundError, CookiesNotFoundError, GenshinHelper
from utils.log import logger
if TYPE_CHECKING:
from telegram import Update
from telegram.ext import ContextTypes
from simnet import GenshinClient
__all__ = ("LedgerPlugin",)
@ -32,8 +38,8 @@ class LedgerPlugin(Plugin):
self.current_dir = os.getcwd()
self.helper = helper
async def _start_get_ledger(self, client, month=None) -> RenderResult:
diary_info = await client.get_diary(client.uid, month=month)
async def _start_get_ledger(self, client: "GenshinClient", month=None) -> RenderResult:
diary_info = await client.get_genshin_diary(client.player_id, month=month)
color = ["#73a9c6", "#d56565", "#70b2b4", "#bd9a5a", "#739970", "#7a6da7", "#597ea0"]
categories = [
{
@ -51,7 +57,7 @@ class LedgerPlugin(Plugin):
return f"{round(amount / 10000, 2)}w" if amount >= 10000 else amount
ledger_data = {
"uid": client.uid,
"uid": client.player_id,
"day": diary_info.month,
"current_primogems": format_amount(diary_info.month_data.current_primogems),
"gacha": int(diary_info.month_data.current_primogems / 160),
@ -69,7 +75,7 @@ class LedgerPlugin(Plugin):
@handler.command(command="ledger", block=False)
@handler.message(filters=filters.Regex("^旅行札记查询(.*)"), block=False)
async def command_start(self, update: Update, context: CallbackContext) -> None:
async def command_start(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
user = update.effective_user
message = update.effective_message
@ -106,19 +112,19 @@ class LedgerPlugin(Plugin):
logger.info("用户 %s[%s] 查询旅行札记", user.full_name, user.id)
await message.reply_chat_action(ChatAction.TYPING)
try:
client = await self.helper.get_genshin_client(user.id)
try:
render_result = await self._start_get_ledger(client, month)
except InvalidCookies as exc: # 如果抛出InvalidCookies 判断是否真的玄学过期(或权限不足?)
await client.get_genshin_user(client.uid)
logger.warning(
"用户 %s[%s] 无法请求旅行札记数据 API返回信息为 [%s]%s", user.full_name, user.id, exc.retcode, exc.original
)
reply_message = await message.reply_text("出错了呜呜呜 ~ 当前访问令牌无法请求角色数数据请尝试重新获取Cookie。")
if filters.ChatType.GROUPS.filter(message):
self.add_delete_message_job(reply_message, delay=30)
self.add_delete_message_job(message, delay=30)
return
async with self.helper.genshin(user.id) as client:
try:
render_result = await self._start_get_ledger(client, month)
except InvalidCookies as exc: # 如果抛出InvalidCookies 判断是否真的玄学过期(或权限不足?)
await client.get_genshin_user(client.player_id)
logger.warning(
"用户 %s[%s] 无法请求旅行札记数据 API返回信息为 [%s]%s", user.full_name, user.id, exc.ret_code, exc.original
)
reply_message = await message.reply_text("出错了呜呜呜 ~ 当前访问令牌无法请求角色数数据请尝试重新获取Cookie。")
if filters.ChatType.GROUPS.filter(message):
self.add_delete_message_job(reply_message, delay=30)
self.add_delete_message_job(message, delay=30)
return
except (PlayerNotFoundError, CookiesNotFoundError):
buttons = [
[
@ -142,10 +148,10 @@ class LedgerPlugin(Plugin):
self.add_delete_message_job(reply_message, delay=30)
self.add_delete_message_job(message, delay=30)
return
except GenshinException as exc:
if exc.retcode == -120:
except SimnetBadRequest as exc:
if exc.ret_code == -120:
await message.reply_text("当前角色冒险等阶不足,暂时无法获取信息")
return
raise exc
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
await render_result.reply_photo(message, filename=f"{client.uid}.png", allow_sending_without_reply=True)
await render_result.reply_photo(message, filename=f"{client.player_id}.png", allow_sending_without_reply=True)

View File

@ -1,4 +1,5 @@
import genshin
from simnet import GenshinClient, Region
from simnet.utils.player import recognize_genshin_game_biz, recognize_genshin_server
from telegram import Update, User, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ChatAction
from telegram.ext import CallbackContext, CommandHandler, MessageHandler, filters, ConversationHandler
@ -12,14 +13,17 @@ from core.services.template.services import TemplateService
from modules.gacha_log.helpers import from_url_get_authkey
from modules.pay_log.error import PayLogNotFound, PayLogAccountNotFound, PayLogInvalidAuthkey, PayLogAuthkeyTimeout
from modules.pay_log.log import PayLog
from plugins.tools.genshin import GenshinHelper, PlayerNotFoundError
from plugins.tools.genshin import GenshinHelper
from plugins.tools.player_info import PlayerInfoSystem
from utils.genshin import get_authkey_by_stoken
from utils.log import logger
INPUT_URL, CONFIRM_DELETE = range(10100, 10102)
class PlayerNotFoundError(Exception):
pass
class PayLogPlugin(Plugin.Conversation):
"""充值记录导入/导出/分析"""
@ -28,15 +32,15 @@ class PayLogPlugin(Plugin.Conversation):
template_service: TemplateService,
players_service: PlayersService,
cookie_service: CookiesService,
helper: GenshinHelper,
player_info: PlayerInfoSystem,
genshin_helper: GenshinHelper,
):
self.template_service = template_service
self.players_service = players_service
self.cookie_service = cookie_service
self.pay_log = PayLog()
self.helper = helper
self.player_info = player_info
self.genshin_helper = genshin_helper
async def _refresh_user_data(self, user: User, authkey: str = None) -> str:
"""刷新用户数据
@ -46,7 +50,7 @@ class PayLogPlugin(Plugin.Conversation):
"""
try:
logger.debug("尝试获取已绑定的原神账号")
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
client = await self.genshin_helper.get_genshin_client(user.id)
new_num = await self.pay_log.get_log_data(user.id, client, authkey)
return "更新完成,本次没有新增数据" if new_num == 0 else f"更新完成,本次共新增{new_num}条充值记录"
except PayLogNotFound:
@ -79,14 +83,14 @@ class PayLogPlugin(Plugin.Conversation):
(value for key, value in cookies.data.items() if key in ["ltuid", "login_uid"]), None
):
cookies.data["stuid"] = stuid
client = genshin.Client(
cookies=cookies.data,
game=genshin.types.Game.GENSHIN,
region=genshin.Region.CHINESE,
lang="zh-cn",
uid=player_info.player_id,
)
authkey = await get_authkey_by_stoken(client, auth_appid="csc")
async with GenshinClient(
cookies=cookies.data, region=Region.CHINESE, lang="zh-cn", player_id=player_info.player_id
) as client:
authkey = await client.get_authkey_by_stoken(
recognize_genshin_game_biz(client.player_id),
recognize_genshin_server(client.player_id),
"csc",
)
if not authkey:
await message.reply_text(
"<b>开始导入充值历史记录:请通过 https://paimon.moe/wish/import 获取抽卡记录链接后发送给我"
@ -130,17 +134,16 @@ class PayLogPlugin(Plugin.Conversation):
message = update.effective_message
user = update.effective_user
logger.info("用户 %s[%s] 删除充值记录命令请求", user.full_name, user.id)
try:
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
except PlayerNotFoundError:
player_info = await self.players_service.get_player(user.id)
if player_info is None:
logger.info("未查询到用户 %s[%s] 所绑定的账号信息", user.full_name, user.id)
await message.reply_text("未查询到您所绑定的账号信息,请先绑定账号")
return ConversationHandler.END
_, status = await self.pay_log.load_history_info(str(user.id), str(client.uid), only_status=True)
_, status = await self.pay_log.load_history_info(str(user.id), str(player_info.player_id), only_status=True)
if not status:
await message.reply_text("你还没有导入充值记录哦~")
return ConversationHandler.END
context.chat_data["uid"] = client.uid
context.chat_data["uid"] = player_info.player_id
await message.reply_text("你确定要删除充值记录吗?(此项操作无法恢复),如果确定请发送 ”确定“,发送其他内容取消")
return CONFIRM_DELETE
@ -167,15 +170,15 @@ class PayLogPlugin(Plugin.Conversation):
cid = int(args[0])
if cid < 0:
raise ValueError("Invalid cid")
client = await self.helper.get_genshin_client(cid, need_cookie=False)
if client is None:
player_info = await self.players_service.get_player(cid)
if player_info is None:
await message.reply_text("该用户暂未绑定账号")
return
_, status = await self.pay_log.load_history_info(str(cid), str(client.uid), only_status=True)
_, status = await self.pay_log.load_history_info(str(cid), str(player_info.player_id), only_status=True)
if not status:
await message.reply_text("该用户还没有导入充值记录")
return
status = await self.pay_log.remove_history_info(str(cid), str(client.uid))
status = await self.pay_log.remove_history_info(str(cid), str(player_info.player_id))
await message.reply_text("充值记录已强制删除" if status else "充值记录删除失败")
except PayLogNotFound:
await message.reply_text("该用户还没有导入充值记录")
@ -189,9 +192,11 @@ class PayLogPlugin(Plugin.Conversation):
user = update.effective_user
logger.info("用户 %s[%s] 导出充值记录命令请求", user.full_name, user.id)
try:
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
player_info = await self.players_service.get_player(user.id)
if player_info is None:
raise PlayerNotFoundError
await message.reply_chat_action(ChatAction.TYPING)
path = self.pay_log.get_file_path(str(user.id), str(client.uid))
path = self.pay_log.get_file_path(str(user.id), str(player_info.player_id))
if not path.exists():
raise PayLogNotFound
await message.reply_chat_action(ChatAction.UPLOAD_DOCUMENT)
@ -214,11 +219,13 @@ class PayLogPlugin(Plugin.Conversation):
user = update.effective_user
logger.info("用户 %s[%s] 充值记录统计命令请求", user.full_name, user.id)
try:
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
player_info = await self.players_service.get_player(user.id)
if player_info is None:
raise PlayerNotFoundError
await message.reply_chat_action(ChatAction.TYPING)
data = await self.pay_log.get_analysis(user.id, client)
data = await self.pay_log.get_analysis(user.id, player_info.player_id)
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
name_card = await self.player_info.get_name_card(client.uid, user)
name_card = await self.player_info.get_name_card(player_info.player_id, user)
data["name_card"] = name_card
png_data = await self.template_service.render(
"genshin/pay_log/pay_log.jinja2", data, full_page=True, query_selector=".container"

View File

@ -1,11 +1,11 @@
from datetime import datetime
from typing import TYPE_CHECKING
from genshin import Client, GenshinException, InvalidCookies
from genshin.client.routes import InternationalRoute # noqa F401
from genshin.utility import recognize_genshin_server, get_ds_headers
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from simnet.client.routes import InternationalRoute
from simnet.errors import InvalidCookies, BadRequest as SIMNetBadRequest
from simnet.utils.player import recognize_genshin_server, recognize_genshin_game_biz
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ParseMode
from telegram.ext import CallbackContext
from telegram.ext import filters
from telegram.helpers import create_deep_linked_url
@ -13,10 +13,14 @@ from core.dependence.redisdb import RedisDB
from core.plugin import Plugin, handler
from core.services.cookies import CookiesService
from core.services.users.services import UserService
from plugins.tools.genshin import GenshinHelper, PlayerNotFoundError, CookiesNotFoundError
from utils.genshin import fetch_hk4e_token_by_cookie, recognize_genshin_game_biz
from plugins.tools.genshin import PlayerNotFoundError, CookiesNotFoundError, GenshinHelper
from utils.log import logger
if TYPE_CHECKING:
from telegram import Update
from telegram.ext import ContextTypes
from simnet import GenshinClient
try:
import ujson as jsonlib
@ -46,53 +50,36 @@ class RegTimePlugin(Plugin):
self.helper = helper
@staticmethod
async def get_reg_time(client: Client) -> str:
async def get_reg_time(client: "GenshinClient") -> str:
"""获取原神注册时间"""
await fetch_hk4e_token_by_cookie(client)
game_biz = recognize_genshin_game_biz(client.player_id)
region = recognize_genshin_server(client.player_id)
await client.get_hk4e_token_by_cookie_token(game_biz, region)
url = REG_TIME_URL.get_url(client.region)
params = {
"game_biz": recognize_genshin_game_biz(client.uid),
"lang": "zh-cn",
"badge_uid": client.uid,
"badge_region": recognize_genshin_server(client.uid),
}
headers = get_ds_headers(
client.region,
params=params,
lang="zh-cn",
)
data = await client.cookie_manager.request(url, method="GET", params=params, headers=headers)
params = {"game_biz": game_biz, "lang": "zh-cn", "badge_uid": client.player_id, "badge_region": region}
data = await client.request_lab(url, method="GET", params=params)
if time := jsonlib.loads(data.get("data", "{}")).get("1", 0):
return datetime.fromtimestamp(time).strftime("%Y-%m-%d %H:%M:%S")
raise RegTimePlugin.NotFoundRegTimeError
async def get_reg_time_from_cache(self, client: Client) -> str:
async def get_reg_time_from_cache(self, client: "GenshinClient") -> str:
"""从缓存中获取原神注册时间"""
if reg_time := await self.cache.get(f"{self.cache_key}{client.uid}"):
if reg_time := await self.cache.get(f"{self.cache_key}{client.player_id}"):
return reg_time.decode("utf-8")
reg_time = await self.get_reg_time(client)
await self.cache.set(f"{self.cache_key}{client.uid}", reg_time)
await self.cache.set(f"{self.cache_key}{client.player_id}", reg_time)
return reg_time
@handler.command("reg_time", block=False)
@handler.message(filters.Regex(r"^原神账号注册时间$"), block=False)
async def reg_time(self, update: Update, context: CallbackContext) -> None:
async def reg_time(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
message = update.effective_message
user = update.effective_user
logger.info("用户 %s[%s] 原神注册时间命令请求", user.full_name, user.id)
try:
client = await self.helper.get_genshin_client(user.id)
game_uid = client.uid
try:
async with self.helper.genshin(user.id) as client:
game_uid = client.player_id
reg_time = await self.get_reg_time_from_cache(client)
except InvalidCookies as exc:
await client.get_genshin_user(client.uid)
logger.warning("用户 %s[%s] 无法请求注册时间 API返回信息为 [%s]%s", user.full_name, user.id, exc.retcode, exc.original)
reply_message = await message.reply_text("出错了呜呜呜 ~ 当前访问令牌无法请求角色数数据,")
if filters.ChatType.GROUPS.filter(message):
self.add_delete_message_job(reply_message, delay=30)
self.add_delete_message_job(message, delay=30)
return
await message.reply_text(f"你的原神账号 [{game_uid}] 注册时间为:{reg_time}")
except (PlayerNotFoundError, CookiesNotFoundError):
buttons = [[InlineKeyboardButton("点我绑定账号", url=create_deep_linked_url(context.bot.username, "set_cookie"))]]
@ -110,8 +97,17 @@ class RegTimePlugin(Plugin):
parse_mode=ParseMode.HTML,
reply_markup=InlineKeyboardMarkup(buttons),
)
except GenshinException as exc:
if exc.retcode == -501101:
except InvalidCookies as exc:
async with self.helper.genshin(user.id) as client:
await client.get_genshin_user(client.player_id)
logger.warning("用户 %s[%s] 无法请求注册时间 API返回信息为 [%s]%s", user.full_name, user.id, exc.ret_code, exc.original)
reply_message = await message.reply_text("出错了呜呜呜 ~ 当前访问令牌无法请求角色数数据,")
if filters.ChatType.GROUPS.filter(message):
self.add_delete_message_job(reply_message, delay=30)
self.add_delete_message_job(message, delay=30)
return
except SIMNetBadRequest as exc:
if exc.ret_code == -501101:
await message.reply_text("当前角色冒险等阶未达到10级暂时无法获取信息")
else:
raise exc

View File

@ -10,10 +10,12 @@ from telegram.helpers import create_deep_linked_url
from core.config import config
from core.handler.callbackqueryhandler import CallbackQueryHandler
from core.plugin import Plugin, handler
from core.services.cookies import CookiesService
from core.services.players import PlayersService
from core.services.sign.models import Sign as SignUser, SignStatusEnum
from core.services.sign.services import SignServices
from core.services.users.services import UserAdminService
from plugins.tools.genshin import GenshinHelper, CookiesNotFoundError, PlayerNotFoundError
from plugins.tools.genshin import PlayerNotFoundError, CookiesNotFoundError, GenshinHelper
from plugins.tools.sign import SignSystem, NeedChallenge
from utils.log import logger
@ -29,16 +31,22 @@ class Sign(Plugin):
sign_service: SignServices,
user_admin_service: UserAdminService,
sign_system: SignSystem,
player: PlayersService,
cookies: CookiesService,
):
self.user_admin_service = user_admin_service
self.sign_service = sign_service
self.sign_system = sign_system
self.genshin_helper = genshin_helper
self.players_service = player
self.cookies_service = cookies
async def _process_auto_sign(self, user_id: int, chat_id: int, method: str) -> str:
try:
await self.genshin_helper.get_genshin_client(user_id)
except (PlayerNotFoundError, CookiesNotFoundError):
player = await self.players_service.get_player(user_id)
if player is None:
return "未查询到账号信息,请先私聊派蒙绑定账号"
cookie_model = await self.cookies_service.get(player.user_id, player.account_id, player.region)
if cookie_model is None:
return "未查询到账号信息,请先私聊派蒙绑定账号"
user: SignUser = await self.sign_service.get_by_user_id(user_id)
if user:
@ -93,20 +101,20 @@ class Sign(Plugin):
if filters.ChatType.GROUPS.filter(message):
self.add_delete_message_job(message)
try:
client = await self.genshin_helper.get_genshin_client(user.id)
await message.reply_chat_action(ChatAction.TYPING)
_, challenge = await self.sign_system.get_challenge(client.uid)
if validate:
_, challenge = await self.sign_system.get_challenge(client.uid)
if challenge:
sign_text = await self.sign_system.start_sign(client, challenge=challenge, validate=validate)
async with self.genshin_helper.genshin(user.id) as client:
await message.reply_chat_action(ChatAction.TYPING)
_, challenge = await self.sign_system.get_challenge(client.player_id)
if validate:
_, challenge = await self.sign_system.get_challenge(client.player_id)
if challenge:
sign_text = await self.sign_system.start_sign(client, challenge=challenge, validate=validate)
else:
reply_message = await message.reply_text("请求已经过期", allow_sending_without_reply=True)
if filters.ChatType.GROUPS.filter(reply_message):
self.add_delete_message_job(reply_message)
return
else:
reply_message = await message.reply_text("请求已经过期", allow_sending_without_reply=True)
if filters.ChatType.GROUPS.filter(reply_message):
self.add_delete_message_job(reply_message)
return
else:
sign_text = await self.sign_system.start_sign(client)
sign_text = await self.sign_system.start_sign(client)
reply_message = await message.reply_text(sign_text, allow_sending_without_reply=True)
if filters.ChatType.GROUPS.filter(reply_message):
self.add_delete_message_job(reply_message)

View File

@ -1,37 +1,38 @@
import random
from typing import Optional
from typing import Optional, TYPE_CHECKING
from genshin import Client, GenshinException
from genshin.models import GenshinUserStats
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from simnet.errors import BadRequest as SimnetBadRequest
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ChatAction
from telegram.ext import CallbackContext, filters
from telegram.ext import filters
from telegram.helpers import create_deep_linked_url
from core.plugin import Plugin, handler
from core.services.cookies.error import TooManyRequestPublicCookies
from core.services.template.models import RenderResult
from core.services.template.services import TemplateService
from plugins.tools.genshin import GenshinHelper, PlayerNotFoundError, CookiesNotFoundError
from plugins.tools.genshin import PlayerNotFoundError, CookiesNotFoundError, GenshinHelper
from utils.log import logger
if TYPE_CHECKING:
from telegram import Update
from telegram.ext import ContextTypes
from simnet.models.genshin.chronicle.stats import GenshinUserStats
from simnet import GenshinClient
__all__ = ("PlayerStatsPlugins",)
class PlayerStatsPlugins(Plugin):
"""玩家统计查询"""
def __init__(
self,
template: TemplateService,
helper: GenshinHelper,
):
def __init__(self, template: TemplateService, helper: GenshinHelper):
self.template_service = template
self.helper = helper
@handler.command("stats", block=False)
@handler.message(filters.Regex("^玩家统计查询(.*)"), block=False)
async def command_start(self, update: Update, context: CallbackContext) -> Optional[int]:
async def command_start(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> Optional[int]:
user = update.effective_user
message = update.effective_message
logger.info("用户 %s[%s] 查询游戏用户命令请求", user.full_name, user.id)
@ -46,10 +47,11 @@ class PlayerStatsPlugins(Plugin):
return
try:
try:
client = await self.helper.get_genshin_client(user.id)
async with self.helper.genshin(user.id) as client:
render_result = await self.render(client, uid)
except CookiesNotFoundError:
client, uid = await self.helper.get_public_genshin_client(user.id)
render_result = await self.render(client, uid)
async with self.helper.public_genshin(user.id) as client:
render_result = await self.render(client, uid)
except PlayerNotFoundError:
buttons = [[InlineKeyboardButton("点我绑定账号", url=create_deep_linked_url(context.bot.username, "set_cookie"))]]
if filters.ChatType.GROUPS.filter(message):
@ -61,8 +63,8 @@ class PlayerStatsPlugins(Plugin):
else:
await message.reply_text("未查询到您所绑定的账号信息,请先绑定账号", reply_markup=InlineKeyboardMarkup(buttons))
return
except GenshinException as exc:
if exc.retcode == 1034 and uid:
except SimnetBadRequest as exc:
if exc.ret_code == 1034 and uid:
await message.reply_text("出错了呜呜呜 ~ 请稍后重试")
return
raise exc
@ -75,14 +77,13 @@ class PlayerStatsPlugins(Plugin):
await message.reply_text("角色数据有误 估计是派蒙晕了")
return
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
await render_result.reply_photo(message, filename=f"{client.uid}.png", allow_sending_without_reply=True)
await render_result.reply_photo(message, filename=f"{client.player_id}.png", allow_sending_without_reply=True)
async def render(self, client: Client, uid: Optional[int] = None) -> RenderResult:
async def render(self, client: "GenshinClient", uid: Optional[int] = None) -> RenderResult:
if uid is None:
uid = client.uid
uid = client.player_id
user_info = await client.get_genshin_user(uid)
logger.debug(user_info)
# 因为需要替换线上图片地址为本地地址,先克隆数据,避免修改原数据
user_info = user_info.copy(deep=True)
@ -122,7 +123,7 @@ class PlayerStatsPlugins(Plugin):
full_page=True,
)
async def cache_images(self, data: GenshinUserStats) -> None:
async def cache_images(self, data: "GenshinUserStats") -> None:
"""缓存所有图片到本地"""
# TODO: 并发下载所有资源

View File

@ -1,8 +1,9 @@
from io import BytesIO
import genshin
from aiofiles import open as async_open
from genshin.models import GenshinBannerType
from simnet import GenshinClient, Region
from simnet.models.genshin.wish import BannerType
from simnet.utils.player import recognize_genshin_game_biz, recognize_genshin_server
from telegram import Document, InlineKeyboardButton, InlineKeyboardMarkup, Message, Update, User
from telegram.constants import ChatAction
from telegram.ext import CallbackContext, CommandHandler, ConversationHandler, MessageHandler, filters
@ -27,9 +28,8 @@ from modules.gacha_log.error import (
)
from modules.gacha_log.helpers import from_url_get_authkey
from modules.gacha_log.log import GachaLog
from plugins.tools.genshin import PlayerNotFoundError, GenshinHelper
from plugins.tools.genshin import GenshinHelper
from plugins.tools.player_info import PlayerInfoSystem
from utils.genshin import get_authkey_by_stoken
from utils.log import logger
try:
@ -41,6 +41,10 @@ except ImportError:
INPUT_URL, INPUT_FILE, CONFIRM_DELETE = range(10100, 10103)
class PlayerNotFoundError(Exception):
pass
class WishLogPlugin(Plugin.Conversation):
"""抽卡记录导入/导出/分析"""
@ -50,8 +54,8 @@ class WishLogPlugin(Plugin.Conversation):
players_service: PlayersService,
assets: AssetsService,
cookie_service: CookiesService,
helper: GenshinHelper,
player_info: PlayerInfoSystem,
genshin_helper: GenshinHelper,
):
self.template_service = template_service
self.players_service = players_service
@ -59,8 +63,8 @@ class WishLogPlugin(Plugin.Conversation):
self.cookie_service = cookie_service
self.zh_dict = None
self.gacha_log = GachaLog()
self.helper = helper
self.player_info = player_info
self.genshin_helper = genshin_helper
async def initialize(self) -> None:
await update_paimon_moe_zh(False)
@ -78,12 +82,12 @@ class WishLogPlugin(Plugin.Conversation):
"""
try:
logger.debug("尝试获取已绑定的原神账号")
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
client = await self.genshin_helper.get_genshin_client(user.id)
if authkey:
new_num = await self.gacha_log.get_gacha_log_data(user.id, client, authkey)
return "更新完成,本次没有新增数据" if new_num == 0 else f"更新完成,本次共新增{new_num}条抽卡记录"
if data:
new_num = await self.gacha_log.import_gacha_log_data(user.id, client, data, verify_uid)
new_num = await self.gacha_log.import_gacha_log_data(user.id, client.player_id, data, verify_uid)
return "更新完成,本次没有新增数据" if new_num == 0 else f"更新完成,本次共新增{new_num}条抽卡记录"
except GachaLogNotFound:
return "派蒙没有找到你的抽卡记录,快来私聊派蒙导入吧~"
@ -169,14 +173,14 @@ class WishLogPlugin(Plugin.Conversation):
(value for key, value in cookies.data.items() if key in ["ltuid", "login_uid"]), None
):
cookies.data["stuid"] = stuid
client = genshin.Client(
cookies=cookies.data,
game=genshin.types.Game.GENSHIN,
region=genshin.Region.CHINESE,
lang="zh-cn",
uid=player_info.player_id,
)
authkey = await get_authkey_by_stoken(client)
async with GenshinClient(
cookies=cookies.data, region=Region.CHINESE, lang="zh-cn", player_id=player_info.player_id
) as client:
authkey = await client.get_authkey_by_stoken(
recognize_genshin_game_biz(client.player_id),
recognize_genshin_server(client.player_id),
"webview_gacha",
)
if not authkey:
await message.reply_text(
"<b>开始导入祈愿历史记录:请通过 https://paimon.moe/wish/import 获取抽卡记录链接后发送给我"
@ -223,13 +227,15 @@ class WishLogPlugin(Plugin.Conversation):
user = update.effective_user
logger.info("用户 %s[%s] 删除抽卡记录命令请求", user.full_name, user.id)
try:
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
context.chat_data["uid"] = client.uid
player_info = await self.players_service.get_player(user.id)
if player_info is None:
raise PlayerNotFoundError
context.chat_data["uid"] = player_info.player_id
except PlayerNotFoundError:
logger.info("未查询到用户 %s[%s] 所绑定的账号信息", user.full_name, user.id)
await message.reply_text("未查询到您所绑定的账号信息,请先绑定账号")
return ConversationHandler.END
_, status = await self.gacha_log.load_history_info(str(user.id), str(client.uid), only_status=True)
_, status = await self.gacha_log.load_history_info(str(user.id), str(player_info.player_id), only_status=True)
if not status:
await message.reply_text("你还没有导入抽卡记录哦~")
return ConversationHandler.END
@ -251,6 +257,7 @@ class WishLogPlugin(Plugin.Conversation):
@handler(CommandHandler, command="gacha_log_force_delete", block=False, admin=True)
async def command_gacha_log_force_delete(self, update: Update, context: CallbackContext):
message = update.effective_message
user = update.effective_user
args = self.get_args(context)
if not args:
await message.reply_text("请指定用户ID")
@ -259,12 +266,14 @@ class WishLogPlugin(Plugin.Conversation):
cid = int(args[0])
if cid < 0:
raise ValueError("Invalid cid")
client = await self.helper.get_genshin_client(cid, need_cookie=False)
_, status = await self.gacha_log.load_history_info(str(cid), str(client.uid), only_status=True)
player_info = await self.players_service.get_player(user.id)
if player_info is None:
raise PlayerNotFoundError
_, status = await self.gacha_log.load_history_info(str(cid), str(player_info.player_id), only_status=True)
if not status:
await message.reply_text("该用户还没有导入抽卡记录")
return
status = await self.gacha_log.remove_history_info(str(cid), str(client.uid))
status = await self.gacha_log.remove_history_info(str(cid), str(player_info.player_id))
await message.reply_text("抽卡记录已强制删除" if status else "抽卡记录删除失败")
except GachaLogNotFound:
await message.reply_text("该用户还没有导入抽卡记录")
@ -280,9 +289,11 @@ class WishLogPlugin(Plugin.Conversation):
user = update.effective_user
logger.info("用户 %s[%s] 导出抽卡记录命令请求", user.full_name, user.id)
try:
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
player_info = await self.players_service.get_player(user.id)
if player_info is None:
raise PlayerNotFoundError
await message.reply_chat_action(ChatAction.TYPING)
path = await self.gacha_log.gacha_log_to_uigf(str(user.id), str(client.uid))
path = await self.gacha_log.gacha_log_to_uigf(str(user.id), str(player_info.player_id))
await message.reply_chat_action(ChatAction.UPLOAD_DOCUMENT)
await message.reply_document(document=open(path, "rb+"), caption="抽卡记录导出文件 - UIGF V2.2")
except GachaLogNotFound:
@ -304,24 +315,26 @@ class WishLogPlugin(Plugin.Conversation):
async def command_start_analysis(self, update: Update, context: CallbackContext) -> None:
message = update.effective_message
user = update.effective_user
pool_type = GenshinBannerType.CHARACTER1
pool_type = BannerType.CHARACTER1
if args := self.get_args(context):
if "武器" in args:
pool_type = GenshinBannerType.WEAPON
pool_type = BannerType.WEAPON
elif "常驻" in args:
pool_type = GenshinBannerType.STANDARD
pool_type = BannerType.STANDARD
logger.info("用户 %s[%s] 抽卡记录命令请求 || 参数 %s", user.full_name, user.id, pool_type.name)
try:
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
player_info = await self.players_service.get_player(user.id)
if player_info is None:
raise PlayerNotFoundError
await message.reply_chat_action(ChatAction.TYPING)
data = await self.gacha_log.get_analysis(user.id, client, pool_type, self.assets_service)
data = await self.gacha_log.get_analysis(user.id, player_info.player_id, pool_type, self.assets_service)
if isinstance(data, str):
reply_message = await message.reply_text(data)
if filters.ChatType.GROUPS.filter(message):
self.add_delete_message_job(reply_message, delay=300)
self.add_delete_message_job(message, delay=300)
else:
name_card = await self.player_info.get_name_card(client.uid, user)
name_card = await self.player_info.get_name_card(player_info.player_id, user)
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
data["name_card"] = name_card
png_data = await self.template_service.render(
@ -358,31 +371,35 @@ class WishLogPlugin(Plugin.Conversation):
async def command_start_count(self, update: Update, context: CallbackContext) -> None:
message = update.effective_message
user = update.effective_user
pool_type = GenshinBannerType.CHARACTER1
pool_type = BannerType.CHARACTER1
all_five = False
if args := self.get_args(context):
if "武器" in args:
pool_type = GenshinBannerType.WEAPON
pool_type = BannerType.WEAPON
elif "常驻" in args:
pool_type = GenshinBannerType.STANDARD
pool_type = BannerType.STANDARD
elif "仅五星" in args:
all_five = True
logger.info("用户 %s[%s] 抽卡统计命令请求 || 参数 %s || 仅五星 %s", user.full_name, user.id, pool_type.name, all_five)
try:
client = await self.helper.get_genshin_client(user.id, need_cookie=False)
player_info = await self.players_service.get_player(user.id)
if player_info is None:
raise PlayerNotFoundError
group = filters.ChatType.GROUPS.filter(message)
await message.reply_chat_action(ChatAction.TYPING)
if all_five:
data = await self.gacha_log.get_all_five_analysis(user.id, client, self.assets_service)
data = await self.gacha_log.get_all_five_analysis(user.id, player_info.player_id, self.assets_service)
else:
data = await self.gacha_log.get_pool_analysis(user.id, client, pool_type, self.assets_service, group)
data = await self.gacha_log.get_pool_analysis(
user.id, player_info.player_id, pool_type, self.assets_service, group
)
if isinstance(data, str):
reply_message = await message.reply_text(data)
if filters.ChatType.GROUPS.filter(message):
self.add_delete_message_job(reply_message)
self.add_delete_message_job(message)
else:
name_card = await self.player_info.get_name_card(client.uid, user)
name_card = await self.player_info.get_name_card(player_info.player_id, user)
document = False
if data["hasMore"] and not group:
document = True

View File

@ -5,8 +5,8 @@ from typing import Optional
import aiofiles
from aiohttp import ClientError, ClientConnectorError
from genshin import DataNotPublic, GenshinException, InvalidCookies, TooManyRequests
from httpx import HTTPError, TimeoutException
from simnet.errors import DataNotPublic, BadRequest as SIMNetBadRequest, InvalidCookies, TooManyRequests
from telegram import ReplyKeyboardRemove, Update, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.constants import ParseMode
from telegram.error import BadRequest, Forbidden, TelegramError, TimedOut, NetworkError
@ -100,7 +100,7 @@ class ErrorHandler(Plugin):
@error_handler()
async def process_genshin_exception(self, update: object, context: CallbackContext):
if not isinstance(context.error, GenshinException) or not isinstance(update, Update):
if not isinstance(context.error, SIMNetBadRequest) or not isinstance(update, Update):
return
exc = context.error
notice: Optional[str] = None

View File

@ -1,13 +1,15 @@
from typing import Tuple, Optional
from genshin import Region, GenshinException
from simnet import Region
from simnet.errors import BadRequest as SIMNetBadRequest
from core.dependence.redisdb import RedisDB
from core.plugin import Plugin
from core.services.cookies import CookiesService
from core.services.players import PlayersService
from modules.apihelper.client.components.verify import Verify
from modules.apihelper.error import ResponseException, APIHelperException
from plugins.tools.genshin import GenshinHelper, PlayerNotFoundError, CookiesNotFoundError
from plugins.tools.genshin import PlayerNotFoundError, CookiesNotFoundError, GenshinHelper
from utils.log import logger
__all__ = ("ChallengeSystemException", "ChallengeSystem")
@ -25,11 +27,13 @@ class ChallengeSystem(Plugin):
cookies_service: CookiesService,
redis: RedisDB,
genshin_helper: GenshinHelper,
player: PlayersService,
) -> None:
self.cookies_service = cookies_service
self.genshin_helper = genshin_helper
self.cache = redis.client
self.qname = "plugin:challenge:"
self.players_service = player
async def get_challenge(self, uid: int) -> Tuple[Optional[str], Optional[str]]:
data = await self.cache.get(f"{self.qname}{uid}")
@ -56,12 +60,16 @@ class ChallengeSystem(Plugin):
if need_verify:
try:
await client.get_genshin_notes()
except GenshinException as exc:
except SIMNetBadRequest as exc:
if exc.retcode != 1034:
raise exc
else:
raise ChallengeSystemException("账户正常,无需验证")
verify = Verify(account_id=client.hoyolab_id, cookies=client.cookie_manager.cookies)
finally:
await client.shutdown()
else:
await client.shutdown()
verify = Verify(cookies=client.cookies)
try:
data = await verify.create()
challenge = data["challenge"]
@ -74,26 +82,26 @@ class ChallengeSystem(Plugin):
validate = await verify.ajax(referer="https://webstatic.mihoyo.com/", gt=gt, challenge=challenge)
if validate:
await verify.verify(challenge, validate)
return client.uid, "ajax", "ajax"
return client.player_id, "ajax", "ajax"
except APIHelperException as exc:
logger.warning("用户 %s ajax 验证失效 错误信息为 %s", user_id, str(exc))
logger.warning("用户 %s ajax 验证失败 重新申请验证", user_id)
return await self.create_challenge(user_id, need_verify, False)
await self.set_challenge(client.uid, gt, challenge)
return client.uid, gt, challenge
await self.set_challenge(client.player_id, gt, challenge)
return client.player_id, gt, challenge
async def pass_challenge(self, user_id: int, validate: str, challenge: Optional[str] = None) -> bool:
try:
client = await self.genshin_helper.get_genshin_client(user_id)
except PlayerNotFoundError:
player = await self.players_service.get_player(user_id)
if player is None:
raise ChallengeSystemException("用户未找到")
if client.region != Region.CHINESE:
if player.region != Region.CHINESE:
raise ChallengeSystemException("非法用户")
cookie_model = await self.cookies_service.get(player.user_id, player.account_id, player.region)
if cookie_model is None:
raise ChallengeSystemException("无需验证")
if challenge is None:
_, challenge = await self.get_challenge(client.uid)
_, challenge = await self.get_challenge(player.player_id)
if challenge is None:
raise ChallengeSystemException("验证失效 请求已经过期")
verify = Verify(account_id=client.hoyolab_id, cookies=client.cookie_manager.cookies)
verify = Verify(cookies=cookie_model.data)
try:
await verify.verify(challenge=challenge, validate=validate)
except ResponseException as exc:

View File

@ -1,46 +1,37 @@
import asyncio
import random
from contextlib import asynccontextmanager
from datetime import datetime, time, timedelta
from typing import Optional, TYPE_CHECKING, Tuple, Union
from typing import Optional
from typing import TYPE_CHECKING, Union
import genshin
from genshin.errors import GenshinException
from genshin.models import BaseCharacter, CalculatorCharacterDetails
from pydantic import ValidationError
from simnet import GenshinClient, Region
from simnet.errors import BadRequest as SimnetBadRequest
from simnet.models.genshin.calculator import CalculatorCharacterDetails
from simnet.models.genshin.chronicle.characters import Character
from sqlalchemy.exc import SQLAlchemyError
from sqlmodel import BigInteger, Column, DateTime, Field, Index, Integer, SQLModel, String, delete, func, select
from telegram.ext import ContextTypes
from core.basemodel import RegionEnum
from core.config import config
from core.dependence.database import Database
from core.dependence.redisdb import RedisDB
from core.error import ServiceNotFoundError
from core.plugin import Plugin
from core.services.cookies.services import CookiesService, PublicCookiesService
from core.services.devices import DevicesService
from core.services.players.services import PlayersService
from core.services.users.services import UserService
from core.sqlmodel.session import AsyncSession
from utils.const import REGION_MAP
from utils.log import logger
if TYPE_CHECKING:
from sqlalchemy import Table
from genshin import Client as GenshinClient
__all__ = ("GenshinHelper", "PlayerNotFoundError", "CookiesNotFoundError", "CharacterDetails")
class PlayerNotFoundError(Exception):
def __init__(self, user_id):
super().__init__(f"User not found, user_id: {user_id}")
class CookiesNotFoundError(Exception):
def __init__(self, user_id):
super().__init__(f"{user_id} cookies not found")
class CharacterDetailsSQLModel(SQLModel, table=True):
__tablename__ = "character_details"
__table_args__ = (
@ -164,132 +155,173 @@ class CharacterDetails(Plugin):
return None
async def get_character_details(
self, client: "GenshinClient", character: "Union[int,BaseCharacter]"
self, client: "GenshinClient", character: "Union[int,Character]"
) -> Optional["CalculatorCharacterDetails"]:
"""缓存 character_details 并定时对其进行数据存储 当遇到 Too Many Requests 可以获取以前的数据
:param client: genshin.py
:param character:
:return:
"""
uid = client.uid
"""缓存 character_details 并定时对其进行数据存储 当遇到 Too Many Requests 可以获取以前的数据"""
uid = client.player_id
if isinstance(character, Character):
character_id = character.id
else:
character_id = character
if uid is not None:
if isinstance(character, BaseCharacter):
character_id = character.id
else:
character_id = character
detail = await self.get_character_details_for_redis(uid, character_id)
if detail is not None:
return detail
try:
detail = await client.get_character_details(character)
except GenshinException as exc:
if "Too Many Requests" in exc.msg:
detail = await client.get_character_details(character_id)
except SimnetBadRequest as exc:
if "Too Many Requests" in exc.message:
return await self.get_character_details_for_mysql(uid, character_id)
raise exc
asyncio.create_task(self.set_character_details(uid, character_id, detail.json()))
asyncio.create_task(self.set_character_details(uid, character_id, detail.json(by_alias=True)))
return detail
try:
return await client.get_character_details(character)
except GenshinException as exc:
if "Too Many Requests" in exc.msg:
return await client.get_character_details(character_id)
except SimnetBadRequest as exc:
if "Too Many Requests" in exc.message:
logger.warning("Too Many Requests")
else:
raise exc
return None
class PlayerNotFoundError(Exception):
def __init__(self, user_id):
super().__init__(f"User not found, user_id: {user_id}")
class CookiesNotFoundError(Exception):
def __init__(self, user_id):
super().__init__(f"{user_id} cookies not found")
class GenshinHelper(Plugin):
def __init__(
self,
cookies: CookiesService,
public_cookies: PublicCookiesService,
user: UserService,
redis: RedisDB,
player: PlayersService,
devices: DevicesService,
) -> None:
self.cookies_service = cookies
self.public_cookies_service = public_cookies
self.user_service = user
self.redis_db = redis
self.players_service = player
if self.redis_db and config.genshin_ttl:
self.genshin_cache = genshin.RedisCache(self.redis_db.client, ttl=config.genshin_ttl)
else:
self.genshin_cache = None
self.devices_service = devices
if None in (temp := [self.user_service, self.cookies_service, self.players_service]):
raise ServiceNotFoundError(*filter(lambda x: x is None, temp))
@staticmethod
def region_server(uid: Union[int, str]) -> RegionEnum:
if isinstance(uid, (int, str)):
region = REGION_MAP.get(str(uid)[0])
else:
raise TypeError("UID variable type error")
if region:
return region
raise ValueError(f"UID {uid} isn't associated with any region.")
async def get_genshin_client(
self, user_id: int, region: Optional[RegionEnum] = None, need_cookie: bool = True
) -> Optional[genshin.Client]:
"""通过 user_id 和 region 获取私有的 `genshin.Client`"""
@asynccontextmanager
async def genshin(self, user_id: int, region: Optional[RegionEnum] = None) -> GenshinClient:
player = await self.players_service.get_player(user_id, region)
if player is None:
raise PlayerNotFoundError(user_id)
cookies = None
if need_cookie:
if player.account_id is None:
raise CookiesNotFoundError(user_id)
cookie_model = await self.cookies_service.get(player.user_id, player.account_id, player.region)
if cookie_model is None:
raise CookiesNotFoundError(user_id)
cookies = cookie_model.data
uid = player.player_id
if player.account_id is None:
raise CookiesNotFoundError(user_id)
cookie_model = await self.cookies_service.get(player.user_id, player.account_id, player.region)
if cookie_model is None:
raise CookiesNotFoundError(user_id)
cookies = cookie_model.data
region = player.region
if region == RegionEnum.HYPERION: # 国服
game_region = genshin.types.Region.CHINESE
game_region = Region.CHINESE
elif region == RegionEnum.HOYOLAB: # 国际服
game_region = genshin.types.Region.OVERSEAS
game_region = Region.OVERSEAS
else:
raise TypeError("Region is not None")
client = genshin.Client(
device_id: Optional[str] = None
device_fp: Optional[str] = None
devices = await self.devices_service.get(player.account_id)
if devices:
device_id = devices.device_id
device_fp = devices.device_fp
async with GenshinClient(
cookies,
lang="zh-cn",
game=genshin.types.Game.GENSHIN,
region=game_region,
uid=uid,
hoyolab_id=player.account_id,
account_id=player.account_id,
player_id=player.player_id,
lang="zh-cn",
device_id=device_id,
device_fp=device_fp,
) as client:
yield client
async def get_genshin_client(self, user_id: int, region: Optional[RegionEnum] = None) -> GenshinClient:
player = await self.players_service.get_player(user_id, region)
if player is None:
raise PlayerNotFoundError(user_id)
if player.account_id is None:
raise CookiesNotFoundError(user_id)
cookie_model = await self.cookies_service.get(player.user_id, player.account_id, player.region)
if cookie_model is None:
raise CookiesNotFoundError(user_id)
cookies = cookie_model.data
region = player.region
if region == RegionEnum.HYPERION:
game_region = Region.CHINESE
elif region == RegionEnum.HOYOLAB:
game_region = Region.OVERSEAS
else:
raise TypeError("Region is not None")
device_id: Optional[str] = None
device_fp: Optional[str] = None
devices = await self.devices_service.get(player.account_id)
if devices:
device_id = devices.device_id
device_fp = devices.device_fp
return GenshinClient(
cookies,
region=game_region,
account_id=player.account_id,
player_id=player.player_id,
lang="zh-cn",
device_id=device_id,
device_fp=device_fp,
)
if self.genshin_cache is not None:
client.cache = self.genshin_cache
return client
async def get_public_genshin_client(self, user_id: int) -> Tuple[genshin.Client, int]:
"""通过 user_id 获取公共的 `genshin.Client`"""
player = await self.players_service.get_player(user_id)
@asynccontextmanager
async def public_genshin(self, user_id: int, region: Optional[RegionEnum] = None) -> GenshinClient:
player = await self.players_service.get_player(user_id, region)
region = player.region
cookies = await self.public_cookies_service.get_cookies(user_id, region)
uid = player.player_id
if region is RegionEnum.HYPERION:
game_region = genshin.types.Region.CHINESE
game_region = Region.CHINESE
elif region is RegionEnum.HOYOLAB:
game_region = genshin.types.Region.OVERSEAS
game_region = Region.OVERSEAS
else:
raise TypeError("Region is not `RegionEnum.NULL`")
client = genshin.Client(
cookies.data, region=game_region, uid=uid, game=genshin.types.Game.GENSHIN, lang="zh-cn"
)
device_id: Optional[str] = None
device_fp: Optional[str] = None
devices = await self.devices_service.get(cookies.account_id)
if devices:
device_id = devices.device_id
device_fp = devices.device_fp
if self.genshin_cache is not None:
client.cache = self.genshin_cache
return client, uid
async with GenshinClient(
cookies,
region=game_region,
account_id=player.account_id,
player_id=uid,
lang="zh-cn",
device_id=device_id,
device_fp=device_fp,
) as client:
try:
yield client
except SimnetBadRequest as exc:
if exc.ret_code == 1034:
await self.public_cookies_service.undo(user_id)
raise exc

View File

@ -6,9 +6,10 @@ from enum import Enum
from typing import Optional, Tuple, List, TYPE_CHECKING
from aiohttp import ClientConnectorError
from genshin import Game, GenshinException, AlreadyClaimed, Client, InvalidCookies
from genshin.utility import recognize_genshin_server
from httpx import TimeoutException
from simnet import Game
from simnet.errors import BadRequest as SimnetBadRequest, AlreadyClaimed, InvalidCookies
from simnet.utils.player import recognize_genshin_server
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ParseMode
from telegram.error import Forbidden, BadRequest
@ -21,11 +22,12 @@ from core.services.sign.models import SignStatusEnum
from core.services.sign.services import SignServices
from core.services.users.services import UserService
from modules.apihelper.client.components.verify import Verify
from plugins.tools.genshin import GenshinHelper, CookiesNotFoundError, PlayerNotFoundError
from plugins.tools.genshin import PlayerNotFoundError, CookiesNotFoundError, GenshinHelper
from plugins.tools.recognize import RecognizeSystem
from utils.log import logger
if TYPE_CHECKING:
from simnet import GenshinClient
from telegram.ext import ContextTypes
@ -104,7 +106,7 @@ class SignSystem(Plugin):
async def start_sign(
self,
client: Client,
client: "GenshinClient",
challenge: Optional[str] = None,
validate: Optional[str] = None,
is_sleep: bool = False,
@ -112,28 +114,28 @@ class SignSystem(Plugin):
title: Optional[str] = "签到结果",
) -> str:
if is_sleep:
if recognize_genshin_server(client.uid) in ("cn_gf01", "cn_qd01"):
if recognize_genshin_server(client.player_id) in ("cn_gf01", "cn_qd01"):
await asyncio.sleep(random.randint(10, 300)) # nosec
else:
await asyncio.sleep(random.randint(0, 3)) # nosec
try:
rewards = await client.get_monthly_rewards(game=Game.GENSHIN, lang="zh-cn")
except GenshinException as error:
logger.warning("UID[%s] 获取签到信息失败API返回信息为 %s", client.uid, str(error))
except SimnetBadRequest as error:
logger.warning("UID[%s] 获取签到信息失败API返回信息为 %s", client.player_id, str(error))
if is_raise:
raise error
return f"获取签到信息失败API返回信息为 {str(error)}"
try:
daily_reward_info = await client.get_reward_info(game=Game.GENSHIN, lang="zh-cn") # 获取签到信息失败
except GenshinException as error:
logger.warning("UID[%s] 获取签到状态失败API返回信息为 %s", client.uid, str(error))
except SimnetBadRequest as error:
logger.warning("UID[%s] 获取签到状态失败API返回信息为 %s", client.player_id, str(error))
if is_raise:
raise error
return f"获取签到状态失败API返回信息为 {str(error)}"
if not daily_reward_info.signed_in:
try:
if validate:
logger.info("UID[%s] 正在尝试通过验证码\nchallenge[%s]\nvalidate[%s]", client.uid, challenge, validate)
logger.info("UID[%s] 正在尝试通过验证码\nchallenge[%s]\nvalidate[%s]", client.player_id, challenge, validate)
request_daily_reward = await client.request_daily_reward(
"sign",
method="POST",
@ -147,8 +149,8 @@ class SignSystem(Plugin):
# 尝试通过 ajax 请求绕过签到
gt = request_daily_reward.get("gt", "")
challenge = request_daily_reward.get("challenge", "")
logger.warning("UID[%s] 触发验证码\ngt[%s]\nchallenge[%s]", client.uid, gt, challenge)
self.verify.account_id = client.hoyolab_id
logger.warning("UID[%s] 触发验证码\ngt[%s]\nchallenge[%s]", client.player_id, gt, challenge)
self.verify.account_id = client.account_id
validate = await self.verify.ajax(
referer=RecognizeSystem.REFERER,
gt=gt,
@ -166,16 +168,16 @@ class SignSystem(Plugin):
)
logger.debug("request_daily_reward 返回 %s", request_daily_reward)
if request_daily_reward and request_daily_reward.get("success", 0) == 1:
logger.warning("UID[%s] 触发验证码\nchallenge[%s]", client.uid, challenge)
logger.warning("UID[%s] 触发验证码\nchallenge[%s]", client.player_id, challenge)
raise NeedChallenge(
uid=client.uid,
uid=client.player_id,
gt=request_daily_reward.get("gt", ""),
challenge=request_daily_reward.get("challenge", ""),
)
elif config.pass_challenge_app_key:
# 如果无法绕过 检查配置文件是否配置识别 API 尝试请求绕过
# 注意 需要重新获取没有进行任何请求的 Challenge
logger.info("UID[%s] 正在使用 recognize 重新请求签到", client.uid)
logger.info("UID[%s] 正在使用 recognize 重新请求签到", client.player_id)
_request_daily_reward = await client.request_daily_reward(
"sign",
method="POST",
@ -186,8 +188,8 @@ class SignSystem(Plugin):
if _request_daily_reward and _request_daily_reward.get("success", 0) == 1:
_gt = _request_daily_reward.get("gt", "")
_challenge = _request_daily_reward.get("challenge", "")
logger.info("UID[%s] 创建验证码\ngt[%s]\nchallenge[%s]", client.uid, _gt, _challenge)
_validate = await RecognizeSystem.recognize(_gt, _challenge, uid=client.uid)
logger.info("UID[%s] 创建验证码\ngt[%s]\nchallenge[%s]", client.player_id, _gt, _challenge)
_validate = await RecognizeSystem.recognize(_gt, _challenge, uid=client.player_id)
if _validate:
logger.success("recognize 通过验证成功\nchallenge[%s]\nvalidate[%s]", _challenge, _validate)
request_daily_reward = await client.request_daily_reward(
@ -199,55 +201,57 @@ class SignSystem(Plugin):
validate=_validate,
)
if request_daily_reward and request_daily_reward.get("success", 0) == 1:
logger.warning("UID[%s] 触发验证码\nchallenge[%s]", client.uid, _challenge)
logger.warning("UID[%s] 触发验证码\nchallenge[%s]", client.player_id, _challenge)
gt = request_daily_reward.get("gt", "")
challenge = request_daily_reward.get("challenge", "")
logger.success("UID[%s] 创建验证成功\ngt[%s]\nchallenge[%s]", client.uid, gt, challenge)
logger.success(
"UID[%s] 创建验证成功\ngt[%s]\nchallenge[%s]", client.player_id, gt, challenge
)
raise NeedChallenge(
uid=client.uid,
uid=client.player_id,
gt=gt,
challenge=challenge,
)
logger.success("UID[%s] 通过 recognize 签到成功", client.uid)
logger.success("UID[%s] 通过 recognize 签到成功", client.player_id)
else:
request_daily_reward = await client.request_daily_reward(
"sign", method="POST", game=Game.GENSHIN, lang="zh-cn"
)
gt = request_daily_reward.get("gt", "")
challenge = request_daily_reward.get("challenge", "")
logger.success("UID[%s] 创建验证成功\ngt[%s]\nchallenge[%s]", client.uid, gt, challenge)
raise NeedChallenge(uid=client.uid, gt=gt, challenge=challenge)
logger.success("UID[%s] 创建验证成功\ngt[%s]\nchallenge[%s]", client.player_id, gt, challenge)
raise NeedChallenge(uid=client.player_id, gt=gt, challenge=challenge)
else:
request_daily_reward = await client.request_daily_reward(
"sign", method="POST", game=Game.GENSHIN, lang="zh-cn"
)
gt = request_daily_reward.get("gt", "")
challenge = request_daily_reward.get("challenge", "")
logger.success("UID[%s] 创建验证成功\ngt[%s]\nchallenge[%s]", client.uid, gt, challenge)
raise NeedChallenge(uid=client.uid, gt=gt, challenge=challenge)
logger.success("UID[%s] 创建验证成功\ngt[%s]\nchallenge[%s]", client.player_id, gt, challenge)
raise NeedChallenge(uid=client.player_id, gt=gt, challenge=challenge)
else:
logger.success("UID[%s] 签到成功", client.uid)
logger.success("UID[%s] 签到成功", client.player_id)
except TimeoutException as error:
logger.warning("UID[%s] 签到请求超时", client.uid)
logger.warning("UID[%s] 签到请求超时", client.player_id)
if is_raise:
raise error
return "签到失败了呜呜呜 ~ 服务器连接超时 服务器熟啦 ~ "
except AlreadyClaimed as error:
logger.warning("UID[%s] 已经签到", client.uid)
logger.warning("UID[%s] 已经签到", client.player_id)
if is_raise:
raise error
result = "今天旅行者已经签到过了~"
except GenshinException as error:
logger.warning("UID %s 签到失败API返回信息为 %s", client.uid, str(error))
except SimnetBadRequest as error:
logger.warning("UID %s 签到失败API返回信息为 %s", client.player_id, str(error))
if is_raise:
raise error
return f"获取签到状态失败API返回信息为 {str(error)}"
else:
result = "OK"
else:
logger.info("UID[%s] 已经签到", client.uid)
logger.info("UID[%s] 已经签到", client.player_id)
result = "今天旅行者已经签到过了~"
logger.info("UID[%s] 签到结果 %s", client.uid, result)
logger.info("UID[%s] 签到结果 %s", client.player_id, result)
reward = rewards[daily_reward_info.claimed_rewards - (1 if daily_reward_info.signed_in else 0)]
today = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
cn_timezone = datetime.timezone(datetime.timedelta(hours=8))
@ -258,7 +262,7 @@ class SignSystem(Plugin):
message = (
f"#### {title} ####\n"
f"时间:{today} (UTC+8)\n"
f"UID: {client.uid}\n"
f"UID: {client.player_id}\n"
f"今日奖励: {reward.name} × {reward.amount}\n"
f"本月漏签次数:{missed_days}\n"
f"签到结果: {result}"
@ -284,15 +288,15 @@ class SignSystem(Plugin):
continue
user_id = sign_db.user_id
try:
client = await self.genshin_helper.get_genshin_client(user_id)
text = await self.start_sign(client, is_sleep=True, is_raise=True, title=title)
async with self.genshin_helper.genshin(user_id) as client:
text = await self.start_sign(client, is_sleep=True, is_raise=True, title=title)
except InvalidCookies:
text = "自动签到执行失败Cookie无效"
sign_db.status = SignStatusEnum.INVALID_COOKIES
except AlreadyClaimed:
text = "今天旅行者已经签到过了~"
sign_db.status = SignStatusEnum.ALREADY_CLAIMED
except GenshinException as exc:
except SimnetBadRequest as exc:
text = f"自动签到执行失败API返回信息为 {str(exc)}"
sign_db.status = SignStatusEnum.GENSHIN_EXCEPTION
except ClientConnectorError:

1426
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -8,9 +8,8 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.8"
httpx = "^0.23.3"
httpx = "^0.24.0"
ujson = "^5.8.0"
genshin = { git = "https://github.com/thesadru/genshin.py" }
Jinja2 = "^3.1.2"
python-telegram-bot = { version = "^20.1", extras = ["ext", "rate-limiter"] }
sqlmodel = "^0.0.8"
@ -45,6 +44,7 @@ cryptography = "^41.0.2"
pillow = "^10.0.0"
playwright = "^1.27.1"
aiosqlite = { extras = ["sqlite"], version = "^0.19.0" }
simnet = { git = "https://github.com/PaiGramTeam/SIMNet" }
[tool.poetry.extras]
pyro = ["Pyrogram", "TgCrypto"]

View File

@ -1,106 +1,104 @@
aiofiles==23.1.0 ; python_version >= "3.8" and python_version < "4.0"
aiohttp==3.8.4 ; python_version >= "3.8" and python_version < "4.0"
aiolimiter==1.0.0 ; python_version >= "3.8" and python_version < "4.0"
aiolimiter==1.1.0 ; python_version >= "3.8" and python_version < "4.0"
aiosignal==1.3.1 ; python_version >= "3.8" and python_version < "4.0"
aiosqlite[sqlite]==0.19.0 ; python_version >= "3.8" and python_version < "4.0"
alembic==1.10.3 ; python_version >= "3.8" and python_version < "4.0"
anyio==3.6.2 ; python_version >= "3.8" and python_version < "4.0"
alembic==1.11.1 ; python_version >= "3.8" and python_version < "4.0"
anyio==3.7.1 ; python_version >= "3.8" and python_version < "4.0"
appdirs==1.4.4 ; python_version >= "3.8" and python_version < "4.0"
apscheduler==3.10.1 ; python_version >= "3.8" and python_version < "4.0"
arko-wrapper==0.2.8 ; python_version >= "3.8" and python_version < "4.0"
async-lru==2.0.2 ; python_version >= "3.8" and python_version < "4.0"
async-lru==2.0.3 ; python_version >= "3.8" and python_version < "4.0"
async-timeout==4.0.2 ; python_version >= "3.8" and python_version < "4.0"
asyncmy==0.2.7 ; python_version >= "3.8" and python_version < "4.0"
asyncmy==0.2.8 ; python_version >= "3.8" and python_version < "4.0"
attrs==23.1.0 ; python_version >= "3.8" and python_version < "4.0"
backports-zoneinfo==0.2.1 ; python_version >= "3.8" and python_version < "3.9"
beautifulsoup4==4.12.2 ; python_version >= "3.8" and python_version < "4.0"
black==23.3.0 ; python_version >= "3.8" and python_version < "4.0"
cachetools==5.3.0 ; python_version >= "3.8" and python_version < "4.0"
certifi==2022.12.7 ; python_version >= "3.8" and python_version < "4.0"
black==23.7.0 ; python_version >= "3.8" and python_version < "4.0"
cachetools==5.3.1 ; python_version >= "3.8" and python_version < "4.0"
certifi==2023.5.7 ; python_version >= "3.8" and python_version < "4.0"
cffi==1.15.1 ; python_version >= "3.8" and python_version < "4.0"
charset-normalizer==3.1.0 ; python_version >= "3.8" and python_version < "4.0"
click==8.1.3 ; python_version >= "3.8" and python_version < "4.0"
colorama==0.4.6 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "win32" or python_version >= "3.8" and python_version < "4.0" and platform_system == "Windows"
charset-normalizer==3.2.0 ; python_version >= "3.8" and python_version < "4.0"
click==8.1.5 ; python_version >= "3.8" and python_version < "4.0"
colorama==0.4.6 ; python_version >= "3.8" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows")
colorlog==6.7.0 ; python_version >= "3.8" and python_version < "4.0"
cryptography==40.0.2 ; python_version >= "3.8" and python_version < "4.0"
cryptography==41.0.2 ; python_version >= "3.8" and python_version < "4.0"
enkanetwork-py @ git+https://github.com/mrwan200/EnkaNetwork.py@master ; python_version >= "3.8" and python_version < "4.0"
et-xmlfile==1.1.0 ; python_version >= "3.8" and python_version < "4.0"
exceptiongroup==1.1.1 ; python_version >= "3.8" and python_version < "3.11"
fakeredis==2.10.3 ; python_version >= "3.8" and python_version < "4.0"
fastapi==0.95.1 ; python_version >= "3.8" and python_version < "4.0"
exceptiongroup==1.1.2 ; python_version >= "3.8" and python_version < "3.11"
fakeredis==2.16.0 ; python_version >= "3.8" and python_version < "4.0"
fastapi==0.99.1 ; python_version >= "3.8" and python_version < "4.0"
flaky==3.7.0 ; python_version >= "3.8" and python_version < "4.0"
frozenlist==1.3.3 ; python_version >= "3.8" and python_version < "4.0"
genshin @ git+https://github.com/thesadru/genshin.py@master ; python_version >= "3.8" and python_version < "4.0"
frozenlist==1.4.0 ; python_version >= "3.8" and python_version < "4.0"
gitdb==4.0.10 ; python_version >= "3.8" and python_version < "4.0"
gitpython==3.1.31 ; python_version >= "3.8" and python_version < "4.0"
gitpython==3.1.32 ; python_version >= "3.8" and python_version < "4.0"
greenlet==1.1.3 ; python_version >= "3.8" and python_version < "4.0"
h11==0.14.0 ; python_version >= "3.8" and python_version < "4.0"
httpcore==0.16.3 ; python_version >= "3.8" and python_version < "4.0"
httptools==0.5.0 ; python_version >= "3.8" and python_version < "4.0"
httpx==0.23.3 ; python_version >= "3.8" and python_version < "4.0"
httpcore==0.17.3 ; python_version >= "3.8" and python_version < "4.0"
httptools==0.6.0 ; python_version >= "3.8" and python_version < "4.0"
httpx==0.24.1 ; python_version >= "3.8" and python_version < "4.0"
idna==3.4 ; python_version >= "3.8" and python_version < "4.0"
importlib-metadata==6.5.0 ; python_version >= "3.8" and python_version < "4.0"
importlib-resources==5.12.0 ; python_version >= "3.8" and python_version < "3.9"
importlib-metadata==6.8.0 ; python_version >= "3.8" and python_version < "4.0"
importlib-resources==6.0.0 ; python_version >= "3.8" and python_version < "3.9"
iniconfig==2.0.0 ; python_version >= "3.8" and python_version < "4.0"
jinja2==3.1.2 ; python_version >= "3.8" and python_version < "4.0"
lxml==4.9.2 ; python_version >= "3.8" and python_version < "4.0"
lxml==4.9.3 ; python_version >= "3.8" and python_version < "4.0"
mako==1.2.4 ; python_version >= "3.8" and python_version < "4.0"
markdown-it-py==2.2.0 ; python_version >= "3.8" and python_version < "4.0"
markupsafe==2.1.2 ; python_version >= "3.8" and python_version < "4.0"
markdown-it-py==3.0.0 ; python_version >= "3.8" and python_version < "4.0"
markupsafe==2.1.3 ; python_version >= "3.8" and python_version < "4.0"
mdurl==0.1.2 ; python_version >= "3.8" and python_version < "4.0"
multidict==6.0.4 ; python_version >= "3.8" and python_version < "4.0"
mypy-extensions==1.0.0 ; python_version >= "3.8" and python_version < "4.0"
openpyxl==3.1.2 ; python_version >= "3.8" and python_version < "4.0"
packaging==23.1 ; python_version >= "3.8" and python_version < "4.0"
pathspec==0.11.1 ; python_version >= "3.8" and python_version < "4.0"
pillow==9.5.0 ; python_version >= "3.8" and python_version < "4.0"
platformdirs==3.2.0 ; python_version >= "3.8" and python_version < "4.0"
pillow==10.0.0 ; python_version >= "3.8" and python_version < "4.0"
platformdirs==3.9.1 ; python_version >= "3.8" and python_version < "4.0"
playwright==1.27.1 ; python_version >= "3.8" and python_version < "4.0"
pluggy==1.0.0 ; python_version >= "3.8" and python_version < "4.0"
pluggy==1.2.0 ; python_version >= "3.8" and python_version < "4.0"
pyaes==1.6.1 ; python_version >= "3.8" and python_version < "4.0"
pycparser==2.21 ; python_version >= "3.8" and python_version < "4.0"
pydantic==1.10.7 ; python_version >= "3.8" and python_version < "4.0"
pydantic==1.10.11 ; python_version >= "3.8" and python_version < "4.0"
pyee==8.1.0 ; python_version >= "3.8" and python_version < "4.0"
pygments==2.15.1 ; python_version >= "3.8" and python_version < "4.0"
pypng==0.20220715.0 ; python_version >= "3.8" and python_version < "4.0"
pyppeteer==1.0.2 ; python_version >= "3.8" and python_version < "4.0"
pyrogram==2.0.104 ; python_version >= "3.8" and python_version < "4.0"
pyrogram==2.0.106 ; python_version >= "3.8" and python_version < "4.0"
pysocks==1.7.1 ; python_version >= "3.8" and python_version < "4.0"
pytest-asyncio==0.21.0 ; python_version >= "3.8" and python_version < "4.0"
pytest==7.3.1 ; python_version >= "3.8" and python_version < "4.0"
pytest-asyncio==0.21.1 ; python_version >= "3.8" and python_version < "4.0"
pytest==7.4.0 ; python_version >= "3.8" and python_version < "4.0"
python-dotenv==1.0.0 ; python_version >= "3.8" and python_version < "4.0"
python-telegram-bot[ext,rate-limiter]==20.2 ; python_version >= "3.8" and python_version < "4.0"
pytz-deprecation-shim==0.1.0.post0 ; python_version >= "3.8" and python_version < "4.0"
python-telegram-bot[ext,rate-limiter]==20.4 ; python_version >= "3.8" and python_version < "4.0"
pytz==2023.3 ; python_version >= "3.8" and python_version < "4.0"
pyyaml==6.0 ; python_version >= "3.8" and python_version < "4.0"
pyyaml==6.0.1 ; python_version >= "3.8" and python_version < "4.0"
qrcode==7.4.2 ; python_version >= "3.8" and python_version < "4.0"
redis==4.5.4 ; python_version >= "3.8" and python_version < "4.0"
rfc3986[idna2008]==1.5.0 ; python_version >= "3.8" and python_version < "4.0"
rich==13.3.4 ; python_version >= "3.8" and python_version < "4.0"
sentry-sdk==1.20.0 ; python_version >= "3.8" and python_version < "4.0"
setuptools==67.7.1 ; python_version >= "3.8" and python_version < "4.0"
redis==4.6.0 ; python_version >= "3.8" and python_version < "4.0"
rich==13.4.2 ; python_version >= "3.8" and python_version < "4.0"
sentry-sdk==1.28.1 ; python_version >= "3.8" and python_version < "4.0"
setuptools==68.0.0 ; python_version >= "3.8" and python_version < "4.0"
simnet @ git+https://github.com/PaiGramTeam/SIMNet@main ; python_version >= "3.8" and python_version < "4.0"
six==1.16.0 ; python_version >= "3.8" and python_version < "4.0"
smmap==5.0.0 ; python_version >= "3.8" and python_version < "4.0"
sniffio==1.3.0 ; python_version >= "3.8" and python_version < "4.0"
sortedcontainers==2.4.0 ; python_version >= "3.8" and python_version < "4.0"
soupsieve==2.4.1 ; python_version >= "3.8" and python_version < "4.0"
sqlalchemy2-stubs==0.0.2a34 ; python_version >= "3.8" and python_version < "4.0"
sqlalchemy2-stubs==0.0.2a35 ; python_version >= "3.8" and python_version < "4.0"
sqlalchemy==1.4.41 ; python_version >= "3.8" and python_version < "4.0"
sqlmodel==0.0.8 ; python_version >= "3.8" and python_version < "4.0"
starlette==0.26.1 ; python_version >= "3.8" and python_version < "4.0"
starlette==0.27.0 ; python_version >= "3.8" and python_version < "4.0"
tgcrypto==1.2.5 ; python_version >= "3.8" and python_version < "4.0"
thefuzz==0.19.0 ; python_version >= "3.8" and python_version < "4.0"
tomli==2.0.1 ; python_version >= "3.8" and python_version < "3.11"
tornado==6.3 ; python_version >= "3.8" and python_version < "4.0"
tornado==6.3.2 ; python_version >= "3.8" and python_version < "4.0"
tqdm==4.65.0 ; python_version >= "3.8" and python_version < "4.0"
typing-extensions==4.5.0 ; python_version >= "3.8" and python_version < "4.0"
tzdata==2023.3 ; python_version >= "3.8" and python_version < "4.0"
tzlocal==4.3 ; python_version >= "3.8" and python_version < "4.0"
ujson==5.7.0 ; python_version >= "3.8" and python_version < "4.0"
urllib3==1.26.15 ; python_version >= "3.8" and python_version < "4.0"
uvicorn[standard]==0.21.1 ; python_version >= "3.8" and python_version < "4.0"
uvloop==0.17.0 ; sys_platform != "win32" and sys_platform != "cygwin" and platform_python_implementation != "PyPy" and python_version >= "3.8" and python_version < "4.0"
typing-extensions==4.7.1 ; python_version >= "3.8" and python_version < "4.0"
tzdata==2023.3 ; python_version >= "3.8" and python_version < "4.0" and platform_system == "Windows"
tzlocal==5.0.1 ; python_version >= "3.8" and python_version < "4.0"
ujson==5.8.0 ; python_version >= "3.8" and python_version < "4.0"
urllib3==1.26.16 ; python_version >= "3.8" and python_version < "4.0"
uvicorn[standard]==0.22.0 ; python_version >= "3.8" and python_version < "4.0"
uvloop==0.17.0 ; (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" and python_version >= "3.8" and python_version < "4.0"
watchfiles==0.19.0 ; python_version >= "3.8" and python_version < "4.0"
websockets==10.4 ; python_version >= "3.8" and python_version < "4.0"
yarl==1.8.2 ; python_version >= "3.8" and python_version < "4.0"
zipp==3.15.0 ; python_version >= "3.8" and python_version < "4.0"
yarl==1.9.2 ; python_version >= "3.8" and python_version < "4.0"
zipp==3.16.2 ; python_version >= "3.8" and python_version < "4.0"

View File

@ -1,64 +0,0 @@
from typing import Optional
from genshin import Client
from genshin.client.routes import InternationalRoute # noqa F401
from genshin.utility import recognize_genshin_server
from modules.apihelper.utility.devices import devices_methods
from modules.apihelper.utility.helpers import hex_digest, get_ds
AUTHKEY_API = "https://api-takumi.mihoyo.com/binding/api/genAuthKey"
HK4E_LOGIN_URL = InternationalRoute(
overseas="https://sg-public-api.hoyoverse.com/common/badge/v1/login/account",
chinese="https://api-takumi.mihoyo.com/common/badge/v1/login/account",
)
GACHA_HEADERS = {
"User-Agent": "okhttp/4.8.0",
"x-rpc-sys_version": "12",
"x-rpc-channel": "mihoyo",
"x-rpc-device_name": "",
"x-rpc-device_model": "",
"Referer": "https://app.mihoyo.com",
"Host": "api-takumi.mihoyo.com",
}
def recognize_genshin_game_biz(game_uid: int) -> str:
return "hk4e_cn" if game_uid < 600000000 else "hk4e_global"
async def get_authkey_by_stoken(client: Client, auth_appid: str = "webview_gacha") -> Optional[str]:
"""通过 stoken 获取 authkey"""
headers = GACHA_HEADERS.copy()
json = {
"auth_appid": auth_appid,
"game_biz": recognize_genshin_game_biz(client.uid),
"game_uid": client.uid,
"region": recognize_genshin_server(client.uid),
}
device_id = hex_digest(str(client.uid))
device = f"Paimon Build {device_id[:5]}"
await devices_methods.update_device_headers(client.hoyolab_id, headers)
headers["x-rpc-device_name"] = device
headers["x-rpc-device_model"] = device
app_version, client_type, ds_sign = get_ds()
headers["x-rpc-app_version"] = app_version
headers["x-rpc-client_type"] = client_type
headers["ds"] = ds_sign
data = await client.cookie_manager.request(AUTHKEY_API, method="POST", json=json, headers=headers)
return data.get("authkey")
async def fetch_hk4e_token_by_cookie(client: Client) -> None:
"""通过 cookie_token 获取 hk4e_token 保存到 client"""
url = HK4E_LOGIN_URL.get_url(client.region)
headers = {
"Content-Type": "application/json;charset=UTF-8",
}
json = {
"game_biz": recognize_genshin_game_biz(client.uid),
"lang": "zh-cn",
"uid": str(client.uid),
"region": recognize_genshin_server(client.uid),
}
await client.cookie_manager.request(url, method="POST", json=json, headers=headers)

View File

@ -1,3 +1,3 @@
from utils.patch import aiohttp, genshin
from utils.patch import aiohttp
__all__ = ["aiohttp", "genshin"]
__all__ = ["aiohttp"]

View File

@ -1,358 +0,0 @@
import asyncio
import typing
import warnings
import aiohttp.typedefs
import genshin # pylint: disable=W0406
import yarl
from genshin import constants, types, utility
from genshin.client import routes
from genshin.utility import generate_dynamic_secret, ds
from modules.apihelper.utility.devices import devices_methods
from modules.apihelper.utility.helpers import get_ds, get_ua, get_device_id, hex_digest
from utils.patch.methods import patch, patchable
DEVICE_ID = get_device_id()
UPDATE_CHARACTERS = False
def get_account_mid_v2(cookies: typing.Dict[str, str]) -> typing.Optional[str]:
return next(
(value for name, value in cookies.items() if name == "account_mid_v2"),
None,
)
@patch(genshin.client.components.calculator.CalculatorClient) # noqa
class CalculatorClient:
@patchable
async def request_calculator(
self,
endpoint: str,
*,
method: str = "POST",
lang: typing.Optional[str] = None,
params: typing.Optional[typing.Mapping[str, typing.Any]] = None,
data: typing.Optional[typing.Mapping[str, typing.Any]] = None,
headers: typing.Optional[aiohttp.typedefs.LooseHeaders] = None,
**kwargs: typing.Any,
) -> typing.Mapping[str, typing.Any]:
global UPDATE_CHARACTERS # pylint: disable=W0603
params = dict(params or {})
headers = dict(headers or {})
base_url = routes.CALCULATOR_URL.get_url(self.region)
url = base_url / endpoint
if method == "GET":
params["lang"] = lang or self.lang
data = None
else:
data = dict(data or {})
data["lang"] = lang or self.lang
if self.region == types.Region.CHINESE:
headers["referer"] = str(routes.CALCULATOR_REFERER_URL.get_url())
update_task = (
None
if UPDATE_CHARACTERS
else asyncio.create_task(utility.update_characters_any(lang or self.lang, lenient=True))
)
data = await self.request(url, method=method, params=params, data=data, headers=headers, **kwargs)
if update_task:
try:
await update_task
UPDATE_CHARACTERS = True
except Exception as e: # pylint: disable=W0703
warnings.warn(f"Failed to update characters: {e!r}")
return data
@patchable
async def get_character_details(
self,
character: genshin.types.IDOr[genshin.models.genshin.Character],
*,
uid: typing.Optional[int] = None,
lang: typing.Optional[str] = None,
):
uid = uid or await self._get_uid(genshin.types.Game.GENSHIN)
data = await self.request_calculator(
"sync/avatar/detail",
method="GET",
lang=lang,
params=dict(
avatar_id=int(character),
uid=uid,
region=genshin.utility.recognize_genshin_server(uid),
),
)
if data.get("weapon") is None:
weapon = {
"id": character.weapon.id,
"name": character.weapon.name,
"icon": character.weapon.icon,
"weapon_cat_id": character.weapon.type,
"weapon_level": character.weapon.rarity,
"max_level": 90,
"level_current": character.weapon.level,
}
data["weapon"] = weapon
return genshin.models.genshin.CalculatorCharacterDetails(**data)
@patch(genshin.client.components.base.BaseClient) # noqa
class BaseClient:
@patchable
async def request_hoyolab(
self: "genshin.Client",
url: aiohttp.typedefs.StrOrURL,
*,
lang: typing.Optional[str] = None,
region: typing.Optional[types.Region] = None,
method: typing.Optional[str] = None,
params: typing.Optional[typing.Mapping[str, typing.Any]] = None,
data: typing.Any = None,
headers: typing.Optional[aiohttp.typedefs.LooseHeaders] = None,
**kwargs: typing.Any,
) -> typing.Mapping[str, typing.Any]:
"""Make a request any hoyolab endpoint."""
if lang is not None and lang not in constants.LANGS:
raise ValueError(f"{lang} is not a valid language, must be one of: " + ", ".join(constants.LANGS))
lang = lang or self.lang
region = region or self.region
url = routes.TAKUMI_URL.get_url(region).join(yarl.URL(url))
if region == types.Region.OVERSEAS:
headers = {
"x-rpc-app_version": "1.5.0",
"x-rpc-client_type": "5",
"x-rpc-language": lang,
"ds": generate_dynamic_secret(),
}
elif region == types.Region.CHINESE:
account_id = self.cookie_manager.user_id
if account_id:
device_id = hex_digest(str(account_id))
else:
account_mid_v2 = get_account_mid_v2(self.cookie_manager.cookies)
if account_mid_v2:
device_id = hex_digest(account_mid_v2)
else:
device_id = DEVICE_ID
app_version, client_type, ds_sign = get_ds(new_ds=True, data=data, params=params)
ua = get_ua(device="Paimon Build " + device_id[0:5], version=app_version)
headers = {
"User-Agent": ua,
"X_Requested_With": "com.mihoyo.hoyolab",
"Referer": "https://webstatic.mihoyo.com",
"x-rpc-app_version": app_version,
"x-rpc-client_type": client_type,
"ds": ds_sign,
}
await devices_methods.update_device_headers(self.hoyolab_id, headers)
else:
raise TypeError(f"{region!r} is not a valid region.")
data = await self.request(url, method=method, params=params, data=data, headers=headers, **kwargs)
return data
@patchable
async def request(
self: "genshin.Client",
url: aiohttp.typedefs.StrOrURL,
*,
method: typing.Optional[str] = None,
params: typing.Optional[typing.Mapping[str, typing.Any]] = None,
data: typing.Any = None,
headers: typing.Optional[aiohttp.typedefs.LooseHeaders] = None,
cache: typing.Any = None,
static_cache: typing.Any = None,
**kwargs: typing.Any,
) -> typing.Mapping[str, typing.Any]:
"""Make a request and return a parsed json response."""
if cache is not None:
value = await self.cache.get(cache)
if value is not None:
return value
elif static_cache is not None:
value = await self.cache.get_static(static_cache)
if value is not None:
return value
# actual request
headers = dict(headers or {})
headers.setdefault("User-Agent", self.USER_AGENT)
await devices_methods.update_device_headers(self.hoyolab_id, headers)
if method is None:
method = "POST" if data else "GET"
if "json" in kwargs:
raise TypeError("Use data instead of json in request.")
await self._request_hook(method, url, params=params, data=data, headers=headers, **kwargs)
response = await self.cookie_manager.request(
url,
method=method,
params=params,
json=data,
headers=headers,
**kwargs,
)
# cache
if cache is not None:
await self.cache.set(cache, response)
elif static_cache is not None:
await self.cache.set_static(static_cache, response)
return response
@patchable
async def request_bbs(
self: "genshin.Client",
url: aiohttp.typedefs.StrOrURL,
*,
lang: typing.Optional[str] = None,
region: typing.Optional[types.Region] = None,
method: typing.Optional[str] = None,
params: typing.Optional[typing.Mapping[str, typing.Any]] = None,
data: typing.Any = None,
headers: typing.Optional[aiohttp.typedefs.LooseHeaders] = None,
**kwargs: typing.Any,
) -> typing.Mapping[str, typing.Any]:
"""Make a request any bbs endpoint."""
if lang is not None and lang not in constants.LANGS:
raise ValueError(f"{lang} is not a valid language, must be one of: " + ", ".join(constants.LANGS))
lang = lang or self.lang
region = region or self.region
url = routes.BBS_URL.get_url(region).join(yarl.URL(url))
headers = dict(headers or {})
if self.region == types.Region.CHINESE:
if self.region == types.Region.CHINESE:
account_id = self.cookie_manager.user_id
if account_id:
device_id = hex_digest(str(account_id))
else:
account_mid_v2 = get_account_mid_v2(self.cookie_manager.cookies)
if account_mid_v2:
device_id = hex_digest(account_mid_v2)
else:
device_id = DEVICE_ID
app_version, _, ds_sign = get_ds()
ua = get_ua(device="Paimon Build " + device_id[0:5], version=app_version)
add_headers = {
"User-Agent": ua,
"Referer": "https://www.miyoushe.com/ys/",
"x-rpc-app_version": app_version,
"x-rpc-client_type": "4",
"ds": ds_sign,
}
headers.update(add_headers)
await devices_methods.update_device_headers(self.hoyolab_id, headers)
elif self.region == types.Region.OVERSEAS:
headers.update(ds.get_ds_headers(data=data, params=params, region=region, lang=lang or self.lang))
headers["Referer"] = str(routes.BBS_REFERER_URL.get_url(self.region))
else:
raise TypeError(f"{region!r} is not a valid region.")
data = await self.request(url, method=method, params=params, data=data, headers=headers, **kwargs)
return data
@patch(genshin.client.components.daily.DailyRewardClient) # noqa
class DailyRewardClient:
@patchable
async def request_daily_reward(
self: "genshin.Client",
endpoint: str,
*,
game: typing.Optional[types.Game] = None,
method: str = "GET",
lang: typing.Optional[str] = None,
params: typing.Optional[typing.Mapping[str, typing.Any]] = None,
headers: typing.Optional[aiohttp.typedefs.LooseHeaders] = None,
**kwargs: typing.Any,
) -> typing.Mapping[str, typing.Any]:
"""Make a request towards the daily reward endpoint."""
params = dict(params or {})
headers = dict(headers or {})
if game is None:
if self.default_game is None:
raise RuntimeError("No default game set.")
game = self.default_game
base_url = routes.REWARD_URL.get_url(self.region, game)
url = (base_url / endpoint).update_query(**base_url.query)
if self.region == types.Region.OVERSEAS:
params["lang"] = lang or self.lang
elif self.region == types.Region.CHINESE:
# TODO: Support cn honkai
player_id = await self._get_uid(types.Game.GENSHIN)
params["uid"] = player_id
params["region"] = utility.recognize_genshin_server(player_id)
account_id = self.cookie_manager.user_id
if account_id:
device_id = hex_digest(str(account_id))
else:
account_mid_v2 = get_account_mid_v2(self.cookie_manager.cookies)
if account_mid_v2:
device_id = hex_digest(account_mid_v2)
else:
device_id = DEVICE_ID
if endpoint == "sign":
app_version, client_type, ds_sign = get_ds()
else:
app_version, client_type, ds_sign = get_ds(new_ds=True, params=params)
device = "Paimon Build " + device_id[0:5]
ua = get_ua(device=device)
headers["User-Agent"] = ua
headers["X_Requested_With"] = "com.mihoyo.hoyolab"
headers["Referer"] = (
"https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?"
"bbs_auth_required=true&act_id=e202009291139501&utm_source=bbs&utm_medium=mys&utm_campaign=icon"
)
headers["x-rpc-device_name"] = device
headers["x-rpc-app_version"] = app_version
headers["x-rpc-client_type"] = client_type
headers["x-rpc-sys_version"] = "12"
headers["x-rpc-platform"] = "android"
headers["x-rpc-channel"] = "miyousheluodi"
headers["x-rpc-device_model"] = device
headers["ds"] = ds_sign
validate = kwargs.get("validate")
challenge = kwargs.get("challenge")
if validate and challenge:
headers["x-rpc-challenge"] = challenge
headers["x-rpc-validate"] = validate
headers["x-rpc-seccode"] = f"{validate}|jordan"
await devices_methods.update_device_headers(self.hoyolab_id, headers)
else:
raise TypeError(f"{self.region!r} is not a valid region.")
kwargs.pop("challenge", None)
kwargs.pop("validate", None)
return await self.request(url, method=method, params=params, headers=headers, **kwargs)