11import asyncio
22import base64
3+ import logging
34import os
45import random
56import uuid
1516from botpy .http import Route
1617from botpy .types import message
1718from botpy .types .message import MarkdownPayload , Media
19+ from tenacity import (
20+ before_sleep_log ,
21+ retry ,
22+ retry_if_exception_type ,
23+ stop_after_attempt ,
24+ wait_exponential ,
25+ )
1826
1927from astrbot .api import logger
2028from astrbot .api .event import AstrMessageEvent , MessageChain
@@ -44,6 +52,20 @@ def _patch_qq_botpy_formdata() -> None:
4452
4553_patch_qq_botpy_formdata ()
4654
55+ # Retry decorator for QQ Official API transient errors (HTTP 500/504)
56+ _qqofficial_retry = retry (
57+ retry = retry_if_exception_type (
58+ (
59+ botpy .errors .ServerError ,
60+ botpy .errors .SequenceNumberError ,
61+ )
62+ ),
63+ stop = stop_after_attempt (3 ),
64+ wait = wait_exponential (multiplier = 1 , min = 1 , max = 10 ),
65+ before_sleep = before_sleep_log (logger , logging .WARNING ),
66+ reraise = True ,
67+ )
68+
4769
4870class QQOfficialMessageEvent (AstrMessageEvent ):
4971 MARKDOWN_NOT_ALLOWED_ERROR = "不允许发送原生 markdown"
@@ -453,21 +475,26 @@ async def upload_group_and_c2c_image(
453475 "srv_send_msg" : False ,
454476 }
455477
456- result = None
457- if "openid" in kwargs :
458- payload ["openid" ] = kwargs ["openid" ]
459- route = Route ("POST" , "/v2/users/{openid}/files" , openid = kwargs ["openid" ])
460- result = await self .bot .api ._http .request (route , json = payload )
461- elif "group_openid" in kwargs :
462- payload ["group_openid" ] = kwargs ["group_openid" ]
463- route = Route (
464- "POST" ,
465- "/v2/groups/{group_openid}/files" ,
466- group_openid = kwargs ["group_openid" ],
467- )
468- result = await self .bot .api ._http .request (route , json = payload )
469- else :
470- raise ValueError ("Invalid upload parameters" )
478+ @_qqofficial_retry
479+ async def _do_upload ():
480+ if "openid" in kwargs :
481+ payload ["openid" ] = kwargs ["openid" ]
482+ route = Route (
483+ "POST" , "/v2/users/{openid}/files" , openid = kwargs ["openid" ]
484+ )
485+ return await self .bot .api ._http .request (route , json = payload )
486+ elif "group_openid" in kwargs :
487+ payload ["group_openid" ] = kwargs ["group_openid" ]
488+ route = Route (
489+ "POST" ,
490+ "/v2/groups/{group_openid}/files" ,
491+ group_openid = kwargs ["group_openid" ],
492+ )
493+ return await self .bot .api ._http .request (route , json = payload )
494+ else :
495+ raise ValueError ("Invalid upload parameters" )
496+
497+ result = await _do_upload ()
471498
472499 if not isinstance (result , dict ):
473500 raise RuntimeError (
@@ -490,7 +517,7 @@ async def upload_group_and_c2c_media(
490517 ) -> Media | None :
491518 """上传媒体文件"""
492519 # 构建基础payload
493- payload = {"file_type" : file_type , "srv_send_msg" : srv_send_msg }
520+ payload : dict = {"file_type" : file_type , "srv_send_msg" : srv_send_msg }
494521 if file_name :
495522 payload ["file_name" ] = file_name
496523
@@ -519,9 +546,12 @@ async def upload_group_and_c2c_media(
519546 else :
520547 return None
521548
549+ @_qqofficial_retry
550+ async def _do_upload ():
551+ return await self .bot .api ._http .request (route , json = payload )
552+
522553 try :
523- # 使用底层HTTP请求
524- result = await self .bot .api ._http .request (route , json = payload )
554+ result = await _do_upload ()
525555
526556 if result :
527557 if not isinstance (result , dict ):
@@ -533,6 +563,8 @@ async def upload_group_and_c2c_media(
533563 file_info = result ["file_info" ],
534564 ttl = result .get ("ttl" , 0 ),
535565 )
566+ except (botpy .errors .ServerError , botpy .errors .SequenceNumberError ):
567+ logger .error (f"上传媒体文件失败,共尝试3次后放弃: { file_source } " )
536568 except Exception as e :
537569 logger .error (f"上传请求错误: { e } " )
538570
0 commit comments