fix(muted): 禁言跳过分析功能兼容 snowluma

由于 SnowLuma 的 get_group_member_info 和 get_group_info 本身没有返回禁言字段,禁言检测的唯一路径就是 set_reaction 失败;依赖 SnowLuma/OIDB 操作被拒时返回特定值判断被禁言实现禁言记录并且跳过不必要的分析
This commit is contained in:
SXP-Simon
2026-07-10 19:46:44 +08:00
parent 3979a30787
commit da410b67cd
3 changed files with 39 additions and 20 deletions
+5 -5
View File
@@ -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
@@ -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(
@@ -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=1200INTERNAL_ERROR
# SnowLuma/OIDB 操作被拒时返回 retcode=100ACTION_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):