From 3696c298f817037ab0ae9a68f409032b11160f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A2=A8=E6=A2=93=E6=9F=92?= <1787882683@qq.com> Date: Sat, 27 Dec 2025 21:00:07 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E6=96=B0=E5=A2=9E=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E6=9C=AC=E5=9C=B0=E5=B7=B2=E5=AE=89=E8=A3=85=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=20README=20=E6=96=87=E4=BB=B6=E7=9A=84=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/webui/expression_routes.py | 6 +++ src/webui/plugin_routes.py | 73 ++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/src/webui/expression_routes.py b/src/webui/expression_routes.py index 7090694e..3a3ae2e6 100644 --- a/src/webui/expression_routes.py +++ b/src/webui/expression_routes.py @@ -23,6 +23,8 @@ class ExpressionResponse(BaseModel): last_active_time: float chat_id: str create_date: Optional[float] + checked: bool + rejected: bool class ExpressionListResponse(BaseModel): @@ -56,6 +58,8 @@ class ExpressionUpdateRequest(BaseModel): situation: Optional[str] = None style: Optional[str] = None chat_id: Optional[str] = None + checked: Optional[bool] = None + rejected: Optional[bool] = None class ExpressionUpdateResponse(BaseModel): @@ -98,6 +102,8 @@ def expression_to_response(expression: Expression) -> ExpressionResponse: last_active_time=expression.last_active_time, chat_id=expression.chat_id, create_date=expression.create_date, + checked=expression.checked, + rejected=expression.rejected, ) diff --git a/src/webui/plugin_routes.py b/src/webui/plugin_routes.py index df459e5e..e85e1263 100644 --- a/src/webui/plugin_routes.py +++ b/src/webui/plugin_routes.py @@ -1397,6 +1397,79 @@ async def get_installed_plugins( raise HTTPException(status_code=500, detail=f"服务器错误: {str(e)}") from e +@router.get("/local-readme/{plugin_id}") +async def get_local_plugin_readme( + plugin_id: str, maibot_session: Optional[str] = Cookie(None), authorization: Optional[str] = Header(None) +) -> Dict[str, Any]: + """ + 获取本地已安装插件的 README 文件内容 + + Args: + plugin_id: 插件 ID + + Returns: + 包含 success 和 data(README 内容) 的字典,如果文件不存在则返回 success=False + """ + # Token 验证 + token = get_token_from_cookie_or_header(maibot_session, authorization) + token_manager = get_token_manager() + if not token or not token_manager.verify_token(token): + raise HTTPException(status_code=401, detail="未授权:无效的访问令牌") + + logger.info(f"获取本地插件 README: {plugin_id}") + + try: + plugins_dir = Path("plugins") + + # 查找插件目录 + plugin_path = None + for folder in plugins_dir.iterdir(): + if not folder.is_dir(): + continue + + manifest_path = folder / "_manifest.json" + if manifest_path.exists(): + try: + import json as json_module + with open(manifest_path, "r", encoding="utf-8") as f: + manifest = json_module.load(f) + + # 检查是否匹配 plugin_id + if manifest.get("id") == plugin_id: + plugin_path = folder + break + except Exception: + continue + + if not plugin_path: + return {"success": False, "error": "插件未安装"} + + # 查找 README 文件(支持多种命名) + readme_files = ["README.md", "readme.md", "Readme.md", "README.MD"] + readme_content = None + + for readme_name in readme_files: + readme_path = plugin_path / readme_name + if readme_path.exists(): + try: + with open(readme_path, "r", encoding="utf-8") as f: + readme_content = f.read() + logger.info(f"成功读取本地 README: {readme_path}") + break + except Exception as e: + logger.warning(f"读取 {readme_path} 失败: {e}") + continue + + if readme_content: + return {"success": True, "data": readme_content} + else: + return {"success": False, "error": "本地未找到 README 文件"} + + except Exception as e: + logger.error(f"获取本地 README 失败: {e}", exc_info=True) + return {"success": False, "error": str(e)} + + # ============ 插件配置管理 API ============