feat:识别群昵称
This commit is contained in:
@@ -4,7 +4,9 @@ HookBot - 机器人核心类
|
||||
处理消息路由和事件分发
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import tomllib
|
||||
import time
|
||||
from typing import Dict, Any
|
||||
from loguru import logger
|
||||
|
||||
@@ -52,6 +54,12 @@ class HookBot:
|
||||
perf_config = main_config.get("Performance", {})
|
||||
self.log_sampling_rate = perf_config.get("log_sampling_rate", 1.0)
|
||||
|
||||
# 消息去重(部分环境会重复回调同一条消息,导致插件回复两次)
|
||||
self._dedup_ttl_seconds = perf_config.get("dedup_ttl_seconds", 30)
|
||||
self._dedup_max_size = perf_config.get("dedup_max_size", 5000)
|
||||
self._dedup_lock = asyncio.Lock()
|
||||
self._recent_message_keys: Dict[str, float] = {}
|
||||
|
||||
# 消息计数和统计
|
||||
self.message_count = 0
|
||||
self.filtered_count = 0
|
||||
@@ -59,6 +67,54 @@ class HookBot:
|
||||
|
||||
logger.info("HookBot 初始化完成")
|
||||
|
||||
def _extract_msg_id(self, data: Dict[str, Any]) -> str:
|
||||
"""从原始回调数据中提取消息ID(用于去重)"""
|
||||
for k in ("msgid", "msg_id", "MsgId", "id"):
|
||||
v = data.get(k)
|
||||
if v:
|
||||
return str(v)
|
||||
return ""
|
||||
|
||||
async def _is_duplicate_message(self, msg_type: int, data: Dict[str, Any]) -> bool:
|
||||
"""判断该条消息是否为短时间内重复回调。"""
|
||||
msg_id = self._extract_msg_id(data)
|
||||
if not msg_id:
|
||||
# 没有稳定 msgid 时不做去重,避免误伤(同一秒内同内容可能是用户真实重复发送)
|
||||
return False
|
||||
|
||||
key = f"msgid:{msg_id}"
|
||||
|
||||
now = time.time()
|
||||
ttl = max(float(self._dedup_ttl_seconds or 0), 0.0)
|
||||
if ttl <= 0:
|
||||
return False
|
||||
|
||||
async with self._dedup_lock:
|
||||
last_seen = self._recent_message_keys.get(key)
|
||||
if last_seen is not None and (now - last_seen) < ttl:
|
||||
return True
|
||||
|
||||
# 记录/刷新
|
||||
self._recent_message_keys.pop(key, None)
|
||||
self._recent_message_keys[key] = now
|
||||
|
||||
# 清理过期 key(按插入顺序从旧到新)
|
||||
cutoff = now - ttl
|
||||
while self._recent_message_keys:
|
||||
first_key = next(iter(self._recent_message_keys))
|
||||
if self._recent_message_keys.get(first_key, now) >= cutoff:
|
||||
break
|
||||
self._recent_message_keys.pop(first_key, None)
|
||||
|
||||
# 限制大小,避免长期运行内存增长
|
||||
max_size = int(self._dedup_max_size or 0)
|
||||
if max_size > 0:
|
||||
while len(self._recent_message_keys) > max_size and self._recent_message_keys:
|
||||
first_key = next(iter(self._recent_message_keys))
|
||||
self._recent_message_keys.pop(first_key, None)
|
||||
|
||||
return False
|
||||
|
||||
def update_profile(self, wxid: str, nickname: str):
|
||||
"""
|
||||
更新机器人信息
|
||||
@@ -80,9 +136,20 @@ class HookBot:
|
||||
data: 消息数据
|
||||
"""
|
||||
# 过滤 API 响应消息
|
||||
if msg_type in [11174, 11230]:
|
||||
# - 11032: 获取群成员信息响应
|
||||
# - 11174/11230: 协议/上传等 API 回调
|
||||
if msg_type in [11032, 11174, 11230]:
|
||||
return
|
||||
|
||||
# 去重:同一条消息重复回调时不再重复触发事件(避免“同一句话回复两次”)
|
||||
try:
|
||||
if await self._is_duplicate_message(msg_type, data):
|
||||
logger.debug(f"[HookBot] 重复消息已丢弃: type={msg_type}, msgid={self._extract_msg_id(data) or 'N/A'}")
|
||||
return
|
||||
except Exception as e:
|
||||
# 去重失败不影响主流程
|
||||
logger.debug(f"[HookBot] 消息去重检查失败: {e}")
|
||||
|
||||
# 消息计数
|
||||
self.message_count += 1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user