Compare commits

...

19 Commits

Author SHA1 Message Date
github-actions[bot]
8674a82eb0 chore: update ECS_VERSION to 0.1.97 in goecs.sh 2025-11-02 17:01:50 +00:00
spiritlhl
5116493338 fix:更新主体版本 2025-11-02 16:56:27 +00:00
spiritlhl
c6f3e7a285 fix: 进一步重构组织架构 2025-11-02 16:54:38 +00:00
spiritlhl
40f63e6cb1 fix:拆分单文件重构组织形式,便于后续维护 2025-11-02 16:37:07 +00:00
spiritlhl
6b42f626b0 feat:设置额外提供的CI参数设置优先级高于选项本身的预设值 2025-11-02 15:41:39 +00:00
spiritlhl
4503fb55b0 fix: 修复PING值测试的过滤条件 2025-11-02 15:19:21 +00:00
spiritlhl
cb1634cb68 fix:统一输出的间隔 2025-11-02 14:31:20 +00:00
spiritlhl
95ece8c28d fix:更新backtrace的go版本,修复cdn检测可用性的漏洞 2025-11-02 14:11:41 +00:00
spiritlhl
fd44c079b3 fix:删除无效头文字输出 2025-11-02 13:24:53 +00:00
spiritlhl
5c5bd37074 fix:修复相关逻辑支持ping值测试改版 2025-11-02 13:22:47 +00:00
spiritlhl
1676045e3d fix: 更新三网ping值测试支持为tgdc和主流网站测试 2025-11-02 12:56:41 +00:00
spiritlhl
58e6941bfa fix:增加控制三网ping值测试单项的参数 2025-11-01 15:02:02 +00:00
github-actions[bot]
3989708c4e chore: update ECS_VERSION to 0.1.93 in goecs.sh 2025-10-31 07:37:11 +00:00
spiritlhl
93fd68bf82 fix:更新版本 2025-10-31 07:23:39 +00:00
spiritlhl
793c44163a fix: 修改已经重命名的字段 2025-10-31 07:22:23 +00:00
spiritlhl
b403d71115 fix:更新版本 2025-10-31 07:15:38 +00:00
spiritlhl
e4e11dd132 fix: 更新nexttrace版本和Golang版本 2025-10-31 07:14:37 +00:00
spiritlhl
1c876f5199 fix: 更新流媒体解锁 2025-10-31 07:09:40 +00:00
github-actions[bot]
fb9ae4d0e0 chore: update ECS_VERSION to 0.1.91 in goecs.sh 2025-10-30 10:49:10 +00:00
30 changed files with 1417 additions and 1014 deletions

View File

@@ -235,7 +235,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.24.5
go-version: 1.25.3
- name: Configure Git for Private Modules
run: |

View File

@@ -283,7 +283,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.24.5
go-version: 1.25.3
- name: Configure Git for Private Modules
run: |

View File

@@ -22,7 +22,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: 1.24.5
go-version: 1.25.3
- name: Configure Git for Private Modules
run: |

View File

@@ -16,7 +16,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.24.5'
go-version: '1.25.3'
- name: Update master branch README files
run: |

3
.gitignore vendored
View File

@@ -1,2 +1,5 @@
vendor/
.idea/
ecs
goecs.txt
*.log

View File

@@ -205,6 +205,8 @@ Usage: goecs [options]
-email
Enable/Disable email port test (default true)
-h Show help information
-help
Show help information
-l string
Set language (supported: en, zh) (default "zh")
-log
@@ -212,26 +214,34 @@ Usage: goecs [options]
-memory
Enable/Disable memory test (default true)
-memorym string
Set memory test method (supported: sysbench, dd, winsat) (default "sysbench")
Set memory test method (supported: stream, sysbench, dd, winsat, auto) (default "stream")
-menu
Enable/Disable menu mode, disable example: -menu=false (default true)
-nt3
Enable/Disable NT3 test (in 'en' language or on windows it always false) (default true)
-nt3loc string
Specify NT3 test location (supported: GZ, SH, BJ, CD for Guangzhou, Shanghai, Beijing, Chengdu) (default "GZ")
Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all) (default "GZ")
-nt3t string
Set NT3 test type (supported: both, ipv4, ipv6) (default "ipv4")
-ping
Enable/Disable ping test
-security
Enable/Disable security test (default true)
-speed
Enable/Disable speed test (default true)
-spnum int
Set the number of servers per operator for speed test (default 2)
-tgdc
Enable/Disable Telegram DC test
-upload
Enable/Disable upload the result (default true)
-ut
Enable/Disable unlock media test (default true)
-v Display version information
-version
Display version information
-web
Enable/Disable popular websites test
```
</details>
@@ -304,7 +314,7 @@ cd ecs
2. 安装 Go 环境(如已安装可跳过)
选择 go 1.24.5 的版本进行安装
选择 go 1.25.3 的版本进行安装
```bash
curl -L https://cdn.spiritlhl.net/https://raw.githubusercontent.com/spiritLHLS/one-click-installation-script/main/install_scripts/go.sh -o go.sh && chmod +x go.sh && bash go.sh

View File

@@ -204,6 +204,8 @@ Usage: goecs [options]
-email
Enable/Disable email port test (default true)
-h Show help information
-help
Show help information
-l string
Set language (supported: en, zh) (default "zh")
-log
@@ -211,26 +213,34 @@ Usage: goecs [options]
-memory
Enable/Disable memory test (default true)
-memorym string
Set memory test method (supported: sysbench, dd, winsat) (default "sysbench")
Set memory test method (supported: stream, sysbench, dd, winsat, auto) (default "stream")
-menu
Enable/Disable menu mode, disable example: -menu=false (default true)
-nt3
Enable/Disable NT3 test (in 'en' language or on windows it always false) (default true)
-nt3loc string
Specify NT3 test location (supported: GZ, SH, BJ, CD for Guangzhou, Shanghai, Beijing, Chengdu) (default "GZ")
Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all) (default "GZ")
-nt3t string
Set NT3 test type (supported: both, ipv4, ipv6) (default "ipv4")
-ping
Enable/Disable ping test
-security
Enable/Disable security test (default true)
-speed
Enable/Disable speed test (default true)
-spnum int
Set the number of servers per operator for speed test (default 2)
-tgdc
Enable/Disable Telegram DC test
-upload
Enable/Disable upload the result (default true)
-ut
Enable/Disable unlock media test (default true)
-v Display version information
-version
Display version information
-web
Enable/Disable popular websites test
```
</details>
@@ -301,7 +311,7 @@ cd ecs
2. Install Go environment (skip if already installed)
Select go 1.24.5 version to install
Select go 1.25.3 version to install
```bash
curl -L https://cdn.spiritlhl.net/https://raw.githubusercontent.com/spiritLHLS/one-click-installation-script/main/install_scripts/go.sh -o go.sh && chmod +x go.sh && bash go.sh

View File

@@ -17,6 +17,7 @@
- [邮件端口检测](#邮件端口检测)
- [上游及回程线路检测](#上游及回程线路检测)
- [三网回程路由检测](#三网回程路由检测)
- [PING值测试](#PING值测试)
- [就近测速](#就近测速)
## English
@@ -27,6 +28,7 @@
- [Streaming Media Unlocking](#Streaming-Media-Unlocking)
- [IP Quality Detection](#IP-Quality-Detection)
- [Email Port Detection](#Email-Port-Detection)
- [PING Testing](#PING-Testing)
- [Nearby Speed Testing](#Nearby-Speed-Testing)
## 日本語
@@ -37,12 +39,15 @@
- [ストリーミングメディアロック解除](#ストリーミングメディアロック解除)
- [IP品質検出](#IP品質検出)
- [メールポート検出](#メールポート検出)
- [PING検出](#PING検出)
- [近隣スピードテスト](#近隣スピードテスト)
---
## 中文
menu模式默认启用执行时显示菜单可选择选项测试在menu模式启用的情况下默认额外提供的CI参数设置优先级高于选项本身的预设值方便用户随时针对某个选项自行修改某些单项测试的参数设置。
### **系统基础信息**
依赖项目:[https://github.com/oneclickvirt/basics](https://github.com/oneclickvirt/basics) [https://github.com/oneclickvirt/gostun](https://github.com/oneclickvirt/gostun)
@@ -407,6 +412,18 @@ Abuser 或 Abuse 的滥用得分会直接影响机器的正常使用(中国境
有时候路由信息完全藏起来了,只知道实际使用的延迟低,实际可能也是优质线路只是查不到信息,这就没办法直接识别了。
### **PING值测试**
依赖项目:[https://github.com/oneclickvirt/pingtest](https://github.com/oneclickvirt/pingtest)
对于选项1如果启用中国模式将仅检测三网全国各省份的PING值延迟从小到大排序。如果不启用中国模式默认将不检测三网全国各省份的PING值延迟仅检测TGDC和主流网站的延迟。
对于选项6和选项10默认都进行测试。
对于中国境内的测试测试TGDC和主流跨国网站的延迟无意义所以默认不测试。
对于参数指定的状态,优先级会高于选项中默认的参数设置。
### **就近测速**
依赖项目:[https://github.com/oneclickvirt/speedtest](https://github.com/oneclickvirt/speedtest)
@@ -423,6 +440,8 @@ Abuser 或 Abuse 的滥用得分会直接影响机器的正常使用(中国境
## English
Menu mode is enabled by default, the menu is displayed to select the option test, in the case of menu mode enabled, the default additional CI parameter setting priority is higher than the preset value of the option itself, which is convenient for the user to modify the parameter settings of some single test for a certain option at any time by themselves.
### Basic System Information
Dependency project: [https://github.com/oneclickvirt/basics](https://github.com/oneclickvirt/basics) [https://github.com/oneclickvirt/gostun](https://github.com/oneclickvirt/gostun)
@@ -733,6 +752,12 @@ Dependency project: [https://github.com/oneclickvirt/portchecker](https://github
If the current host doesn't function as a mail server and doesn't send/receive emails, this project indicator can be ignored.
### PING Testing
Dependency project: [https://github.com/oneclickvirt/pingtest](https://github.com/oneclickvirt/pingtest)
Measure the latency from the current IP address to each TG data center and major websites.
### Nearby Speed Testing
Dependency project: [https://github.com/oneclickvirt/speedtest](https://github.com/oneclickvirt/speedtest)
@@ -747,6 +772,8 @@ In daily use, I prefer to use servers with 1Gbps bandwidth, at least the speed o
## 日本語
メニューモードはデフォルトで有効化されており、実行時にメニューを表示してオプションテストを選択できます。メニューモードが有効な場合、デフォルトで追加提供されるCIパラメータ設定はオプション自体のプリセット値よりも優先度が高く、ユーザーが特定のオプションに対して随時個別のテストパラメータ設定を変更できるようにします。
### システム基本情報
依存プロジェクト:[https://github.com/oneclickvirt/basics](https://github.com/oneclickvirt/basics) [https://github.com/oneclickvirt/gostun](https://github.com/oneclickvirt/gostun)
@@ -1057,6 +1084,12 @@ IPタイプの分類について詳しく説明する必要がある
現在のホストがメール局として機能せず電子メールの送受信を行わない場合この項目指標は無視して構いません
### PING検出
依存プロジェクト[https://github.com/oneclickvirt/pingtest](https://github.com/oneclickvirt/pingtest)
現在のIPアドレスからTGの各データセンターおよび主要ウェブサイトまでの遅延を測定します
### 近隣スピードテスト
依存プロジェクト[https://github.com/oneclickvirt/speedtest](https://github.com/oneclickvirt/speedtest)

View File

@@ -1,11 +0,0 @@
package cputest
import (
"fmt"
"testing"
)
func Test(t *testing.T) {
_, res := CpuTest("zh", "sysbench", "1")
fmt.Print(res)
}

View File

@@ -1,11 +0,0 @@
package disktest
import (
"fmt"
"testing"
)
func TestDiskIoTest(t *testing.T) {
_, res := DiskTest("zh", "sysbench", "", false, false)
fmt.Print(res)
}

46
go.mod
View File

@@ -1,23 +1,23 @@
module github.com/oneclickvirt/ecs
go 1.24.5
go 1.25.3
require (
github.com/imroc/req/v3 v3.54.0
github.com/oneclickvirt/CommonMediaTests v0.0.4-20250329123841
github.com/oneclickvirt/UnlockTests v0.0.29-20251030094944
github.com/oneclickvirt/backtrace v0.0.7-20250811023541
github.com/oneclickvirt/UnlockTests v0.0.30-20251030112700
github.com/oneclickvirt/backtrace v0.0.8-20251102140847
github.com/oneclickvirt/basics v0.0.16-20251030093657
github.com/oneclickvirt/cputest v0.0.12-20250720122317
github.com/oneclickvirt/defaultset v0.0.2-20240624082446
github.com/oneclickvirt/disktest v0.0.10-20250924030424
github.com/oneclickvirt/gostun v0.0.5-20250727155022
github.com/oneclickvirt/memorytest v0.0.10-20250924154648
github.com/oneclickvirt/nt3 v0.0.8-20250811123903
github.com/oneclickvirt/pingtest v0.0.8-20250728015259
github.com/oneclickvirt/nt3 v0.0.9-20251031045617
github.com/oneclickvirt/pingtest v0.0.9-20251102150730
github.com/oneclickvirt/portchecker v0.0.3-20250728015900
github.com/oneclickvirt/security v0.0.7-20251030094114
github.com/oneclickvirt/speedtest v0.0.10-20250728015734
github.com/oneclickvirt/speedtest v0.0.11-20251102151740
)
require (
@@ -57,7 +57,7 @@ require (
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nxtrace/NTrace-core v1.4.2 // indirect
github.com/nxtrace/NTrace-core v1.4.3-rc.1 // indirect
github.com/oneclickvirt/dd v0.0.2-20250808062818 // indirect
github.com/oneclickvirt/fio v0.0.2-20250808045755 // indirect
github.com/oneclickvirt/mbw v0.0.1-20250808061222 // indirect
@@ -77,35 +77,37 @@ require (
github.com/refraction-networking/utls v1.7.3 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rodaine/table v1.3.0 // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/schollz/progressbar/v3 v3.14.4 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
github.com/showwin/speedtest-go v1.7.10 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.14.0 // indirect
github.com/spf13/cast v1.9.2 // indirect
github.com/spf13/pflag v1.0.7 // indirect
github.com/spf13/viper v1.20.1 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/spf13/viper v1.21.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.8.0 // indirect
github.com/tsosunchia/powclient v0.1.5 // indirect
github.com/tsosunchia/powclient v0.2.0 // indirect
github.com/xjasonlyu/windivert-go v0.0.0-20201010013527-4239d0afa76f // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.uber.org/mock v0.5.2 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/term v0.33.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/tools v0.34.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.42.0 // indirect
golang.org/x/mod v0.27.0 // indirect
golang.org/x/net v0.44.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/term v0.35.0 // indirect
golang.org/x/text v0.29.0 // indirect
golang.org/x/tools v0.36.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
howett.net/plist v1.0.0 // indirect
)

97
go.sum
View File

@@ -92,14 +92,14 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nxtrace/NTrace-core v1.4.2 h1:dSRP18Bn3VGf5CZBzKt8gQWW9mDkq62Np9TCF9RAtp0=
github.com/nxtrace/NTrace-core v1.4.2/go.mod h1:wIDOlccuYzY3wBqU89pv2KGHT41i3JA0eRqJU/x9eX4=
github.com/nxtrace/NTrace-core v1.4.3-rc.1 h1:V19tkw3kKAMQOOh7Ibb/jZFBk4kMUfQYmpxxtsOfYWo=
github.com/nxtrace/NTrace-core v1.4.3-rc.1/go.mod h1:lGhfZ916pEUJh+VzWZTYu7bKBo06pAn+/gXb0A/7gGg=
github.com/oneclickvirt/CommonMediaTests v0.0.4-20250329123841 h1:Zef93z9UiZQwRAKnnZYALmpBKvvuVaq34MEsuWwk6nc=
github.com/oneclickvirt/CommonMediaTests v0.0.4-20250329123841/go.mod h1:DAmFPRjFV5p9fEzUUSml5jJGn2f1NZJQCzTxITHDjc4=
github.com/oneclickvirt/UnlockTests v0.0.29-20251030094944 h1:c81MmwD3yO/7kkKN+j88VfBuRQcr3zKp0wGTu1zjUug=
github.com/oneclickvirt/UnlockTests v0.0.29-20251030094944/go.mod h1:oOa6wj/qECtRMxwBO6D7o0L0F0Q/5sQ747OCnFQqoGE=
github.com/oneclickvirt/backtrace v0.0.7-20250811023541 h1:GzkzvUC6U9b6Dkz/Bl4JRPeQ7XBGoW7Qw1aWqzhF+MQ=
github.com/oneclickvirt/backtrace v0.0.7-20250811023541/go.mod h1:/+KUtOWz48TyiTTbhVTsp3D6b5WY+4pCgvFBYtUGtns=
github.com/oneclickvirt/UnlockTests v0.0.30-20251030112700 h1:dtHHyWZdshox8StzrjgImj3Rvqwup5OZJ8FAZZQjPIA=
github.com/oneclickvirt/UnlockTests v0.0.30-20251030112700/go.mod h1:oOa6wj/qECtRMxwBO6D7o0L0F0Q/5sQ747OCnFQqoGE=
github.com/oneclickvirt/backtrace v0.0.8-20251102140847 h1:OiwD06+Mql3pYP8jJFFdP3OUlCrx/qJT7y2YCd/OMiI=
github.com/oneclickvirt/backtrace v0.0.8-20251102140847/go.mod h1:mj9TSow7FNszBb3bQj2Hhm41LwBo7HQP6sgaPtovKdM=
github.com/oneclickvirt/basics v0.0.16-20251030093657 h1:6SWWILNjJfMTXbspqYRpktUEOe/QIVhGonKO8ODC7n4=
github.com/oneclickvirt/basics v0.0.16-20251030093657/go.mod h1:2PV+1ge01zb0Sqzj2V2I7P0wAdFSLF1XgAiumchJJbg=
github.com/oneclickvirt/cputest v0.0.12-20250720122317 h1:toiwAK1hZE5b8klu2mOQ7J4sv5yV9lpPKwgPahfRYBQ=
@@ -118,16 +118,16 @@ github.com/oneclickvirt/mbw v0.0.1-20250808061222 h1:WGXOe6QvHiDRhPVMI0VcctjzW08
github.com/oneclickvirt/mbw v0.0.1-20250808061222/go.mod h1:0Vq6NRpyLmGUdfHfL3uDcFsuZhi7KlG+OCs5ky2757Y=
github.com/oneclickvirt/memorytest v0.0.10-20250924154648 h1:trk6oZ7xs1eVtr+6oIv5IX8LDVtEMG+E6GVzQ810BtU=
github.com/oneclickvirt/memorytest v0.0.10-20250924154648/go.mod h1:4kiHsEWkW9r3/1ZcV5xIweU0smiKP0IRfQj74AUIiVI=
github.com/oneclickvirt/nt3 v0.0.8-20250811123903 h1:ubSPLh/DSrXj+tOgmRABgi2vrVmbmjjSne+NrVFNmNc=
github.com/oneclickvirt/nt3 v0.0.8-20250811123903/go.mod h1:F1v+6xInBKnbUa8gV1M40R1HOzxg+obtduNhx3CTnmA=
github.com/oneclickvirt/pingtest v0.0.8-20250728015259 h1:egoxZRZBOWN3JqBwqEsULDyRo2/dpGMeWcmV3U87zig=
github.com/oneclickvirt/pingtest v0.0.8-20250728015259/go.mod h1:gxwsxxwitNQiGq2OI0ZogYoOLwc8DtuOdSRe6/EvRqs=
github.com/oneclickvirt/nt3 v0.0.9-20251031045617 h1:g42R4kwOL4s6sDsG+axEXJsI82pldv42HLMYCDFypNg=
github.com/oneclickvirt/nt3 v0.0.9-20251031045617/go.mod h1:yo1ufkduFt9QjqG7nqSUf1D3YlQOmFpdlTYniJfclQI=
github.com/oneclickvirt/pingtest v0.0.9-20251102150730 h1:pipqyJUaUI0K5waFvDNNfqbIikMp+9Kg8Dw/kLEUYF8=
github.com/oneclickvirt/pingtest v0.0.9-20251102150730/go.mod h1:gxwsxxwitNQiGq2OI0ZogYoOLwc8DtuOdSRe6/EvRqs=
github.com/oneclickvirt/portchecker v0.0.3-20250728015900 h1:AomzdppSOFB70AJESQhlp0IPbsHTTJGimAWDk2TzCWM=
github.com/oneclickvirt/portchecker v0.0.3-20250728015900/go.mod h1:9sjMDPCd4Z40wkYB0S9gQPGH8YPtnNE1ZJthVIuHUzA=
github.com/oneclickvirt/security v0.0.7-20251030094114 h1:Ax8J1TYqprXyiWNAIJJ3xhoyGhvBlKw4m9j6va5Q2nM=
github.com/oneclickvirt/security v0.0.7-20251030094114/go.mod h1:YfDilPFW22szjdUNgv4VOuSwHnZzsFsdPOfRYiMoc3I=
github.com/oneclickvirt/speedtest v0.0.10-20250728015734 h1:HKO7/JQ74ueXA8Wo8NIvcK9DphbEG/YTfAAVz/akSiY=
github.com/oneclickvirt/speedtest v0.0.10-20250728015734/go.mod h1:0W8vnMbA3iucXLXFdGfe9Ia6RPS0izRO7jvu/SnH1P8=
github.com/oneclickvirt/speedtest v0.0.11-20251102151740 h1:1NUrNt5ay6/xVNC5x62UrQjPqK8jgbKtyjBml/3boZg=
github.com/oneclickvirt/speedtest v0.0.11-20251102151740/go.mod h1:fy0II2Wo7kDWVBKTwcHdodZwyfmJo0g8N9V02EwQDZE=
github.com/oneclickvirt/stream v0.0.2-20250924154001 h1:GuJWdiPkoK84+y/+oHKr2Ghl3c/MzS9Z5m1nM+lMmy4=
github.com/oneclickvirt/stream v0.0.2-20250924154001/go.mod h1:oWaizaHTC2VQciBC9RfaLbAOf8qeR6n20/gY7QxriDE=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
@@ -164,10 +164,10 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rodaine/table v1.3.0 h1:4/3S3SVkHnVZX91EHFvAMV7K42AnJ0XuymRR2C5HlGE=
github.com/rodaine/table v1.3.0/go.mod h1:47zRsHar4zw0jgxGxL9YtFfs7EGN6B/TaS+/Dmk4WxU=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/schollz/progressbar/v3 v3.14.4 h1:W9ZrDSJk7eqmQhd3uxFNNcTr0QL+xuGNI9dEMrw0r74=
github.com/schollz/progressbar/v3 v3.14.4/go.mod h1:aT3UQ7yGm+2ZjeXPqsjTenwL3ddUiuZ0kfQ/2tHlyNI=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
@@ -176,16 +176,16 @@ github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dI
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/showwin/speedtest-go v1.7.10 h1:9o5zb7KsuzZKn+IE2//z5btLKJ870JwO6ETayUkqRFw=
github.com/showwin/speedtest-go v1.7.10/go.mod h1:Ei7OCTmNPdWofMadzcfgq1rUO7mvJy9Jycj//G7vyfA=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -196,8 +196,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
@@ -211,8 +211,10 @@ github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZ
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
github.com/tsosunchia/powclient v0.1.5 h1:hpixFWoPbWSEC0zc9osSltyjtr1+SnhCueZVLkEpyyU=
github.com/tsosunchia/powclient v0.1.5/go.mod h1:yNlzyq+w9llYZV+0q7nrX83ULy4ghq2mCjpTLJFJ2pg=
github.com/tsosunchia/powclient v0.2.0 h1:BDrI3O69CbzarbD+CnnY10Kuwn8xlmtQR0m5tBp+BG8=
github.com/tsosunchia/powclient v0.2.0/go.mod h1:fkb7tTW+HMH3ZWZzQUgwvvFKMj/8Ys+C8Sm/uGQzDA0=
github.com/xjasonlyu/windivert-go v0.0.0-20201010013527-4239d0afa76f h1:glX3VZCYwW1/OmFxOjazfCtBLxXB3YNZk9LF2lYx+Lw=
github.com/xjasonlyu/windivert-go v0.0.0-20201010013527-4239d0afa76f/go.mod h1:gh//RKyt2Gesx3eOj3ulzrSQ60ySj2UA4qnOdrtarvg=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@@ -226,19 +228,21 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -247,17 +251,18 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201008064518-c1f3e3309c71/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -269,8 +274,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -278,23 +283,23 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

854
goecs.go
View File

@@ -1,16 +1,11 @@
package main
import (
"bufio"
"context"
"flag"
"fmt"
"net/http"
"os"
"os/signal"
"regexp"
"runtime"
"strings"
"sync"
"syscall"
"time"
@@ -21,151 +16,25 @@ import (
basicmodel "github.com/oneclickvirt/basics/model"
cputestmodel "github.com/oneclickvirt/cputest/model"
disktestmodel "github.com/oneclickvirt/disktest/disk"
"github.com/oneclickvirt/ecs/cputest"
"github.com/oneclickvirt/ecs/disktest"
"github.com/oneclickvirt/ecs/memorytest"
"github.com/oneclickvirt/ecs/nexttrace"
"github.com/oneclickvirt/ecs/speedtest"
"github.com/oneclickvirt/ecs/unlocktest"
"github.com/oneclickvirt/ecs/upstreams"
menu "github.com/oneclickvirt/ecs/internal/menu"
params "github.com/oneclickvirt/ecs/internal/params"
"github.com/oneclickvirt/ecs/internal/runner"
"github.com/oneclickvirt/ecs/utils"
gostunmodel "github.com/oneclickvirt/gostun/model"
memorytestmodel "github.com/oneclickvirt/memorytest/memory"
nt3model "github.com/oneclickvirt/nt3/model"
ptmodel "github.com/oneclickvirt/pingtest/model"
"github.com/oneclickvirt/pingtest/pt"
"github.com/oneclickvirt/portchecker/email"
speedtestmodel "github.com/oneclickvirt/speedtest/model"
)
var (
ecsVersion = "v0.1.91"
menuMode bool
onlyChinaTest bool
input, choice string
showVersion bool
enableLogger bool
language string
cpuTestMethod, cpuTestThreadMode string
memoryTestMethod string
diskTestMethod, diskTestPath string
diskMultiCheck bool
nt3CheckType, nt3Location string
spNum int
width = 82
basicStatus, cpuTestStatus, memoryTestStatus, diskTestStatus bool
commTestStatus, utTestStatus, securityTestStatus, emailTestStatus bool
backtraceStatus, nt3Status, speedTestStatus, pingTestStatus bool
autoChangeDiskTestMethod = true
filePath = "goecs.txt"
enabelUpload = true
onlyIpInfoCheckStatus, help bool
goecsFlag = flag.NewFlagSet("goecs", flag.ContinueOnError)
finish bool
ecsVersion = "v0.1.97" // 融合怪版本号
configs = params.NewConfig(ecsVersion) // 全局配置实例
userSetFlags = make(map[string]bool) // 用于跟踪哪些参数是用户显式设置的
)
func getMenuChoice(language string) string {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(sigChan)
inputChan := make(chan string, 1)
go func() {
select {
case <-sigChan:
fmt.Println("\n程序在选择过程中被用户中断")
os.Exit(0)
case <-ctx.Done():
return
}
}()
for {
go func() {
var input string
fmt.Print("请输入选项 / Please enter your choice: ")
fmt.Scanln(&input)
input = strings.TrimSpace(input)
input = strings.TrimRight(input, "\n")
select {
case inputChan <- input:
case <-ctx.Done():
return
}
}()
select {
case input := <-inputChan:
re := regexp.MustCompile(`^\d+$`)
if re.MatchString(input) {
inChoice := input
switch inChoice {
case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10":
return inChoice
default:
if language == "zh" {
fmt.Println("无效的选项")
} else {
fmt.Println("Invalid choice")
}
}
} else {
if language == "zh" {
fmt.Println("输入错误,请输入一个纯数字")
} else {
fmt.Println("Invalid input, please enter a number")
}
}
case <-ctx.Done():
return ""
}
}
}
func parseFlags() {
goecsFlag.BoolVar(&help, "h", false, "Show help information")
goecsFlag.BoolVar(&showVersion, "v", false, "Display version information")
goecsFlag.BoolVar(&menuMode, "menu", true, "Enable/Disable menu mode, disable example: -menu=false") // true 默认启用菜单栏模式
goecsFlag.StringVar(&language, "l", "zh", "Set language (supported: en, zh)")
goecsFlag.BoolVar(&basicStatus, "basic", true, "Enable/Disable basic test")
goecsFlag.BoolVar(&cpuTestStatus, "cpu", true, "Enable/Disable CPU test")
goecsFlag.BoolVar(&memoryTestStatus, "memory", true, "Enable/Disable memory test")
goecsFlag.BoolVar(&diskTestStatus, "disk", true, "Enable/Disable disk test")
goecsFlag.BoolVar(&commTestStatus, "comm", true, "Enable/Disable common media test")
goecsFlag.BoolVar(&utTestStatus, "ut", true, "Enable/Disable unlock media test")
goecsFlag.BoolVar(&securityTestStatus, "security", true, "Enable/Disable security test")
goecsFlag.BoolVar(&emailTestStatus, "email", true, "Enable/Disable email port test")
goecsFlag.BoolVar(&backtraceStatus, "backtrace", true, "Enable/Disable backtrace test (in 'en' language or on windows it always false)")
goecsFlag.BoolVar(&nt3Status, "nt3", true, "Enable/Disable NT3 test (in 'en' language or on windows it always false)")
goecsFlag.BoolVar(&speedTestStatus, "speed", true, "Enable/Disable speed test")
goecsFlag.StringVar(&cpuTestMethod, "cpum", "sysbench", "Set CPU test method (supported: sysbench, geekbench, winsat)")
goecsFlag.StringVar(&cpuTestThreadMode, "cput", "multi", "Set CPU test thread mode (supported: single, multi)")
goecsFlag.StringVar(&memoryTestMethod, "memorym", "stream", "Set memory test method (supported: stream, sysbench, dd, winsat, auto)")
goecsFlag.StringVar(&diskTestMethod, "diskm", "fio", "Set disk test method (supported: fio, dd, winsat)")
goecsFlag.StringVar(&diskTestPath, "diskp", "", "Set disk test path, e.g., -diskp /root")
goecsFlag.BoolVar(&diskMultiCheck, "diskmc", false, "Enable/Disable multiple disk checks, e.g., -diskmc=false")
goecsFlag.StringVar(&nt3Location, "nt3loc", "GZ", "Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all)")
goecsFlag.StringVar(&nt3CheckType, "nt3t", "ipv4", "Set NT3 test type (supported: both, ipv4, ipv6)")
goecsFlag.IntVar(&spNum, "spnum", 2, "Set the number of servers per operator for speed test")
goecsFlag.BoolVar(&enableLogger, "log", false, "Enable/Disable logging in the current path")
goecsFlag.BoolVar(&enabelUpload, "upload", true, "Enable/Disable upload the result")
goecsFlag.Parse(os.Args[1:])
}
func handleHelpAndVersion() bool {
if help {
fmt.Printf("Usage: %s [options]\n", os.Args[0])
goecsFlag.PrintDefaults()
return true
}
if showVersion {
fmt.Println(ecsVersion)
return true
}
return false
}
func initLogger() {
if enableLogger {
if configs.EnableLogger {
gostunmodel.EnableLoger = true
basicmodel.EnableLoger = true
cputestmodel.EnableLoger = true
@@ -180,694 +49,19 @@ func initLogger() {
}
}
func handleMenuMode(preCheck utils.NetCheckResult) {
basicStatus, cpuTestStatus, memoryTestStatus, diskTestStatus = false, false, false, false
commTestStatus, utTestStatus, securityTestStatus, emailTestStatus = false, false, false, false
backtraceStatus, nt3Status, speedTestStatus = false, false, false
autoChangeDiskTestMethod = true
printMenuOptions(preCheck)
Loop:
for {
choice = getMenuChoice(language)
switch choice {
case "0":
os.Exit(0)
case "1":
setFullTestStatus(preCheck)
onlyChinaTest = utils.CheckChina(enableLogger)
break Loop
case "2":
setMinimalTestStatus(preCheck)
break Loop
case "3":
setStandardTestStatus(preCheck)
break Loop
case "4":
setNetworkFocusedTestStatus(preCheck)
break Loop
case "5":
setUnlockFocusedTestStatus(preCheck)
break Loop
case "6":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
setNetworkOnlyTestStatus()
break Loop
case "7":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
setUnlockOnlyTestStatus()
break Loop
case "8":
setHardwareOnlyTestStatus(preCheck)
break Loop
case "9":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
setIPQualityTestStatus()
break Loop
case "10":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
nt3Location = "ALL"
setRouteTestStatus()
break Loop
default:
printInvalidChoice()
}
}
}
func printMenuOptions(preCheck utils.NetCheckResult) {
var stats *utils.StatsResponse
var statsErr error
var githubInfo *utils.GitHubRelease
var githubErr error
// 只有在网络连接正常时才获取统计信息和版本信息
if preCheck.Connected {
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()
} else {
statsErr = fmt.Errorf("network not connected")
githubErr = fmt.Errorf("network not connected")
}
var statsInfo string
var cmp int
if preCheck.Connected {
// 网络连接正常时处理统计信息和版本比较
if statsErr != nil {
statsInfo = "NULL"
} else {
switch language {
case "zh":
statsInfo = fmt.Sprintf("总使用量: %s | 今日使用: %s",
utils.FormatGoecsNumber(stats.Total),
utils.FormatGoecsNumber(stats.Daily))
case "en":
statsInfo = fmt.Sprintf("Total Usage: %s | Daily Usage: %s",
utils.FormatGoecsNumber(stats.Total),
utils.FormatGoecsNumber(stats.Daily))
}
}
if githubErr == nil {
cmp = utils.CompareVersions(ecsVersion, githubInfo.TagName)
} else {
cmp = 0
}
}
switch language {
case "zh":
fmt.Printf("VPS融合怪版本: %s\n", ecsVersion)
if preCheck.Connected {
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个)")
fmt.Println("4. 精简网络版(系统信息+CPU+内存+磁盘+回程+路由+测速节点5个)")
fmt.Println("5. 精简解锁版(系统信息+CPU+内存+磁盘IO+御三家+常用流媒体+测速节点5个)")
fmt.Println("6. 网络单项(IP质量检测+上游及三网回程+广州三网回程详细路由+全国延迟+测速节点11个)")
fmt.Println("7. 解锁单项(御三家解锁+常用流媒体解锁)")
fmt.Println("8. 硬件单项(系统信息+CPU+dd磁盘测试+fio磁盘测试)")
fmt.Println("9. IP质量检测(15个数据库的IP质量检测+邮件端口检测)")
fmt.Println("10. 三网回程线路检测+三网回程详细路由(北京上海广州成都)+三网延迟测试(全国)")
fmt.Println("0. 退出程序")
case "en":
fmt.Printf("VPS Fusion Monster Test Version: %s\n", ecsVersion)
if preCheck.Connected {
switch cmp {
case -1:
fmt.Printf("New version detected %s update if necessary!\n", githubInfo.TagName)
}
fmt.Printf("%s\n", statsInfo)
}
fmt.Println("1. VPS Fusion Monster Test (Full Test)")
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)")
fmt.Println("4. Network-Focused Test Suite (System Info + CPU + Memory + Disk + 5 Speed Test Nodes)")
fmt.Println("5. Unlock-Focused Test Suite (System Info + CPU + Memory + Disk IO + Basic Unlock Tests + Common Streaming Services + 5 Speed Test Nodes)")
fmt.Println("6. Network-Only Test (IP Quality Test + 5 Speed Test Nodes)")
fmt.Println("7. Unlock-Only Test (Basic Unlock Tests + Common Streaming Services Unlock)")
fmt.Println("8. Hardware-Only Test (System Info + CPU + Memory + dd Disk Test + fio Disk Test)")
fmt.Println("9. IP Quality Test (IP Test with 15 Databases + Email Port Test)")
fmt.Println("0. Exit Program")
}
}
func setFullTestStatus(preCheck utils.NetCheckResult) {
basicStatus = true
cpuTestStatus = true
memoryTestStatus = true
diskTestStatus = true
if preCheck.Connected {
commTestStatus = true
utTestStatus = true
securityTestStatus = true
emailTestStatus = true
backtraceStatus = true
nt3Status = true
speedTestStatus = true
}
}
func setMinimalTestStatus(preCheck utils.NetCheckResult) {
basicStatus = true
cpuTestStatus = true
memoryTestStatus = true
diskTestStatus = true
if preCheck.Connected {
speedTestStatus = true
}
}
func setStandardTestStatus(preCheck utils.NetCheckResult) {
basicStatus = true
cpuTestStatus = true
memoryTestStatus = true
diskTestStatus = true
if preCheck.Connected {
utTestStatus = true
nt3Status = true
speedTestStatus = true
}
}
func setNetworkFocusedTestStatus(preCheck utils.NetCheckResult) {
basicStatus = true
cpuTestStatus = true
memoryTestStatus = true
diskTestStatus = true
if preCheck.Connected {
backtraceStatus = true
nt3Status = true
speedTestStatus = true
}
}
func setUnlockFocusedTestStatus(preCheck utils.NetCheckResult) {
basicStatus = true
cpuTestStatus = true
memoryTestStatus = true
diskTestStatus = true
if preCheck.Connected {
commTestStatus = true
utTestStatus = true
speedTestStatus = true
}
}
func setNetworkOnlyTestStatus() {
onlyIpInfoCheckStatus = true
securityTestStatus = true
speedTestStatus = true
backtraceStatus = true
nt3Status = true
pingTestStatus = true
}
func setUnlockOnlyTestStatus() {
onlyIpInfoCheckStatus = true
commTestStatus = true
utTestStatus = true
}
func setHardwareOnlyTestStatus(preCheck utils.NetCheckResult) {
_ = preCheck
basicStatus = true
cpuTestStatus = true
memoryTestStatus = true
diskTestStatus = true
securityTestStatus = false
autoChangeDiskTestMethod = false
}
func setIPQualityTestStatus() {
onlyIpInfoCheckStatus = true
securityTestStatus = true
emailTestStatus = true
}
func setRouteTestStatus() {
onlyIpInfoCheckStatus = true
backtraceStatus = true
nt3Status = true
pingTestStatus = true
}
func printInvalidChoice() {
if language == "zh" {
fmt.Println("无效的选项")
} else {
fmt.Println("Invalid choice")
}
}
func handleLanguageSpecificSettings() {
if language == "en" {
backtraceStatus = false
nt3Status = false
}
if !enabelUpload {
securityTestStatus = false
}
}
func handleSignalInterrupt(sig chan os.Signal, startTime *time.Time, output *string, _ string, uploadDone chan bool, outputMutex *sync.Mutex) {
select {
case <-sig:
if !finish {
endTime := time.Now()
duration := endTime.Sub(*startTime)
minutes := int(duration.Minutes())
seconds := int(duration.Seconds()) % 60
currentTime := time.Now().Format("Mon Jan 2 15:04:05 MST 2006")
outputMutex.Lock()
timeInfo := utils.PrintAndCapture(func() {
utils.PrintCenteredTitle("", width)
if language == "zh" {
fmt.Printf("花费 : %d 分 %d 秒\n", minutes, seconds)
fmt.Printf("时间 : %s\n", currentTime)
} else {
fmt.Printf("Cost Time : %d min %d sec\n", minutes, seconds)
fmt.Printf("Current Time : %s\n", currentTime)
}
utils.PrintCenteredTitle("", width)
}, "", "")
*output += timeInfo
finalOutput := *output
outputMutex.Unlock()
resultChan := make(chan struct {
httpURL string
httpsURL string
}, 1)
if enabelUpload {
go func() {
httpURL, httpsURL := utils.ProcessAndUpload(finalOutput, filePath, enabelUpload)
resultChan <- struct {
httpURL string
httpsURL string
}{httpURL, httpsURL}
uploadDone <- true
}()
select {
case result := <-resultChan:
if result.httpURL != "" || result.httpsURL != "" {
if language == "en" {
fmt.Printf("Upload successfully!\nHttp URL: %s\nHttps URL: %s\n", result.httpURL, result.httpsURL)
} else {
fmt.Printf("上传成功!\nHttp URL: %s\nHttps URL: %s\n", result.httpURL, result.httpsURL)
}
}
time.Sleep(100 * time.Millisecond)
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(0)
case <-time.After(30 * time.Second):
if language == "en" {
fmt.Println("Upload timeout, program exit")
} else {
fmt.Println("上传超时,程序退出")
}
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(1)
}
} else {
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(0)
}
}
os.Exit(0)
}
}
func runChineseTests(preCheck utils.NetCheckResult, wg1, wg2, wg3 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo *string, output *string, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) {
*output = runBasicTests(preCheck, basicInfo, securityInfo, *output, tempOutput, outputMutex)
*output = runCPUTest(*output, tempOutput, outputMutex)
*output = runMemoryTest(*output, tempOutput, outputMutex)
*output = runDiskTest(*output, tempOutput, outputMutex)
if onlyIpInfoCheckStatus && !basicStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = runIpInfoCheck(*output, tempOutput, outputMutex)
}
if utTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" && !onlyChinaTest {
wg1.Add(1)
go func() {
defer wg1.Done()
*mediaInfo = unlocktest.MediaTest(language)
}()
}
if emailTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
wg2.Add(1)
go func() {
defer wg2.Done()
*emailInfo = email.EmailCheck()
}()
}
if (onlyChinaTest || pingTestStatus) && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
wg3.Add(1)
go func() {
defer wg3.Done()
*ptInfo = pt.PingTest()
}()
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = runStreamingTests(wg1, mediaInfo, *output, tempOutput, outputMutex)
*output = runSecurityTests(*securityInfo, *output, tempOutput, outputMutex)
*output = runEmailTests(wg2, emailInfo, *output, tempOutput, outputMutex)
}
if runtime.GOOS != "windows" && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = runNetworkTests(wg3, ptInfo, *output, tempOutput, outputMutex)
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = runSpeedTests(*output, tempOutput, outputMutex)
}
*output = appendTimeInfo(*output, tempOutput, startTime, outputMutex)
}
func runEnglishTests(preCheck utils.NetCheckResult, wg1, wg2 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo *string, output *string, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) {
*output = runBasicTests(preCheck, basicInfo, securityInfo, *output, tempOutput, outputMutex)
*output = runCPUTest(*output, tempOutput, outputMutex)
*output = runMemoryTest(*output, tempOutput, outputMutex)
*output = runDiskTest(*output, tempOutput, outputMutex)
if onlyIpInfoCheckStatus && !basicStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = runIpInfoCheck(*output, tempOutput, outputMutex)
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
if utTestStatus {
wg1.Add(1)
go func() {
defer wg1.Done()
*mediaInfo = unlocktest.MediaTest(language)
}()
}
if emailTestStatus {
wg2.Add(1)
go func() {
defer wg2.Done()
*emailInfo = email.EmailCheck()
}()
}
*output = runStreamingTests(wg1, mediaInfo, *output, tempOutput, outputMutex)
*output = runSecurityTests(*securityInfo, *output, tempOutput, outputMutex)
*output = runEmailTests(wg2, emailInfo, *output, tempOutput, outputMutex)
*output = runEnglishSpeedTests(*output, tempOutput, outputMutex)
}
*output = appendTimeInfo(*output, tempOutput, startTime, outputMutex)
}
// runIpInfoCheck 系统和网络基础信息检测不进行测试的时候该函数检测取得本机IP信息并显示(单项测试中输出)
func runIpInfoCheck(output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
var ipinfo string
upstreams.IPV4, upstreams.IPV6, ipinfo = utils.OnlyBasicsIpInfo(language)
if ipinfo != "" {
if language == "zh" {
utils.PrintCenteredTitle("IP信息", width)
} else {
utils.PrintCenteredTitle("IP-Information", width)
}
fmt.Printf("%s", ipinfo)
}
}, tempOutput, output)
}
func runBasicTests(preCheck utils.NetCheckResult, basicInfo, securityInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
utils.PrintHead(language, width, ecsVersion)
if basicStatus || securityTestStatus {
if basicStatus {
if language == "zh" {
utils.PrintCenteredTitle("系统基础信息", width)
} else {
utils.PrintCenteredTitle("System-Basic-Information", width)
}
}
if preCheck.Connected && preCheck.StackType == "DualStack" {
upstreams.IPV4, upstreams.IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, nt3CheckType, securityTestStatus)
} else if preCheck.Connected && preCheck.StackType == "IPv4" {
upstreams.IPV4, upstreams.IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "ipv4", securityTestStatus)
} else if preCheck.Connected && preCheck.StackType == "IPv6" {
upstreams.IPV4, upstreams.IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "ipv6", securityTestStatus)
} else {
upstreams.IPV4, upstreams.IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "", false)
securityTestStatus = false
}
if basicStatus {
fmt.Printf("%s", *basicInfo)
} else if (input == "6" || input == "9") && securityTestStatus {
scanner := bufio.NewScanner(strings.NewReader(*basicInfo))
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "IPV") {
fmt.Println(line)
}
}
}
}
}, tempOutput, output)
}
func runCPUTest(output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if cpuTestStatus {
realTestMethod, res := cputest.CpuTest(language, cpuTestMethod, cpuTestThreadMode)
if language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("CPU测试-通过%s测试", realTestMethod), width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("CPU-Test--%s-Method", realTestMethod), width)
}
fmt.Print(res)
}
}, tempOutput, output)
}
func runMemoryTest(output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if memoryTestStatus {
realTestMethod, res := memorytest.MemoryTest(language, memoryTestMethod)
if language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("内存测试-通过%s测试", realTestMethod), width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Memory-Test--%s-Method", realTestMethod), width)
}
fmt.Print(res)
}
}, tempOutput, output)
}
func runDiskTest(output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if diskTestStatus && autoChangeDiskTestMethod {
realTestMethod, res := disktest.DiskTest(language, diskTestMethod, diskTestPath, diskMultiCheck, autoChangeDiskTestMethod)
if language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", realTestMethod), width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", realTestMethod), width)
}
fmt.Print(res)
} else if diskTestStatus && !autoChangeDiskTestMethod {
if language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", "dd"), width)
_, res := disktest.DiskTest(language, "dd", diskTestPath, diskMultiCheck, autoChangeDiskTestMethod)
fmt.Print(res)
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", "fio"), width)
_, res = disktest.DiskTest(language, "fio", diskTestPath, diskMultiCheck, autoChangeDiskTestMethod)
fmt.Print(res)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", "dd"), width)
_, res := disktest.DiskTest(language, "dd", diskTestPath, diskMultiCheck, autoChangeDiskTestMethod)
fmt.Print(res)
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", "fio"), width)
_, res = disktest.DiskTest(language, "fio", diskTestPath, diskMultiCheck, autoChangeDiskTestMethod)
fmt.Print(res)
}
}
}, tempOutput, output)
}
func runStreamingTests(wg1 *sync.WaitGroup, mediaInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if language == "zh" {
if commTestStatus && !onlyChinaTest {
utils.PrintCenteredTitle("御三家流媒体解锁", width)
fmt.Printf("%s", commediatests.MediaTests(language))
}
}
if utTestStatus && (language == "zh" && !onlyChinaTest || language == "en") {
wg1.Wait()
if language == "zh" {
utils.PrintCenteredTitle("跨国流媒体解锁", width)
} else {
utils.PrintCenteredTitle("Cross-Border-Streaming-Media-Unlock", width)
}
fmt.Printf("%s", *mediaInfo)
}
}, tempOutput, output)
}
func runSecurityTests(securityInfo, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if securityTestStatus {
if language == "zh" {
utils.PrintCenteredTitle("IP质量检测", width)
} else {
utils.PrintCenteredTitle("IP-Quality-Check", width)
}
fmt.Printf("%s", securityInfo)
}
}, tempOutput, output)
}
func runEmailTests(wg2 *sync.WaitGroup, emailInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if emailTestStatus {
wg2.Wait()
if language == "zh" {
utils.PrintCenteredTitle("邮件端口检测", width)
} else {
utils.PrintCenteredTitle("Email-Port-Check", width)
}
fmt.Println(*emailInfo)
}
}, tempOutput, output)
}
func runNetworkTests(wg3 *sync.WaitGroup, ptInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if backtraceStatus && !onlyChinaTest {
utils.PrintCenteredTitle("上游及回程线路检测", width)
upstreams.UpstreamsCheck() // 不能在重定向的同时外部并发,此处仅可以顺序执行
}
if nt3Status && !onlyChinaTest {
utils.PrintCenteredTitle("三网回程路由检测", width)
nexttrace.NextTrace3Check(language, nt3Location, nt3CheckType) // 不能在重定向的同时外部并发,此处仅可以顺序执行
}
if (onlyChinaTest || pingTestStatus) && *ptInfo != "" {
wg3.Wait()
utils.PrintCenteredTitle("三网ICMP的PING值检测", width)
fmt.Println(*ptInfo)
}
}, tempOutput, output)
}
func runSpeedTests(output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if speedTestStatus {
utils.PrintCenteredTitle("就近节点测速", width)
speedtest.ShowHead(language)
if choice == "1" || !menuMode {
speedtest.NearbySP()
speedtest.CustomSP("net", "global", 2, language)
speedtest.CustomSP("net", "cu", spNum, language)
speedtest.CustomSP("net", "ct", spNum, language)
speedtest.CustomSP("net", "cmcc", spNum, language)
} else if choice == "2" || choice == "3" || choice == "4" || choice == "5" {
speedtest.CustomSP("net", "global", 4, language)
} else if choice == "6" {
speedtest.CustomSP("net", "global", 11, language)
}
}
}, tempOutput, output)
}
func runEnglishSpeedTests(output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if speedTestStatus {
utils.PrintCenteredTitle("Speed-Test", width)
speedtest.ShowHead(language)
speedtest.NearbySP()
speedtest.CustomSP("net", "global", -1, language)
}
}, tempOutput, output)
}
func appendTimeInfo(output, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
endTime := time.Now()
duration := endTime.Sub(startTime)
minutes := int(duration.Minutes())
seconds := int(duration.Seconds()) % 60
currentTime := time.Now().Format("Mon Jan 2 15:04:05 MST 2006")
return utils.PrintAndCapture(func() {
utils.PrintCenteredTitle("", width)
if language == "zh" {
fmt.Printf("花费 : %d 分 %d 秒\n", minutes, seconds)
fmt.Printf("时间 : %s\n", currentTime)
} else {
fmt.Printf("Cost Time : %d min %d sec\n", minutes, seconds)
fmt.Printf("Current Time : %s\n", currentTime)
}
utils.PrintCenteredTitle("", width)
}, tempOutput, output)
}
func handleUploadResults(output string) {
httpURL, httpsURL := utils.ProcessAndUpload(output, filePath, enabelUpload)
if httpURL != "" || httpsURL != "" {
if language == "en" {
fmt.Printf("Upload successfully!\nHttp URL: %s\nHttps URL: %s\n", httpURL, httpsURL)
fmt.Println("Each Test Benchmark: https://bash.spiritlhl.net/ecsguide")
} else {
fmt.Printf("上传成功!\nHttp URL: %s\nHttps URL: %s\n", httpURL, httpsURL)
fmt.Println("每项测试基准见: https://bash.spiritlhl.net/ecsguide")
if configs.Language == "en" {
configs.BacktraceStatus = false
configs.Nt3Status = false
}
if !configs.EnableUpload {
configs.SecurityTestStatus = false
}
}
func main() {
parseFlags()
if handleHelpAndVersion() {
configs.ParseFlags(os.Args[1:])
if configs.HandleHelpAndVersion("goecs") {
return
}
initLogger()
@@ -877,14 +71,14 @@ func main() {
http.Get("https://hits.spiritlhl.net/goecs.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false")
}
}()
if menuMode {
handleMenuMode(preCheck)
if configs.MenuMode {
menu.HandleMenuMode(preCheck, configs)
} else {
onlyIpInfoCheckStatus = true
configs.OnlyIpInfoCheck = true
}
handleLanguageSpecificSettings()
if !preCheck.Connected {
enabelUpload = false
configs.EnableUpload = false
}
var (
wg1, wg2, wg3 sync.WaitGroup
@@ -896,19 +90,19 @@ func main() {
uploadDone := make(chan bool, 1)
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
go handleSignalInterrupt(sig, &startTime, &output, tempOutput, uploadDone, &outputMutex)
switch language {
go runner.HandleSignalInterrupt(sig, configs, &startTime, &output, tempOutput, uploadDone, &outputMutex)
switch configs.Language {
case "zh":
runChineseTests(preCheck, &wg1, &wg2, &wg3, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo, &output, tempOutput, startTime, &outputMutex)
runner.RunChineseTests(preCheck, configs, &wg1, &wg2, &wg3, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo, &output, tempOutput, startTime, &outputMutex)
case "en":
runEnglishTests(preCheck, &wg1, &wg2, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &output, tempOutput, startTime, &outputMutex)
runner.RunEnglishTests(preCheck, configs, &wg1, &wg2, &wg3, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo, &output, tempOutput, startTime, &outputMutex)
default:
fmt.Println("Unsupported language")
}
if preCheck.Connected {
handleUploadResults(output)
runner.HandleUploadResults(configs, output)
}
finish = true
configs.Finish = true
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()

View File

@@ -152,7 +152,7 @@ goecs_check() {
os=$(uname -s 2>/dev/null || echo "Unknown")
arch=$(uname -m 2>/dev/null || echo "Unknown")
check_china
ECS_VERSION="0.1.90"
ECS_VERSION="0.1.97"
for api in \
"https://api.github.com/repos/oneclickvirt/ecs/releases/latest" \
"https://githubapi.spiritlhl.workers.dev/repos/oneclickvirt/ecs/releases/latest" \
@@ -164,8 +164,8 @@ goecs_check() {
sleep 1
done
if [ -z "$ECS_VERSION" ]; then
_yellow "Unable to get version info, using default version 0.1.90"
ECS_VERSION="0.1.90"
_yellow "Unable to get version info, using default version 0.1.97"
ECS_VERSION="0.1.97"
fi
version_output=""
for cmd_path in "goecs" "./goecs" "/usr/bin/goecs" "/usr/local/bin/goecs"; do

365
internal/menu/menu.go Normal file
View File

@@ -0,0 +1,365 @@
package menu
import (
"context"
"fmt"
"os"
"os/signal"
"regexp"
"strings"
"sync"
"syscall"
"github.com/oneclickvirt/ecs/internal/params"
"github.com/oneclickvirt/ecs/utils"
)
// GetMenuChoice prompts user for menu choice
func GetMenuChoice(language string) string {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(sigChan)
inputChan := make(chan string, 1)
go func() {
select {
case <-sigChan:
fmt.Println("\n程序在选择过程中被用户中断")
os.Exit(0)
case <-ctx.Done():
return
}
}()
for {
go func() {
var input string
fmt.Print("请输入选项 / Please enter your choice: ")
fmt.Scanln(&input)
input = strings.TrimSpace(input)
input = strings.TrimRight(input, "\n")
select {
case inputChan <- input:
case <-ctx.Done():
return
}
}()
select {
case input := <-inputChan:
re := regexp.MustCompile(`^\d+$`)
if re.MatchString(input) {
inChoice := input
switch inChoice {
case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10":
return inChoice
default:
if language == "zh" {
fmt.Println("无效的选项")
} else {
fmt.Println("Invalid choice")
}
}
} else {
if language == "zh" {
fmt.Println("输入错误,请输入一个纯数字")
} else {
fmt.Println("Invalid input, please enter a number")
}
}
case <-ctx.Done():
return ""
}
}
}
// PrintMenuOptions displays menu options
func PrintMenuOptions(preCheck utils.NetCheckResult, config *params.Config) {
var stats *utils.StatsResponse
var statsErr error
var githubInfo *utils.GitHubRelease
var githubErr error
if preCheck.Connected {
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()
} else {
statsErr = fmt.Errorf("network not connected")
githubErr = fmt.Errorf("network not connected")
}
var statsInfo string
var cmp int
if preCheck.Connected {
if statsErr != nil {
statsInfo = "NULL"
} else {
switch config.Language {
case "zh":
statsInfo = fmt.Sprintf("总使用量: %s | 今日使用: %s",
utils.FormatGoecsNumber(stats.Total),
utils.FormatGoecsNumber(stats.Daily))
case "en":
statsInfo = fmt.Sprintf("Total Usage: %s | Daily Usage: %s",
utils.FormatGoecsNumber(stats.Total),
utils.FormatGoecsNumber(stats.Daily))
}
}
if githubErr == nil {
cmp = utils.CompareVersions(config.EcsVersion, githubInfo.TagName)
} else {
cmp = 0
}
}
switch config.Language {
case "zh":
fmt.Printf("VPS融合怪版本: %s\n", config.EcsVersion)
if preCheck.Connected {
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个)")
fmt.Println("4. 精简网络版(系统信息+CPU+内存+磁盘+回程+路由+测速节点5个)")
fmt.Println("5. 精简解锁版(系统信息+CPU+内存+磁盘IO+御三家+常用流媒体+测速节点5个)")
fmt.Println("6. 网络单项(IP质量检测+上游及三网回程+广州三网回程详细路由+全国延迟+TGDC+网站延迟+测速节点11个)")
fmt.Println("7. 解锁单项(御三家解锁+常用流媒体解锁)")
fmt.Println("8. 硬件单项(系统信息+CPU+dd磁盘测试+fio磁盘测试)")
fmt.Println("9. IP质量检测(15个数据库的IP质量检测+邮件端口检测)")
fmt.Println("10. 三网回程线路检测+三网回程详细路由(北京上海广州成都)+全国延迟+TGDC+网站延迟")
fmt.Println("0. 退出程序")
case "en":
fmt.Printf("VPS Fusion Monster Test Version: %s\n", config.EcsVersion)
if preCheck.Connected {
switch cmp {
case -1:
fmt.Printf("New version detected %s update if necessary!\n", githubInfo.TagName)
}
fmt.Printf("%s\n", statsInfo)
}
fmt.Println("1. VPS Fusion Monster Test (Full Test)")
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)")
fmt.Println("4. Network-Focused Test Suite (System Info + CPU + Memory + Disk + 5 Speed Test Nodes)")
fmt.Println("5. Unlock-Focused Test Suite (System Info + CPU + Memory + Disk IO + Basic Unlock Tests + Common Streaming Services + 5 Speed Test Nodes)")
fmt.Println("6. Network-Only Test (IP Quality Test + TGDC + Websites + 11 Speed Test Nodes)")
fmt.Println("7. Unlock-Only Test (Basic Unlock Tests + Common Streaming Services Unlock)")
fmt.Println("8. Hardware-Only Test (System Info + CPU + Memory + dd Disk Test + fio Disk Test)")
fmt.Println("9. IP Quality Test (IP Test with 15 Databases + Email Port Test)")
fmt.Println("0. Exit Program")
}
}
// HandleMenuMode handles menu selection
func HandleMenuMode(preCheck utils.NetCheckResult, config *params.Config) {
savedParams := config.SaveUserSetParams()
config.BasicStatus = false
config.CpuTestStatus = false
config.MemoryTestStatus = false
config.DiskTestStatus = false
config.CommTestStatus = false
config.UtTestStatus = false
config.SecurityTestStatus = false
config.EmailTestStatus = false
config.BacktraceStatus = false
config.Nt3Status = false
config.SpeedTestStatus = false
config.TgdcTestStatus = false
config.WebTestStatus = false
config.AutoChangeDiskMethod = true
PrintMenuOptions(preCheck, config)
Loop:
for {
config.Choice = GetMenuChoice(config.Language)
switch config.Choice {
case "0":
os.Exit(0)
case "1":
SetFullTestStatus(preCheck, config)
config.OnlyChinaTest = utils.CheckChina(config.EnableLogger)
break Loop
case "2":
SetMinimalTestStatus(preCheck, config)
break Loop
case "3":
SetStandardTestStatus(preCheck, config)
break Loop
case "4":
SetNetworkFocusedTestStatus(preCheck, config)
break Loop
case "5":
SetUnlockFocusedTestStatus(preCheck, config)
break Loop
case "6":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
SetNetworkOnlyTestStatus(config)
break Loop
case "7":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
SetUnlockOnlyTestStatus(config)
break Loop
case "8":
SetHardwareOnlyTestStatus(preCheck, config)
break Loop
case "9":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
SetIPQualityTestStatus(config)
break Loop
case "10":
if !preCheck.Connected {
fmt.Println("Can not test without network connection!")
return
}
config.Nt3Location = "ALL"
SetRouteTestStatus(config)
break Loop
default:
PrintInvalidChoice(config.Language)
}
}
config.RestoreUserSetParams(savedParams)
}
// SetFullTestStatus enables all tests
func SetFullTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.CommTestStatus = true
config.UtTestStatus = true
config.SecurityTestStatus = true
config.EmailTestStatus = true
config.BacktraceStatus = true
config.Nt3Status = true
config.SpeedTestStatus = true
config.TgdcTestStatus = true
config.WebTestStatus = true
}
}
// SetMinimalTestStatus sets minimal test configuration
func SetMinimalTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.SpeedTestStatus = true
}
}
// SetStandardTestStatus sets standard test configuration
func SetStandardTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.UtTestStatus = true
config.Nt3Status = true
config.SpeedTestStatus = true
}
}
// SetNetworkFocusedTestStatus sets network-focused test configuration
func SetNetworkFocusedTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.BacktraceStatus = true
config.Nt3Status = true
config.SpeedTestStatus = true
}
}
// SetUnlockFocusedTestStatus sets unlock-focused test configuration
func SetUnlockFocusedTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.CommTestStatus = true
config.UtTestStatus = true
config.SpeedTestStatus = true
}
}
// SetNetworkOnlyTestStatus sets network-only test configuration
func SetNetworkOnlyTestStatus(config *params.Config) {
config.OnlyIpInfoCheck = true
config.SecurityTestStatus = true
config.SpeedTestStatus = true
config.BacktraceStatus = true
config.Nt3Status = true
config.PingTestStatus = true
config.TgdcTestStatus = true
config.WebTestStatus = true
}
// SetUnlockOnlyTestStatus sets unlock-only test configuration
func SetUnlockOnlyTestStatus(config *params.Config) {
config.OnlyIpInfoCheck = true
config.CommTestStatus = true
config.UtTestStatus = true
}
// SetHardwareOnlyTestStatus sets hardware-only test configuration
func SetHardwareOnlyTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
_ = preCheck
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
config.SecurityTestStatus = false
config.AutoChangeDiskMethod = false
}
// SetIPQualityTestStatus sets IP quality test configuration
func SetIPQualityTestStatus(config *params.Config) {
config.OnlyIpInfoCheck = true
config.SecurityTestStatus = true
config.EmailTestStatus = true
}
// SetRouteTestStatus sets route test configuration
func SetRouteTestStatus(config *params.Config) {
config.OnlyIpInfoCheck = true
config.BacktraceStatus = true
config.Nt3Status = true
config.PingTestStatus = true
config.TgdcTestStatus = true
config.WebTestStatus = true
}
// PrintInvalidChoice prints invalid choice message
func PrintInvalidChoice(language string) {
if language == "zh" {
fmt.Println("无效的选项")
} else {
fmt.Println("Invalid choice")
}
}

369
internal/params/params.go Normal file
View File

@@ -0,0 +1,369 @@
package params
import (
"flag"
"fmt"
)
// Config holds all configuration parameters
type Config struct {
EcsVersion string
MenuMode bool
OnlyChinaTest bool
Input string
Choice string
ShowVersion bool
EnableLogger bool
Language string
CpuTestMethod string
CpuTestThreadMode string
MemoryTestMethod string
DiskTestMethod string
DiskTestPath string
DiskMultiCheck bool
Nt3CheckType string
Nt3Location string
SpNum int
Width int
BasicStatus bool
CpuTestStatus bool
MemoryTestStatus bool
DiskTestStatus bool
CommTestStatus bool
UtTestStatus bool
SecurityTestStatus bool
EmailTestStatus bool
BacktraceStatus bool
Nt3Status bool
SpeedTestStatus bool
PingTestStatus bool
TgdcTestStatus bool
WebTestStatus bool
AutoChangeDiskMethod bool
FilePath string
EnableUpload bool
OnlyIpInfoCheck bool
Help bool
Finish bool
UserSetFlags map[string]bool
GoecsFlag *flag.FlagSet
}
// NewConfig creates a new Config with default values
func NewConfig(version string) *Config {
return &Config{
EcsVersion: version,
MenuMode: true,
Language: "zh",
CpuTestMethod: "sysbench",
CpuTestThreadMode: "multi",
MemoryTestMethod: "stream",
DiskTestMethod: "fio",
SpNum: 2,
Width: 82,
BasicStatus: true,
CpuTestStatus: true,
MemoryTestStatus: true,
DiskTestStatus: true,
CommTestStatus: true,
UtTestStatus: true,
SecurityTestStatus: true,
EmailTestStatus: true,
BacktraceStatus: true,
Nt3Status: true,
SpeedTestStatus: true,
Nt3Location: "GZ",
Nt3CheckType: "ipv4",
AutoChangeDiskMethod: true,
FilePath: "goecs.txt",
EnableUpload: true,
UserSetFlags: make(map[string]bool),
GoecsFlag: flag.NewFlagSet("goecs", flag.ContinueOnError),
}
}
// ParseFlags parses command line flags
func (c *Config) ParseFlags(args []string) {
c.GoecsFlag.BoolVar(&c.Help, "h", false, "Show help information")
c.GoecsFlag.BoolVar(&c.Help, "help", false, "Show help information")
c.GoecsFlag.BoolVar(&c.ShowVersion, "v", false, "Display version information")
c.GoecsFlag.BoolVar(&c.ShowVersion, "version", false, "Display version information")
c.GoecsFlag.BoolVar(&c.MenuMode, "menu", true, "Enable/Disable menu mode, disable example: -menu=false")
c.GoecsFlag.StringVar(&c.Language, "l", "zh", "Set language (supported: en, zh)")
c.GoecsFlag.BoolVar(&c.BasicStatus, "basic", true, "Enable/Disable basic test")
c.GoecsFlag.BoolVar(&c.CpuTestStatus, "cpu", true, "Enable/Disable CPU test")
c.GoecsFlag.BoolVar(&c.MemoryTestStatus, "memory", true, "Enable/Disable memory test")
c.GoecsFlag.BoolVar(&c.DiskTestStatus, "disk", true, "Enable/Disable disk test")
c.GoecsFlag.BoolVar(&c.CommTestStatus, "comm", true, "Enable/Disable common media test")
c.GoecsFlag.BoolVar(&c.UtTestStatus, "ut", true, "Enable/Disable unlock media test")
c.GoecsFlag.BoolVar(&c.SecurityTestStatus, "security", true, "Enable/Disable security test")
c.GoecsFlag.BoolVar(&c.EmailTestStatus, "email", true, "Enable/Disable email port test")
c.GoecsFlag.BoolVar(&c.BacktraceStatus, "backtrace", true, "Enable/Disable backtrace test (in 'en' language or on windows it always false)")
c.GoecsFlag.BoolVar(&c.Nt3Status, "nt3", true, "Enable/Disable NT3 test (in 'en' language or on windows it always false)")
c.GoecsFlag.BoolVar(&c.SpeedTestStatus, "speed", true, "Enable/Disable speed test")
c.GoecsFlag.BoolVar(&c.PingTestStatus, "ping", false, "Enable/Disable ping test")
c.GoecsFlag.BoolVar(&c.TgdcTestStatus, "tgdc", false, "Enable/Disable Telegram DC test")
c.GoecsFlag.BoolVar(&c.WebTestStatus, "web", false, "Enable/Disable popular websites test")
c.GoecsFlag.StringVar(&c.CpuTestMethod, "cpum", "sysbench", "Set CPU test method (supported: sysbench, geekbench, winsat)")
c.GoecsFlag.StringVar(&c.CpuTestThreadMode, "cput", "multi", "Set CPU test thread mode (supported: single, multi)")
c.GoecsFlag.StringVar(&c.MemoryTestMethod, "memorym", "stream", "Set memory test method (supported: stream, sysbench, dd, winsat, auto)")
c.GoecsFlag.StringVar(&c.DiskTestMethod, "diskm", "fio", "Set disk test method (supported: fio, dd, winsat)")
c.GoecsFlag.StringVar(&c.DiskTestPath, "diskp", "", "Set disk test path, e.g., -diskp /root")
c.GoecsFlag.BoolVar(&c.DiskMultiCheck, "diskmc", false, "Enable/Disable multiple disk checks, e.g., -diskmc=false")
c.GoecsFlag.StringVar(&c.Nt3Location, "nt3loc", "GZ", "Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all)")
c.GoecsFlag.StringVar(&c.Nt3CheckType, "nt3t", "ipv4", "Set NT3 test type (supported: both, ipv4, ipv6)")
c.GoecsFlag.IntVar(&c.SpNum, "spnum", 2, "Set the number of servers per operator for speed test")
c.GoecsFlag.BoolVar(&c.EnableLogger, "log", false, "Enable/Disable logging in the current path")
c.GoecsFlag.BoolVar(&c.EnableUpload, "upload", true, "Enable/Disable upload the result")
c.GoecsFlag.Parse(args)
c.GoecsFlag.Visit(func(f *flag.Flag) {
c.UserSetFlags[f.Name] = true
})
}
// HandleHelpAndVersion handles help and version flags
func (c *Config) HandleHelpAndVersion(programName string) bool {
if c.Help {
fmt.Printf("Usage: %s [options]\n", programName)
c.GoecsFlag.PrintDefaults()
return true
}
if c.ShowVersion {
fmt.Println(c.EcsVersion)
return true
}
return false
}
// SaveUserSetParams saves user-set parameters
func (c *Config) SaveUserSetParams() map[string]interface{} {
saved := make(map[string]interface{})
if c.UserSetFlags["basic"] {
saved["basic"] = c.BasicStatus
}
if c.UserSetFlags["cpu"] {
saved["cpu"] = c.CpuTestStatus
}
if c.UserSetFlags["memory"] {
saved["memory"] = c.MemoryTestStatus
}
if c.UserSetFlags["disk"] {
saved["disk"] = c.DiskTestStatus
}
if c.UserSetFlags["comm"] {
saved["comm"] = c.CommTestStatus
}
if c.UserSetFlags["ut"] {
saved["ut"] = c.UtTestStatus
}
if c.UserSetFlags["security"] {
saved["security"] = c.SecurityTestStatus
}
if c.UserSetFlags["email"] {
saved["email"] = c.EmailTestStatus
}
if c.UserSetFlags["backtrace"] {
saved["backtrace"] = c.BacktraceStatus
}
if c.UserSetFlags["nt3"] {
saved["nt3"] = c.Nt3Status
}
if c.UserSetFlags["speed"] {
saved["speed"] = c.SpeedTestStatus
}
if c.UserSetFlags["ping"] {
saved["ping"] = c.PingTestStatus
}
if c.UserSetFlags["tgdc"] {
saved["tgdc"] = c.TgdcTestStatus
}
if c.UserSetFlags["web"] {
saved["web"] = c.WebTestStatus
}
if c.UserSetFlags["cpum"] {
saved["cpum"] = c.CpuTestMethod
}
if c.UserSetFlags["cput"] {
saved["cput"] = c.CpuTestThreadMode
}
if c.UserSetFlags["memorym"] {
saved["memorym"] = c.MemoryTestMethod
}
if c.UserSetFlags["diskm"] {
saved["diskm"] = c.DiskTestMethod
}
if c.UserSetFlags["diskp"] {
saved["diskp"] = c.DiskTestPath
}
if c.UserSetFlags["diskmc"] {
saved["diskmc"] = c.DiskMultiCheck
}
if c.UserSetFlags["nt3loc"] {
saved["nt3loc"] = c.Nt3Location
}
if c.UserSetFlags["nt3t"] {
saved["nt3t"] = c.Nt3CheckType
}
if c.UserSetFlags["spnum"] {
saved["spnum"] = c.SpNum
}
return saved
}
// RestoreUserSetParams restores user-set parameters
func (c *Config) RestoreUserSetParams(saved map[string]interface{}) {
if val, ok := saved["basic"]; ok {
c.BasicStatus = val.(bool)
}
if val, ok := saved["cpu"]; ok {
c.CpuTestStatus = val.(bool)
}
if val, ok := saved["memory"]; ok {
c.MemoryTestStatus = val.(bool)
}
if val, ok := saved["disk"]; ok {
c.DiskTestStatus = val.(bool)
}
if val, ok := saved["comm"]; ok {
c.CommTestStatus = val.(bool)
}
if val, ok := saved["ut"]; ok {
c.UtTestStatus = val.(bool)
}
if val, ok := saved["security"]; ok {
c.SecurityTestStatus = val.(bool)
}
if val, ok := saved["email"]; ok {
c.EmailTestStatus = val.(bool)
}
if val, ok := saved["backtrace"]; ok {
c.BacktraceStatus = val.(bool)
}
if val, ok := saved["nt3"]; ok {
c.Nt3Status = val.(bool)
}
if val, ok := saved["speed"]; ok {
c.SpeedTestStatus = val.(bool)
}
if val, ok := saved["ping"]; ok {
c.PingTestStatus = val.(bool)
}
if val, ok := saved["tgdc"]; ok {
c.TgdcTestStatus = val.(bool)
}
if val, ok := saved["web"]; ok {
c.WebTestStatus = val.(bool)
}
if val, ok := saved["cpum"]; ok {
c.CpuTestMethod = val.(string)
}
if val, ok := saved["cput"]; ok {
c.CpuTestThreadMode = val.(string)
}
if val, ok := saved["memorym"]; ok {
c.MemoryTestMethod = val.(string)
}
if val, ok := saved["diskm"]; ok {
c.DiskTestMethod = val.(string)
}
if val, ok := saved["diskp"]; ok {
c.DiskTestPath = val.(string)
}
if val, ok := saved["diskmc"]; ok {
c.DiskMultiCheck = val.(bool)
}
if val, ok := saved["nt3loc"]; ok {
if c.Choice != "10" {
c.Nt3Location = val.(string)
}
}
if val, ok := saved["nt3t"]; ok {
c.Nt3CheckType = val.(string)
}
if val, ok := saved["spnum"]; ok {
c.SpNum = val.(int)
}
c.ValidateParams()
}
// ValidateParams validates parameter values
func (c *Config) ValidateParams() {
validCpuMethods := map[string]bool{"sysbench": true, "geekbench": true, "winsat": true}
if !validCpuMethods[c.CpuTestMethod] {
if c.Language == "zh" {
fmt.Printf("警告: CPU测试方法 '%s' 无效,使用默认值 'sysbench'\n", c.CpuTestMethod)
} else {
fmt.Printf("Warning: Invalid CPU test method '%s', using default 'sysbench'\n", c.CpuTestMethod)
}
c.CpuTestMethod = "sysbench"
}
validThreadModes := map[string]bool{"single": true, "multi": true}
if !validThreadModes[c.CpuTestThreadMode] {
if c.Language == "zh" {
fmt.Printf("警告: CPU线程模式 '%s' 无效,使用默认值 'multi'\n", c.CpuTestThreadMode)
} else {
fmt.Printf("Warning: Invalid CPU thread mode '%s', using default 'multi'\n", c.CpuTestThreadMode)
}
c.CpuTestThreadMode = "multi"
}
validMemoryMethods := map[string]bool{"stream": true, "sysbench": true, "dd": true, "winsat": true, "auto": true}
if !validMemoryMethods[c.MemoryTestMethod] {
if c.Language == "zh" {
fmt.Printf("警告: 内存测试方法 '%s' 无效,使用默认值 'stream'\n", c.MemoryTestMethod)
} else {
fmt.Printf("Warning: Invalid memory test method '%s', using default 'stream'\n", c.MemoryTestMethod)
}
c.MemoryTestMethod = "stream"
}
validDiskMethods := map[string]bool{"fio": true, "dd": true, "winsat": true}
if !validDiskMethods[c.DiskTestMethod] {
if c.Language == "zh" {
fmt.Printf("警告: 磁盘测试方法 '%s' 无效,使用默认值 'fio'\n", c.DiskTestMethod)
} else {
fmt.Printf("Warning: Invalid disk test method '%s', using default 'fio'\n", c.DiskTestMethod)
}
c.DiskTestMethod = "fio"
}
validNt3Locations := map[string]bool{"GZ": true, "SH": true, "BJ": true, "CD": true, "ALL": true}
if !validNt3Locations[c.Nt3Location] {
if c.Language == "zh" {
fmt.Printf("警告: NT3测试位置 '%s' 无效,使用默认值 'GZ'\n", c.Nt3Location)
} else {
fmt.Printf("Warning: Invalid NT3 location '%s', using default 'GZ'\n", c.Nt3Location)
}
c.Nt3Location = "GZ"
}
validNt3Types := map[string]bool{"both": true, "ipv4": true, "ipv6": true}
if !validNt3Types[c.Nt3CheckType] {
if c.Language == "zh" {
fmt.Printf("警告: NT3测试类型 '%s' 无效,使用默认值 'ipv4'\n", c.Nt3CheckType)
} else {
fmt.Printf("Warning: Invalid NT3 check type '%s', using default 'ipv4'\n", c.Nt3CheckType)
}
c.Nt3CheckType = "ipv4"
}
if c.SpNum < 0 {
if c.Language == "zh" {
fmt.Printf("警告: 测速节点数量 '%d' 无效,使用默认值 2\n", c.SpNum)
} else {
fmt.Printf("Warning: Invalid speed test node count '%d', using default 2\n", c.SpNum)
}
c.SpNum = 2
}
validLanguages := map[string]bool{"zh": true, "en": true}
if !validLanguages[c.Language] {
fmt.Printf("Warning: Invalid language '%s', using default 'zh'\n", c.Language)
c.Language = "zh"
}
}

483
internal/runner/runner.go Normal file
View File

@@ -0,0 +1,483 @@
package runner
import (
"bufio"
"fmt"
"os"
"runtime"
"strings"
"sync"
"time"
"github.com/oneclickvirt/CommonMediaTests/commediatests"
"github.com/oneclickvirt/ecs/internal/params"
"github.com/oneclickvirt/ecs/internal/tests"
"github.com/oneclickvirt/ecs/utils"
"github.com/oneclickvirt/pingtest/pt"
"github.com/oneclickvirt/portchecker/email"
)
// RunChineseTests runs all tests in Chinese mode
func RunChineseTests(preCheck utils.NetCheckResult, config *params.Config, wg1, wg2, wg3 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo *string, output *string, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) {
*output = RunBasicTests(preCheck, config, basicInfo, securityInfo, *output, tempOutput, outputMutex)
*output = RunCPUTest(config, *output, tempOutput, outputMutex)
*output = RunMemoryTest(config, *output, tempOutput, outputMutex)
*output = RunDiskTest(config, *output, tempOutput, outputMutex)
if config.OnlyIpInfoCheck && !config.BasicStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunIpInfoCheck(config, *output, tempOutput, outputMutex)
}
if config.UtTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" && !config.OnlyChinaTest {
wg1.Add(1)
go func() {
defer wg1.Done()
*mediaInfo = tests.MediaTest(config.Language)
}()
}
if config.EmailTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
wg2.Add(1)
go func() {
defer wg2.Done()
*emailInfo = email.EmailCheck()
}()
}
if (config.OnlyChinaTest || config.PingTestStatus) && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
wg3.Add(1)
go func() {
defer wg3.Done()
*ptInfo = pt.PingTest()
}()
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunStreamingTests(config, wg1, mediaInfo, *output, tempOutput, outputMutex)
*output = RunSecurityTests(config, *securityInfo, *output, tempOutput, outputMutex)
*output = RunEmailTests(config, wg2, emailInfo, *output, tempOutput, outputMutex)
}
if runtime.GOOS != "windows" && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunNetworkTests(config, wg3, ptInfo, *output, tempOutput, outputMutex)
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunSpeedTests(config, *output, tempOutput, outputMutex)
}
*output = AppendTimeInfo(config, *output, tempOutput, startTime, outputMutex)
}
// RunEnglishTests runs all tests in English mode
func RunEnglishTests(preCheck utils.NetCheckResult, config *params.Config, wg1, wg2, wg3 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo *string, output *string, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) {
*output = RunBasicTests(preCheck, config, basicInfo, securityInfo, *output, tempOutput, outputMutex)
*output = RunCPUTest(config, *output, tempOutput, outputMutex)
*output = RunMemoryTest(config, *output, tempOutput, outputMutex)
*output = RunDiskTest(config, *output, tempOutput, outputMutex)
if config.OnlyIpInfoCheck && !config.BasicStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunIpInfoCheck(config, *output, tempOutput, outputMutex)
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
if config.UtTestStatus {
wg1.Add(1)
go func() {
defer wg1.Done()
*mediaInfo = tests.MediaTest(config.Language)
}()
}
if config.EmailTestStatus {
wg2.Add(1)
go func() {
defer wg2.Done()
*emailInfo = email.EmailCheck()
}()
}
*output = RunStreamingTests(config, wg1, mediaInfo, *output, tempOutput, outputMutex)
*output = RunSecurityTests(config, *securityInfo, *output, tempOutput, outputMutex)
*output = RunEmailTests(config, wg2, emailInfo, *output, tempOutput, outputMutex)
*output = RunEnglishNetworkTests(config, wg3, ptInfo, *output, tempOutput, outputMutex)
*output = RunEnglishSpeedTests(config, *output, tempOutput, outputMutex)
}
*output = AppendTimeInfo(config, *output, tempOutput, startTime, outputMutex)
}
// RunIpInfoCheck performs IP info check
func RunIpInfoCheck(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
var ipinfo string
tests.IPV4, tests.IPV6, ipinfo = utils.OnlyBasicsIpInfo(config.Language)
if ipinfo != "" {
if config.Language == "zh" {
utils.PrintCenteredTitle("IP信息", config.Width)
} else {
utils.PrintCenteredTitle("IP-Information", config.Width)
}
fmt.Printf("%s", ipinfo)
}
}, tempOutput, output)
}
// RunBasicTests runs basic system tests
func RunBasicTests(preCheck utils.NetCheckResult, config *params.Config, basicInfo, securityInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
utils.PrintHead(config.Language, config.Width, config.EcsVersion)
if config.BasicStatus || config.SecurityTestStatus {
if config.BasicStatus {
if config.Language == "zh" {
utils.PrintCenteredTitle("系统基础信息", config.Width)
} else {
utils.PrintCenteredTitle("System-Basic-Information", config.Width)
}
}
if preCheck.Connected && preCheck.StackType == "DualStack" {
tests.IPV4, tests.IPV6, *basicInfo, *securityInfo, config.Nt3CheckType = utils.BasicsAndSecurityCheck(config.Language, config.Nt3CheckType, config.SecurityTestStatus)
} else if preCheck.Connected && preCheck.StackType == "IPv4" {
tests.IPV4, tests.IPV6, *basicInfo, *securityInfo, config.Nt3CheckType = utils.BasicsAndSecurityCheck(config.Language, "ipv4", config.SecurityTestStatus)
} else if preCheck.Connected && preCheck.StackType == "IPv6" {
tests.IPV4, tests.IPV6, *basicInfo, *securityInfo, config.Nt3CheckType = utils.BasicsAndSecurityCheck(config.Language, "ipv6", config.SecurityTestStatus)
} else {
tests.IPV4, tests.IPV6, *basicInfo, *securityInfo, config.Nt3CheckType = utils.BasicsAndSecurityCheck(config.Language, "", false)
config.SecurityTestStatus = false
}
if config.BasicStatus {
fmt.Printf("%s", *basicInfo)
} else if (config.Input == "6" || config.Input == "9") && config.SecurityTestStatus {
scanner := bufio.NewScanner(strings.NewReader(*basicInfo))
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "IPV") {
fmt.Println(line)
}
}
}
}
}, tempOutput, output)
}
// RunCPUTest runs CPU test
func RunCPUTest(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.CpuTestStatus {
realTestMethod, res := tests.CpuTest(config.Language, config.CpuTestMethod, config.CpuTestThreadMode)
if config.Language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("CPU测试-通过%s测试", realTestMethod), config.Width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("CPU-Test--%s-Method", realTestMethod), config.Width)
}
fmt.Print(res)
}
}, tempOutput, output)
}
// RunMemoryTest runs memory test
func RunMemoryTest(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.MemoryTestStatus {
realTestMethod, res := tests.MemoryTest(config.Language, config.MemoryTestMethod)
if config.Language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("内存测试-通过%s测试", realTestMethod), config.Width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Memory-Test--%s-Method", realTestMethod), config.Width)
}
fmt.Print(res)
}
}, tempOutput, output)
}
// RunDiskTest runs disk test
func RunDiskTest(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.DiskTestStatus && config.AutoChangeDiskMethod {
realTestMethod, res := tests.DiskTest(config.Language, config.DiskTestMethod, config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
if config.Language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", realTestMethod), config.Width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", realTestMethod), config.Width)
}
fmt.Print(res)
} else if config.DiskTestStatus && !config.AutoChangeDiskMethod {
if config.Language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", "dd"), config.Width)
_, res := tests.DiskTest(config.Language, "dd", config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
fmt.Print(res)
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", "fio"), config.Width)
_, res = tests.DiskTest(config.Language, "fio", config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
fmt.Print(res)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", "dd"), config.Width)
_, res := tests.DiskTest(config.Language, "dd", config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
fmt.Print(res)
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", "fio"), config.Width)
_, res = tests.DiskTest(config.Language, "fio", config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
fmt.Print(res)
}
}
}, tempOutput, output)
}
// RunStreamingTests runs streaming media tests
func RunStreamingTests(config *params.Config, wg1 *sync.WaitGroup, mediaInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.Language == "zh" {
if config.CommTestStatus && !config.OnlyChinaTest {
utils.PrintCenteredTitle("御三家流媒体解锁", config.Width)
fmt.Printf("%s", commediatests.MediaTests(config.Language))
}
}
if config.UtTestStatus && (config.Language == "zh" && !config.OnlyChinaTest || config.Language == "en") {
wg1.Wait()
if config.Language == "zh" {
utils.PrintCenteredTitle("跨国流媒体解锁", config.Width)
} else {
utils.PrintCenteredTitle("Cross-Border-Streaming-Media-Unlock", config.Width)
}
fmt.Printf("%s", *mediaInfo)
}
}, tempOutput, output)
}
// RunSecurityTests runs security tests
func RunSecurityTests(config *params.Config, securityInfo, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.SecurityTestStatus {
if config.Language == "zh" {
utils.PrintCenteredTitle("IP质量检测", config.Width)
} else {
utils.PrintCenteredTitle("IP-Quality-Check", config.Width)
}
fmt.Printf("%s", securityInfo)
}
}, tempOutput, output)
}
// RunEmailTests runs email port tests
func RunEmailTests(config *params.Config, wg2 *sync.WaitGroup, emailInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.EmailTestStatus {
wg2.Wait()
if config.Language == "zh" {
utils.PrintCenteredTitle("邮件端口检测", config.Width)
} else {
utils.PrintCenteredTitle("Email-Port-Check", config.Width)
}
fmt.Println(*emailInfo)
}
}, tempOutput, output)
}
// RunNetworkTests runs network tests (Chinese mode)
func RunNetworkTests(config *params.Config, wg3 *sync.WaitGroup, ptInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.BacktraceStatus && !config.OnlyChinaTest {
utils.PrintCenteredTitle("上游及回程线路检测", config.Width)
tests.UpstreamsCheck()
}
if config.Nt3Status && !config.OnlyChinaTest {
utils.PrintCenteredTitle("三网回程路由检测", config.Width)
tests.NextTrace3Check(config.Language, config.Nt3Location, config.Nt3CheckType)
}
if config.OnlyChinaTest && *ptInfo != "" {
wg3.Wait()
utils.PrintCenteredTitle("PING值检测", config.Width)
fmt.Println(*ptInfo)
}
if config.PingTestStatus && *ptInfo != "" {
wg3.Wait()
utils.PrintCenteredTitle("PING值检测", config.Width)
fmt.Println(*ptInfo)
if config.TgdcTestStatus {
fmt.Println(pt.TelegramDCTest())
}
if config.WebTestStatus {
fmt.Println(pt.WebsiteTest())
}
}
if !config.OnlyChinaTest && !config.PingTestStatus && (config.TgdcTestStatus || config.WebTestStatus) {
utils.PrintCenteredTitle("PING值检测", config.Width)
if config.TgdcTestStatus {
fmt.Println(pt.TelegramDCTest())
}
if config.WebTestStatus {
fmt.Println(pt.WebsiteTest())
}
}
}, tempOutput, output)
}
// RunSpeedTests runs speed tests (Chinese mode)
func RunSpeedTests(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.SpeedTestStatus {
utils.PrintCenteredTitle("就近节点测速", config.Width)
tests.ShowHead(config.Language)
if config.Choice == "1" || !config.MenuMode {
tests.NearbySP()
tests.CustomSP("net", "global", 2, config.Language)
tests.CustomSP("net", "cu", config.SpNum, config.Language)
tests.CustomSP("net", "ct", config.SpNum, config.Language)
tests.CustomSP("net", "cmcc", config.SpNum, config.Language)
} else if config.Choice == "2" || config.Choice == "3" || config.Choice == "4" || config.Choice == "5" {
tests.CustomSP("net", "global", 4, config.Language)
} else if config.Choice == "6" {
tests.CustomSP("net", "global", 11, config.Language)
}
}
}, tempOutput, output)
}
// RunEnglishNetworkTests runs network tests (English mode)
func RunEnglishNetworkTests(config *params.Config, wg3 *sync.WaitGroup, ptInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.TgdcTestStatus || config.WebTestStatus {
utils.PrintCenteredTitle("PING-Test", config.Width)
if config.TgdcTestStatus {
fmt.Println(pt.TelegramDCTest())
}
if config.WebTestStatus {
fmt.Println(pt.WebsiteTest())
}
}
}, tempOutput, output)
}
// RunEnglishSpeedTests runs speed tests (English mode)
func RunEnglishSpeedTests(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.SpeedTestStatus {
utils.PrintCenteredTitle("Speed-Test", config.Width)
tests.ShowHead(config.Language)
tests.NearbySP()
tests.CustomSP("net", "global", -1, config.Language)
}
}, tempOutput, output)
}
// AppendTimeInfo appends timing information
func AppendTimeInfo(config *params.Config, output, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
endTime := time.Now()
duration := endTime.Sub(startTime)
minutes := int(duration.Minutes())
seconds := int(duration.Seconds()) % 60
currentTime := time.Now().Format("Mon Jan 2 15:04:05 MST 2006")
return utils.PrintAndCapture(func() {
utils.PrintCenteredTitle("", config.Width)
if config.Language == "zh" {
fmt.Printf("花费 : %d 分 %d 秒\n", minutes, seconds)
fmt.Printf("时间 : %s\n", currentTime)
} else {
fmt.Printf("Cost Time : %d min %d sec\n", minutes, seconds)
fmt.Printf("Current Time : %s\n", currentTime)
}
utils.PrintCenteredTitle("", config.Width)
}, tempOutput, output)
}
// HandleSignalInterrupt handles interrupt signals
func HandleSignalInterrupt(sig chan os.Signal, config *params.Config, startTime *time.Time, output *string, tempOutput string, uploadDone chan bool, outputMutex *sync.Mutex) {
select {
case <-sig:
if !config.Finish {
endTime := time.Now()
duration := endTime.Sub(*startTime)
minutes := int(duration.Minutes())
seconds := int(duration.Seconds()) % 60
currentTime := time.Now().Format("Mon Jan 2 15:04:05 MST 2006")
outputMutex.Lock()
timeInfo := utils.PrintAndCapture(func() {
utils.PrintCenteredTitle("", config.Width)
if config.Language == "zh" {
fmt.Printf("花费 : %d 分 %d 秒\n", minutes, seconds)
fmt.Printf("时间 : %s\n", currentTime)
} else {
fmt.Printf("Cost Time : %d min %d sec\n", minutes, seconds)
fmt.Printf("Current Time : %s\n", currentTime)
}
utils.PrintCenteredTitle("", config.Width)
}, "", "")
*output += timeInfo
finalOutput := *output
outputMutex.Unlock()
resultChan := make(chan struct {
httpURL string
httpsURL string
}, 1)
if config.EnableUpload {
go func() {
httpURL, httpsURL := utils.ProcessAndUpload(finalOutput, config.FilePath, config.EnableUpload)
resultChan <- struct {
httpURL string
httpsURL string
}{httpURL, httpsURL}
uploadDone <- true
}()
select {
case result := <-resultChan:
if result.httpURL != "" || result.httpsURL != "" {
if config.Language == "en" {
fmt.Printf("Upload successfully!\nHttp URL: %s\nHttps URL: %s\n", result.httpURL, result.httpsURL)
} else {
fmt.Printf("上传成功!\nHttp URL: %s\nHttps URL: %s\n", result.httpURL, result.httpsURL)
}
}
time.Sleep(100 * time.Millisecond)
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(0)
case <-time.After(30 * time.Second):
if config.Language == "en" {
fmt.Println("Upload timeout, program exit")
} else {
fmt.Println("上传超时,程序退出")
}
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(1)
}
} else {
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(0)
}
}
os.Exit(0)
}
}
// HandleUploadResults handles uploading results
func HandleUploadResults(config *params.Config, output string) {
httpURL, httpsURL := utils.ProcessAndUpload(output, config.FilePath, config.EnableUpload)
if httpURL != "" || httpsURL != "" {
if config.Language == "en" {
fmt.Printf("Upload successfully!\nHttp URL: %s\nHttps URL: %s\n", httpURL, httpsURL)
fmt.Println("Each Test Benchmark: https://bash.spiritlhl.net/ecsguide")
} else {
fmt.Printf("上传成功!\nHttp URL: %s\nHttps URL: %s\n", httpURL, httpsURL)
fmt.Println("每项测试基准见: https://bash.spiritlhl.net/ecsguide")
}
}
}

View File

@@ -1,4 +1,4 @@
package cputest
package tests
import (
"runtime"

View File

@@ -1,4 +1,4 @@
package disktest
package tests
import (
"runtime"

View File

@@ -1,4 +1,4 @@
package memorytest
package tests
import (
"runtime"

View File

@@ -1,4 +1,4 @@
package nexttrace
package tests
import (
"fmt"

View File

@@ -1,10 +1,11 @@
package speedtest
package tests
import (
"github.com/oneclickvirt/speedtest/model"
"github.com/oneclickvirt/speedtest/sp"
"runtime"
"strings"
"github.com/oneclickvirt/speedtest/model"
"github.com/oneclickvirt/speedtest/sp"
)
func ShowHead(language string) {

View File

@@ -1,25 +1,25 @@
package unlocktest
package tests
import (
"github.com/oneclickvirt/UnlockTests/executor"
"github.com/oneclickvirt/UnlockTests/utils"
"github.com/oneclickvirt/UnlockTests/uts"
"github.com/oneclickvirt/defaultset"
)
func MediaTest(language string) string {
var res string
readStatus := uts.ReadSelect(language, "0")
readStatus := executor.ReadSelect(language, "0")
if !readStatus {
return ""
}
if uts.IPV4 {
if executor.IPV4 {
res += defaultset.Blue("IPV4:") + "\n"
res += uts.RunTests(utils.Ipv4HttpClient, "ipv4", language, false)
res += executor.RunTests(utils.Ipv4HttpClient, "ipv4", language, false)
return res
}
if uts.IPV6 {
if executor.IPV6 {
res += defaultset.Blue("IPV6:") + "\n"
res += uts.RunTests(utils.Ipv6HttpClient, "ipv6", language, false)
res += executor.RunTests(utils.Ipv6HttpClient, "ipv6", language, false)
return res
}
return ""

View File

@@ -1,11 +1,11 @@
package upstreams
package tests
import (
"fmt"
"sync"
"time"
"github.com/oneclickvirt/UnlockTests/uts"
"github.com/oneclickvirt/UnlockTests/executor"
bgptools "github.com/oneclickvirt/backtrace/bgptools"
backtrace "github.com/oneclickvirt/backtrace/bk"
. "github.com/oneclickvirt/defaultset"
@@ -51,7 +51,7 @@ func UpstreamsCheck() {
wg.Add(1)
go func() {
defer wg.Done()
result := backtrace.BackTrace(uts.IPV6)
result := backtrace.BackTrace(executor.IPV6)
results.backtraceResult = result
}()
wg.Wait()

View File

@@ -1,11 +0,0 @@
package memorytest
import (
"fmt"
"testing"
)
func Test(t *testing.T) {
_, res := MemoryTest("zh", "stream")
fmt.Print(res)
}

View File

@@ -1,13 +0,0 @@
package nexttrace
import (
"testing"
"time"
)
func TestNextTrace3Check(t *testing.T) {
start := time.Now()
NextTrace3Check("zh", "ALL", "ipv4")
duration := time.Since(start)
t.Logf("执行耗时: %s", duration)
}

View File

@@ -1,8 +0,0 @@
package speedtest
import "testing"
func Test(t *testing.T) {
ShowHead("en")
NearbySP()
}

View File

@@ -1,10 +0,0 @@
package unlocktest
import (
"fmt"
"testing"
)
func Test(t *testing.T) {
fmt.Printf("%s", MediaTest("zh"))
}

View File

@@ -1,8 +0,0 @@
package upstreams
import "testing"
func TestUpstreamsCheck(t *testing.T) {
IPV4 = "148.100.85.25"
UpstreamsCheck()
}

View File

@@ -18,7 +18,7 @@ import (
"unicode/utf8"
"github.com/imroc/req/v3"
"github.com/oneclickvirt/UnlockTests/uts"
"github.com/oneclickvirt/UnlockTests/executor"
bnetwork "github.com/oneclickvirt/basics/network"
"github.com/oneclickvirt/basics/system"
butils "github.com/oneclickvirt/basics/utils"
@@ -124,14 +124,14 @@ func OnlyBasicsIpInfo(language string) (string, string, string) {
}
basicInfo := ipInfo
if strings.Contains(ipInfo, "IPV4") && strings.Contains(ipInfo, "IPV6") && ipv4 != "" && ipv6 != "" {
uts.IPV4 = true
uts.IPV6 = true
executor.IPV4 = true
executor.IPV6 = true
} else if strings.Contains(ipInfo, "IPV4") && ipv4 != "" {
uts.IPV4 = true
uts.IPV6 = false
executor.IPV4 = true
executor.IPV6 = false
} else if strings.Contains(ipInfo, "IPV6") && ipv6 != "" {
uts.IPV6 = true
uts.IPV4 = false
executor.IPV6 = true
executor.IPV4 = false
}
basicInfo = strings.ReplaceAll(basicInfo, "\n\n", "\n")
return ipv4, ipv6, basicInfo
@@ -157,20 +157,20 @@ func BasicsAndSecurityCheck(language, nt3CheckType string, securityCheckStatus b
wgt.Wait()
basicInfo := systemInfo + ipInfo
if strings.Contains(ipInfo, "IPV4") && strings.Contains(ipInfo, "IPV6") && ipv4 != "" && ipv6 != "" {
uts.IPV4 = true
uts.IPV6 = true
executor.IPV4 = true
executor.IPV6 = true
if nt3CheckType == "" {
nt3CheckType = "ipv4"
}
} else if strings.Contains(ipInfo, "IPV4") && ipv4 != "" {
uts.IPV4 = true
uts.IPV6 = false
executor.IPV4 = true
executor.IPV6 = false
if nt3CheckType == "" {
nt3CheckType = "ipv4"
}
} else if strings.Contains(ipInfo, "IPV6") && ipv6 != "" {
uts.IPV6 = true
uts.IPV4 = false
executor.IPV6 = true
executor.IPV4 = false
if nt3CheckType == "" {
nt3CheckType = "ipv6"
}