mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-15 14:22:55 +08:00
214 lines
5.7 KiB
Python
214 lines
5.7 KiB
Python
from enum import Enum
|
|
from abc import ABC, abstractmethod
|
|
|
|
from aiocache import cached
|
|
from strenum import StrEnum
|
|
from pydantic import BaseModel
|
|
|
|
from ..http_utils import AsyncHttpx
|
|
from .consts import CACHED_API_TTL, GIT_API_TREES_FORMAT, JSD_PACKAGE_API_FORMAT
|
|
from .func import (
|
|
get_fastest_raw_format,
|
|
get_fastest_archive_format,
|
|
get_fastest_release_source_format,
|
|
)
|
|
|
|
|
|
class RepoInfo(BaseModel):
|
|
"""仓库信息"""
|
|
|
|
owner: str
|
|
repo: str
|
|
branch: str = "main"
|
|
|
|
async def get_raw_download_url(self, path: str):
|
|
url_format = await get_fastest_raw_format()
|
|
return url_format.format(**self.dict(), path=path)
|
|
|
|
async def get_archive_download_url(self):
|
|
url_format = await get_fastest_archive_format()
|
|
return url_format.format(**self.dict())
|
|
|
|
async def get_release_source_download_url_tgz(self, version: str):
|
|
url_format = await get_fastest_release_source_format()
|
|
return url_format.format(**self.dict(), version=version, compress="tar.gz")
|
|
|
|
async def get_release_source_download_url_zip(self, version: str):
|
|
url_format = await get_fastest_release_source_format()
|
|
return url_format.format(**self.dict(), version=version, compress="zip")
|
|
|
|
|
|
class FileType(StrEnum):
|
|
"""文件类型"""
|
|
|
|
FILE = "file"
|
|
DIR = "directory"
|
|
PACKAGE = "gh"
|
|
|
|
|
|
class BaseAPI(BaseModel, ABC):
|
|
"""基础接口"""
|
|
|
|
@classmethod
|
|
@abstractmethod
|
|
@cached(ttl=CACHED_API_TTL)
|
|
async def parse_repo_info(cls, repo_info: RepoInfo) -> "BaseAPI": ...
|
|
|
|
@abstractmethod
|
|
def get_files(cls, module_path: str, is_dir) -> list[str]: ...
|
|
|
|
|
|
class JsdelivrAPI(BaseAPI):
|
|
"""jsdelivr接口"""
|
|
|
|
type: FileType
|
|
name: str
|
|
files: list["JsdelivrAPI"] = []
|
|
|
|
def recurrence_files(self, dir_path: str, is_dir: bool = True) -> list[str]:
|
|
"""
|
|
递归获取文件路径
|
|
|
|
参数:
|
|
files: 文件列表
|
|
dir_path: 目录路径
|
|
is_dir: 是否为目录
|
|
|
|
返回:
|
|
list[str]: 文件路径
|
|
"""
|
|
if not is_dir and dir_path.endswith(self.name):
|
|
return [dir_path]
|
|
if self.files is None:
|
|
raise ValueError("文件列表为空")
|
|
paths = []
|
|
for file in self.files:
|
|
if is_dir and file.type == FileType.DIR and file.files:
|
|
paths.extend(self.recurrence_files(f"{dir_path}/{file.name}", is_dir))
|
|
elif file.type == FileType.FILE:
|
|
if is_dir:
|
|
paths.append(f"{dir_path}/{file.name}")
|
|
elif dir_path.endswith(file.name):
|
|
paths.append(dir_path)
|
|
return paths
|
|
|
|
def full_files_path(self, module_path: str, is_dir: bool = True) -> "JsdelivrAPI":
|
|
"""
|
|
获取文件路径
|
|
|
|
参数:
|
|
module_path: 模块路径
|
|
is_dir: 是否为目录
|
|
|
|
返回:
|
|
list[FileInfo]: 文件路径
|
|
"""
|
|
paths: list[str] = module_path.split("/")
|
|
if not is_dir:
|
|
paths = paths[:-1]
|
|
cur_file: JsdelivrAPI = self
|
|
|
|
for path in paths:
|
|
for file in cur_file.files:
|
|
if file.type == FileType.DIR and file.name == path and file.files:
|
|
cur_file = file
|
|
break
|
|
else:
|
|
raise ValueError(f"模块路径 {module_path} 不存在")
|
|
return cur_file
|
|
|
|
@classmethod
|
|
@cached(ttl=CACHED_API_TTL)
|
|
async def parse_repo_info(cls, repo_info: RepoInfo) -> "JsdelivrAPI":
|
|
"""解析仓库信息"""
|
|
|
|
"""获取插件包信息
|
|
|
|
参数:
|
|
repo_info: 仓库信息
|
|
|
|
返回:
|
|
FileInfo: 插件包信息
|
|
"""
|
|
jsd_package_url: str = JSD_PACKAGE_API_FORMAT.format(
|
|
owner=repo_info.owner, repo=repo_info.repo, branch=repo_info.branch
|
|
)
|
|
res = await AsyncHttpx.get(url=jsd_package_url)
|
|
if res.status_code != 200:
|
|
raise ValueError(f"下载错误, code: {res.status_code}")
|
|
return JsdelivrAPI(**res.json())
|
|
|
|
def get_files(self, module_path: str, is_dir: bool = True) -> list[str]:
|
|
"""获取文件路径"""
|
|
|
|
file = self.full_files_path(module_path, is_dir)
|
|
files = file.recurrence_files(
|
|
module_path,
|
|
is_dir,
|
|
)
|
|
return files
|
|
|
|
|
|
class TreeType(StrEnum):
|
|
"""树类型"""
|
|
|
|
FILE = "blob"
|
|
DIR = "tree"
|
|
|
|
|
|
class Tree(BaseModel):
|
|
"""树"""
|
|
|
|
path: str
|
|
mode: str
|
|
type: TreeType
|
|
sha: str
|
|
size: int | None
|
|
url: str
|
|
|
|
|
|
class GitHubAPI(BaseAPI):
|
|
"""github接口"""
|
|
|
|
sha: str
|
|
url: str
|
|
tree: list[Tree]
|
|
|
|
def export_files(self, module_path: str) -> list[str]:
|
|
"""导出文件路径"""
|
|
return [
|
|
file.path
|
|
for file in self.tree
|
|
if file.type == TreeType.FILE and file.path.startswith(module_path)
|
|
]
|
|
|
|
@classmethod
|
|
@cached(ttl=CACHED_API_TTL)
|
|
async def parse_repo_info(cls, repo_info: RepoInfo) -> "GitHubAPI":
|
|
"""获取仓库树
|
|
|
|
参数:
|
|
repo_info: 仓库信息
|
|
|
|
返回:
|
|
TreesInfo: 仓库树信息
|
|
"""
|
|
git_tree_url: str = GIT_API_TREES_FORMAT.format(
|
|
owner=repo_info.owner, repo=repo_info.repo, branch=repo_info.branch
|
|
)
|
|
res = await AsyncHttpx.get(url=git_tree_url)
|
|
if res.status_code != 200:
|
|
raise ValueError(f"下载错误, code: {res.status_code}")
|
|
return GitHubAPI(**res.json())
|
|
|
|
def get_files(self, module_path: str, is_dir: bool = True) -> list[str]:
|
|
"""获取文件路径"""
|
|
return self.export_files(module_path)
|
|
|
|
|
|
class PackageApi(Enum):
|
|
"""插件包接口"""
|
|
|
|
GITHUB = GitHubAPI
|
|
JSDELIVR = JsdelivrAPI
|