From b42608c49a2b61e2eed22aa7edd1300b11a2e3dd Mon Sep 17 00:00:00 2001 From: minecraft1024a Date: Sat, 20 Sep 2025 20:45:56 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E4=B9=9F=E8=AE=B8=E6=98=AF=E4=BF=AE?= =?UTF-8?q?=E5=A5=BD=E4=BA=86=E8=A1=A8=E8=BE=BE=E5=AD=A6=E4=B9=A0=E5=90=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat_loop/heartFC_chat.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/chat/chat_loop/heartFC_chat.py b/src/chat/chat_loop/heartFC_chat.py index 47afab50d..b1c7455b0 100644 --- a/src/chat/chat_loop/heartFC_chat.py +++ b/src/chat/chat_loop/heartFC_chat.py @@ -435,6 +435,13 @@ class HeartFChatting: # Messages should be processed action_type = await self.cycle_processor.observe(interest_value=interest_value) + # 尝试触发表达学习 + if self.context.expression_learner: + try: + await self.context.expression_learner.trigger_learning_for_chat() + except Exception as e: + logger.error(f"{self.context.log_prefix} 表达学习触发失败: {e}") + # 管理no_reply计数器 if action_type != "no_reply": self.recent_interest_records.clear() From ca780919a89a6013afd2a7409ee80c1bb84ec4e7 Mon Sep 17 00:00:00 2001 From: tt-P607 <68868379+tt-P607@users.noreply.github.com> Date: Sat, 20 Sep 2025 22:04:43 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix(core):=20=E4=BF=AE=E6=AD=A3=E5=9B=A0?= =?UTF-8?q?=E5=BC=82=E6=AD=A5=E6=94=B9=E9=80=A0=E9=81=97=E6=BC=8F=E7=9A=84?= =?UTF-8?q?=20await=20=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在最近的数据库异步化重构后,部分函数的调用处忘记添加 `await` 关键字,导致协程未被正确执行。 本次提交修复了以下模块中的问题: - `ExpressionLearner` - `ChatMessageBuilder` - `EmojiAction --- src/chat/express/expression_learner.py | 2 +- src/chat/utils/chat_message_builder.py | 2 +- src/plugins/built_in/core_actions/emoji.py | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/chat/express/expression_learner.py b/src/chat/express/expression_learner.py index b7dabe6e1..fb22a4115 100644 --- a/src/chat/express/expression_learner.py +++ b/src/chat/express/expression_learner.py @@ -167,7 +167,7 @@ class ExpressionLearner: Returns: bool: 是否成功触发学习 """ - if not self.should_trigger_learning(): + if not await self.should_trigger_learning(): return False try: diff --git a/src/chat/utils/chat_message_builder.py b/src/chat/utils/chat_message_builder.py index e49c218c4..9555e08c8 100644 --- a/src/chat/utils/chat_message_builder.py +++ b/src/chat/utils/chat_message_builder.py @@ -1232,7 +1232,7 @@ async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str: # 在最前面添加图片映射信息 final_output_lines = [] - pic_mapping_info = build_pic_mapping_info(pic_id_mapping) + pic_mapping_info = await build_pic_mapping_info(pic_id_mapping) if pic_mapping_info: final_output_lines.append(pic_mapping_info) final_output_lines.append("\n\n") diff --git a/src/plugins/built_in/core_actions/emoji.py b/src/plugins/built_in/core_actions/emoji.py index 2c0940fcc..84dd45981 100644 --- a/src/plugins/built_in/core_actions/emoji.py +++ b/src/plugins/built_in/core_actions/emoji.py @@ -77,7 +77,7 @@ class EmojiAction(BaseAction): # 3. 根据历史记录筛选表情 try: - recent_emojis_desc = get_recent_emojis(self.chat_id, limit=10) + recent_emojis_desc = await get_recent_emojis(self.chat_id, limit=10) if recent_emojis_desc: filtered_emojis = [emoji for emoji in all_emojis_obj if emoji.description not in recent_emojis_desc] if filtered_emojis: @@ -122,7 +122,7 @@ class EmojiAction(BaseAction): emoji_base64, emoji_description = random.choice(all_emojis_data) else: # 获取最近的5条消息内容用于判断 - recent_messages = message_api.get_recent_messages(chat_id=self.chat_id, limit=5) + recent_messages = await message_api.get_recent_messages(chat_id=self.chat_id, limit=5) messages_text = "" if recent_messages: messages_text = await message_api.build_readable_messages( @@ -181,7 +181,7 @@ class EmojiAction(BaseAction): elif global_config.emoji.emoji_selection_mode == "description": # --- 详细描述选择模式 --- # 获取最近的5条消息内容用于判断 - recent_messages = message_api.get_recent_messages(chat_id=self.chat_id, limit=5) + recent_messages = await message_api.get_recent_messages(chat_id=self.chat_id, limit=5) messages_text = "" if recent_messages: messages_text = await message_api.build_readable_messages( @@ -260,7 +260,7 @@ class EmojiAction(BaseAction): # 发送成功后,记录到历史 try: - add_emoji_to_history(self.chat_id, emoji_description) + await add_emoji_to_history(self.chat_id, emoji_description) except Exception as e: logger.error(f"{self.log_prefix} 添加表情到历史记录时出错: {e}") From 0286d75228c26fa3d0ce1d049cb6903921f3a6ff Mon Sep 17 00:00:00 2001 From: tt-P607 <68868379+tt-P607@users.noreply.github.com> Date: Sat, 20 Sep 2025 22:12:07 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix(emoji):=20=E4=BF=AE=E6=AD=A3=E5=AF=B9?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=87=BD=E6=95=B0=E7=9A=84=20await=20?= =?UTF-8?q?=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `get_recent_emojis` 和 `add_emoji_to_history` 函数已被重构为同步方法。本次提交移除了对这两个函数不必要的 `await` 调用,以修复由此引发的 `TypeError`。 --- src/plugins/built_in/core_actions/emoji.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/built_in/core_actions/emoji.py b/src/plugins/built_in/core_actions/emoji.py index 84dd45981..3ebf4610a 100644 --- a/src/plugins/built_in/core_actions/emoji.py +++ b/src/plugins/built_in/core_actions/emoji.py @@ -77,7 +77,7 @@ class EmojiAction(BaseAction): # 3. 根据历史记录筛选表情 try: - recent_emojis_desc = await get_recent_emojis(self.chat_id, limit=10) + recent_emojis_desc = get_recent_emojis(self.chat_id, limit=10) if recent_emojis_desc: filtered_emojis = [emoji for emoji in all_emojis_obj if emoji.description not in recent_emojis_desc] if filtered_emojis: @@ -260,7 +260,7 @@ class EmojiAction(BaseAction): # 发送成功后,记录到历史 try: - await add_emoji_to_history(self.chat_id, emoji_description) + add_emoji_to_history(self.chat_id, emoji_description) except Exception as e: logger.error(f"{self.log_prefix} 添加表情到历史记录时出错: {e}") From a9a9f380d608929187805688197f2d5afa19190e Mon Sep 17 00:00:00 2001 From: minecraft1024a Date: Sat, 20 Sep 2025 22:21:35 +0800 Subject: [PATCH 4/4] =?UTF-8?q?refactor(person=5Finfo):=20=E5=BC=95?= =?UTF-8?q?=E5=85=A5=E5=90=8C=E6=AD=A5=E6=96=B9=E6=B3=95=20get=5Fvalue=20?= =?UTF-8?q?=E5=B9=B6=E6=9B=BF=E6=8D=A2=E6=97=A7=E7=9A=84=20get=5Fvalue=5Fs?= =?UTF-8?q?ync(=E5=9B=A0=E4=B8=BA=E6=A0=B9=E6=9C=AC=E5=B0=B1=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E8=BF=99=E4=B8=AA=E6=96=B9=E6=B3=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为了解决在不同异步上下文中同步调用数据库可能引发的运行时错误,实现了一个新的、更健壮的同步方法 `PersonInfoManager.get_value`。 - 新方法能够正确处理已在运行的 asyncio 事件循环,提高了在混合代码环境中调用的稳定性。 - 全面替换了原有的 `get_value_sync` 方法调用,统一了同步获取用户信息的接口。 --- src/chat/utils/chat_message_builder.py | 4 +-- src/chat/utils/prompt.py | 2 +- src/chat/utils/utils.py | 2 +- src/person_info/person_info.py | 40 ++++++++++++++++++++++++- src/person_info/relationship_builder.py | 8 ++--- 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/chat/utils/chat_message_builder.py b/src/chat/utils/chat_message_builder.py index e49c218c4..b5fe33373 100644 --- a/src/chat/utils/chat_message_builder.py +++ b/src/chat/utils/chat_message_builder.py @@ -46,8 +46,8 @@ def replace_user_references_sync( if replace_bot_name and user_id == global_config.bot.qq_account: return f"{global_config.bot.nickname}(你)" person_id = PersonInfoManager.get_person_id(platform, user_id) - return person_info_manager.get_value_sync(person_id, "person_name") or user_id # type: ignore - + return person_info_manager.get_value(person_id, "person_name") or user_id # type: ignore + name_resolver = default_resolver # 处理回复格式 diff --git a/src/chat/utils/prompt.py b/src/chat/utils/prompt.py index 2f115aa98..db31acfa5 100644 --- a/src/chat/utils/prompt.py +++ b/src/chat/utils/prompt.py @@ -965,7 +965,7 @@ class Prompt: person_info_manager = get_person_info_manager() person_id = person_info_manager.get_person_id_by_person_name(sender) if person_id: - user_id = person_info_manager.get_value_sync(person_id, "user_id") + user_id = person_info_manager.get_value(person_id, "user_id") return str(user_id) if user_id else "" return "" diff --git a/src/chat/utils/utils.py b/src/chat/utils/utils.py index 99647e36c..5eb4cc991 100644 --- a/src/chat/utils/utils.py +++ b/src/chat/utils/utils.py @@ -663,7 +663,7 @@ def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Dict]]: if person_id: # get_value is async, so await it directly person_info_manager = get_person_info_manager() - person_name = person_info_manager.get_value_sync(person_id, "person_name") + person_name = person_info_manager.get_value(person_id, "person_name") target_info["person_id"] = person_id target_info["person_name"] = person_name diff --git a/src/person_info/person_info.py b/src/person_info/person_info.py index 3a036d029..f5bf8a515 100644 --- a/src/person_info/person_info.py +++ b/src/person_info/person_info.py @@ -512,6 +512,45 @@ class PersonInfoManager: logger.debug(f"删除失败:未找到 person_id={person_id} 或删除未影响行") + @staticmethod + def get_value(person_id: str, field_name: str) -> Any: + """获取单个字段值(同步版本)""" + if not person_id: + logger.debug("get_value获取失败:person_id不能为空") + return None + + import asyncio + + async def _get_record_sync(): + async with get_db_session() as session: + return (await session.execute(select(PersonInfo).where(PersonInfo.person_id == person_id))).scalar() + + try: + record = asyncio.run(_get_record_sync()) + except RuntimeError: + # 如果当前线程已经有事件循环在运行,则使用现有的循环 + loop = asyncio.get_running_loop() + record = loop.run_until_complete(_get_record_sync()) + + model_fields = [column.name for column in PersonInfo.__table__.columns] + + if field_name not in model_fields: + if field_name in person_info_default: + logger.debug(f"字段'{field_name}'不在SQLAlchemy模型中,使用默认配置值。") + return copy.deepcopy(person_info_default[field_name]) + else: + logger.debug(f"get_value查询失败:字段'{field_name}'未在SQLAlchemy模型和默认配置中定义。") + return None + + if record: + value = getattr(record, field_name) + if value is not None: + return value + else: + return copy.deepcopy(person_info_default.get(field_name)) + else: + return copy.deepcopy(person_info_default.get(field_name)) + @staticmethod async def get_values(person_id: str, field_names: list) -> dict: """获取指定person_id文档的多个字段值,若不存在该字段,则返回该字段的全局默认值""" @@ -550,7 +589,6 @@ class PersonInfoManager: result[field_name] = copy.deepcopy(person_info_default.get(field_name)) return result - @staticmethod async def get_specific_value_list( field_name: str, diff --git a/src/person_info/relationship_builder.py b/src/person_info/relationship_builder.py index 1ff90a99d..35ac76d7d 100644 --- a/src/person_info/relationship_builder.py +++ b/src/person_info/relationship_builder.py @@ -138,7 +138,7 @@ class RelationshipBuilder: "message_count": await self._count_messages_in_timerange(potential_start_time, message_time), } segments.append(new_segment) - person_name = get_person_info_manager().get_value_sync(person_id, "person_name") or person_id + person_name = get_person_info_manager().get_value(person_id, "person_name") or person_id logger.debug( f"{self.log_prefix} 眼熟用户 {person_name} 在 {time.strftime('%H:%M:%S', time.localtime(potential_start_time))} - {time.strftime('%H:%M:%S', time.localtime(message_time))} 之间有 {new_segment['message_count']} 条消息" ) @@ -178,7 +178,7 @@ class RelationshipBuilder: } segments.append(new_segment) person_info_manager = get_person_info_manager() - person_name = person_info_manager.get_value_sync(person_id, "person_name") or person_id + person_name = person_info_manager.get_value(person_id, "person_name") or person_id logger.debug( f"{self.log_prefix} 重新眼熟用户 {person_name} 创建新消息段(超过10条消息间隔): {new_segment}" ) @@ -368,8 +368,8 @@ class RelationshipBuilder: users_to_build_relationship = [] for person_id, segments in self.person_engaged_cache.items(): total_message_count = self._get_total_message_count(person_id) - person_name = get_person_info_manager().get_value_sync(person_id, "person_name") or person_id - + person_name = get_person_info_manager().get_value(person_id, "person_name") or person_id + if total_message_count >= max_build_threshold or ( total_message_count >= 5 and (immediate_build == person_id or immediate_build == "all") ):