mirror of
https://github.com/A-Minos/nonebot-plugin-tetris-stats.git
synced 2026-03-05 05:36:54 +08:00
✨ 支持茶服多个api地址的故障转移 (#301)
* ✨ RequestError 新增 status_code 参数 * ✨ 新增支持故障转移的请求方法 * ✨ 支持茶服多个api地址的故障转移
This commit is contained in:
@@ -1,4 +1,10 @@
|
|||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
GAME_TYPE: Literal['TOS'] = 'TOS'
|
GAME_TYPE: Literal['TOS'] = 'TOS'
|
||||||
BASE_URL = 'https://teatube.cn:8888/'
|
BASE_URL = {
|
||||||
|
'https://teatube.cn:8888/',
|
||||||
|
'http://cafuuchino1.studio26f.org:19970',
|
||||||
|
'http://cafuuchino2.studio26f.org:19970',
|
||||||
|
'http://cafuuchino3.studio26f.org:19970',
|
||||||
|
'http://cafuuchino4.studio26f.org:19970',
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from re import match
|
|||||||
from typing import Literal
|
from typing import Literal
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
from httpx import TimeoutException
|
||||||
from nonebot.compat import type_validate_json
|
from nonebot.compat import type_validate_json
|
||||||
from nonebot_plugin_orm import get_session
|
from nonebot_plugin_orm import get_session
|
||||||
|
|
||||||
@@ -104,22 +105,30 @@ class Processor(ProcessorMeta):
|
|||||||
"""获取用户信息"""
|
"""获取用户信息"""
|
||||||
if self.processed_data.user_info is None:
|
if self.processed_data.user_info is None:
|
||||||
if self.user.teaid is not None:
|
if self.user.teaid is not None:
|
||||||
url = splice_url(
|
url = [
|
||||||
|
splice_url(
|
||||||
[
|
[
|
||||||
BASE_URL,
|
i,
|
||||||
'getTeaIdInfo',
|
'getTeaIdInfo',
|
||||||
f'?{urlencode({"teaId":self.user.teaid})}',
|
f'?{urlencode({"teaId":self.user.teaid})}',
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
for i in BASE_URL
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
url = splice_url(
|
url = [
|
||||||
|
splice_url(
|
||||||
[
|
[
|
||||||
BASE_URL,
|
i,
|
||||||
'getUsernameInfo',
|
'getUsernameInfo',
|
||||||
f'?{urlencode({"username":self.user.name})}',
|
f'?{urlencode({"username":self.user.name})}',
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
self.raw_response.user_info = await Request.request(url)
|
for i in BASE_URL
|
||||||
|
]
|
||||||
|
self.raw_response.user_info = await Request.failover_request(
|
||||||
|
url, failover_code=[502], failover_exc=(TimeoutException,)
|
||||||
|
)
|
||||||
user_info: UserInfo = type_validate_json(UserInfo, self.raw_response.user_info) # type: ignore[arg-type]
|
user_info: UserInfo = type_validate_json(UserInfo, self.raw_response.user_info) # type: ignore[arg-type]
|
||||||
if not isinstance(user_info, InfoSuccess):
|
if not isinstance(user_info, InfoSuccess):
|
||||||
raise RequestError(f'用户信息请求错误:\n{user_info.error}')
|
raise RequestError(f'用户信息请求错误:\n{user_info.error}')
|
||||||
@@ -132,14 +141,19 @@ class Processor(ProcessorMeta):
|
|||||||
other_parameter = {}
|
other_parameter = {}
|
||||||
params = urlencode(dict(sorted(other_parameter.items())))
|
params = urlencode(dict(sorted(other_parameter.items())))
|
||||||
if self.processed_data.user_profile.get(params) is None:
|
if self.processed_data.user_profile.get(params) is None:
|
||||||
self.raw_response.user_profile[params] = await Request.request(
|
self.raw_response.user_profile[params] = await Request.failover_request(
|
||||||
|
[
|
||||||
splice_url(
|
splice_url(
|
||||||
[
|
[
|
||||||
BASE_URL,
|
i,
|
||||||
'getProfile',
|
'getProfile',
|
||||||
f'?{urlencode({"id":self.user.teaid or self.user.name,**other_parameter})}',
|
f'?{urlencode({"id":self.user.teaid or self.user.name,**other_parameter})}',
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
for i in BASE_URL
|
||||||
|
],
|
||||||
|
failover_code=[502],
|
||||||
|
failover_exc=(TimeoutException,),
|
||||||
)
|
)
|
||||||
self.processed_data.user_profile[params] = UserProfile.model_validate_json(
|
self.processed_data.user_profile[params] = UserProfile.model_validate_json(
|
||||||
self.raw_response.user_profile[params]
|
self.raw_response.user_profile[params]
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ class NeedCatchError(TetrisStatsError):
|
|||||||
class RequestError(NeedCatchError):
|
class RequestError(NeedCatchError):
|
||||||
"""请求错误"""
|
"""请求错误"""
|
||||||
|
|
||||||
|
def __init__(self, message: str = '', *, status_code: int | None = None):
|
||||||
|
super().__init__(message)
|
||||||
|
self.status_code = status_code
|
||||||
|
|
||||||
|
|
||||||
class MessageFormatError(NeedCatchError):
|
class MessageFormatError(NeedCatchError):
|
||||||
"""用户发送的消息格式不正确"""
|
"""用户发送的消息格式不正确"""
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from collections.abc import Sequence
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from urllib.parse import urljoin, urlparse
|
from urllib.parse import urljoin, urlparse
|
||||||
|
|
||||||
@@ -116,7 +117,8 @@ class Request:
|
|||||||
response = await session.get(url, headers=cls._headers)
|
response = await session.get(url, headers=cls._headers)
|
||||||
if response.status_code != HTTPStatus.OK:
|
if response.status_code != HTTPStatus.OK:
|
||||||
raise RequestError(
|
raise RequestError(
|
||||||
f'请求错误 code: {response.status_code} {HTTPStatus(response.status_code).phrase}\n{response.text}'
|
f'请求错误 code: {response.status_code} {HTTPStatus(response.status_code).phrase}\n{response.text}',
|
||||||
|
status_code=response.status_code,
|
||||||
)
|
)
|
||||||
if is_json:
|
if is_json:
|
||||||
loads(response.content)
|
loads(response.content)
|
||||||
@@ -127,3 +129,33 @@ class Request:
|
|||||||
if urlparse(url).netloc.lower().endswith('tetr.io'):
|
if urlparse(url).netloc.lower().endswith('tetr.io'):
|
||||||
return await cls._anti_cloudflare(url)
|
return await cls._anti_cloudflare(url)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def failover_request(
|
||||||
|
cls,
|
||||||
|
urls: Sequence[str],
|
||||||
|
*,
|
||||||
|
failover_code: Sequence[int],
|
||||||
|
failover_exc: tuple[type[BaseException], ...],
|
||||||
|
is_json: bool = True,
|
||||||
|
) -> bytes:
|
||||||
|
error_list: list[RequestError] = []
|
||||||
|
for i in urls:
|
||||||
|
logger.debug(f'尝试请求 {i}')
|
||||||
|
try:
|
||||||
|
return await cls.request(i, is_json=is_json)
|
||||||
|
except RequestError as e:
|
||||||
|
if e.status_code in failover_code: # 如果状态码在 failover_code 中, 则继续尝试下一个URL
|
||||||
|
error_list.append(e)
|
||||||
|
continue
|
||||||
|
# 如果状态码不在故障转移列表中, 则查找异常栈, 如果异常栈内有 failover_exc 内的异常类型, 则继续尝试下一个URL
|
||||||
|
tb = e.__traceback__
|
||||||
|
while tb is not None:
|
||||||
|
if isinstance(tb.tb_frame.f_locals.get('exc_value'), failover_exc):
|
||||||
|
error_list.append(e)
|
||||||
|
break
|
||||||
|
tb = tb.tb_next
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
continue
|
||||||
|
raise RequestError(f'所有地址皆不可用\n{error_list!r}')
|
||||||
|
|||||||
Reference in New Issue
Block a user