mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-14 21:52:56 +08:00
Some checks failed
检查bot是否运行正常 / bot check (push) Has been cancelled
CodeQL Code Security Analysis / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been cancelled
CodeQL Code Security Analysis / Analyze (${{ matrix.language }}) (none, python) (push) Has been cancelled
Sequential Lint and Type Check / ruff-call (push) Has been cancelled
Release Drafter / Update Release Draft (push) Has been cancelled
Force Sync to Aliyun / sync (push) Has been cancelled
Update Version / update-version (push) Has been cancelled
Sequential Lint and Type Check / pyright-call (push) Has been cancelled
- 优化 `检查更新` 默认行为,未指定类型时直接显示版本信息 - 扩展版本详情显示:当前版本、最新开发版/正式版(含日期)、资源版本及更新提示 - 新增更新后资源兼容性检查,自动读取 `resources.spec` 并提示更新 - 使用 `asyncio.gather` 并发获取版本信息,引入 `packaging` 库提高比较准确性 - 优化错误处理与日志记录 Co-authored-by: webjoin111 <455457521@qq.com>
320 lines
12 KiB
Python
320 lines
12 KiB
Python
import asyncio
|
|
from typing import Literal
|
|
|
|
from nonebot.adapters import Bot
|
|
from packaging.specifiers import SpecifierSet
|
|
from packaging.version import InvalidVersion, Version
|
|
|
|
from zhenxun.services.log import logger
|
|
from zhenxun.utils.http_utils import AsyncHttpx
|
|
from zhenxun.utils.manager.virtual_env_package_manager import VirtualEnvPackageManager
|
|
from zhenxun.utils.manager.zhenxun_repo_manager import (
|
|
ZhenxunRepoConfig,
|
|
ZhenxunRepoManager,
|
|
)
|
|
from zhenxun.utils.platform import PlatformUtils
|
|
from zhenxun.utils.repo_utils import RepoFileManager
|
|
|
|
LOG_COMMAND = "AutoUpdate"
|
|
|
|
|
|
class UpdateManager:
|
|
@staticmethod
|
|
async def _get_latest_commit_date(owner: str, repo: str, path: str) -> str:
|
|
"""获取文件最新 commit 日期"""
|
|
api_url = f"https://api.github.com/repos/{owner}/{repo}/commits"
|
|
params = {"path": path, "page": 1, "per_page": 1}
|
|
try:
|
|
data = await AsyncHttpx.get_json(api_url, params=params)
|
|
if data and isinstance(data, list) and data[0]:
|
|
date_str = data[0]["commit"]["committer"]["date"]
|
|
return date_str.split("T")[0]
|
|
except Exception as e:
|
|
logger.warning(f"获取 {owner}/{repo}/{path} 的 commit 日期失败", e=e)
|
|
return "获取失败"
|
|
|
|
@classmethod
|
|
async def check_version(cls) -> str:
|
|
"""检查真寻和资源的版本"""
|
|
bot_cur_version = cls.__get_version()
|
|
|
|
release_task = ZhenxunRepoManager.zhenxun_get_latest_releases_data()
|
|
dev_version_task = RepoFileManager.get_file_content(
|
|
ZhenxunRepoConfig.ZHENXUN_BOT_GITHUB_URL, "__version__"
|
|
)
|
|
bot_commit_date_task = cls._get_latest_commit_date(
|
|
"HibiKier", "zhenxun_bot", "__version__"
|
|
)
|
|
res_commit_date_task = cls._get_latest_commit_date(
|
|
"zhenxun-org", "zhenxun-bot-resources", "__version__"
|
|
)
|
|
|
|
(
|
|
release_data,
|
|
dev_version_text,
|
|
bot_commit_date,
|
|
res_commit_date,
|
|
) = await asyncio.gather(
|
|
release_task,
|
|
dev_version_task,
|
|
bot_commit_date_task,
|
|
res_commit_date_task,
|
|
return_exceptions=True,
|
|
)
|
|
|
|
if isinstance(release_data, dict):
|
|
bot_release_version = release_data.get("name", "获取失败")
|
|
bot_release_date = release_data.get("created_at", "").split("T")[0]
|
|
else:
|
|
bot_release_version = "获取失败"
|
|
bot_release_date = "获取失败"
|
|
logger.warning(f"获取 Bot release 信息失败: {release_data}")
|
|
|
|
if isinstance(dev_version_text, str):
|
|
bot_dev_version = dev_version_text.split(":")[-1].strip()
|
|
else:
|
|
bot_dev_version = "获取失败"
|
|
bot_commit_date = "获取失败"
|
|
logger.warning(f"获取 Bot dev 版本信息失败: {dev_version_text}")
|
|
|
|
bot_update_hint = ""
|
|
try:
|
|
cur_base_v = bot_cur_version.split("-")[0].lstrip("v")
|
|
dev_base_v = bot_dev_version.split("-")[0].lstrip("v")
|
|
|
|
if Version(cur_base_v) < Version(dev_base_v):
|
|
bot_update_hint = "\n-> 发现新开发版本, 可用 `检查更新 main` 更新"
|
|
elif (
|
|
Version(cur_base_v) == Version(dev_base_v)
|
|
and bot_cur_version != bot_dev_version
|
|
):
|
|
bot_update_hint = "\n-> 发现新开发版本, 可用 `检查更新 main` 更新"
|
|
except (InvalidVersion, TypeError, IndexError):
|
|
if bot_cur_version != bot_dev_version and bot_dev_version != "获取失败":
|
|
bot_update_hint = "\n-> 发现新开发版本, 可用 `检查更新 main` 更新"
|
|
|
|
bot_update_info = (
|
|
f"当前版本: {bot_cur_version}\n"
|
|
f"最新开发版: {bot_dev_version} (更新于: {bot_commit_date})\n"
|
|
f"最新正式版: {bot_release_version} (发布于: {bot_release_date})"
|
|
f"{bot_update_hint}"
|
|
)
|
|
|
|
res_version_file = ZhenxunRepoConfig.RESOURCE_PATH / "__version__"
|
|
res_cur_version = "未找到"
|
|
if res_version_file.exists():
|
|
if text := res_version_file.open(encoding="utf8").readline():
|
|
res_cur_version = text.split(":")[-1].strip()
|
|
|
|
res_latest_version = "获取失败"
|
|
try:
|
|
res_latest_version_text = await RepoFileManager.get_file_content(
|
|
ZhenxunRepoConfig.RESOURCE_GITHUB_URL, "__version__"
|
|
)
|
|
res_latest_version = res_latest_version_text.split(":")[-1].strip()
|
|
except Exception as e:
|
|
res_commit_date = "获取失败"
|
|
logger.warning(f"获取资源版本信息失败: {e}")
|
|
|
|
res_update_hint = ""
|
|
try:
|
|
if Version(res_cur_version) < Version(res_latest_version):
|
|
res_update_hint = "\n-> 发现新资源版本, 可用 `检查更新 resource` 更新"
|
|
except (InvalidVersion, TypeError):
|
|
pass
|
|
|
|
res_update_info = (
|
|
f"当前版本: {res_cur_version}\n"
|
|
f"最新版本: {res_latest_version} (更新于: {res_commit_date})"
|
|
f"{res_update_hint}"
|
|
)
|
|
|
|
return f"『绪山真寻 Bot』\n{bot_update_info}\n\n『真寻资源』\n{res_update_info}"
|
|
|
|
@classmethod
|
|
async def update_webui(
|
|
cls,
|
|
source: Literal["git", "ali"] | None,
|
|
branch: str = "dist",
|
|
force: bool = False,
|
|
):
|
|
"""更新WebUI
|
|
|
|
参数:
|
|
source: 更新源
|
|
branch: 分支
|
|
force: 是否强制更新
|
|
|
|
返回:
|
|
str: 返回消息
|
|
"""
|
|
if not source:
|
|
await ZhenxunRepoManager.webui_zip_update()
|
|
return "WebUI更新完成!"
|
|
result = await ZhenxunRepoManager.webui_git_update(
|
|
source,
|
|
branch=branch,
|
|
force=force,
|
|
)
|
|
if not result.success:
|
|
logger.error(f"WebUI更新失败...错误: {result.error_message}", LOG_COMMAND)
|
|
return f"WebUI更新失败...错误: {result.error_message}"
|
|
return "WebUI更新完成!"
|
|
|
|
@classmethod
|
|
async def update_resources(
|
|
cls,
|
|
source: Literal["git", "ali"] | None,
|
|
branch: str = "main",
|
|
force: bool = False,
|
|
) -> str:
|
|
"""更新资源
|
|
|
|
参数:
|
|
source: 更新源
|
|
branch: 分支
|
|
force: 是否强制更新
|
|
|
|
返回:
|
|
str: 返回消息
|
|
"""
|
|
if not source:
|
|
await ZhenxunRepoManager.resources_zip_update()
|
|
return "真寻资源更新完成!"
|
|
result = await ZhenxunRepoManager.resources_git_update(
|
|
source,
|
|
branch=branch,
|
|
force=force,
|
|
)
|
|
if not result.success:
|
|
logger.error(
|
|
f"真寻资源更新失败...错误: {result.error_message}", LOG_COMMAND
|
|
)
|
|
return f"真寻资源更新失败...错误: {result.error_message}"
|
|
return "真寻资源更新完成!"
|
|
|
|
@classmethod
|
|
async def update_zhenxun(
|
|
cls,
|
|
bot: Bot,
|
|
user_id: str,
|
|
version_type: Literal["main", "release"],
|
|
force: bool,
|
|
source: Literal["git", "ali"],
|
|
zip: bool,
|
|
) -> str:
|
|
"""更新操作
|
|
|
|
参数:
|
|
bot: Bot
|
|
user_id: 用户id
|
|
version_type: 更新版本类型
|
|
force: 是否强制更新
|
|
source: 更新源
|
|
zip: 是否下载zip文件
|
|
update_type: 更新方式
|
|
|
|
返回:
|
|
str | None: 返回消息
|
|
"""
|
|
cur_version = cls.__get_version()
|
|
await PlatformUtils.send_superuser(
|
|
bot,
|
|
f"检测真寻已更新,当前版本:{cur_version}\n开始更新...",
|
|
user_id,
|
|
)
|
|
result_message = ""
|
|
if zip:
|
|
new_version = await ZhenxunRepoManager.zhenxun_zip_update(version_type)
|
|
await PlatformUtils.send_superuser(
|
|
bot, "真寻更新完成,开始安装依赖...", user_id
|
|
)
|
|
await VirtualEnvPackageManager.install_requirement(
|
|
ZhenxunRepoConfig.REQUIREMENTS_FILE
|
|
)
|
|
result_message = (
|
|
f"版本更新完成!\n版本: {cur_version} -> {new_version}\n"
|
|
"请重新启动真寻以完成更新!"
|
|
)
|
|
else:
|
|
result = await ZhenxunRepoManager.zhenxun_git_update(
|
|
source,
|
|
branch=version_type,
|
|
force=force,
|
|
)
|
|
if not result.success:
|
|
logger.error(
|
|
f"真寻版本更新失败...错误: {result.error_message}",
|
|
LOG_COMMAND,
|
|
)
|
|
return f"版本更新失败...错误: {result.error_message}"
|
|
await PlatformUtils.send_superuser(
|
|
bot, "真寻更新完成,开始安装依赖...", user_id
|
|
)
|
|
await VirtualEnvPackageManager.install_requirement(
|
|
ZhenxunRepoConfig.REQUIREMENTS_FILE
|
|
)
|
|
result_message = (
|
|
f"版本更新完成!\n"
|
|
f"版本: {cur_version} -> {result.new_version}\n"
|
|
f"变更文件个数: {len(result.changed_files)}"
|
|
f"{'' if source == 'git' else '(阿里云更新不支持查看变更文件)'}\n"
|
|
"请重新启动真寻以完成更新!"
|
|
)
|
|
resource_warning = ""
|
|
if version_type == "main":
|
|
try:
|
|
spec_content = await RepoFileManager.get_file_content(
|
|
ZhenxunRepoConfig.ZHENXUN_BOT_GITHUB_URL, "resources.spec"
|
|
)
|
|
required_spec_str = None
|
|
for line in spec_content.splitlines():
|
|
if line.startswith("require_resources_version:"):
|
|
required_spec_str = line.split(":", 1)[1].strip().strip("\"'")
|
|
break
|
|
if required_spec_str:
|
|
res_version_file = ZhenxunRepoConfig.RESOURCE_PATH / "__version__"
|
|
local_res_version_str = "0.0.0"
|
|
if res_version_file.exists():
|
|
if text := res_version_file.open(encoding="utf8").readline():
|
|
local_res_version_str = text.split(":")[-1].strip()
|
|
|
|
spec = SpecifierSet(required_spec_str)
|
|
local_ver = Version(local_res_version_str)
|
|
if not spec.contains(local_ver):
|
|
warning_header = (
|
|
f"⚠️ **资源版本不兼容!**\n"
|
|
f"当前代码需要资源版本: `{required_spec_str}`\n"
|
|
f"您当前的资源版本是: `{local_res_version_str}`\n"
|
|
"**将自动为您更新资源文件...**"
|
|
)
|
|
await PlatformUtils.send_superuser(bot, warning_header, user_id)
|
|
resource_update_source = None if zip else source
|
|
resource_update_result = await cls.update_resources(
|
|
source=resource_update_source, force=force
|
|
)
|
|
resource_warning = (
|
|
f"\n\n{warning_header}\n{resource_update_result}"
|
|
)
|
|
except Exception as e:
|
|
logger.warning(f"检查资源版本兼容性时出错: {e}", LOG_COMMAND, e=e)
|
|
resource_warning = (
|
|
"\n\n⚠️ 检查资源版本兼容性时出错,建议手动运行 `检查更新 resource`"
|
|
)
|
|
return result_message + resource_warning
|
|
|
|
@classmethod
|
|
def __get_version(cls) -> str:
|
|
"""获取当前版本
|
|
|
|
返回:
|
|
str: 当前版本号
|
|
"""
|
|
_version = "v0.0.0"
|
|
if ZhenxunRepoConfig.ZHENXUN_BOT_VERSION_FILE.exists():
|
|
if text := ZhenxunRepoConfig.ZHENXUN_BOT_VERSION_FILE.open(
|
|
encoding="utf8"
|
|
).readline():
|
|
_version = text.split(":")[-1].strip()
|
|
return _version
|