feat:初版

This commit is contained in:
2025-12-03 15:48:44 +08:00
commit b4df26f61d
199 changed files with 23434 additions and 0 deletions

198
utils/hookbot.py Normal file
View File

@@ -0,0 +1,198 @@
"""
HookBot - 机器人核心类
处理消息路由和事件分发
"""
import tomllib
from typing import Dict, Any
from loguru import logger
from WechatHook import WechatHookClient, MESSAGE_TYPE_MAP, normalize_message
from utils.event_manager import EventManager
class HookBot:
"""
HookBot 核心类
负责消息处理、路由和事件分发
"""
def __init__(self, client: WechatHookClient):
"""
初始化 HookBot
Args:
client: WechatHookClient 实例
"""
self.client = client
self.wxid = None
self.nickname = None
# 读取配置
with open("main_config.toml", "rb") as f:
main_config = tomllib.load(f)
bot_config = main_config.get("Bot", {})
preset_wxid = bot_config.get("wxid") or bot_config.get("bot_wxid")
preset_nickname = bot_config.get("nickname") or bot_config.get("bot_nickname")
if preset_wxid:
self.wxid = preset_wxid
logger.info(f"使用配置中的机器人 wxid: {self.wxid}")
if preset_nickname:
self.nickname = preset_nickname
logger.info(f"使用配置中的机器人昵称: {self.nickname}")
self.ignore_mode = bot_config.get("ignore-mode", "None")
self.whitelist = bot_config.get("whitelist", [])
self.blacklist = bot_config.get("blacklist", [])
# 性能配置
perf_config = main_config.get("Performance", {})
self.log_sampling_rate = perf_config.get("log_sampling_rate", 1.0)
# 消息计数和统计
self.message_count = 0
self.filtered_count = 0
self.processed_count = 0
logger.info("HookBot 初始化完成")
def update_profile(self, wxid: str, nickname: str):
"""
更新机器人信息
Args:
wxid: 机器人 wxid
nickname: 机器人昵称
"""
self.wxid = wxid
self.nickname = nickname
logger.info(f"机器人信息: wxid={wxid}, nickname={nickname}")
async def process_message(self, msg_type: int, data: dict):
"""
处理接收到的消息
Args:
msg_type: 消息类型
data: 消息数据
"""
# 过滤 API 响应消息
if msg_type in [11174, 11230]:
return
# 消息计数
self.message_count += 1
# 日志采样 - 只记录部分消息以减少日志量
should_log = self._should_log_message(msg_type)
if should_log:
logger.debug(f"处理消息: type={msg_type}")
# 重要事件始终记录
if msg_type in [11098, 11099, 11058]: # 群成员变动、系统消息
logger.info(f"重要事件: type={msg_type}")
# 获取事件类型
event_type = MESSAGE_TYPE_MAP.get(msg_type)
if should_log and event_type:
logger.info(f"[HookBot] 消息类型映射: {msg_type} -> {event_type}")
if not event_type:
# 记录未知消息类型的详细信息,帮助调试
content_preview = str(data.get('raw_msg', data.get('msg', '')))[:200]
logger.warning(f"未映射的消息类型: {msg_type}, wx_type: {data.get('wx_type')}, 内容预览: {content_preview}")
return
# 格式转换
try:
message = normalize_message(msg_type, data)
except Exception as e:
logger.error(f"格式转换失败: {e}")
return
# 过滤消息
if not self._check_filter(message):
self.filtered_count += 1
if should_log:
logger.debug(f"消息被过滤: {message.get('FromWxid')}")
return
self.processed_count += 1
# 采样记录处理的消息
if should_log:
content = message.get('Content', '')
if len(content) > 50:
content = content[:50] + "..."
logger.info(f"处理消息: type={event_type}, from={message.get('FromWxid')}, content={content}")
# 触发事件
try:
await EventManager.emit(event_type, self.client, message)
except Exception as e:
logger.error(f"事件处理失败: {e}")
def _should_log_message(self, msg_type: int) -> bool:
"""判断是否应该记录此消息的日志"""
# 重要消息类型始终记录
important_types = {
11058, 11098, 11099, 11025, # 系统消息、群成员变动、登录信息
11051, 11047, 11052, 11055 # 视频、图片、表情、文件消息
}
if msg_type in important_types:
return True
# 其他消息按采样率记录
import random
return random.random() < self.log_sampling_rate
def _check_filter(self, message: Dict[str, Any]) -> bool:
"""
检查消息是否通过过滤
Args:
message: 消息字典
Returns:
是否通过过滤
"""
from_wxid = message.get("FromWxid", "")
sender_wxid = message.get("SenderWxid", "")
msg_type = message.get("MsgType", 0)
# 系统消息type=11058不过滤因为包含重要的群聊事件信息
if msg_type == 11058:
return True
# 过滤机器人自己发送的消息,避免无限循环
if self.wxid and (from_wxid == self.wxid or sender_wxid == self.wxid):
return False
# None 模式:处理所有消息
if self.ignore_mode == "None":
return True
# Whitelist 模式:仅处理白名单
if self.ignore_mode == "Whitelist":
return from_wxid in self.whitelist or sender_wxid in self.whitelist
# Blacklist 模式:屏蔽黑名单
if self.ignore_mode == "Blacklist":
return from_wxid not in self.blacklist and sender_wxid not in self.blacklist
return True
def get_stats(self) -> dict:
"""获取消息处理统计信息"""
return {
"total_messages": self.message_count,
"filtered_messages": self.filtered_count,
"processed_messages": self.processed_count,
"filter_rate": self.filtered_count / max(self.message_count, 1),
"process_rate": self.processed_count / max(self.message_count, 1)
}