mirror of https://github.com/Mai-with-u/MaiBot.git
137 lines
5.1 KiB
Python
137 lines
5.1 KiB
Python
import asyncio
|
|
import traceback
|
|
from src.common.logger_manager import get_logger
|
|
from src.chat.utils.timer_calculator import Timer
|
|
from src.chat.focus_chat.planners.actions.base_action import BaseAction, register_action, ActionActivationType
|
|
from typing import Tuple, List
|
|
from src.chat.heart_flow.observation.observation import Observation
|
|
from src.chat.heart_flow.observation.chatting_observation import ChattingObservation
|
|
from src.chat.focus_chat.hfc_utils import parse_thinking_id_to_timestamp
|
|
|
|
logger = get_logger("action_taken")
|
|
|
|
# 常量定义
|
|
WAITING_TIME_THRESHOLD = 1200 # 等待新消息时间阈值,单位秒
|
|
|
|
|
|
@register_action
|
|
class NoReplyAction(BaseAction):
|
|
"""不回复动作处理类
|
|
|
|
处理决定不回复的动作。
|
|
"""
|
|
|
|
action_name = "no_reply"
|
|
action_description = "暂时不回复消息"
|
|
action_parameters = {}
|
|
action_require = [
|
|
"你连续发送了太多消息,且无人回复",
|
|
"想要休息一下",
|
|
]
|
|
default = True
|
|
|
|
# 激活类型设置
|
|
action_activation_type = ActionActivationType.ALWAYS
|
|
|
|
def __init__(
|
|
self,
|
|
action_data: dict,
|
|
reasoning: str,
|
|
cycle_timers: dict,
|
|
thinking_id: str,
|
|
observations: List[Observation],
|
|
log_prefix: str,
|
|
shutting_down: bool = False,
|
|
**kwargs,
|
|
):
|
|
"""初始化不回复动作处理器
|
|
|
|
Args:
|
|
action_name: 动作名称
|
|
action_data: 动作数据
|
|
reasoning: 执行该动作的理由
|
|
cycle_timers: 计时器字典
|
|
thinking_id: 思考ID
|
|
observations: 观察列表
|
|
log_prefix: 日志前缀
|
|
shutting_down: 是否正在关闭
|
|
"""
|
|
super().__init__(action_data, reasoning, cycle_timers, thinking_id)
|
|
self.observations = observations
|
|
self.log_prefix = log_prefix
|
|
self._shutting_down = shutting_down
|
|
|
|
async def handle_action(self) -> Tuple[bool, str]:
|
|
"""
|
|
处理不回复的情况
|
|
|
|
工作流程:
|
|
1. 等待新消息、超时或关闭信号
|
|
2. 根据等待结果更新连续不回复计数
|
|
3. 如果达到阈值,触发回调
|
|
|
|
Returns:
|
|
Tuple[bool, str]: (是否执行成功, 空字符串)
|
|
"""
|
|
logger.info(f"{self.log_prefix} 决定不回复: {self.reasoning}")
|
|
|
|
observation = self.observations[0] if self.observations else None
|
|
|
|
try:
|
|
with Timer("等待新消息", self.cycle_timers):
|
|
# 等待新消息、超时或关闭信号,并获取结果
|
|
await self._wait_for_new_message(observation, self.thinking_id, self.log_prefix)
|
|
|
|
return True, "" # 不回复动作没有回复文本
|
|
|
|
except asyncio.CancelledError:
|
|
logger.info(f"{self.log_prefix} 处理 'no_reply' 时等待被中断 (CancelledError)")
|
|
raise
|
|
except Exception as e: # 捕获调用管理器或其他地方可能发生的错误
|
|
logger.error(f"{self.log_prefix} 处理 'no_reply' 时发生错误: {e}")
|
|
logger.error(traceback.format_exc())
|
|
return False, ""
|
|
|
|
async def _wait_for_new_message(self, observation: ChattingObservation, thinking_id: str, log_prefix: str) -> bool:
|
|
"""
|
|
等待新消息 或 检测到关闭信号
|
|
|
|
参数:
|
|
observation: 观察实例
|
|
thinking_id: 思考ID
|
|
log_prefix: 日志前缀
|
|
|
|
返回:
|
|
bool: 是否检测到新消息 (如果因关闭信号退出则返回 False)
|
|
"""
|
|
wait_start_time = asyncio.get_event_loop().time()
|
|
while True:
|
|
# --- 在每次循环开始时检查关闭标志 ---
|
|
if self._shutting_down:
|
|
logger.info(f"{log_prefix} 等待新消息时检测到关闭信号,中断等待。")
|
|
return False # 表示因为关闭而退出
|
|
# -----------------------------------
|
|
|
|
thinking_id_timestamp = parse_thinking_id_to_timestamp(thinking_id)
|
|
|
|
# 检查新消息
|
|
if await observation.has_new_messages_since(thinking_id_timestamp):
|
|
logger.info(f"{log_prefix} 检测到新消息")
|
|
return True
|
|
|
|
# 检查超时 (放在检查新消息和关闭之后)
|
|
if asyncio.get_event_loop().time() - wait_start_time > WAITING_TIME_THRESHOLD:
|
|
logger.warning(f"{log_prefix} 等待新消息超时({WAITING_TIME_THRESHOLD}秒)")
|
|
return False
|
|
|
|
try:
|
|
# 短暂休眠,让其他任务有机会运行,并能更快响应取消或关闭
|
|
await asyncio.sleep(0.5) # 缩短休眠时间
|
|
except asyncio.CancelledError:
|
|
# 如果在休眠时被取消,再次检查关闭标志
|
|
# 如果是正常关闭,则不需要警告
|
|
if not self._shutting_down:
|
|
logger.warning(f"{log_prefix} _wait_for_new_message 的休眠被意外取消")
|
|
# 无论如何,重新抛出异常,让上层处理
|
|
raise
|