feat: implement retry mechanism for QQ Official API file uploads (#7430)

* feat: implement retry mechanism for QQ Official API file uploads

* fix: update error logging message for media file upload retries
This commit is contained in:
時壹
2026-04-12 13:47:44 +08:00
committed by GitHub
parent 09848956e2
commit a7ce8df024
@@ -1,5 +1,6 @@
import asyncio
import base64
import logging
import os
import random
import uuid
@@ -15,6 +16,13 @@ from botpy import Client
from botpy.http import Route
from botpy.types import message
from botpy.types.message import MarkdownPayload, Media
from tenacity import (
before_sleep_log,
retry,
retry_if_exception_type,
stop_after_attempt,
wait_exponential,
)
from astrbot.api import logger
from astrbot.api.event import AstrMessageEvent, MessageChain
@@ -44,6 +52,20 @@ 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,
)
),
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=1, max=10),
before_sleep=before_sleep_log(logger, logging.WARNING),
reraise=True,
)
class QQOfficialMessageEvent(AstrMessageEvent):
MARKDOWN_NOT_ALLOWED_ERROR = "不允许发送原生 markdown"
@@ -453,21 +475,26 @@ class QQOfficialMessageEvent(AstrMessageEvent):
"srv_send_msg": False,
}
result = None
if "openid" in kwargs:
payload["openid"] = kwargs["openid"]
route = Route("POST", "/v2/users/{openid}/files", openid=kwargs["openid"])
result = await self.bot.api._http.request(route, json=payload)
elif "group_openid" in kwargs:
payload["group_openid"] = kwargs["group_openid"]
route = Route(
"POST",
"/v2/groups/{group_openid}/files",
group_openid=kwargs["group_openid"],
)
result = await self.bot.api._http.request(route, json=payload)
else:
raise ValueError("Invalid upload parameters")
@_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(
"POST",
"/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()
if not isinstance(result, dict):
raise RuntimeError(
@@ -490,7 +517,7 @@ class QQOfficialMessageEvent(AstrMessageEvent):
) -> Media | None:
"""上传媒体文件"""
# 构建基础payload
payload = {"file_type": file_type, "srv_send_msg": srv_send_msg}
payload: dict = {"file_type": file_type, "srv_send_msg": srv_send_msg}
if file_name:
payload["file_name"] = file_name
@@ -519,9 +546,12 @@ class QQOfficialMessageEvent(AstrMessageEvent):
else:
return None
@_qqofficial_retry
async def _do_upload():
return await self.bot.api._http.request(route, json=payload)
try:
# 使用底层HTTP请求
result = await self.bot.api._http.request(route, json=payload)
result = await _do_upload()
if result:
if not isinstance(result, dict):
@@ -533,6 +563,8 @@ class QQOfficialMessageEvent(AstrMessageEvent):
file_info=result["file_info"],
ttl=result.get("ttl", 0),
)
except (botpy.errors.ServerError, botpy.errors.SequenceNumberError):
logger.error(f"上传媒体文件失败,共尝试3次后放弃: {file_source}")
except Exception as e:
logger.error(f"上传请求错误: {e}")