From 92613792bd9cbec8bdd9190bf50625dfcedaf0f5 Mon Sep 17 00:00:00 2001 From: A0000Xz <122650088+A0000Xz@users.noreply.github.com> Date: Fri, 13 Jun 2025 23:52:55 +0800 Subject: [PATCH 1/5] Add files via upload --- src/chat/normal_chat/normal_chat_expressor.py | 521 +++++++++--------- 1 file changed, 260 insertions(+), 261 deletions(-) diff --git a/src/chat/normal_chat/normal_chat_expressor.py b/src/chat/normal_chat/normal_chat_expressor.py index d49b1228a..416376b74 100644 --- a/src/chat/normal_chat/normal_chat_expressor.py +++ b/src/chat/normal_chat/normal_chat_expressor.py @@ -1,261 +1,260 @@ -""" -Normal Chat Expressor - -为Normal Chat专门设计的表达器,不需要经过LLM风格化处理, -直接发送消息,主要用于插件动作中需要发送消息的场景。 -""" - -import time -from typing import List, Optional, Tuple, Dict, Any -from src.chat.message_receive.message import MessageRecv, MessageSending, MessageThinking, Seg -from src.chat.message_receive.message import UserInfo -from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager -from src.chat.message_receive.message_sender import message_manager -from src.config.config import global_config -from src.common.logger import get_logger - -logger = get_logger("normal_chat_expressor") - - -class NormalChatExpressor: - """Normal Chat专用表达器 - - 特点: - 1. 不经过LLM风格化,直接发送消息 - 2. 支持文本和表情包发送 - 3. 为插件动作提供简化的消息发送接口 - 4. 保持与focus_chat expressor相似的API,但去掉复杂的风格化流程 - """ - - def __init__(self, chat_stream: ChatStream): - """初始化Normal Chat表达器 - - Args: - chat_stream: 聊天流对象 - stream_name: 流名称 - """ - self.chat_stream = chat_stream - self.stream_name = get_chat_manager().get_stream_name(self.chat_stream.stream_id) or self.chat_stream.stream_id - self.log_prefix = f"[{self.stream_name}]Normal表达器" - - logger.debug(f"{self.log_prefix} 初始化完成") - - async def create_thinking_message( - self, anchor_message: Optional[MessageRecv], thinking_id: str - ) -> Optional[MessageThinking]: - """创建思考消息 - - Args: - anchor_message: 锚点消息 - thinking_id: 思考ID - - Returns: - MessageThinking: 创建的思考消息,如果失败返回None - """ - if not anchor_message or not anchor_message.chat_stream: - logger.error(f"{self.log_prefix} 无法创建思考消息,缺少有效的锚点消息或聊天流") - return None - - messageinfo = anchor_message.message_info - thinking_time_point = time.time() - - bot_user_info = UserInfo( - user_id=global_config.bot.qq_account, - user_nickname=global_config.bot.nickname, - platform=messageinfo.platform, - ) - - thinking_message = MessageThinking( - message_id=thinking_id, - chat_stream=self.chat_stream, - bot_user_info=bot_user_info, - reply=anchor_message, - thinking_start_time=thinking_time_point, - ) - - await message_manager.add_message(thinking_message) - logger.debug(f"{self.log_prefix} 创建思考消息: {thinking_id}") - return thinking_message - - async def send_response_messages( - self, - anchor_message: Optional[MessageRecv], - response_set: List[Tuple[str, str]], - thinking_id: str = "", - display_message: str = "", - ) -> Optional[MessageSending]: - """发送回复消息 - - Args: - anchor_message: 锚点消息 - response_set: 回复内容集合,格式为 [(type, content), ...] - thinking_id: 思考ID - display_message: 显示消息 - - Returns: - MessageSending: 发送的第一条消息,如果失败返回None - """ - try: - if not response_set: - logger.warning(f"{self.log_prefix} 回复内容为空") - return None - - # 如果没有thinking_id,生成一个 - if not thinking_id: - thinking_time_point = round(time.time(), 2) - thinking_id = "mt" + str(thinking_time_point) - - # 创建思考消息 - if anchor_message: - await self.create_thinking_message(anchor_message, thinking_id) - - # 创建消息集 - - first_bot_msg = None - mark_head = False - is_emoji = False - if len(response_set) == 0: - return None - message_id = f"{thinking_id}_{len(response_set)}" - response_type, content = response_set[0] - if len(response_set) > 1: - message_segment = Seg(type="seglist", data=[Seg(type=t, data=c) for t, c in response_set]) - else: - message_segment = Seg(type=response_type, data=content) - if response_type == "emoji": - is_emoji = True - - bot_msg = await self._build_sending_message( - message_id=message_id, - message_segment=message_segment, - thinking_id=thinking_id, - anchor_message=anchor_message, - thinking_start_time=time.time(), - reply_to=mark_head, - is_emoji=is_emoji, - display_message=display_message, - ) - logger.debug(f"{self.log_prefix} 添加{response_type}类型消息: {content}") - - # 提交消息集 - if bot_msg: - await message_manager.add_message(bot_msg) - logger.info(f"{self.log_prefix} 成功发送 {response_type}类型消息: {content}") - container = await message_manager.get_container(self.chat_stream.stream_id) # 使用 self.stream_id - for msg in container.messages[:]: - if isinstance(msg, MessageThinking) and msg.message_info.message_id == thinking_id: - container.messages.remove(msg) - logger.debug(f"[{self.stream_name}] 已移除未产生回复的思考消息 {thinking_id}") - break - return first_bot_msg - else: - logger.warning(f"{self.log_prefix} 没有有效的消息被创建") - return None - - except Exception as e: - logger.error(f"{self.log_prefix} 发送消息失败: {e}") - import traceback - - traceback.print_exc() - return None - - async def _build_sending_message( - self, - message_id: str, - message_segment: Seg, - thinking_id: str, - anchor_message: Optional[MessageRecv], - thinking_start_time: float, - reply_to: bool = False, - is_emoji: bool = False, - display_message: str = "", - ) -> MessageSending: - """构建发送消息 - - Args: - message_id: 消息ID - message_segment: 消息段 - thinking_id: 思考ID - anchor_message: 锚点消息 - thinking_start_time: 思考开始时间 - reply_to: 是否回复 - is_emoji: 是否为表情包 - - Returns: - MessageSending: 构建的发送消息 - """ - bot_user_info = UserInfo( - user_id=global_config.bot.qq_account, - user_nickname=global_config.bot.nickname, - platform=anchor_message.message_info.platform if anchor_message else "unknown", - ) - - message_sending = MessageSending( - message_id=message_id, - chat_stream=self.chat_stream, - bot_user_info=bot_user_info, - message_segment=message_segment, - sender_info=self.chat_stream.user_info, - reply=anchor_message if reply_to else None, - thinking_start_time=thinking_start_time, - is_emoji=is_emoji, - display_message=display_message, - ) - - return message_sending - - async def deal_reply( - self, - cycle_timers: dict, - action_data: Dict[str, Any], - reasoning: str, - anchor_message: MessageRecv, - thinking_id: str, - ) -> Tuple[bool, Optional[str]]: - """处理回复动作 - 兼容focus_chat expressor API - - Args: - cycle_timers: 周期计时器(normal_chat中不使用) - action_data: 动作数据,包含text、target、emojis等 - reasoning: 推理说明 - anchor_message: 锚点消息 - thinking_id: 思考ID - - Returns: - Tuple[bool, Optional[str]]: (是否成功, 回复文本) - """ - try: - response_set = [] - - # 处理文本内容 - text_content = action_data.get("text", "") - if text_content: - response_set.append(("text", text_content)) - - # 处理表情包 - emoji_content = action_data.get("emojis", "") - if emoji_content: - response_set.append(("emoji", emoji_content)) - - if not response_set: - logger.warning(f"{self.log_prefix} deal_reply: 没有有效的回复内容") - return False, None - - # 发送消息 - result = await self.send_response_messages( - anchor_message=anchor_message, - response_set=response_set, - thinking_id=thinking_id, - ) - - if result: - return True, text_content if text_content else "发送成功" - else: - return False, None - - except Exception as e: - logger.error(f"{self.log_prefix} deal_reply执行失败: {e}") - import traceback - - traceback.print_exc() - return False, None +""" +Normal Chat Expressor + +为Normal Chat专门设计的表达器,不需要经过LLM风格化处理, +直接发送消息,主要用于插件动作中需要发送消息的场景。 +""" + +import time +from typing import List, Optional, Tuple, Dict, Any +from src.chat.message_receive.message import MessageRecv, MessageSending, MessageThinking, Seg +from src.chat.message_receive.message import UserInfo +from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager +from src.chat.message_receive.message_sender import message_manager +from src.config.config import global_config +from src.common.logger import get_logger + +logger = get_logger("normal_chat_expressor") + + +class NormalChatExpressor: + """Normal Chat专用表达器 + + 特点: + 1. 不经过LLM风格化,直接发送消息 + 2. 支持文本和表情包发送 + 3. 为插件动作提供简化的消息发送接口 + 4. 保持与focus_chat expressor相似的API,但去掉复杂的风格化流程 + """ + + def __init__(self, chat_stream: ChatStream): + """初始化Normal Chat表达器 + + Args: + chat_stream: 聊天流对象 + stream_name: 流名称 + """ + self.chat_stream = chat_stream + self.stream_name = get_chat_manager().get_stream_name(self.chat_stream.stream_id) or self.chat_stream.stream_id + self.log_prefix = f"[{self.stream_name}]Normal表达器" + + logger.debug(f"{self.log_prefix} 初始化完成") + + async def create_thinking_message( + self, anchor_message: Optional[MessageRecv], thinking_id: str + ) -> Optional[MessageThinking]: + """创建思考消息 + + Args: + anchor_message: 锚点消息 + thinking_id: 思考ID + + Returns: + MessageThinking: 创建的思考消息,如果失败返回None + """ + if not anchor_message or not anchor_message.chat_stream: + logger.error(f"{self.log_prefix} 无法创建思考消息,缺少有效的锚点消息或聊天流") + return None + + messageinfo = anchor_message.message_info + thinking_time_point = time.time() + + bot_user_info = UserInfo( + user_id=global_config.bot.qq_account, + user_nickname=global_config.bot.nickname, + platform=messageinfo.platform, + ) + + thinking_message = MessageThinking( + message_id=thinking_id, + chat_stream=self.chat_stream, + bot_user_info=bot_user_info, + reply=anchor_message, + thinking_start_time=thinking_time_point, + ) + + await message_manager.add_message(thinking_message) + logger.debug(f"{self.log_prefix} 创建思考消息: {thinking_id}") + return thinking_message + + async def send_response_messages( + self, + anchor_message: Optional[MessageRecv], + response_set: List[Tuple[str, str]], + thinking_id: str = "", + display_message: str = "", + ) -> Optional[MessageSending]: + """发送回复消息 + + Args: + anchor_message: 锚点消息 + response_set: 回复内容集合,格式为 [(type, content), ...] + thinking_id: 思考ID + display_message: 显示消息 + + Returns: + MessageSending: 发送的第一条消息,如果失败返回None + """ + try: + if not response_set: + logger.warning(f"{self.log_prefix} 回复内容为空") + return None + + # 如果没有thinking_id,生成一个 + if not thinking_id: + thinking_time_point = round(time.time(), 2) + thinking_id = "mt" + str(thinking_time_point) + + # 创建思考消息 + if anchor_message: + await self.create_thinking_message(anchor_message, thinking_id) + + # 创建消息集 + + mark_head = False + is_emoji = False + if len(response_set) == 0: + return None + message_id = f"{thinking_id}_{len(response_set)}" + response_type, content = response_set[0] + if len(response_set) > 1: + message_segment = Seg(type="seglist", data=[Seg(type=t, data=c) for t, c in response_set]) + else: + message_segment = Seg(type=response_type, data=content) + if response_type == "emoji": + is_emoji = True + + bot_msg = await self._build_sending_message( + message_id=message_id, + message_segment=message_segment, + thinking_id=thinking_id, + anchor_message=anchor_message, + thinking_start_time=time.time(), + reply_to=mark_head, + is_emoji=is_emoji, + display_message=display_message, + ) + logger.debug(f"{self.log_prefix} 添加{response_type}类型消息: {content}") + + # 提交消息集 + if bot_msg: + await message_manager.add_message(bot_msg) + logger.info(f"{self.log_prefix} 成功发送 {response_type}类型消息: {str(content)[:200] + '...' if len(str(content)) > 200 else content}") + container = await message_manager.get_container(self.chat_stream.stream_id) # 使用 self.stream_id + for msg in container.messages[:]: + if isinstance(msg, MessageThinking) and msg.message_info.message_id == thinking_id: + container.messages.remove(msg) + logger.debug(f"[{self.stream_name}] 已移除未产生回复的思考消息 {thinking_id}") + break + return bot_msg + else: + logger.warning(f"{self.log_prefix} 没有有效的消息被创建") + return None + + except Exception as e: + logger.error(f"{self.log_prefix} 发送消息失败: {e}") + import traceback + + traceback.print_exc() + return None + + async def _build_sending_message( + self, + message_id: str, + message_segment: Seg, + thinking_id: str, + anchor_message: Optional[MessageRecv], + thinking_start_time: float, + reply_to: bool = False, + is_emoji: bool = False, + display_message: str = "", + ) -> MessageSending: + """构建发送消息 + + Args: + message_id: 消息ID + message_segment: 消息段 + thinking_id: 思考ID + anchor_message: 锚点消息 + thinking_start_time: 思考开始时间 + reply_to: 是否回复 + is_emoji: 是否为表情包 + + Returns: + MessageSending: 构建的发送消息 + """ + bot_user_info = UserInfo( + user_id=global_config.bot.qq_account, + user_nickname=global_config.bot.nickname, + platform=anchor_message.message_info.platform if anchor_message else "unknown", + ) + + message_sending = MessageSending( + message_id=message_id, + chat_stream=self.chat_stream, + bot_user_info=bot_user_info, + message_segment=message_segment, + sender_info=self.chat_stream.user_info, + reply=anchor_message if reply_to else None, + thinking_start_time=thinking_start_time, + is_emoji=is_emoji, + display_message=display_message, + ) + + return message_sending + + async def deal_reply( + self, + cycle_timers: dict, + action_data: Dict[str, Any], + reasoning: str, + anchor_message: MessageRecv, + thinking_id: str, + ) -> Tuple[bool, Optional[str]]: + """处理回复动作 - 兼容focus_chat expressor API + + Args: + cycle_timers: 周期计时器(normal_chat中不使用) + action_data: 动作数据,包含text、target、emojis等 + reasoning: 推理说明 + anchor_message: 锚点消息 + thinking_id: 思考ID + + Returns: + Tuple[bool, Optional[str]]: (是否成功, 回复文本) + """ + try: + response_set = [] + + # 处理文本内容 + text_content = action_data.get("text", "") + if text_content: + response_set.append(("text", text_content)) + + # 处理表情包 + emoji_content = action_data.get("emojis", "") + if emoji_content: + response_set.append(("emoji", emoji_content)) + + if not response_set: + logger.warning(f"{self.log_prefix} deal_reply: 没有有效的回复内容") + return False, None + + # 发送消息 + result = await self.send_response_messages( + anchor_message=anchor_message, + response_set=response_set, + thinking_id=thinking_id, + ) + + if result: + return True, text_content if text_content else "发送成功" + else: + return False, None + + except Exception as e: + logger.error(f"{self.log_prefix} deal_reply执行失败: {e}") + import traceback + + traceback.print_exc() + return False, None From 83431ecf6e791e0eaefcf6764ae5296023ccce64 Mon Sep 17 00:00:00 2001 From: A0000Xz <122650088+A0000Xz@users.noreply.github.com> Date: Fri, 13 Jun 2025 23:53:40 +0800 Subject: [PATCH 2/5] Delete src/plugins/built_in/tts_plgin directory --- src/plugins/built_in/tts_plgin/__init__.py | 0 .../built_in/tts_plgin/actions/__init__.py | 1 - .../built_in/tts_plgin/actions/tts_action.py | 84 ------------------- 3 files changed, 85 deletions(-) delete mode 100644 src/plugins/built_in/tts_plgin/__init__.py delete mode 100644 src/plugins/built_in/tts_plgin/actions/__init__.py delete mode 100644 src/plugins/built_in/tts_plgin/actions/tts_action.py diff --git a/src/plugins/built_in/tts_plgin/__init__.py b/src/plugins/built_in/tts_plgin/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/plugins/built_in/tts_plgin/actions/__init__.py b/src/plugins/built_in/tts_plgin/actions/__init__.py deleted file mode 100644 index 00737d902..000000000 --- a/src/plugins/built_in/tts_plgin/actions/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import tts_action # noqa diff --git a/src/plugins/built_in/tts_plgin/actions/tts_action.py b/src/plugins/built_in/tts_plgin/actions/tts_action.py deleted file mode 100644 index 19830a11a..000000000 --- a/src/plugins/built_in/tts_plgin/actions/tts_action.py +++ /dev/null @@ -1,84 +0,0 @@ -from src.common.logger import get_logger -from src.plugin_system.base.base_action import ActionActivationType -from src.plugin_system.base.base_action import BaseAction as PluginAction, register_action -from typing import Tuple - -logger = get_logger("tts_action") - - -@register_action -class TTSAction(PluginAction): - """TTS语音转换动作处理类""" - - action_name = "tts_action" - action_description = "将文本转换为语音进行播放,适用于需要语音输出的场景" - action_parameters = { - "text": "需要转换为语音的文本内容,必填,内容应当适合语音播报,语句流畅、清晰", - } - action_require = [ - "当需要发送语音信息时使用", - "当用户明确要求使用语音功能时使用", - "当表达内容更适合用语音而不是文字传达时使用", - "当用户想听到语音回答而非阅读文本时使用", - ] - enable_plugin = True # 启用插件 - associated_types = ["tts_text"] - - focus_activation_type = ActionActivationType.LLM_JUDGE - normal_activation_type = ActionActivationType.KEYWORD - - # 关键词配置 - Normal模式下使用关键词触发 - activation_keywords = ["语音", "tts", "播报", "读出来", "语音播放", "听", "朗读"] - keyword_case_sensitive = False - - # 并行执行设置 - TTS可以与回复并行执行,不覆盖回复内容 - parallel_action = False - - async def process(self) -> Tuple[bool, str]: - """处理TTS文本转语音动作""" - logger.info(f"{self.log_prefix} 执行TTS动作: {self.reasoning}") - - # 获取要转换的文本 - text = self.action_data.get("text") - - if not text: - logger.error(f"{self.log_prefix} 执行TTS动作时未提供文本内容") - return False, "执行TTS动作失败:未提供文本内容" - - # 确保文本适合TTS使用 - processed_text = self._process_text_for_tts(text) - - try: - # 发送TTS消息 - await self.send_message(type="tts_text", data=processed_text) - - logger.info(f"{self.log_prefix} TTS动作执行成功,文本长度: {len(processed_text)}") - return True, "TTS动作执行成功" - - except Exception as e: - logger.error(f"{self.log_prefix} 执行TTS动作时出错: {e}") - return False, f"执行TTS动作时出错: {e}" - - def _process_text_for_tts(self, text: str) -> str: - """ - 处理文本使其更适合TTS使用 - - 移除不必要的特殊字符和表情符号 - - 修正标点符号以提高语音质量 - - 优化文本结构使语音更流畅 - """ - # 这里可以添加文本处理逻辑 - # 例如:移除多余的标点、表情符号,优化语句结构等 - - # 简单示例实现 - processed_text = text - - # 移除多余的标点符号 - import re - - processed_text = re.sub(r"([!?,.;:。!?,、;:])\1+", r"\1", processed_text) - - # 确保句子结尾有合适的标点 - if not any(processed_text.endswith(end) for end in [".", "?", "!", "。", "!", "?"]): - processed_text = processed_text + "。" - - return processed_text From 68eb82128f3ada7bcbacf33578047340b9fd5f12 Mon Sep 17 00:00:00 2001 From: A0000Xz <122650088+A0000Xz@users.noreply.github.com> Date: Fri, 13 Jun 2025 23:54:30 +0800 Subject: [PATCH 3/5] Create config.toml --- src/plugins/built_in/tts_plugin/config.toml | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/plugins/built_in/tts_plugin/config.toml diff --git a/src/plugins/built_in/tts_plugin/config.toml b/src/plugins/built_in/tts_plugin/config.toml new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/plugins/built_in/tts_plugin/config.toml @@ -0,0 +1 @@ + From efd65d6b808103bf9b6f3f227626c0fbd72e78eb Mon Sep 17 00:00:00 2001 From: A0000Xz <122650088+A0000Xz@users.noreply.github.com> Date: Fri, 13 Jun 2025 23:55:03 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E9=87=8D=E6=96=B0=E9=80=82=E9=85=8D?= =?UTF-8?q?=E4=BA=86TTS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/built_in/tts_plugin/config.toml | 17 ++- src/plugins/built_in/tts_plugin/plugin.py | 124 ++++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/plugins/built_in/tts_plugin/plugin.py diff --git a/src/plugins/built_in/tts_plugin/config.toml b/src/plugins/built_in/tts_plugin/config.toml index 8b1378917..dd2dd103b 100644 --- a/src/plugins/built_in/tts_plugin/config.toml +++ b/src/plugins/built_in/tts_plugin/config.toml @@ -1 +1,16 @@ - +# 文字转语音插件配置文件 + +[plugin] +name = "tts_plugin" +version = "0.1.0" +enabled = true +description = "文字转语音插件" + +# 组件启用控制 +[components] +enable_tarots = true + +# 日志配置 +[logging] +level = "INFO" +prefix = "[TTS]" \ No newline at end of file diff --git a/src/plugins/built_in/tts_plugin/plugin.py b/src/plugins/built_in/tts_plugin/plugin.py new file mode 100644 index 000000000..8fa9345fc --- /dev/null +++ b/src/plugins/built_in/tts_plugin/plugin.py @@ -0,0 +1,124 @@ +from src.plugin_system.base.base_plugin import BasePlugin, register_plugin +from src.plugin_system.base.component_types import ComponentInfo +from src.common.logger import get_logger +from src.plugin_system.base.base_action import BaseAction, ActionActivationType, ChatMode +from typing import Tuple, List, Type + +logger = get_logger("tts") + +class TTSAction(BaseAction): + """TTS语音转换动作处理类""" + + action_name = "tts" + action_description = "将文本转换为语音进行播放,适用于需要语音输出的场景" + action_parameters = { + "text": "需要转换为语音的文本内容,必填,内容应当适合语音播报,语句流畅、清晰", + } + action_require = [ + "当需要发送语音信息时使用", + "当用户明确要求使用语音功能时使用", + "当表达内容更适合用语音而不是文字传达时使用", + "当用户想听到语音回答而非阅读文本时使用", + ] + enable_plugin = True # 启用插件 + associated_types = ["tts_text"] + + # 模式和并行控制 + mode_enable = ChatMode.ALL + parallel_action = False + + focus_activation_type = ActionActivationType.LLM_JUDGE + normal_activation_type = ActionActivationType.KEYWORD + + # 关键词配置 - Normal模式下使用关键词触发 + activation_keywords = ["语音", "tts", "播报", "读出来", "语音播放", "听", "朗读"] + keyword_case_sensitive = False + + # 并行执行设置 - TTS可以与回复并行执行,不覆盖回复内容 + parallel_action = False + + async def execute(self) -> Tuple[bool, str]: + """处理TTS文本转语音动作""" + logger.info(f"{self.log_prefix} 执行TTS动作: {self.reasoning}") + + # 获取要转换的文本 + text = self.action_data.get("text") + + if not text: + logger.error(f"{self.log_prefix} 执行TTS动作时未提供文本内容") + return False, "执行TTS动作失败:未提供文本内容" + + # 确保文本适合TTS使用 + processed_text = self._process_text_for_tts(text) + + try: + # 发送TTS消息 + await self.send_type(type="tts_text", text=processed_text) + + logger.info(f"{self.log_prefix} TTS动作执行成功,文本长度: {len(processed_text)}") + return True, "TTS动作执行成功" + + except Exception as e: + logger.error(f"{self.log_prefix} 执行TTS动作时出错: {e}") + return False, f"执行TTS动作时出错: {e}" + + def _process_text_for_tts(self, text: str) -> str: + """ + 处理文本使其更适合TTS使用 + - 移除不必要的特殊字符和表情符号 + - 修正标点符号以提高语音质量 + - 优化文本结构使语音更流畅 + """ + # 这里可以添加文本处理逻辑 + # 例如:移除多余的标点、表情符号,优化语句结构等 + + # 简单示例实现 + processed_text = text + + # 移除多余的标点符号 + import re + + processed_text = re.sub(r"([!?,.;:。!?,、;:])\1+", r"\1", processed_text) + + # 确保句子结尾有合适的标点 + if not any(processed_text.endswith(end) for end in [".", "?", "!", "。", "!", "?"]): + processed_text = processed_text + "。" + + return processed_text + +@register_plugin +class TTSPlugin(BasePlugin): + """TTS插件 + - 这是文字转语音插件 + - Normal模式下依靠关键词触发 + - Focus模式下由LLM判断触发 + - 具有一定的文本预处理能力 + """ + + # 插件基本信息 + plugin_name = "tts_plugin" + plugin_description = "文字转语音插件" + plugin_version = "0.1.0" + plugin_author = "MaiBot开发团队" + enable_plugin = True + config_file_name = "config.toml" + + def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: + """返回插件包含的组件列表""" + + # 从配置获取组件启用状态 + enable_tts = self.get_config("components.enable_tts", True) + components = [] + + # 添加Action组件 + if enable_tts: + components.append( + ( + TTSAction.get_action_info( + name="tarots_action", description="文字转语音插件" + ), + TTSAction, + ) + ) + + return components \ No newline at end of file From bc145bc379652e280ee94b8cdd362a5538653715 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 14 Jun 2025 02:16:34 +0000 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/normal_chat/normal_chat_expressor.py | 4 +++- src/plugins/built_in/tts_plugin/plugin.py | 10 +++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/chat/normal_chat/normal_chat_expressor.py b/src/chat/normal_chat/normal_chat_expressor.py index 416376b74..c89ad8534 100644 --- a/src/chat/normal_chat/normal_chat_expressor.py +++ b/src/chat/normal_chat/normal_chat_expressor.py @@ -139,7 +139,9 @@ class NormalChatExpressor: # 提交消息集 if bot_msg: await message_manager.add_message(bot_msg) - logger.info(f"{self.log_prefix} 成功发送 {response_type}类型消息: {str(content)[:200] + '...' if len(str(content)) > 200 else content}") + logger.info( + f"{self.log_prefix} 成功发送 {response_type}类型消息: {str(content)[:200] + '...' if len(str(content)) > 200 else content}" + ) container = await message_manager.get_container(self.chat_stream.stream_id) # 使用 self.stream_id for msg in container.messages[:]: if isinstance(msg, MessageThinking) and msg.message_info.message_id == thinking_id: diff --git a/src/plugins/built_in/tts_plugin/plugin.py b/src/plugins/built_in/tts_plugin/plugin.py index 8fa9345fc..e1ef47994 100644 --- a/src/plugins/built_in/tts_plugin/plugin.py +++ b/src/plugins/built_in/tts_plugin/plugin.py @@ -6,6 +6,7 @@ from typing import Tuple, List, Type logger = get_logger("tts") + class TTSAction(BaseAction): """TTS语音转换动作处理类""" @@ -86,13 +87,14 @@ class TTSAction(BaseAction): return processed_text + @register_plugin class TTSPlugin(BasePlugin): """TTS插件 - 这是文字转语音插件 - Normal模式下依靠关键词触发 - Focus模式下由LLM判断触发 - - 具有一定的文本预处理能力 + - 具有一定的文本预处理能力 """ # 插件基本信息 @@ -114,11 +116,9 @@ class TTSPlugin(BasePlugin): if enable_tts: components.append( ( - TTSAction.get_action_info( - name="tarots_action", description="文字转语音插件" - ), + TTSAction.get_action_info(name="tarots_action", description="文字转语音插件"), TTSAction, ) ) - return components \ No newline at end of file + return components