diff --git a/goecs.go b/goecs.go index 1a22dd4..5238f5f 100644 --- a/goecs.go +++ b/goecs.go @@ -246,9 +246,43 @@ Loop: } func printMenuOptions() { + var stats *utils.StatsResponse + var statsErr error + var githubInfo *utils.GitHubRelease + var githubErr error + var pwg sync.WaitGroup + pwg.Add(2) + go func() { + defer pwg.Done() + stats, statsErr = utils.GetGoescStats() + }() + go func() { + defer pwg.Done() + githubInfo, githubErr = utils.GetLatestEcsRelease() + }() + pwg.Wait() + var statsInfo string + if statsErr != nil { + statsInfo = "NULL" + } else { + statsInfo = fmt.Sprintf("总使用量: %s | 今日使用: %s", + utils.FormatGoecsNumber(stats.Total), + utils.FormatGoecsNumber(stats.Daily)) + } + var cmp int + if githubErr == nil { + cmp = utils.CompareVersions(ecsVersion, githubInfo.TagName) + } else { + cmp = 0 + } switch language { case "zh": - fmt.Println("VPS融合怪版本: ", ecsVersion) + fmt.Printf("VPS融合怪版本: %s\n", ecsVersion) + switch cmp { + case -1: + fmt.Printf("检测到新版本 %s 如有必要请更新!\n", githubInfo.TagName) + } + fmt.Printf("使用统计: %s\n", statsInfo) fmt.Println("1. 融合怪完全体(能测全测)") fmt.Println("2. 极简版(系统信息+CPU+内存+磁盘+测速节点5个)") fmt.Println("3. 精简版(系统信息+CPU+内存+磁盘+常用流媒体+路由+测速节点5个)") @@ -256,11 +290,18 @@ func printMenuOptions() { fmt.Println("5. 精简解锁版(系统信息+CPU+内存+磁盘IO+御三家+常用流媒体+测速节点5个)") fmt.Println("6. 网络单项(IP质量检测+上游及三网回程+广州三网回程详细路由+全国延迟+测速节点11个)") fmt.Println("7. 解锁单项(御三家解锁+常用流媒体解锁)") - fmt.Println("8. 硬件单项(系统信息+CPU+内存+dd磁盘测试+fio磁盘测试)") + fmt.Println("8. 硬件单项(系统信息+CPU+dd磁盘测试+fio磁盘测试)") fmt.Println("9. IP质量检测(15个数据库的IP检测+邮件端口检测)") fmt.Println("10. 三网回程线路检测+三网回程详细路由(北京上海广州成都)+三网延迟测试(全国)") case "en": - fmt.Println("VPS Fusion Monster Test Version: ", ecsVersion) + fmt.Printf("VPS Fusion Monster Test Version: %s\n", ecsVersion) + switch cmp { + case -1: + fmt.Printf("New version detected %s update if necessary!\n", githubInfo.TagName) + } + fmt.Printf("Total Usage: %s | Daily Usage: %s\n", + utils.FormatGoecsNumber(stats.Total), + utils.FormatGoecsNumber(stats.Daily)) fmt.Println("1. VPS Fusion Monster Test Comprehensive Test Suite") fmt.Println("2. Minimal Test Suite (System Info + CPU + Memory + Disk + 5 Speed Test Nodes)") fmt.Println("3. Standard Test Suite (System Info + CPU + Memory + Disk + Basic Unlock Tests + 5 Speed Test Nodes)") diff --git a/utils/utils.go b/utils/utils.go index 7e23fc1..922d301 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -11,6 +11,7 @@ import ( "os" "path/filepath" "regexp" + "strconv" "strings" "sync" "time" @@ -24,6 +25,21 @@ import ( "github.com/oneclickvirt/security/network" ) +// 获取本程序本日及总执行的统计信息 +type StatsResponse struct { + Counter string `json:"counter"` + Action string `json:"action"` + Total int `json:"total"` + Daily int `json:"daily"` + Date string `json:"date"` + Timestamp string `json:"timestamp"` +} + +// 获取最新的Github的仓库中的版本 +type GitHubRelease struct { + TagName string `json:"tag_name"` +} + // PrintCenteredTitle 根据指定的宽度打印居中标题 func PrintCenteredTitle(title string, width int) { // 计算字符串的字符数 @@ -336,8 +352,6 @@ func ProcessAndUpload(output string, filePath string, enableUplaod bool) (string return "", "" } -// ============================= 前置联网能力检测 ============================= - var StackType string type NetCheckResult struct { @@ -359,6 +373,7 @@ func makeResolver(proto, dnsAddr string) *net.Resolver { } } +// 前置联网能力检测 func CheckPublicAccess(timeout time.Duration) NetCheckResult { if timeout < 2*time.Second { timeout = 2 * time.Second @@ -492,3 +507,77 @@ result: StackType: stack, } } + +// 获取每日/总的程序执行统计信息 +func GetGoescStats() (*StatsResponse, error) { + client := req.C().SetTimeout(5 * time.Second) + var stats StatsResponse + resp, err := client.R(). + SetSuccessResult(&stats). + Get("https://hits.spiritlhl.net/goecs") + if err != nil { + return nil, err + } + if !resp.IsSuccessState() { + return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + return &stats, nil +} + +// 统计结果单位转换 +func FormatGoecsNumber(num int) string { + if num >= 1000000 { + return fmt.Sprintf("%.1fM", float64(num)/1000000) + } else if num >= 1000 { + return fmt.Sprintf("%.1fK", float64(num)/1000) + } + return fmt.Sprintf("%d", num) +} + +// 通过Github的API检索仓库最新TAG的版本 +func GetLatestEcsRelease() (*GitHubRelease, error) { + urls := []string{ + "https://api.github.com/repos/oneclickvirt/ecs/releases/latest", + "https://fd.spiritlhl.top/https://api.github.com/repos/oneclickvirt/ecs/releases/latest", + "https://githubapi.spiritlhl.top/repos/oneclickvirt/ecs/releases/latest", + "https://githubapi.spiritlhl.workers.dev/repos/oneclickvirt/ecs/releases/latest", + } + client := req.C().SetTimeout(3 * time.Second) + for _, url := range urls { + var release GitHubRelease + resp, err := client.R(). + SetSuccessResult(&release). + Get(url) + if err != nil { + continue + } + if resp.IsSuccessState() && release.TagName != "" { + return &release, nil + } + } + return nil, fmt.Errorf("failed to fetch release from all sources") +} + +// 比较程序版本是否需要升级 +func CompareVersions(v1, v2 string) int { + normalize := func(s string) []int { + s = strings.TrimPrefix(strings.ToLower(s), "v") + parts := strings.Split(s, ".") + result := make([]int, 3) + for i := 0; i < 3 && i < len(parts); i++ { + n, _ := strconv.Atoi(parts[i]) + result[i] = n + } + return result + } + a := normalize(v1) + b := normalize(v2) + for i := 0; i < 3; i++ { + if a[i] < b[i] { + return -1 + } else if a[i] > b[i] { + return 1 + } + } + return 0 +}