Compare commits

...

12 Commits

Author SHA1 Message Date
spiritlhl
e4a759fceb fix:修改非选项1时的测速逻辑,区分中英文测速使用不同的节点逻辑 2026-01-13 11:23:21 +08:00
spiritlhl
e5c4b0ce8e Clean up newlines in create_public_branch.py
Remove unnecessary newlines around specific strings in content.
2026-01-12 23:48:03 +08:00
spiritsoul
188a1153e6 fix:删除对应的依赖 2026-01-12 23:40:58 +08:00
spiritsoul
4c4887f487 fix:以文本结构匹配避免正则匹配越界 2026-01-12 23:31:27 +08:00
spiritsoul
feac73a427 fix:修复正则匹配 2026-01-12 23:28:45 +08:00
spiritlhl
f3048d074c Clean up Go module cache and remove go.sum 2026-01-12 23:19:18 +08:00
spiritlhl
58702b54e7 Remove privatespeedtest code from speed.go
Brutally remove privatespeedtest-related code from speed.go, ensuring the file remains syntactically correct.
2026-01-12 23:16:49 +08:00
spiritlhl
1e4d63ef57 Update Go version to 1.25.4 in workflow 2026-01-12 23:08:23 +08:00
spiritlhl
9f3acacae0 Disable modify_go_mod function and related calls
Comment out the modify_go_mod function and its calls.
2026-01-12 23:07:43 +08:00
spiritlhl
e9755f0c20 Refine speed test description in README.md 2026-01-12 23:01:19 +08:00
GitHub Actions
d8b397b31b Auto update README files 2026-01-12 14:41:00 +00:00
github-actions[bot]
9e22d1bc23 chore: update ECS_VERSION to 0.1.108 in goecs.sh 2026-01-12 14:37:10 +00:00
9 changed files with 202 additions and 157 deletions

View File

@@ -21,112 +21,131 @@ def write_file(filepath, content):
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
def modify_speed_go(filepath):
def modify_go_mod(filepath):
"""
Modify internal/tests/speed.go to comment out privatespeedtest import and usage.
This ensures the file remains syntactically correct.
Modify go.mod to remove privatespeedtest (and optional security) dependencies.
Automatically matches module names regardless of version or indirect comment.
"""
content = read_file(filepath)
# Modules to remove
remove_modules = [
r'github\.com/oneclickvirt/privatespeedtest',
r'github\.com/oneclickvirt/security',
]
for mod in remove_modules:
# Remove full require line (with or without // indirect)
content = re.sub(
rf'^[ \t]*{mod}[ \t]+v[^\s]+(?:[ \t]+// indirect)?[ \t]*\n',
'',
content,
flags=re.MULTILINE
)
write_file(filepath, content)
print(f"✓ Removed privatespeedtest/security from {filepath}")
def remove_code_block(lines, start_marker, end_condition='empty_line'):
"""
Remove code block from lines starting with start_marker.
# Comment out the privatespeedtest import
content = re.sub(
r'(\t)"github\.com/oneclickvirt/privatespeedtest/pst"',
r'\1// "github.com/oneclickvirt/privatespeedtest/pst"',
content
)
Args:
lines: List of file lines
start_marker: String or list of strings to identify block start
end_condition: 'empty_line' (default) or 'closing_brace' or custom function
Returns:
Modified lines with the block removed
"""
if isinstance(start_marker, str):
start_marker = [start_marker]
result = []
skip_mode = False
brace_depth = 0
# Process line by line for precise control
lines = content.split('\n')
new_lines = []
in_private_func = False
in_private_call_block = False
func_brace_count = 0
i = 0
while i < len(lines):
line = lines[i]
# Detect start of privateSpeedTest function
if 'func privateSpeedTest(num int, operator string) error {' in line:
in_private_func = True
func_brace_count = 1
new_lines.append('// ' + line)
i += 1
continue
# If we're inside the privateSpeedTest function, comment out everything
if in_private_func:
# Count braces
func_brace_count += line.count('{') - line.count('}')
# Check if we should start skipping
if not skip_mode:
for marker in start_marker:
if marker in line:
skip_mode = True
if end_condition == 'closing_brace':
# Count opening braces on the function declaration line
brace_depth = line.count('{') - line.count('}')
break
# Comment out the line
if line.strip():
if not line.strip().startswith('//'):
# Preserve indentation
indent = len(line) - len(line.lstrip())
new_lines.append(' ' * indent + '// ' + line.lstrip())
else:
new_lines.append(line)
else:
new_lines.append(line)
# Check if function ends
if func_brace_count == 0:
in_private_func = False
i += 1
continue
if not skip_mode:
result.append(line)
else:
# We're in skip mode
if end_condition == 'empty_line':
# Skip until we find an empty line
if line.strip() == '':
skip_mode = False
# Don't add the empty line, continue to next
elif end_condition == 'closing_brace':
# Track brace depth
brace_depth += line.count('{') - line.count('}')
if brace_depth == 0 and '}' in line:
# Function ended, skip until next empty line
end_condition = 'empty_line'
# Detect the call to privateSpeedTest in CustomSP
if '\terr := privateSpeedTest(num, opLower)' in line:
in_private_call_block = True
new_lines.append('\t\t// err := privateSpeedTest(num, opLower)')
i += 1
continue
# Handle the if-else block after the call
if in_private_call_block:
stripped = line.strip()
# Comment out these lines
if stripped.startswith('if err != nil'):
new_lines.append('\t\t// if err != nil {')
i += 1
continue
elif 'fmt.Fprintf(os.Stderr, "[WARN] privatespeedtest failed' in line:
new_lines.append('\t\t\t// fmt.Fprintf(os.Stderr, "[WARN] privatespeedtest failed\\n")')
i += 1
continue
elif stripped == '// 继续使用原有的兜底方案':
new_lines.append('\t\t\t// // 继续使用原有的兜底方案')
i += 1
continue
elif stripped == '} else {':
new_lines.append('\t\t// } else {')
i += 1
continue
elif stripped == '// 测速成功,直接返回':
new_lines.append('\t\t\t// // 测速成功,直接返回')
i += 1
continue
elif stripped == 'return':
new_lines.append('\t\t\t// return')
i += 1
# Next should be the closing brace
if i < len(lines) and lines[i].strip() == '}':
new_lines.append('\t\t// }')
i += 1
in_private_call_block = False
continue
new_lines.append(line)
i += 1
content = '\n'.join(new_lines)
write_file(filepath, content)
print(f"✓ Modified {filepath}")
return result
def modify_speed_go(filepath):
"""
Remove privatespeedtest-related code from speed.go.
Uses line-by-line processing for reliability.
"""
content = read_file(filepath)
lines = content.split('\n')
# Remove specific code blocks by their comment markers
blocks_to_remove = [
'// formatString 格式化字符串到指定宽度',
'// printTableRow 打印表格行',
'// privateSpeedTest 使用 privatespeedtest 进行单个运营商测速',
'// privateSpeedTestWithFallback 使用私有测速,如果失败则回退到 global 节点',
'// 对于三网测速cmcc、cu、ct和 other优先使用 privatespeedtest 进行私有测速',
]
for block_marker in blocks_to_remove:
lines = remove_code_block(lines, block_marker)
# Reconstruct content
content = '\n'.join(lines)
# Remove privatespeedtest import
content = re.sub(
r'\n\s*"github\.com/oneclickvirt/privatespeedtest/pst"\s*\n',
'\n',
content,
flags=re.MULTILINE
)
# Remove time import (only used by privatespeedtest)
content = re.sub(
r'\n\s*"time"\s*\n',
'\n',
content,
flags=re.MULTILINE
)
# Clean up multiple consecutive empty lines (optional)
content = re.sub(r'\n{3,}', '\n\n', content)
write_file(filepath, content)
print(f"✓ Removed privatespeedtest from {filepath}")
def modify_utils_go(filepath):
"""
Modify utils/utils.go to:
@@ -188,31 +207,6 @@ def modify_params_go(filepath):
write_file(filepath, content)
print(f"✓ Modified {filepath}")
def modify_go_mod(filepath):
"""
Modify go.mod to remove security and privatespeedtest dependencies.
"""
content = read_file(filepath)
# Remove security dependency from require section
content = re.sub(
r'\s+github\.com/oneclickvirt/security v[^\n]+\n',
'',
content
)
# Remove privatespeedtest dependency from require section (including indirect)
content = re.sub(
r'\s+github\.com/oneclickvirt/privatespeedtest v[^\n]+\n',
'',
content
)
write_file(filepath, content)
print(f"✓ Modified {filepath}")
def modify_readme(filepath, is_english=False):
"""
Modify README files to update Go version and security status.
@@ -297,7 +291,7 @@ def main():
print("Modifying go.mod...")
modify_go_mod('go.mod')
print()
# Modify README files
print("Modifying README files...")
modify_readme('README.md', is_english=False)

View File

@@ -16,7 +16,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25.3'
go-version: '1.25.4'
- name: Update master branch README files
run: |
@@ -62,6 +62,9 @@ jobs:
- name: Remove security package references
run: |
python3 .back/create_public_branch.py
rm -f go.sum
go clean -modcache
go clean -cache -testcache -fuzzcache
go mod tidy
- name: Update Go version in README files
run: |

View File

@@ -60,7 +60,7 @@ Shell 版本:[https://github.com/spiritLHLS/ecs](https://github.com/spiritLHLS
- 邮件端口测试:[portchecker](https://github.com/oneclickvirt/portchecker)
- 上游及回程路由线路检测:借鉴 [zhanghanyun/backtrace](https://github.com/zhanghanyun/backtrace),二次开发至 [oneclickvirt/backtrace](https://github.com/oneclickvirt/backtrace)
- 三网路由测试:基于 [NTrace-core](https://github.com/nxtrace/NTrace-core),二次开发至 [nt3](https://github.com/oneclickvirt/nt3)
- 网速测试:基于 [speedtest.net](https://github.com/spiritLHLS/speedtest.net-CN-ID) 和 [speedtest.cn](https://github.com/spiritLHLS/speedtest.cn-CN-ID) 数据,开发 [oneclickvirt/speedtest](https://github.com/oneclickvirt/speedtest),同时融合私有国内测速平台
- 网速测试:基于 [speedtest.net](https://github.com/spiritLHLS/speedtest.net-CN-ID) 和 [speedtest.cn](https://github.com/spiritLHLS/speedtest.cn-CN-ID) 数据,开发 [oneclickvirt/speedtest](https://github.com/oneclickvirt/speedtest),同时融合私有国内测速节点
- 三网 Ping 值测试:借鉴 [ecsspeed](https://github.com/spiritLHLS/ecsspeed),二次开发至 [pingtest](https://github.com/oneclickvirt/pingtest)
- 支持root或admin环境下测试支持非root或非admin环境下测试支持离线环境下进行测试**暂未**支持无DNS的在线环境下进行测试
@@ -333,7 +333,7 @@ cd ecs
2. 安装 Go 环境(如已安装可跳过)
选择 go 1.25.3 的版本进行安装
选择 go 1.25.4 的版本进行安装
```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

@@ -330,7 +330,7 @@ cd ecs
2. Install Go environment (skip if already installed)
Select go 1.25.3 version to install
Select go 1.25.4 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

@@ -482,6 +482,8 @@ Abuser 或 Abuse 的滥用得分会直接影响机器的正常使用(中国境
先测的官方推荐的测速点然后测有代表性的国际测速点最后测国内三大运营商ping值最低的测速点。
由于 speedtest.net 和 speedtest.cn 平台公开的测速节点被刷BTPT的刷烂了(他们为了对等上传PCDN的流量狂刷下载)所以这块本人独家融合的境内私有测速节点不再公开优先使用私有的境内运营商测速节点进行测速且写死限制每个IP每日仅支持获取测速数据10次超限自动降级为使用公共测速节点进行测速
境内使用为主就看境内测速即可,境外使用看境外测速,官方测速点可以代表受测的宿主机本地带宽基准。
一般来说中国境外的服务器的带宽100Mbps起步中国境内的服务器1Mbps带宽起步具体看线路优劣带宽特别大有时候未必用得上够用就行了。

View File

@@ -27,7 +27,7 @@ import (
)
var (
ecsVersion = "v0.1.108" // 融合怪版本号
ecsVersion = "v0.1.109" // 融合怪版本号
configs = params.NewConfig(ecsVersion) // 全局配置实例
userSetFlags = make(map[string]bool) // 用于跟踪哪些参数是用户显式设置的
)

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.107"
ECS_VERSION="0.1.108"
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.107"
ECS_VERSION="0.1.107"
_yellow "Unable to get version info, using default version 0.1.108"
ECS_VERSION="0.1.108"
fi
version_output=""
for cmd_path in "goecs" "./goecs" "/usr/bin/goecs" "/usr/local/bin/goecs"; do

View File

@@ -348,7 +348,17 @@ func RunSpeedTests(config *params.Config, output, tempOutput string, outputMutex
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)
// 中文模式:就近测速 + 三网各1个 + Other 1个带回退
if config.Language == "zh" {
tests.NearbySP()
tests.CustomSP("net", "other", 1, config.Language)
tests.CustomSP("net", "cu", 1, config.Language)
tests.CustomSP("net", "ct", 1, config.Language)
tests.CustomSP("net", "cmcc", 1, config.Language)
} else {
// 英文模式保持原有逻辑测4个global节点
tests.CustomSP("net", "global", 4, config.Language)
}
} else if config.Choice == "6" {
tests.CustomSP("net", "global", 11, config.Language)
}
@@ -442,7 +452,7 @@ func HandleSignalInterrupt(sig chan os.Signal, config *params.Config, startTime
// 使用context来控制上传goroutine
uploadCtx, uploadCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer uploadCancel()
go func() {
httpURL, httpsURL := utils.ProcessAndUpload(finalOutput, config.FilePath, config.EnableUpload)
select {
@@ -455,7 +465,7 @@ func HandleSignalInterrupt(sig chan os.Signal, config *params.Config, startTime
return
}
}()
select {
case result := <-resultChan:
uploadCancel() // 成功完成取消context

View File

@@ -69,7 +69,7 @@ func printTableRow(result pst.SpeedTestResult) {
}
latency := fmt.Sprintf("%.2f ms", result.PingLatency.Seconds()*1000)
packetLoss := "N/A"
fmt.Print(formatString(location, 16))
fmt.Print(formatString(location, 15))
fmt.Print(formatString(upload, 16))
fmt.Print(formatString(download, 16))
fmt.Print(formatString(latency, 16))
@@ -78,25 +78,26 @@ func printTableRow(result pst.SpeedTestResult) {
}
// privateSpeedTest 使用 privatespeedtest 进行单个运营商测速
// operator 参数:只支持 "cmcc"、"cu"、"ct"
func privateSpeedTest(num int, operator string) error {
// operator 参数:只支持 "cmcc"、"cu"、"ct"、"other"
// 返回值:实际测试的节点数量和错误信息
func privateSpeedTest(num int, operator string) (int, error) {
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "[WARN] privateSpeedTest panic: %v\n", r)
}
}()
*pst.NoProgress = true
*pst.Quiet = true
*pst.NoHeader = true
*pst.NoProjectURL = true
*pst.Quiet = true
*pst.NoHeader = true
*pst.NoProjectURL = true
// 加载服务器列表
serverList, err := pst.LoadServerList()
if err != nil {
return fmt.Errorf("加载自定义服务器列表失败")
return 0, fmt.Errorf("加载自定义服务器列表失败")
}
// 使用三网测速模式(每个运营商选择指定数量的最低延迟节点)
serversPerISP := num
if serversPerISP <= 0 || serversPerISP > 5{
if serversPerISP <= 0 || serversPerISP > 5 {
serversPerISP = 2
}
// 单个运营商测速:先过滤服务器列表
@@ -108,8 +109,10 @@ func privateSpeedTest(num int, operator string) error {
carrierType = "Unicom"
case "ct":
carrierType = "Telecom"
case "other":
carrierType = "Other"
default:
return fmt.Errorf("不支持的运营商类型: %s", operator)
return 0, fmt.Errorf("不支持的运营商类型: %s", operator)
}
// 过滤出指定运营商的服务器
filteredServers := pst.FilterServersByISP(serverList.Servers, carrierType)
@@ -121,13 +124,13 @@ func privateSpeedTest(num int, operator string) error {
// 使用 FindBestServers 选择最佳服务器
candidateServers, err := pst.FindBestServers(
filteredServers,
candidateCount, // 选择更多候选节点用于去重
5*time.Second, // ping 超时
true, // 显示进度条
true, // 静默
candidateCount, // 选择更多候选节点用于去重
5*time.Second, // ping 超时
true, // 显示进度条
true, // 静默
)
if err != nil {
return fmt.Errorf("分组查找失败")
return 0, fmt.Errorf("分组查找失败")
}
// 去重:确保同一运营商内城市不重复
seenCities := make(map[string]bool)
@@ -147,18 +150,18 @@ func privateSpeedTest(num int, operator string) error {
}
}
if len(bestServers) == 0 {
return fmt.Errorf("去重后没有可用的服务器")
return 0, fmt.Errorf("去重后没有可用的服务器")
}
// 执行测速并逐个打印结果(不打印表头)
for i, serverInfo := range bestServers {
result := pst.RunSpeedTest(
serverInfo.Server,
false, // 不禁用下载测试
false, // 不禁用上传测试
6, // 并发线程数
12*time.Second, // 超时时间
false, // 不禁用下载测试
false, // 不禁用上传测试
6, // 并发线程数
12*time.Second, // 超时时间
&serverInfo,
false, // 不显示进度条
false, // 不显示进度条
)
if result.Success {
printTableRow(result)
@@ -168,7 +171,31 @@ func privateSpeedTest(num int, operator string) error {
time.Sleep(1 * time.Second)
}
}
return nil
// 返回实际测试的节点数量
return len(bestServers), nil
}
// privateSpeedTestWithFallback 使用私有测速,如果失败则回退到 global 节点
// 主要用于 Other 类型的测速
func privateSpeedTestWithFallback(num int, operator, language string) {
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "[WARN] privateSpeedTestWithFallback panic: %v\n", r)
}
}()
// 先尝试私有节点测速
testedCount, err := privateSpeedTest(num, operator)
if err != nil || testedCount == 0 {
// 私有节点失败,回退到 global 节点
var url, parseType string
url = model.NetGlobal
parseType = "id"
if runtime.GOOS == "windows" || sp.OfficialAvailableTest() != nil {
sp.CustomSpeedTest(url, parseType, num, language)
} else {
sp.OfficialCustomSpeedTest(url, parseType, num, language)
}
}
}
func CustomSP(platform, operator string, num int, language string) {
@@ -177,18 +204,26 @@ func CustomSP(platform, operator string, num int, language string) {
fmt.Fprintf(os.Stderr, "[WARN] CustomSP panic: %v\n", r)
}
}()
// 对于三网测速cmcc、cu、ct优先使用 privatespeedtest 进行私有测速
// 对于三网测速cmcc、cu、ct和 other,优先使用 privatespeedtest 进行私有测速
opLower := strings.ToLower(operator)
if opLower == "cmcc" || opLower == "cu" || opLower == "ct" {
err := privateSpeedTest(num, opLower)
if opLower == "cmcc" || opLower == "cu" || opLower == "ct" || opLower == "other" {
testedCount, err := privateSpeedTest(num, opLower)
if err != nil {
fmt.Fprintf(os.Stderr, "[WARN] privatespeedtest failed\n")
// 继续使用原有的兜底方案
} else {
// 测速成功,直接返回
fmt.Fprintf(os.Stderr, "[WARN] privatespeedtest failed: %v\n", err)
// 全部失败,继续使用原有的公共节点兜底方案
} else if testedCount >= num {
// 私有节点测速成功且数量达标,直接返回
return
} else if testedCount > 0 {
// 部分私有节点测速成功,但数量不足,用公共节点补充
fmt.Fprintf(os.Stderr, "[INFO] 私有节点仅测试了 %d 个,补充 %d 个公共节点\n", testedCount, num-testedCount)
num = num - testedCount // 只测剩余数量的公共节点
// 继续执行下面的公共节点测速逻辑
} else {
// testedCount == 0继续使用公共节点
}
}
var url, parseType string
if strings.ToLower(platform) == "cn" {
if strings.ToLower(operator) == "cmcc" {
@@ -222,7 +257,8 @@ func CustomSP(platform, operator string, num int, language string) {
url = model.NetJP
} else if strings.ToLower(operator) == "sg" {
url = model.NetSG
} else if strings.ToLower(operator) == "global" {
} else if strings.ToLower(operator) == "global" || strings.ToLower(operator) == "other" {
// other 类型回退到 global 节点
url = model.NetGlobal
}
parseType = "id"