mirror of https://github.com/Mai-with-u/MaiBot.git
148 lines
6.7 KiB
Python
148 lines
6.7 KiB
Python
import traceback
|
|
from typing import Optional, Dict
|
|
import asyncio
|
|
from asyncio import Lock
|
|
from ...moods.moods import MoodManager
|
|
from ....config.config import global_config
|
|
from ...chat.emoji_manager import emoji_manager
|
|
from .heartFC_generator import ResponseGenerator
|
|
from .messagesender import MessageManager
|
|
from src.heart_flow.heartflow import heartflow
|
|
from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig
|
|
from src.plugins.person_info.relationship_manager import relationship_manager
|
|
from src.do_tool.tool_use import ToolUser
|
|
from .interest import InterestManager
|
|
from src.plugins.chat.chat_stream import chat_manager
|
|
from .pf_chatting import PFChatting
|
|
|
|
# 定义日志配置
|
|
chat_config = LogConfig(
|
|
console_format=CHAT_STYLE_CONFIG["console_format"],
|
|
file_format=CHAT_STYLE_CONFIG["file_format"],
|
|
)
|
|
|
|
logger = get_module_logger("HeartFC_Controller", config=chat_config)
|
|
|
|
# 检测群聊兴趣的间隔时间
|
|
INTEREST_MONITOR_INTERVAL_SECONDS = 1
|
|
|
|
|
|
class HeartFC_Controller:
|
|
_instance = None # For potential singleton access if needed by MessageManager
|
|
|
|
def __init__(self):
|
|
# --- Updated Init ---
|
|
if HeartFC_Controller._instance is not None:
|
|
# Prevent re-initialization if used as a singleton
|
|
return
|
|
self.gpt = ResponseGenerator()
|
|
self.mood_manager = MoodManager.get_instance()
|
|
self.mood_manager.start_mood_update()
|
|
self.tool_user = ToolUser()
|
|
self.interest_manager = InterestManager()
|
|
self._interest_monitor_task: Optional[asyncio.Task] = None
|
|
# --- New PFChatting Management ---
|
|
self.pf_chatting_instances: Dict[str, PFChatting] = {}
|
|
self._pf_chatting_lock = Lock()
|
|
# --- End New PFChatting Management ---
|
|
HeartFC_Controller._instance = self # Register instance
|
|
# --- End Updated Init ---
|
|
# --- Make dependencies accessible for PFChatting ---
|
|
# These are accessed via the passed instance in PFChatting
|
|
self.emoji_manager = emoji_manager
|
|
self.relationship_manager = relationship_manager
|
|
self.global_config = global_config
|
|
self.MessageManager = MessageManager # Pass the class/singleton access
|
|
# --- End dependencies ---
|
|
|
|
# --- Added Class Method for Singleton Access ---
|
|
@classmethod
|
|
def get_instance(cls):
|
|
if cls._instance is None:
|
|
# This might indicate an issue if called before initialization
|
|
logger.warning("HeartFC_Controller get_instance called before initialization.")
|
|
# Optionally, initialize here if a strict singleton pattern is desired
|
|
# cls._instance = cls()
|
|
return cls._instance
|
|
|
|
# --- End Added Class Method ---
|
|
|
|
async def start(self):
|
|
"""启动异步任务,如回复启动器"""
|
|
logger.debug("HeartFC_Controller 正在启动异步任务...")
|
|
self._initialize_monitor_task()
|
|
logger.info("HeartFC_Controller 异步任务启动完成")
|
|
|
|
def _initialize_monitor_task(self):
|
|
"""启动后台兴趣监控任务,可以检查兴趣是否足以开启心流对话"""
|
|
if self._interest_monitor_task is None or self._interest_monitor_task.done():
|
|
try:
|
|
loop = asyncio.get_running_loop()
|
|
self._interest_monitor_task = loop.create_task(self._interest_monitor_loop())
|
|
except RuntimeError:
|
|
logger.error("创建兴趣监控任务失败:没有运行中的事件循环。")
|
|
raise
|
|
else:
|
|
logger.warning("跳过兴趣监控任务创建:任务已存在或正在运行。")
|
|
|
|
# --- Added PFChatting Instance Manager ---
|
|
async def _get_or_create_pf_chatting(self, stream_id: str) -> Optional[PFChatting]:
|
|
"""获取现有PFChatting实例或创建新实例。"""
|
|
async with self._pf_chatting_lock:
|
|
if stream_id not in self.pf_chatting_instances:
|
|
logger.info(f"为流 {stream_id} 创建新的PFChatting实例")
|
|
# 传递 self (HeartFC_Controller 实例) 进行依赖注入
|
|
instance = PFChatting(stream_id, self)
|
|
# 执行异步初始化
|
|
if not await instance._initialize():
|
|
logger.error(f"为流 {stream_id} 初始化PFChatting失败")
|
|
return None
|
|
self.pf_chatting_instances[stream_id] = instance
|
|
return self.pf_chatting_instances[stream_id]
|
|
|
|
# --- End Added PFChatting Instance Manager ---
|
|
|
|
async def _interest_monitor_loop(self):
|
|
"""后台任务,定期检查兴趣度变化并触发回复"""
|
|
logger.info("兴趣监控循环开始...")
|
|
while True:
|
|
await asyncio.sleep(INTEREST_MONITOR_INTERVAL_SECONDS)
|
|
try:
|
|
# 从心流中获取活跃流
|
|
active_stream_ids = list(heartflow.get_all_subheartflows_streams_ids())
|
|
for stream_id in active_stream_ids:
|
|
stream_name = chat_manager.get_stream_name(stream_id) or stream_id # 获取流名称
|
|
sub_hf = heartflow.get_subheartflow(stream_id)
|
|
if not sub_hf:
|
|
logger.warning(f"监控循环: 无法获取活跃流 {stream_name} 的 sub_hf")
|
|
continue
|
|
|
|
should_trigger = False
|
|
try:
|
|
interest_chatting = self.interest_manager.get_interest_chatting(stream_id)
|
|
if interest_chatting:
|
|
should_trigger = interest_chatting.should_evaluate_reply()
|
|
else:
|
|
logger.trace(
|
|
f"[{stream_name}] 没有找到对应的 InterestChatting 实例,跳过基于兴趣的触发检查。"
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"检查兴趣触发器时出错 流 {stream_name}: {e}")
|
|
logger.error(traceback.format_exc())
|
|
|
|
if should_trigger:
|
|
# 启动一次麦麦聊天
|
|
pf_instance = await self._get_or_create_pf_chatting(stream_id)
|
|
if pf_instance:
|
|
asyncio.create_task(pf_instance.add_time())
|
|
else:
|
|
logger.error(f"[{stream_name}] 无法获取或创建PFChatting实例。跳过触发。")
|
|
|
|
except asyncio.CancelledError:
|
|
logger.info("兴趣监控循环已取消。")
|
|
break
|
|
except Exception as e:
|
|
logger.error(f"兴趣监控循环错误: {e}")
|
|
logger.error(traceback.format_exc())
|
|
await asyncio.sleep(5) # 发生错误时等待
|