diff --git a/.github/workflows/update_version_pr.yml b/.github/workflows/update_version_pr.yml index 6f650aa2..88cb1a34 100644 --- a/.github/workflows/update_version_pr.yml +++ b/.github/workflows/update_version_pr.yml @@ -18,7 +18,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - token: ${{ secrets.GH_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} - name: Read current version id: read_version @@ -62,7 +62,7 @@ jobs: if: steps.check_diff.outputs.version_changed == 'false' uses: peter-evans/create-pull-request@v7 with: - token: ${{ secrets.GH_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} branch: create-pr/update_version title: ":tada: chore(version): 自动更新版本到 ${{ steps.update_version.outputs.new_version }}" body: "This PR updates the version file." diff --git a/.gitignore b/.gitignore index 09193394..c21816ef 100644 --- a/.gitignore +++ b/.gitignore @@ -155,6 +155,6 @@ bot.py .idea/ resources/ /configs/config.py -configs/config.yaml +configs/config.yaml .vscode/launch.json -plugins_/ \ No newline at end of file +plugins_/ diff --git a/zhenxun/builtin_plugins/init/init_plugin.py b/zhenxun/builtin_plugins/init/init_plugin.py index 494ea1d1..d248e393 100644 --- a/zhenxun/builtin_plugins/init/init_plugin.py +++ b/zhenxun/builtin_plugins/init/init_plugin.py @@ -97,14 +97,15 @@ async def _handle_setting( async def fix_db_schema(): """修复数据库架构问题""" - from tortoise import connections + from tortoise.connection import connections + conn = connections.get("default") # 检查是否存在superuser列并处理 try: await conn.execute_query(""" DO $$ BEGIN - IF EXISTS (SELECT 1 FROM information_schema.columns + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='plugin_info' AND column_name='superuser') THEN ALTER TABLE plugin_info DROP COLUMN superuser; END IF; @@ -122,7 +123,7 @@ async def _(): """ # 修复数据库架构问题 await fix_db_schema() - + plugin_list: list[PluginInfo] = [] limit_list: list[PluginLimit] = [] module2id = {} @@ -150,26 +151,31 @@ async def _(): "is_show", "ignore_prompt", # 移除了 menu_type - # 确保 level, default_status, limit_superuser, cost_gold, impression, status, block_type 等用户配置不在此列表 + # 确保 level, default_status, limit_superuser, + # cost_gold, impression, status, block_type 等用户配置不在此列表 ] ) - + # # 验证更新是否成功 # updated_plugin = await PluginInfo.get(id=plugin.id) # if updated_plugin.menu_type != plugin.menu_type: - # logger.warning(f"插件 {plugin.name} 的menu_type更新失败: 期望值 '{plugin.menu_type}', 实际值 '{updated_plugin.menu_type}'") + # logger.warning( + # f"插件 {plugin.name} 的menu_type更新失败: " + # f"期望值 '{plugin.menu_type}', " + # f"实际值 '{updated_plugin.menu_type}'" + # ) # # 尝试单独更新menu_type # updated_plugin.menu_type = plugin.menu_type # await updated_plugin.save(update_fields=["menu_type"]) - + update_list.append(plugin) - + if create_list: await PluginInfo.bulk_create(create_list, 10) - + # 对于批量更新操作,逐个更新替代批量操作 # 这里不使用被注释的批量更新代码,而是在上面的循环中已经处理 - + await data_migration() await PluginInfo.filter(module_path__in=load_plugin).update(load_status=True) await PluginInfo.filter(module_path__not_in=load_plugin).update(load_status=False) @@ -441,4 +447,4 @@ async def group_migration(): if create_list: await GroupConsole.bulk_create(create_list, 10) group_file.unlink() - logger.info("迁移群组数据完成!") \ No newline at end of file + logger.info("迁移群组数据完成!") diff --git a/zhenxun/builtin_plugins/web_ui/__init__.py b/zhenxun/builtin_plugins/web_ui/__init__.py index b18e74bd..705d0057 100644 --- a/zhenxun/builtin_plugins/web_ui/__init__.py +++ b/zhenxun/builtin_plugins/web_ui/__init__.py @@ -93,6 +93,8 @@ WsApiRouter.include_router(chat_routes) async def _(): try: + _tasks = [] + async def log_sink(message: str): loop = None if not loop: @@ -102,7 +104,10 @@ async def _(): logger.warning("Web Ui log_sink", e=e) if not loop: loop = asyncio.new_event_loop() - loop.create_task(LOG_STORAGE.add(message.rstrip("\n"))) + task = loop.create_task(LOG_STORAGE.add(message.rstrip("\n"))) + _tasks.append(task) + while _tasks and _tasks[0].done(): + _tasks.pop(0) logger_.add( log_sink, colorize=True, filter=default_filter, format=default_format diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/__init__.py b/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/__init__.py index 31db911a..c04234ce 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/__init__.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/__init__.py @@ -9,13 +9,13 @@ from ....base_model import Result from ....utils import authentication from .data_source import ApiDataSource from .model import ( + BatchUpdatePlugins, PluginCount, PluginDetail, PluginInfo, PluginSwitch, - UpdatePlugin, - BatchUpdatePlugins, RenameMenuTypePayload, + UpdatePlugin, ) router = APIRouter(prefix="/plugin") @@ -165,23 +165,31 @@ async def batch_update_plugin_config_api(params: BatchUpdatePlugins): # 这里我们返回包含错误详情的 200 OK,让前端处理 # 或者可以抛出 HTTPException # from fastapi import HTTPException - # raise HTTPException(status_code=400, detail={"message": "部分插件更新失败", "errors": result["errors"]}) + # raise HTTPException( + # status_code=400, + # detail={"message": "部分插件更新失败", "errors": result["errors"]} + # ) pass # 暂时只返回结果字典 return result # 新增:重命名菜单类型路由 @router.put( - "/menu_type/rename", - dependencies=[authentication()], + "/menu_type/rename", + dependencies=[authentication()], response_model=Result, summary="重命名菜单类型" ) async def rename_menu_type_api(payload: RenameMenuTypePayload) -> Result: try: - result = await ApiDataSource.rename_menu_type(old_name=payload.old_name, new_name=payload.new_name) + result = await ApiDataSource.rename_menu_type( + old_name=payload.old_name, new_name=payload.new_name) if result.get("success"): - return Result.ok(info=result.get("info", f"成功将 {result.get('updated_count', 0)} 个插件的菜单类型从 '{payload.old_name}' 修改为 '{payload.new_name}'")) + return Result.ok(info=result.get( + "info", + f"成功将 {result.get('updated_count', 0)} 个插件的菜单类型" + f"从 '{payload.old_name}' 修改为 '{payload.new_name}'" + )) else: # 这种情况理论上不会发生,因为 rename_menu_type 失败会抛异常 return Result.fail(info=result.get("info", "重命名失败")) diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/data_source.py b/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/data_source.py index 82dd1554..56331b54 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/data_source.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/data_source.py @@ -2,12 +2,12 @@ import re import cattrs from fastapi import Query +from tortoise.exceptions import DoesNotExist from zhenxun.configs.config import Config from zhenxun.configs.utils import ConfigGroup from zhenxun.models.plugin_info import PluginInfo as DbPluginInfo from zhenxun.utils.enum import BlockType, PluginType -from tortoise.exceptions import DoesNotExist from .model import ( BatchUpdatePlugins, @@ -134,7 +134,7 @@ class ApiDataSource: errors.append( { "module": item.module, - "error": f"Save block_type failed: {str(e_save)}", + "error": f"Save block_type failed: {e_save!s}", } ) plugin_changed_other = False @@ -158,7 +158,7 @@ class ApiDataSource: errors.append( { "module": "batch_update_other", - "error": f"Bulk update failed: {str(e_bulk)}", + "error": f"Bulk update failed: {e_bulk!s}", } ) @@ -217,8 +217,12 @@ class ApiDataSource: if not old_name or not new_name: raise ValueError("旧名称和新名称都不能为空") if old_name == new_name: - return {"success": True, "updated_count": 0, "info": "新旧名称相同,无需更新"} - + return { + "success": True, + "updated_count": 0, + "info": "新旧名称相同,无需更新" + } + # 检查新名称是否已存在(理论上前端会校验,后端再保险一次) exists = await DbPluginInfo.filter(menu_type=new_name).exists() if exists: @@ -226,11 +230,12 @@ class ApiDataSource: try: # 使用 filter().update() 进行批量更新 - updated_count = await DbPluginInfo.filter(menu_type=old_name).update(menu_type=new_name) + updated_count = await DbPluginInfo.filter(menu_type=old_name).update( + menu_type=new_name) return {"success": True, "updated_count": updated_count} except Exception as e: # 可以添加更详细的日志记录 - raise RuntimeError(f"数据库更新菜单类型失败: {str(e)}") + raise RuntimeError(f"数据库更新菜单类型失败: {e!s}") @classmethod async def get_plugin_detail(cls, module: str) -> PluginDetail: diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/model.py b/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/model.py index b943641f..79b7352b 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/model.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/plugin_manage/model.py @@ -113,11 +113,13 @@ class BatchUpdatePluginItem(BaseModel): module: str = Field(..., description="插件模块名") default_status: bool | None = Field(None, description="默认状态(开关)") menu_type: str | None = Field(None, description="菜单类型") - block_type: BlockType | None = Field(None, description="插件禁用状态 (None: 启用, ALL: 禁用)") + block_type: BlockType | None = Field( + None, description="插件禁用状态 (None: 启用, ALL: 禁用)") class BatchUpdatePlugins(BaseModel): - updates: list[BatchUpdatePluginItem] = Field(..., description="要批量更新的插件列表") + updates: list[BatchUpdatePluginItem] = Field( + ..., description="要批量更新的插件列表") class PluginDetail(PluginInfo): diff --git a/zhenxun/configs/utils/__init__.py b/zhenxun/configs/utils/__init__.py index 83d9de39..f3da2c52 100644 --- a/zhenxun/configs/utils/__init__.py +++ b/zhenxun/configs/utils/__init__.py @@ -366,13 +366,13 @@ class ConfigsManager: if not module or not key: raise ValueError("add_plugin_config: module和key不能为为空") - + key_upper = key.upper() self.add_module.append(f"{module}:{key}".lower()) - + # 检查配置是否已存在 config_exists = module in self._data and key_upper in self._data[module].configs - + # 如果配置已存在,仅更新元数据,保留原始值 if config_exists: config = self._data[module].configs[key_upper] @@ -397,7 +397,7 @@ class ConfigsManager: default_value=default_value, type=type, ) - + # 同时更新simple_data if module not in self._simple_data: self._simple_data[module] = {} @@ -512,7 +512,7 @@ class ConfigsManager: _yaml.dump(self._simple_data, f) # 原子替换 temp_simple_file.replace(self._simple_file) - + path = path or self.file data = {} for module in self._data: @@ -522,7 +522,7 @@ class ConfigsManager: del value["type"] del value["arg_parser"] data[module][config] = value - + # 使用临时文件进行原子写入 temp_file = Path(str(path) + ".tmp") with open(temp_file, "w", encoding="utf8") as f: @@ -530,7 +530,7 @@ class ConfigsManager: # 原子替换 temp_file.replace(path) except Exception as e: - logger.error(f"保存配置文件失败: {str(e)}") + logger.error(f"保存配置文件失败: {e!s}") def reload(self): """重新加载配置文件""" @@ -540,24 +540,24 @@ class ConfigsManager: if self._simple_file.exists(): with open(self._simple_file, encoding="utf8") as f: self._simple_data = _yaml.load(f) - + # 检查加载的数据是否为None if self._simple_data is None: logger.error("配置文件为空或格式错误,保留原有配置") self._simple_data = {} - + # 更新配置值 for key in self._simple_data.keys(): for k in self._simple_data[key].keys(): if key in self._data and k in self._data[key].configs: self._data[key].configs[k].value = self._simple_data[key][k] - + self.save() except ScannerError as e: - logger.error(f"配置文件解析失败: {str(e)},保留现有配置") + logger.error(f"配置文件解析失败: {e!s},保留现有配置") self._data = backup_data except Exception as e: - logger.error(f"重新加载配置失败: {str(e)}") + logger.error(f"重新加载配置失败: {e!s}") # 发生错误时恢复到备份配置 self._data = backup_data