From 03f2b43590de98293fb9089fea11a4df773af61a Mon Sep 17 00:00:00 2001 From: spiritlhl <103393591+spiritLHLS@users.noreply.github.com> Date: Sat, 29 Jun 2024 05:33:05 +0000 Subject: [PATCH] =?UTF-8?q?v0.0.2=20-=20=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E6=95=B4=E4=B8=AA=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yaml | 2 +- README.md | 59 ++++++++++++++- cmd/main.go | 69 +++++++++++++++++- cmd/main_test.go | 3 +- model/model.go | 6 +- sp/sp.go | 148 ++++++++++++++++++++------------------ spt_install.sh | 110 ++++++++++++++++++++++++++++ 7 files changed, 321 insertions(+), 76 deletions(-) create mode 100644 spt_install.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0c53bbb..47bb19a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,7 +24,7 @@ jobs: run: | git config --global user.name 'github-actions' git config --global user.email 'github-actions@github.com' - TAG="v0.0.1-$(date +'%Y%m%d%H%M%S')" + TAG="v0.0.2-$(date +'%Y%m%d%H%M%S')" git tag $TAG git push origin $TAG env: diff --git a/README.md b/README.md index 5a0a89b..aa3210c 100644 --- a/README.md +++ b/README.md @@ -1 +1,58 @@ -# speedtest \ No newline at end of file +# speedtest + +[![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Foneclickvirt%2Fspeedtest&count_bg=%232EFFF8&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com) + +就近节点测速模块 + +## 说明 + +- [x] 基于[speedtest.net-爬虫](https://github.com/spiritLHLS/speedtest.net-CN-ID)、[speedtest.cn-爬虫](https://github.com/spiritLHLS/speedtest.cn-CN-ID)的数据 +- [x] 基于[speedtest-go](https://github.com/showwin/speedtest-go)二次开发 +- [x] 主体逻辑借鉴了[ecsspeed](https://github.com/spiritLHLS/ecsspeed) + +## 使用 + +下载及安装 + +``` +curl https://raw.githubusercontent.com/oneclickvirt/speedtest/main/spt_install.sh -sSf | bash +``` + +或 + +``` +curl https://cdn.spiritlhl.net/https://raw.githubusercontent.com/oneclickvirt/speedtest/main/spt_install.sh -sSf | bash +``` + +使用 + +``` +spt +``` + +或 + +``` +./spt +``` + +进行测试 + +无环境依赖,理论上适配所有系统和主流架构,更多架构请查看 https://github.com/oneclickvirt/speedtest/releases/tag/output + +``` + +``` + +## 卸载 + +``` +rm -rf /root/spt +rm -rf /usr/bin/spt +``` + +## 在Golang中使用 + +``` +go get github.com/oneclickvirt/speedtest@latest +``` \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index 6aa073c..1870134 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,8 +1,71 @@ package main -import "github.com/oneclickvirt/speedtest/sp" +import ( + "flag" + "fmt" + "net/http" + "strings" + + "github.com/oneclickvirt/speedtest/model" + "github.com/oneclickvirt/speedtest/sp" +) func main() { - sp.NearbySpeedTest("en") - sp.CustomSpeedTest("https://raw.githubusercontent.com/spiritLHLS/speedtest.net-CN-ID/main/CN_Telecom.csv", 2) + go func() { + http.Get("https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Foneclickvirt%2Fspeedtest&count_bg=%2323E01C&title_bg=%23555555&icon=sonarcloud.svg&icon_color=%23E7E7E7&title=hits&edge_flat=false") + }() + fmt.Println("项目地址:", "https://github.com/oneclickvirt/speedtest") + var showVersion, nearByServer bool + var language, operator, platform string + var num int + flag.BoolVar(&showVersion, "v", false, "Show version information") + flag.BoolVar(&nearByServer, "nearby", false, "Test only nearby servers") + flag.StringVar(&language, "l", "zh", "Language parameter (options: en, zh)") + flag.StringVar(&platform, "pf", "net", "Platform parameter (options: net, cn)") + flag.StringVar(&operator, "opt", "", "Operator parameter (options: cmcc, cu, ct, sg, tw, jp, hk, global)") + flag.IntVar(&num, "num", -1, "Number of test servers") + flag.Parse() + if showVersion { + fmt.Println(model.SpeedTestVersion) + return + } + if nearByServer { + sp.NearbySpeedTest(language) + return + } + var url string + if strings.ToLower(platform) == "net" { + if strings.ToLower(operator) == "cmcc" { + url = model.CnCMCC + } else if strings.ToLower(operator) == "cu" { + url = model.CnCU + } else if strings.ToLower(operator) == "ct" { + url = model.CnCT + } else if strings.ToLower(operator) == "hk" { + url = model.CnHK + } else if strings.ToLower(operator) == "tw" { + url = model.CnTW + } else if strings.ToLower(operator) == "jp" { + url = model.CnJP + } else if strings.ToLower(operator) == "sg" { + url = model.CnSG + } + } else if strings.ToLower(platform) == "cn" { + if strings.ToLower(operator) == "cmcc" { + url = model.NetCMCC + } else if strings.ToLower(operator) == "cu" { + url = model.NetCU + } else if strings.ToLower(operator) == "ct" { + url = model.NetCT + } else if strings.ToLower(operator) == "hk" { + url = model.NetHK + } else if strings.ToLower(operator) == "tw" { + url = model.NetTW + } else if strings.ToLower(operator) == "jp" { + url = model.NetJP + } else if strings.ToLower(operator) == "sg" { + url = model.NetSG + } + } + sp.CustomSpeedTest(url, "id", num) } diff --git a/cmd/main_test.go b/cmd/main_test.go index 2e7e1f5..3477ecf 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -4,4 +4,5 @@ import "testing" func Test(t *testing.T) { main() -} // /usr/local/go/bin/go test -timeout 360s -run ^Test$ github.com/oneclickvirt/speedtest/cmd \ No newline at end of file +} +// /usr/local/go/bin/go test -timeout 360s -run ^Test$ github.com/oneclickvirt/speedtest/cmd \ No newline at end of file diff --git a/model/model.go b/model/model.go index 35ff5fa..2aabe81 100644 --- a/model/model.go +++ b/model/model.go @@ -1,5 +1,7 @@ package model +const SpeedTestVersion = "v0.0.2" + var ( NetCMCC = "https://raw.githubusercontent.com/spiritLHLS/speedtest.net-CN-ID/main/CN_Mobile.csv" NetCT = "https://raw.githubusercontent.com/spiritLHLS/speedtest.net-CN-ID/main/CN_Telecom.csv" @@ -11,7 +13,7 @@ var ( NetGlobal = "https://raw.githubusercontent.com/spiritLHLS/speedtest.net-CN-ID/main/ls_sg_hk_jp.csv" CnCMCC = "https://raw.githubusercontent.com/spiritLHLS/speedtest.cn-CN-ID/main/mobile.csv" - CntCT = "https://raw.githubusercontent.com/spiritLHLS/speedtest.cn-CN-ID/main/telecom.csv" + CnCT = "https://raw.githubusercontent.com/spiritLHLS/speedtest.cn-CN-ID/main/telecom.csv" CnCU = "https://raw.githubusercontent.com/spiritLHLS/speedtest.cn-CN-ID/main/unicom.csv" CnHK = "https://raw.githubusercontent.com/spiritLHLS/speedtest.cn-CN-ID/main/HK.csv" CnJP = "https://raw.githubusercontent.com/spiritLHLS/speedtest.cn-CN-ID/main/JP.csv" @@ -25,4 +27,4 @@ var ( "http://cdn3.spiritlhl.net/", "http://cdn2.spiritlhl.net/", } -) \ No newline at end of file +) diff --git a/sp/sp.go b/sp/sp.go index 4bc870d..d6d01b8 100644 --- a/sp/sp.go +++ b/sp/sp.go @@ -21,49 +21,6 @@ func checkError(err error) { } } -func NearbySpeedTest(language string) { - if language == "zh" { - fmt.Printf("%-12s\t %-11s\t %-11s\t %-11s\t %-12s\n", - "位置", "上传速度", "下载速度", "延迟", "丢包率") - } else if language == "en" { - fmt.Printf("%-12s\t %-11s\t %-11s\t %-11s\t %-12s\n", - "Location", "Upload Speed", "Download Speed", "Latency", "PacketLoss") - } - var speedtestClient = speedtest.New() - serverList, _ := speedtestClient.FetchServers() - targets, _ := serverList.FindServer([]int{}) - analyzer := speedtest.NewPacketLossAnalyzer(nil) - var LowestLatency time.Duration - var NearbyServer *speedtest.Server - var PacketLoss string - for _, server := range targets { - server.PingTest(nil) - if LowestLatency == 0 && NearbyServer == nil { - LowestLatency = server.Latency - NearbyServer = server - } else if server.Latency < LowestLatency && NearbyServer != nil { - NearbyServer = server - } - server.Context.Reset() - } - if NearbyServer != nil { - NearbyServer.DownloadTest() - NearbyServer.UploadTest() - err := analyzer.Run(NearbyServer.Host, func(packetLoss *transport.PLoss) { - PacketLoss = strings.ReplaceAll(packetLoss.String(), "Packet Loss: ", "") - }) - checkError(err) - fmt.Printf("%-12s\t %-11s\t %-11s\t %-11s\t %-12s\n", - //NearbyServer.Name, - "Speedtest.net", - fmt.Sprintf("%.2f Mbps", NearbyServer.ULSpeed.Mbps()), - fmt.Sprintf("%.2f Mbps", NearbyServer.DLSpeed.Mbps()), - NearbyServer.Latency, - PacketLoss) - NearbyServer.Context.Reset() - } -} - func getData(endpoint string) string { client := req.C() client.SetTimeout(10 * time.Second) @@ -87,28 +44,28 @@ func getData(endpoint string) string { return "" } -// func parseData(data string) speedtest.Servers { -// var targets speedtest.Servers -// reader := csv.NewReader(strings.NewReader(data)) -// reader.Comma = ',' -// records, err := reader.ReadAll() -// if err != nil { -// log.Fatal(err) -// } -// speedtestClient := speedtest.New() -// for _, record := range records { -// customURL := record[5] + ":" + record[6] -// target, errFetch := speedtestClient.CustomServer(customURL) -// if errFetch != nil { -// continue -// } -// target.Name = record[3] -// targets = append(targets, target) -// } -// return targets -// } +func parseDataFromURL(data string) speedtest.Servers { + var targets speedtest.Servers + reader := csv.NewReader(strings.NewReader(data)) + reader.Comma = ',' + records, err := reader.ReadAll() + if err != nil { + log.Fatal(err) + } + speedtestClient := speedtest.New() + for _, record := range records { + customURL := record[5] + ":" + record[6] + target, errFetch := speedtestClient.CustomServer(customURL) + if errFetch != nil { + continue + } + target.Name = record[3] + targets = append(targets, target) + } + return targets +} -func parseData(data string) speedtest.Servers { +func parseDataFromID(data string) speedtest.Servers { var targets speedtest.Servers reader := csv.NewReader(strings.NewReader(data)) reader.Comma = ',' @@ -129,15 +86,65 @@ func parseData(data string) speedtest.Servers { return targets } -func CustomSpeedTest(url string, num int) { +func NearbySpeedTest(language string) { + if language == "zh" { + fmt.Printf("%-12s\t %-11s\t %-11s\t %-11s\t %-12s\n", + "位置", "上传速度", "下载速度", "延迟", "丢包率") + } else if language == "en" { + fmt.Printf("%-12s\t %-11s\t %-11s\t %-11s\t %-12s\n", + "Location", "Upload Speed", "Download Speed", "Latency", "PacketLoss") + } + var speedtestClient = speedtest.New() + serverList, _ := speedtestClient.FetchServers() + targets, _ := serverList.FindServer([]int{}) + analyzer := speedtest.NewPacketLossAnalyzer(nil) + var LowestLatency time.Duration + var NearbyServer *speedtest.Server + var PacketLoss string + for _, server := range targets { + server.PingTest(nil) + if LowestLatency == 0 && NearbyServer == nil { + LowestLatency = server.Latency + NearbyServer = server + } else if server.Latency < LowestLatency && NearbyServer != nil { + NearbyServer = server + } + server.Context.Reset() + } + if NearbyServer != nil { + NearbyServer.DownloadTest() + NearbyServer.UploadTest() + err := analyzer.Run(NearbyServer.Host, func(packetLoss *transport.PLoss) { + PacketLoss = strings.ReplaceAll(packetLoss.String(), "Packet Loss: ", "") + }) + checkError(err) + fmt.Printf("%-12s\t %-11s\t %-11s\t %-11s\t %-12s\n", + //NearbyServer.Name, + "Speedtest.net", + fmt.Sprintf("%.2f Mbps", NearbyServer.ULSpeed.Mbps()), + fmt.Sprintf("%.2f Mbps", NearbyServer.DLSpeed.Mbps()), + NearbyServer.Latency, + PacketLoss) + NearbyServer.Context.Reset() + } +} + +func CustomSpeedTest(url, byWhat string, num int) { data := getData(url) - targets := parseData(data) + var targets speedtest.Servers + if byWhat == "id" { + targets = parseDataFromID(data) + } else if byWhat == "url" { + targets = parseDataFromURL(data) + } var pingList []time.Duration var err error serverMap := make(map[time.Duration]*speedtest.Server) for _, server := range targets { err = server.PingTest(nil) - checkError(err) + if err != nil { + server.Latency = 1000 * time.Millisecond + } pingList = append(pingList, server.Latency) serverMap[server.Latency] = server server.Context.Reset() @@ -147,6 +154,9 @@ func CustomSpeedTest(url string, num int) { }) analyzer := speedtest.NewPacketLossAnalyzer(nil) var PacketLoss string + if num == -1 && num >= len(pingList) { + num = len(pingList) + } for i := 0; i < num && i < len(pingList); i++ { server := serverMap[pingList[i]] server.DownloadTest() @@ -154,7 +164,9 @@ func CustomSpeedTest(url string, num int) { err = analyzer.Run(server.Host, func(packetLoss *transport.PLoss) { PacketLoss = strings.ReplaceAll(packetLoss.String(), "Packet Loss: ", "") }) - checkError(err) + if err != nil { + PacketLoss = "N/A" + } fmt.Printf("%-12s\t %-11s\t %-11s\t %-11s\t %-12s\n", server.Name, fmt.Sprintf("%.2f Mbps", server.ULSpeed.Mbps()), diff --git a/spt_install.sh b/spt_install.sh new file mode 100644 index 0000000..8f071fa --- /dev/null +++ b/spt_install.sh @@ -0,0 +1,110 @@ +#!/bin/bash +#From https://github.com/oneclickvirt/speedtest +#2024.06.29 + +rm -rf /usr/bin/spt +rm -rf spt +os=$(uname -s) +arch=$(uname -m) + +check_cdn() { + local o_url=$1 + for cdn_url in "${cdn_urls[@]}"; do + if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then + export cdn_success_url="$cdn_url" + return + fi + sleep 0.5 + done + export cdn_success_url="" +} + +check_cdn_file() { + check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" + if [ -n "$cdn_success_url" ]; then + echo "CDN available, using CDN" + else + echo "No CDN available, no use CDN" + fi +} + +cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn3.spiritlhl.net/" "http://cdn1.spiritlhl.net/" "http://cdn2.spiritlhl.net/") +check_cdn_file + +case $os in +Linux) + case $arch in + "x86_64" | "x86" | "amd64" | "x64") + wget -O spt "${cdn_success_url}https://github.com/oneclickvirt/speedtest/releases/download/output/speedtest-linux-amd64" + ;; + "i386" | "i686") + wget -O spt "${cdn_success_url}https://github.com/oneclickvirt/speedtest/releases/download/output/speedtest-linux-386" + ;; + "armv7l" | "armv8" | "armv8l" | "aarch64" | "arm64") + wget -O spt "${cdn_success_url}https://github.com/oneclickvirt/speedtest/releases/download/output/speedtest-linux-arm64" + ;; + *) + echo "Unsupported architecture: $arch" + exit 1 + ;; + esac + ;; +Darwin) + case $arch in + "x86_64" | "x86" | "amd64" | "x64") + wget -O spt "${cdn_success_url}https://github.com/oneclickvirt/speedtest/releases/download/output/speedtest-darwin-amd64" + ;; + "i386" | "i686") + wget -O spt "${cdn_success_url}https://github.com/oneclickvirt/speedtest/releases/download/output/speedtest-darwin-386" + ;; + "armv7l" | "armv8" | "armv8l" | "aarch64" | "arm64") + wget -O spt "${cdn_success_url}https://github.com/oneclickvirt/speedtest/releases/download/output/speedtest-darwin-arm64" + ;; + *) + echo "Unsupported architecture: $arch" + exit 1 + ;; + esac + ;; +FreeBSD) + case $arch in + amd64) + wget -O spt "${cdn_success_url}https://github.com/oneclickvirt/speedtest/releases/download/output/speedtest-freebsd-amd64" + ;; + "i386" | "i686") + wget -O spt "${cdn_success_url}https://github.com/oneclickvirt/speedtest/releases/download/output/speedtest-freebsd-386" + ;; + "armv7l" | "armv8" | "armv8l" | "aarch64" | "arm64") + wget -O spt "${cdn_success_url}https://github.com/oneclickvirt/speedtest/releases/download/output/speedtest-freebsd-arm64" + ;; + *) + echo "Unsupported architecture: $arch" + exit 1 + ;; + esac + ;; +# OpenBSD) +# case $arch in +# amd64) +# wget -O spt https://github.com/oneclickvirt/speedtest/releases/download/output/speedtest-openbsd-amd64 +# ;; +# "i386" | "i686") +# wget -O spt https://github.com/oneclickvirt/speedtest/releases/download/output/speedtest-openbsd-386 +# ;; +# "armv7l" | "armv8" | "armv8l" | "aarch64" | "arm64") +# wget -O spt https://github.com/oneclickvirt/speedtest/releases/download/output/speedtest-openbsd-arm64 +# ;; +# *) +# echo "Unsupported architecture: $arch" +# exit 1 +# ;; +# esac +# ;; +*) + echo "Unsupported operating system: $os" + exit 1 + ;; +esac + +chmod 777 spt +cp spt /usr/bin/spt \ No newline at end of file