fix(session_waiter): bind empty-mention waiter to the initiating sender (#9377) - #9422
fix(session_waiter): bind empty-mention waiter to the initiating sender (#9377)#9422Qixuan112 wants to merge 4 commits into
Conversation
…er to avoid intercepting other group members (AstrBotDevs#9377)
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- Consider moving
_SenderSessionFilterout ofhandle_empty_mentioninto module scope so it isn’t redefined on every call and can be reused or tested independently. - When building the session key in
_SenderSessionFilter.filter, consider explicitly handling cases whereget_sender_id()might beNoneor unexpected to avoid collisions or hard-to-debug behavior.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider moving `_SenderSessionFilter` out of `handle_empty_mention` into module scope so it isn’t redefined on every call and can be reused or tested independently.
- When building the session key in `_SenderSessionFilter.filter`, consider explicitly handling cases where `get_sender_id()` might be `None` or unexpected to avoid collisions or hard-to-debug behavior.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
Addressed both suggestions in dfd6c00:
(🤖 Addressed by Claude Code) |
xiaoyuyu6420
left a comment
There was a problem hiding this comment.
Review
Reviewed in the context of issue #9377 alongside the other open PRs (#9378, #9442).
Strengths
- Minimal diff — only touches
main.py, adds a small private class. Lowest risk of unintended side effects. - Private
_SenderSessionFilter— scoped to the module, no new public API surface to maintain. __unknown_sender__sentinel — degrades gracefully whensender_idis unavailable instead of skipping the waiter entirely.
Issues
1. No tests
This is a bug fix with zero test coverage. Given that two other PRs for the same issue provide extensive tests, this is a significant gap. At minimum, the following should be tested:
- Two senders in the same group produce different keys
- The original sender's follow-up is still captured
- Cross-conversation isolation
__unknown_sender__sentinel behavior
2. Does not fix the non-text message swallowing bug (mentioned in #9377 as a "连带问题")
The handler body is unchanged:
if not event.message_str or not event.message_str.strip():
return # pure images are still silently dropped3. Does not fix the _cleanup race condition (mentioned in #9377)
USER_SESSIONS.pop(self.session_id) in _cleanup() can evict a newer waiter registered under the same key.
4. Only fixes empty_mention_waiter, not is_wake_prefix_only
The issue report states the cross-user interception also applies to the wake-prefix-only path, which still uses DefaultSessionFilter.
5. __unknown_sender__ could collide with a real sender_id
While extremely unlikely, a platform could theoretically return "__unknown_sender__" as an actual user ID. A value like "<unknown>" (with angle brackets) or a UUID-based sentinel would be safer since real IDs never contain brackets. Alternatively, PR #9378's approach of skipping the waiter (with a log warning) avoids the collision entirely.
6. Key collision with : separator
The format f"{ev.unified_msg_origin}:{sender_id}" uses : as separator, but unified_msg_origin already contains : (e.g., napcat2:GroupMessage:984306989). While in practice this does not cause ambiguity because the format is fixed (platform:Type:id), a Telegram topic-group origin like telegram:GroupMessage:-100123#42 combined with a sender ID containing : could theoretically produce collisions. PR #9378 addresses this with length-prefixed encoding.
Summary
This is the most minimal fix for the core cross-user interception bug, but it lacks tests and does not address the two related issues (non-text swallowing, cleanup race) documented in #9377. The sentinel value and key format have minor theoretical concerns that are unlikely to cause problems in practice but worth noting.
Correction to my earlier reviewI need to correct issue #4 in my previous review. I wrote that the After re-reading the control flow: both Thanks to @hedssaz's response on #9378 for catching this — I made the same error in both reviews. The other points (missing tests, non-text swallowing, cleanup race) still stand. |
Add locked tests covering the sender-binding fix in AstrBotDevs#9422 / AstrBotDevs#9377: - Different senders in the same group produce different session keys - Cross-session isolation: same sender in different groups differ - Repeatability: same (group, sender) maps to the same key - Falsy sender ids ("", None, 0) fall back to the "<unknown>" sentinel - A real sender id equal to the legacy sentinel string does not collide with the unknown-sender fallback key - Integration: the original sender's follow-up is captured, other group members' messages are not, cross-conversation messages are not, and blank follow-ups do not satisfy the waiter These tests encode the expected behavior and serve as the acceptance contract for the sender-scoped waiter.
Address review feedback AstrBotDevs#5 on AstrBotDevs#9422: the previous "__unknown_sender__" sentinel could collide with a real platform sender id of the same string, letting that member still hijack another user's empty-mention waiter. Switch the fallback to "<unknown>": angle brackets cannot appear in any real platform sender id, so an unknown sender can never share a session key with a real member. The sentinel value is locked by the unit tests. Review items AstrBotDevs#4 (wake-prefix path) and AstrBotDevs#6 (':' separator) are confirmed already covered; AstrBotDevs#2/AstrBotDevs#3 are related issues from AstrBotDevs#9377 outside this PR's minimal scope.
|
@xiaoyuyu6420 感谢详细审查与自我更正。已逐项处理并推送: 1. 测试 → 已补齐新增
5. 哨兵碰撞 → 已修复采纳你的建议:哨兵从 4. wake-prefix 路径 → 确认已覆盖(与你的更正一致)独立核验: 6.
|
Summary
Fixes #9377
Problem
When a user sent an empty @mention (or wake-prefix-only message) in a group chat, the
empty_mention_waiterusedDefaultSessionFilterwhich keyed sessions only byevent.unified_msg_origin(the group/conversation ID). This meant any subsequent message from any member in that group would trigger the waiter and be consumed viaevent.stop_event(), causing other members' messages to be silently intercepted.Fix
Added a custom
_SenderSessionFilterthat includesevent.get_sender_id()in the session key:This ensures the waiter only responds to messages from the same sender who initiated the empty mention, matching expected multi-user group chat behavior.
Changes
astrbot/builtin_stars/astrbot/main.py: importedSessionFilter, added a sender-scoped filter forempty_mention_waiter(+6/-1 lines)Summary by Sourcery
Bug Fixes: