From 7a68ab0319d550ce911fd6f5e691dc09d0e34e4c Mon Sep 17 00:00:00 2001 From: UnCLAS-Prommer Date: Tue, 19 Aug 2025 01:05:20 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/utils/chat_message_builder.py | 95 ++++++------- src/common/data_models/database_data_model.py | 127 +++++++++++------- src/common/data_models/message_data_model.py | 16 +++ 3 files changed, 142 insertions(+), 96 deletions(-) create mode 100644 src/common/data_models/message_data_model.py diff --git a/src/chat/utils/chat_message_builder.py b/src/chat/utils/chat_message_builder.py index 7e477597..170ba050 100644 --- a/src/chat/utils/chat_message_builder.py +++ b/src/chat/utils/chat_message_builder.py @@ -2,13 +2,14 @@ import time # 导入 time 模块以获取当前时间 import random import re -from typing import List, Dict, Any, Tuple, Optional, Callable +from typing import List, Dict, Any, Tuple, Optional, Callable, Union from rich.traceback import install from src.config.config import global_config from src.common.logger import get_logger from src.common.message_repository import find_messages, count_messages from src.common.data_models.database_data_model import DatabaseMessages +from src.common.data_models.message_data_model import MessageAndActionModel from src.common.database.database_model import ActionRecords from src.common.database.database_model import Images from src.person_info.person_info import Person, get_person_id @@ -394,7 +395,7 @@ def num_new_messages_since_with_users( def _build_readable_messages_internal( - messages: List[Dict[str, Any]], + messages: List[MessageAndActionModel], replace_bot_name: bool = True, merge_messages: bool = False, timestamp_mode: str = "relative", @@ -402,7 +403,7 @@ def _build_readable_messages_internal( pic_id_mapping: Optional[Dict[str, str]] = None, pic_counter: int = 1, show_pic: bool = True, - message_id_list: Optional[List[Dict[str, Any]]] = None, + message_id_list: Optional[List[DatabaseMessages]] = None, ) -> Tuple[str, List[Tuple[float, str, str]], Dict[str, str], int]: """ 内部辅助函数,构建可读消息字符串和原始消息详情列表。 @@ -433,14 +434,15 @@ def _build_readable_messages_internal( timestamp_to_id = {} if message_id_list: for item in message_id_list: - message = item.get("message", {}) - timestamp = message.get("time") + timestamp = item.time if timestamp is not None: - timestamp_to_id[timestamp] = item.get("id", "") + timestamp_to_id[timestamp] = item.message_id - def process_pic_ids(content: str) -> str: + def process_pic_ids(content: Optional[str]) -> str: """处理内容中的图片ID,将其替换为[图片x]格式""" - nonlocal current_pic_counter + if content is None: + logger.warning("Content is None when processing pic IDs.") + raise ValueError("Content is None") # 匹配 [picid:xxxxx] 格式 pic_pattern = r"\[picid:([^\]]+)\]" @@ -460,38 +462,23 @@ def _build_readable_messages_internal( # 1 & 2: 获取发送者信息并提取消息组件 for msg in messages: # 检查是否是动作记录 - if msg.get("is_action_record", False): + if msg.is_action_record: is_action = True - timestamp: float = msg.get("time") # type: ignore - content = msg.get("display_message", "") + timestamp: float = msg.time + content = msg.display_message # 对于动作记录,也处理图片ID content = process_pic_ids(content) message_details_raw.append((timestamp, global_config.bot.nickname, content, is_action)) continue - # 检查并修复缺少的user_info字段 - if "user_info" not in msg: - # 创建user_info字段 - msg["user_info"] = { - "platform": msg.get("user_platform", ""), - "user_id": msg.get("user_id", ""), - "user_nickname": msg.get("user_nickname", ""), - "user_cardname": msg.get("user_cardname", ""), - } + platform = msg.user_platform + user_id = msg.user_id - user_info = msg.get("user_info", {}) - platform = user_info.get("platform") - user_id = user_info.get("user_id") + user_nickname = msg.user_nickname + user_cardname = msg.user_cardname - user_nickname = user_info.get("user_nickname") - user_cardname = user_info.get("user_cardname") - - timestamp: float = msg.get("time") # type: ignore - content: str - if msg.get("display_message"): - content = msg.get("display_message", "") - else: - content = msg.get("processed_plain_text", "") # 默认空字符串 + timestamp = msg.time + content = msg.display_message or msg.processed_plain_text or "" if "ᶠ" in content: content = content.replace("ᶠ", "") @@ -819,7 +806,7 @@ def build_readable_messages( truncate: bool = False, show_actions: bool = False, show_pic: bool = True, - message_id_list: Optional[List[Dict[str, Any]]] = None, + message_id_list: Optional[List[DatabaseMessages]] = None, ) -> str: # sourcery skip: extract-method """ 将消息列表转换为可读的文本格式。 @@ -835,11 +822,24 @@ def build_readable_messages( truncate: 是否截断长消息 show_actions: 是否显示动作记录 """ + # WIP HERE and BELOW ---------------------------------------------- # 创建messages的深拷贝,避免修改原始列表 if not messages: return "" - copy_messages = list(messages) + copy_messages: List[MessageAndActionModel] = [ + MessageAndActionModel( + msg.time, + msg.user_info.user_id, + msg.user_info.platform, + msg.user_info.user_nickname, + msg.user_info.user_cardname, + msg.processed_plain_text, + msg.display_message, + msg.chat_info.platform, + ) + for msg in messages + ] if show_actions and copy_messages: # 获取所有消息的时间范围 @@ -847,7 +847,7 @@ def build_readable_messages( max_time = max(msg.time or 0 for msg in copy_messages) # 从第一条消息中获取chat_id - chat_id = copy_messages[0].chat_id if copy_messages else None + chat_id = messages[0].chat_id if messages else None # 获取这个时间范围内的动作记录,并匹配chat_id actions_in_range = ( @@ -867,23 +867,24 @@ def build_readable_messages( ) # 合并两部分动作记录 - actions = list(actions_in_range) + list(action_after_latest) + actions: List[ActionRecords] = list(actions_in_range) + list(action_after_latest) # 将动作记录转换为消息格式 for action in actions: # 只有当build_into_prompt为True时才添加动作记录 if action.action_build_into_prompt: - action_msg = { - "time": action.time, - "user_id": global_config.bot.qq_account, # 使用机器人的QQ账号 - "user_nickname": global_config.bot.nickname, # 使用机器人的昵称 - "user_cardname": "", # 机器人没有群名片 - "processed_plain_text": f"{action.action_prompt_display}", - "display_message": f"{action.action_prompt_display}", - "chat_info_platform": action.chat_info_platform, - "is_action_record": True, # 添加标识字段 - "action_name": action.action_name, # 保存动作名称 - } + action_msg = MessageAndActionModel( + time=float(action.time), # type: ignore + user_id=global_config.bot.qq_account, # 使用机器人的QQ账号 + user_platform=global_config.bot.platform, # 使用机器人的平台 + user_nickname=global_config.bot.nickname, # 使用机器人的用户名 + user_cardname="", # 机器人没有群名片 + processed_plain_text=f"{action.action_prompt_display}", + display_message=f"{action.action_prompt_display}", + chat_info_platform=str(action.chat_info_platform), + is_action_record=True, # 添加标识字段 + action_name=str(action.action_name), # 保存动作名称 + ) copy_messages.append(action_msg) # 重新按时间排序 diff --git a/src/common/data_models/database_data_model.py b/src/common/data_models/database_data_model.py index 53716f64..6e1d6d5e 100644 --- a/src/common/data_models/database_data_model.py +++ b/src/common/data_models/database_data_model.py @@ -56,70 +56,99 @@ class DatabaseChatInfo(AbstractClassFlag): @dataclass(init=False) class DatabaseMessages(AbstractClassFlag): - message_id: str = field(default_factory=str) - time: float = field(default_factory=float) - chat_id: str = field(default_factory=str) - reply_to: Optional[str] = None - interest_value: Optional[float] = None + def __init__( + self, + message_id: str = "", + time: float = 0.0, + chat_id: str = "", + reply_to: Optional[str] = None, + interest_value: Optional[float] = None, + key_words: Optional[str] = None, + key_words_lite: Optional[str] = None, + is_mentioned: Optional[bool] = None, + processed_plain_text: Optional[str] = None, + display_message: Optional[str] = None, + priority_mode: Optional[str] = None, + priority_info: Optional[str] = None, + additional_config: Optional[str] = None, + is_emoji: bool = False, + is_picid: bool = False, + is_command: bool = False, + is_notify: bool = False, + selected_expressions: Optional[str] = None, + user_id: str = "", + user_nickname: str = "", + user_cardname: Optional[str] = None, + user_platform: str = "", + chat_info_group_id: Optional[str] = None, + chat_info_group_name: Optional[str] = None, + chat_info_group_platform: Optional[str] = None, + chat_info_user_id: str = "", + chat_info_user_nickname: str = "", + chat_info_user_cardname: Optional[str] = None, + chat_info_user_platform: str = "", + chat_info_stream_id: str = "", + chat_info_platform: str = "", + chat_info_create_time: float = 0.0, + chat_info_last_active_time: float = 0.0, + **kwargs: Any, + ): + self.message_id = message_id + self.time = time + self.chat_id = chat_id + self.reply_to = reply_to + self.interest_value = interest_value - key_words: Optional[str] = None - key_words_lite: Optional[str] = None - is_mentioned: Optional[bool] = None + self.key_words = key_words + self.key_words_lite = key_words_lite + self.is_mentioned = is_mentioned - processed_plain_text: Optional[str] = None # 处理后的纯文本消息 - display_message: Optional[str] = None # 显示的消息 + self.processed_plain_text = processed_plain_text + self.display_message = display_message - priority_mode: Optional[str] = None - priority_info: Optional[str] = None + self.priority_mode = priority_mode + self.priority_info = priority_info - additional_config: Optional[str] = None - is_emoji: bool = False - is_picid: bool = False - is_command: bool = False - is_notify: bool = False + self.additional_config = additional_config + self.is_emoji = is_emoji + self.is_picid = is_picid + self.is_command = is_command + self.is_notify = is_notify - selected_expressions: Optional[str] = None + self.selected_expressions = selected_expressions - def __init__(self, **kwargs: Any): - defined = {f.name: f for f in fields(self.__class__)} - for name, f in defined.items(): - if name in kwargs: - setattr(self, name, kwargs.pop(name)) - elif f.default is not MISSING: - setattr(self, name, f.default) - else: - raise TypeError(f"缺失必需字段: {name}") - - self.group_info = None + self.group_info: Optional[DatabaseGroupInfo] = None self.user_info = DatabaseUserInfo( - user_id=kwargs.get("user_id"), # type: ignore - user_nickname=kwargs.get("user_nickname"), # type: ignore - user_cardname=kwargs.get("user_cardname"), # type: ignore - platform=kwargs.get("user_platform"), # type: ignore + user_id=user_id, + user_nickname=user_nickname, + user_cardname=user_cardname, + platform=user_platform, ) - if kwargs.get("chat_info_group_id") and kwargs.get("chat_info_group_name"): + if chat_info_group_id and chat_info_group_name: self.group_info = DatabaseGroupInfo( - group_id=kwargs.get("chat_info_group_id"), # type: ignore - group_name=kwargs.get("chat_info_group_name"), # type: ignore - group_platform=kwargs.get("chat_info_group_platform"), # type: ignore + group_id=chat_info_group_id, + group_name=chat_info_group_name, + group_platform=chat_info_group_platform, ) - chat_user_info = DatabaseUserInfo( - user_id=kwargs.get("chat_info_user_id"), # type: ignore - user_nickname=kwargs.get("chat_info_user_nickname"), # type: ignore - user_cardname=kwargs.get("chat_info_user_cardname"), # type: ignore - platform=kwargs.get("chat_info_user_platform"), # type: ignore - ) - self.chat_info = DatabaseChatInfo( - stream_id=kwargs.get("chat_info_stream_id"), # type: ignore - platform=kwargs.get("chat_info_platform"), # type: ignore - create_time=kwargs.get("chat_info_create_time"), # type: ignore - last_active_time=kwargs.get("chat_info_last_active_time"), # type: ignore - user_info=chat_user_info, + stream_id=chat_info_stream_id, + platform=chat_info_platform, + create_time=chat_info_create_time, + last_active_time=chat_info_last_active_time, + user_info=DatabaseUserInfo( + user_id=chat_info_user_id, + user_nickname=chat_info_user_nickname, + user_cardname=chat_info_user_cardname, + platform=chat_info_user_platform, + ), group_info=self.group_info, ) + if kwargs: + for key, value in kwargs.items(): + setattr(self, key, value) + # def __post_init__(self): # assert isinstance(self.message_id, str), "message_id must be a string" # assert isinstance(self.time, float), "time must be a float" diff --git a/src/common/data_models/message_data_model.py b/src/common/data_models/message_data_model.py new file mode 100644 index 00000000..73d0539c --- /dev/null +++ b/src/common/data_models/message_data_model.py @@ -0,0 +1,16 @@ +from typing import Optional +from dataclasses import dataclass, field + + +@dataclass +class MessageAndActionModel: + time: float = field(default_factory=float) + user_id: str = field(default_factory=str) + user_platform: str = field(default_factory=str) + user_nickname: str = field(default_factory=str) + user_cardname: Optional[str] = None + processed_plain_text: Optional[str] = None + display_message: Optional[str] = None + chat_info_platform: str = field(default_factory=str) + is_action_record: bool = field(default=False) + action_name: Optional[str] = None