Skip to content

Commit 7505a11

Browse files
committed
🐛 fix: 修复插件更新检查逻辑
1 parent f149a46 commit 7505a11

File tree

3 files changed

+958
-977
lines changed

3 files changed

+958
-977
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ dependencies = [
2727
"nonebot-adapter-onebot>=2.4.6",
2828
"nonebot-plugin-apscheduler>=0.5.0",
2929
"nonebot2>=2.4.2",
30+
"packaging>=25.0",
3031
"setuptools>=80.9.0",
3132
]
3233

Lines changed: 77 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import asyncio
2-
from pathlib import Path
32
import sys
4-
from typing import Union
3+
from pathlib import Path
54

65
import httpx
76
import logging
@@ -10,72 +9,62 @@
109
from nonebot import logger
1110
from nonebot.matcher import Matcher
1211

13-
logging.captureWarnings(True) # 去掉建议使用SSL验证的显示
12+
from packaging.version import parse as parse_version, Version
13+
14+
logging.captureWarnings(True)
1415

1516
try:
1617
__version__ = metadata.version("nonebot-plugin-sky")
1718
except metadata.PackageNotFoundError:
1819
__version__ = "0.0.0-dev"
1920

21+
CURRENT_PLUGIN_VERSION: Version = parse_version(__version__)
2022

21-
async def get_datapack_ver() -> str | None:
22-
"""
23-
检查本地数据包版本
24-
"""
2523

26-
ver = Path("SkyDataPack/version")
27-
if not ver.exists():
28-
return
29-
with open(ver, "r", encoding="utf-8") as f:
30-
return f.read()
24+
async def get_datapack_ver() -> str | None:
25+
"""检查本地数据包版本"""
26+
ver_path = Path("SkyDataPack/version")
27+
if not ver_path.exists():
28+
return None
29+
try:
30+
import aiofiles
3131

32+
async with aiofiles.open(ver_path, "r", encoding="utf-8") as f:
33+
return (await f.read()).strip()
34+
except (ImportError, IOError):
35+
with open(ver_path, "r", encoding="utf-8") as f:
36+
return f.read().strip()
3237

33-
async def check_plugin_latest() -> str:
34-
"""
35-
通过 PyPI API 检查插件的最新版本
36-
"""
3738

39+
async def check_plugin_latest() -> Version | None:
40+
"""通过 PyPI API 检查插件的最新版本,返回 Version 对象"""
3841
pypi_url = "https://pypi.org/pypi/nonebot-plugin-sky/json"
39-
42+
pypi_mirror_url = "https://pypi.tuna.tsinghua.edu.cn/pypi/nonebot-plugin-sky/json"
4043
headers = {"User-Agent": "nonebot_plugin_sky_update_checker/1.0"}
4144

42-
try:
43-
async with httpx.AsyncClient(timeout=10) as client:
44-
# 优先尝试官方源,如果失败则尝试镜像源
45+
async with httpx.AsyncClient(timeout=10) as client:
46+
for url in [pypi_url, pypi_mirror_url]:
4547
try:
46-
res = await client.get(url=pypi_url, headers=headers)
47-
res.raise_for_status()
48-
except httpx.RequestError:
49-
logger.warning("访问 PyPI 官方源失败,正在尝试镜像源...")
50-
pypi_mirror_url = (
51-
"https://pypi.tuna.tsinghua.edu.cn/pypi/nonebot-plugin-sky/json"
52-
)
53-
res = await client.get(url=pypi_mirror_url, headers=headers)
48+
res = await client.get(url=url, headers=headers)
5449
res.raise_for_status()
55-
return "网络错误"
56-
data = res.json()
57-
latest_version = data.get("info", {}).get("version")
58-
59-
if latest_version:
60-
return str(latest_version)
61-
else:
62-
return ""
63-
64-
except httpx.HTTPStatusError:
65-
return ""
66-
except Exception:
67-
return ""
68-
69-
70-
async def check_datapack_latest() -> str:
71-
"""
72-
通过 Gitee API 检查最新发布的数据包版本,稳定且快速。
73-
"""
74-
50+
data = res.json()
51+
latest_version_str = data.get("info", {}).get("version")
52+
if latest_version_str:
53+
return parse_version(latest_version_str)
54+
return None
55+
except httpx.RequestError as e:
56+
logger.warning(f"访问 PyPI 源 {url} 失败: {e}")
57+
except (KeyError, TypeError, Exception) as e:
58+
logger.error(f"解析 PyPI 响应失败: {e}")
59+
return None
60+
return None
61+
62+
63+
async def check_datapack_latest() -> Version | None:
64+
"""通过 Gitee API 检查最新数据包版本,返回 Version 对象"""
7565
gitee_api_url = (
7666
"https://gitee.com/api/v5/repos/Kaguyaaa/nonebot_plugin_sky/releases/latest"
7767
)
78-
7968
headers = {"User-Agent": "nonebot_plugin_sky_datapack_checker/1.0"}
8069

8170
try:
@@ -85,94 +74,79 @@ async def check_datapack_latest() -> str:
8574
data = res.json()
8675
tag_name = data.get("tag_name")
8776
if not tag_name:
88-
return ""
77+
return None
78+
8979
version_prefix = "SkyDataPack-v"
9080
if tag_name.startswith(version_prefix):
91-
version = tag_name.replace(version_prefix, "", 1)
92-
return version
93-
else:
94-
logger.warning(f"Gitee 最新 Release 的 tag 格式不符合预期: {tag_name}")
95-
return ""
81+
version_str = tag_name.replace(version_prefix, "", 1)
82+
return parse_version(version_str)
9683

97-
except httpx.HTTPStatusError as e:
98-
if e.response.status_code == 404:
99-
logger.info("Gitee 仓库中没有找到任何 Release。")
100-
else:
101-
logger.error(f"访问 Gitee API 时发生HTTP错误: {e}")
102-
return ""
84+
logger.warning(f"Gitee 最新 Release 的 tag 格式不符合预期: {tag_name}")
85+
return None
10386
except Exception as e:
10487
logger.error(f"获取数据包更新信息失败: {e}")
105-
return ""
88+
return None
10689

10790

108-
async def upgrade_plugin() -> Union[bool, None]:
109-
latest = await check_plugin_latest()
110-
if latest:
111-
if latest == __version__:
112-
logger.info("已是最新版本,无需更新")
113-
return False
114-
else:
115-
logger.info("发现新版本,开始下载更新")
91+
async def upgrade_plugin() -> bool:
92+
"""检查插件是否有新版本可供升级"""
93+
latest_version = await check_plugin_latest()
94+
if latest_version:
95+
if latest_version > CURRENT_PLUGIN_VERSION:
96+
logger.info(f"发现新版本: {CURRENT_PLUGIN_VERSION} -> {latest_version}")
11697
return True
117-
else:
118-
return None
98+
return False
11999

120100

121101
async def check_handle(matcher: Matcher):
122-
await matcher.send("正在检查更新,请稍候")
102+
await matcher.send("正在检查更新,请稍候...")
123103
results = ""
124-
plugin_latest = await check_plugin_latest()
125104

105+
# 检查插件更新
106+
plugin_latest = await check_plugin_latest()
126107
if plugin_latest:
127-
if plugin_latest == __version__:
128-
results += "-当前插件已是最新版本(v%s)\n" % __version__
129-
logger.info("当前插件已是最新版本(v%s)" % __version__)
108+
if plugin_latest > CURRENT_PLUGIN_VERSION:
109+
results += f"-[New] 发现新版本插件:v{CURRENT_PLUGIN_VERSION} -> v{plugin_latest}\n"
110+
elif plugin_latest < CURRENT_PLUGIN_VERSION:
111+
results += f"-[Info] 当前插件版本 v{CURRENT_PLUGIN_VERSION} 高于 PyPI 最新版 v{plugin_latest}\n"
130112
else:
131-
results += f"-[New]发现新版本插件:v{__version__} -> v{plugin_latest}\n"
132-
logger.info("发现新版本插件:v%s" % plugin_latest)
113+
results += f"- 当前插件已是最新版本 (v{CURRENT_PLUGIN_VERSION})\n"
133114
else:
134-
results += "-[Error]插件检查更新失败,请稍后重试"
115+
results += "-[Error] 插件检查更新失败,请稍后重试\n"
135116

117+
# 检查数据包更新
136118
datapack_latest = await check_datapack_latest()
137-
datapack_current = await get_datapack_ver()
138-
datapack_current = "1.0.0"
139-
if not datapack_current:
140-
results += "-数据包未安装"
141-
logger.info("数据包未安装")
119+
datapack_current_str = await get_datapack_ver()
120+
121+
if not datapack_current_str:
122+
results += "- 数据包未安装"
142123
else:
124+
datapack_current = parse_version(datapack_current_str)
143125
if datapack_latest:
144-
if datapack_latest == datapack_current:
145-
results += "-本地数据包已是最新版本(v%s)" % datapack_current
146-
logger.info("本地数据包已是最新版本(v%s)" % datapack_current)
126+
if datapack_latest > datapack_current:
127+
results += f"-[New] 发现新版本数据包:v{datapack_current} -> v{datapack_latest}"
128+
elif datapack_latest < datapack_current:
129+
results += f"-[Info] 当前数据包版本 v{datapack_current} 高于 Gitee 最新版 v{datapack_latest}"
147130
else:
148-
results += (
149-
f"-[New]发现新版本数据包:v{datapack_current} -> v{datapack_latest}"
150-
)
151-
logger.warning("发现新版本数据包:v%s" % datapack_latest)
131+
results += f"- 本地数据包已是最新版本 (v{datapack_current})"
152132
else:
153-
results += "-[Error]数据包检查更新失败,请稍后重试"
133+
results += "-[Error] 数据包检查更新失败,请稍后重试"
154134

155-
await matcher.send(results)
135+
await matcher.send(results.strip())
156136

157137

158138
async def upgrade_handle(matcher: Matcher):
159139
await matcher.send("正在检查插件更新...")
160140

161141
has_update = await upgrade_plugin()
162142

163-
if has_update is None:
164-
await matcher.send("检查更新失败,请检查网络或稍后再试。")
165-
logger.error("检查更新失败")
166-
return
167-
168143
if not has_update:
169-
await matcher.send("插件已是最新版本,无需更新。")
144+
await matcher.send("插件已是最新版本或检查更新失败,无需更新。")
170145
return
171146

172147
await matcher.send("发现新版本,正在尝试更新,请稍候...")
173148

174149
python_executable = sys.executable
175-
176150
command = [
177151
python_executable,
178152
"-m",
@@ -191,16 +165,11 @@ async def upgrade_handle(matcher: Matcher):
191165
logger.info("插件更新成功。")
192166
log_output = stdout.decode("utf-8", errors="ignore")
193167
logger.debug(f"Pip output:\n{log_output}")
194-
await matcher.send("插件更新成功!\n请手动重启 NoneBot 以使新版本生效。")
168+
await matcher.send("插件更新成功!\n请手动重启 NoneBot 以使新版本生效。")
195169
else:
196170
error_message = stderr.decode("utf-8", errors="ignore")
197171
logger.error(f"插件更新失败,返回码: {process.returncode}")
198172
logger.error(f"Pip 错误信息:\n{error_message}")
199-
200-
# 将关键错误信息发送给用户
201173
await matcher.send(
202-
f"插件更新失败!\n错误信息: `{error_message[-200:]}`\n请检查日志获取详细信息。"
174+
f"插件更新失败!\n错误信息: `{error_message[-200:]}`\n请检查日志获取详细信息。"
203175
)
204-
205-
206-
__all__ = ("check_handle", "upgrade_handle", "check_datapack_latest")

0 commit comments

Comments
 (0)