From ae1a1d4a567a8e18c1e33dc175bb7c2b26c760b2 Mon Sep 17 00:00:00 2001 From: magisk317 Date: Wed, 29 Oct 2025 18:10:03 +0800 Subject: [PATCH] feat: prefer qq group cards across messaging --- .../heart_flow/heartflow_message_processor.py | 6 ++-- src/chat/message_receive/bot.py | 13 +++++++-- src/chat/message_receive/chat_stream.py | 6 ++-- src/chat/message_receive/message.py | 6 +++- src/chat/utils/chat_message_builder.py | 9 +++--- src/chat/utils/statistic.py | 6 ++-- src/chat/utils/utils.py | 5 ++-- src/person_info/person_info.py | 29 +++++++++++++++---- 8 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/chat/heart_flow/heartflow_message_processor.py b/src/chat/heart_flow/heartflow_message_processor.py index 032c52cd..5d0ca665 100644 --- a/src/chat/heart_flow/heartflow_message_processor.py +++ b/src/chat/heart_flow/heartflow_message_processor.py @@ -81,12 +81,14 @@ class HeartFCMessageReceiver: # if not processed_plain_text: # print(message) - logger.info(f"[{mes_name}]{userinfo.user_nickname}:{processed_plain_text}") # type: ignore + display_name = userinfo.user_cardname or userinfo.user_nickname # type: ignore + logger.info(f"[{mes_name}]{display_name}:{processed_plain_text}") # type: ignore + preferred_name = display_name or userinfo.user_nickname # type: ignore _ = Person.register_person( platform=message.message_info.platform, # type: ignore user_id=message.message_info.user_info.user_id, # type: ignore - nickname=userinfo.user_nickname, # type: ignore + nickname=preferred_name, # type: ignore ) except Exception as e: diff --git a/src/chat/message_receive/bot.py b/src/chat/message_receive/bot.py index 43d2754a..62dc0b2d 100644 --- a/src/chat/message_receive/bot.py +++ b/src/chat/message_receive/bot.py @@ -40,7 +40,8 @@ def _check_ban_words(text: str, userinfo: UserInfo, group_info: Optional[GroupIn for word in global_config.message_receive.ban_words: if word in text: chat_name = group_info.group_name if group_info else "私聊" - logger.info(f"[{chat_name}]{userinfo.user_nickname}:{text}") + display_name = userinfo.user_cardname or userinfo.user_nickname + logger.info(f"[{chat_name}]{display_name}:{text}") logger.info(f"[过滤词识别]消息中含有{word},filtered") return True return False @@ -64,7 +65,8 @@ def _check_ban_regex(text: str, userinfo: UserInfo, group_info: Optional[GroupIn for pattern in global_config.message_receive.ban_msgs_regex: if re.search(pattern, text): chat_name = group_info.group_name if group_info else "私聊" - logger.info(f"[{chat_name}]{userinfo.user_nickname}:{text}") + display_name = userinfo.user_cardname or userinfo.user_nickname + logger.info(f"[{chat_name}]{display_name}:{text}") logger.info(f"[正则表达式过滤]消息匹配到{pattern},filtered") return True return False @@ -206,6 +208,13 @@ class ChatBot: group_info = message.message_info.group_info user_info = message.message_info.user_info + if ( + group_info + and user_info + and (not user_info.user_cardname or not str(user_info.user_cardname).strip()) + ): + user_info.user_cardname = user_info.user_nickname or "" + continue_flag, modified_message = await events_manager.handle_mai_events( EventType.ON_MESSAGE_PRE_PROCESS, message ) diff --git a/src/chat/message_receive/chat_stream.py b/src/chat/message_receive/chat_stream.py index 81f78901..a631c8e6 100644 --- a/src/chat/message_receive/chat_stream.py +++ b/src/chat/message_receive/chat_stream.py @@ -313,8 +313,10 @@ class ChatManager: if stream.group_info and stream.group_info.group_name: return stream.group_info.group_name - elif stream.user_info and stream.user_info.user_nickname: - return f"{stream.user_info.user_nickname}的私聊" + elif stream.user_info: + display_name = stream.user_info.user_cardname or stream.user_info.user_nickname + if display_name: + return f"{display_name}的私聊" else: return None diff --git a/src/chat/message_receive/message.py b/src/chat/message_receive/message.py index 6c1ec1a7..0dd6f273 100644 --- a/src/chat/message_receive/message.py +++ b/src/chat/message_receive/message.py @@ -283,7 +283,11 @@ class MessageProcessBase(Message): if self.reply and hasattr(self.reply, "processed_plain_text"): # print(f"self.reply.processed_plain_text: {self.reply.processed_plain_text}") # print(f"reply: {self.reply}") - return f"[回复<{self.reply.message_info.user_info.user_nickname}:{self.reply.message_info.user_info.user_id}> 的消息:{self.reply.processed_plain_text}]" # type: ignore + reply_user_info = self.reply.message_info.user_info # type: ignore + reply_name = reply_user_info.user_cardname or reply_user_info.user_nickname # type: ignore + return ( + f"[回复<{reply_name}:{reply_user_info.user_id}> 的消息:{self.reply.processed_plain_text}]" + ) # type: ignore return "" else: return f"[{segment.type}:{str(segment.data)}]" diff --git a/src/chat/utils/chat_message_builder.py b/src/chat/utils/chat_message_builder.py index 8915e810..3b4e8e1a 100644 --- a/src/chat/utils/chat_message_builder.py +++ b/src/chat/utils/chat_message_builder.py @@ -413,7 +413,8 @@ def _build_readable_messages_internal( if message.is_action_record: # 对于动作记录,也处理图片ID content = process_pic_ids(message.display_message) - detailed_messages_raw.append((message.time, message.user_nickname, content, True)) + action_name = message.user_cardname or message.user_nickname or "系统" + detailed_messages_raw.append((message.time, action_name, content, True)) continue platform = message.user_platform @@ -434,9 +435,9 @@ def _build_readable_messages_internal( person = Person(platform=platform, user_id=user_id) # 根据 replace_bot_name 参数决定是否替换机器人名称 - person_name = ( - person.person_name or f"{user_nickname}" or (f"昵称:{user_cardname}" if user_cardname else "某人") - ) + preferred_display = user_cardname or user_nickname + fallback_display = user_nickname or user_cardname or "某人" + person_name = person.person_name or preferred_display or fallback_display if replace_bot_name and ( (platform == global_config.bot.platform and user_id == global_config.bot.qq_account) or (platform == "telegram" and user_id == getattr(global_config.bot, "telegram_account", "")) diff --git a/src/chat/utils/statistic.py b/src/chat/utils/statistic.py index 18c00474..6c631d80 100644 --- a/src/chat/utils/statistic.py +++ b/src/chat/utils/statistic.py @@ -480,7 +480,7 @@ class StatisticOutputTask(AsyncTask): elif message.user_id: # Fallback to sender's info for chat_id if not a group_info based chat # This uses the message SENDER's ID as per original logic's fallback chat_id = f"u{message.user_id}" # SENDER's user_id - chat_name = message.user_nickname # SENDER's nickname + chat_name = message.user_cardname or message.user_nickname # SENDER's display name else: # If neither group_id nor sender_id is available for chat identification logger.warning( @@ -704,7 +704,7 @@ class StatisticOutputTask(AsyncTask): if group_name and group_name.strip(): return group_name.strip() elif stream.user_info and hasattr(stream.user_info, "user_nickname"): - user_name = stream.user_info.user_nickname + user_name = stream.user_info.user_cardname or stream.user_info.user_nickname if user_name and user_name.strip(): return user_name.strip() @@ -1293,7 +1293,7 @@ class StatisticOutputTask(AsyncTask): if message.chat_info_group_id: chat_name = message.chat_info_group_name or f"群{message.chat_info_group_id}" elif message.user_id: - chat_name = message.user_nickname or f"用户{message.user_id}" + chat_name = (message.user_cardname or message.user_nickname) or f"用户{message.user_id}" else: continue diff --git a/src/chat/utils/utils.py b/src/chat/utils/utils.py index ce3eab08..be29f6f1 100644 --- a/src/chat/utils/utils.py +++ b/src/chat/utils/utils.py @@ -585,10 +585,11 @@ def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional["TargetP from src.common.data_models.info_data_model import TargetPersonInfo # 解决循环导入问题 # Initialize target_info with basic info + display_name = user_info.user_cardname or user_info.user_nickname # type: ignore target_info = TargetPersonInfo( platform=platform, user_id=user_id, - user_nickname=user_info.user_nickname, # type: ignore + user_nickname=display_name, # type: ignore person_id=None, person_name=None, ) @@ -597,7 +598,7 @@ def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional["TargetP try: person = Person(platform=platform, user_id=user_id) if not person.is_known: - logger.warning(f"用户 {user_info.user_nickname} 尚未认识") + logger.warning(f"用户 {display_name} 尚未认识") # 如果用户尚未认识,则返回False和None return False, None if person.person_id: diff --git a/src/person_info/person_info.py b/src/person_info/person_info.py index 7793da31..379e27aa 100644 --- a/src/person_info/person_info.py +++ b/src/person_info/person_info.py @@ -173,7 +173,11 @@ class Person: Returns: Person: 新注册的Person实例 """ - if not platform or not user_id or not nickname: + display_name = (nickname or "").strip() + if not display_name: + display_name = f"用户{user_id}" if user_id else "未知用户" + + if not platform or not user_id: logger.error("注册用户失败:platform、user_id 和 nickname 都是必需参数") return None @@ -181,8 +185,21 @@ class Person: person_id = get_person_id(platform, user_id) if is_person_known(person_id=person_id): - logger.debug(f"用户 {nickname} 已存在") - return Person(person_id=person_id) + person = Person(person_id=person_id) + if not person or not person.is_known: + return person + + old_nickname = person.nickname or "" + old_person_name = person.person_name + if display_name != old_nickname: + person.nickname = display_name + if not old_person_name or old_person_name == old_nickname: + person.person_name = display_name + person.sync_to_database() + logger.debug( + f"更新用户 {person_id} 的昵称信息: '{old_nickname}' -> '{display_name}'" + ) + return person # 创建Person实例 person = cls.__new__(cls) @@ -191,11 +208,11 @@ class Person: person.person_id = person_id person.platform = platform person.user_id = user_id - person.nickname = nickname + person.nickname = display_name # 初始化默认值 person.is_known = True # 注册后立即标记为已认识 - person.person_name = nickname # 使用nickname作为初始person_name + person.person_name = display_name # 使用nickname作为初始person_name person.name_reason = "用户注册时设置的昵称" person.know_times = 1 person.know_since = time.time() @@ -205,7 +222,7 @@ class Person: # 同步到数据库 person.sync_to_database() - logger.info(f"成功注册新用户:{person_id},平台:{platform},昵称:{nickname}") + logger.info(f"成功注册新用户:{person_id},平台:{platform},昵称:{display_name}") return person