11import os
22import sys
33import time
4- from json import JSONDecodeError
54
6- import aiohttp
75import psutil
86
97from astrbot .core import logger
1816
1917from .zip_updator import (
2018 PRERELEASE_TAG_REGEX ,
21- FetchReleaseError ,
2219 ReleaseInfo ,
2320 RepoZipUpdator ,
2421)
2522
2623
27- class InvalidUpdateTargetError ( ValueError ):
28- """Raised when update target arguments are invalid ."""
24+ class AstrBotUpdateError ( RuntimeError ):
25+ """Domain error for update-related failures ."""
2926
30-
31- class NoAvailableReleaseError (RuntimeError ):
32- """Raised when no eligible release can be selected."""
33-
34-
35- class AlreadyUpToDateError (RuntimeError ):
36- """Raised when current version is already the latest stable version."""
37-
38-
39- class UpdateFileNotFoundError (RuntimeError ):
40- """Raised when no update file exists for a requested tag/version."""
27+ def __init__ (self , reason : str , message : str ) -> None :
28+ super ().__init__ (message )
29+ self .reason = reason
4130
4231
4332class AstrBotUpdator (RepoZipUpdator ):
@@ -172,32 +161,20 @@ async def check_update(
172161 consider_prerelease ,
173162 )
174163
175- async def get_releases (self , include_nightly : bool = False ) -> list [dict ]:
176- releases = await self .fetch_release_info (self .ASTRBOT_RELEASE_API )
177- if not include_nightly :
178- return releases
164+ async def get_releases (self ) -> list [dict ]:
165+ return await self .fetch_release_info (self .ASTRBOT_RELEASE_API )
179166
167+ async def get_releases_with_nightly (self ) -> list [dict ]:
168+ releases = await self .get_releases ()
180169 nightly_release_url = f"{ self .GITHUB_RELEASE_API } /tags/{ self .NIGHTLY_TAG } "
181- expected_error_types = (
182- TimeoutError ,
183- aiohttp .ClientError ,
184- JSONDecodeError ,
185- FetchReleaseError ,
186- )
187170 try :
188171 nightly_releases = await self .fetch_release_info (nightly_release_url )
189172 except Exception as e :
190- if isinstance (e , expected_error_types ):
191- logger .warning (
192- "获取 nightly 发布信息失败,跳过 nightly。"
193- f"url={ nightly_release_url } , error_type={ type (e ).__name__ } , detail={ e } " ,
194- )
195- return releases
196- logger .exception (
197- "获取 nightly 发布信息出现非预期异常。"
198- f"url={ nightly_release_url } , error_type={ type (e ).__name__ } " ,
173+ logger .warning (
174+ "获取 nightly 发布信息失败,跳过 nightly。"
175+ f"url={ nightly_release_url } , error_type={ type (e ).__name__ } , detail={ e } " ,
199176 )
200- raise
177+ return releases
201178
202179 if not nightly_releases :
203180 return releases
@@ -215,15 +192,16 @@ def _parse_update_target(
215192 version_str = str (version ).strip () if version is not None else ""
216193
217194 if latest and version_str :
218- raise InvalidUpdateTargetError (
219- "latest=True 时不能同时指定 version,请将 latest 设为 False。"
195+ raise AstrBotUpdateError (
196+ "invalid_target" ,
197+ "latest=True 时不能同时指定 version,请将 latest 设为 False。" ,
220198 )
221199
222200 if latest :
223201 return "latest" , ""
224202
225203 if not version_str :
226- raise InvalidUpdateTargetError ( "未指定有效的更新目标。" )
204+ raise AstrBotUpdateError ( "invalid_target" , "未指定有效的更新目标。" )
227205
228206 if version_str .lower () == self .NIGHTLY_TAG :
229207 return "nightly" , self .NIGHTLY_TAG
@@ -232,67 +210,63 @@ def _parse_update_target(
232210 return "tag" , version_str
233211
234212 if len (version_str ) != 40 :
235- raise InvalidUpdateTargetError ("commit hash 长度不正确,应为 40" )
213+ raise AstrBotUpdateError (
214+ "invalid_target" , "commit hash 长度不正确,应为 40"
215+ )
236216 return "commit" , version_str
237217
238- async def _resolve_latest_target (self ) -> tuple [str , str ]:
239- releases = await self .get_releases ()
240- latest_release = next (
241- (
242- item
243- for item in releases
244- if (tag := item .get ("tag_name" , "" ))
245- and not PRERELEASE_TAG_REGEX .search (tag )
246- ),
247- None ,
248- )
249- if latest_release is None :
250- raise NoAvailableReleaseError ("未找到可用的发布版本。" )
251-
252- latest_version = latest_release ["tag_name" ]
253- if self .compare_version (VERSION , latest_version ) >= 0 :
254- raise AlreadyUpToDateError ("当前已经是最新版本。" )
255- return latest_version , latest_release ["zipball_url" ]
256-
257- async def _resolve_nightly_target (self ) -> tuple [str , str ]:
258- releases = await self .get_releases (include_nightly = True )
259- nightly_release = next (
260- (
261- item
262- for item in releases
263- if item .get ("tag_name" , "" ).lower () == self .NIGHTLY_TAG
264- ),
265- None ,
266- )
267- if nightly_release is not None :
268- return self .NIGHTLY_TAG , nightly_release ["zipball_url" ]
269- return self .NIGHTLY_TAG , (
270- f"{ self .GITHUB_ARCHIVE_BASE } /refs/tags/{ self .NIGHTLY_TAG } .zip"
271- )
272-
273- async def _resolve_tag_target (self , version_str : str ) -> tuple [str , str ]:
274- releases = await self .get_releases ()
275- for data in releases :
276- if data .get ("tag_name" ) == version_str :
277- return version_str , data ["zipball_url" ]
278- raise UpdateFileNotFoundError (f"未找到版本号为 { version_str } 的更新文件。" )
279-
280- def _resolve_commit_target (self , version_str : str ) -> tuple [str , str ]:
281- return version_str , f"{ self .GITHUB_ARCHIVE_BASE } /{ version_str } .zip"
282-
283218 async def _resolve_update_target (
284219 self ,
285220 latest : bool ,
286221 version : str | None ,
287222 ) -> tuple [str , str ]:
288223 kind , value = self ._parse_update_target (latest , version )
224+
289225 if kind == "latest" :
290- return await self ._resolve_latest_target ()
226+ releases = await self .get_releases ()
227+ latest_release = next (
228+ (
229+ item
230+ for item in releases
231+ if (tag := item .get ("tag_name" , "" ))
232+ and not PRERELEASE_TAG_REGEX .search (tag )
233+ ),
234+ None ,
235+ )
236+ if latest_release is None :
237+ raise AstrBotUpdateError ("no_release" , "未找到可用的发布版本。" )
238+ latest_version = latest_release ["tag_name" ]
239+ if self .compare_version (VERSION , latest_version ) >= 0 :
240+ raise AstrBotUpdateError ("up_to_date" , "当前已经是最新版本。" )
241+ return latest_version , latest_release ["zipball_url" ]
242+
291243 if kind == "nightly" :
292- return await self ._resolve_nightly_target ()
244+ releases = await self .get_releases_with_nightly ()
245+ nightly_release = next (
246+ (
247+ item
248+ for item in releases
249+ if item .get ("tag_name" , "" ).lower () == self .NIGHTLY_TAG
250+ ),
251+ None ,
252+ )
253+ if nightly_release is not None :
254+ return self .NIGHTLY_TAG , nightly_release ["zipball_url" ]
255+ return self .NIGHTLY_TAG , (
256+ f"{ self .GITHUB_ARCHIVE_BASE } /refs/tags/{ self .NIGHTLY_TAG } .zip"
257+ )
258+
293259 if kind == "tag" :
294- return await self ._resolve_tag_target (value )
295- return self ._resolve_commit_target (value )
260+ releases = await self .get_releases ()
261+ for data in releases :
262+ if data .get ("tag_name" ) == value :
263+ return value , data ["zipball_url" ]
264+ raise AstrBotUpdateError (
265+ "file_not_found" ,
266+ f"未找到版本号为 { value } 的更新文件。" ,
267+ )
268+
269+ return value , f"{ self .GITHUB_ARCHIVE_BASE } /{ value } .zip"
296270
297271 async def update (self , reboot = False , latest = True , version = None , proxy = "" ) -> None :
298272 if os .environ .get ("ASTRBOT_CLI" ) or os .environ .get ("ASTRBOT_LAUNCHER" ):
@@ -305,8 +279,10 @@ async def update(self, reboot=False, latest=True, version=None, proxy="") -> Non
305279 latest ,
306280 version ,
307281 )
308- except InvalidUpdateTargetError as e :
309- raise InvalidUpdateTargetError (f"更新参数错误: { e } " ) from e
282+ except AstrBotUpdateError as e :
283+ if e .reason == "invalid_target" :
284+ raise AstrBotUpdateError ("invalid_target" , f"更新参数错误: { e } " ) from e
285+ raise
310286
311287 logger .info (f"准备更新至 AstrBot Core: { target_version } " )
312288
0 commit comments