-
-
Notifications
You must be signed in to change notification settings - Fork 809
解决wechatpadpro接收不到消息的问题! #2042
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
解决wechatpadpro接收不到消息的问题! #2042
Conversation
## 审查者指南
将之前的基于 websockets 的连接和手动轮询循环替换为 aiohttp 的 ClientSession.ws_connect 和 async-for 消息循环,从而提高可靠性并简化错误处理。
### 文件级别更改
| 变更 | 详情 | 文件 |
| ------ | ------- | ----- |
| 切换到 aiohttp 以进行 WebSocket 连接和消息处理 | <ul><li>将 websockets.connect 替换为 aiohttp.ClientSession.ws_connect</li><li>删除手动基于超时的 recv 循环和相关的异常分支</li><li>引入了处理 TEXT、ERROR 和 CLOSED 类型的 ws 消息的 async for 循环</li><li>在 ERROR 和 CLOSED 消息类型上引发异常以触发重新连接</li></ul> | `astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py` |
### 可能相关的 issue
- **#1977**: 此 PR 使用 aiohttp 替换 websockets 客户端以修复该问题。
---
<details>
<summary>提示和命令</summary>
#### 与 Sourcery 互动
- **触发新的审查:** 在 pull request 上评论 `@sourcery-ai review`。
- **继续讨论:** 直接回复 Sourcery 的审查评论。
- **从审查评论生成 GitHub issue:** 通过回复审查评论,要求 Sourcery 从审查评论创建一个 issue。 您也可以回复带有 `@sourcery-ai issue` 的审查评论以从中创建一个 issue。
- **生成 pull request 标题:** 在 pull request 标题中的任何位置写入 `@sourcery-ai` 以随时生成标题。 您也可以在 pull request 上评论 `@sourcery-ai title` 以随时(重新)生成标题。
- **生成 pull request 摘要:** 在 pull request 正文中的任何位置写入 `@sourcery-ai summary` 以随时在你想要的位置生成 PR 摘要。 您也可以在 pull request 上评论 `@sourcery-ai summary` 以随时(重新)生成摘要。
- **生成审查者指南:** 在 pull request 上评论 `@sourcery-ai guide` 以随时(重新)生成审查者指南。
- **解决所有 Sourcery 评论:** 在 pull request 上评论 `@sourcery-ai resolve` 以解决所有 Sourcery 评论。 如果您已经解决了所有评论并且不想再看到它们,这将非常有用。
- **驳回所有 Sourcery 审查:** 在 pull request 上评论 `@sourcery-ai dismiss` 以驳回所有现有的 Sourcery 审查。 如果你想用一个新的审查重新开始,这尤其有用 - 不要忘记评论 `@sourcery-ai review` 以触发一个新的审查!
#### 自定义您的体验
访问您的 [仪表板](https://app.sourcery.ai) 以:
- 启用或禁用审查功能,例如 Sourcery 生成的 pull request 摘要、审查者指南等。
- 更改审查语言。
- 添加、删除或编辑自定义审查说明。
- 调整其他审查设置。
#### 获得帮助
- [联系我们的支持团队](mailto:[email protected]) 提出问题或反馈。
- 访问我们的[文档](https://docs.sourcery.ai) 以获取详细的指南和信息。
- 通过在 [X/Twitter](https://x.com/SourceryAI)、[LinkedIn](https://www.linkedin.com/company/sourcery-ai/) 或 [GitHub](https://github.com/sourcery-ai) 上关注我们,与 Sourcery 团队保持联系。
</details> Original review guide in EnglishReviewer's GuideReplaces the previous websockets-based connection and manual polling loop with aiohttp’s ClientSession.ws_connect and an async-for message loop, improving reliability and simplifying error handling. File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
嘿 @yigehaozi - 我已经查看了你的更改 - 这里有一些反馈:
- 为了避免在持久性故障时出现紧密的重新连接循环,请在重试 WebSocket 连接之前添加一个小的退避延迟。
- 使用更具体的自定义异常(例如,WebSocketClosedError)而不是通用的 Exception,以区分正常关闭和实际错误。
- 如果服务可能会发送二进制帧,请考虑处理 WSMsgType.BINARY(或记录意外的消息类型)以实现更强大的处理。
AI 代理的提示
请解决此代码审查中的评论:
## 总体评论
- 为了避免在持久性故障时出现紧密的重新连接循环,请在重试 WebSocket 连接之前添加一个小的退避延迟。
- 使用更具体的自定义异常(例如,WebSocketClosedError)而不是通用的 Exception,以区分正常关闭和实际错误。
- 如果服务可能会发送二进制帧,请考虑处理 WSMsgType.BINARY(或记录意外的消息类型)以实现更强大的处理。
## 单独评论
### 评论 1
<location> `astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py:372` </location>
<code_context>
+ async with aiohttp.ClientSession() as session:
+ async with session.ws_connect(ws_url) as ws:
+ logger.info("WebSocket 连接成功。")
+ async for msg in ws:
+ if msg.type == aiohttp.WSMsgType.TEXT:
+ asyncio.create_task(self.handle_websocket_message(msg.data))
</code_context>
<issue_to_address>
新的循环中没有空闲超时或重新连接逻辑。
如果没有空闲超时或心跳,客户端可能会在服务器无响应时挂起。请添加一种机制来检测和处理空闲连接。
</issue_to_address>
### 评论 2
<location> `astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py:375` </location>
<code_context>
+ async for msg in ws:
+ if msg.type == aiohttp.WSMsgType.TEXT:
+ asyncio.create_task(self.handle_websocket_message(msg.data))
+ elif msg.type == aiohttp.WSMsgType.ERROR:
+ message = f"WebSocket 链接错误: {ws.exception()}"
+ logger.info(message)
+ raise Exception(message)
+ elif msg.type == aiohttp.WSMsgType.CLOSED:
+ message = f"WebSocket 链接正常关闭。"
</code_context>
<issue_to_address>
在 WebSocket 错误或关闭时引发通用 Exception 可能会掩盖原始原因。
在此处引发通用 Exception 会丢弃原始异常上下文。使用 'raise ws.exception()' 或 'raise Exception(message) from ws.exception()' 来保留堆栈跟踪并帮助调试。
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
elif msg.type == aiohttp.WSMsgType.ERROR:
message = f"WebSocket 链接错误: {ws.exception()}"
logger.info(message)
raise Exception(message)
elif msg.type == aiohttp.WSMsgType.CLOSED:
message = f"WebSocket 链接正常关闭。"
logger.info(message)
raise Exception(message)
=======
elif msg.type == aiohttp.WSMsgType.ERROR:
message = f"WebSocket 链接错误: {ws.exception()}"
logger.info(message)
raise Exception(message) from ws.exception()
elif msg.type == aiohttp.WSMsgType.CLOSED:
message = f"WebSocket 链接正常关闭。"
logger.info(message)
if ws.exception() is not None:
raise Exception(message) from ws.exception()
else:
raise Exception(message)
>>>>>>> REPLACE
</suggested_fix>
帮助我更有用!请点击每个评论上的 👍 或 👎,我将使用反馈来改进您的评论。
Original comment in English
Hey @yigehaozi - I've reviewed your changes - here's some feedback:
- To avoid tight reconnection loops on persistent failures, add a small backoff delay before retrying the WebSocket connection.
- Use a more specific custom exception (e.g., WebSocketClosedError) instead of generic Exception to distinguish between normal closures and actual errors.
- If the service might send binary frames, consider handling WSMsgType.BINARY (or logging unexpected message types) for more robust handling.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- To avoid tight reconnection loops on persistent failures, add a small backoff delay before retrying the WebSocket connection.
- Use a more specific custom exception (e.g., WebSocketClosedError) instead of generic Exception to distinguish between normal closures and actual errors.
- If the service might send binary frames, consider handling WSMsgType.BINARY (or logging unexpected message types) for more robust handling.
## Individual Comments
### Comment 1
<location> `astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py:372` </location>
<code_context>
+ async with aiohttp.ClientSession() as session:
+ async with session.ws_connect(ws_url) as ws:
+ logger.info("WebSocket 连接成功。")
+ async for msg in ws:
+ if msg.type == aiohttp.WSMsgType.TEXT:
+ asyncio.create_task(self.handle_websocket_message(msg.data))
</code_context>
<issue_to_address>
No idle timeout or reconnection logic is present in the new loop.
Without an idle timeout or heartbeat, the client may hang if the server becomes unresponsive. Please add a mechanism to detect and handle idle connections.
</issue_to_address>
### Comment 2
<location> `astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py:375` </location>
<code_context>
+ async for msg in ws:
+ if msg.type == aiohttp.WSMsgType.TEXT:
+ asyncio.create_task(self.handle_websocket_message(msg.data))
+ elif msg.type == aiohttp.WSMsgType.ERROR:
+ message = f"WebSocket 链接错误: {ws.exception()}"
+ logger.info(message)
+ raise Exception(message)
+ elif msg.type == aiohttp.WSMsgType.CLOSED:
+ message = f"WebSocket 链接正常关闭。"
</code_context>
<issue_to_address>
Raising a generic Exception on WebSocket error or close may obscure the original cause.
Raising a generic Exception here discards the original exception context. Use 'raise ws.exception()' or 'raise Exception(message) from ws.exception()' to retain the stack trace and aid debugging.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
elif msg.type == aiohttp.WSMsgType.ERROR:
message = f"WebSocket 链接错误: {ws.exception()}"
logger.info(message)
raise Exception(message)
elif msg.type == aiohttp.WSMsgType.CLOSED:
message = f"WebSocket 链接正常关闭。"
logger.info(message)
raise Exception(message)
=======
elif msg.type == aiohttp.WSMsgType.ERROR:
message = f"WebSocket 链接错误: {ws.exception()}"
logger.info(message)
raise Exception(message) from ws.exception()
elif msg.type == aiohttp.WSMsgType.CLOSED:
message = f"WebSocket 链接正常关闭。"
logger.info(message)
if ws.exception() is not None:
raise Exception(message) from ws.exception()
else:
raise Exception(message)
>>>>>>> REPLACE
</suggested_fix>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
elif msg.type == aiohttp.WSMsgType.ERROR: | ||
message = f"WebSocket 链接错误: {ws.exception()}" | ||
logger.info(message) | ||
raise Exception(message) | ||
elif msg.type == aiohttp.WSMsgType.CLOSED: | ||
message = f"WebSocket 链接正常关闭。" | ||
logger.info(message) | ||
raise Exception(message) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): 在 WebSocket 错误或关闭时引发通用 Exception 可能会掩盖原始原因。
在此处引发通用 Exception 会丢弃原始异常上下文。使用 'raise ws.exception()' 或 'raise Exception(message) from ws.exception()' 来保留堆栈跟踪并帮助调试。
elif msg.type == aiohttp.WSMsgType.ERROR: | |
message = f"WebSocket 链接错误: {ws.exception()}" | |
logger.info(message) | |
raise Exception(message) | |
elif msg.type == aiohttp.WSMsgType.CLOSED: | |
message = f"WebSocket 链接正常关闭。" | |
logger.info(message) | |
raise Exception(message) | |
elif msg.type == aiohttp.WSMsgType.ERROR: | |
message = f"WebSocket 链接错误: {ws.exception()}" | |
logger.info(message) | |
raise Exception(message) from ws.exception() | |
elif msg.type == aiohttp.WSMsgType.CLOSED: | |
message = f"WebSocket 链接正常关闭。" | |
logger.info(message) | |
if ws.exception() is not None: | |
raise Exception(message) from ws.exception() | |
else: | |
raise Exception(message) |
Original comment in English
suggestion (bug_risk): Raising a generic Exception on WebSocket error or close may obscure the original cause.
Raising a generic Exception here discards the original exception context. Use 'raise ws.exception()' or 'raise Exception(message) from ws.exception()' to retain the stack trace and aid debugging.
elif msg.type == aiohttp.WSMsgType.ERROR: | |
message = f"WebSocket 链接错误: {ws.exception()}" | |
logger.info(message) | |
raise Exception(message) | |
elif msg.type == aiohttp.WSMsgType.CLOSED: | |
message = f"WebSocket 链接正常关闭。" | |
logger.info(message) | |
raise Exception(message) | |
elif msg.type == aiohttp.WSMsgType.ERROR: | |
message = f"WebSocket 链接错误: {ws.exception()}" | |
logger.info(message) | |
raise Exception(message) from ws.exception() | |
elif msg.type == aiohttp.WSMsgType.CLOSED: | |
message = f"WebSocket 链接正常关闭。" | |
logger.info(message) | |
if ws.exception() is not None: | |
raise Exception(message) from ws.exception() | |
else: | |
raise Exception(message) |
elif msg.type == aiohttp.WSMsgType.ERROR: | ||
message = f"WebSocket 链接错误: {ws.exception()}" | ||
logger.info(message) | ||
raise Exception(message) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (code-quality): 引发特定的错误,而不是通用的 Exception
或 BaseException
(raise-specific-error
)
Explanation
如果一段代码引发一个特定的异常类型,而不是通用的 [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) 或 [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception),则调用代码可以:- 获取关于错误类型的更多信息
- 为其定义特定的异常处理
这样,代码的调用者可以适当地处理错误。
您如何解决这个问题?
因此,与其让代码引发 Exception
或 BaseException
,例如:
if incorrect_input(value):
raise Exception("The input is incorrect")
您可以让代码引发一个特定的错误,例如:
if incorrect_input(value):
raise ValueError("The input is incorrect")
或者:
class IncorrectInputError(Exception):
pass
if incorrect_input(value):
raise IncorrectInputError("The input is incorrect")
Original comment in English
issue (code-quality): Raise a specific error instead of the general Exception
or BaseException
(raise-specific-error
)
Explanation
If a piece of code raises a specific exception type rather than the generic [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) or [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception), the calling code can:- get more information about what type of error it is
- define specific exception handling for it
This way, callers of the code can handle the error appropriately.
How can you solve this?
- Use one of the built-in exceptions of the standard library.
- Define your own error class that subclasses
Exception
.
So instead of having code raising Exception
or BaseException
like
if incorrect_input(value):
raise Exception("The input is incorrect")
you can have code raising a specific error like
if incorrect_input(value):
raise ValueError("The input is incorrect")
or
class IncorrectInputError(Exception):
pass
if incorrect_input(value):
raise IncorrectInputError("The input is incorrect")
elif msg.type == aiohttp.WSMsgType.CLOSED: | ||
message = f"WebSocket 链接正常关闭。" | ||
logger.info(message) | ||
raise Exception(message) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (code-quality): 引发特定的错误,而不是通用的 Exception
或 BaseException
(raise-specific-error
)
Explanation
如果一段代码引发一个特定的异常类型,而不是通用的 [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) 或 [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception),则调用代码可以:- 获取关于错误类型的更多信息
- 为其定义特定的异常处理
这样,代码的调用者可以适当地处理错误。
您如何解决这个问题?
因此,与其让代码引发 Exception
或 BaseException
,例如:
if incorrect_input(value):
raise Exception("The input is incorrect")
您可以让代码引发一个特定的错误,例如:
if incorrect_input(value):
raise ValueError("The input is incorrect")
或者:
class IncorrectInputError(Exception):
pass
if incorrect_input(value):
raise IncorrectInputError("The input is incorrect")
Original comment in English
issue (code-quality): Raise a specific error instead of the general Exception
or BaseException
(raise-specific-error
)
Explanation
If a piece of code raises a specific exception type rather than the generic [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) or [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception), the calling code can:- get more information about what type of error it is
- define specific exception handling for it
This way, callers of the code can handle the error appropriately.
How can you solve this?
- Use one of the built-in exceptions of the standard library.
- Define your own error class that subclasses
Exception
.
So instead of having code raising Exception
or BaseException
like
if incorrect_input(value):
raise Exception("The input is incorrect")
you can have code raising a specific error like
if incorrect_input(value):
raise ValueError("The input is incorrect")
or
class IncorrectInputError(Exception):
pass
if incorrect_input(value):
raise IncorrectInputError("The input is incorrect")
logger.info(message) | ||
raise Exception(message) | ||
elif msg.type == aiohttp.WSMsgType.CLOSED: | ||
message = f"WebSocket 链接正常关闭。" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): 将没有插值的 f-string 替换为字符串 (remove-redundant-fstring
)
message = f"WebSocket 链接正常关闭。" | |
message = "WebSocket 链接正常关闭。" |
Original comment in English
suggestion (code-quality): Replace f-string with no interpolated values with string (remove-redundant-fstring
)
message = f"WebSocket 链接正常关闭。" | |
message = "WebSocket 链接正常关闭。" |
为什么这样改可以解决这个问题呢 |
解决了 #1977
Motivation
Modifications
Check
requirements.txt
和pyproject.toml
文件相应位置。好的,这是翻译成中文的 pull request 总结:
Sourcery 总结
在 WeChatPadPro 适配器中使用 aiohttp 客户端进行 WebSocket 连接,以确保可靠的消息接收,并简化连接和错误处理。
Bug 修复:
websockets.connect
切换到aiohttp.ws_connect
,以修复 WeChatPadPro 中消息丢失的问题。增强功能:
Original summary in English
Summary by Sourcery
Use aiohttp client for WebSocket connections in the WeChatPadPro adapter to ensure reliable message reception and simplify connection and error handling
Bug Fixes:
Enhancements: