Skip to content

[Bug]qqofficial(websocket)适配器在某些情况下发送消息失败后不会重试发送 #8977

Description

@shuiping233

What happened / 发生了什么

代码中的###是我特意在此写的说明,并不是原本代码里有的内容

astrbot/core/platform/sources/qqofficial/qqofficial_message_event.pypost_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.pybotpy 里的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 吗?

  • Yes!

Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:platformThe bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on.bugSomething isn't workingpriority: p1

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions