commit 28d5fd9476459581177578368eeeb672df820e84
Author: taynpg <taynpg@163.com>
Date:   Wed Jan 22 10:22:58 2025 +0800

    add: transm

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..aeb7268
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+downloads
+*.exe
+.DS_Store
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..664b24b
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,15 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Launch Program with External cmd",
+            "type": "go",
+            "request": "launch",
+            "mode": "debug",
+            "program": "${workspaceFolder}",
+            "env": {}, // 可以在这里设置环境变量
+            "args": [], // 传递给程序的参数
+            "console": "externalTerminal"
+        }
+    ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..30e3d75
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,6 @@
+{
+    "files.autoSave": "onFocusChange",
+    "editor.fontSize": 14,
+    "editor.fontFamily": "'Source Code Pro', 'Source Code Pro', monospace",
+    "terminal.integrated.fontFamily": "Source Code Pro",
+}
\ No newline at end of file
diff --git a/REAME.md b/REAME.md
new file mode 100644
index 0000000..4efb493
--- /dev/null
+++ b/REAME.md
@@ -0,0 +1,3 @@
+# 简介
+
+`sinxmiao`站点下的工具快速下载器。
\ No newline at end of file
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..ac9e389
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module tooldown
+
+go 1.23.5
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..0bc84f9
--- /dev/null
+++ b/main.go
@@ -0,0 +1,178 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+)
+
+// 仓库配置
+var repositories = []string{
+	"https://www.sinxmiao.cn/taynpg/transm",
+}
+
+const downloadDir = "./downloads/" // 下载路径
+
+// Release 结构体用于解析 API 返回的 JSON 数据
+type Release struct {
+	TagName   string  `json:"tag_name"`
+	CreatedAt string  `json:"created_at"`
+	Assets    []Asset `json:"assets"`
+}
+
+// Asset 结构体用于解析每个发行版的附件信息
+type Asset struct {
+	Name               string `json:"name"`
+	BrowserDownloadURL string `json:"browser_download_url"`
+}
+
+func main() {
+	// 选择仓库
+	repo := chooseRepository(repositories)
+	apiURL := convertToAPIURL(repo)
+
+	// 获取所有版本信息
+	releases, err := fetchReleases(apiURL)
+	if err != nil {
+		fmt.Printf("获取发行版信息失败: %v\n", err)
+		return
+	}
+
+	// 打印版本信息并选择版本
+	release := chooseRelease(releases)
+
+	// 打印附件信息并选择附件
+	asset := chooseAsset(release)
+
+	// 创建下载目录
+	if err := os.MkdirAll(downloadDir, os.ModePerm); err != nil {
+		fmt.Printf("创建下载目录失败: %v\n", err)
+		return
+	}
+
+	// 下载选择的附件
+	fmt.Printf("开始下载: %s\n", asset.Name)
+	if err := downloadFile(asset.BrowserDownloadURL, filepath.Join(downloadDir, asset.Name)); err != nil {
+		fmt.Printf("下载失败: %v\n", err)
+	} else {
+		fmt.Printf("下载完成: %s\n", asset.Name)
+	}
+}
+
+// chooseRepository 选择仓库
+func chooseRepository(repositories []string) string {
+	for {
+		fmt.Println("可用的仓库:")
+		for i, repo := range repositories {
+			fmt.Printf("[%d] %s\n", i+1, repo[strings.LastIndex(repo, "/")+1:])
+		}
+		fmt.Print("请选择一个仓库 (输入编号): ")
+
+		var choice int
+		_, err := fmt.Scan(&choice)
+		if err == nil && choice > 0 && choice <= len(repositories) {
+			return repositories[choice-1]
+		}
+		fmt.Println("无效的选择,请重新输入。")
+	}
+}
+
+// convertToAPIURL 将仓库链接转换为 API 地址
+func convertToAPIURL(repo string) string {
+	// 将仓库链接转换为 Gitea API 地址
+	return strings.Replace(repo, "https://www.sinxmiao.cn", "https://www.sinxmiao.cn/api/v1/repos", 1) + "/releases"
+}
+
+// fetchReleases 获取所有版本信息
+func fetchReleases(apiURL string) ([]Release, error) {
+	resp, err := http.Get(apiURL)
+	if err != nil {
+		return nil, fmt.Errorf("请求 API 失败: %v", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("API 响应状态码: %d", resp.StatusCode)
+	}
+
+	var releases []Release
+	if err := json.NewDecoder(resp.Body).Decode(&releases); err != nil {
+		return nil, fmt.Errorf("解析 JSON 失败: %v", err)
+	}
+
+	// 按创建时间排序
+	sort.Slice(releases, func(i, j int) bool {
+		return releases[i].CreatedAt < releases[j].CreatedAt
+	})
+
+	return releases, nil
+}
+
+// chooseRelease 打印版本信息并选择版本
+func chooseRelease(releases []Release) *Release {
+	for {
+		fmt.Println("可用的版本:")
+		for i, release := range releases {
+			fmt.Printf("[%d] %s (%s)\n", i+1, release.TagName, release.CreatedAt)
+		}
+		fmt.Print("请选择一个版本 (输入编号): ")
+
+		var choice int
+		_, err := fmt.Scan(&choice)
+		if err == nil && choice > 0 && choice <= len(releases) {
+			return &releases[choice-1]
+		}
+		fmt.Println("无效的选择,请重新输入。")
+	}
+}
+
+// chooseAsset 打印附件信息并选择附件
+func chooseAsset(release *Release) *Asset {
+	for {
+		fmt.Println("可用的附件:")
+		for i, asset := range release.Assets {
+			fmt.Printf("[%d] %s\n", i+1, asset.Name)
+		}
+		fmt.Print("请选择一个附件 (输入编号): ")
+
+		var choice int
+		_, err := fmt.Scan(&choice)
+		if err == nil && choice > 0 && choice <= len(release.Assets) {
+			return &release.Assets[choice-1]
+		}
+		fmt.Println("无效的选择,请重新输入。")
+	}
+}
+
+// downloadFile 下载文件到指定路径
+func downloadFile(url, filePath string) error {
+	resp, err := http.Get(url)
+	if err != nil {
+		return fmt.Errorf("请求下载链接失败: %v", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return fmt.Errorf("下载链接响应状态码: %d", resp.StatusCode)
+	}
+
+	// 创建文件
+	out, err := os.Create(filePath)
+	if err != nil {
+		return fmt.Errorf("创建文件失败: %v", err)
+	}
+	defer out.Close()
+
+	// 写入文件
+	_, err = io.Copy(out, resp.Body)
+	if err != nil {
+		return fmt.Errorf("保存文件失败: %v", err)
+	}
+
+	return nil
+}