feat: QQ昵称剥离功能增强,支持【头衔】用户名格式

- 新增QQ群头衔+用户名格式剥离:【头衔】用户名 或 [头衔] 用户名
- 处理OCR将【识别为[、】识别为]的情况
- 保持原有纯文本昵称剥离功能
- 测试用例全部通过
This commit is contained in:
youpaishidifu
2026-08-09 16:34:54 +08:00
parent eef12a2694
commit 3cd760699d
2 changed files with 63 additions and 0 deletions
+1
View File
@@ -14,6 +14,7 @@ DEFAULT_CONFIG = {
"qq_enabled": True,
"auto_reply_enabled": False,
"auto_reply_min_score": 0.35,
"qq_nicknames": [], # 需要从消息文本开头剥离的QQ昵称列表
}
+62
View File
@@ -3155,6 +3155,8 @@ class MessageMonitor:
# 永久丢失(bot 回复一次后再也不回复)。剥离后保留用户提问。
msg = self._strip_bot_reply_fragments(msg)
if msg:
# ⭐ 剥离QQ用户昵称,防止昵称拼凑进消息文本导致查库关键词被压住
msg = self._strip_qq_nickname(msg)
if self._is_reply_fragment(msg):
logger.info(f"[ocr] stage[{i}] [filter4/回复残片] DROP 回复残片跳过: {msg[:60]!r}")
else:
@@ -3199,6 +3201,8 @@ class MessageMonitor:
# ⭐ 同上:尾部消息也要先剥离 bot 旧回复片段,避免整段误判为残片
msg = self._strip_bot_reply_fragments(msg)
if msg:
# ⭐ 剥离QQ用户昵称,防止昵称拼凑进消息文本导致查库关键词被压住
msg = self._strip_qq_nickname(msg)
if self._is_reply_fragment(msg):
logger.info(f"[ocr] stage[{i}] [filter4/回复残片] DROP 回复残片跳过(尾部): {msg[:60]!r}")
else:
@@ -4311,6 +4315,64 @@ class MessageMonitor:
ts = re.sub(r'\s+', ' ', ts).strip().strip(',').strip()
return ts
def _strip_qq_nickname(self, text: str) -> str:
"""从消息文本开头剥离QQ用户昵称,防止昵称拼凑进消息文本导致查库关键词被压住。
QQ群聊截图中,OCR会把用户昵称和消息文本拼在一起,如:
"草原探险服务器备用号游戏专用在哪下载登陆插件"
其中"草原探险服务器备用号"和"游戏专用"是用户昵称,"在哪下载登陆插件"是实际消息。
策略:
1. 检查消息文本是否以已知昵称列表中的某个昵称开头
2. 如果是,剥离该昵称(可能包括后续的空格/标点)
3. 重复步骤1-2,直到没有匹配的昵称
4. 如果消息文本以"//"或"=>"开头,这些可能是OCR识别的箭头,一并剥离
"""
if not text or not text.strip():
return text
ts = text.strip()
# 剥离箭头前缀(OCR可能识别为"//"或"=>")
if ts.startswith('//') or ts.startswith('=>'):
ts = ts[2:].lstrip()
# ⭐ 剥离QQ群头衔+用户名格式:【头衔】用户名 或 [头衔] 用户名
# OCR可能把【识别为[,把】识别为],所以同时处理两种括号
# 匹配模式:[【\[]任意内容】\]] + 可选空格 + 用户名(中文/英文/数字/下划线)
qq_title_pattern = re.compile(r'^[\[【][^】\]]+[】\]]\s*[\w\u4e00-\u9fff]+[,,]?\s*')
while True:
m = qq_title_pattern.match(ts)
if m:
ts = ts[m.end():].lstrip()
else:
break
# 加载配置中的昵称列表
try:
from config import load_config
cfg = load_config()
nicknames = cfg.get('qq_nicknames', [])
except Exception:
nicknames = []
# 如果没有配置昵称,使用默认的已知昵称(从日志中提取)
if not nicknames:
nicknames = [
"草原探险服务器备用号",
"游戏专用",
# 可以添加更多已知昵称
]
# 逐个尝试剥离昵称
changed = True
while changed:
changed = False
for nick in nicknames:
if ts.startswith(nick):
ts = ts[len(nick):].lstrip()
changed = True
break
# 清理残留的标点/空格
ts = re.sub(r'^[\s,,.。::;;!!??]+', '', ts)
return ts.strip()
def _is_reply_fragment(self, text: str) -> bool:
"""检查文本是否为 bot 回复内容的碎片(OCR 截取的 md 残片/跨行碎片)。