From f68b9aa1096de81a21b0eaca6e600e85b4641b8b Mon Sep 17 00:00:00 2001 From: Ronifue Date: Sat, 29 Nov 2025 15:53:33 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E5=AF=B9=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8C=96=E5=A4=84=E7=90=86=E7=9A=84toml=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=EF=BC=8C=E4=BB=A5=E5=8F=8A=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E9=80=9A=E7=9F=A5=E6=B6=88=E6=81=AF=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E7=9A=84=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../heart_flow/heartflow_message_processor.py | 5 ++ src/chat/message_receive/storage.py | 5 ++ src/common/toml_utils.py | 70 +++++++++++++++++++ src/config/config.py | 7 +- src/webui/config_routes.py | 21 +++--- src/webui/plugin_routes.py | 9 ++- 6 files changed, 97 insertions(+), 20 deletions(-) create mode 100644 src/common/toml_utils.py diff --git a/src/chat/heart_flow/heartflow_message_processor.py b/src/chat/heart_flow/heartflow_message_processor.py index d0e1f9c9..09a3a94d 100644 --- a/src/chat/heart_flow/heartflow_message_processor.py +++ b/src/chat/heart_flow/heartflow_message_processor.py @@ -39,6 +39,11 @@ class HeartFCMessageReceiver: message_data: 原始消息字符串 """ try: + # 通知消息不处理 + if message.is_notify: + logger.debug("通知消息,跳过处理") + return + # 1. 消息解析与初始化 userinfo = message.message_info.user_info chat = message.chat_stream diff --git a/src/chat/message_receive/storage.py b/src/chat/message_receive/storage.py index 8d85d166..b70f30ff 100644 --- a/src/chat/message_receive/storage.py +++ b/src/chat/message_receive/storage.py @@ -33,6 +33,11 @@ class MessageStorage: async def store_message(message: Union[MessageSending, MessageRecv], chat_stream: ChatStream) -> None: """存储消息到数据库""" try: + # 通知消息不存储 + if isinstance(message, MessageRecv) and message.is_notify: + logger.debug("通知消息,跳过存储") + return + pattern = r".*?|.*?|.*?" # print(message) diff --git a/src/common/toml_utils.py b/src/common/toml_utils.py new file mode 100644 index 00000000..c5a0824a --- /dev/null +++ b/src/common/toml_utils.py @@ -0,0 +1,70 @@ +""" +TOML 工具函数 + +提供 TOML 文件的格式化保存功能,确保数组等元素以美观的多行格式输出。 +""" + +from typing import Any +import tomlkit +from tomlkit.items import AoT, Table, Array + + +def _format_toml_value(obj: Any, threshold: int, depth: int = 0) -> Any: + """递归格式化 TOML 值,将数组转换为多行格式""" + # 处理 AoT (Array of Tables) - 保持原样,递归处理内部 + if isinstance(obj, AoT): + for item in obj: + _format_toml_value(item, threshold, depth) + return obj + + # 处理字典类型 (dict 或 Table) + if isinstance(obj, (dict, Table)): + for k, v in obj.items(): + obj[k] = _format_toml_value(v, threshold, depth) + return obj + + # 处理列表类型 (list 或 Array) + if isinstance(obj, (list, Array)): + # 如果包含字典/表,视为 AoT 的列表形式或内联表数组,保持结构递归处理 + if obj and isinstance(obj[0], (dict, Table)): + for i, item in enumerate(obj): + # 原地修改或递归处理 + if isinstance(obj, list): + obj[i] = _format_toml_value(item, threshold, depth) + else: + _format_toml_value(item, threshold, depth) + return obj + + # 决定是否多行:仅在顶层且长度超过阈值时 + should_multiline = (depth == 0 and len(obj) > threshold) + + # 如果已经是 tomlkit Array,原地修改以保留注释 + if isinstance(obj, Array): + obj.multiline(should_multiline) + for i, item in enumerate(obj): + obj[i] = _format_toml_value(item, threshold, depth + 1) + return obj + + # 普通 list:转换为 tomlkit 数组 + arr = tomlkit.array() + arr.multiline(should_multiline) + + for item in obj: + arr.append(_format_toml_value(item, threshold, depth + 1)) + return arr + + # 其他基本类型直接返回 + return obj + + +def save_toml_with_format(data: Any, file_path: str, multiline_threshold: int = 1) -> None: + """格式化 TOML 数据并保存到文件""" + formatted = _format_toml_value(data, multiline_threshold) if multiline_threshold >= 0 else data + with open(file_path, "w", encoding="utf-8") as f: + tomlkit.dump(formatted, f) + + +def format_toml_string(data: Any, multiline_threshold: int = 1) -> str: + """格式化 TOML 数据并返回字符串""" + formatted = _format_toml_value(data, multiline_threshold) if multiline_threshold >= 0 else data + return tomlkit.dumps(formatted) diff --git a/src/config/config.py b/src/config/config.py index 1eca07fb..3f5db816 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -11,6 +11,7 @@ from rich.traceback import install from typing import List, Optional from src.common.logger import get_logger +from src.common.toml_utils import format_toml_string from src.config.config_base import ConfigBase from src.config.official_configs import ( BotConfig, @@ -252,7 +253,7 @@ def _update_config_generic(config_name: str, template_name: str): # 如果配置有更新,立即保存到文件 if config_updated: with open(old_config_path, "w", encoding="utf-8") as f: - f.write(tomlkit.dumps(old_config)) + f.write(format_toml_string(old_config)) logger.info(f"已保存更新后的{config_name}配置文件") else: logger.info(f"未检测到{config_name}模板默认值变动") @@ -313,9 +314,9 @@ def _update_config_generic(config_name: str, template_name: str): logger.info(f"开始合并{config_name}新旧配置...") _update_dict(new_config, old_config) - # 保存更新后的配置(保留注释和格式) + # 保存更新后的配置(保留注释和格式,数组多行格式化) with open(new_config_path, "w", encoding="utf-8") as f: - f.write(tomlkit.dumps(new_config)) + f.write(format_toml_string(new_config)) logger.info(f"{config_name}配置文件更新完成,建议检查新配置文件中的内容,以免丢失重要信息") diff --git a/src/webui/config_routes.py b/src/webui/config_routes.py index f26bcb09..5e54c8f7 100644 --- a/src/webui/config_routes.py +++ b/src/webui/config_routes.py @@ -8,6 +8,7 @@ from fastapi import APIRouter, HTTPException, Body from typing import Any, Annotated from src.common.logger import get_logger +from src.common.toml_utils import save_toml_with_format from src.config.config import Config, APIAdapterConfig, CONFIG_DIR, PROJECT_ROOT from src.config.official_configs import ( BotConfig, @@ -237,10 +238,9 @@ async def update_bot_config(config_data: ConfigBody): except Exception as e: raise HTTPException(status_code=400, detail=f"配置数据验证失败: {str(e)}") from e - # 保存配置文件 + # 保存配置文件(格式化数组为多行) config_path = os.path.join(CONFIG_DIR, "bot_config.toml") - with open(config_path, "w", encoding="utf-8") as f: - tomlkit.dump(config_data, f) + save_toml_with_format(config_data, config_path) logger.info("麦麦主程序配置已更新") return {"success": True, "message": "配置已保存"} @@ -261,10 +261,9 @@ async def update_model_config(config_data: ConfigBody): except Exception as e: raise HTTPException(status_code=400, detail=f"配置数据验证失败: {str(e)}") from e - # 保存配置文件 + # 保存配置文件(格式化数组为多行) config_path = os.path.join(CONFIG_DIR, "model_config.toml") - with open(config_path, "w", encoding="utf-8") as f: - tomlkit.dump(config_data, f) + save_toml_with_format(config_data, config_path) logger.info("模型配置已更新") return {"success": True, "message": "配置已保存"} @@ -312,9 +311,8 @@ async def update_bot_config_section(section_name: str, section_data: SectionBody except Exception as e: raise HTTPException(status_code=400, detail=f"配置数据验证失败: {str(e)}") from e - # 保存配置(tomlkit.dump 会保留注释) - with open(config_path, "w", encoding="utf-8") as f: - tomlkit.dump(config_data, f) + # 保存配置(格式化数组为多行,保留注释) + save_toml_with_format(config_data, config_path) logger.info(f"配置节 '{section_name}' 已更新(保留注释)") return {"success": True, "message": f"配置节 '{section_name}' 已保存"} @@ -411,9 +409,8 @@ async def update_model_config_section(section_name: str, section_data: SectionBo except Exception as e: raise HTTPException(status_code=400, detail=f"配置数据验证失败: {str(e)}") from e - # 保存配置(tomlkit.dump 会保留注释) - with open(config_path, "w", encoding="utf-8") as f: - tomlkit.dump(config_data, f) + # 保存配置(格式化数组为多行,保留注释) + save_toml_with_format(config_data, config_path) logger.info(f"配置节 '{section_name}' 已更新(保留注释)") return {"success": True, "message": f"配置节 '{section_name}' 已保存"} diff --git a/src/webui/plugin_routes.py b/src/webui/plugin_routes.py index 1236b02e..bf4784df 100644 --- a/src/webui/plugin_routes.py +++ b/src/webui/plugin_routes.py @@ -4,6 +4,7 @@ from typing import Optional, List, Dict, Any from pathlib import Path import json from src.common.logger import get_logger +from src.common.toml_utils import save_toml_with_format from src.config.config import MMC_VERSION from .git_mirror_service import get_git_mirror_service, set_update_progress_callback from .token_manager import get_token_manager @@ -1416,8 +1417,7 @@ async def update_plugin_config( # 更新值 for key, value in request.config.items(): existing_doc[key] = value - with open(config_path, "w", encoding="utf-8") as f: - tomlkit.dump(existing_doc, f) + save_toml_with_format(existing_doc, str(config_path)) logger.info(f"已更新插件配置: {plugin_id}") @@ -1544,9 +1544,8 @@ async def toggle_plugin(plugin_id: str, authorization: Optional[str] = Header(No new_enabled = not current_enabled config["plugin"]["enabled"] = new_enabled - # 写入配置(保留注释) - with open(config_path, "w", encoding="utf-8") as f: - tomlkit.dump(config, f) + # 写入配置(保留注释,格式化数组) + save_toml_with_format(config, str(config_path)) status = "启用" if new_enabled else "禁用" logger.info(f"已{status}插件: {plugin_id}") From 08c0b5929e015f9721c57c3d1051ca29e1296d8c Mon Sep 17 00:00:00 2001 From: Ronifue Date: Sat, 29 Nov 2025 16:16:31 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E4=B8=80=E4=B8=AA?= =?UTF-8?q?Maibot=E9=81=87=E4=B8=8D=E5=88=B0=E4=BD=86=E6=98=AF=E4=B8=8D?= =?UTF-8?q?=E8=83=BD=E6=B6=B5=E7=9B=96Toml=E6=95=B4=E4=B8=AA=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/toml_utils.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/common/toml_utils.py b/src/common/toml_utils.py index c5a0824a..0c34f6ab 100644 --- a/src/common/toml_utils.py +++ b/src/common/toml_utils.py @@ -25,14 +25,11 @@ def _format_toml_value(obj: Any, threshold: int, depth: int = 0) -> Any: # 处理列表类型 (list 或 Array) if isinstance(obj, (list, Array)): - # 如果包含字典/表,视为 AoT 的列表形式或内联表数组,保持结构递归处理 - if obj and isinstance(obj[0], (dict, Table)): + # 如果是纯 list (非 tomlkit Array) 且包含字典/表,视为 AoT 的列表形式 + # 保持结构递归处理,避免转换为 Inline Table Array (因为 Inline Table 必须单行,复杂对象不友好) + if isinstance(obj, list) and not isinstance(obj, Array) and obj and isinstance(obj[0], (dict, Table)): for i, item in enumerate(obj): - # 原地修改或递归处理 - if isinstance(obj, list): - obj[i] = _format_toml_value(item, threshold, depth) - else: - _format_toml_value(item, threshold, depth) + obj[i] = _format_toml_value(item, threshold, depth) return obj # 决定是否多行:仅在顶层且长度超过阈值时 @@ -67,4 +64,4 @@ def save_toml_with_format(data: Any, file_path: str, multiline_threshold: int = def format_toml_string(data: Any, multiline_threshold: int = 1) -> str: """格式化 TOML 数据并返回字符串""" formatted = _format_toml_value(data, multiline_threshold) if multiline_threshold >= 0 else data - return tomlkit.dumps(formatted) + return tomlkit.dumps(formatted) \ No newline at end of file