Skip to content

feat: Enhance context handling and notifications for sensitive messages#2

Open
rurudo-ruo wants to merge 19 commits into
MAA1999:mainfrom
rurudo-ruo:main
Open

feat: Enhance context handling and notifications for sensitive messages#2
rurudo-ruo wants to merge 19 commits into
MAA1999:mainfrom
rurudo-ruo:main

Conversation

@rurudo-ruo

@rurudo-ruo rurudo-ruo commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Summary by Sourcery

增强敏感消息监控插件,对可配置的上下文处理进行丰富升级,提升 LLM 判定结果解析的健壮性,并支持按群组设置通知冷却时间,以减少通知噪音。

新功能:

  • 支持按群组配置在敏感消息通知中是否追加最近会话上下文,并可限制包含的消息数量和总字数。
  • 引入按群组配置的通知冷却时间,用于抑制同一群组中短时间内的敏感消息通知爆发。
  • 新增管理员指令,用于在群组内配置通知冷却时长。

功能增强:

  • 从结构化消息片段构建更干净的消息文本和上下文,跳过不支持的预览和回复内容,并在上下文中对之前已标记为敏感的消息进行掩码处理。
  • 使用拼接后的消息文本替代 raw_message,并将消息 ID 规范化为字符串,用于近期处理的跟踪。
  • 通过使上下文描述更清晰,并更健壮地解析模型的“是/否”回答(同时截断存储的原因说明),来改进 LLM 的敏感内容判定。
  • 扩展状态指令,报告新的与上下文和冷却时间相关的配置选项。
Original summary in English

Summary by Sourcery

Enhance the sensitive message monitoring plugin with richer configurable context handling, more robust LLM judgment parsing, and per-group notification cooldowns to reduce noise.

New Features:

  • Allow per-group configuration of whether to append recent conversation context to sensitive message notifications, with limits on included message count and total characters.
  • Introduce a configurable per-group notification cooldown to suppress bursts of sensitive alerts in the same group.
  • Add an admin command to configure the notification cooldown duration in groups.

Enhancements:

  • Build cleaner message text and context from structured message segments, skipping unsupported previews and replies, and masking previously flagged sensitive messages in context.
  • Use concatenated message text instead of raw_message and normalize message IDs to strings for recent-processing tracking.
  • Improve LLM sensitive-content judgment by making the context description clearer and by more robustly parsing the model’s yes/no answer while truncating stored reasons.
  • Extend the status command to report new context and cooldown-related configuration options.

rurudo-ruo and others added 9 commits May 19, 2026 20:26
…isplay

Decouple context-for-LLM from append_context config flag so the LLM
always receives conversation context for sensitivity judgment regardless
of whether notifications include it. Move truncation logic into _notify
where display formatting belongs.
Use event.message (MessageArray) instead of event.raw_message to avoid
passing CQ code strings like [CQ:image,...] to the LLM and notifications.
Non-text segments are replaced with summaries ([图片], [文件], [语音] etc.)
and pure attachment messages are skipped entirely.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - 我发现了 1 个问题,并给出了一些整体建议:

  • 在将 group_id 作为 LAST_NOTIFY_TIME 的键使用时,建议统一其类型(例如像 config 和 RECENT_* 中那样始终使用 str(group_id)),以避免由于同一个 ID 使用了不同类型而产生多个独立条目的隐性 bug。
  • LAST_NOTIFY_TIME 是一个按群组分组的、未做上界限制的全局字典;如果这个插件会在很多群里长期运行,建议定期清理非常久远的条目(例如在清空 RECENT_* 时或通过定时任务),以避免无界增长。
给 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
- Consider normalizing the type of `group_id` when using it as a key in `LAST_NOTIFY_TIME` (e.g., always `str(group_id)` as you do for config and `RECENT_*`) to avoid subtle bugs where different types for the same ID create separate entries.
- `LAST_NOTIFY_TIME` is an unbounded global dict keyed by group; if this plugin runs in many groups over time, you may want to periodically prune very old entries (e.g., when clearing `RECENT_*` or on a timer) to avoid unbounded growth.

## Individual Comments

### Comment 1
<location path="plugins/_ai/llm.py" line_range="258-267" />
<code_context>
-        is_sensitive = reply.strip().startswith("是")
-        reason = reply.strip()
-        return is_sensitive, reason
+        cleaned = reply.strip()
+        # Match the first 「是」/「否」 or bare 是/否 at the start
+        m = re.search(r'[「((]?\s*([是否])\s*[」))]?', cleaned)
+        if m:
+            is_sensitive = m.group(1) == "是"
+        else:
+            # Fallback: check first non-whitespace character
+            first_char = cleaned.lstrip()[:1] if cleaned else ""
+            is_sensitive = first_char == "是"
+        return is_sensitive, cleaned[:200]

     async def judge_question(self, project: str, message_text: str, context: str = "") -> bool:
</code_context>
<issue_to_address>
**issue (bug_risk):** 用于抽取开头「是/否」的正则没有锚定到字符串开头,可能会被后面的文本误导而产生错误分类。

由于这里使用的是 `re.search`,它可能会匹配到后面位置的「是/否」,而不是真正的开头回答,这与“匹配开头”这一意图相违背。比如:`"否,你这条不是敏感消息,但前面那条是敏感内容"` 会因为后面的「是」而被判断为敏感。为了只匹配最前面的回答,应当锚定正则并使用 `re.match`,例如 `m = re.match(r'^\s*[「((]?\s*([是否])\s*[」))]?', cleaned)`,只有在这个匹配失败时再回退到“检查第一个非空白字符”的启发式逻辑。
</issue_to_address>

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

Hey - I've found 1 issue, and left some high level feedback:

  • Consider normalizing the type of group_id when using it as a key in LAST_NOTIFY_TIME (e.g., always str(group_id) as you do for config and RECENT_*) to avoid subtle bugs where different types for the same ID create separate entries.
  • LAST_NOTIFY_TIME is an unbounded global dict keyed by group; if this plugin runs in many groups over time, you may want to periodically prune very old entries (e.g., when clearing RECENT_* or on a timer) to avoid unbounded growth.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider normalizing the type of `group_id` when using it as a key in `LAST_NOTIFY_TIME` (e.g., always `str(group_id)` as you do for config and `RECENT_*`) to avoid subtle bugs where different types for the same ID create separate entries.
- `LAST_NOTIFY_TIME` is an unbounded global dict keyed by group; if this plugin runs in many groups over time, you may want to periodically prune very old entries (e.g., when clearing `RECENT_*` or on a timer) to avoid unbounded growth.

## Individual Comments

### Comment 1
<location path="plugins/_ai/llm.py" line_range="258-267" />
<code_context>
-        is_sensitive = reply.strip().startswith("是")
-        reason = reply.strip()
-        return is_sensitive, reason
+        cleaned = reply.strip()
+        # Match the first 「是」/「否」 or bare 是/否 at the start
+        m = re.search(r'[「((]?\s*([是否])\s*[」))]?', cleaned)
+        if m:
+            is_sensitive = m.group(1) == "是"
+        else:
+            # Fallback: check first non-whitespace character
+            first_char = cleaned.lstrip()[:1] if cleaned else ""
+            is_sensitive = first_char == "是"
+        return is_sensitive, cleaned[:200]

     async def judge_question(self, project: str, message_text: str, context: str = "") -> bool:
</code_context>
<issue_to_address>
**issue (bug_risk):** The regex for extracting the leading 「是/否」 is not anchored and can misclassify based on later text.

Because this uses `re.search`, it may pick up a later 「是/否」 instead of the leading answer, contradicting the intent of matching “at the start”. For example, `"否,你这条不是敏感消息,但前面那条是敏感内容"` would be treated as sensitive due to the later "是". To match only the leading answer, anchor the regex and use `re.match`, e.g. `m = re.match(r'^\s*[「((]?\s*([是否])\s*[」))]?', cleaned)`, and fall back to the first-character heuristic only if this fails.
</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 thread plugins/_ai/llm.py Outdated
rurudo-ruo added 10 commits June 5, 2026 00:37
- Implemented Skland sign-in functionality with account management.
- Added commands for immediate sign-in, status check, and configuration management.
- Integrated SMS login with verification code handling.
- Included QR login placeholder with a warning about current limitations.
- Enhanced error handling and notification for authentication failures.
- Updated project dependencies in pyproject.toml.
- Refactored sensitive monitor plugin to prune stale notification cooldown entries.
- Cleaned up unused imports and improved code readability in various plugins.
…n and notification features

- Added SensitiveMonitorPlugin to monitor and handle sensitive messages in group chats.
- Implemented configuration loading and saving for group-specific settings.
- Introduced commands for enabling/disabling monitoring, managing notification recipients, and setting cooldowns.
- Integrated LLM for sensitive content detection with timeout handling.
- Enhanced message processing to build clean message context for better detection accuracy.
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.

2 participants