What happened / 发生了什么
代码中的###是我特意在此写的说明,并不是原本代码里有的内容
astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py 的 post_c2c_message 没有额外的重试机制来处理botpy request返回none的情况
async def post_c2c_message(
self,
openid: str,
msg_type: int = 0,
...
) -> message.Message | None:
payload = locals()
payload.pop("self", None)
if payload.get("msg_id") is None:
payload.pop("msg_id", None)
# QQ API does not accept stream.id=None; remove it when not yet assigned
if "stream" in payload and payload["stream"] is not None:
stream_data = dict(payload["stream"])
if stream_data.get("id") is None:
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)
### 这里botpy请求返回None没有重试
if result is None:
logger.warning("[QQOfficial] post_c2c_message: API 返回 None,跳过本次发送")
return None
if not isinstance(result, dict):
logger.error(f"[QQOfficial] post_c2c_message: 响应不是 dict: {result}")
return None
return message.Message(**result)
.venv\Lib\site-packages\botpy\http.py 而 botpy 里的request方法又写的比较难泵
async def request(self, route: Route, retry_time: int = 0, **kwargs: Any):
if retry_time > 2:
return
# some checking if it's a JSON request
if "json" in kwargs:
json_ = kwargs["json"]
json__get = json_.get("file_image")
if json__get and isinstance(json__get, bytes):
kwargs["data"] = _FormData()
for k, v in kwargs.pop("json").items():
if v:
if isinstance(v, dict):
if k == "message_reference":
_log.error(
f"[botpy] 接口参数传入异常, 请求连接: {route.url}, "
f"错误原因: file_image与message_reference不能同时传入,"
f"备注: sdk已按照优先级,去除message_reference参数"
)
else:
kwargs["data"].add_field(k, v)
await self.check_session()
route.is_sandbox = self.is_sandbox
_log.debug(f"[botpy] 请求头部: {self._headers}, 请求方式: {route.method}, 请求url: {route.url}")
_log.debug(self._session)
try:
async with self._session.request(
method=route.method,
url=route.url,
headers=self._headers,
timeout=(aiohttp.ClientTimeout(total=self.timeout)),
**kwargs,
) as response:
_log.debug(response)
return await _handle_response(response)
### `_handle_response`函数本身就可能返回None,这里却没处理
except asyncio.TimeoutError:
_log.warning(f"请求超时,请求连接: {route.url}")
except ConnectionResetError:
_log.debug("session connection broken retry")
await self.request(route, retry_time + 1, **kwargs)
### 重试后的response有没有return出来, 上游不知道到底成功了没有
而且astrbot日志这边,完全没有捕获到有效的信息,接口响应是怎么样的也完全不知道,所以我也不知道为什么消息发不出去,我觉得目前只能临时在astrbot适配器这边额外加一层重试机制来保证尽可能的把消息发出去,botpy那边两年没更新了,就算给那边提pr,也应该不会有人鸟我我的pr的
更优的解法是把botpy依赖移除重新在适配器这边重新实现一遍请求,应该要花不少时间写的,只能后面在锁了
Reproduce / 如何复现?
我不知道.jpg
AstrBot version, deployment method (e.g., Windows Docker Desktop deployment), provider used, and messaging platform used. / AstrBot 版本、部署方式(如 Windows Docker Desktop 部署)、使用的提供商、使用的消息平台适配器
AstrBot v4.25.6 / Linux 源码+uv部署 / qqofficial(websocket)适配器
OS
Linux
Logs / 报错日志
[2026-06-24 11:16:43.014] [Core] [INFO] [respond.stage:183]: Prepare to send - /B73536AF2D0605BBD52A4AC1219FF0BE: [引用消息] [my_plugin][898e43ec8514] 成功下载黑名单列表, 文件名为 : "xxxx黑名单表格.xlsx"
[2026-06-24 11:16:43.014] [Core] [DBUG] [qqofficial.qqofficial_message_event:735]: qq_official 忽略 ComponentType.Reply
[2026-06-24 11:16:43.203] [Plug] [INFO] [utils.log:50]: [my_plugin][898e43ec8514] "xxxxxxxxxx" 1个QQ号, 全部不在黑名单中
[2026-06-24 11:17:03.323] [Core] [WARN] [v4.25.6] [qqofficial.qqofficial_message_event:663]: [QQOfficial] post_c2c_message: API 返回 None,跳过本次发送
[2026-06-24 11:17:03.323] [Core] [DBUG] [qqofficial.qqofficial_message_event:438]: Message sent to C2C: None
[2026-06-24 11:17:03.324] [Core] [DBUG] [pipeline.context_utils:95]: hook(OnAfterMessageSentEvent) -> astrbot - after_message_sent
[2026-06-24 11:17:03.327] [Core] [INFO] [respond.stage:183]: Prepare to send - /B73536AF2D0605BBD52A4AC1219FF0BE: [引用消息] [my_plugin][898e43ec8514] "xxxxxxxxxx" 1个QQ号, 全部不在黑名单中
[2026-06-24 11:17:03.327] [Core] [DBUG] [qqofficial.qqofficial_message_event:735]: qq_official 忽略 ComponentType.Reply
[2026-06-24 11:17:04.310] [Core] [DBUG] [qqofficial.qqofficial_message_event:438]: Message sent to C2C: {'id': 'ROBOT1.0_a5zqUE948T9dGfHSOgu2QKr.XOIeSQ6sVHSeg0TfaO-gx1jOGJNaRTQ20CsEqKGY-71cumXj10ZXi-dwe-7ytQ!!', 'timestamp': '2026-06-24T11:17:04+08:00', 'ext_info': {'ref_idx': 'REFIDX_+D+hNLHGqt/A9jvr5tyRv8tG81ovPjw88HwjHppK6Gc='}}
[2026-06-24 11:17:04.310] [Core] [DBUG] [pipeline.context_utils:95]: hook(OnAfterMessageSentEvent) -> astrbot - after_message_sent
[2026-06-24 11:17:04.311] [Core] [DBUG] [method.star_request:46]: plugin -> astrbot - on_message
[2026-06-24 11:17:04.311] [Core] [DBUG] [pipeline.scheduler:93]: pipeline 执行完毕。
[2026-06-24 11:17:51.252] [Core] [INFO] [respond.stage:183]: Prepare to send - /B73536AF2D0605BBD52A4AC1219FF0BE: [引用消息] [my_plugin][4cda347d3a62] "xxxxxxxxxx","xxxxxxxxxx" 2个QQ号, 全部不在黑名单中
[2026-06-24 11:17:51.252] [Core] [DBUG] [qqofficial.qqofficial_message_event:735]: qq_official 忽略 ComponentType.Reply
[2026-06-24 11:17:52.075] [Core] [DBUG] [qqofficial.qqofficial_message_event:438]: Message sent to C2C: {'id': 'ROBOT1.0_a5zqUE948T9dgfHSOgu6QaTFubHc.gtH4PwUsZhzLRSafz0DwSwQbc0TGM5xd9eu85m6fTRIIC8Bebd4WbPmOw!!', 'timestamp': '2026-06-24T11:17:51+08:00', 'ext_info': {'ref_idx': 'REFIDX_jdWWxKc6oet/bvEvQWpKbstG81ovPjw88HwjHppK6Gc='}}
[2026-06-24 11:17:52.075] [Core] [DBUG] [pipeline.context_utils:95]: hook(OnAfterMessageSentEvent) -> astrbot - after_message_sent
[2026-06-24 11:17:52.075] [Core] [DBUG] [method.star_request:46]: plugin -> astrbot - on_message
[2026-06-24 11:17:52.076] [Core] [DBUG] [pipeline.scheduler:93]: pipeline 执行完毕。
[2026-06-24 11:18:03.323] [Core][WARN] [v4.25.6] [qqofficial.qqofficial_message_event:663]: [QQOfficial] post_c2c_message: API 返回 None,跳过本次发送
[2026-06-24 11:18:03.323] [Core] [DBUG] [qqofficial.qqofficial_message_event:438]: Message sent to C2C: None
[2026-06-24 11:18:03.323] [Core] [DBUG] [pipeline.context_utils:95]: hook(OnAfterMessageSentEvent) -> astrbot - after_message_sent
[2026-06-24 11:18:03.324] [Core] [DBUG] [method.star_request:46]: plugin -> astrbot - on_message
[2026-06-24 11:18:03.324] [Core] [DBUG] [pipeline.scheduler:93]: pipeline 执行完毕。
Are you willing to submit a PR? / 你愿意提交 PR 吗?
Code of Conduct
What happened / 发生了什么
代码中的
###是我特意在此写的说明,并不是原本代码里有的内容astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py的post_c2c_message没有额外的重试机制来处理botpy request返回none的情况.venv\Lib\site-packages\botpy\http.py而 botpy 里的request方法又写的比较难泵而且astrbot日志这边,完全没有捕获到有效的信息,接口响应是怎么样的也完全不知道,所以我也不知道为什么消息发不出去,我觉得目前只能临时在astrbot适配器这边额外加一层重试机制来保证尽可能的把消息发出去,botpy那边两年没更新了,就算给那边提pr,也应该不会有人鸟我我的pr的
更优的解法是把botpy依赖移除重新在适配器这边重新实现一遍请求,应该要花不少时间写的,只能后面在锁了
Reproduce / 如何复现?
我不知道.jpg
AstrBot version, deployment method (e.g., Windows Docker Desktop deployment), provider used, and messaging platform used. / AstrBot 版本、部署方式(如 Windows Docker Desktop 部署)、使用的提供商、使用的消息平台适配器
AstrBot v4.25.6 / Linux 源码+uv部署 /
qqofficial(websocket)适配器OS
Linux
Logs / 报错日志
Are you willing to submit a PR? / 你愿意提交 PR 吗?
Code of Conduct