From f4ce789ffc89f9007235f9f7f6853b22d748ba8e Mon Sep 17 00:00:00 2001 From: HibiKier <775757368@qq.com> Date: Sun, 11 Aug 2024 20:52:06 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=8E=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zhenxun/builtin_plugins/platform/qq/group_handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zhenxun/builtin_plugins/platform/qq/group_handle.py b/zhenxun/builtin_plugins/platform/qq/group_handle.py index e04f8e5a..262a67f1 100644 --- a/zhenxun/builtin_plugins/platform/qq/group_handle.py +++ b/zhenxun/builtin_plugins/platform/qq/group_handle.py @@ -231,7 +231,7 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent | GroupMemberIncreaseEvent """群欢迎消息""" _flmt.start_cd(group_id) path = DATA_PATH / "welcome_message" / "qq" / f"{group_id}" - data = json.load((path / "text.json").open()) + data = json.load((path / "text.json").open(encoding="utf-8")) message = data["message"] msg_split = re.split(r"\[image:\d+\]", message) msg_list = [] From 5f0797e3ed6acde45310d6894fc7aa1591f8e6cb Mon Sep 17 00:00:00 2001 From: HibiKier <775757368@qq.com> Date: Sun, 11 Aug 2024 21:04:08 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=8E=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 246 +++++++++++++++++++++++++----------------------------- 1 file changed, 116 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index 36399c2e..abd2f589 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,76 @@ -
-
-![maven](https://img.shields.io/badge/python-3.9%2B-blue) -![maven](https://img.shields.io/badge/nonebot-2.1.3-yellow) +
-# 绪山真寻Bot +![python](https://img.shields.io/badge/python-v3.9%2B-blue) +![nonebot](https://img.shields.io/badge/nonebot-v2.1.3-yellow) +![onebot](https://img.shields.io/badge/onebot-v11-black) + +
+ +
+ + +[![license](https://img.shields.io/badge/license-AGPL3.0-FE7D37)](https://github.com/HibiKier/zhenxun_bot/blob/main/LICENSE) +[![tencent-qq](https://img.shields.io/badge/%E7%BE%A4-是真寻酱哒-red?style=flat-square&logo=tencent-qq)](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) +[![tencent-qq](https://img.shields.io/badge/%E7%BE%A4-真寻的技术群-c73e7e?style=flat-square&logo=tencent-qq)](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) + + +
+ +
+ +[文档](https://hibikier.github.io/zhenxun_bot/) + +
+ +
+ +## 绪山真寻Bot
-****
“真寻是[椛椛](https://github.com/FloatTech/ZeroBot-Plugin)的好朋友!” +:tada:喜欢真寻,于是真寻就来了!:tada: + +本项目符合 [OneBot](https://github.com/howmanybots/onebot) 标准,可基于以下项目与机器人框架/平台进行交互 +| 项目地址 | 平台 | 核心作者 | 备注 | +| :---: | :---: | :---: | :---: | +| [LLOneBot](https://github.com/LLOneBot/LLOneBot) | NTQQ | linyuchen | 可用 | +| [Napcat](https://github.com/NapNeko/NapCatQQ) | NTQQ | NapNeko | 可用 | +| [Lagrange.Core](https://github.com/LagrangeDev/Lagrange.Core) | ? | LagrangeDev/Linwenxuan04 | 可用 +
-**** - -## 关于 - -用爱发电,某些功能学习借鉴了大佬们的代码,因为绪山真寻实在太可爱了因此开发了 -绪山真寻bot,实现了一些对群友的娱乐功能和实用功能(大概)。 - -如果该项目的图片等等侵犯猫豆腐老师权益请联系我删除! - -讨论插件开发,nonebot2开发,或者有 安装使用问题开发建议,可以发送issues或加入[ [真寻酱的技术群](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) ] (在这里请不要吹水!) - -希望有个地方讨论绪山真寻Bot,渴望吹水聊天,可以加入[ [是真寻酱哒(萌新版)](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) ] - - -## 声明 - -此项目仅用于学习交流,请勿用于非法用途 - -
-# Nonebot2 - - - -非常 [ **[NICE](https://github.com/nonebot/nonebot2)** ] 的OneBot框架 +![Star Trend](https://api.star-history.com/svg?repos=HibiKier/zhenxun_bot&type=Timeline)
-## 未完成的文档 +## 真寻觉得你需要帮助 -# [传送门](https://hibikier.github.io/zhenxun_bot/) +
-## 真寻的帮助 - -请对真寻说: '真寻帮助' or '管理员帮助' or '超级用户帮助' or '真寻帮助 指令' - -## 普通帮助图片 - -![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/help.png) - -## HTML版帮助图片 - -![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/html_help.png) + + +
## 这是一份扩展 -### 0. 体验一下? +### 1. 体验一下? -提供dev版本的zhenxun +这是一个免费的,版本为dev的zhenxun,你可以通过napcat或拉格朗日等直接连接用于体验与测试 +(球球了测试君!) ``` Url: 43.143.112.57:11451/onebot/v11/ws AccessToken: PUBLIC_ZHENXUN_TEST @@ -79,12 +78,23 @@ AccessToken: PUBLIC_ZHENXUN_TEST 注:你无法获得超级用户权限 ``` -### 1. Web UI +### 2. 额外扩展 -项目地址: [Web UI](https://github.com/HibiKier/zhenxun_bot_webui) +
+ +“不要害怕,你的背后还有千千万万的 伙伴 啊!” + +| 项目名称 | 主要用途 | 仓库作者 | 备注 | +| :---: | :---: | :---: | :---: | +| [WebUi](https://github.com/HibiKier/zhenxun_bot_webui) | 管理 | [hibikier](https://github.com/HibiKier) | 基于真寻WebApi的webui实现 +| [一键安装](https://github.com/zhenxun-org/zhenxun_bot-deploy) | 安装 | [AkashiCoin](https://github.com/AkashiCoin) | 新版本未测试 +| [Docker单机版](https://github.com/Sakuracio/zhenxun_bot_docker) | 安装 | [zhenxun-org](https://github.com/zhenxun-org) | 新版本未测试 +| [Docker全量版](https://shields.io/badge/GITHUB-SinKy--Yan-4476AF?logo=github&style=for-the-badge) | 安装 | [zhenxun-org](https://github.com/zhenxun-org) | 包含 真寻Bot PostgreSQL数据库 go-cqhttp webui等(新版本未测试) + +PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能无法正常启动全量版容器**
-后台示例图 + WebUI 后台示例图 ![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui1.png) ![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui2.png) @@ -96,16 +106,11 @@ AccessToken: PUBLIC_ZHENXUN_TEST
+
-### 一键安装脚本(新版未测试) +
-[zhenxun_bot-deploy](https://github.com/AkashiCoin/zhenxun_bot-deploy) - -### 提供符合真寻标准的插件仓库(旧版) - -[AkashiCoin/nonebot_plugins_zhenxun_bot](https://github.com/AkashiCoin/nonebot_plugins_zhenxun_bot) - -## 来点优点? +## ~~来点优点?~~ 可爱难道还不够吗 * 实现了许多功能,且提供了大量功能管理命令 * 通过Config配置项将所有插件配置统计保存至config.yaml,利于统一用户修改 @@ -113,7 +118,61 @@ AccessToken: PUBLIC_ZHENXUN_TEST * 提供了cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`PluginCdBlock` 等 * **..... 更多详细请通过`传送门`查看文档!** -## 功能列表 +## 简单部署 + +``` +# 获取代码 +git clone https://github.com/HibiKier/zhenxun.git + +# 进入目录 +cd zhenxun_bot + +# 安装依赖 +pip install poetry # 安装 poetry +poetry install # 安装依赖 + +# 开始运行 +poetry shell # 进入虚拟环境 +python bot.py + +# 首次后会在data目录下生成database.json和config.yaml文件 +# database.json用户配置数据库信息 +# config.yaml用户配置插件 +``` + +## 简单配置 + +``` +1.在.env.dev文件中 + + SUPERUSERS = [""] # 填写你的QQ + + PLATFORM_SUPERUSERS = ' + { + "qq": [""], # 在此处填写你的qq + "dodo": [], + "kaiheila": [], + "discord": [] + } +' + +2.在data/database.json文件中修改数据库配置 +{ + "bind": "", + "sql_name": "postgres", + "user": "", # 用户们 + "password": "", # 密码 + "address": "", # 数据库地址ip + "port": "", # 数据库端口 + "database": "" # 数据库名称 +} + +3.在configs/config.yaml文件中 # 该文件需要启动一次后生成 + * 修改插件配置项 + +``` + +## 功能列表 (旧版列表)
已实现的功能 @@ -263,79 +322,6 @@ AccessToken: PUBLIC_ZHENXUN_TEST
-## 详细配置请前往文档,以下为最简部署和配置,如果你有基础并学习过nonebot2的话 - -## 简单部署 - -``` - -# 使用napcat或拉格朗日 - -# 获取代码 -git clone https://github.com/HibiKier/zhenxun.git - -# 进入目录 -cd zhenxun_bot - -# 安装依赖 -pip install poetry # 安装 poetry -poetry install # 安装依赖 - -# 进行基础配置 -####请查看 配置 部分#### - -# 开始运行 -poetry shell # 进入虚拟环境 -python bot.py - -# 运行后会在data目录下生成database.json文件,请根据自身数据库配置修改 -# 其他插件配置在data/config.yaml文件中(需要运行一次) -``` - -## 简单配置 - -``` -1.在.env.dev文件中 - - SUPERUSERS = [""] # 填写你的QQ - - PLATFORM_SUPERUSERS = ' - { - "qq": [""], # 在此处填写你的qq - "dodo": [], - "kaiheila": [], - "discord": [] - } -' - -2.在data/database.json文件中修改数据库配置 -{ - "bind": "", - "sql_name": "postgres", - "user": "", # 用户们 - "password": "", # 密码 - "address": "", # 数据库地址ip - "port": "", # 数据库端口 - "database": "" # 数据库名称 -} - -3.在configs/config.yaml文件中 # 该文件需要启动一次后生成 - * 修改插件配置项 - -``` - -## 使用Docker (新版未测试过) - -**Docker 单机版(仅真寻Bot)** -**点击下方的 GitHub 徽标查看教程** -[![Github](https://shields.io/badge/GITHUB-Sakuracio-4476AF?logo=github&style=for-the-badge)](https://github.com/Sakuracio/zhenxun_bot_docker) -[![DOCKER](https://shields.io/badge/docker-hibikier/zhenxun_bot-4476AF?logo=docker&style=for-the-badge)](https://hub.docker.com/r/hibikier/zhenxun_bot) -**Docker 全量版(包含 真寻Bot PostgreSQL数据库 go-cqhttp webui等)** -[![Github](https://shields.io/badge/GITHUB-SinKy--Yan-4476AF?logo=github&style=for-the-badge)](https://github.com/SinKy-Yan/zhenxunbot-docker) -[![DOCKER](https://shields.io/badge/docker-jyishit/zhenxun_bot-4476AF?logo=docker&style=for-the-badge)](https://hub.docker.com/r/jyishit/zhenxun_bot) -**点击上方的 GitHub 徽标查看教程** -PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能无法正常启动全量版容器** - ## [爱发电](https://afdian.net/@HibiKier)
From da595b8c8eaf55e70dd2d527582a27b6707fc049 Mon Sep 17 00:00:00 2001 From: HibiKier <775757368@qq.com> Date: Mon, 12 Aug 2024 00:09:38 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=F0=9F=8E=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zhenxun/builtin_plugins/shop/_data_source.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zhenxun/builtin_plugins/shop/_data_source.py b/zhenxun/builtin_plugins/shop/_data_source.py index c7b5c68b..38ee3ddb 100644 --- a/zhenxun/builtin_plugins/shop/_data_source.py +++ b/zhenxun/builtin_plugins/shop/_data_source.py @@ -297,9 +297,8 @@ class ShopManage: raise ValueError("该商品使用函数已被注册!") kwargs["send_success_msg"] = send_success_msg kwargs["max_num_limit"] = max_num_limit - # TODO: create_model(f"{uuid}_model", __base__=ShopParam, **kwargs) cls.uuid2goods[uuid] = Goods( - model=None,# create_model(f"{uuid}_model", __base__=ShopParam, **kwargs), + model=create_model(f"{uuid}_model", __base__=ShopParam, **kwargs), params=kwargs, before_handle=before_handle, after_handle=after_handle, From b6fc114a14b1856c1f6b014debf9090c01d83cc3 Mon Sep 17 00:00:00 2001 From: BalconyJH Date: Mon, 12 Aug 2024 15:38:37 +0800 Subject: [PATCH 4/5] =?UTF-8?q?:children=5Fcrossing:=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=20Chromium=20=E5=AE=89=E8=A3=85=E9=80=BB=E8=BE=91,=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20Playwright=20=E4=BE=9D=E8=B5=96=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zhenxun/utils/browser.py | 59 +++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/zhenxun/utils/browser.py b/zhenxun/utils/browser.py index c7d89727..432c28ed 100644 --- a/zhenxun/utils/browser.py +++ b/zhenxun/utils/browser.py @@ -1,5 +1,10 @@ +import os +import sys + from nonebot import get_driver +from playwright.__main__ import main from playwright.async_api import Browser, Playwright, async_playwright +from zhenxun.configs.config import SYSTEM_PROXY from zhenxun.services.log import logger @@ -13,6 +18,8 @@ _browser: Browser | None = None async def start_browser(): global _playwright global _browser + install() + await check_playwright_env() _playwright = await async_playwright().start() _browser = await _playwright.chromium.launch() @@ -33,13 +40,51 @@ def get_browser() -> Browser: def install(): """自动安装、更新 Chromium""" - logger.info("正在检查 Chromium 更新") - import sys - from playwright.__main__ import main + def set_env_variables(): + os.environ[ + "PLAYWRIGHT_DOWNLOAD_HOST"] = "https://npmmirror.com/mirrors/playwright/" + if SYSTEM_PROXY: + os.environ["HTTPS_PROXY"] = SYSTEM_PROXY - sys.argv = ["", "install", "chromium"] + def restore_env_variables(): + os.environ.pop("PLAYWRIGHT_DOWNLOAD_HOST", None) + if SYSTEM_PROXY: + os.environ.pop("HTTPS_PROXY", None) + if original_proxy is not None: + os.environ["HTTPS_PROXY"] = original_proxy + + def try_install_chromium(): + try: + sys.argv = ["", "install", "chromium"] + main() + except SystemExit as e: + return e.code == 0 + return False + + logger.info("检查 Chromium 更新") + + original_proxy = os.environ.get("HTTPS_PROXY") + set_env_variables() + + success = try_install_chromium() + + if not success: + logger.info("Chromium 更新失败,尝试从原始仓库下载,速度较慢") + os.environ["PLAYWRIGHT_DOWNLOAD_HOST"] = "" + success = try_install_chromium() + + restore_env_variables() + + if not success: + raise RuntimeError("未知错误,Chromium 下载失败") + + +async def check_playwright_env(): + """检查 Playwright 依赖""" + logger.info("检查 Playwright 依赖") try: - main() - except SystemExit: - pass + async with async_playwright() as p: + await p.chromium.launch() + except Exception as e: + raise ImportError("加载失败,Playwright 依赖不全,") from e 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 5/5] =?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 )