From 18f97b9bd5c5b496902cae7035c6a156e6fa81c7 Mon Sep 17 00:00:00 2001 From: HibiKier <775757368@qq.com> Date: Mon, 12 Aug 2024 22:26:13 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E6=B7=BB=E5=8A=A0=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=A5=BD=E5=8F=8B=E7=BE=A4=E7=BB=84=E4=B8=8E=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=85=B6=E4=BB=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/group_member_update/__init__.py | 34 ++- .../builtin_plugins/hooks/_auth_checker.py | 3 + zhenxun/builtin_plugins/init/__init__.py | 8 +- .../scheduler/auto_update_group.py | 6 +- zhenxun/plugins/one_friend/__init__.py | 8 +- zhenxun/plugins/parse_bilibili/__init__.py | 86 ++++---- zhenxun/plugins/parse_bilibili/get_image.py | 18 +- .../parse_bilibili/information_container.py | 9 +- zhenxun/plugins/restart/__init__.py | 96 +++++++++ .../web_ui/api/tabs/manage/__init__.py | 19 +- .../plugins/web_ui/api/tabs/manage/model.py | 10 + .../web_ui/api/tabs/system/__init__.py | 201 +++++++++++------- .../plugins/web_ui/api/tabs/system/model.py | 81 ++++--- zhenxun/plugins/word_bank/_model.py | 7 +- 14 files changed, 384 insertions(+), 202 deletions(-) create mode 100644 zhenxun/plugins/restart/__init__.py diff --git a/zhenxun/builtin_plugins/admin/group_member_update/__init__.py b/zhenxun/builtin_plugins/admin/group_member_update/__init__.py index c7036cc8..48508b21 100644 --- a/zhenxun/builtin_plugins/admin/group_member_update/__init__.py +++ b/zhenxun/builtin_plugins/admin/group_member_update/__init__.py @@ -1,8 +1,10 @@ +import nonebot from nonebot import on_notice from nonebot.adapters import Bot from nonebot.adapters.onebot.v11 import GroupIncreaseNoticeEvent from nonebot.plugin import PluginMetadata from nonebot_plugin_alconna import Alconna, Arparma, on_alconna +from nonebot_plugin_apscheduler import scheduler from nonebot_plugin_session import EventSession from zhenxun.configs.config import NICKNAME @@ -10,6 +12,7 @@ from zhenxun.configs.utils import PluginExtraData from zhenxun.services.log import logger from zhenxun.utils.enum import PluginType from zhenxun.utils.message import MessageUtils +from zhenxun.utils.platform import PlatformUtils from zhenxun.utils.rules import admin_check, ensure_group from ._data_source import MemberUpdateManage @@ -39,6 +42,9 @@ _matcher = on_alconna( ) +_notice = on_notice(priority=1, block=False) + + @_matcher.handle() async def _(bot: Bot, session: EventSession, arparma: Arparma): if gid := session.id3 or session.id2: @@ -50,9 +56,6 @@ async def _(bot: Bot, session: EventSession, arparma: Arparma): await MessageUtils.build_message("群组id为空...").send() -_notice = on_notice(priority=1, block=False) - - @_notice.handle() async def _(bot: Bot, event: GroupIncreaseNoticeEvent): # TODO: 其他适配器的加群自动更新群组成员信息 @@ -64,3 +67,28 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent): session=event.user_id, group_id=event.group_id, ) + + +@scheduler.scheduled_job( + "interval", + minutes=5, +) +async def _(): + for bot in nonebot.get_bots().values(): + if PlatformUtils.get_platform(bot) == "qq": + try: + group_list, _ = await PlatformUtils.get_group_list(bot) + if group_list: + for group in group_list: + try: + await MemberUpdateManage.update(bot, group.group_id) + logger.debug("自动更新群组成员信息成功...") + except Exception as e: + logger.error( + f"Bot: {bot.self_id} 自动更新群组成员信息成功", + target=group.group_id, + e=e, + ) + except Exception as e: + logger.error(f"Bot: {bot.self_id} 自动更新群组信息", e=e) + logger.debug(f"自动 Bot: {bot.self_id} 更新群组成员信息成功...") diff --git a/zhenxun/builtin_plugins/hooks/_auth_checker.py b/zhenxun/builtin_plugins/hooks/_auth_checker.py index 8a277165..e103cf95 100644 --- a/zhenxun/builtin_plugins/hooks/_auth_checker.py +++ b/zhenxun/builtin_plugins/hooks/_auth_checker.py @@ -236,6 +236,9 @@ class AuthChecker: LimitManage.unblock( matcher.plugin.name, user_id, group_id, channel_id ) + except AssertionError as e: + is_ignore = True + logger.debug("消息无法发送", session=session, e=e) if cost_gold and user_id: """花费金币""" try: diff --git a/zhenxun/builtin_plugins/init/__init__.py b/zhenxun/builtin_plugins/init/__init__.py index fa10c4d7..b3bab4db 100644 --- a/zhenxun/builtin_plugins/init/__init__.py +++ b/zhenxun/builtin_plugins/init/__init__.py @@ -23,13 +23,15 @@ async def _(bot: Bot): if PlatformUtils.get_platform(bot) == "qq": logger.debug(f"更新Bot: {bot.self_id} 的群认证...") group_list, _ = await PlatformUtils.get_group_list(bot) - gid_list = [g.group_id for g in group_list] + gid_list = [(g.group_id, g.group_name) for g in group_list] db_group_list = await GroupConsole.all().values_list("group_id", flat=True) create_list = [] update_id = [] - for gid in gid_list: + for gid, name in gid_list: if gid not in db_group_list: - create_list.append(GroupConsole(group_id=gid, group_flag=1)) + create_list.append( + GroupConsole(group_id=gid, group_name=name, group_flag=1) + ) else: update_id.append(gid) if create_list: diff --git a/zhenxun/builtin_plugins/scheduler/auto_update_group.py b/zhenxun/builtin_plugins/scheduler/auto_update_group.py index 8e62bc69..dda1f3e3 100644 --- a/zhenxun/builtin_plugins/scheduler/auto_update_group.py +++ b/zhenxun/builtin_plugins/scheduler/auto_update_group.py @@ -17,7 +17,7 @@ async def _(): try: await PlatformUtils.update_group(bot) except Exception as e: - logger.error(f"Bot: {bot.self_id} 自动更新群组信息", e=e) + logger.error(f"Bot: {bot.self_id} 自动更新群组信息", "自动更新群组", e=e) logger.info("自动更新群组成员信息成功...") @@ -33,5 +33,7 @@ async def _(): try: await PlatformUtils.update_friend(bot) except Exception as e: - logger.error(f"自动更新好友信息错误", "自动更新好友", e=e) + logger.error( + f"Bot: {bot.self_id} 自动更新好友信息错误", "自动更新好友", e=e + ) logger.info("自动更新好友信息成功...") diff --git a/zhenxun/plugins/one_friend/__init__.py b/zhenxun/plugins/one_friend/__init__.py index 0191084f..7fa7c8bf 100644 --- a/zhenxun/plugins/one_friend/__init__.py +++ b/zhenxun/plugins/one_friend/__init__.py @@ -3,9 +3,7 @@ from io import BytesIO from nonebot.adapters import Bot from nonebot.plugin import PluginMetadata -from nonebot_plugin_alconna import Alconna, Args -from nonebot_plugin_alconna import At as alcAt -from nonebot_plugin_alconna import Match, on_alconna +from nonebot_plugin_alconna import Alconna, Args, At, Match, on_alconna from nonebot_plugin_session import EventSession from zhenxun.configs.utils import PluginExtraData @@ -29,7 +27,7 @@ __plugin_meta__ = PluginMetadata( ) _matcher = on_alconna( - Alconna("one-friend", Args["text", str]["at?", alcAt]), priority=5, block=True + Alconna("one-friend", Args["text", str]["at?", At]), priority=5, block=True ) _matcher.shortcut( @@ -41,7 +39,7 @@ _matcher.shortcut( @_matcher.handle() -async def _(bot: Bot, text: str, at: Match[alcAt], session: EventSession): +async def _(bot: Bot, text: str, at: Match[At], session: EventSession): gid = session.id3 or session.id2 if not gid: await MessageUtils.build_message("群组id为空...").finish() diff --git a/zhenxun/plugins/parse_bilibili/__init__.py b/zhenxun/plugins/parse_bilibili/__init__.py index 1d319093..3269a526 100644 --- a/zhenxun/plugins/parse_bilibili/__init__.py +++ b/zhenxun/plugins/parse_bilibili/__init__.py @@ -115,27 +115,28 @@ async def _(session: EventSession, message: UniMsg): if get_url: # 将链接统一发送给处理函数 - vd_info, live_info, vd_url, live_url, image_info, image_url = ( - await parse_bili_url(get_url, information_container) - ) - if vd_info: + data = await parse_bili_url(get_url, information_container) + if data.vd_info: # 判断一定时间内是否解析重复内容,或者是第一次解析 if ( - vd_url in _tmp.keys() and time.time() - _tmp[vd_url] > repet_second - ) or vd_url not in _tmp.keys(): - pic = vd_info.get("pic", "") # 封面 - aid = vd_info.get("aid", "") # av号 - title = vd_info.get("title", "") # 标题 - author = vd_info.get("owner", {}).get("name", "") # UP主 - reply = vd_info.get("stat", {}).get("reply", "") # 回复 - favorite = vd_info.get("stat", {}).get("favorite", "") # 收藏 - coin = vd_info.get("stat", {}).get("coin", "") # 投币 - like = vd_info.get("stat", {}).get("like", "") # 点赞 - danmuku = vd_info.get("stat", {}).get("danmaku", "") # 弹幕 - ctime = vd_info["ctime"] + data.vd_url in _tmp.keys() + and time.time() - _tmp[data.vd_url] > repet_second + ) or data.vd_url not in _tmp.keys(): + pic = data.vd_info.get("pic", "") # 封面 + aid = data.vd_info.get("aid", "") # av号 + title = data.vd_info.get("title", "") # 标题 + author = data.vd_info.get("owner", {}).get("name", "") # UP主 + reply = data.vd_info.get("stat", {}).get("reply", "") # 回复 + favorite = data.vd_info.get("stat", {}).get("favorite", "") # 收藏 + coin = data.vd_info.get("stat", {}).get("coin", "") # 投币 + like = data.vd_info.get("stat", {}).get("like", "") # 点赞 + danmuku = data.vd_info.get("stat", {}).get("danmaku", "") # 弹幕 + ctime = data.vd_info["ctime"] date = time.strftime("%Y-%m-%d", time.localtime(ctime)) - logger.info(f"解析bilibili转发 {vd_url}", "b站解析", session=session) - _tmp[vd_url] = time.time() + logger.info( + f"解析bilibili转发 {data.vd_url}", "b站解析", session=session + ) + _tmp[data.vd_url] = time.time() _path = TEMP_PATH / f"{aid}.jpg" await AsyncHttpx.download_file(pic, _path) await MessageUtils.build_message( @@ -145,33 +146,40 @@ async def _(session: EventSession, message: UniMsg): ] ).send() - elif live_info: + elif data.live_info: if ( - live_url in _tmp.keys() and time.time() - _tmp[live_url] > repet_second - ) or live_url not in _tmp.keys(): - uid = live_info.get("uid", "") # 主播uid - title = live_info.get("title", "") # 直播间标题 - description = live_info.get("description", "") # 简介,可能会出现标签 - user_cover = live_info.get("user_cover", "") # 封面 - keyframe = live_info.get("keyframe", "") # 关键帧画面 - live_time = live_info.get("live_time", "") # 开播时间 - area_name = live_info.get("area_name", "") # 分区 - parent_area_name = live_info.get("parent_area_name", "") # 父分区 - logger.info(f"解析bilibili转发 {live_url}", "b站解析", session=session) - _tmp[live_url] = time.time() + data.live_url in _tmp.keys() + and time.time() - _tmp[data.live_url] > repet_second + ) or data.live_url not in _tmp.keys(): + uid = data.live_info.get("uid", "") # 主播uid + title = data.live_info.get("title", "") # 直播间标题 + description = data.live_info.get( + "description", "" + ) # 简介,可能会出现标签 + user_cover = data.live_info.get("user_cover", "") # 封面 + keyframe = data.live_info.get("keyframe", "") # 关键帧画面 + live_time = data.live_info.get("live_time", "") # 开播时间 + area_name = data.live_info.get("area_name", "") # 分区 + parent_area_name = data.live_info.get("parent_area_name", "") # 父分区 + logger.info( + f"解析bilibili转发 {data.live_url}", "b站解析", session=session + ) + _tmp[data.live_url] = time.time() await MessageUtils.build_message( [ Image(url=user_cover), f"开播用户:https://space.bilibili.com/{uid}\n开播时间:{live_time}\n直播分区:{parent_area_name}——>{area_name}\n标题:{title}\n简介:{description}\n直播截图:\n", Image(url=keyframe), - f"{live_url}", + f"{data.live_url}", ] ).send() - elif image_info: + elif data.image_info: if ( - image_url in _tmp.keys() - and time.time() - _tmp[image_url] > repet_second - ) or image_url not in _tmp.keys(): - logger.info(f"解析bilibili转发 {image_url}", "b站解析", session=session) - _tmp[image_url] = time.time() - await image_info.send() + data.image_url in _tmp.keys() + and time.time() - _tmp[data.image_url] > repet_second + ) or data.image_url not in _tmp.keys(): + logger.info( + f"解析bilibili转发 {data.image_url}", "b站解析", session=session + ) + _tmp[data.image_url] = time.time() + await data.image_info.send() diff --git a/zhenxun/plugins/parse_bilibili/get_image.py b/zhenxun/plugins/parse_bilibili/get_image.py index bbc005c5..3b8c70c8 100644 --- a/zhenxun/plugins/parse_bilibili/get_image.py +++ b/zhenxun/plugins/parse_bilibili/get_image.py @@ -1,5 +1,6 @@ import os import re +from pathlib import Path from nonebot_plugin_alconna import UniMessage @@ -11,14 +12,14 @@ from zhenxun.utils.message import MessageUtils from zhenxun.utils.user_agent import get_user_agent_str -async def resize(path: str): +async def resize(path: Path): """调整图像大小的异步函数 参数: path (str): 图像文件路径 """ - A = BuildImage(background=path) - await A.resize(0.5) + A = BuildImage.open(path) + await A.resize(0.8) await A.save(path) @@ -56,21 +57,18 @@ async def get_image(url) -> UniMessage | None: # 根据编号构建保存路径 if cv_number: - screenshot_path = f"{TEMP_PATH}/bilibili_cv_{cv_number}.png" + screenshot_path = TEMP_PATH / "bilibili_cv_{cv_number}.png" elif opus_number: - screenshot_path = f"{TEMP_PATH}/bilibili_opus_{opus_number}.png" + screenshot_path = TEMP_PATH / "bilibili_opus_{opus_number}.png" elif t_opus_number: - screenshot_path = f"{TEMP_PATH}/bilibili_opus_{t_opus_number}.png" + screenshot_path = TEMP_PATH / "bilibili_opus_{t_opus_number}.png" # t.bilibili.com和https://www.bilibili.com/opus在内容上是一样的,为便于维护,调整url至https://www.bilibili.com/opus/ url = f"https://www.bilibili.com/opus/{t_opus_number}" - if screenshot_path: try: # 如果文件不存在,进行截图 - if not os.path.exists(screenshot_path): + if not screenshot_path.exists(): # 创建页面 - # random.choice(),从列表中随机抽取一个对象 - user_agent = get_user_agent_str() try: async with AsyncPlaywright.new_page() as page: await page.set_viewport_size({"width": 5120, "height": 2560}) diff --git a/zhenxun/plugins/parse_bilibili/information_container.py b/zhenxun/plugins/parse_bilibili/information_container.py index 1cbf651f..ddb685f8 100644 --- a/zhenxun/plugins/parse_bilibili/information_container.py +++ b/zhenxun/plugins/parse_bilibili/information_container.py @@ -50,11 +50,4 @@ class InformationContainer: setattr(self, f"_{info_type}", new_value) def get_information(self): - return ( - self.vd_info, - self.live_info, - self.vd_url, - self.live_url, - self.image_info, - self.image_url, - ) + return self diff --git a/zhenxun/plugins/restart/__init__.py b/zhenxun/plugins/restart/__init__.py new file mode 100644 index 00000000..eed6fa17 --- /dev/null +++ b/zhenxun/plugins/restart/__init__.py @@ -0,0 +1,96 @@ +import os +import platform +from pathlib import Path + +import nonebot +from nonebot import on_command +from nonebot.adapters import Bot +from nonebot.params import ArgStr +from nonebot.permission import SUPERUSER +from nonebot.plugin import PluginMetadata +from nonebot.rule import to_me +from nonebot_plugin_session import EventSession + +from zhenxun.configs.config import NICKNAME +from zhenxun.configs.utils import PluginExtraData +from zhenxun.services.log import logger +from zhenxun.utils.enum import PluginType +from zhenxun.utils.message import MessageUtils +from zhenxun.utils.platform import PlatformUtils + +__plugin_meta__ = PluginMetadata( + name="重启", + description="执行脚本重启真寻", + usage=f""" + 重启 + """.strip(), + extra=PluginExtraData( + author="HibiKier", version="0.1", plugin_type=PluginType.SUPERUSER + ).dict(), +) + + +_matcher = on_command( + "重启", + permission=SUPERUSER, + rule=to_me(), + priority=1, + block=True, +) + +driver = nonebot.get_driver() + + +RESTART_MARK = Path() / "is_restart" + +RESTART_FILE = Path() / "restart.sh" + + +@_matcher.got( + "flag", + prompt=f"确定是否重启{NICKNAME}?确定请回复[是|好|确定](重启失败咱们将失去联系,请谨慎!)", +) +async def _(bot: Bot, session: EventSession, flag: str = ArgStr("flag")): + if flag.lower() in ["true", "是", "好", "确定", "确定是"]: + await MessageUtils.build_message(f"开始重启{NICKNAME}..请稍等...").send() + with open(RESTART_MARK, "w", encoding="utf8") as f: + f.write(f"{bot.self_id} {session.id1}") + logger.info("开始重启真寻...", "重启", session=session) + if str(platform.system()).lower() == "windows": + import sys + + python = sys.executable + os.execl(python, python, *sys.argv) + else: + os.system("./restart.sh") + else: + await MessageUtils.build_message("已取消操作...").send() + + +@driver.on_bot_connect +async def _(bot: Bot): + if str(platform.system()).lower() != "windows": + if not RESTART_FILE.exists(): + with open(RESTART_FILE, "w", encoding="utf8") as f: + f.write( + f"pid=$(netstat -tunlp | grep " + + str(bot.config.port) + + " | awk '{print $7}')\n" + "pid=${pid%/*}\n" + "kill -9 $pid\n" + "sleep 3\n" + "python3 bot.py" + ) + os.system("chmod +x ./restart.sh") + logger.info( + "已自动生成 restart.sh(重启) 文件,请检查脚本是否与本地指令符合..." + ) + if RESTART_MARK.exists(): + with open(RESTART_MARK, "r", encoding="utf8") as f: + bot_id, session_id = f.read().split() + if bot := nonebot.get_bot(bot_id): + if target := PlatformUtils.get_target(bot, session_id): + await MessageUtils.build_message(f"{NICKNAME}已成功重启!").send( + target, bot=bot + ) + RESTART_MARK.unlink() diff --git a/zhenxun/plugins/web_ui/api/tabs/manage/__init__.py b/zhenxun/plugins/web_ui/api/tabs/manage/__init__.py index 1067cfb8..683b3a71 100644 --- a/zhenxun/plugins/web_ui/api/tabs/manage/__init__.py +++ b/zhenxun/plugins/web_ui/api/tabs/manage/__init__.py @@ -27,6 +27,7 @@ from ....config import AVA_URL, GROUP_AVA_URL from ....utils import authentication from ...logs.log_manager import LOG_STORAGE from .model import ( + ClearRequest, DeleteFriend, Friend, FriendRequestResult, @@ -143,8 +144,12 @@ async def _(bot_id: str) -> Result: "/get_request_count", dependencies=[authentication()], description="获取请求数量" ) async def _() -> Result: - f_count = await FgRequest.filter(request_type=RequestType.FRIEND).count() - g_count = await FgRequest.filter(request_type=RequestType.GROUP).count() + f_count = await FgRequest.filter( + request_type=RequestType.FRIEND, handle_type__isnull=True + ).count() + g_count = await FgRequest.filter( + request_type=RequestType.GROUP, handle_type__isnull=True + ).count() data = { "friend_count": f_count, "group_count": g_count, @@ -196,13 +201,13 @@ async def _() -> Result: return Result.ok(req_result, f"{NICKNAME}带来了最新的数据!") -@router.delete( +@router.post( "/clear_request", dependencies=[authentication()], description="清空请求列表" ) -async def _(request_type: Literal["private", "group"]) -> Result: - await FgRequest.filter(handle_type__not_isnull=True).update( - handle_type=RequestHandleType.IGNORE - ) +async def _(cr: ClearRequest) -> Result: + await FgRequest.filter( + handle_type__isnull=True, request_type=cr.request_type + ).update(handle_type=RequestHandleType.IGNORE) return Result.ok(info="成功清除了数据!") diff --git a/zhenxun/plugins/web_ui/api/tabs/manage/model.py b/zhenxun/plugins/web_ui/api/tabs/manage/model.py index 8df5a6cd..64b64e8e 100644 --- a/zhenxun/plugins/web_ui/api/tabs/manage/model.py +++ b/zhenxun/plugins/web_ui/api/tabs/manage/model.py @@ -2,6 +2,8 @@ from typing import Literal from pydantic import BaseModel +from zhenxun.utils.enum import RequestType + class Group(BaseModel): """ @@ -123,6 +125,14 @@ class GroupRequestResult(FriendRequestResult): """群聊名称""" +class ClearRequest(BaseModel): + """ + 清空请求 + """ + + request_type: RequestType + + class HandleRequest(BaseModel): """ 操作请求接收数据 diff --git a/zhenxun/plugins/web_ui/api/tabs/system/__init__.py b/zhenxun/plugins/web_ui/api/tabs/system/__init__.py index 61430144..55c56764 100644 --- a/zhenxun/plugins/web_ui/api/tabs/system/__init__.py +++ b/zhenxun/plugins/web_ui/api/tabs/system/__init__.py @@ -5,117 +5,158 @@ from typing import List, Optional from fastapi import APIRouter +from zhenxun.utils._build_image import BuildImage + from ....base_model import Result from ....utils import authentication, get_system_disk from .model import AddFile, DeleteFile, DirFile, RenameFile, SaveFile router = APIRouter(prefix="/system") +IMAGE_TYPE = ["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"] -@router.get("/get_dir_list", dependencies=[authentication()], description="获取文件列表") +@router.get( + "/get_dir_list", dependencies=[authentication()], description="获取文件列表" +) async def _(path: Optional[str] = None) -> Result: - base_path = Path(path) if path else Path() - data_list = [] - for file in os.listdir(base_path): - data_list.append(DirFile(is_file=not (base_path / file).is_dir(), name=file, parent=path)) - return Result.ok(data_list) + base_path = Path(path) if path else Path() + data_list = [] + for file in os.listdir(base_path): + file_path = base_path / file + is_image = False + for t in IMAGE_TYPE: + if file.endswith(f".{t}"): + is_image = True + break + data_list.append( + DirFile( + is_file=not file_path.is_dir(), + is_image=is_image, + name=file, + parent=path, + ) + ) + return Result.ok(data_list) -@router.get("/get_resources_size", dependencies=[authentication()], description="获取文件列表") +@router.get( + "/get_resources_size", dependencies=[authentication()], description="获取文件列表" +) async def _(full_path: Optional[str] = None) -> Result: - return Result.ok(await get_system_disk(full_path)) + return Result.ok(await get_system_disk(full_path)) @router.post("/delete_file", dependencies=[authentication()], description="删除文件") async def _(param: DeleteFile) -> Result: - path = Path(param.full_path) - if not path or not path.exists(): - return Result.warning_("文件不存在...") - try: - path.unlink() - return Result.ok('删除成功!') - except Exception as e: - return Result.warning_('删除失败: ' + str(e)) - -@router.post("/delete_folder", dependencies=[authentication()], description="删除文件夹") + path = Path(param.full_path) + if not path or not path.exists(): + return Result.warning_("文件不存在...") + try: + path.unlink() + return Result.ok("删除成功!") + except Exception as e: + return Result.warning_("删除失败: " + str(e)) + + +@router.post( + "/delete_folder", dependencies=[authentication()], description="删除文件夹" +) async def _(param: DeleteFile) -> Result: - path = Path(param.full_path) - if not path or not path.exists() or path.is_file(): - return Result.warning_("文件夹不存在...") - try: - shutil.rmtree(path.absolute()) - return Result.ok('删除成功!') - except Exception as e: - return Result.warning_('删除失败: ' + str(e)) - + path = Path(param.full_path) + if not path or not path.exists() or path.is_file(): + return Result.warning_("文件夹不存在...") + try: + shutil.rmtree(path.absolute()) + return Result.ok("删除成功!") + except Exception as e: + return Result.warning_("删除失败: " + str(e)) + @router.post("/rename_file", dependencies=[authentication()], description="重命名文件") async def _(param: RenameFile) -> Result: - path = (Path(param.parent) / param.old_name) if param.parent else Path(param.old_name) - if not path or not path.exists(): - return Result.warning_("文件不存在...") - try: - path.rename(path.parent / param.name) - return Result.ok('重命名成功!') - except Exception as e: - return Result.warning_('重命名失败: ' + str(e)) - + path = ( + (Path(param.parent) / param.old_name) if param.parent else Path(param.old_name) + ) + if not path or not path.exists(): + return Result.warning_("文件不存在...") + try: + path.rename(path.parent / param.name) + return Result.ok("重命名成功!") + except Exception as e: + return Result.warning_("重命名失败: " + str(e)) -@router.post("/rename_folder", dependencies=[authentication()], description="重命名文件夹") + +@router.post( + "/rename_folder", dependencies=[authentication()], description="重命名文件夹" +) async def _(param: RenameFile) -> Result: - path = (Path(param.parent) / param.old_name) if param.parent else Path(param.old_name) - if not path or not path.exists() or path.is_file(): - return Result.warning_("文件夹不存在...") - try: - new_path = path.parent / param.name - shutil.move(path.absolute(), new_path.absolute()) - return Result.ok('重命名成功!') - except Exception as e: - return Result.warning_('重命名失败: ' + str(e)) - + path = ( + (Path(param.parent) / param.old_name) if param.parent else Path(param.old_name) + ) + if not path or not path.exists() or path.is_file(): + return Result.warning_("文件夹不存在...") + try: + new_path = path.parent / param.name + shutil.move(path.absolute(), new_path.absolute()) + return Result.ok("重命名成功!") + except Exception as e: + return Result.warning_("重命名失败: " + str(e)) + @router.post("/add_file", dependencies=[authentication()], description="新建文件") async def _(param: AddFile) -> Result: - path = (Path(param.parent) / param.name) if param.parent else Path(param.name) - if path.exists(): - return Result.warning_("文件已存在...") - try: - path.open('w') - return Result.ok('新建文件成功!') - except Exception as e: - return Result.warning_('新建文件失败: ' + str(e)) - + path = (Path(param.parent) / param.name) if param.parent else Path(param.name) + if path.exists(): + return Result.warning_("文件已存在...") + try: + path.open("w") + return Result.ok("新建文件成功!") + except Exception as e: + return Result.warning_("新建文件失败: " + str(e)) + @router.post("/add_folder", dependencies=[authentication()], description="新建文件夹") async def _(param: AddFile) -> Result: - path = (Path(param.parent) / param.name) if param.parent else Path(param.name) - if path.exists(): - return Result.warning_("文件夹已存在...") - try: - path.mkdir() - return Result.ok('新建文件夹成功!') - except Exception as e: - return Result.warning_('新建文件夹失败: ' + str(e)) + path = (Path(param.parent) / param.name) if param.parent else Path(param.name) + if path.exists(): + return Result.warning_("文件夹已存在...") + try: + path.mkdir() + return Result.ok("新建文件夹成功!") + except Exception as e: + return Result.warning_("新建文件夹失败: " + str(e)) @router.get("/read_file", dependencies=[authentication()], description="读取文件") async def _(full_path: str) -> Result: - path = Path(full_path) - if not path.exists(): - return Result.warning_("文件不存在...") - try: - text = path.read_text(encoding='utf-8') - return Result.ok(text) - except Exception as e: - return Result.warning_('新建文件夹失败: ' + str(e)) - + path = Path(full_path) + if not path.exists(): + return Result.warning_("文件不存在...") + try: + text = path.read_text(encoding="utf-8") + return Result.ok(text) + except Exception as e: + return Result.warning_("读取文件失败: " + str(e)) + + @router.post("/save_file", dependencies=[authentication()], description="读取文件") async def _(param: SaveFile) -> Result: - path = Path(param.full_path) - try: - with path.open('w') as f: - f.write(param.content) - return Result.ok("更新成功!") - except Exception as e: - return Result.warning_('新建文件夹失败: ' + str(e)) \ No newline at end of file + path = Path(param.full_path) + try: + with path.open("w") as f: + f.write(param.content) + return Result.ok("更新成功!") + except Exception as e: + return Result.warning_("保存文件失败: " + str(e)) + + +@router.get("/get_image", dependencies=[authentication()], description="读取图片base64") +async def _(full_path: str) -> Result: + path = Path(full_path) + if not path.exists(): + return Result.warning_("文件不存在...") + try: + return Result.ok(BuildImage.open(path).pic2bs4()) + except Exception as e: + return Result.warning_("获取图片失败: " + str(e)) diff --git a/zhenxun/plugins/web_ui/api/tabs/system/model.py b/zhenxun/plugins/web_ui/api/tabs/system/model.py index b3b5a45f..7cabc86a 100644 --- a/zhenxun/plugins/web_ui/api/tabs/system/model.py +++ b/zhenxun/plugins/web_ui/api/tabs/system/model.py @@ -1,6 +1,3 @@ - - - from datetime import datetime from typing import Literal, Optional @@ -8,57 +5,59 @@ from pydantic import BaseModel class DirFile(BaseModel): + """ + 文件或文件夹 + """ - """ - 文件或文件夹 - """ + is_file: bool + """是否为文件""" + is_image: bool + """是否为图片""" + name: str + """文件夹或文件名称""" + parent: Optional[str] = None + """父级""" - is_file: bool - """是否为文件""" - name: str - """文件夹或文件名称""" - parent: Optional[str] = None - """父级""" class DeleteFile(BaseModel): + """ + 删除文件 + """ - """ - 删除文件 - """ + full_path: str + """文件全路径""" - full_path: str - """文件全路径""" class RenameFile(BaseModel): + """ + 删除文件 + """ - """ - 删除文件 - """ - parent: Optional[str] - """父路径""" - old_name: str - """旧名称""" - name: str - """新名称""" + parent: Optional[str] + """父路径""" + old_name: str + """旧名称""" + name: str + """新名称""" class AddFile(BaseModel): + """ + 新建文件 + """ - """ - 新建文件 - """ - parent: Optional[str] - """父路径""" - name: str - """新名称""" + parent: Optional[str] + """父路径""" + name: str + """新名称""" class SaveFile(BaseModel): - - """ - 保存文件 - """ - full_path: str - """全路径""" - content: str - """内容""" + """ + 保存文件 + """ + + full_path: str + """全路径""" + content: str + """内容""" diff --git a/zhenxun/plugins/word_bank/_model.py b/zhenxun/plugins/word_bank/_model.py index abf03779..f31620a2 100644 --- a/zhenxun/plugins/word_bank/_model.py +++ b/zhenxun/plugins/word_bank/_model.py @@ -123,13 +123,12 @@ class WordBank(Model): # 对图片做额外处理 image_path = None if word_type == 3: - _file = ( - path / "problem" / f"{group_id}" / f"{user_id}_{int(time.time())}.jpg" - ) + _uuid = uuid.uuid1() + _file = path / "problem" / f"{group_id}" / f"{user_id}_{_uuid}.jpg" _file.parent.mkdir(exist_ok=True, parents=True) await AsyncHttpx.download_file(problem, _file) problem = get_img_hash(_file) - image_path = f"problem/{group_id}/{user_id}_{int(time.time())}.jpg" + image_path = f"problem/{group_id}/{user_id}_{_uuid}.jpg" new_answer, placeholder_list = await cls._answer2format( answer, user_id, group_id )