11import asyncio
2- from pathlib import Path
32import sys
4- from typing import Union
3+ from pathlib import Path
54
65import httpx
76import logging
109from nonebot import logger
1110from 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
1516try :
1617 __version__ = metadata .version ("nonebot-plugin-sky" )
1718except 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
121101async 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
158138async 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