From da410b67cd5bc24b4a05c0b1afb8d2ca9c3b583e Mon Sep 17 00:00:00 2001 From: SXP-Simon Date: Fri, 10 Jul 2026 19:46:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(muted):=20=E7=A6=81=E8=A8=80=E8=B7=B3?= =?UTF-8?q?=E8=BF=87=E5=88=86=E6=9E=90=E5=8A=9F=E8=83=BD=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=20snowluma?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由于 SnowLuma 的 get_group_member_info 和 get_group_info 本身没有返回禁言字段,禁言检测的唯一路径就是 set_reaction 失败;依赖 SnowLuma/OIDB 操作被拒时返回特定值判断被禁言实现禁言记录并且跳过不必要的分析 --- .gitattributes | 10 ++-- .../analysis/analyzers/base_analyzer.py | 2 +- .../platform/adapters/onebot_adapter.py | 47 +++++++++++++------ 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/.gitattributes b/.gitattributes index 1a67759..857cc7d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,15 +1,15 @@ -### Demo preview images — not needed at runtime, kept in repo for reference/docs +### 模板预览图 — 仅用于文档/仓库展示,插件运行时不需要 assets/*.jpg export-ignore assets/*.png export-ignore -### CI/CD workflow files — not needed in production plugin zip +### CI/CD 工作流 — 插件打包时不需要 .github/ export-ignore -### Documentation — design docs, not needed at runtime +### 设计文档 — 运行时不需要 docs/ export-ignore -### Scripts — release utilities, not needed by end users +### 发布/调试脚本 — 终端用户不需要 scripts/ export-ignore -### Test directory — not needed in production +### 测试文件 — 生产包不需要 tests/ export-ignore diff --git a/src/infrastructure/analysis/analyzers/base_analyzer.py b/src/infrastructure/analysis/analyzers/base_analyzer.py index 7036d17..0df3ed1 100644 --- a/src/infrastructure/analysis/analyzers/base_analyzer.py +++ b/src/infrastructure/analysis/analyzers/base_analyzer.py @@ -381,7 +381,7 @@ class BaseAnalyzer(ABC, Generic[TDataObject, TInputData]): # 2. 调用LLM(使用配置的 provider) provider_id_key = self.get_provider_id_key() - # Resolve provider ID once; pass downstream to avoid duplicate resolve logs + # 只 resolve 一次 provider ID,同时传递给温度解析和 LLM 调用,避免重复日志 resolved_provider_id = None if provider_id_key: resolved_provider_id = await get_provider_id_with_fallback( diff --git a/src/infrastructure/platform/adapters/onebot_adapter.py b/src/infrastructure/platform/adapters/onebot_adapter.py index 7dec09a..1fa4406 100644 --- a/src/infrastructure/platform/adapters/onebot_adapter.py +++ b/src/infrastructure/platform/adapters/onebot_adapter.py @@ -923,7 +923,7 @@ class OneBotAdapter(PlatformAdapter): ) if member_info: role = member_info.get("role", "member") - # Cache the role for timeout fallback + # 缓存角色信息,用于 get_group_member_info 超时时降级使用 self._group_role_cache[group_id_str] = (role, time.time()) shut_up_time = member_info.get("shut_up_time", 0) if shut_up_time > 0: @@ -935,7 +935,7 @@ class OneBotAdapter(PlatformAdapter): # 否则认为是相对禁言剩余时间(秒) is_individually_muted = True except asyncio.TimeoutError: - # Fall back to cached role if available (roles rarely change) + # 超时降级:优先使用缓存的角色(角色几乎不会变) cached_role = self._group_role_cache.get(group_id_str) if cached_role: role = cached_role[0] @@ -1008,21 +1008,40 @@ class OneBotAdapter(PlatformAdapter): if not e: return False err_str = str(e) - if "1200" in err_str and ( - "禁言" in err_str or "操作失败" in err_str or "下游群鉴权" in err_str + + mute_keywords = ("禁言", "操作失败", "下游群鉴权") + + # --- 方法一:根据 retcode 检测 --- + # NapCat/LLOneBot 被禁言时返回 retcode=1200(INTERNAL_ERROR) + # SnowLuma/OIDB 操作被拒时返回 retcode=100(ACTION_FAILED)+ wording 含错误描述 + # SnowLuma 发送消息被拒时返回 result=120 + if any(rc in err_str for rc in ("1200", "retcode=100", "result=120")): + if any(kw in err_str for kw in mute_keywords): + return True + + # --- 方法二:检查 exception 的 message/wording 属性 --- + # 适配不同协议端对错误信息的字段命名差异 + for attr in ("message", "wording"): + val = getattr(e, attr, "") or "" + if any(kw in val for kw in mute_keywords): + return True + if "shut up" in val.lower(): + return True + + # --- 方法三:检测 SnowLuma 发消息被拒的特定模式 --- + # SnowLuma 在群内发消息失败时返回: + # retcode=100, wording="send group message rejected: result=120 err=" + err_lower = err_str.lower() + if "rejected" in err_lower and ( + "result=120" in err_lower or "muted" in err_lower ): return True - err_msg = getattr(e, "message", "") or "" - err_word = getattr(e, "wording", "") or "" - if ( - "禁言" in err_msg - or "禁言" in err_word - or "操作失败" in err_msg - or "操作失败" in err_word - or "shut up" in err_msg.lower() - or "shut up" in err_word.lower() - ): + + # --- 方法四:兜底 --- 直接从 err_str 匹配禁言关键词 --- + # 即使 getattr 获取不到 wording 属性,str(e) 本身仍包含关键词文本 + if any(kw in err_str for kw in mute_keywords): return True + return False def _record_mute_status(self, group_id: Any, is_muted: bool):