Skip to content

Conversation

@Soulter
Copy link
Member

@Soulter Soulter commented Dec 27, 2025

fixes: #4195

Modifications / 改动点

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

Screenshots or Test Results / 运行截图或测试结果


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

在唤醒检查(waking check)流水线阶段集中处理唯一会话逻辑,使 unique_session 配置在各平台以及非默认 AstrBot 配置中表现一致。

Bug Fixes(错误修复):

  • 通过将会话 ID 逻辑从各个平台适配器中抽离到共享的流水线阶段,确保在非默认 AstrBot 配置中也能正确遵循 unique_session 配置。

Enhancements(增强改进):

  • 通过在适配器中统一默认的群聊和私聊会话行为,并将带有唯一会话(unique-session)变体的逻辑委托给核心流水线,实现多个平台适配器(aiocqhttpqqofficialqqofficial_webhooklarkslackwechatpadprodingtalkmisskeywebchat)的会话 ID 生成逻辑统一。
Original summary in English

Summary by Sourcery

Centralize unique session handling in the waking check pipeline stage so that unique_session configuration applies consistently across platforms and non-default AstrBot configs.

Bug Fixes:

  • Ensure unique_session configuration is respected for non-default AstrBot configurations by moving session ID logic out of individual platform adapters into a shared pipeline stage.

Enhancements:

  • Unify session ID generation across multiple platform adapters (aiocqhttp, qqofficial, qqofficial_webhook, lark, slack, wechatpadpro, dingtalk, misskey, webchat) by standardizing default group and private session behavior in adapters and delegating unique-session variants to the core pipeline.

@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. area:platform The bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on. labels Dec 27, 2025
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 - 我发现了 3 个问题,并给出了一些高层次的反馈:

  • WakingCheckStage.initialize 中,直接访问 self.ctx.astrbot_config["platform_settings"] 会在配置缺少该键时抛出 KeyError;可以考虑先使用 .get("platform_settings", {}),再调用 .get("unique_session", False)
  • WakingCheckStage.process 中对 unique_session 的处理使用了一长串 if event.get_platform_name() == ... 分支;可以考虑把各平台特定的 session ID 构造逻辑移动到一个映射或按平台划分的辅助函数中,以减少重复并让扩展更容易。
给 AI Agent 的提示
请根据这次代码审查中的评论进行修改:

## 总体评论
-`WakingCheckStage.initialize` 中,直接访问 `self.ctx.astrbot_config["platform_settings"]` 会在配置缺少该键时抛出 `KeyError`;可以考虑先使用 `.get("platform_settings", {})`,再调用 `.get("unique_session", False)`-`WakingCheckStage.process` 中对 `unique_session` 的处理使用了一长串 `if event.get_platform_name() == ...` 分支;可以考虑把各平台特定的 session ID 构造逻辑移动到一个映射或按平台划分的辅助函数中,以减少重复并让扩展更容易。

## 逐条评论

### Comment 1
<location> `astrbot/core/pipeline/waking_check/stage.py:57-58` </location>
<code_context>
         self.disable_builtin_commands = self.ctx.astrbot_config.get(
             "disable_builtin_commands", False
         )
+        self.unique_session = self.ctx.astrbot_config["platform_settings"].get(
+            "unique_session",
+            False,
+        )
</code_context>

<issue_to_address>
**issue:** 请为 `platform_settings` 使用更安全的访问方式,以避免在缺少该配置时出现 KeyError。

这里假设 `astrbot_config` 一定包含 `platform_settings`。如果省略了该配置项,pipeline 初始化会抛出 `KeyError`,而 `disable_builtin_commands` 的读取方式则是防御性的。你可以先为字典提供默认值来避免这个问题:

```python
platform_settings = self.ctx.astrbot_config.get("platform_settings", {})
self.unique_session = platform_settings.get("unique_session", False)
```
</issue_to_address>

### Comment 2
<location> `astrbot/core/platform/sources/misskey/misskey_adapter.py:641-644` </location>
<code_context>
             sender_info,
             self.client_self_id,
             is_chat=False,
-            unique_session=self.unique_session,
         )
         cache_user_info(
</code_context>

<issue_to_address>
**issue (bug_risk):** 在这里去掉 `unique_session` 会改变 Misskey 房间 `session_id` 的格式,与之前的 `create_base_message` 行为不一致。

如果没有 `unique_session=True`,房间的 `session_id` 会从 `room%{room_id}_{sender_id}` 变为 `room%{room_id}`,而后续 Misskey 的 `waking_check` 重写后会生成 `{group_id}_{sender_id}`,而不是 `room%{room_id}_{sender_id}`。这样会丢失 Misskey 房间 session 上的 `room%` 前缀。如果任何下游消费者(状态、路由、已有数据)依赖该前缀,这可能会破坏兼容性,或与非房间的群组 ID 产生冲突。请确认该前缀是否仍然是必需的;如果是,请在当前 pipeline 中保留它,或者在调整 `waking_check` 时仍保持原有的 `session_id` 结构。
</issue_to_address>

### Comment 3
<location> `astrbot/core/pipeline/waking_check/stage.py:66` </location>
<code_context>
         self,
         event: AstrMessageEvent,
     ) -> None | AsyncGenerator[None, None]:
+        # apply unique session
+        if self.unique_session and event.message_obj.type == MessageType.GROUP_MESSAGE:
+            if event.get_platform_name() in ["aiocqhttp", "slack"]:
</code_context>

<issue_to_address>
**issue (complexity):** 建议把 unique-session 的平台分支逻辑抽取到一个专门的 helper/dispatcher 中,这样可以保持 `process` 足够小,并且在平台增多时更易扩展。

你可以保留这个新特性,但通过解耦各平台分支逻辑,降低 `process` 的复杂度和未来的增长。

### 1. 用一个小的 dispatcher 替代 `if/elif`

把 session-id 的逻辑移到一个专门的 helper(可以是该类的方法,也可以是模块级函数)中。这样可以保持 `process` 的简单性,并把平台规则集中管理:

```python
# at module level
SESSION_ID_BUILDERS: dict[str, callable] = {
    "aiocqhttp": lambda e: f"{e.get_sender_id()}_{e.get_group_id()}",
    "slack": lambda e: f"{e.get_sender_id()}_{e.get_group_id()}",
    "dingtalk": lambda e: e.get_sender_id(),
    "qq_official": lambda e: e.get_sender_id(),
    "qq_official_webhook": lambda e: e.get_sender_id(),
    "lark": lambda e: f"{e.get_sender_id()}%{e.get_group_id()}",
    "misskey": lambda e: f"{e.get_group_id()}_{e.get_sender_id()}",
    "wechatpadpro": lambda e: f"{e.get_group_id()}#{e.get_sender_id()}",
}

def build_unique_session_id(event: AstrMessageEvent) -> str | None:
    platform = event.get_platform_name()
    builder = SESSION_ID_BUILDERS.get(platform)
    return builder(event) if builder else None
```

然后 `process` 可以变为:

```python
async def process(
    self,
    event: AstrMessageEvent,
) -> None | AsyncGenerator[None, None]:
    if self.unique_session and event.message_obj.type == MessageType.GROUP_MESSAGE:
        sid = build_unique_session_id(event)
        if sid is not None:
            event.session_id = sid

    # existing logic continues...
```

好处:

- `process` 不再会随着平台数量增加而不断膨胀。
- 各平台特定规则被集中管理、相互独立。
- 新增平台时只需要在 `SESSION_ID_BUILDERS` 中增加一行,而不是再写一个分支。
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的 review 有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据反馈改进之后的 review。
Original comment in English

Hey - I've found 3 issues, and left some high level feedback:

  • In WakingCheckStage.initialize, accessing self.ctx.astrbot_config["platform_settings"] directly can raise a KeyError for configs without that key; consider using .get("platform_settings", {}) before calling .get("unique_session", False).
  • The unique_session handling in WakingCheckStage.process has a long chain of if event.get_platform_name() == ... branches; consider moving the platform-specific session ID construction into a mapping or a per-platform helper to reduce duplication and make it easier to extend.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `WakingCheckStage.initialize`, accessing `self.ctx.astrbot_config["platform_settings"]` directly can raise a `KeyError` for configs without that key; consider using `.get("platform_settings", {})` before calling `.get("unique_session", False)`.
- The `unique_session` handling in `WakingCheckStage.process` has a long chain of `if event.get_platform_name() == ...` branches; consider moving the platform-specific session ID construction into a mapping or a per-platform helper to reduce duplication and make it easier to extend.

## Individual Comments

### Comment 1
<location> `astrbot/core/pipeline/waking_check/stage.py:57-58` </location>
<code_context>
         self.disable_builtin_commands = self.ctx.astrbot_config.get(
             "disable_builtin_commands", False
         )
+        self.unique_session = self.ctx.astrbot_config["platform_settings"].get(
+            "unique_session",
+            False,
+        )
</code_context>

<issue_to_address>
**issue:** Use a safer access pattern for `platform_settings` to avoid KeyError in configs that omit it.

This assumes `astrbot_config` always has `platform_settings`. If that section is omitted, pipeline init will raise a `KeyError`, unlike `disable_builtin_commands` which is read defensively. You can avoid this by first defaulting the dict:

```python
platform_settings = self.ctx.astrbot_config.get("platform_settings", {})
self.unique_session = platform_settings.get("unique_session", False)
```
</issue_to_address>

### Comment 2
<location> `astrbot/core/platform/sources/misskey/misskey_adapter.py:641-644` </location>
<code_context>
             sender_info,
             self.client_self_id,
             is_chat=False,
-            unique_session=self.unique_session,
         )
         cache_user_info(
</code_context>

<issue_to_address>
**issue (bug_risk):** Dropping `unique_session` here changes Misskey room session_id format vs previous `create_base_message` behavior.

Without `unique_session=True`, room `session_id` changes from `room%{room_id}_{sender_id}` to `room%{room_id}`, and the later Misskey `waking_check` rewrite produces `{group_id}_{sender_id}` instead of `room%{room_id}_{sender_id}`. This drops the `room%` prefix for Misskey room sessions. If any downstream consumers (state, routing, existing data) depend on that prefix, this may break compatibility or collide with non-room group IDs. Please confirm whether the prefix is still required and, if so, either keep it in this pipeline or adjust `waking_check` while preserving the previous `session_id` shape.
</issue_to_address>

### Comment 3
<location> `astrbot/core/pipeline/waking_check/stage.py:66` </location>
<code_context>
         self,
         event: AstrMessageEvent,
     ) -> None | AsyncGenerator[None, None]:
+        # apply unique session
+        if self.unique_session and event.message_obj.type == MessageType.GROUP_MESSAGE:
+            if event.get_platform_name() in ["aiocqhttp", "slack"]:
</code_context>

<issue_to_address>
**issue (complexity):** Consider extracting the unique-session platform branching into a dedicated helper/dispatcher so `process` stays small and easier to extend as platforms grow.

You can keep the new feature but decouple the platform-specific branching to reduce complexity and future growth in `process`.

### 1. Extract a small dispatcher instead of the `if/elif` chain

Move the session-id logic into a dedicated helper (either as a method on this class or a module-level function). This keeps `process` simple and localizes platform rules:

```python
# at module level
SESSION_ID_BUILDERS: dict[str, callable] = {
    "aiocqhttp": lambda e: f"{e.get_sender_id()}_{e.get_group_id()}",
    "slack": lambda e: f"{e.get_sender_id()}_{e.get_group_id()}",
    "dingtalk": lambda e: e.get_sender_id(),
    "qq_official": lambda e: e.get_sender_id(),
    "qq_official_webhook": lambda e: e.get_sender_id(),
    "lark": lambda e: f"{e.get_sender_id()}%{e.get_group_id()}",
    "misskey": lambda e: f"{e.get_group_id()}_{e.get_sender_id()}",
    "wechatpadpro": lambda e: f"{e.get_group_id()}#{e.get_sender_id()}",
}

def build_unique_session_id(event: AstrMessageEvent) -> str | None:
    platform = event.get_platform_name()
    builder = SESSION_ID_BUILDERS.get(platform)
    return builder(event) if builder else None
```

Then `process` becomes:

```python
async def process(
    self,
    event: AstrMessageEvent,
) -> None | AsyncGenerator[None, None]:
    if self.unique_session and event.message_obj.type == MessageType.GROUP_MESSAGE:
        sid = build_unique_session_id(event)
        if sid is not None:
            event.session_id = sid

    # existing logic continues...
```

Benefits:

- `process` no longer grows with each platform.
- Platform-specific rules are centralized and self-contained.
- Adding a new platform is a one-line addition to `SESSION_ID_BUILDERS` instead of another branch.
</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.

@Soulter Soulter merged commit fc61f7a into master Dec 28, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:platform The bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

关于群成员对话隔离的问题

2 participants