chore: format code and remove redundant blank lines

This commit applies automated code formatting across the project. The changes primarily involve removing unnecessary blank lines and ensuring consistent code style, improving readability and maintainability without altering functionality.
This commit is contained in:
minecraft1024a
2025-09-05 20:58:03 +08:00
parent 488e959577
commit 513757a8ee
37 changed files with 439 additions and 419 deletions

View File

@@ -37,6 +37,7 @@ def get_classes_in_module(module):
classes.append(member) classes.append(member)
return classes return classes
async def message_recv(server_connection: Server.ServerConnection): async def message_recv(server_connection: Server.ServerConnection):
await message_handler.set_server_connection(server_connection) await message_handler.set_server_connection(server_connection)
asyncio.create_task(notice_handler.set_server_connection(server_connection)) asyncio.create_task(notice_handler.set_server_connection(server_connection))
@@ -76,6 +77,7 @@ async def message_recv(server_connection: Server.ServerConnection):
logger.error(f"处理消息时出错: {e}") logger.error(f"处理消息时出错: {e}")
logger.debug(f"原始消息: {raw_message[:500]}...") logger.debug(f"原始消息: {raw_message[:500]}...")
async def message_process(): async def message_process():
"""消息处理主循环""" """消息处理主循环"""
logger.info("消息处理器已启动") logger.info("消息处理器已启动")
@@ -132,6 +134,7 @@ async def message_process():
except Exception as e: except Exception as e:
logger.debug(f"清理消息队列时出错: {e}") logger.debug(f"清理消息队列时出错: {e}")
async def napcat_server(): async def napcat_server():
"""启动 Napcat WebSocket 连接(支持正向和反向连接)""" """启动 Napcat WebSocket 连接(支持正向和反向连接)"""
mode = global_config.napcat_server.mode mode = global_config.napcat_server.mode
@@ -143,6 +146,7 @@ async def napcat_server():
logger.error(f"启动 WebSocket 连接失败: {e}") logger.error(f"启动 WebSocket 连接失败: {e}")
raise raise
async def graceful_shutdown(): async def graceful_shutdown():
"""优雅关闭所有组件""" """优雅关闭所有组件"""
try: try:
@@ -189,10 +193,7 @@ async def graceful_shutdown():
# 等待任务取消完成,忽略 CancelledError # 等待任务取消完成,忽略 CancelledError
try: try:
await asyncio.wait_for( await asyncio.wait_for(asyncio.gather(*tasks, return_exceptions=True), timeout=10)
asyncio.gather(*tasks, return_exceptions=True),
timeout=10
)
except asyncio.TimeoutError: except asyncio.TimeoutError:
logger.warning("部分任务取消超时") logger.warning("部分任务取消超时")
except Exception as e: except Exception as e:
@@ -214,6 +215,7 @@ async def graceful_shutdown():
except Exception: except Exception:
pass pass
class LauchNapcatAdapterHandler(BaseEventHandler): class LauchNapcatAdapterHandler(BaseEventHandler):
"""自动启动Adapter""" """自动启动Adapter"""
@@ -245,6 +247,7 @@ class LauchNapcatAdapterHandler(BaseEventHandler):
asyncio.create_task(message_process()) asyncio.create_task(message_process())
asyncio.create_task(check_timeout_response()) asyncio.create_task(check_timeout_response())
class StopNapcatAdapterHandler(BaseEventHandler): class StopNapcatAdapterHandler(BaseEventHandler):
"""关闭Adapter""" """关闭Adapter"""

View File

@@ -58,6 +58,7 @@ class VoiceConfig(ConfigBase):
use_tts: bool = False use_tts: bool = False
"""是否启用TTS功能""" """是否启用TTS功能"""
@dataclass @dataclass
class SlicingConfig(ConfigBase): class SlicingConfig(ConfigBase):
max_frame_size: int = 64 max_frame_size: int = 64
@@ -66,6 +67,7 @@ class SlicingConfig(ConfigBase):
delay_ms: int = 10 delay_ms: int = 10
"""切片发送间隔时间,单位为毫秒""" """切片发送间隔时间,单位为毫秒"""
@dataclass @dataclass
class DebugConfig(ConfigBase): class DebugConfig(ConfigBase):
level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO" level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO"

View File

@@ -3,6 +3,7 @@
用于在 Ada 发送给 MMC 时进行消息切片,利用 WebSocket 协议的自动重组特性 用于在 Ada 发送给 MMC 时进行消息切片,利用 WebSocket 协议的自动重组特性
仅在 Ada -> MMC 方向进行切片其他方向MMC -> AdaAda <-> Napcat不切片 仅在 Ada -> MMC 方向进行切片其他方向MMC -> AdaAda <-> Napcat不切片
""" """
import json import json
import uuid import uuid
import asyncio import asyncio
@@ -15,7 +16,6 @@ from src.common.logger import get_logger
logger = get_logger("napcat_adapter") logger = get_logger("napcat_adapter")
class MessageChunker: class MessageChunker:
"""消息切片器,用于处理大消息的分片发送""" """消息切片器,用于处理大消息的分片发送"""
@@ -29,12 +29,14 @@ class MessageChunker:
message_str = json.dumps(message, ensure_ascii=False) message_str = json.dumps(message, ensure_ascii=False)
else: else:
message_str = message message_str = message
return len(message_str.encode('utf-8')) > self.max_chunk_size return len(message_str.encode("utf-8")) > self.max_chunk_size
except Exception as e: except Exception as e:
logger.error(f"检查消息大小时出错: {e}") logger.error(f"检查消息大小时出错: {e}")
return False return False
def chunk_message(self, message: Union[str, Dict[str, Any]], chunk_id: Optional[str] = None) -> List[Dict[str, Any]]: def chunk_message(
self, message: Union[str, Dict[str, Any]], chunk_id: Optional[str] = None
) -> List[Dict[str, Any]]:
""" """
将消息切片 将消息切片
@@ -62,7 +64,7 @@ class MessageChunker:
if chunk_id is None: if chunk_id is None:
chunk_id = str(uuid.uuid4()) chunk_id = str(uuid.uuid4())
message_bytes = message_str.encode('utf-8') message_bytes = message_str.encode("utf-8")
total_size = len(message_bytes) total_size = len(message_bytes)
# 计算需要多少个切片 # 计算需要多少个切片
@@ -83,10 +85,10 @@ class MessageChunker:
"total_chunks": num_chunks, "total_chunks": num_chunks,
"chunk_size": len(chunk_data), "chunk_size": len(chunk_data),
"total_size": total_size, "total_size": total_size,
"timestamp": time.time() "timestamp": time.time(),
}, },
"__mmc_chunk_data__": chunk_data.decode('utf-8', errors='ignore'), "__mmc_chunk_data__": chunk_data.decode("utf-8", errors="ignore"),
"__mmc_is_chunked__": True "__mmc_is_chunked__": True,
} }
chunks.append(chunk_message) chunks.append(chunk_message)
@@ -111,10 +113,10 @@ class MessageChunker:
data = message data = message
return ( return (
isinstance(data, dict) and isinstance(data, dict)
"__mmc_chunk_info__" in data and and "__mmc_chunk_info__" in data
"__mmc_chunk_data__" in data and and "__mmc_chunk_data__" in data
"__mmc_is_chunked__" in data and "__mmc_is_chunked__" in data
) )
except (json.JSONDecodeError, TypeError): except (json.JSONDecodeError, TypeError):
return False return False
@@ -152,7 +154,7 @@ class MessageReassembler:
expired_chunks = [] expired_chunks = []
for chunk_id, buffer_info in self.chunk_buffers.items(): for chunk_id, buffer_info in self.chunk_buffers.items():
if current_time - buffer_info['timestamp'] > self.timeout: if current_time - buffer_info["timestamp"] > self.timeout:
expired_chunks.append(chunk_id) expired_chunks.append(chunk_id)
for chunk_id in expired_chunks: for chunk_id in expired_chunks:
@@ -207,7 +209,7 @@ class MessageReassembler:
"chunks": {}, "chunks": {},
"total_chunks": total_chunks, "total_chunks": total_chunks,
"received_chunks": 0, "received_chunks": 0,
"timestamp": chunk_timestamp "timestamp": chunk_timestamp,
} }
buffer = self.chunk_buffers[chunk_id] buffer = self.chunk_buffers[chunk_id]
@@ -260,7 +262,7 @@ class MessageReassembler:
"received": buffer["received_chunks"], "received": buffer["received_chunks"],
"total": buffer["total_chunks"], "total": buffer["total_chunks"],
"progress": f"{buffer['received_chunks']}/{buffer['total_chunks']}", "progress": f"{buffer['received_chunks']}/{buffer['total_chunks']}",
"age_seconds": time.time() - buffer["timestamp"] "age_seconds": time.time() - buffer["timestamp"],
} }
return info return info

View File

@@ -800,7 +800,10 @@ class MessageHandler:
content_parts.append(f"链接: {extracted_info['short_url']}") content_parts.append(f"链接: {extracted_info['short_url']}")
formatted_content = "\n".join(content_parts) formatted_content = "\n".join(content_parts)
return Seg(type="text", data=f"这是一条小程序分享消息,可以根据来源,考虑使用对应解析工具\n{formatted_content}") return Seg(
type="text",
data=f"这是一条小程序分享消息,可以根据来源,考虑使用对应解析工具\n{formatted_content}",
)
# 如果没有提取到关键信息返回None # 如果没有提取到关键信息返回None
return None return None

View File

@@ -128,15 +128,23 @@ class CycleProcessor:
x0 = 1.0 # 控制曲线中心点 x0 = 1.0 # 控制曲线中心点
return 1.0 / (1.0 + math.exp(-k * (interest_val - x0))) return 1.0 / (1.0 + math.exp(-k * (interest_val - x0)))
normal_mode_probability = calculate_normal_mode_probability(interest_value) * 0.5 / global_config.chat.get_current_talk_frequency(self.context.stream_id) normal_mode_probability = (
calculate_normal_mode_probability(interest_value)
* 0.5
/ global_config.chat.get_current_talk_frequency(self.context.stream_id)
)
# 根据概率决定使用哪种模式 # 根据概率决定使用哪种模式
if random.random() < normal_mode_probability: if random.random() < normal_mode_probability:
mode = ChatMode.NORMAL mode = ChatMode.NORMAL
logger.info(f"{self.log_prefix} 基于兴趣值 {interest_value:.2f},概率 {normal_mode_probability:.2f}选择Normal planner模式") logger.info(
f"{self.log_prefix} 基于兴趣值 {interest_value:.2f},概率 {normal_mode_probability:.2f}选择Normal planner模式"
)
else: else:
mode = ChatMode.FOCUS mode = ChatMode.FOCUS
logger.info(f"{self.log_prefix} 基于兴趣值 {interest_value:.2f},概率 {normal_mode_probability:.2f}选择Focus planner模式") logger.info(
f"{self.log_prefix} 基于兴趣值 {interest_value:.2f},概率 {normal_mode_probability:.2f}选择Focus planner模式"
)
cycle_timers, thinking_id = self.cycle_tracker.start_cycle() cycle_timers, thinking_id = self.cycle_tracker.start_cycle()
logger.info(f"{self.log_prefix} 开始第{self.context.cycle_counter}次思考") logger.info(f"{self.log_prefix} 开始第{self.context.cycle_counter}次思考")
@@ -165,7 +173,9 @@ class CycleProcessor:
from src.plugin_system.core.event_manager import event_manager from src.plugin_system.core.event_manager import event_manager
from src.plugin_system import EventType from src.plugin_system import EventType
result = await event_manager.trigger_event(EventType.ON_PLAN,plugin_name="SYSTEM", stream_id=self.context.chat_stream) result = await event_manager.trigger_event(
EventType.ON_PLAN, plugin_name="SYSTEM", stream_id=self.context.chat_stream
)
if not result.all_continue_process(): if not result.all_continue_process():
raise UserWarning(f"插件{result.get_summary().get('stopped_handlers', '')}于规划前中断了内容生成") raise UserWarning(f"插件{result.get_summary().get('stopped_handlers', '')}于规划前中断了内容生成")
@@ -195,12 +205,7 @@ class CycleProcessor:
action_name="no_reply", action_name="no_reply",
) )
return { return {"action_type": "no_reply", "success": True, "reply_text": "", "command": ""}
"action_type": "no_reply",
"success": True,
"reply_text": "",
"command": ""
}
elif action_info["action_type"] != "reply": elif action_info["action_type"] != "reply":
# 执行普通动作 # 执行普通动作
with Timer("动作执行", cycle_timers): with Timer("动作执行", cycle_timers):
@@ -210,13 +215,13 @@ class CycleProcessor:
action_info["action_data"], action_info["action_data"],
cycle_timers, cycle_timers,
thinking_id, thinking_id,
action_info["action_message"] action_info["action_message"],
) )
return { return {
"action_type": action_info["action_type"], "action_type": action_info["action_type"],
"success": success, "success": success,
"reply_text": reply_text, "reply_text": reply_text,
"command": command "command": command,
} }
else: else:
try: try:
@@ -229,21 +234,13 @@ class CycleProcessor:
from_plugin=False, from_plugin=False,
) )
if not success or not response_set: if not success or not response_set:
logger.info(f"{action_info['action_message'].get('processed_plain_text')} 的回复生成失败") logger.info(
return { f"{action_info['action_message'].get('processed_plain_text')} 的回复生成失败"
"action_type": "reply", )
"success": False, return {"action_type": "reply", "success": False, "reply_text": "", "loop_info": None}
"reply_text": "",
"loop_info": None
}
except asyncio.CancelledError: except asyncio.CancelledError:
logger.debug(f"{self.log_prefix} 并行执行:回复生成任务已被取消") logger.debug(f"{self.log_prefix} 并行执行:回复生成任务已被取消")
return { return {"action_type": "reply", "success": False, "reply_text": "", "loop_info": None}
"action_type": "reply",
"success": False,
"reply_text": "",
"loop_info": None
}
loop_info, reply_text, cycle_timers_reply = await self._send_and_store_reply( loop_info, reply_text, cycle_timers_reply = await self._send_and_store_reply(
response_set, response_set,
@@ -253,12 +250,7 @@ class CycleProcessor:
thinking_id, thinking_id,
actions, actions,
) )
return { return {"action_type": "reply", "success": True, "reply_text": reply_text, "loop_info": loop_info}
"action_type": "reply",
"success": True,
"reply_text": reply_text,
"loop_info": loop_info
}
except Exception as e: except Exception as e:
logger.error(f"{self.log_prefix} 执行动作时出错: {e}") logger.error(f"{self.log_prefix} 执行动作时出错: {e}")
logger.error(f"{self.log_prefix} 错误信息: {traceback.format_exc()}") logger.error(f"{self.log_prefix} 错误信息: {traceback.format_exc()}")
@@ -267,7 +259,7 @@ class CycleProcessor:
"success": False, "success": False,
"reply_text": "", "reply_text": "",
"loop_info": None, "loop_info": None,
"error": str(e) "error": str(e),
} }
# 创建所有动作的后台任务 # 创建所有动作的后台任务

View File

@@ -91,25 +91,24 @@ class CycleTracker:
# 获取动作类型,兼容新旧格式 # 获取动作类型,兼容新旧格式
action_type = "未知动作" action_type = "未知动作"
if hasattr(self, '_current_cycle_detail') and self._current_cycle_detail: if hasattr(self, "_current_cycle_detail") and self._current_cycle_detail:
loop_plan_info = self._current_cycle_detail.loop_plan_info loop_plan_info = self._current_cycle_detail.loop_plan_info
if isinstance(loop_plan_info, dict): if isinstance(loop_plan_info, dict):
action_result = loop_plan_info.get('action_result', {}) action_result = loop_plan_info.get("action_result", {})
if isinstance(action_result, dict): if isinstance(action_result, dict):
# 旧格式action_result是字典 # 旧格式action_result是字典
action_type = action_result.get('action_type', '未知动作') action_type = action_result.get("action_type", "未知动作")
elif isinstance(action_result, list) and action_result: elif isinstance(action_result, list) and action_result:
# 新格式action_result是actions列表 # 新格式action_result是actions列表
action_type = action_result[0].get('action_type', '未知动作') action_type = action_result[0].get("action_type", "未知动作")
elif isinstance(loop_plan_info, list) and loop_plan_info: elif isinstance(loop_plan_info, list) and loop_plan_info:
# 直接是actions列表的情况 # 直接是actions列表的情况
action_type = loop_plan_info[0].get('action_type', '未知动作') action_type = loop_plan_info[0].get("action_type", "未知动作")
if self.context.current_cycle_detail.end_time and self.context.current_cycle_detail.start_time: if self.context.current_cycle_detail.end_time and self.context.current_cycle_detail.start_time:
duration = self.context.current_cycle_detail.end_time - self.context.current_cycle_detail.start_time duration = self.context.current_cycle_detail.end_time - self.context.current_cycle_detail.start_time
logger.info( logger.info(
f"{self.context.log_prefix}{self.context.current_cycle_detail.cycle_id}次思考," f"{self.context.log_prefix}{self.context.current_cycle_detail.cycle_id}次思考,"
f"耗时: {duration:.1f}秒, " f"耗时: {duration:.1f}秒, "
f"选择动作: {action_type}" f"选择动作: {action_type}" + (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "")
+ (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "")
) )

View File

@@ -183,7 +183,7 @@ class HeartFChatting:
event = ProactiveTriggerEvent( event = ProactiveTriggerEvent(
source="silence_monitor", source="silence_monitor",
reason=f"聊天已沉默 {formatted_time}", reason=f"聊天已沉默 {formatted_time}",
metadata={"silence_duration": silence_duration} metadata={"silence_duration": silence_duration},
) )
await self.proactive_thinker.think(event) await self.proactive_thinker.think(event)
self.context.last_message_time = current_time self.context.last_message_time = current_time
@@ -205,20 +205,29 @@ class HeartFChatting:
stream_parts = self.context.stream_id.split(":") stream_parts = self.context.stream_id.split(":")
current_chat_identifier = f"{stream_parts}:{stream_parts}" if len(stream_parts) >= 2 else self.context.stream_id current_chat_identifier = f"{stream_parts}:{stream_parts}" if len(stream_parts) >= 2 else self.context.stream_id
enable_list = getattr(global_config.chat, "proactive_thinking_enable_in_groups" if is_group_chat else "proactive_thinking_enable_in_private", []) enable_list = getattr(
global_config.chat,
"proactive_thinking_enable_in_groups" if is_group_chat else "proactive_thinking_enable_in_private",
[],
)
return not enable_list or current_chat_identifier in enable_list return not enable_list or current_chat_identifier in enable_list
def _get_dynamic_thinking_interval(self) -> float: def _get_dynamic_thinking_interval(self) -> float:
try: try:
from src.utils.timing_utils import get_normal_distributed_interval from src.utils.timing_utils import get_normal_distributed_interval
base_interval = global_config.chat.proactive_thinking_interval base_interval = global_config.chat.proactive_thinking_interval
delta_sigma = getattr(global_config.chat, "delta_sigma", 120) delta_sigma = getattr(global_config.chat, "delta_sigma", 120)
if base_interval <= 0: base_interval = abs(base_interval) if base_interval <= 0:
if delta_sigma < 0: delta_sigma = abs(delta_sigma) base_interval = abs(base_interval)
if delta_sigma < 0:
delta_sigma = abs(delta_sigma)
if base_interval == 0 and delta_sigma == 0: return 300 if base_interval == 0 and delta_sigma == 0:
if delta_sigma == 0: return base_interval return 300
if delta_sigma == 0:
return base_interval
sigma_percentage = delta_sigma / base_interval if base_interval > 0 else delta_sigma / 1000 sigma_percentage = delta_sigma / base_interval if base_interval > 0 else delta_sigma / 1000
return get_normal_distributed_interval(base_interval, sigma_percentage, 1, 86400, use_3sigma_rule=True) return get_normal_distributed_interval(base_interval, sigma_percentage, 1, 86400, use_3sigma_rule=True)
@@ -356,8 +365,9 @@ class HeartFChatting:
self.context.energy_value += 1 / global_config.chat.focus_value self.context.energy_value += 1 / global_config.chat.focus_value
# 重置累积兴趣值,因为消息已经被成功处理 # 重置累积兴趣值,因为消息已经被成功处理
self.context.breaking_accumulated_interest = 0.0 self.context.breaking_accumulated_interest = 0.0
logger.info(f"{self.context.log_prefix} 能量值增加,当前能量值:{self.context.energy_value:.1f},重置累积兴趣值") logger.info(
f"{self.context.log_prefix} 能量值增加,当前能量值:{self.context.energy_value:.1f},重置累积兴趣值"
)
# 更新上一帧的睡眠状态 # 更新上一帧的睡眠状态
self.context.was_sleeping = is_sleeping self.context.was_sleeping = is_sleeping
@@ -378,7 +388,6 @@ class HeartFChatting:
return has_new_messages return has_new_messages
def _handle_wakeup_messages(self, messages): def _handle_wakeup_messages(self, messages):
""" """
处理休眠状态下的消息,累积唤醒度 处理休眠状态下的消息,累积唤醒度
@@ -433,7 +442,9 @@ class HeartFChatting:
# 计算调整后的阈值 # 计算调整后的阈值
adjusted_threshold = 1 / global_config.chat.get_current_talk_frequency(self.context.stream_id) adjusted_threshold = 1 / global_config.chat.get_current_talk_frequency(self.context.stream_id)
logger.info(f"{self.context.log_prefix} 累积兴趣值: {total_interest:.2f}, 调整后阈值: {adjusted_threshold:.2f}") logger.info(
f"{self.context.log_prefix} 累积兴趣值: {total_interest:.2f}, 调整后阈值: {adjusted_threshold:.2f}"
)
# 如果累积兴趣值小于阈值进入breaking形式 # 如果累积兴趣值小于阈值进入breaking形式
if total_interest < adjusted_threshold: if total_interest < adjusted_threshold:
@@ -489,7 +500,9 @@ class HeartFChatting:
if new_message_count > 0: if new_message_count > 0:
# 只在兴趣值变化时输出log # 只在兴趣值变化时输出log
if not hasattr(self, "_last_accumulated_interest") or total_interest != self._last_accumulated_interest: if not hasattr(self, "_last_accumulated_interest") or total_interest != self._last_accumulated_interest:
logger.info(f"{self.context.log_prefix} breaking形式当前累积兴趣值: {total_interest:.2f}, 专注度: {global_config.chat.focus_value:.1f}") logger.info(
f"{self.context.log_prefix} breaking形式当前累积兴趣值: {total_interest:.2f}, 专注度: {global_config.chat.focus_value:.1f}"
)
self._last_accumulated_interest = total_interest self._last_accumulated_interest = total_interest
if total_interest >= modified_exit_interest_threshold: if total_interest >= modified_exit_interest_threshold:
# 记录兴趣度到列表 # 记录兴趣度到列表
@@ -502,7 +515,10 @@ class HeartFChatting:
return True, total_interest / new_message_count return True, total_interest / new_message_count
# 每10秒输出一次等待状态 # 每10秒输出一次等待状态
if int(time.time() - self.context.last_read_time) > 0 and int(time.time() - self.context.last_read_time) % 10 == 0: if (
int(time.time() - self.context.last_read_time) > 0
and int(time.time() - self.context.last_read_time) % 10 == 0
):
logger.info( logger.info(
f"{self.context.log_prefix} 已等待{time.time() - self.context.last_read_time:.0f}秒,累计{new_message_count}条消息,累积兴趣{total_interest:.1f},继续等待..." f"{self.context.log_prefix} 已等待{time.time() - self.context.last_read_time:.0f}秒,累计{new_message_count}条消息,累积兴趣{total_interest:.1f},继续等待..."
) )

View File

@@ -50,7 +50,7 @@ class HfcContext:
self.last_read_time = time.time() - 10 self.last_read_time = time.time() - 10
# 从聊天流恢复breaking累积兴趣值 # 从聊天流恢复breaking累积兴趣值
self.breaking_accumulated_interest = getattr(self.chat_stream, 'breaking_accumulated_interest', 0.0) self.breaking_accumulated_interest = getattr(self.chat_stream, "breaking_accumulated_interest", 0.0)
self.action_manager = ActionManager() self.action_manager = ActionManager()

View File

@@ -122,6 +122,7 @@ class CycleDetail:
self.loop_plan_info = loop_info["loop_plan_info"] self.loop_plan_info = loop_info["loop_plan_info"]
self.loop_action_info = loop_info["loop_action_info"] self.loop_action_info = loop_info["loop_action_info"]
async def send_typing(): async def send_typing():
""" """
发送打字状态指示 发送打字状态指示

View File

@@ -1,11 +1,13 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Optional, Dict, Any from typing import Optional, Dict, Any
@dataclass @dataclass
class ProactiveTriggerEvent: class ProactiveTriggerEvent:
""" """
主动思考触发事件的数据类 主动思考触发事件的数据类
""" """
source: str # 触发源的标识,例如 "silence_monitor", "insomnia_manager" source: str # 触发源的标识,例如 "silence_monitor", "insomnia_manager"
reason: str # 触发的具体原因,例如 "聊天已沉默10分钟", "深夜emo" reason: str # 触发的具体原因,例如 "聊天已沉默10分钟", "深夜emo"
metadata: Optional[Dict[str, Any]] = field(default_factory=dict) # 可选的元数据,用于传递额外信息 metadata: Optional[Dict[str, Any]] = field(default_factory=dict) # 可选的元数据,用于传递额外信息

View File

@@ -37,8 +37,10 @@ class ProactiveThinker:
Args: Args:
trigger_event: 描述触发上下文的事件对象 trigger_event: 描述触发上下文的事件对象
""" """
logger.info(f"{self.context.log_prefix} 接收到主动思考事件: " logger.info(
f"来源='{trigger_event.source}', 原因='{trigger_event.reason}'") f"{self.context.log_prefix} 接收到主动思考事件: "
f"来源='{trigger_event.source}', 原因='{trigger_event.reason}'"
)
try: try:
# 1. 根据事件类型执行前置操作 # 1. 根据事件类型执行前置操作
@@ -63,6 +65,7 @@ class ProactiveThinker:
try: try:
from src.mood.mood_manager import mood_manager from src.mood.mood_manager import mood_manager
mood_obj = mood_manager.get_mood_by_chat_id(self.context.stream_id) mood_obj = mood_manager.get_mood_by_chat_id(self.context.stream_id)
new_mood = None new_mood = None
@@ -76,8 +79,10 @@ class ProactiveThinker:
if new_mood: if new_mood:
mood_obj.mood_state = new_mood mood_obj.mood_state = new_mood
mood_obj.last_change_time = time.time() mood_obj.last_change_time = time.time()
logger.info(f"{self.context.log_prefix}'{trigger_event.reason}'" logger.info(
f"情绪状态被强制更新为: {mood_obj.mood_state}") f"{self.context.log_prefix}'{trigger_event.reason}'"
f"情绪状态被强制更新为: {mood_obj.mood_state}"
)
except Exception as e: except Exception as e:
logger.error(f"{self.context.log_prefix} 设置失眠情绪时出错: {e}") logger.error(f"{self.context.log_prefix} 设置失眠情绪时出错: {e}")
@@ -91,19 +96,17 @@ class ProactiveThinker:
""" """
try: try:
# 直接调用 planner 的 PROACTIVE 模式 # 直接调用 planner 的 PROACTIVE 模式
actions, target_message = await self.cycle_processor.action_planner.plan( actions, target_message = await self.cycle_processor.action_planner.plan(mode=ChatMode.PROACTIVE)
mode=ChatMode.PROACTIVE
)
# 获取第一个规划出的动作作为主要决策 # 获取第一个规划出的动作作为主要决策
action_result = actions[0] if actions else {} action_result = actions[0] if actions else {}
# 如果决策不是 do_nothing则执行 # 如果决策不是 do_nothing则执行
if action_result and action_result.get("action_type") != "do_nothing": if action_result and action_result.get("action_type") != "do_nothing":
# 在主动思考时,如果 target_message 为 None则默认选取最新 message 作为 target_message # 在主动思考时,如果 target_message 为 None则默认选取最新 message 作为 target_message
if target_message is None and self.context.chat_stream and self.context.chat_stream.context: if target_message is None and self.context.chat_stream and self.context.chat_stream.context:
from src.chat.message_receive.message import MessageRecv from src.chat.message_receive.message import MessageRecv
latest_message = self.context.chat_stream.context.get_last_message() latest_message = self.context.chat_stream.context.get_last_message()
if isinstance(latest_message, MessageRecv): if isinstance(latest_message, MessageRecv):
user_info = latest_message.message_info.user_info user_info = latest_message.message_info.user_info

View File

@@ -259,7 +259,7 @@ class ChatManager:
"user_cardname": model_instance.user_cardname or "", "user_cardname": model_instance.user_cardname or "",
} }
group_info_data = None group_info_data = None
if model_instance and getattr(model_instance, 'group_id', None): if model_instance and getattr(model_instance, "group_id", None):
group_info_data = { group_info_data = {
"platform": model_instance.group_platform, "platform": model_instance.group_platform,
"group_id": model_instance.group_id, "group_id": model_instance.group_id,
@@ -405,7 +405,7 @@ class ChatManager:
"user_cardname": model_instance.user_cardname or "", "user_cardname": model_instance.user_cardname or "",
} }
group_info_data = None group_info_data = None
if model_instance and getattr(model_instance, 'group_id', None): if model_instance and getattr(model_instance, "group_id", None):
group_info_data = { group_info_data = {
"platform": model_instance.group_platform, "platform": model_instance.group_platform,
"group_id": model_instance.group_id, "group_id": model_instance.group_id,

View File

@@ -162,9 +162,7 @@ class ActionModifier:
available_actions = list(self.action_manager.get_using_actions().keys()) available_actions = list(self.action_manager.get_using_actions().keys())
available_actions_text = "".join(available_actions) if available_actions else "" available_actions_text = "".join(available_actions) if available_actions else ""
logger.info( logger.info(f"{self.log_prefix} 当前可用动作: {available_actions_text}||移除: {removals_summary}")
f"{self.log_prefix} 当前可用动作: {available_actions_text}||移除: {removals_summary}"
)
def _check_action_associated_types(self, all_actions: Dict[str, ActionInfo], chat_context: ChatMessageContext): def _check_action_associated_types(self, all_actions: Dict[str, ActionInfo], chat_context: ChatMessageContext):
type_mismatched_actions: List[Tuple[str, str]] = [] type_mismatched_actions: List[Tuple[str, str]] = []

View File

@@ -188,15 +188,12 @@ class ActionPlanner:
param_text = "" param_text = ""
if action_info.action_parameters: if action_info.action_parameters:
param_text = "\n" + "\n".join( param_text = "\n" + "\n".join(
f' "{p_name}":"{p_desc}"' f' "{p_name}":"{p_desc}"' for p_name, p_desc in action_info.action_parameters.items()
for p_name, p_desc in action_info.action_parameters.items()
) )
require_text = "\n".join(f"- {req}" for req in action_info.action_require) require_text = "\n".join(f"- {req}" for req in action_info.action_require)
using_action_prompt = await global_prompt_manager.get_prompt_async( using_action_prompt = await global_prompt_manager.get_prompt_async("action_prompt")
"action_prompt"
)
action_options_block += using_action_prompt.format( action_options_block += using_action_prompt.format(
action_name=action_name, action_name=action_name,
action_description=action_info.description, action_description=action_info.description,
@@ -205,9 +202,7 @@ class ActionPlanner:
) )
return action_options_block return action_options_block
def find_message_by_id( def find_message_by_id(self, message_id: str, message_id_list: list) -> Optional[Dict[str, Any]]:
self, message_id: str, message_id_list: list
) -> Optional[Dict[str, Any]]:
# sourcery skip: use-next # sourcery skip: use-next
""" """
根据message_id从message_id_list中查找对应的原始消息 根据message_id从message_id_list中查找对应的原始消息
@@ -323,11 +318,15 @@ class ActionPlanner:
# 如果获取的target_message为None输出warning并重新plan # 如果获取的target_message为None输出warning并重新plan
if target_message is None: if target_message is None:
self.plan_retry_count += 1 self.plan_retry_count += 1
logger.warning(f"{self.log_prefix}无法找到target_message_id '{target_message_id}' 对应的消息,重试次数: {self.plan_retry_count}/{self.max_plan_retries}") logger.warning(
f"{self.log_prefix}无法找到target_message_id '{target_message_id}' 对应的消息,重试次数: {self.plan_retry_count}/{self.max_plan_retries}"
)
# 如果连续三次plan均为None输出error并选取最新消息 # 如果连续三次plan均为None输出error并选取最新消息
if self.plan_retry_count >= self.max_plan_retries: if self.plan_retry_count >= self.max_plan_retries:
logger.error(f"{self.log_prefix}连续{self.max_plan_retries}次plan获取target_message失败选择最新消息作为target_message") logger.error(
f"{self.log_prefix}连续{self.max_plan_retries}次plan获取target_message失败选择最新消息作为target_message"
)
target_message = self.get_latest_message(message_id_list) target_message = self.get_latest_message(message_id_list)
self.plan_retry_count = 0 # 重置计数器 self.plan_retry_count = 0 # 重置计数器
else: else:
@@ -339,7 +338,6 @@ class ActionPlanner:
else: else:
logger.warning(f"{self.log_prefix}动作'{action}'缺少target_message_id") logger.warning(f"{self.log_prefix}动作'{action}'缺少target_message_id")
if action != "no_reply" and action != "reply" and action not in current_available_actions: if action != "no_reply" and action != "reply" and action not in current_available_actions:
logger.warning( logger.warning(
f"{self.log_prefix}LLM 返回了当前不可用或无效的动作: '{action}' (可用: {list(current_available_actions.keys())}),将强制使用 'no_reply'" f"{self.log_prefix}LLM 返回了当前不可用或无效的动作: '{action}' (可用: {list(current_available_actions.keys())}),将强制使用 'no_reply'"
@@ -363,26 +361,25 @@ class ActionPlanner:
if mode == ChatMode.NORMAL and action in current_available_actions: if mode == ChatMode.NORMAL and action in current_available_actions:
is_parallel = current_available_actions[action].parallel_action is_parallel = current_available_actions[action].parallel_action
action_data["loop_start_time"] = loop_start_time action_data["loop_start_time"] = loop_start_time
actions = [] actions = []
# 1. 添加Planner取得的动作 # 1. 添加Planner取得的动作
actions.append({ actions.append(
{
"action_type": action, "action_type": action,
"reasoning": reasoning, "reasoning": reasoning,
"action_data": action_data, "action_data": action_data,
"action_message": target_message, "action_message": target_message,
"available_actions": available_actions # 添加这个字段 "available_actions": available_actions, # 添加这个字段
}) }
)
if action != "reply" and is_parallel: if action != "reply" and is_parallel:
actions.append({ actions.append(
"action_type": "reply", {"action_type": "reply", "action_message": target_message, "available_actions": available_actions}
"action_message": target_message, )
"available_actions": available_actions
})
return actions, target_message return actions, target_message
@@ -400,21 +397,15 @@ class ActionPlanner:
time_block = f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" time_block = f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
bot_name = global_config.bot.nickname bot_name = global_config.bot.nickname
bot_nickname = ( bot_nickname = (
f",也有人叫你{','.join(global_config.bot.alias_names)}" f",也有人叫你{','.join(global_config.bot.alias_names)}" if global_config.bot.alias_names else ""
if global_config.bot.alias_names
else ""
) )
bot_core_personality = global_config.personality.personality_core bot_core_personality = global_config.personality.personality_core
identity_block = ( identity_block = f"你的名字是{bot_name}{bot_nickname},你{bot_core_personality}"
f"你的名字是{bot_name}{bot_nickname},你{bot_core_personality}"
)
schedule_block = "" schedule_block = ""
if global_config.schedule.enable: if global_config.schedule.enable:
if current_activity := schedule_manager.get_current_activity(): if current_activity := schedule_manager.get_current_activity():
schedule_block = ( schedule_block = f"你当前正在:{current_activity},但注意它与群聊的聊天无关。"
f"你当前正在:{current_activity},但注意它与群聊的聊天无关。"
)
mood_block = "" mood_block = ""
if global_config.mood.enable_mood: if global_config.mood.enable_mood:
@@ -424,13 +415,9 @@ class ActionPlanner:
# --- 根据模式构建不同的Prompt --- # --- 根据模式构建不同的Prompt ---
if mode == ChatMode.PROACTIVE: if mode == ChatMode.PROACTIVE:
long_term_memory_block = await self._get_long_term_memory_context() long_term_memory_block = await self._get_long_term_memory_context()
action_options_text = await self._build_action_options( action_options_text = await self._build_action_options(current_available_actions, mode)
current_available_actions, mode
)
prompt_template = await global_prompt_manager.get_prompt_async( prompt_template = await global_prompt_manager.get_prompt_async("proactive_planner_prompt")
"proactive_planner_prompt"
)
prompt = prompt_template.format( prompt = prompt_template.format(
time_block=time_block, time_block=time_block,
identity_block=identity_block, identity_block=identity_block,
@@ -463,12 +450,8 @@ class ActionPlanner:
limit=5, limit=5,
) )
actions_before_now_block = build_readable_actions( actions_before_now_block = build_readable_actions(actions=actions_before_now)
actions=actions_before_now actions_before_now_block = f"你刚刚选择并执行过的action是\n{actions_before_now_block}"
)
actions_before_now_block = (
f"你刚刚选择并执行过的action是\n{actions_before_now_block}"
)
if refresh_time: if refresh_time:
self.last_obs_time_mark = time.time() self.last_obs_time_mark = time.time()
@@ -507,27 +490,19 @@ class ActionPlanner:
chat_target_name = None chat_target_name = None
if not is_group_chat and chat_target_info: if not is_group_chat and chat_target_info:
chat_target_name = ( chat_target_name = (
chat_target_info.get("person_name") chat_target_info.get("person_name") or chat_target_info.get("user_nickname") or "对方"
or chat_target_info.get("user_nickname")
or "对方"
) )
chat_context_description = f"你正在和 {chat_target_name} 私聊" chat_context_description = f"你正在和 {chat_target_name} 私聊"
action_options_block = await self._build_action_options( action_options_block = await self._build_action_options(current_available_actions, mode)
current_available_actions, mode
)
moderation_prompt_block = "请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。" moderation_prompt_block = "请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。"
custom_prompt_block = "" custom_prompt_block = ""
if global_config.custom_prompt.planner_custom_prompt_content: if global_config.custom_prompt.planner_custom_prompt_content:
custom_prompt_block = ( custom_prompt_block = global_config.custom_prompt.planner_custom_prompt_content
global_config.custom_prompt.planner_custom_prompt_content
)
planner_prompt_template = await global_prompt_manager.get_prompt_async( planner_prompt_template = await global_prompt_manager.get_prompt_async("planner_prompt")
"planner_prompt"
)
prompt = planner_prompt_template.format( prompt = planner_prompt_template.format(
schedule_block=schedule_block, schedule_block=schedule_block,
mood_block=mood_block, mood_block=mood_block,
@@ -555,9 +530,7 @@ class ActionPlanner:
""" """
is_group_chat = True is_group_chat = True
is_group_chat, chat_target_info = get_chat_type_and_target_info(self.chat_id) is_group_chat, chat_target_info = get_chat_type_and_target_info(self.chat_id)
logger.debug( logger.debug(f"{self.log_prefix}获取到聊天信息 - 群聊: {is_group_chat}, 目标信息: {chat_target_info}")
f"{self.log_prefix}获取到聊天信息 - 群聊: {is_group_chat}, 目标信息: {chat_target_info}"
)
current_available_actions_dict = self.action_manager.get_using_actions() current_available_actions_dict = self.action_manager.get_using_actions()
@@ -568,13 +541,9 @@ class ActionPlanner:
current_available_actions = {} current_available_actions = {}
for action_name in current_available_actions_dict: for action_name in current_available_actions_dict:
if action_name in all_registered_actions: if action_name in all_registered_actions:
current_available_actions[action_name] = all_registered_actions[ current_available_actions[action_name] = all_registered_actions[action_name]
action_name
]
else: else:
logger.warning( logger.warning(f"{self.log_prefix}使用中的动作 {action_name} 未在已注册动作中找到")
f"{self.log_prefix}使用中的动作 {action_name} 未在已注册动作中找到"
)
# 将no_reply作为系统级特殊动作添加到可用动作中 # 将no_reply作为系统级特殊动作添加到可用动作中
# no_reply虽然是系统级决策但需要让规划器认为它是可用的 # no_reply虽然是系统级决策但需要让规划器认为它是可用的

View File

@@ -830,7 +830,7 @@ class DefaultReplyer:
) )
person_name = await person_info_manager.get_value(person_id, "person_name") person_name = await person_info_manager.get_value(person_id, "person_name")
sender = person_name sender = person_name
target = reply_message.get('processed_plain_text') target = reply_message.get("processed_plain_text")
person_info_manager = get_person_info_manager() person_info_manager = get_person_info_manager()
person_id = person_info_manager.get_person_id_by_person_name(sender) person_id = person_info_manager.get_person_id_by_person_name(sender)
@@ -1181,7 +1181,9 @@ class DefaultReplyer:
else: else:
logger.debug(f"\n{prompt}\n") logger.debug(f"\n{prompt}\n")
content, (reasoning_content, model_name, tool_calls) = await self.express_model.generate_response_async(prompt) content, (reasoning_content, model_name, tool_calls) = await self.express_model.generate_response_async(
prompt
)
logger.debug(f"replyer生成内容: {content}") logger.debug(f"replyer生成内容: {content}")
return content, reasoning_content, model_name, tool_calls return content, reasoning_content, model_name, tool_calls

View File

@@ -12,6 +12,7 @@ from src.config.config import global_config
from src.chat.message_receive.chat_stream import get_chat_manager from src.chat.message_receive.chat_stream import get_chat_manager
from src.person_info.person_info import get_person_info_manager from src.person_info.person_info import get_person_info_manager
from src.plugin_system.apis import cross_context_api from src.plugin_system.apis import cross_context_api
logger = get_logger("prompt_utils") logger = get_logger("prompt_utils")

View File

@@ -194,7 +194,7 @@ class SmartPromptBuilder:
core_dialogue, background_dialogue = await self._build_s4u_chat_history_prompts( core_dialogue, background_dialogue = await self._build_s4u_chat_history_prompts(
params.message_list_before_now_long, params.message_list_before_now_long,
params.target_user_info.get("user_id") if params.target_user_info else "", params.target_user_info.get("user_id") if params.target_user_info else "",
params.sender params.sender,
) )
context_data["core_dialogue_prompt"] = core_dialogue context_data["core_dialogue_prompt"] = core_dialogue

View File

@@ -35,8 +35,7 @@ def is_image_message(message: Dict[str, Any]) -> bool:
bool: 是否为图片消息 bool: 是否为图片消息
""" """
return message.get("type") == "image" or ( return message.get("type") == "image" or (
isinstance(message.get("content"), dict) and isinstance(message.get("content"), dict) and message["content"].get("type") == "image"
message["content"].get("type") == "image"
) )
@@ -596,7 +595,6 @@ class ImageManager:
return "", "[图片]" return "", "[图片]"
# 创建全局单例 # 创建全局单例
image_manager = None image_manager = None

View File

@@ -62,10 +62,12 @@ def get_active_plans_for_month(month: str) -> List[MonthlyPlan]:
""" """
with get_db_session() as session: with get_db_session() as session:
try: try:
plans = session.query(MonthlyPlan).filter( plans = (
MonthlyPlan.target_month == month, session.query(MonthlyPlan)
MonthlyPlan.status == 'active' .filter(MonthlyPlan.target_month == month, MonthlyPlan.status == "active")
).order_by(MonthlyPlan.created_at.desc()).all() .order_by(MonthlyPlan.created_at.desc())
.all()
)
return plans return plans
except Exception as e: except Exception as e:
logger.error(f"查询 {month} 的有效月度计划时发生错误: {e}") logger.error(f"查询 {month} 的有效月度计划时发生错误: {e}")

View File

@@ -259,7 +259,6 @@ class NormalChatConfig(ValidatedConfigBase):
"""普通聊天配置类""" """普通聊天配置类"""
class ExpressionRule(ValidatedConfigBase): class ExpressionRule(ValidatedConfigBase):
"""表达学习规则""" """表达学习规则"""
@@ -653,7 +652,8 @@ class ContextGroup(ValidatedConfigBase):
name: str = Field(..., description="共享组的名称") name: str = Field(..., description="共享组的名称")
chat_ids: List[List[str]] = Field( chat_ids: List[List[str]] = Field(
..., description='属于该组的聊天ID列表格式为 [["type", "chat_id"], ...],例如 [["group", "123456"], ["private", "789012"]]' ...,
description='属于该组的聊天ID列表格式为 [["type", "chat_id"], ...],例如 [["group", "123456"], ["private", "789012"]]',
) )

View File

@@ -30,28 +30,49 @@ from src.plugin_system.core.plugin_hot_reload import hot_reload_manager
from src.common.message import get_global_api from src.common.message import get_global_api
from src.chat.memory_system.Hippocampus import hippocampus_manager from src.chat.memory_system.Hippocampus import hippocampus_manager
if not global_config.memory.enable_memory: if not global_config.memory.enable_memory:
import src.chat.memory_system.Hippocampus as hippocampus_module import src.chat.memory_system.Hippocampus as hippocampus_module
class MockHippocampusManager: class MockHippocampusManager:
def initialize(self): def initialize(self):
pass pass
def get_hippocampus(self): def get_hippocampus(self):
return None return None
async def build_memory(self): async def build_memory(self):
pass pass
async def forget_memory(self, percentage: float = 0.005): async def forget_memory(self, percentage: float = 0.005):
pass pass
async def consolidate_memory(self): async def consolidate_memory(self):
pass pass
async def get_memory_from_text(self, text: str, max_memory_num: int = 3, max_memory_length: int = 2, max_depth: int = 3, fast_retrieval: bool = False) -> list:
async def get_memory_from_text(
self,
text: str,
max_memory_num: int = 3,
max_memory_length: int = 2,
max_depth: int = 3,
fast_retrieval: bool = False,
) -> list:
return [] return []
async def get_memory_from_topic(self, valid_keywords: list[str], max_memory_num: int = 3, max_memory_length: int = 2, max_depth: int = 3) -> list:
async def get_memory_from_topic(
self, valid_keywords: list[str], max_memory_num: int = 3, max_memory_length: int = 2, max_depth: int = 3
) -> list:
return [] return []
async def get_activate_from_text(self, text: str, max_depth: int = 3, fast_retrieval: bool = False) -> tuple[float, list[str]]:
async def get_activate_from_text(
self, text: str, max_depth: int = 3, fast_retrieval: bool = False
) -> tuple[float, list[str]]:
return 0.0, [] return 0.0, []
def get_memory_from_keyword(self, keyword: str, max_depth: int = 2) -> list: def get_memory_from_keyword(self, keyword: str, max_depth: int = 2) -> list:
return [] return []
def get_all_node_names(self) -> list: def get_all_node_names(self) -> list:
return [] return []
@@ -207,7 +228,6 @@ MoFox_Bot(第三方修改版)
get_emoji_manager().initialize() get_emoji_manager().initialize()
logger.info("表情包管理器初始化成功") logger.info("表情包管理器初始化成功")
# 启动情绪管理器 # 启动情绪管理器
await mood_manager.start() await mood_manager.start()
logger.info("情绪管理器初始化成功") logger.info("情绪管理器初始化成功")

View File

@@ -36,27 +36,19 @@ def get_context_groups(chat_id: str) -> Optional[List[List[str]]]:
# 检查当前聊天的ID和类型是否在组的chat_ids中 # 检查当前聊天的ID和类型是否在组的chat_ids中
if [current_type, str(current_chat_raw_id)] in group.chat_ids: if [current_type, str(current_chat_raw_id)] in group.chat_ids:
# 返回组内其他聊天的 [type, id] 列表 # 返回组内其他聊天的 [type, id] 列表
return [ return [chat_info for chat_info in group.chat_ids if chat_info != [current_type, str(current_chat_raw_id)]]
chat_info
for chat_info in group.chat_ids
if chat_info != [current_type, str(current_chat_raw_id)]
]
return None return None
async def build_cross_context_normal( async def build_cross_context_normal(chat_stream: ChatStream, other_chat_infos: List[List[str]]) -> str:
chat_stream: ChatStream, other_chat_infos: List[List[str]]
) -> str:
""" """
构建跨群聊/私聊上下文 (Normal模式) 构建跨群聊/私聊上下文 (Normal模式)
""" """
cross_context_messages = [] cross_context_messages = []
for chat_type, chat_raw_id in other_chat_infos: for chat_type, chat_raw_id in other_chat_infos:
is_group = chat_type == "group" is_group = chat_type == "group"
stream_id = get_chat_manager().get_stream_id( stream_id = get_chat_manager().get_stream_id(chat_stream.platform, chat_raw_id, is_group=is_group)
chat_stream.platform, chat_raw_id, is_group=is_group
)
if not stream_id: if not stream_id:
continue continue
@@ -68,9 +60,7 @@ async def build_cross_context_normal(
) )
if messages: if messages:
chat_name = get_chat_manager().get_stream_name(stream_id) or chat_raw_id chat_name = get_chat_manager().get_stream_name(stream_id) or chat_raw_id
formatted_messages, _ = build_readable_messages_with_id( formatted_messages, _ = build_readable_messages_with_id(messages, timestamp_mode="relative")
messages, timestamp_mode="relative"
)
cross_context_messages.append(f'[以下是来自"{chat_name}"的近期消息]\n{formatted_messages}') cross_context_messages.append(f'[以下是来自"{chat_name}"的近期消息]\n{formatted_messages}')
except Exception as e: except Exception as e:
logger.error(f"获取聊天 {chat_raw_id} 的消息失败: {e}") logger.error(f"获取聊天 {chat_raw_id} 的消息失败: {e}")
@@ -97,9 +87,7 @@ async def build_cross_context_s4u(
if user_id: if user_id:
for chat_type, chat_raw_id in other_chat_infos: for chat_type, chat_raw_id in other_chat_infos:
is_group = chat_type == "group" is_group = chat_type == "group"
stream_id = get_chat_manager().get_stream_id( stream_id = get_chat_manager().get_stream_id(chat_stream.platform, chat_raw_id, is_group=is_group)
chat_stream.platform, chat_raw_id, is_group=is_group
)
if not stream_id: if not stream_id:
continue continue
@@ -114,9 +102,7 @@ async def build_cross_context_s4u(
if user_messages: if user_messages:
chat_name = get_chat_manager().get_stream_name(stream_id) or chat_raw_id chat_name = get_chat_manager().get_stream_name(stream_id) or chat_raw_id
user_name = ( user_name = (
target_user_info.get("person_name") target_user_info.get("person_name") or target_user_info.get("user_nickname") or user_id
or target_user_info.get("user_nickname")
or user_id
) )
formatted_messages, _ = build_readable_messages_with_id( formatted_messages, _ = build_readable_messages_with_id(
user_messages, timestamp_mode="relative" user_messages, timestamp_mode="relative"
@@ -182,9 +168,7 @@ async def get_chat_history_by_group_name(group_name: str) -> str:
) )
if messages: if messages:
chat_name = get_chat_manager().get_stream_name(stream_id) or chat_raw_id chat_name = get_chat_manager().get_stream_name(stream_id) or chat_raw_id
formatted_messages, _ = build_readable_messages_with_id( formatted_messages, _ = build_readable_messages_with_id(messages, timestamp_mode="relative")
messages, timestamp_mode="relative"
)
cross_context_messages.append(f'[以下是来自"{chat_name}"的近期消息]\n{formatted_messages}') cross_context_messages.append(f'[以下是来自"{chat_name}"的近期消息]\n{formatted_messages}')
except Exception as e: except Exception as e:
logger.error(f"获取聊天 {chat_raw_id} 的消息失败: {e}") logger.error(f"获取聊天 {chat_raw_id} 的消息失败: {e}")

View File

@@ -107,9 +107,7 @@ async def generate_reply(
""" """
try: try:
# 获取回复器 # 获取回复器
replyer = get_replyer( replyer = get_replyer(chat_stream, chat_id, request_type=request_type)
chat_stream, chat_id, request_type=request_type
)
if not replyer: if not replyer:
logger.error("[GeneratorAPI] 无法获取回复器") logger.error("[GeneratorAPI] 无法获取回复器")
return False, [], None return False, [], None

View File

@@ -51,6 +51,7 @@ logger = get_logger("send_api")
# 适配器命令响应等待池 # 适配器命令响应等待池
_adapter_response_pool: Dict[str, asyncio.Future] = {} _adapter_response_pool: Dict[str, asyncio.Future] = {}
def message_dict_to_message_recv(message_dict: Dict[str, Any]) -> Optional[MessageRecv]: def message_dict_to_message_recv(message_dict: Dict[str, Any]) -> Optional[MessageRecv]:
"""查找要回复的消息 """查找要回复的消息
@@ -101,6 +102,7 @@ def message_dict_to_message_recv(message_dict: Dict[str, Any]) -> Optional[Messa
logger.info(f"[SendAPI] 找到匹配的回复消息,发送者: {message_dict.get('user_nickname', '')}") logger.info(f"[SendAPI] 找到匹配的回复消息,发送者: {message_dict.get('user_nickname', '')}")
return message_recv return message_recv
def put_adapter_response(request_id: str, response_data: dict) -> None: def put_adapter_response(request_id: str, response_data: dict) -> None:
"""将适配器响应放入响应池""" """将适配器响应放入响应池"""
if request_id in _adapter_response_pool: if request_id in _adapter_response_pool:
@@ -234,7 +236,6 @@ async def _send_to_target(
return False return False
# ============================================================================= # =============================================================================
# 公共API函数 - 预定义类型的发送函数 # 公共API函数 - 预定义类型的发送函数
# ============================================================================= # =============================================================================
@@ -274,7 +275,9 @@ async def text_to_stream(
) )
async def emoji_to_stream(emoji_base64: str, stream_id: str, storage_message: bool = True, set_reply: bool = False) -> bool: async def emoji_to_stream(
emoji_base64: str, stream_id: str, storage_message: bool = True, set_reply: bool = False
) -> bool:
"""向指定流发送表情包 """向指定流发送表情包
Args: Args:
@@ -285,10 +288,14 @@ async def emoji_to_stream(emoji_base64: str, stream_id: str, storage_message: bo
Returns: Returns:
bool: 是否发送成功 bool: 是否发送成功
""" """
return await _send_to_target("emoji", emoji_base64, stream_id, "", typing=False, storage_message=storage_message, set_reply=set_reply) return await _send_to_target(
"emoji", emoji_base64, stream_id, "", typing=False, storage_message=storage_message, set_reply=set_reply
)
async def image_to_stream(image_base64: str, stream_id: str, storage_message: bool = True, set_reply: bool = False) -> bool: async def image_to_stream(
image_base64: str, stream_id: str, storage_message: bool = True, set_reply: bool = False
) -> bool:
"""向指定流发送图片 """向指定流发送图片
Args: Args:
@@ -299,11 +306,17 @@ async def image_to_stream(image_base64: str, stream_id: str, storage_message: bo
Returns: Returns:
bool: 是否发送成功 bool: 是否发送成功
""" """
return await _send_to_target("image", image_base64, stream_id, "", typing=False, storage_message=storage_message, set_reply=set_reply) return await _send_to_target(
"image", image_base64, stream_id, "", typing=False, storage_message=storage_message, set_reply=set_reply
)
async def command_to_stream( async def command_to_stream(
command: Union[str, dict], stream_id: str, storage_message: bool = True, display_message: str = "", set_reply: bool = False command: Union[str, dict],
stream_id: str,
storage_message: bool = True,
display_message: str = "",
set_reply: bool = False,
) -> bool: ) -> bool:
"""向指定流发送命令 """向指定流发送命令

View File

@@ -53,7 +53,9 @@ class MaiZoneRefactoredPlugin(BasePlugin):
"enable_reply": ConfigField(type=bool, default=True, description="完成后是否回复"), "enable_reply": ConfigField(type=bool, default=True, description="完成后是否回复"),
"ai_image_number": ConfigField(type=int, default=1, description="AI生成图片数量"), "ai_image_number": ConfigField(type=int, default=1, description="AI生成图片数量"),
"image_number": ConfigField(type=int, default=1, description="本地配图数量1-9张"), "image_number": ConfigField(type=int, default=1, description="本地配图数量1-9张"),
"image_directory": ConfigField(type=str, default=(Path(__file__).parent / "images").as_posix(), description="图片存储目录") "image_directory": ConfigField(
type=str, default=(Path(__file__).parent / "images").as_posix(), description="图片存储目录"
),
}, },
"read": { "read": {
"permission": ConfigField(type=list, default=[], description="阅读权限QQ号列表"), "permission": ConfigField(type=list, default=[], description="阅读权限QQ号列表"),
@@ -75,7 +77,9 @@ class MaiZoneRefactoredPlugin(BasePlugin):
"forbidden_hours_end": ConfigField(type=int, default=6, description="禁止发送的结束小时(24小时制)"), "forbidden_hours_end": ConfigField(type=int, default=6, description="禁止发送的结束小时(24小时制)"),
}, },
"cookie": { "cookie": {
"http_fallback_host": ConfigField(type=str, default="127.0.0.1", description="备用Cookie获取服务的主机地址"), "http_fallback_host": ConfigField(
type=str, default="127.0.0.1", description="备用Cookie获取服务的主机地址"
),
"http_fallback_port": ConfigField(type=int, default=9999, description="备用Cookie获取服务的端口"), "http_fallback_port": ConfigField(type=int, default=9999, description="备用Cookie获取服务的端口"),
"napcat_token": ConfigField(type=str, default="", description="Napcat服务的认证Token可选"), "napcat_token": ConfigField(type=str, default="", description="Napcat服务的认证Token可选"),
}, },
@@ -102,7 +106,7 @@ class MaiZoneRefactoredPlugin(BasePlugin):
content_service, content_service,
image_service, image_service,
cookie_service, cookie_service,
reply_tracker_service # 传入已创建的实例 reply_tracker_service, # 传入已创建的实例
) )
scheduler_service = SchedulerService(self.get_config, qzone_service) scheduler_service = SchedulerService(self.get_config, qzone_service)
monitor_service = MonitorService(self.get_config, qzone_service) monitor_service = MonitorService(self.get_config, qzone_service)

View File

@@ -272,8 +272,10 @@ class QZoneService:
# 检查是否已经在持久化记录中标记为已回复 # 检查是否已经在持久化记录中标记为已回复
if not self.reply_tracker.has_replied(fid, comment_tid): if not self.reply_tracker.has_replied(fid, comment_tid):
# 记录日志以便追踪 # 记录日志以便追踪
logger.debug(f"发现新评论需要回复 - 说说ID: {fid}, 评论ID: {comment_tid}, " logger.debug(
f"评论人: {comment.get('nickname', '')}, 内容: {comment.get('content', '')}") f"发现新评论需要回复 - 说说ID: {fid}, 评论ID: {comment_tid}, "
f"评论人: {comment.get('nickname', '')}, 内容: {comment.get('content', '')}"
)
comments_to_reply.append(comment) comments_to_reply.append(comment)
if not comments_to_reply: if not comments_to_reply:

View File

@@ -74,8 +74,10 @@ class ReplyTrackerService:
data = json.loads(file_content) data = json.loads(file_content)
if self._validate_data(data): if self._validate_data(data):
self.replied_comments = data self.replied_comments = data
logger.info(f"已加载 {len(self.replied_comments)} 条说说的回复记录," logger.info(
f"总计 {sum(len(comments) for comments in self.replied_comments.values())}评论") f"已加载 {len(self.replied_comments)}说说的回复记录,"
f"总计 {sum(len(comments) for comments in self.replied_comments.values())} 条评论"
)
else: else:
logger.error("加载的数据格式无效,将创建新的记录") logger.error("加载的数据格式无效,将创建新的记录")
self.replied_comments = {} self.replied_comments = {}
@@ -112,7 +114,7 @@ class ReplyTrackerService:
self._cleanup_old_records() self._cleanup_old_records()
# 创建临时文件 # 创建临时文件
temp_file = self.reply_record_file.with_suffix('.tmp') temp_file = self.reply_record_file.with_suffix(".tmp")
# 先写入临时文件 # 先写入临时文件
with open(temp_file, "w", encoding="utf-8") as f: with open(temp_file, "w", encoding="utf-8") as f:

View File

@@ -291,6 +291,8 @@ class MonthlyPlanManager:
except Exception as e: except Exception as e:
logger.error(f" 归档 {target_month} 月度计划时发生错误: {e}") logger.error(f" 归档 {target_month} 月度计划时发生错误: {e}")
class MonthlyPlanGenerationTask(AsyncTask): class MonthlyPlanGenerationTask(AsyncTask):
"""每月初自动生成新月度计划的任务""" """每月初自动生成新月度计划的任务"""

View File

@@ -165,7 +165,9 @@ class ScheduleManager:
schedule_str = f"已成功加载今天的日程 ({today_str})\n" schedule_str = f"已成功加载今天的日程 ({today_str})\n"
if self.today_schedule: if self.today_schedule:
for item in self.today_schedule: for item in self.today_schedule:
schedule_str += f" - {item.get('time_range', '未知时间')}: {item.get('activity', '未知活动')}\n" schedule_str += (
f" - {item.get('time_range', '未知时间')}: {item.get('activity', '未知活动')}\n"
)
logger.info(schedule_str) logger.info(schedule_str)
return # 成功加载,直接返回 return # 成功加载,直接返回
else: else: