Files
Mofox-Core/src/chat/chat_loop/heartFC_chat.py
雅诺狐 f308adcf5b refactor: 清理代码质量和移除未使用文件
- 移除未使用的导入语句和变量
- 修复代码风格问题(空格、格式化等)
- 删除备份文件和测试文件
- 改进异常处理链式调用
- 添加权限系统数据库模型和配置
- 更新版本号至6.4.4
- 优化SQL查询使用正确的布尔表达式
2025-08-24 22:11:20 +08:00

338 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import asyncio
import time
import traceback
from typing import Optional
from src.common.logger import get_logger
from src.config.config import global_config
from src.person_info.relationship_builder_manager import relationship_builder_manager
from src.chat.express.expression_learner import expression_learner_manager
from src.plugin_system.base.component_types import ChatMode
from src.manager.schedule_manager import schedule_manager
from src.plugin_system.apis import message_api
from .hfc_context import HfcContext
from .energy_manager import EnergyManager
from .proactive_thinker import ProactiveThinker
from .cycle_processor import CycleProcessor
from .response_handler import ResponseHandler
from .normal_mode_handler import NormalModeHandler
from .cycle_tracker import CycleTracker
from .wakeup_manager import WakeUpManager
logger = get_logger("hfc")
class HeartFChatting:
def __init__(self, chat_id: str):
"""
初始化心跳聊天管理器
Args:
chat_id: 聊天ID标识符
功能说明:
- 创建聊天上下文和所有子管理器
- 初始化循环跟踪器、响应处理器、循环处理器等核心组件
- 设置能量管理器、主动思考器和普通模式处理器
- 初始化聊天模式并记录初始化完成日志
"""
self.context = HfcContext(chat_id)
self.cycle_tracker = CycleTracker(self.context)
self.response_handler = ResponseHandler(self.context)
self.cycle_processor = CycleProcessor(self.context, self.response_handler, self.cycle_tracker)
self.energy_manager = EnergyManager(self.context)
self.proactive_thinker = ProactiveThinker(self.context, self.cycle_processor)
self.normal_mode_handler = NormalModeHandler(self.context, self.cycle_processor)
self.wakeup_manager = WakeUpManager(self.context)
# 将唤醒度管理器设置到上下文中
self.context.wakeup_manager = self.wakeup_manager
self._loop_task: Optional[asyncio.Task] = None
self._initialize_chat_mode()
logger.info(f"{self.context.log_prefix} HeartFChatting 初始化完成")
def _initialize_chat_mode(self):
"""
初始化聊天模式
功能说明:
- 检测是否为群聊环境
- 根据全局配置设置强制聊天模式
- 在focus模式下设置能量值为35
- 在normal模式下设置能量值为15
- 如果是auto模式则保持默认设置
"""
is_group_chat = self.context.chat_stream.group_info is not None if self.context.chat_stream else False
if is_group_chat and global_config.chat.group_chat_mode != "auto":
if global_config.chat.group_chat_mode == "focus":
self.context.loop_mode = ChatMode.FOCUS
self.context.energy_value = 35
elif global_config.chat.group_chat_mode == "normal":
self.context.loop_mode = ChatMode.NORMAL
self.context.energy_value = 15
async def start(self):
"""
启动心跳聊天系统
功能说明:
- 检查是否已经在运行,避免重复启动
- 初始化关系构建器和表达学习器
- 启动能量管理器和主动思考器
- 创建主聊天循环任务并设置完成回调
- 记录启动完成日志
"""
if self.context.running:
return
self.context.running = True
self.context.relationship_builder = relationship_builder_manager.get_or_create_builder(self.context.stream_id)
self.context.expression_learner = expression_learner_manager.get_expression_learner(self.context.stream_id)
await self.energy_manager.start()
await self.proactive_thinker.start()
await self.wakeup_manager.start()
self._loop_task = asyncio.create_task(self._main_chat_loop())
self._loop_task.add_done_callback(self._handle_loop_completion)
logger.info(f"{self.context.log_prefix} HeartFChatting 启动完成")
async def stop(self):
"""
停止心跳聊天系统
功能说明:
- 检查是否正在运行,避免重复停止
- 设置运行状态为False
- 停止能量管理器和主动思考器
- 取消主聊天循环任务
- 记录停止完成日志
"""
if not self.context.running:
return
self.context.running = False
await self.energy_manager.stop()
await self.proactive_thinker.stop()
await self.wakeup_manager.stop()
if self._loop_task and not self._loop_task.done():
self._loop_task.cancel()
await asyncio.sleep(0)
logger.info(f"{self.context.log_prefix} HeartFChatting 已停止")
def _handle_loop_completion(self, task: asyncio.Task):
"""
处理主循环任务完成
Args:
task: 完成的异步任务对象
功能说明:
- 处理任务异常完成的情况
- 区分正常停止和异常终止
- 记录相应的日志信息
- 处理取消任务的情况
"""
try:
if exception := task.exception():
logger.error(f"{self.context.log_prefix} HeartFChatting: 脱离了聊天(异常): {exception}")
logger.error(traceback.format_exc())
else:
logger.info(f"{self.context.log_prefix} HeartFChatting: 脱离了聊天 (外部停止)")
except asyncio.CancelledError:
logger.info(f"{self.context.log_prefix} HeartFChatting: 结束了聊天")
async def _main_chat_loop(self):
"""
主聊天循环
功能说明:
- 持续运行聊天处理循环
- 只有在有新消息时才进行思考循环
- 无新消息时等待新消息到达(由主动思考系统单独处理主动发言)
- 处理取消和异常情况
- 在异常时尝试重新启动循环
"""
try:
while self.context.running:
has_new_messages = await self._loop_body()
if has_new_messages:
# 有新消息时,继续快速检查是否还有更多消息
await asyncio.sleep(1)
else:
# 无新消息时,等待较长时间再检查
# 这里只是为了定期检查系统状态,不进行思考循环
# 真正的新消息响应依赖于消息到达时的通知
await asyncio.sleep(1.0)
except asyncio.CancelledError:
logger.info(f"{self.context.log_prefix} 麦麦已关闭聊天")
except Exception:
logger.error(f"{self.context.log_prefix} 麦麦聊天意外错误将于3s后尝试重新启动")
print(traceback.format_exc())
await asyncio.sleep(3)
self._loop_task = asyncio.create_task(self._main_chat_loop())
logger.error(f"{self.context.log_prefix} 结束了当前聊天循环")
async def _loop_body(self) -> bool:
"""
单次循环体处理
Returns:
bool: 是否处理了新消息
功能说明:
- 检查是否处于睡眠模式,如果是则处理唤醒度逻辑
- 获取最近的新消息(过滤机器人自己的消息和命令)
- 只有在有新消息时才进行思考循环处理
- 更新最后消息时间和读取时间
- 根据当前聊天模式执行不同的处理逻辑
- FOCUS模式直接处理所有消息并检查退出条件
- NORMAL模式检查进入FOCUS模式的条件并通过normal_mode_handler处理消息
"""
is_sleeping = schedule_manager.is_sleeping(self.wakeup_manager)
# 核心修复:在睡眠模式下获取消息时,不过滤命令消息,以确保@消息能被接收
filter_command_flag = not is_sleeping
recent_messages = message_api.get_messages_by_time_in_chat(
chat_id=self.context.stream_id,
start_time=self.context.last_read_time,
end_time=time.time(),
limit=10,
limit_mode="latest",
filter_mai=True,
filter_command=filter_command_flag,
)
has_new_messages = bool(recent_messages)
# 只有在有新消息时才进行思考循环处理
if has_new_messages:
self.context.last_message_time = time.time()
self.context.last_read_time = time.time()
# 处理唤醒度逻辑
if is_sleeping:
self._handle_wakeup_messages(recent_messages)
# 如果仍在睡眠状态,跳过正常处理但仍返回有新消息
if schedule_manager.is_sleeping(self.wakeup_manager):
return has_new_messages
# 根据聊天模式处理新消息
if self.context.loop_mode == ChatMode.FOCUS:
for message in recent_messages:
await self.cycle_processor.observe(message)
self._check_focus_exit()
elif self.context.loop_mode == ChatMode.NORMAL:
self._check_focus_entry(len(recent_messages))
for message in recent_messages:
await self.normal_mode_handler.handle_message(message)
else:
# 无新消息时,只进行模式检查,不进行思考循环
if self.context.loop_mode == ChatMode.FOCUS:
self._check_focus_exit()
elif self.context.loop_mode == ChatMode.NORMAL:
self._check_focus_entry(0) # 传入0表示无新消息
return has_new_messages
def _check_focus_exit(self):
"""
检查是否应该退出FOCUS模式
功能说明:
- 区分私聊和群聊环境
- 在强制私聊focus模式下能量值低于1时重置为5但不退出
- 在群聊focus模式下如果配置为focus则不退出
- 其他情况下能量值低于1时退出到NORMAL模式
"""
is_private_chat = self.context.chat_stream.group_info is None if self.context.chat_stream else False
is_group_chat = not is_private_chat
if global_config.chat.force_focus_private and is_private_chat:
if self.context.energy_value <= 1:
self.context.energy_value = 5
return
if is_group_chat and global_config.chat.group_chat_mode == "focus":
return
if self.context.energy_value <= 1: # 如果能量值小于等于1非强制情况
self.context.energy_value = 1 # 将能量值设置为1
self.context.loop_mode = ChatMode.NORMAL # 切换到普通模式
def _check_focus_entry(self, new_message_count: int):
"""
检查是否应该进入FOCUS模式
Args:
new_message_count: 新消息数量
功能说明:
- 区分私聊和群聊环境
- 强制私聊focus模式直接进入FOCUS模式并设置能量值为10
- 群聊normal模式不进入FOCUS模式
- 根据focus_value配置和消息数量决定是否进入FOCUS模式
- 当消息数量超过阈值或能量值达到30时进入FOCUS模式
"""
is_private_chat = self.context.chat_stream.group_info is None if self.context.chat_stream else False
is_group_chat = not is_private_chat
if global_config.chat.force_focus_private and is_private_chat:
self.context.loop_mode = ChatMode.FOCUS
self.context.energy_value = 10
return
if is_group_chat and global_config.chat.group_chat_mode == "normal":
return
if global_config.chat.focus_value != 0: # 如果专注值配置不为0启用自动专注
if new_message_count > 3 / pow(global_config.chat.focus_value, 0.5): # 如果新消息数超过阈值(基于专注值计算)
self.context.loop_mode = ChatMode.FOCUS # 进入专注模式
self.context.energy_value = 10 + (new_message_count / (3 / pow(global_config.chat.focus_value, 0.5))) * 10 # 根据消息数量计算能量值
return # 返回,不再检查其他条件
if self.context.energy_value >= 30: # 如果能量值达到或超过30
self.context.loop_mode = ChatMode.FOCUS # 进入专注模式
def _handle_wakeup_messages(self, messages):
"""
处理休眠状态下的消息,累积唤醒度
Args:
messages: 消息列表
功能说明:
- 区分私聊和群聊消息
- 检查群聊消息是否艾特了机器人
- 调用唤醒度管理器累积唤醒度
- 如果达到阈值则唤醒并进入愤怒状态
"""
if not self.wakeup_manager:
return
is_private_chat = self.context.chat_stream.group_info is None if self.context.chat_stream else False
for message in messages:
is_mentioned = False
# 检查群聊消息是否艾特了机器人
if not is_private_chat:
# 最终修复:直接使用消息对象中由上游处理好的 is_mention 字段。
# 该字段在 message.py 的 MessageRecv._process_single_segment 中被设置。
if message.get("is_mentioned"):
is_mentioned = True
# 累积唤醒度
woke_up = self.wakeup_manager.add_wakeup_value(is_private_chat, is_mentioned)
if woke_up:
logger.info(f"{self.context.log_prefix} 被消息吵醒,进入愤怒状态!")
break