feat:新增表情包发送动作

This commit is contained in:
SengokuCola
2025-06-02 18:56:11 +08:00
parent d957aef202
commit b068b6a31e
4 changed files with 187 additions and 3 deletions

View File

@@ -395,7 +395,7 @@ class DefaultExpressor:
thinking_start_time = time.time() thinking_start_time = time.time()
if thinking_start_time is None: if thinking_start_time is None:
logger.error(f"[{stream_name}]思考过程未找到或已结束,无法发送回复。") logger.error(f"[{stream_name}]expressor思考过程未找到或已结束,无法发送回复。")
return None return None
mark_head = False mark_head = False

View File

@@ -2,5 +2,6 @@
from . import reply_action # noqa from . import reply_action # noqa
from . import no_reply_action # noqa from . import no_reply_action # noqa
from . import exit_focus_chat_action # noqa from . import exit_focus_chat_action # noqa
from . import emoji_action # noqa
# 在此处添加更多动作模块导入 # 在此处添加更多动作模块导入

View File

@@ -0,0 +1,135 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from src.common.logger_manager import get_logger
from src.chat.focus_chat.planners.actions.base_action import BaseAction, register_action
from typing import Tuple, List
from src.chat.heart_flow.observation.observation import Observation
from src.chat.focus_chat.replyer.default_replyer import DefaultReplyer
from src.chat.message_receive.chat_stream import ChatStream
from src.chat.heart_flow.observation.chatting_observation import ChattingObservation
from src.chat.focus_chat.hfc_utils import create_empty_anchor_message
logger = get_logger("action_taken")
@register_action
class EmojiAction(BaseAction):
"""表情动作处理类
处理构建和发送消息表情的动作。
"""
action_name: str = "emoji"
action_description: str = "当你想发送一个表情"
action_parameters: dict[str:str] = {
"description": "描述你想要发送的表情",
}
action_require: list[str] = [
"你想要发送一个表情",
"表达情绪时可以选择使用",
]
associated_types: list[str] = ["emoji"]
default = True
def __init__(
self,
action_data: dict,
reasoning: str,
cycle_timers: dict,
thinking_id: str,
observations: List[Observation],
chat_stream: ChatStream,
log_prefix: str,
replyer: DefaultReplyer,
**kwargs,
):
"""初始化回复动作处理器
Args:
action_name: 动作名称
action_data: 动作数据,包含 message, emojis, target 等
reasoning: 执行该动作的理由
cycle_timers: 计时器字典
thinking_id: 思考ID
observations: 观察列表
replyer: 回复器
chat_stream: 聊天流
log_prefix: 日志前缀
"""
super().__init__(action_data, reasoning, cycle_timers, thinking_id)
self.observations = observations
self.replyer = replyer
self.chat_stream = chat_stream
self.log_prefix = log_prefix
async def handle_action(self) -> Tuple[bool, str]:
"""
处理回复动作
Returns:
Tuple[bool, str]: (是否执行成功, 回复文本)
"""
# 注意: 此处可能会使用不同的expressor实现根据任务类型切换不同的回复策略
return await self._handle_reply(
reasoning=self.reasoning,
reply_data=self.action_data,
cycle_timers=self.cycle_timers,
thinking_id=self.thinking_id,
)
async def _handle_reply(
self, reasoning: str, reply_data: dict, cycle_timers: dict, thinking_id: str
) -> tuple[bool, str]:
"""
处理统一的回复动作 - 可包含文本和表情,顺序任意
reply_data格式:
{
"description": "描述你想要发送的表情"
}
"""
logger.info(f"{self.log_prefix} 决定发送表情: {self.reasoning}")
# 从聊天观察获取锚定消息
# chatting_observation: ChattingObservation = next(
# obs for obs in self.observations if isinstance(obs, ChattingObservation)
# )
# if reply_data.get("target"):
# anchor_message = chatting_observation.search_message_by_text(reply_data["target"])
# else:
# anchor_message = None
# 如果没有找到锚点消息,创建一个占位符
# if not anchor_message:
# logger.info(f"{self.log_prefix} 未找到锚点消息,创建占位符")
# anchor_message = await create_empty_anchor_message(
# self.chat_stream.platform, self.chat_stream.group_info, self.chat_stream
# )
# else:
# anchor_message.update_chat_stream(self.chat_stream)
logger.info(f"{self.log_prefix} 为了表情包创建占位符")
anchor_message = await create_empty_anchor_message(
self.chat_stream.platform, self.chat_stream.group_info, self.chat_stream
)
success, reply_set = await self.replyer.deal_emoji(
cycle_timers=cycle_timers,
action_data=reply_data,
anchor_message=anchor_message,
# reasoning=reasoning,
thinking_id=thinking_id,
)
reply_text = ""
for reply in reply_set:
type = reply[0]
data = reply[1]
if type == "text":
reply_text += data
elif type == "emoji":
reply_text += data
return success, reply_text

View File

@@ -177,6 +177,52 @@ class DefaultReplyer:
return False, None return False, None
# --- 回复器 (Replier) 的定义 --- # # --- 回复器 (Replier) 的定义 --- #
async def deal_emoji(
self,
anchor_message: MessageRecv,
thinking_id: str,
action_data: Dict[str, Any],
cycle_timers: dict,
) -> Optional[List[str]]:
"""
表情动作处理类
"""
await self._create_thinking_message(anchor_message, thinking_id)
try:
reply = []
with Timer("选择表情", cycle_timers):
emoji_keyword = action_data.get("description", [])
emoji_base64 = await self._choose_emoji(emoji_keyword)
if emoji_base64:
reply.append(("emoji", emoji_base64))
if reply:
with Timer("发送表情", cycle_timers):
sent_msg_list = await self.send_response_messages(
anchor_message=anchor_message,
thinking_id=thinking_id,
response_set=reply,
)
has_sent_something = True
else:
logger.warning(f"{self.log_prefix} 表情发送失败")
if not has_sent_something:
logger.warning(f"{self.log_prefix} 表情发送失败")
return has_sent_something, sent_msg_list
except Exception as e:
logger.error(f"回复失败: {e}")
traceback.print_exc()
return False, None
async def reply( async def reply(
self, self,
@@ -426,13 +472,15 @@ class DefaultReplyer:
# 检查思考过程是否仍在进行,并获取开始时间 # 检查思考过程是否仍在进行,并获取开始时间
if thinking_id: if thinking_id:
# print(f"thinking_id: {thinking_id}")
thinking_start_time = await self.heart_fc_sender.get_thinking_start_time(chat_id, thinking_id) thinking_start_time = await self.heart_fc_sender.get_thinking_start_time(chat_id, thinking_id)
else: else:
thinking_id = "ds" + str(round(time.time(), 2)) print("thinking_id is None")
# thinking_id = "ds" + str(round(time.time(), 2))
thinking_start_time = time.time() thinking_start_time = time.time()
if thinking_start_time is None: if thinking_start_time is None:
logger.error(f"[{stream_name}]思考过程未找到或已结束,无法发送回复。") logger.error(f"[{stream_name}]replyer思考过程未找到或已结束,无法发送回复。")
return None return None
mark_head = False mark_head = False