From 9cda0e5d8f2a930ca778b1b3b1e7014e9f6a1f44 Mon Sep 17 00:00:00 2001 From: HibiKier <45528451+HibiKier@users.noreply.github.com> Date: Thu, 15 May 2025 23:52:20 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E9=80=82=E9=85=8D=E6=96=B0?= =?UTF-8?q?=E7=89=88=E6=9C=ACwebui=20(#1905)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: molanp <104612722+molanp@users.noreply.github.com> Co-authored-by: BalconyJH <73932916+BalconyJH@users.noreply.github.com> --- zhenxun/builtin_plugins/help/_data_source.py | 4 +++- zhenxun/builtin_plugins/help/_utils.py | 10 ++++++++-- zhenxun/builtin_plugins/help/html_help.py | 17 +++++++++++++---- zhenxun/builtin_plugins/help/zhenxun_help.py | 14 ++++++++++++-- .../web_ui/api/menu/data_source.py | 5 ++++- .../web_ui/api/tabs/main/__init__.py | 3 ++- .../web_ui/api/tabs/main/data_source.py | 2 +- .../web_ui/api/tabs/manage/chat.py | 11 +++++++---- .../web_ui/api/tabs/manage/model.py | 2 ++ .../web_ui/api/tabs/plugin_manage/__init__.py | 6 +++++- .../api/tabs/plugin_manage/data_source.py | 4 ++++ .../web_ui/api/tabs/plugin_manage/model.py | 7 +++++++ .../web_ui/api/tabs/system/__init__.py | 12 ++++++++++++ .../web_ui/api/tabs/system/model.py | 4 ++++ zhenxun/builtin_plugins/web_ui/utils.py | 18 +++++++++++++++++- 15 files changed, 101 insertions(+), 18 deletions(-) diff --git a/zhenxun/builtin_plugins/help/_data_source.py b/zhenxun/builtin_plugins/help/_data_source.py index cfaa4503..86f42536 100644 --- a/zhenxun/builtin_plugins/help/_data_source.py +++ b/zhenxun/builtin_plugins/help/_data_source.py @@ -40,7 +40,9 @@ async def create_help_img( match help_type: case "html": - result = BuildImage.open(await build_html_image(group_id, is_detail)) + result = BuildImage.open( + await build_html_image(session, group_id, is_detail) + ) case "zhenxun": result = BuildImage.open( await build_zhenxun_image(session, group_id, is_detail) diff --git a/zhenxun/builtin_plugins/help/_utils.py b/zhenxun/builtin_plugins/help/_utils.py index 6c382c7d..0554fc8d 100644 --- a/zhenxun/builtin_plugins/help/_utils.py +++ b/zhenxun/builtin_plugins/help/_utils.py @@ -1,5 +1,8 @@ from collections.abc import Callable +from nonebot_plugin_uninfo import Uninfo + +from zhenxun.models.bot_console import BotConsole from zhenxun.models.group_console import GroupConsole from zhenxun.models.plugin_info import PluginInfo from zhenxun.utils.enum import PluginType @@ -27,13 +30,15 @@ async def sort_type() -> dict[str, list[PluginInfo]]: async def classify_plugin( - group_id: str | None, is_detail: bool, handle: Callable + session: Uninfo, group_id: str | None, is_detail: bool, handle: Callable ) -> dict[str, list]: """对插件进行分类并判断状态 参数: + session: Uninfo对象 group_id: 群组id is_detail: 是否详细帮助 + handle: 回调方法 返回: dict[str, list[Item]]: 分类插件数据 @@ -41,9 +46,10 @@ async def classify_plugin( sort_data = await sort_type() classify: dict[str, list] = {} group = await GroupConsole.get_or_none(group_id=group_id) if group_id else None + bot = await BotConsole.get_or_none(bot_id=session.self_id) for menu, value in sort_data.items(): for plugin in value: if not classify.get(menu): classify[menu] = [] - classify[menu].append(handle(plugin, group, is_detail)) + classify[menu].append(handle(bot, plugin, group, is_detail)) return classify diff --git a/zhenxun/builtin_plugins/help/html_help.py b/zhenxun/builtin_plugins/help/html_help.py index 1815b99a..7c552a0d 100644 --- a/zhenxun/builtin_plugins/help/html_help.py +++ b/zhenxun/builtin_plugins/help/html_help.py @@ -2,9 +2,11 @@ import os import random from nonebot_plugin_htmlrender import template_to_pic +from nonebot_plugin_uninfo import Uninfo from pydantic import BaseModel from zhenxun.configs.path_config import TEMPLATE_PATH +from zhenxun.models.bot_console import BotConsole from zhenxun.models.group_console import GroupConsole from zhenxun.models.plugin_info import PluginInfo from zhenxun.utils.enum import BlockType @@ -48,11 +50,12 @@ ICON2STR = { def __handle_item( - plugin: PluginInfo, group: GroupConsole | None, is_detail: bool + bot: BotConsole, plugin: PluginInfo, group: GroupConsole | None, is_detail: bool ) -> Item: """构造Item 参数: + bot: BotConsole plugin: PluginInfo group: 群组 is_detail: 是否详细 @@ -73,10 +76,13 @@ def __handle_item( ]: sta = 2 if group: - if f"{plugin.module}:super," in group.block_plugin: + if f"{plugin.module}," in group.superuser_block_plugin: sta = 2 if f"{plugin.module}," in group.block_plugin: sta = 1 + if bot: + if f"{plugin.module}," in bot.block_plugins: + sta = 2 return Item(plugin_name=plugin.name, sta=sta) @@ -119,14 +125,17 @@ def build_plugin_data(classify: dict[str, list[Item]]) -> list[dict[str, str]]: return plugin_list -async def build_html_image(group_id: str | None, is_detail: bool) -> bytes: +async def build_html_image( + session: Uninfo, group_id: str | None, is_detail: bool +) -> bytes: """构造HTML帮助图片 参数: + session: Uninfo group_id: 群号 is_detail: 是否详细帮助 """ - classify = await classify_plugin(group_id, is_detail, __handle_item) + classify = await classify_plugin(session, group_id, is_detail, __handle_item) plugin_list = build_plugin_data(classify) return await template_to_pic( template_path=str((TEMPLATE_PATH / "menu").absolute()), diff --git a/zhenxun/builtin_plugins/help/zhenxun_help.py b/zhenxun/builtin_plugins/help/zhenxun_help.py index f6d930e6..b96d3c59 100644 --- a/zhenxun/builtin_plugins/help/zhenxun_help.py +++ b/zhenxun/builtin_plugins/help/zhenxun_help.py @@ -6,6 +6,7 @@ from pydantic import BaseModel from zhenxun.configs.config import BotConfig from zhenxun.configs.path_config import TEMPLATE_PATH from zhenxun.configs.utils import PluginExtraData +from zhenxun.models.bot_console import BotConsole from zhenxun.models.group_console import GroupConsole from zhenxun.models.plugin_info import PluginInfo from zhenxun.utils.enum import BlockType @@ -21,12 +22,19 @@ class Item(BaseModel): """插件命令""" -def __handle_item(plugin: PluginInfo, group: GroupConsole | None, is_detail: bool): +def __handle_item( + bot: BotConsole | None, + plugin: PluginInfo, + group: GroupConsole | None, + is_detail: bool, +): """构造Item 参数: + bot: BotConsole plugin: PluginInfo group: 群组 + is_detail: 是否为详细 返回: Item: Item @@ -40,6 +48,8 @@ def __handle_item(plugin: PluginInfo, group: GroupConsole | None, is_detail: boo plugin.name = f"{plugin.name}(不可用)" elif group and f"{plugin.module}," in group.block_plugin: plugin.name = f"{plugin.name}(不可用)" + elif bot and f"{plugin.module}," in bot.block_plugins: + plugin.name = f"{plugin.name}(不可用)" commands = [] nb_plugin = nonebot.get_plugin_by_module_name(plugin.module_path) if is_detail and nb_plugin and nb_plugin.metadata and nb_plugin.metadata.extra: @@ -142,7 +152,7 @@ async def build_zhenxun_image( group_id: 群号 is_detail: 是否详细帮助 """ - classify = await classify_plugin(group_id, is_detail, __handle_item) + classify = await classify_plugin(session, group_id, is_detail, __handle_item) plugin_list = build_plugin_data(classify) platform = PlatformUtils.get_platform(session) bot_id = BotConfig.get_qbot_uid(session.self_id) or session.self_id diff --git a/zhenxun/builtin_plugins/web_ui/api/menu/data_source.py b/zhenxun/builtin_plugins/web_ui/api/menu/data_source.py index 9cfcd244..14f5c928 100644 --- a/zhenxun/builtin_plugins/web_ui/api/menu/data_source.py +++ b/zhenxun/builtin_plugins/web_ui/api/menu/data_source.py @@ -46,7 +46,10 @@ class MenuManage: icon="database", ), MenuItem( - name="系统信息", module="system", router="/system", icon="system" + name="文件管理", module="system", router="/system", icon="system" + ), + MenuItem( + name="关于我们", module="about", router="/about", icon="about" ), ] self.save() diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/main/__init__.py b/zhenxun/builtin_plugins/web_ui/api/tabs/main/__init__.py index 36059101..f93d0ab1 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/main/__init__.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/main/__init__.py @@ -16,7 +16,7 @@ from zhenxun.utils.platform import PlatformUtils from ....base_model import Result from ....config import QueryDateType -from ....utils import authentication, get_system_status +from ....utils import authentication, clear_help_image, get_system_status from .data_source import ApiDataSource from .model import ( ActiveGroup, @@ -234,6 +234,7 @@ async def _(param: BotManageUpdateParam): bot_data.block_plugins = CommonUtils.convert_module_format(param.block_plugins) bot_data.block_tasks = CommonUtils.convert_module_format(param.block_tasks) await bot_data.save(update_fields=["block_plugins", "block_tasks"]) + clear_help_image() return Result.ok() except Exception as e: logger.error(f"{router.prefix}/update_bot_manage 调用错误", "WebUi", e=e) diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py b/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py index f9ff6fca..e87647dd 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py @@ -92,7 +92,7 @@ class ApiDataSource: """ version_file = Path() / "__version__" if version_file.exists(): - if text := version_file.open().read(): + if text := version_file.open(encoding="utf-8").read(): return text.replace("__version__: ", "").strip() return "unknown" diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/manage/chat.py b/zhenxun/builtin_plugins/web_ui/api/tabs/manage/chat.py index d20149fb..389546ca 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/manage/chat.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/manage/chat.py @@ -1,3 +1,5 @@ +from datetime import datetime + from fastapi import APIRouter import nonebot from nonebot import on_message @@ -49,13 +51,14 @@ async def message_handle( message: UniMsg, group_id: str | None, ): + time = str(datetime.now().replace(microsecond=0)) messages = [] for m in message: if isinstance(m, Text | str): - messages.append(MessageItem(type="text", msg=str(m))) + messages.append(MessageItem(type="text", msg=str(m), time=time)) elif isinstance(m, Image): if m.url: - messages.append(MessageItem(type="img", msg=m.url)) + messages.append(MessageItem(type="img", msg=m.url, time=time)) elif isinstance(m, At): if group_id: if m.target == "0": @@ -72,9 +75,9 @@ async def message_handle( uname = group_user.user_name if m.target not in ID2NAME[group_id]: ID2NAME[group_id][m.target] = uname - messages.append(MessageItem(type="at", msg=f"@{uname}")) + messages.append(MessageItem(type="at", msg=f"@{uname}", time=time)) elif isinstance(m, Hyper): - messages.append(MessageItem(type="text", msg="[分享消息]")) + messages.append(MessageItem(type="text", msg="[分享消息]", time=time)) return messages diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/manage/model.py b/zhenxun/builtin_plugins/web_ui/api/tabs/manage/model.py index 7149cee1..68772d0f 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/manage/model.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/manage/model.py @@ -237,6 +237,8 @@ class MessageItem(BaseModel): """消息类型""" msg: str """内容""" + time: str + """发送日期""" class Message(BaseModel): 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 45878880..9dd134a4 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 @@ -6,7 +6,7 @@ from zhenxun.services.log import logger from zhenxun.utils.enum import BlockType, PluginType from ....base_model import Result -from ....utils import authentication +from ....utils import authentication, clear_help_image from .data_source import ApiDataSource from .model import ( BatchUpdatePlugins, @@ -80,6 +80,7 @@ async def _() -> Result[PluginCount]: async def _(param: UpdatePlugin) -> Result: try: await ApiDataSource.update_plugin(param) + clear_help_image() return Result.ok(info="已经帮你写好啦!") except (ValueError, KeyError): return Result.fail("插件数据不存在...") @@ -107,6 +108,7 @@ async def _(param: PluginSwitch) -> Result: db_plugin.block_type = None db_plugin.status = True await db_plugin.save() + clear_help_image() return Result.ok(info="成功改变了开关状态!") except Exception as e: logger.error(f"{router.prefix}/change_switch 调用错误", "WebUi", e=e) @@ -173,6 +175,7 @@ async def batch_update_plugin_config_api( updated_count=result_dict["updated_count"], errors=result_dict["errors"], ) + clear_help_image() return Result.ok(result_model, "插件配置更新完成") except Exception as e: logger.error(f"{router.prefix}/plugins/batch_update 调用错误", "WebUi", e=e) @@ -192,6 +195,7 @@ async def rename_menu_type_api(payload: RenameMenuTypePayload) -> Result: old_name=payload.old_name, new_name=payload.new_name ) if result.get("success"): + clear_help_image() return Result.ok( 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 d525c9bf..0f2c3676 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 @@ -52,6 +52,10 @@ class ApiDataSource: status=plugin.status, author=plugin.author, block_type=plugin.block_type, + is_builtin="builtin_plugins" in plugin.module_path + or plugin.plugin_type == PluginType.HIDDEN, + allow_setting=plugin.plugin_type != PluginType.HIDDEN, + allow_switch=plugin.plugin_type != PluginType.HIDDEN, ) plugin_list.append(plugin_info) return plugin_list 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 c2bcc4bb..579f3104 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 @@ -78,6 +78,13 @@ class PluginInfo(BaseModel): author: str | None = None """作者""" block_type: BlockType | None = Field(None, description="插件禁用状态 (None: 启用)") + """禁用状态""" + is_builtin: bool = False + """是否为内置插件""" + allow_switch: bool = True + """是否允许开关""" + allow_setting: bool = True + """是否允许设置""" class PluginConfig(BaseModel): diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/system/__init__.py b/zhenxun/builtin_plugins/web_ui/api/tabs/system/__init__.py index aa92306a..ffcd05be 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/system/__init__.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/system/__init__.py @@ -36,6 +36,8 @@ async def _(path: str | None = None) -> Result[list[DirFile]]: is_image=is_image, name=file, parent=path, + size=None if file_path.is_dir() else file_path.stat().st_size, + mtime=file_path.stat().st_mtime, ) ) return Result.ok(data_list) @@ -215,3 +217,13 @@ async def _(full_path: str) -> Result[str]: return Result.ok(BuildImage.open(path).pic2bs4()) except Exception as e: return Result.warning_(f"获取图片失败: {e!s}") + + +@router.get( + "/ping", + response_model=Result[str], + response_class=JSONResponse, + description="检查服务器状态", +) +async def _() -> Result[str]: + return Result.ok("pong") diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/system/model.py b/zhenxun/builtin_plugins/web_ui/api/tabs/system/model.py index 3c2357f2..2959a0e1 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/system/model.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/system/model.py @@ -14,6 +14,10 @@ class DirFile(BaseModel): """文件夹或文件名称""" parent: str | None = None """父级""" + size: int | None = None + """文件大小""" + mtime: float | None = None + """修改时间""" class DeleteFile(BaseModel): diff --git a/zhenxun/builtin_plugins/web_ui/utils.py b/zhenxun/builtin_plugins/web_ui/utils.py index df2fdd35..a7e22a07 100644 --- a/zhenxun/builtin_plugins/web_ui/utils.py +++ b/zhenxun/builtin_plugins/web_ui/utils.py @@ -11,7 +11,7 @@ import psutil import ujson as json from zhenxun.configs.config import Config -from zhenxun.configs.path_config import DATA_PATH +from zhenxun.configs.path_config import DATA_PATH, IMAGE_PATH from .base_model import SystemFolderSize, SystemStatus, User @@ -28,6 +28,22 @@ if token_file.exists(): token_data = json.load(open(token_file, encoding="utf8")) +GROUP_HELP_PATH = DATA_PATH / "group_help" +SIMPLE_HELP_IMAGE = IMAGE_PATH / "SIMPLE_HELP.png" +SIMPLE_DETAIL_HELP_IMAGE = IMAGE_PATH / "SIMPLE_DETAIL_HELP.png" + + +def clear_help_image(): + """清理帮助图片""" + if SIMPLE_HELP_IMAGE.exists(): + SIMPLE_HELP_IMAGE.unlink() + if SIMPLE_DETAIL_HELP_IMAGE.exists(): + SIMPLE_DETAIL_HELP_IMAGE.unlink() + for file in GROUP_HELP_PATH.iterdir(): + if file.is_file(): + file.unlink() + + def get_user(uname: str) -> User | None: """获取账号密码