Просмотр исходного кода

Refactor Minecraft Version Module
& fix #333

Nattiden 7 месяцев назад
Родитель
Сommit
6bced09728

+ 34 - 29
Project/modules/apis.py

@@ -7,14 +7,20 @@ from homepagebuilder.interfaces import page_class_handles, require
 from homepagebuilder.core.page import CodeBasedPage
 from homepagebuilder.core.types import Context
 
+mcv = require('minecraft_versions')  # 需求前置 MinecraftVersions
+MCVM = mcv.MCVM
+
+
 def get_args(context):
     if setter := context.setter:
         return setter.override.get('args', {})
     return {}
 
-def is_true(string:str):
-    return string and isinstance(string,str) and string.lower() != 'false'
-    
+
+def is_true(string: str) -> bool:
+    return bool(string and isinstance(string, str) and string.lower() != 'false')
+
+
 @page_class_handles('apis/status')
 class StatusPage(CodeBasedPage):
 
@@ -25,22 +31,20 @@ class StatusPage(CodeBasedPage):
     def generate(self, *args, **kwargs):
         return 'ok'
 
-    def get_content_type(self, setter, *args, **kwargs):
+    def get_content_type(self, *args, **kwargs):
         return 'text/plain'
 
-mcv = require('minecraft_version') # 需求前置 MinecraftVersions
-latest_version = mcv.get_latest()
 
 @page_class_handles('apis/versions/latest')
 class LatestVersionAPI(CodeBasedPage):
 
     def __init__(self, project):
         super().__init__(project)
-        self.latest_release = self.project.base_library.get_card(mcv.get_latest('release'),False)
-        if latest_snap_shot_id := mcv.get_latest('snapshot'):
-            self.latest_snapshot = self.project.base_library.get_card(latest_snap_shot_id,False)
+        self.latest_release = self.project.base_library.get_card(MCVM.get_latest_version('release').id, False)
+        if latest_developing := MCVM.get_latest_version('developing'):
+            self.latest_developing = self.project.base_library.get_card(latest_developing.id, False)
         else:
-            self.latest_snapshot = None
+            self.latest_developing = None
         self.respond = None
 
     @property
@@ -49,42 +53,43 @@ class LatestVersionAPI(CodeBasedPage):
 
     def pregen_respond(self):
         respond = {}
-        if self.latest_snapshot:
-            respond['snapshot'] = self.extract_version_info(self.latest_snapshot)
+        if self.latest_developing:
+            respond['snapshot'] = self.extract_version_info(self.latest_developing)
         respond['release'] = self.extract_version_info(self.latest_release)
         self.respond = respond
-    
-    def extract_version_info(self,card):
+
+    def extract_version_info(self, card):
         respond = {}
         context = Context.get_current_context()
-        expendedcard = context.builder.template_manager.expend_card_placeholders(card,'')
-        self.copyinfo(respond,expendedcard,'version-type')
-        self.copyinfo(respond,expendedcard,'intro')
-        self.copyinfo(respond,expendedcard,'version-image-link')
-        self.copyinfo(respond,expendedcard,'server-jar')
-        self.copyinfo(respond,expendedcard,'translator')
-        self.copyinfo(respond,expendedcard,'official-link')
-        self.copyinfo(respond,expendedcard,'wiki-link')
-        self.copyinfo(respond,expendedcard,'version-id')
-        self.copyinfo(respond,expendedcard,'title')
+        expendedcard = context.builder.template_manager.expend_card_placeholders(card, '')
+        self.copyinfo(respond, expendedcard, 'version-type')
+        self.copyinfo(respond, expendedcard, 'intro')
+        self.copyinfo(respond, expendedcard, 'version-image-link')
+        self.copyinfo(respond, expendedcard, 'server-jar')
+        self.copyinfo(respond, expendedcard, 'translator')
+        self.copyinfo(respond, expendedcard, 'official-link')
+        self.copyinfo(respond, expendedcard, 'wiki-link')
+        self.copyinfo(respond, expendedcard, 'version-id')
+        self.copyinfo(respond, expendedcard, 'title')
         respond['homepage-json-link'] = f"https://news.bugjump.net/VersionDetail.json?ver={expendedcard['version-id']}"
         return respond
 
-    def copyinfo(self,respond,card,key):
+    def copyinfo(self, respond, card, key):
         respond[key] = card.get(key)
 
     def generate(self, *args, **kwargs):
         context = Context.get_current_context()
         args = get_args(context)
-        ensure_ascii = is_true(args.get('ascii',False))
+        ensure_ascii = is_true(args.get('ascii', False))
         print(args)
         if not self.respond:
             self.pregen_respond()
-        return json.dumps(self.respond,ensure_ascii=ensure_ascii)
+        return json.dumps(self.respond, ensure_ascii=ensure_ascii)
 
-    def get_content_type(self, setter, *args, **kwargs):
+    def get_content_type(self, *args, **kwargs):
         return 'application/json'
 
+
 @page_class_handles('apis/versions/latest-card')
 class LatestVersionCardAPI(CodeBasedPage):
     def generate(self, *args, **kwargs):
@@ -94,5 +99,5 @@ class LatestVersionCardAPI(CodeBasedPage):
         card = setter.decorate(card)
         return context.builder.template_manager.build(card)
 
-    def get_content_type(self, setter, *args, **kwargs):
+    def get_content_type(self, *args, **kwargs):
         return 'application/xml'

+ 0 - 17
Project/modules/mc_version_category.py

@@ -1,17 +0,0 @@
-"""
-获取版本分类的卡片ID列表
-"""
-from homepagebuilder.interfaces import script,require
-
-mcv = require('minecraft_version') # 需求前置 MinecraftVersions
-MANIFSET = mcv.get_manifset()
-FULL_VERSIONS = MANIFSET.get('versions')
-ID_LIST = [version.get('id') for version in FULL_VERSIONS]
-
-@script('VersionCategory')
-def version_category(cat_name,context,**_):
-    cards = list(filter(lambda card:isinstance(card.get('cats'),list)
-           and cat_name in card.get('cats'), context.project.get_all_card()))
-    cardrefs = [card['card_id'] for card in cards]
-    cardrefs.sort(key=lambda ver:ID_LIST.index(ver.split(':')[1]))
-    return str.join(';',cardrefs)

+ 0 - 42
Project/modules/mc_version_lists.py

@@ -1,42 +0,0 @@
-"""
-版本最新、存档页面列表
-"""
-
-from homepagebuilder.interfaces import require,script,format_code
-
-mcv = require('minecraft_version') # 需求前置 MinecraftVersions
-MANIFSET = mcv.get_manifset()
-FULL_VERSIONS = MANIFSET.get('versions')
-ID_LIST = [version.get('id') for version in FULL_VERSIONS]
-
-
-@script('VersionArchiveList')
-def version_achive_list(cat_name,context,card,**_):
-    components = context.components
-    cat_name = format_code(code = cat_name,data=card)
-    cards = list(filter(lambda card:isinstance(card.get('cats'),list)
-           and cat_name in card.get('cats'), context.project.get_all_card()))
-    code = '<StackPanel Margin="8,0,8,0">'
-    cards.sort(key=lambda card:ID_LIST.index(format_code(card['version-id'],
-                                                         data=card)))
-    first_card_version_type = format_code(cards[0]['version-type-id'], cards[0])
-    if len(cards) > 0 and first_card_version_type not in ['Release','April-Fools']:
-        code += components['VersionLinks/Future'].toxaml(card={})
-    for vercard in cards:
-        if vercard.get('lack'):
-            code += components['VersionLinks/Lack'].toxaml(card=vercard)
-        else:
-            code += components['VersionLinks/Common'].toxaml(card=vercard)
-    code += '</StackPanel>'
-    return code
-
-@script('VersionLatestList')
-def version_latest_list(context,**_):
-    proj = context.project
-    components = context.components
-    code = ''
-    for version_type in ['release','snapshot']:
-        if latest_version := mcv.get_latest(version_type):
-            vercard = proj.base_library.get_card(latest_version,False)
-            code += components['VersionLinks/Latest'].toxaml(card=vercard)
-    return code

+ 0 - 20
Project/modules/mc_version_mainpage.py

@@ -1,20 +0,0 @@
-"""
-主页MC版本卡片选择器
-"""
-from homepagebuilder.interfaces import script,require,Logger
-
-mcv = require('minecraft_version') # 需求前置 MinecraftVersions
-latest_version = mcv.get_latest()
-assert latest_version is not None
-latest_type = mcv.get_version_type(latest_version)
-logger = Logger('MainPageVersions')
-
-@script('MainPageVersions')
-def mainPageVersion(page_area,**_):
-    '''脚本体'''
-    if page_area == 'new':
-        return f"{latest_version} | latest = true | s = false"
-    else:
-        if latest_type == 'release':
-            return ''
-        return f"{mcv.get_latest('release')} | latest = true"

+ 0 - 11
Project/modules/mc_version_server.py

@@ -1,11 +0,0 @@
-"""
-获取MC版本服务器端 jar 包下载链接
-"""
-from homepagebuilder.interfaces import require,script
-
-mcv = require('minecraft_version') # 需求前置 MinecraftVersions
-get_server_jar = mcv.get_server_jar
-
-@script('ServerJar')
-def get_server_jar_script(card,**_):
-    return get_server_jar(card['version-id'])

+ 0 - 104
Project/modules/minecraft_version.py

@@ -1,104 +0,0 @@
-"""
-获取 MC 版本信息
-"""
-
-from homepagebuilder.interfaces import Logger
-from homepagebuilder.core.config import config
-from typing import TYPE_CHECKING
-import requests
-import json
-import re
-
-if TYPE_CHECKING:
-    from homepagebuilder.core.types import Context
-
-LAUNCHER_MANIFSET_URL = 'https://piston-meta.mojang.com/mc/game/version_manifest.json'
-MANIFSET = None
-VERSIONS_DICT = None
-FULL_VERSIONS = None
-LATEST_VERSIONS = None
-
-logger = Logger("VersionScript")
-
-def fetch_manifset():
-    logger.info("正在加载 MC 版本列表")
-    try:
-        response = requests.get(LAUNCHER_MANIFSET_URL,timeout=10000)
-        return json.loads(response.content)
-    except Exception as e:
-        logger.warning("加载 MC 版本列表失败")
-        logger.exception(e)
-        return {}
-
-def get_manifset():
-    global MANIFSET
-    if not MANIFSET:
-        MANIFSET = fetch_manifset()
-    return MANIFSET
-
-def init_version_dict():
-    global LATEST_VERSIONS,FULL_VERSIONS,VERSIONS_DICT
-    VERSIONS_DICT = {}
-    LATEST_VERSIONS = {'snapshot':None,'release':None}
-    manifset = get_manifset()
-    LATEST_VERSIONS.update(manifset['latest'])
-    FULL_VERSIONS = manifset.get('versions')
-    for version in FULL_VERSIONS:
-        VERSIONS_DICT[version['id']] = version
-    if get_version_type(LATEST_VERSIONS["snapshot"]) == 'release':
-        LATEST_VERSIONS["snapshot"] = None
-    logger.info(f'最新快照版:{LATEST_VERSIONS.get("snapshot")}; 最新正式版:{LATEST_VERSIONS["release"]}')
-
-def get_latest(version_type = 'all',version_rank = -1):
-    global LATEST_VERSIONS
-    if not LATEST_VERSIONS:
-        init_version_dict()
-    version_rank = int(version_rank)
-    if version_rank <= 0:
-        if(version_type == 'all'):
-            if latest_snapshot := LATEST_VERSIONS.get('snapshot'):
-                return latest_snapshot
-            else:
-                return LATEST_VERSIONS['release']
-        return LATEST_VERSIONS.get(version_type)
-    count = version_rank
-    for version in FULL_VERSIONS:
-        if version['type'] == version_type:
-            count -= 1
-            if count == 0:
-                return version['id']
-    return ''
-
-def get_server_jar(version_id):
-    global VERSIONS_DICT
-    if not (ver_info := VERSIONS_DICT.get(version_id)):
-        return None
-    response = requests.get(ver_info['url'],timeout=10000)
-    return json.loads(response.content)['downloads']['server']['url']
-
-SNAPSHOT_PATTERN = re.compile(r'^\d{2}[w|W]\d{2}[A-Fa-f]$')
-NEW_SNAPSHOT_PATTERN = re.compile(r'^\d{2}-snapshot-\d+$')
-PRE_RELEASE_PATTERN = re.compile(r'^.*-pre-\d+$')
-RELEASE_CANDIDATE_PATTERN = re.compile(r'^.*-rc-\d+$')
-RELEASE_PATTERN = re.compile(r'^\d{1,2}\.\d+(\.\d+)?$')
-
-def get_version_type(version_id:str) -> str:
-    '''获取版本类型'''
-    assert isinstance(version_id, str)
-    if re.match(SNAPSHOT_PATTERN,version_id) or re.match(NEW_SNAPSHOT_PATTERN,version_id):
-        return 'snapshot'
-    if re.match(PRE_RELEASE_PATTERN,version_id):
-        return 'pre_release'
-    if re.match(RELEASE_CANDIDATE_PATTERN,version_id):
-        return 'release_candidate'
-    if re.match(RELEASE_PATTERN,version_id):
-        return 'release'
-    return 'other'
-
-def init(context:'Context', *args, **kwargs):
-    global MANIFSET
-    if config('NewsHomepage.MCV.UseReloadCache', config('NewsHomepage.Debug')):
-        if mcv_manifset_cache := context.builder.get_data('mcv_manifset_cache'):
-            MANIFSET = mcv_manifset_cache
-        else:
-            context.builder.set_data('mcv_manifset_cache', get_manifset())

+ 274 - 0
Project/modules/minecraft_versions.py

@@ -0,0 +1,274 @@
+import re
+import json
+import requests
+from enum import Enum
+from typing import Optional, TypedDict, Literal
+from homepagebuilder.core.logger import Logger
+from homepagebuilder.interfaces import script, format_code
+from homepagebuilder.core.types import Context
+from homepagebuilder.core.utils.event import listen_event as on
+
+# region 版本模式定义
+DEVELOP_VERSION_PATTERN = re.compile(r'^(?P<major>[\d.]+)-(?P<type>[a-zA-Z]+)-?(?P<index>\d)+$')
+"开发版本号模式"
+RELEASE_PATTERN = re.compile(r'^\d{1,2}\.\d+(\.\d+)?$')
+"正式版本号模式"
+OLD_SNAPSHOT_PATTERN = re.compile(r'^\d{2}[w|W]\d{2}[A-Fa-f]$')
+"旧版本快照模式,用于 1.21.11 及以前的版本"
+# endregion
+
+logger = Logger('MinecraftVersion')
+
+# region 常量定义
+OFFICIAL_CHANGELOG_URL_PREFIX = 'https://minecraft.net/article/minecraft'
+LAUNCHER_MANIFEST_URL = 'https://piston-meta.mojang.com/mc/game/version_manifest.json'
+# endregion
+
+# region 官网更新日志重载词典
+OFFICIAL_CHANGE_LOG_OVERRIDE_LINKS: dict[str, str] = {}
+
+
+@on('project.import.return')
+def setup_official_changelog_override_links(*_, **__) -> None:
+    global OFFICIAL_CHANGE_LOG_OVERRIDE_LINKS
+    OFFICIAL_CHANGE_LOG_OVERRIDE_LINKS = Context.get_current_context().data.get('OfficialLink')
+# endregion
+
+# region 版本信息类定义
+class MinecraftManifestVersionInfo(TypedDict):
+    id: str
+    type: Literal['snapshot', 'release', 'old_beta', 'old_alpha']
+    url: str
+    time: str
+    releaseTime: str
+
+
+class MinecraftManifestLatestVersionInfo(TypedDict):
+    snapshot: Optional[str]
+    release: Optional[str]
+
+
+class MinecraftManifest(TypedDict):
+    latest: MinecraftManifestLatestVersionInfo
+    versions: list[MinecraftManifestVersionInfo]
+
+
+# endregion
+
+# region 版本类定义
+class VersionType(Enum):
+    SNAPSHOT = 'snapshot'
+    PRE_RELEASE = 'pre'
+    RELEASE_CANDIDATE = 'rc'
+    RELEASE = 'release'
+    OTHER = 'other'
+
+
+class MinecraftVersion:
+    id: str
+    type: VersionType
+    url: str
+    time: str
+    release_time: str
+    use_old_id_format: bool = False
+    major_version: Optional[str] = None
+    release_index: Optional[str] = None
+
+    __server_jar_url: Optional[str] = None
+
+    @property
+    def server_jar_url(self) -> str:
+        if self.__server_jar_url:
+            return self.__server_jar_url
+        server_jar_url: Optional[str]
+        response = requests.get(self.url, timeout=10000)
+        server_jar_url = json.loads(response.content).get('downloads').get('server').get('url')
+        if not server_jar_url:
+            raise ValueError(f"版本 {self.id} 的服务器端下载链接不存在")
+        self.__server_jar_url = server_jar_url
+        return server_jar_url
+
+    __change_log_url: Optional[str] = None
+
+    @property
+    def change_log_url(self) -> str:
+        """获取版本更新日志链接"""
+        if self.__change_log_url:
+            return self.__change_log_url
+        if override_url := OFFICIAL_CHANGE_LOG_OVERRIDE_LINKS.get(self.id):
+            self.__change_log_url = override_url
+            return override_url
+        if self.type == VersionType.OTHER:
+            return ""
+        if self.use_old_id_format:
+            self.__change_log_url = OFFICIAL_CHANGELOG_URL_PREFIX + '-snapshot-' + self.id.lower()
+        elif self.type == VersionType.RELEASE:
+            self.__change_log_url = f"{OFFICIAL_CHANGELOG_URL_PREFIX}-java-edition-{self.id.replace('.', '-')}"
+        else:
+            assert self.major_version is not None
+            self.__change_log_url = f"{OFFICIAL_CHANGELOG_URL_PREFIX}-" \
+                                    f"{self.major_version.replace('.', '-')}-" \
+                                    f"{self.type.name.lower().replace('_', '-')}-{self.release_index}"
+        return self.__change_log_url
+
+    def __init__(self, version_id: str, url: str, time: str, release_time: str):
+        self.id = version_id
+        self.url = url
+        self.time = time
+        self.release_time = release_time
+        if m := re.match(DEVELOP_VERSION_PATTERN, version_id):
+            self.type = VersionType(m.group('type').lower())
+            self.major_version = m.group('major')
+            self.release_index = m.group('index')
+        elif re.match(OLD_SNAPSHOT_PATTERN, version_id):
+            self.type = VersionType.SNAPSHOT
+            self.use_old_id_format = True
+        elif re.match(RELEASE_PATTERN, version_id):
+            self.type = VersionType.RELEASE
+            self.major_version = version_id
+        else:
+            self.type = VersionType.OTHER
+
+
+class MinecraftVersionManager:
+    __latest_development_version_id: Optional[str]
+    __latest_release_version_id: Optional[str]
+    __manifest: MinecraftManifest
+    __manifest_versions: list[MinecraftManifestVersionInfo]
+    __versions: dict[str, MinecraftVersion] = {}
+    __version_ids: list[str] = []
+
+    def __init__(self):
+        self.__manifest = self.fetch_manifest()
+        self.__latest_development_version_id = self.__manifest['latest']['snapshot']
+        self.__latest_release_version_id = self.__manifest['latest']['release']
+        self.__manifest_versions = self.__manifest.get('versions', [])
+        logger.info(f"最新快照版:{self.__latest_development_version_id}, "
+                    f"最新正式版:{self.__latest_release_version_id}")
+        self.__version_ids = [version['id'] for version in self.__manifest_versions]
+
+    def get(self, version_id: str) -> Optional[MinecraftVersion]:
+        if version_id in self.__versions:
+            return self.__versions[version_id]
+        for version_info in self.__manifest_versions:
+            if version_info['id'] == version_id:
+                version = MinecraftVersion(version_id=version_info['id'],
+                                           url=version_info['url'],
+                                           time=version_info['time'],
+                                           release_time=version_info['releaseTime'])
+                self.__versions[version_id] = version
+                return version
+        logger.warning(f"版本 {version_id} 不存在于版本列表中")
+        return None
+
+    def get_version_id_list(self) -> list[str]:
+        return self.__version_ids
+
+    def get_latest_version(self, version_type: Literal['any', 'development', 'release'] = 'any'
+                           ) -> Optional[MinecraftVersion]:
+        if version_type == 'any':
+            if self.__latest_development_version_id:
+                return self.get(self.__latest_development_version_id)
+            elif self.__latest_release_version_id:
+                return self.get(self.__latest_release_version_id)
+            else:
+                return None
+        elif version_type == 'development' and self.__latest_development_version_id:
+            return self.get(self.__latest_development_version_id)
+        elif version_type == 'release' and self.__latest_release_version_id:
+            return self.get(self.__latest_release_version_id)
+        else:
+            return None
+
+    @classmethod
+    def fetch_manifest(cls) -> MinecraftManifest:
+        logger.info("正在加载 MC 版本列表")
+        try:
+            response = requests.get(LAUNCHER_MANIFEST_URL, timeout=10000)
+            return json.loads(response.content)
+        except Exception as e:
+            logger.warning("加载 MC 版本列表失败")
+            logger.exception(e)
+            return MinecraftManifest(latest=MinecraftManifestLatestVersionInfo(snapshot=None, release=None),
+                                     versions=[])
+
+
+# endregion
+
+MCVM = MinecraftVersionManager()
+
+# region 脚本接口
+
+
+@script('ServerJar')
+def get_server_jar_script(card, **_) -> str:
+    """获取该卡片MC版本服务器端 jar 包下载链接"""
+    version = MCVM.get(card['version-id'])
+    assert version is not None, f"无法获取版本 {card['version-id']} 的信息,无法获取服务器端下载链接"
+    return version.server_jar_url
+
+
+@script('MainPageVersions')
+def main_page_versions_script(page_area, **_) -> str:
+    """获取特定区域的最新 MC 版本的卡片引用"""
+    latest_version = MCVM.get_latest_version(version_type='any')
+    assert latest_version is not None, "无法获取最新版本信息"
+    if page_area == 'new':
+        return f"{latest_version.id} | latest = true | s = false"
+    else:
+        if latest_version.type == VersionType.RELEASE:
+            return ''
+        latest_development_version = MCVM.get_latest_version(version_type='release')
+        assert latest_development_version is not None, "无法获取最新开发版本信息"
+        return f"{latest_development_version.id} | latest = true"
+
+
+@script('VersionLatestList')
+def version_latest_list_script(**_):
+    """获取最新版本展示列表(用于新闻主页精简版)"""
+    context = Context.get_current_context()
+    library = context.project.base_library
+    components = context.components
+    code = ''
+    version_type: Literal['development', 'release']
+    for version_type in ['release', 'development']:
+        if latest_version := MCVM.get_latest_version(version_type=version_type):
+            version_card = library.get_card(latest_version.id, False)
+            code += components['VersionLinks/Latest'].toxaml(card=version_card)
+    return code
+
+
+@script('VersionArchiveList')
+def version_archive_list_script(cat_name, card, **_):
+    context = Context.get_current_context()
+    components = context.components
+    cat_name = format_code(code=cat_name, data=card)
+    cards = list(filter(lambda c: isinstance(c.get('cats'), list)
+                                  and cat_name in c.get('cats'), context.project.get_all_card()))
+    code = '<StackPanel Margin="8,0,8,0">'
+    cards.sort(key=lambda c: MCVM.get_version_id_list().index(format_code(c['version-id'], data=c)))
+    first_card_version_type = format_code(cards[0]['version-type-id'], cards[0])
+    if len(cards) > 0 and first_card_version_type not in ['Release', 'April-Fools']:
+        code += components['VersionLinks/Future'].toxaml(card={})
+    for version_card in cards:
+        if version_card.get('lack'):
+            code += components['VersionLinks/Lack'].toxaml(card=version_card)
+        else:
+            code += components['VersionLinks/Common'].toxaml(card=version_card)
+    code += '</StackPanel>'
+    return code
+
+
+@script('GetLink')
+def get_link(link_type, card, link_key=None, **kwargs):
+    name: str = card['version-id']
+    name = format_code(name, card)
+    if link_type == 'Official':
+        return MCVM.get(name).change_log_url
+    if card.get('wip') == 'true' and link_type != 'Official':
+        return ''
+    data = Context.get_current_context().data[f'{link_type}Link']
+    if link_key:  # 如果指定查找键
+        return data[link_key]
+    return data.get(name, "")
+# endregion

+ 0 - 45
Project/modules/offical_link.py

@@ -1,45 +0,0 @@
-"""
-生成官网链接
-"""
-
-import re
-from homepagebuilder.interfaces import script
-from homepagebuilder.core.formatter import format_code
-
-SNAPSHOT_PATTERN = re.compile(r'^[0-9]{2}[w|W][0-9]{2}[A-Fa-f]$')
-NEW_SNAPSHOT_PATTERN = re.compile(r'^[0-9]{2}(\.[0-9]+)*-snapshot-[0-9]+$')
-PRE_RELEASE_PATTERN = re.compile(r'^.*-pre[0-9]+$')
-RELEASE_CANDIDATE_PATTERN = re.compile(r'^.*-rc[0-9]+$')
-RELEASE_PATTERN = re.compile(r'^1\.[0-9]+(\.[0-9]+)?$')
-
-def gen_official_link(vid:str):
-    '''生成官网链接'''
-    if re.match(SNAPSHOT_PATTERN,vid):
-        return f'https://minecraft.net/article/minecraft-snapshot-{vid.lower()}'
-    if re.match(NEW_SNAPSHOT_PATTERN,vid):
-        return f'https://minecraft.net/article/minecraft-{vid.replace(".","-")}'
-    if re.match(PRE_RELEASE_PATTERN,vid):
-        version,sub=vid.split('-pre')
-        return f'https://minecraft.net/article/minecraft-{version.replace(".","-")}-pre-release-{sub}'
-    if re.match(RELEASE_CANDIDATE_PATTERN,vid):
-        version,sub=vid.split('-rc')
-        return f'https://minecraft.net/article/minecraft-{version.replace(".","-")}-release-candidate-{sub}'
-    if re.match(RELEASE_PATTERN,vid):
-        return f'https://minecraft.net/article/minecraft-java-edition-{vid.replace(".","-")}'
-    return ''
-
-@script('GetLink')
-def get_link(link_type,link_key=None,**kwargs):
-    card = kwargs['card']
-    context = kwargs['context']
-    name:str = card['version-id']
-    if card.get('wip') == 'true' and link_type != 'Official':
-        return ''
-    data = context.data[f'{link_type}Link']
-    if link_key:
-        return data[link_key]
-    name = format_code(name,card,context)
-    if link_type == 'Official' and name not in data:
-        return gen_official_link(name)
-    result = data.get(name)
-    return result if result else ""