mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-18 18:10:37 +08:00
* fix: qq_official websocket适配器发送消息收到None时现在会重试了 (#8977) * fix: qq_official websocket适配器重试发送消息的时候不会重复打印重试告警了 * fix: qq_official websocket适配器发送媒体/文件消息时遇到api返回None也会重试了
This commit is contained in:
@@ -30,6 +30,10 @@ from astrbot.api.platform import AstrBotMessage, PlatformMetadata
|
||||
from astrbot.core.utils.media_utils import MediaResolver, file_uri_to_path, is_file_uri
|
||||
|
||||
|
||||
class APIReturnNoneError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _patch_qq_botpy_formdata() -> None:
|
||||
"""Patch qq-botpy for aiohttp>=3.12 compatibility.
|
||||
|
||||
@@ -49,21 +53,25 @@ def _patch_qq_botpy_formdata() -> None:
|
||||
|
||||
_patch_qq_botpy_formdata()
|
||||
|
||||
# Retry decorator for QQ Official API transient errors (HTTP 500/504)
|
||||
_qqofficial_retry = retry(
|
||||
retry=retry_if_exception_type(
|
||||
(
|
||||
botpy.errors.ServerError,
|
||||
botpy.errors.SequenceNumberError,
|
||||
OSError,
|
||||
asyncio.TimeoutError,
|
||||
)
|
||||
),
|
||||
stop=stop_after_attempt(5),
|
||||
wait=wait_exponential(multiplier=2, min=2, max=30),
|
||||
before_sleep=before_sleep_log(logger, logging.WARNING),
|
||||
reraise=True,
|
||||
)
|
||||
|
||||
def _qqofficial_retry(max_attempts: int = 5):
|
||||
"""Retry decorator for QQ Official API transient errors (HTTP 500/504)"""
|
||||
return retry(
|
||||
retry=retry_if_exception_type(
|
||||
(
|
||||
botpy.errors.ServerError,
|
||||
botpy.errors.SequenceNumberError,
|
||||
OSError,
|
||||
asyncio.TimeoutError,
|
||||
APIReturnNoneError,
|
||||
)
|
||||
),
|
||||
stop=stop_after_attempt(max_attempts),
|
||||
wait=wait_exponential(multiplier=2, min=2, max=30),
|
||||
before_sleep=before_sleep_log(logger, logging.WARNING),
|
||||
reraise=True,
|
||||
)
|
||||
|
||||
|
||||
_QQOFFICIAL_SEND_API_ERRORS = (
|
||||
botpy.errors.ForbiddenError,
|
||||
@@ -558,14 +566,13 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
||||
"srv_send_msg": False,
|
||||
}
|
||||
|
||||
@_qqofficial_retry
|
||||
@_qqofficial_retry()
|
||||
async def _do_upload():
|
||||
if "openid" in kwargs:
|
||||
payload["openid"] = kwargs["openid"]
|
||||
route = Route(
|
||||
"POST", "/v2/users/{openid}/files", openid=kwargs["openid"]
|
||||
)
|
||||
return await self.bot.api._http.request(route, json=payload)
|
||||
elif "group_openid" in kwargs:
|
||||
payload["group_openid"] = kwargs["group_openid"]
|
||||
route = Route(
|
||||
@@ -573,11 +580,20 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
||||
"/v2/groups/{group_openid}/files",
|
||||
group_openid=kwargs["group_openid"],
|
||||
)
|
||||
return await self.bot.api._http.request(route, json=payload)
|
||||
else:
|
||||
raise ValueError("Invalid upload parameters")
|
||||
|
||||
result = await _do_upload()
|
||||
result = await self.bot.api._http.request(route, json=payload)
|
||||
if result is None:
|
||||
err_msg = "上传图片API返回None,触发重试"
|
||||
raise APIReturnNoneError(err_msg)
|
||||
return result
|
||||
|
||||
try:
|
||||
result = await _do_upload()
|
||||
except APIReturnNoneError:
|
||||
logger.warning(f"上传图片API返回None,共尝试5次后放弃: {payload}")
|
||||
raise
|
||||
|
||||
if not isinstance(result, dict):
|
||||
raise RuntimeError(
|
||||
@@ -629,9 +645,13 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
||||
else:
|
||||
return None
|
||||
|
||||
@_qqofficial_retry
|
||||
@_qqofficial_retry()
|
||||
async def _do_upload():
|
||||
return await self.bot.api._http.request(route, json=payload)
|
||||
result = await self.bot.api._http.request(route, json=payload)
|
||||
if result is None:
|
||||
err_msg = "上传文件API返回None,触发重试"
|
||||
raise APIReturnNoneError(err_msg)
|
||||
return result
|
||||
|
||||
try:
|
||||
result = await _do_upload()
|
||||
@@ -646,6 +666,8 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
||||
file_info=result["file_info"],
|
||||
ttl=result.get("ttl", 0),
|
||||
)
|
||||
except APIReturnNoneError:
|
||||
logger.warning(f"上传文件API返回None,共尝试5次后放弃: {file_source}")
|
||||
except (botpy.errors.ServerError, botpy.errors.SequenceNumberError):
|
||||
logger.error(f"上传媒体文件失败,共尝试5次后放弃: {file_source}")
|
||||
except Exception as e:
|
||||
@@ -680,11 +702,26 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
||||
stream_data.pop("id", None)
|
||||
payload["stream"] = stream_data
|
||||
route = Route("POST", "/v2/users/{openid}/messages", openid=openid)
|
||||
result = await self.bot.api._http.request(route, json=payload)
|
||||
|
||||
if result is None:
|
||||
logger.warning("[QQOfficial] post_c2c_message: API 返回 None,跳过本次发送")
|
||||
retry_times = 3
|
||||
|
||||
@_qqofficial_retry(retry_times)
|
||||
async def _do_request():
|
||||
result = await self.bot.api._http.request(route, json=payload)
|
||||
if result is None:
|
||||
err_msg = "发送消息API返回None,触发重试"
|
||||
raise APIReturnNoneError(err_msg)
|
||||
return result
|
||||
|
||||
result = None
|
||||
try:
|
||||
result = await _do_request()
|
||||
except APIReturnNoneError:
|
||||
logger.warning(
|
||||
f"[QQOfficial] post_c2c_message: 发送消息失败,API 返回 None,共尝试{retry_times}次后放弃"
|
||||
)
|
||||
return None
|
||||
|
||||
if not isinstance(result, dict):
|
||||
logger.error(f"[QQOfficial] post_c2c_message: 响应不是 dict: {result}")
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user