♻ 重写 apihelper 模块

This commit is contained in:
洛水居室 2022-10-08 08:59:08 +08:00 committed by GitHub
parent 0fe61d5f44
commit 29a5508174
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 386 additions and 539 deletions

View File

@ -16,9 +16,7 @@ class GameStrategyService:
async def _get_strategy_from_hyperion(self, collection_id: int, character_name: str) -> int: async def _get_strategy_from_hyperion(self, collection_id: int, character_name: str) -> int:
post_id: int = -1 post_id: int = -1
post_full_in_collection = await self._hyperion.get_post_full_in_collection(collection_id) post_full_in_collection = await self._hyperion.get_post_full_in_collection(collection_id)
if post_full_in_collection.error: for post_data in post_full_in_collection["posts"]:
return post_id
for post_data in post_full_in_collection.data["posts"]:
topics = post_data["topics"] topics = post_data["topics"]
for topic in topics: for topic in topics:
if character_name == topic["name"]: if character_name == topic["name"]:
@ -40,9 +38,9 @@ class GameStrategyService:
else: else:
return "" return ""
artwork_info = await self._hyperion.get_artwork_info(2, post_id) artwork_info = await self._hyperion.get_post_info(2, post_id)
await self._cache.set_url_list(character_name, artwork_info.results.image_url_list) await self._cache.set_url_list(character_name, artwork_info.image_urls)
return artwork_info.results.image_url_list[0] return artwork_info.image_urls[0]
class GameMaterialService: class GameMaterialService:
@ -55,9 +53,7 @@ class GameMaterialService:
async def _get_material_from_hyperion(self, collection_id: int, character_name: str) -> int: async def _get_material_from_hyperion(self, collection_id: int, character_name: str) -> int:
post_id: int = -1 post_id: int = -1
post_full_in_collection = await self._hyperion.get_post_full_in_collection(collection_id) post_full_in_collection = await self._hyperion.get_post_full_in_collection(collection_id)
if post_full_in_collection.error: for post_data in post_full_in_collection["posts"]:
return post_id
for post_data in post_full_in_collection.data["posts"]:
topics = post_data["topics"] topics = post_data["topics"]
for topic in topics: for topic in topics:
if character_name == topic["name"]: if character_name == topic["name"]:
@ -84,9 +80,9 @@ class GameMaterialService:
else: else:
return "" return ""
artwork_info = await self._hyperion.get_artwork_info(2, post_id) artwork_info = await self._hyperion.get_post_info(2, post_id)
await self._cache.set_url_list(character_name, artwork_info.results.image_url_list) await self._cache.set_url_list(character_name, artwork_info.image_urls)
image_url_list = artwork_info.results.image_url_list image_url_list = artwork_info.image_urls
if len(image_url_list) == 0: if len(image_url_list) == 0:
return "" return ""
elif len(image_url_list) == 1: elif len(image_url_list) == 1:

View File

@ -50,7 +50,8 @@ class ArtifactOcrRate:
'sec-fetch-mode': 'cors', 'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-site', 'sec-fetch-site': 'same-site',
'sec-gpc': '1', 'sec-gpc': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.115 Safari/537.36', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/104.0.5112.115 Safari/537.36',
} }
def __init__(self): def __init__(self):

View File

@ -1,158 +1,50 @@
import imghdr import imghdr
from enum import Enum from typing import List, Any
from pydantic import BaseModel, PrivateAttr
class ArtworkImage: class ArtworkImage(BaseModel):
art_id: int
page: int = 0
data: bytes = b""
is_error: bool = False
def __init__(self, art_id: int, page: int = 0, is_error: bool = False, data: bytes = b""): @property
""" def format(self) -> str:
:param art_id: 插画ID if self.is_error:
:param page: 页数 return ""
:param is_error: 插画是否有问题
:param data: 插画数据
"""
self.art_id = art_id
self.data = data
self.is_error = is_error
if not is_error:
self.format: str = imghdr.what(None, self.data)
self.page = page
class BaseResponseData:
def __init__(self, response=None, error_message: str = ""):
"""
:param response: 相应
:param error_message: 错误信息
"""
if response is None:
self.error: bool = True
self.message: str = error_message
return
self.response: dict = response
self.code = response["retcode"]
if self.code == 0:
self.error = False
else: else:
self.error = True imghdr.what(None, self.data)
self.message = response["message"]
self.data = response["data"]
class Stat: class PostInfo(BaseModel):
def __init__(self, view_num: int = 0, reply_num: int = 0, like_num: int = 0, bookmark_num: int = 0, _data: dict = PrivateAttr()
forward_num: int = 0): post_id: int
self.forward_num = forward_num # 关注数 user_uid: int
self.bookmark_num = bookmark_num # 收藏数 subject: str
self.like_num = like_num # 喜欢数 image_urls: List[str]
self.reply_num = reply_num # 回复数 created_at: int
self.view_num = view_num # 观看数
def __init__(self, _data: dict, **data: Any):
super().__init__(**data)
self._data = _data
class ArtworkInfo: @classmethod
def __init__(self, post_id: int = 0, subject: str = "", tags=None, def paste_data(cls, data: dict) -> "PostInfo":
image_url_list=None, stat: Stat = None, uid: int = 0, created_at: int = 0): image_urls = []
""" _data_post = data["post"]
:param post_id: post_id post = _data_post["post"]
:param subject: 标题
:param tags: 标签
:param image_url_list: 图片URL列表
:param stat: 统计
:param uid: 用户UID
:param created_at: 创建时间
"""
if tags is None:
self.tags = []
else:
self.tags = tags
if image_url_list is None:
self.image_url_list = []
else:
self.image_url_list = image_url_list
self.Stat = stat
self.created_at = created_at
self.uid = uid
self.subject = subject
self.post_id = post_id
class HyperionResponse:
"""
:param response: 相应
:param error_message: 错误信息
"""
def __init__(self, response=None, error_message: str = ""):
if response is None:
self.error: bool = True
self.message: str = error_message
return
self.response: dict = response
self.code = response["retcode"]
if self.code == 0:
self.error = False
else:
if self.code == 1102:
self.message = "作品不存在"
self.error = True
return
if response["data"] is None:
self.error = True
self.message: str = response["message"]
if self.error:
return
try:
self._data_post = response["data"]["post"]
post = self._data_post["post"] # 投稿信息
post_id = post["post_id"] post_id = post["post_id"]
subject = post["subject"] # 介绍类似title标题 subject = post["subject"]
created_at = post["created_at"] # 创建时间 image_list = _data_post["image_list"]
user = self._data_post["user"] # 用户数据
uid = user["uid"] # 用户ID
topics = self._data_post["topics"] # 存放 Tag
image_list = self._data_post["image_list"] # image_list
except (AttributeError, TypeError) as err:
self.error: bool = True
self.message: str = err
return
topics_list = []
image_url_list = []
for topic in topics:
topics_list.append(topic["name"])
for image in image_list: for image in image_list:
image_url_list.append(image["url"]) image_urls.append(image["url"])
self.post_id = post["post_id"] created_at = post["created_at"]
self.user_id = user["uid"] user = _data_post["user"] # 用户数据
self.created_at = post["created_at"] user_uid = user["uid"] # 用户ID
stat = Stat(view_num=self._data_post["stat"]["view_num"], return PostInfo(_data=data, post_id=post_id, user_uid=user_uid, subject=subject, image_urls=image_urls,
reply_num=self._data_post["stat"]["reply_num"], created_at=created_at)
like_num=self._data_post["stat"]["like_num"],
bookmark_num=self._data_post["stat"]["bookmark_num"],
forward_num=self._data_post["stat"]["forward_num"],
)
self.results = ArtworkInfo(
subject=subject,
created_at=created_at,
uid=uid,
stat=stat,
tags=topics_list,
post_id=post_id,
image_url_list=image_url_list
)
def __bool__(self): def __getitem__(self, item):
""" return self._data[item]
:return: 是否错误
"""
return self.error
def __len__(self):
"""
:return: 插画连接数量
"""
return len(self.results.image_url_list)
class ServiceEnum(Enum):
HYPERION = 1
HOYOLAB = 2

View File

@ -0,0 +1,41 @@
from typing import Mapping, Any, Optional
class APIHelperException(Exception):
pass
class NetworkException(APIHelperException):
pass
class TimedOut(APIHelperException):
pass
class ResponseException(APIHelperException):
code: int = 0
message: str = ""
def __init__(self, response: Optional[Mapping[str, Any]] = None, message: Optional[str] = None) -> None:
if response is None:
self.message = message
_message = message
else:
self.code = response.get("retcode", self.code)
self.message = response.get("message", "")
_message = f"[{self.code}] {self.message}"
super().__init__(_message)
class DataNotFoundError(ResponseException):
def __init__(self):
message = "response data not find"
super().__init__(message=message)
class ReturnCodeError(ResponseException):
def __init__(self):
message = "response return code error"
super().__init__(message=message)

View File

@ -1,44 +0,0 @@
import time
import httpx
from modules.apihelper.base import BaseResponseData
class GachaInfo:
GACHA_LIST_URL = "https://webstatic.mihoyo.com/hk4e/gacha_info/cn_gf01/gacha/list.json"
GACHA_INFO_URL = "https://webstatic.mihoyo.com/hk4e/gacha_info/cn_gf01/%s/zh-cn.json"
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " \
"Chrome/90.0.4430.72 Safari/537.36"
def __init__(self):
self.headers = {
'User-Agent': self.USER_AGENT,
}
self.client = httpx.AsyncClient(headers=self.headers)
self.cache = {}
self.cache_ttl = 600
async def get_gacha_list_info(self) -> BaseResponseData:
if self.cache.get("time", 0) + self.cache_ttl < time.time():
self.cache.clear()
cache = self.cache.get("gacha_list_info")
if cache is not None:
return BaseResponseData(cache)
req = await self.client.get(self.GACHA_LIST_URL)
if req.is_error:
return BaseResponseData(error_message="请求错误")
self.cache["gacha_list_info"] = req.json()
self.cache["time"] = time.time()
return BaseResponseData(req.json())
async def get_gacha_info(self, gacha_id: str) -> dict:
cache = self.cache.get(gacha_id)
if cache is not None:
return cache
req = await self.client.get(self.GACHA_INFO_URL % gacha_id)
if req.is_error:
return {}
self.cache[gacha_id] = req.json()
return req.json()

View File

@ -1,96 +0,0 @@
from httpx import AsyncClient
from modules.apihelper.base import BaseResponseData
from modules.apihelper.helpers import get_ds, get_device_id, get_recognize_server
class Genshin:
SIGN_INFO_URL = "https://hk4e-api-os.hoyoverse.com/event/sol/info"
SIGN_URL = "https://hk4e-api-os.hoyoverse.com/event/sol/sign"
SIGN_HOME_URL = "https://hk4e-api-os.hoyoverse.com/event/sol/home"
APP_VERSION = "2.11.1"
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " \
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
REFERER = "https://webstatic.hoyoverse.com"
ORIGIN = "https://webstatic.hoyoverse.com"
ACT_ID = "e202102251931481"
DS_SALT = "6cqshh5dhw73bzxn20oexa9k516chk7s"
def __init__(self):
self.headers = {
"Origin": self.ORIGIN,
'DS': get_ds(self.DS_SALT),
'x-rpc-app_version': self.APP_VERSION,
'User-Agent': self.USER_AGENT,
'x-rpc-client_type': '5', # 1为ios 2为安卓 4为pc_web 5为mobile_web
'Referer': self.REFERER,
'x-rpc-device_id': get_device_id(self.USER_AGENT)}
self.client = AsyncClient(headers=self.headers)
async def is_sign(self, uid: int, region: str = "", cookies: dict = None, lang: str = 'zh-cn'):
"""
检查是否签到
:param lang: 语言
:param uid: 游戏UID
:param region: 服务器
:param cookies: cookie
:return:
"""
if region == "":
region = get_recognize_server(uid)
params = {
"act_id": self.ACT_ID,
"region": region,
"uid": uid,
"lang": lang
}
req = await self.client.get(self.SIGN_INFO_URL, params=params, cookies=cookies)
if req.is_error:
return BaseResponseData(error_message="请求错误")
return BaseResponseData(req.json())
async def sign(self, uid: int, region: str = "", cookies: dict = None, lang: str = 'zh-cn'):
"""
执行签到
:param lang:
:param uid: 游戏UID
:param region: 服务器
:param cookies: cookie
:return:
"""
if region == "":
region = get_recognize_server(uid)
data = {
"act_id": self.ACT_ID,
"region": region,
"uid": uid,
"lang": lang
}
req = await self.client.post(self.SIGN_URL, json=data, cookies=cookies)
if req.is_error:
return BaseResponseData(error_message="签到失败")
return BaseResponseData(req.json())
async def get_sign_give(self, cookies: dict = None, lang: str = 'zh-cn'):
"""
返回今日签到信息
:param lang:
:param cookies:
:return:
"""
params = {
"act_id": self.ACT_ID,
"lang": lang
}
req = await self.client.get(self.SIGN_HOME_URL, params=params, cookies=cookies)
if req.is_error:
return
return BaseResponseData(req.json())
async def __aenter__(self):
pass
async def __aexit__(self, exc_type, exc, tb):
await self.client.aclose()

View File

@ -1,17 +1,19 @@
import asyncio import asyncio
import re import re
import time
from typing import List from typing import List
import httpx
from httpx import AsyncClient from httpx import AsyncClient
from modules.apihelper.base import HyperionResponse, ArtworkImage, BaseResponseData from modules.apihelper.base import ArtworkImage, PostInfo
from modules.apihelper.helpers import get_ds, get_device_id from modules.apihelper.helpers import get_device_id
from modules.apihelper.request.hoyorequest import HOYORequest
from utils.typedefs import JSONDict
class Hyperion: class Hyperion:
""" """米忽悠bbs相关API请求
米忽悠bbs相关API请求
该名称来源于米忽悠的安卓BBS包名结尾考虑到大部分重要的功能确实是在移动端实现了 该名称来源于米忽悠的安卓BBS包名结尾考虑到大部分重要的功能确实是在移动端实现了
""" """
@ -22,7 +24,7 @@ class Hyperion:
"Chrome/90.0.4430.72 Safari/537.36" "Chrome/90.0.4430.72 Safari/537.36"
def __init__(self): def __init__(self):
self.client = httpx.AsyncClient(headers=self.get_headers()) self.client = HOYORequest(headers=self.get_headers())
@staticmethod @staticmethod
def extract_post_id(text: str) -> int: def extract_post_id(text: str) -> int:
@ -81,47 +83,30 @@ class Hyperion:
f"{auto_orient}/interlace,{interlace}/format,{images_format}" f"{auto_orient}/interlace,{interlace}/format,{images_format}"
return {"x-oss-process": params} return {"x-oss-process": params}
async def get_post_full_in_collection(self, collection_id: int, gids: int = 2, order_type=1) -> BaseResponseData: async def get_post_full_in_collection(self, collection_id: int, gids: int = 2, order_type=1) -> JSONDict:
params = { params = {
"collection_id": collection_id, "collection_id": collection_id,
"gids": gids, "gids": gids,
"order_type": order_type "order_type": order_type
} }
response = await self.client.get(url=self.POST_FULL_IN_COLLECTION_URL, params=params) response = await self.client.get(url=self.POST_FULL_IN_COLLECTION_URL, params=params)
if response.is_error: return response
return BaseResponseData(error_message="请求错误")
return BaseResponseData(response.json())
async def get_artwork_info(self, gids: int, post_id: int, read: int = 1) -> HyperionResponse: async def get_post_info(self, gids: int, post_id: int, read: int = 1) -> PostInfo:
params = { params = {
"gids": gids, "gids": gids,
"post_id": post_id, "post_id": post_id,
"read": read "read": read
} }
response = await self.client.get(self.POST_FULL_URL, params=params) response = await self.client.get(self.POST_FULL_URL, params=params)
if response.is_error: return PostInfo.paste_data(response)
return HyperionResponse(error_message="请求错误")
return HyperionResponse(response.json())
async def get_post_full_info(self, gids: int, post_id: int, read: int = 1) -> BaseResponseData:
params = {
"gids": gids,
"post_id": post_id,
"read": read
}
response = await self.client.get(self.POST_FULL_URL, params=params)
if response.is_error:
return BaseResponseData(error_message="请求错误")
return BaseResponseData(response.json())
async def get_images_by_post_id(self, gids: int, post_id: int) -> List[ArtworkImage]: async def get_images_by_post_id(self, gids: int, post_id: int) -> List[ArtworkImage]:
artwork_info = await self.get_artwork_info(gids, post_id) post_info = await self.get_post_info(gids, post_id)
if artwork_info.error:
return []
urls = artwork_info.results.image_url_list
art_list = [] art_list = []
task_list = [ task_list = [
self.download_image(artwork_info.post_id, urls[page], page) for page in range(len(urls)) self.download_image(post_info.post_id, post_info.image_urls[page], page)
for page in range(len(post_info.image_urls))
] ]
result_list = await asyncio.gather(*task_list) result_list = await asyncio.gather(*task_list)
for result in result_list: for result in result_list:
@ -135,10 +120,8 @@ class Hyperion:
return art_list return art_list
async def download_image(self, art_id: int, url: str, page: int = 0) -> ArtworkImage: async def download_image(self, art_id: int, url: str, page: int = 0) -> ArtworkImage:
response = await self.client.get(url, params=self.get_images_params(resize=2000), timeout=5) response = await self.client.get(url, params=self.get_images_params(resize=2000), timeout=10, de_json=False)
if response.is_error: return ArtworkImage(art_id=art_id, page=page, data=response)
return ArtworkImage(art_id, page, True)
return ArtworkImage(art_id, page, data=response.content)
async def get_new_list(self, gids: int, type_id: int, page_size: int = 20): async def get_new_list(self, gids: int, type_id: int, page_size: int = 20):
""" """
@ -151,106 +134,48 @@ class Hyperion:
"type": type_id "type": type_id
} }
response = await self.client.get(url=self.GET_NEW_LIST_URL, params=params) response = await self.client.get(url=self.GET_NEW_LIST_URL, params=params)
if response.is_error: return response
return BaseResponseData(error_message="请求错误")
return BaseResponseData(response.json())
async def close(self): async def close(self):
await self.client.aclose() await self.client.shutdown()
class YuanShen: class GachaInfo:
SIGN_INFO_URL = "https://api-takumi.mihoyo.com/event/bbs_sign_reward/info" GACHA_LIST_URL = "https://webstatic.mihoyo.com/hk4e/gacha_info/cn_gf01/gacha/list.json"
SIGN_URL = "https://api-takumi.mihoyo.com/event/bbs_sign_reward/sign" GACHA_INFO_URL = "https://webstatic.mihoyo.com/hk4e/gacha_info/cn_gf01/%s/zh-cn.json"
SIGN_HOME_URL = "https://api-takumi.mihoyo.com/event/bbs_sign_reward/home"
APP_VERSION = "2.3.0" USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " \
USER_AGENT = "Mozilla/5.0 (Linux; Android 9; Unspecified Device) AppleWebKit/537.36 (KHTML, like Gecko) " \ "Chrome/90.0.4430.72 Safari/537.36"
"Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36 miHoYoBBS/2.3.0"
REFERER = "https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?" \
"bbs_auth_required=true&act_id=e202009291139501&utm_source=hyperion&utm_medium=mys&utm_campaign=icon"
ORIGIN = "https://webstatic.mihoyo.com"
ACT_ID = "e202009291139501"
DS_SALT = "h8w582wxwgqvahcdkpvdhbh2w9casgfl"
def __init__(self): def __init__(self):
self.headers = { self.headers = {
"Origin": self.ORIGIN,
'DS': get_ds(self.DS_SALT),
'x-rpc-app_version': self.APP_VERSION,
'User-Agent': self.USER_AGENT, 'User-Agent': self.USER_AGENT,
'x-rpc-client_type': '5', # 1为ios 2为安卓 4为pc_web 5为mobile_web
'Referer': self.REFERER,
'x-rpc-device_id': get_device_id(self.USER_AGENT)}
self.client = AsyncClient(headers=self.headers)
async def is_sign(self, uid: int, region: str = "cn_gf01", cookies: dict = None):
"""
检查是否签到
:param uid: 游戏UID
:param region: 服务器
:param cookies: cookie
:return:
"""
params = {
"act_id": self.ACT_ID,
"region": region,
"uid": uid
} }
req = await self.client.get(self.SIGN_INFO_URL, params=params, cookies=cookies) self.client = HOYORequest(headers=self.headers)
if req.is_error: self.cache = {}
return BaseResponseData(error_message="请求错误") self.cache_ttl = 600
return BaseResponseData(req.json())
async def sign(self, uid: int, region: str = "cn_gf01", cookies: dict = None): async def get_gacha_list_info(self) -> dict:
""" if self.cache.get("time", 0) + self.cache_ttl < time.time():
执行签到 self.cache.clear()
:param uid: 游戏UID cache = self.cache.get("gacha_list_info")
:param region: 服务器 if cache is not None:
:param cookies: cookie return cache
:return: req = await self.client.get(self.GACHA_LIST_URL)
""" self.cache["gacha_list_info"] = req
data = { self.cache["time"] = time.time()
"act_id": self.ACT_ID, return req
"region": region,
"uid": uid
}
req = await self.client.post(self.SIGN_URL, json=data, cookies=cookies)
if req.is_error:
return BaseResponseData(error_message="签到失败")
return BaseResponseData(req.json())
async def get_sign_give(self, cookies: dict = None): async def get_gacha_info(self, gacha_id: str) -> dict:
""" cache = self.cache.get(gacha_id)
返回今日签到信息 if cache is not None:
:param cookies: return cache
:return: req = await self.client.get(self.GACHA_INFO_URL % gacha_id)
""" self.cache[gacha_id] = req
params = { return req
"act_id": self.ACT_ID
}
req = await self.client.get(self.SIGN_HOME_URL, params=params, cookies=cookies)
if req.is_error:
return
return BaseResponseData(req.json())
async def __aenter__(self):
"""
:return:
"""
pass
async def __aexit__(self, exc_type, exc, tb): class SignIn:
"""
:param exc_type:
:param exc:
:param tb:
:return:
"""
await self.client.aclose()
class SignIn:
LOGIN_URL = "https://webapi.account.mihoyo.com/Api/login_by_mobilecaptcha" LOGIN_URL = "https://webapi.account.mihoyo.com/Api/login_by_mobilecaptcha"
S_TOKEN_URL = "https://api-takumi.mihoyo.com/auth/api/getMultiTokenByLoginTicket?" \ S_TOKEN_URL = "https://api-takumi.mihoyo.com/auth/api/getMultiTokenByLoginTicket?" \
"login_ticket={0}&token_types=3&uid={1}" "login_ticket={0}&token_types=3&uid={1}"

View File

@ -0,0 +1,39 @@
from typing import Union
import httpx
from modules.apihelper.error import NetworkException, ResponseException, DataNotFoundError, TimedOut
from modules.apihelper.request.httpxrequest import HTTPXRequest
from modules.apihelper.typedefs import POST_DATA, JSON_DATA
class HOYORequest(HTTPXRequest):
async def get(self, url: str, *args, de_json: bool = True, re_json_data: bool = False, **kwargs) \
-> Union[POST_DATA, JSON_DATA, bytes]:
try:
response = await self._client.get(url=url, *args, **kwargs)
except httpx.TimeoutException as err:
raise TimedOut from err
except httpx.HTTPError as exc:
raise NetworkException(f"Unknown error in HTTP implementation: {repr(exc)}") from exc
if response.is_error:
raise ResponseException(message=f"response error in status code: {response.status_code}")
if not de_json:
return response.content
json_data = response.json()
return_code = json_data.get("retcode", None)
data = json_data.get("data", None)
message = json_data.get("message", None)
if return_code is None:
if data is None:
raise DataNotFoundError
return json_data
if return_code != 0:
if message is None:
raise ResponseException(message=f"response error in return code: {return_code}")
else:
raise ResponseException(response=json_data)
if not re_json_data and data is not None:
return data
return json_data

View File

@ -0,0 +1,33 @@
from contextlib import AbstractAsyncContextManager
from types import TracebackType
from typing import Optional, Type
import httpx
class HTTPXRequest(AbstractAsyncContextManager):
def __init__(self, *args, headers=None, **kwargs):
self._client = httpx.AsyncClient(headers=headers, *args, **kwargs)
async def __aenter__(self):
try:
await self.initialize()
return self
except Exception as exc:
await self.shutdown()
raise exc
async def __aexit__(self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType]) -> None:
await self.initialize()
async def initialize(self):
if self._client.is_closed:
self._client = httpx.AsyncClient()
async def shutdown(self):
if self._client.is_closed:
return
await self._client.aclose()

View File

@ -0,0 +1,4 @@
from typing import Dict, Any
POST_DATA = Dict[str, Any]
JSON_DATA = Dict[str, Any]

View File

@ -15,7 +15,7 @@ from core.plugin import Plugin, handler, conversation
from core.user.error import UserNotFoundError from core.user.error import UserNotFoundError
from core.user.models import User from core.user.models import User
from core.user.services import UserService from core.user.services import UserService
from modules.apihelper.hyperion import YuanShen from modules.apihelper.hyperion import SignIn
from utils.decorators.error import error_callable from utils.decorators.error import error_callable
from utils.decorators.restricts import restricts from utils.decorators.restricts import restricts
from utils.log import logger from utils.log import logger
@ -29,7 +29,7 @@ class AddUserCommandData(TelegramObject):
cookies: dict = {} cookies: dict = {}
game_uid: int = 0 game_uid: int = 0
phone: int = 0 phone: int = 0
sign_in_client: Optional[YuanShen.SignIn] = None sign_in_client: Optional[SignIn] = None
CHECK_SERVER, CHECK_PHONE, CHECK_CAPTCHA, INPUT_COOKIES, COMMAND_RESULT = range(10100, 10105) CHECK_SERVER, CHECK_PHONE, CHECK_CAPTCHA, INPUT_COOKIES, COMMAND_RESULT = range(10100, 10105)
@ -171,7 +171,7 @@ class SetUserCookies(Plugin.Conversation, BasePlugin.Conversation):
add_user_command_data: AddUserCommandData = context.chat_data.get("add_user_command_data") add_user_command_data: AddUserCommandData = context.chat_data.get("add_user_command_data")
if not add_user_command_data.sign_in_client: if not add_user_command_data.sign_in_client:
phone = add_user_command_data.phone phone = add_user_command_data.phone
client = YuanShen.SignIn(phone) client = SignIn(phone)
try: try:
success = await client.login(captcha) success = await client.login(captcha)
if not success: if not success:

View File

@ -11,7 +11,7 @@ from telegram.ext import CallbackContext, CommandHandler, MessageHandler, filter
from core.baseplugin import BasePlugin from core.baseplugin import BasePlugin
from core.plugin import Plugin, handler from core.plugin import Plugin, handler
from core.template import TemplateService from core.template import TemplateService
from modules.apihelper.gacha import GachaInfo from modules.apihelper.hyperion import GachaInfo
from plugins.genshin.gacha.wish import WishCountInfo, get_one from plugins.genshin.gacha.wish import WishCountInfo, get_one
from utils.bot import get_all_args from utils.bot import get_all_args
from utils.decorators.error import error_callable from utils.decorators.error import error_callable
@ -41,12 +41,12 @@ class Gacha(Plugin, BasePlugin):
async def gacha_info(self, gacha_name: str = "角色活动", default: bool = False): async def gacha_info(self, gacha_name: str = "角色活动", default: bool = False):
gacha_list_info = await self.gacha.get_gacha_list_info() gacha_list_info = await self.gacha.get_gacha_list_info()
gacha_id = "" gacha_id = ""
for gacha in gacha_list_info.data["list"]: for gacha in gacha_list_info["list"]:
if gacha["gacha_name"] == gacha_name: if gacha["gacha_name"] == gacha_name:
gacha_id = gacha["gacha_id"] gacha_id = gacha["gacha_id"]
if gacha_id == "": if gacha_id == "":
if default and len(gacha_list_info.data["list"]) > 0: if default and len(gacha_list_info["list"]) > 0:
gacha_id = gacha_list_info.data["list"][0]["gacha_id"] gacha_id = gacha_list_info["list"][0]["gacha_id"]
else: else:
raise GachaNotFound(gacha_name) raise GachaNotFound(gacha_name)
gacha_info = await self.gacha.get_gacha_info(gacha_id) gacha_info = await self.gacha.get_gacha_info(gacha_id)

View File

@ -74,9 +74,9 @@ class Post(Plugin.Conversation, BasePlugin):
if post_id == -1: if post_id == -1:
await message.reply_text("获取作品ID错误请检查连接是否合法", reply_markup=ReplyKeyboardRemove()) await message.reply_text("获取作品ID错误请检查连接是否合法", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END return ConversationHandler.END
post_full_info = await self.bbs.get_post_full_info(2, post_id) post_info = await self.bbs.get_post_info(2, post_id)
post_images = await self.bbs.get_images_by_post_id(2, post_id) post_images = await self.bbs.get_images_by_post_id(2, post_id)
post_data = post_full_info.data["post"]["post"] post_data = post_info["post"]["post"]
post_subject = post_data['subject'] post_subject = post_data['subject']
post_soup = BeautifulSoup(post_data["content"], features="html.parser") post_soup = BeautifulSoup(post_data["content"], features="html.parser")
post_p = post_soup.find_all('p') post_p = post_soup.find_all('p')
@ -93,6 +93,9 @@ class Post(Plugin.Conversation, BasePlugin):
if len(post_images) > 1: if len(post_images) > 1:
media = [InputMediaPhoto(img_info.data) for img_info in post_images] media = [InputMediaPhoto(img_info.data) for img_info in post_images]
media[0] = InputMediaPhoto(post_images[0].data, caption=post_text, parse_mode=ParseMode.MARKDOWN_V2) media[0] = InputMediaPhoto(post_images[0].data, caption=post_text, parse_mode=ParseMode.MARKDOWN_V2)
if len(media) > 10:
media = media[0:10]
await message.reply_text("获取到的图片已经超过10张为了保证发送成功已经删除一部分图片")
await message.reply_media_group(media) await message.reply_media_group(media)
elif len(post_images) == 1: elif len(post_images) == 1:
image = post_images[0] image = post_images[0]
@ -284,3 +287,4 @@ class Post(Plugin.Conversation, BasePlugin):
return ConversationHandler.END return ConversationHandler.END
await message.reply_text("推送成功", reply_markup=ReplyKeyboardRemove()) await message.reply_text("推送成功", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END return ConversationHandler.END

View File

@ -21,9 +21,7 @@ async def test_get_strategy(hyperion):
async def get_post_id(_collection_id: int, character_name: str) -> str: async def get_post_id(_collection_id: int, character_name: str) -> str:
post_full_in_collection = await hyperion.get_post_full_in_collection(_collection_id) post_full_in_collection = await hyperion.get_post_full_in_collection(_collection_id)
if post_full_in_collection.error: for post_data in post_full_in_collection["posts"]:
raise RuntimeError(f"获取收藏信息错误,错误信息为:{post_full_in_collection.message}")
for post_data in post_full_in_collection.data["posts"]:
topics = post_data["topics"] topics = post_data["topics"]
for topic in topics: for topic in topics:
if character_name == topic["name"]: if character_name == topic["name"]:

View File

@ -0,0 +1,45 @@
"""Test Url
https://bbs.mihoyo.com/ys/article/29023709
"""
import logging
import pytest
import pytest_asyncio
from bs4 import BeautifulSoup
from flaky import flaky
from modules.apihelper.base import PostInfo
from modules.apihelper.hyperion import Hyperion
LOGGER = logging.getLogger(__name__)
@pytest_asyncio.fixture
async def hyperion():
_hyperion = Hyperion()
yield _hyperion
await _hyperion.close()
# noinspection PyShadowingNames
@pytest.mark.asyncio
@flaky(3, 1)
async def test_get_post_info(hyperion):
post_info = await hyperion.get_post_info(2, 29023709)
assert post_info
assert isinstance(post_info, PostInfo)
assert post_info["post"]["post"]["post_id"] == '29023709'
assert post_info.post_id == 29023709
assert post_info["post"]["post"]["subject"] == "《原神》长期项目启动·概念PV"
assert post_info.subject == "《原神》长期项目启动·概念PV"
assert len(post_info["post"]["post"]["images"]) == 1
post_soup = BeautifulSoup(post_info["post"]["post"]["content"], features="html.parser")
assert post_soup.find_all('p')
# noinspection PyShadowingNames
@pytest.mark.asyncio
@flaky(3, 1)
async def test_get_images_by_post_id(hyperion):
post_images = await hyperion.get_images_by_post_id(2, 29023709)
assert len(post_images) == 1

View File

@ -9,6 +9,7 @@ from telegram import Update, ReplyKeyboardRemove
from telegram.error import BadRequest, TimedOut, Forbidden from telegram.error import BadRequest, TimedOut, Forbidden
from telegram.ext import CallbackContext, ConversationHandler from telegram.ext import CallbackContext, ConversationHandler
from modules.apihelper.error import APIHelperException, ReturnCodeError
from utils.error import UrlResourcesNotFoundError from utils.error import UrlResourcesNotFoundError
from utils.log import logger from utils.log import logger
@ -104,6 +105,14 @@ def error_callable(func: Callable) -> Callable:
logger.exception(exc) logger.exception(exc)
await send_user_notification(update, context, "出错了呜呜呜 ~ 获取账号信息发生错误") await send_user_notification(update, context, "出错了呜呜呜 ~ 获取账号信息发生错误")
return ConversationHandler.END return ConversationHandler.END
except ReturnCodeError as exc:
await send_user_notification(update, context, f"出错了呜呜呜 ~ API请求错误 错误信息为 {exc.message}")
return ConversationHandler.END
except APIHelperException as exc:
logger.error("APIHelperException")
logger.exception(exc)
await send_user_notification(update, context, "出错了呜呜呜 ~ API请求错误")
return ConversationHandler.END
except BadRequest as exc: except BadRequest as exc:
logger.warning("python-telegram-bot 请求错误") logger.warning("python-telegram-bot 请求错误")
logger.exception(exc) logger.exception(exc)