Skip to content

Conversation

shangxueink
Copy link
Contributor

@shangxueink shangxueink commented Oct 16, 2025

Motivation / 动机

支持合并转发功能,在 Satori 适配器中添加对 ForwardNodeNodes 消息组件的支持。

当前适配器缺少对这些组件的处理,导致发送合并转发消息时失败。

Modifications / 改动点

  • 修改文件: astrbot/core/platform/sources/satori/satori_event.py

  • 主要改动:

    1. 导入新增的 ForwardNodeNodes 组件类
    2. 重构消息组件处理逻辑,提取公共方法 _convert_component_to_satori()_convert_component_to_satori_static()
    3. 实现转发节点转换方法 _convert_node_to_satori()_convert_node_to_satori_static()
    4. 实现合并转发转换方法 _convert_nodes_to_satori()_convert_nodes_to_satori_static()
    5. 更新 send_with_adapter()send() 方法以支持转发组件
  • 支持的功能:

    • ✅ 单条消息转发: <message id="123456789" forward/>
    • ✅ 合并转发: <message forward><message><author id="123" name="用户"/>{内容}</message>...</message>
    • ✅ 模拟用户消息: <message><author id="123" name="Alice"/>{内容}</message>

Compatibility & Breaking Changes / 兼容性与破坏性变更

  • 这是一个破坏性变更 (Breaking Change)。/ This is a breaking change.
  • 这不是一个破坏性变更。/ This is NOT a breaking change.

兼容性说明: 此变更完全向后兼容,只是新增功能,不会影响现有代码的正常运行。


Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
  • 👀 我的更改经过了良好的测试,并已在上方提供了"验证步骤"和"运行截图"。/ My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

Summary by Sourcery

在 Satori 适配器中启用合并转发消息,通过引入 Forward、Node 和 Nodes 转换,并整合组件到 Satori 的格式化逻辑

New Features:

  • 支持 Forward、Node 和 Nodes 组件,以在 Satori 中启用单个转发、合并转发和用户原创消息

Enhancements:

  • 将消息组件转换重构为共享的辅助方法,用于实例和静态上下文
Original summary in English

Summary by Sourcery

Enable merged forwarding messages in the Satori adapter by introducing Forward, Node, and Nodes conversions and consolidating component-to-Satori formatting logic

New Features:

  • Support Forward, Node, and Nodes components to enable single forward, merged forward, and authored user messages in Satori

Enhancements:

  • Refactor message component conversion into shared helper methods for instance and static contexts

@auto-assign auto-assign bot requested review from Fridemn and Raven95676 October 16, 2025 10:56
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • You’ve introduced a lot of near-identical logic between the instance and static conversion methods—consider consolidating them into one reusable conversion function or a type→handler registry to eliminate duplication.
  • The send and send_with_adapter methods share almost identical loops for building content_parts—extract that into a single helper to keep your code DRY and easier to maintain.
  • Instead of special-casing Node/Nodes in the loop, consider folding their conversion into your main _convert_component_to_satori pipeline so the send methods can treat all components uniformly.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- You’ve introduced a lot of near-identical logic between the instance and static conversion methods—consider consolidating them into one reusable conversion function or a type→handler registry to eliminate duplication.
- The send and send_with_adapter methods share almost identical loops for building `content_parts`—extract that into a single helper to keep your code DRY and easier to maintain.
- Instead of special-casing Node/Nodes in the loop, consider folding their conversion into your main `_convert_component_to_satori` pipeline so the send methods can treat all components uniformly.

## Individual Comments

### Comment 1
<location> `astrbot/core/platform/sources/satori/satori_event.py:188-197` </location>
<code_context>
+    async def _convert_component_to_satori(self, component) -> str:
</code_context>

<issue_to_address>
**suggestion:** The method returns an empty string for unhandled component types, which may silently drop unknown components.

Consider adding a warning or error log when an unrecognized component type is encountered to improve maintainability and debugging.

Suggested implementation:

```python
    async def _convert_component_to_satori(self, component) -> str:
        """将单个消息组件转换为 Satori 格式"""
        try:
            if isinstance(component, Plain):
                text = (
                    component.text.replace("&", "&amp;")
                    .replace("<", "&lt;")
                    .replace(">", "&gt;")
                )
                return text
            # Add a warning log for unrecognized component types
            logger.warning(f"Unrecognized component type encountered: {type(component).__name__}")
            return ""

```

If `logger` is not already imported in this file, you should add:
```python
from astrbot.core.utils import logger
```
or the appropriate import statement for your project's logger at the top of the file.
</issue_to_address>

### Comment 2
<location> `astrbot/core/platform/sources/satori/satori_event.py:262-268` </location>
<code_context>
+                content = "[转发消息]"
+            
+            # 构建 Satori 格式的转发节点
+            author_attrs = []
+            if node.uin:
+                author_attrs.append(f'id="{node.uin}"')
+            if node.name:
+                author_attrs.append(f'name="{node.name}"')
+            
+            author_attr_str = " ".join(author_attrs)
+            
+            return f'<message><author {author_attr_str}/>{content}</message>'
</code_context>

<issue_to_address>
**issue (bug_risk):** Author attributes are concatenated without escaping, which may cause issues if values contain special characters.

Escape attribute values to prevent malformed XML when special characters are present.
</issue_to_address>

### Comment 3
<location> `astrbot/core/platform/sources/satori/satori_event.py:365-372` </location>
<code_context>
+            logger.error(f"转换转发节点失败: {e}")
+            return ""
+
+    async def _convert_nodes_to_satori(self, nodes: Nodes) -> str:
+        """将多个转发节点转换为 Satori 格式的合并转发"""
+        try:
+            node_parts = []
+            
+            for node in nodes.nodes:
+                node_content = await self._convert_node_to_satori(node)
+                if node_content:
</code_context>

<issue_to_address>
**suggestion:** No handling for empty nodes list in _convert_nodes_to_satori.

Returning an empty string when nodes.nodes is empty could lead to confusion. Please add explicit handling for this case or log a warning to improve clarity.

```suggestion
    async def _convert_nodes_to_satori(self, nodes: Nodes) -> str:
        """将多个转发节点转换为 Satori 格式的合并转发"""
        try:
            if not nodes.nodes:
                logger.warning("尝试转换空的转发节点列表")
                return ""
            node_parts = []

            for node in nodes.nodes:
                node_content = await self._convert_node_to_satori(node)
                if node_content:
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +188 to +197
async def _convert_component_to_satori(self, component) -> str:
"""将单个消息组件转换为 Satori 格式"""
try:
if isinstance(component, Plain):
text = (
component.text.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
)
return text
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The method returns an empty string for unhandled component types, which may silently drop unknown components.

Consider adding a warning or error log when an unrecognized component type is encountered to improve maintainability and debugging.

Suggested implementation:

    async def _convert_component_to_satori(self, component) -> str:
        """将单个消息组件转换为 Satori 格式"""
        try:
            if isinstance(component, Plain):
                text = (
                    component.text.replace("&", "&amp;")
                    .replace("<", "&lt;")
                    .replace(">", "&gt;")
                )
                return text
            # Add a warning log for unrecognized component types
            logger.warning(f"Unrecognized component type encountered: {type(component).__name__}")
            return ""

If logger is not already imported in this file, you should add:

from astrbot.core.utils import logger

or the appropriate import statement for your project's logger at the top of the file.

Comment on lines +365 to +372
async def _convert_nodes_to_satori(self, nodes: Nodes) -> str:
"""将多个转发节点转换为 Satori 格式的合并转发"""
try:
node_parts = []

for node in nodes.nodes:
node_content = await self._convert_node_to_satori(node)
if node_content:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: No handling for empty nodes list in _convert_nodes_to_satori.

Returning an empty string when nodes.nodes is empty could lead to confusion. Please add explicit handling for this case or log a warning to improve clarity.

Suggested change
async def _convert_nodes_to_satori(self, nodes: Nodes) -> str:
"""将多个转发节点转换为 Satori 格式的合并转发"""
try:
node_parts = []
for node in nodes.nodes:
node_content = await self._convert_node_to_satori(node)
if node_content:
async def _convert_nodes_to_satori(self, nodes: Nodes) -> str:
"""将多个转发节点转换为 Satori 格式的合并转发"""
try:
if not nodes.nodes:
logger.warning("尝试转换空的转发节点列表")
return ""
node_parts = []
for node in nodes.nodes:
node_content = await self._convert_node_to_satori(node)
if node_content:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant