feat: Add a new cleaner page for managing and automatically cleaning error S2A accounts, supported by new backend services for logging, authentication, and client operations.

This commit is contained in:
2026-02-02 03:27:33 +08:00
parent d05c19a8d5
commit daf4c68e4f
10 changed files with 704 additions and 506 deletions

View File

@@ -0,0 +1,161 @@
package client
import (
"math/rand"
"github.com/bogdanfinn/tls-client/profiles"
)
// ============================================================
// 浏览器指纹配置文件
// 整合 tls-client 高成功率指纹
// ============================================================
// BrowserFingerprint 浏览器指纹完整配置
type BrowserFingerprint struct {
Browser string
Version string
Platform string
Mobile bool
TLSProfile profiles.ClientProfile
}
// ============================================================
// Firefox 指纹池 (100% 成功率)
// ============================================================
var firefoxFingerprints = []BrowserFingerprint{
// 最新版本
{Browser: "firefox", Version: "135", Platform: "Windows", TLSProfile: profiles.Firefox_135},
{Browser: "firefox", Version: "135", Platform: "macOS", TLSProfile: profiles.Firefox_135},
{Browser: "firefox", Version: "135", Platform: "Linux", TLSProfile: profiles.Firefox_135},
{Browser: "firefox", Version: "133", Platform: "Windows", TLSProfile: profiles.Firefox_133},
{Browser: "firefox", Version: "133", Platform: "macOS", TLSProfile: profiles.Firefox_133},
{Browser: "firefox", Version: "133", Platform: "Linux", TLSProfile: profiles.Firefox_133},
{Browser: "firefox", Version: "132", Platform: "Windows", TLSProfile: profiles.Firefox_132},
{Browser: "firefox", Version: "132", Platform: "macOS", TLSProfile: profiles.Firefox_132},
{Browser: "firefox", Version: "123", Platform: "Windows", TLSProfile: profiles.Firefox_123},
{Browser: "firefox", Version: "120", Platform: "Windows", TLSProfile: profiles.Firefox_120},
{Browser: "firefox", Version: "120", Platform: "macOS", TLSProfile: profiles.Firefox_120},
{Browser: "firefox", Version: "117", Platform: "Windows", TLSProfile: profiles.Firefox_117},
{Browser: "firefox", Version: "110", Platform: "Windows", TLSProfile: profiles.Firefox_110},
{Browser: "firefox", Version: "108", Platform: "Windows", TLSProfile: profiles.Firefox_108},
{Browser: "firefox", Version: "106", Platform: "Windows", TLSProfile: profiles.Firefox_106},
{Browser: "firefox", Version: "105", Platform: "Windows", TLSProfile: profiles.Firefox_105},
{Browser: "firefox", Version: "104", Platform: "Windows", TLSProfile: profiles.Firefox_104},
{Browser: "firefox", Version: "102", Platform: "Windows", TLSProfile: profiles.Firefox_102},
}
// ============================================================
// Safari 指纹池 (100% 成功率)
// ============================================================
var safariFingerprints = []BrowserFingerprint{
// macOS Safari
{Browser: "safari", Version: "16.0", Platform: "macOS", TLSProfile: profiles.Safari_16_0},
{Browser: "safari", Version: "15.6.1", Platform: "macOS", TLSProfile: profiles.Safari_15_6_1},
// iOS Safari
{Browser: "safari", Version: "18.5", Platform: "iOS", Mobile: true, TLSProfile: profiles.Safari_IOS_18_5},
{Browser: "safari", Version: "18.0", Platform: "iOS", Mobile: true, TLSProfile: profiles.Safari_IOS_18_0},
{Browser: "safari", Version: "17.0", Platform: "iOS", Mobile: true, TLSProfile: profiles.Safari_IOS_17_0},
{Browser: "safari", Version: "16.0", Platform: "iOS", Mobile: true, TLSProfile: profiles.Safari_IOS_16_0},
{Browser: "safari", Version: "15.6", Platform: "iOS", Mobile: true, TLSProfile: profiles.Safari_IOS_15_6},
{Browser: "safari", Version: "15.5", Platform: "iOS", Mobile: true, TLSProfile: profiles.Safari_IOS_15_5},
// iPadOS Safari
{Browser: "safari", Version: "15.6", Platform: "iPadOS", Mobile: true, TLSProfile: profiles.Safari_Ipad_15_6},
}
// ============================================================
// Opera 指纹池 (高成功率)
// ============================================================
var operaFingerprints = []BrowserFingerprint{
{Browser: "opera", Version: "91", Platform: "Windows", TLSProfile: profiles.Opera_91},
{Browser: "opera", Version: "90", Platform: "Windows", TLSProfile: profiles.Opera_90},
{Browser: "opera", Version: "89", Platform: "Windows", TLSProfile: profiles.Opera_89},
}
// ============================================================
// OkHttp 指纹池 (100% 成功率 - Android 原生)
// ============================================================
var okhttpFingerprints = []BrowserFingerprint{
{Browser: "okhttp", Version: "13", Platform: "Android", Mobile: true, TLSProfile: profiles.Okhttp4Android13},
{Browser: "okhttp", Version: "12", Platform: "Android", Mobile: true, TLSProfile: profiles.Okhttp4Android12},
{Browser: "okhttp", Version: "11", Platform: "Android", Mobile: true, TLSProfile: profiles.Okhttp4Android11},
{Browser: "okhttp", Version: "10", Platform: "Android", Mobile: true, TLSProfile: profiles.Okhttp4Android10},
{Browser: "okhttp", Version: "9", Platform: "Android", Mobile: true, TLSProfile: profiles.Okhttp4Android9},
{Browser: "okhttp", Version: "8", Platform: "Android", Mobile: true, TLSProfile: profiles.Okhttp4Android8},
{Browser: "okhttp", Version: "7", Platform: "Android", Mobile: true, TLSProfile: profiles.Okhttp4Android7},
}
// ============================================================
// Chrome 指纹池 (测试通过的版本)
// 注意: 新版 Chrome (120+) 在 tls-client 中被检测,只保留旧版本
// ============================================================
var chromeFingerprints = []BrowserFingerprint{
// 只保留测试通过的旧版本
{Browser: "chrome", Version: "112", Platform: "Windows", TLSProfile: profiles.Chrome_112},
{Browser: "chrome", Version: "111", Platform: "Windows", TLSProfile: profiles.Chrome_111},
}
// ============================================================
// 合并所有指纹
// ============================================================
var allFingerprints []BrowserFingerprint
func init() {
allFingerprints = make([]BrowserFingerprint, 0, 100)
// Firefox (高优先级,多份)
allFingerprints = append(allFingerprints, firefoxFingerprints...)
allFingerprints = append(allFingerprints, firefoxFingerprints...)
// Safari (高成功率)
allFingerprints = append(allFingerprints, safariFingerprints...)
// Opera
allFingerprints = append(allFingerprints, operaFingerprints...)
// OkHttp (Android)
allFingerprints = append(allFingerprints, okhttpFingerprints...)
// Chrome (低优先级)
allFingerprints = append(allFingerprints, chromeFingerprints...)
}
// GetRandomFingerprint 获取随机指纹
func GetRandomFingerprint() BrowserFingerprint {
return allFingerprints[rand.Intn(len(allFingerprints))]
}
// GetRandomDesktopFingerprint 获取随机桌面端指纹
func GetRandomDesktopFingerprint() BrowserFingerprint {
desktopFps := make([]BrowserFingerprint, 0)
for _, fp := range allFingerprints {
if !fp.Mobile {
desktopFps = append(desktopFps, fp)
}
}
if len(desktopFps) == 0 {
return allFingerprints[rand.Intn(len(allFingerprints))]
}
return desktopFps[rand.Intn(len(desktopFps))]
}
// GetTotalFingerprintCount 获取总指纹数量
func GetTotalFingerprintCount() int {
return len(allFingerprints)
}
// GetUniqueFingerprintCount 获取去重后的指纹数量
func GetUniqueFingerprintCount() int {
unique := make(map[string]bool)
for _, fp := range allFingerprints {
key := fp.Browser + fp.Version + fp.Platform
unique[key] = true
}
return len(unique)
}

View File

@@ -3,6 +3,7 @@ package client
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"math/rand"
"net/http"
@@ -14,35 +15,46 @@ import (
"github.com/andybalholm/brotli"
http2 "github.com/bogdanfinn/fhttp"
tls_client "github.com/bogdanfinn/tls-client"
"github.com/bogdanfinn/tls-client/profiles"
)
// TLSClient 使用 tls-client 模拟浏览器指纹的 HTTP 客户端
type TLSClient struct {
client tls_client.HttpClient
userAgent string
chromeVer string
acceptLang string
client tls_client.HttpClient
fingerprint BrowserFingerprint
userAgent string
acceptLang string
}
// 语言偏好池
var languagePrefs = []string{
"en-US,en;q=0.9",
"en-GB,en;q=0.9,en-US;q=0.8",
"en-US,en;q=0.9,de;q=0.8",
"de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7",
"en-US,en;q=0.9,fr;q=0.8",
"en-US,en;q=0.9,es;q=0.8",
"fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7",
"es-ES,es;q=0.9,en-US;q=0.8,en;q=0.7",
}
// New 创建一个新的 TLS 客户端
// New 创建一个新的 TLS 客户端(使用随机指纹)
func New(proxyStr string) (*TLSClient, error) {
// 获取随机桌面端指纹
fp := GetRandomDesktopFingerprint()
return NewWithFingerprint(fp, proxyStr)
}
// NewWithFingerprint 使用指定指纹创建客户端
func NewWithFingerprint(fp BrowserFingerprint, proxyStr string) (*TLSClient, error) {
jar := tls_client.NewCookieJar()
chromeVer := "133"
options := []tls_client.HttpClientOption{
tls_client.WithTimeoutSeconds(60),
tls_client.WithClientProfile(profiles.Chrome_133),
tls_client.WithTimeoutSeconds(90),
tls_client.WithClientProfile(fp.TLSProfile),
tls_client.WithRandomTLSExtensionOrder(),
tls_client.WithCookieJar(jar),
tls_client.WithInsecureSkipVerify(),
tls_client.WithNotFollowRedirects(),
}
if proxyStr != "" {
@@ -59,46 +71,162 @@ func New(proxyStr string) (*TLSClient, error) {
}
acceptLang := languagePrefs[rand.Intn(len(languagePrefs))]
userAgent := generateUserAgent(chromeVer)
userAgent := generateUserAgent(fp)
return &TLSClient{
client: client,
userAgent: userAgent,
chromeVer: chromeVer,
acceptLang: acceptLang,
client: client,
fingerprint: fp,
userAgent: userAgent,
acceptLang: acceptLang,
}, nil
}
// generateUserAgent 生成随机化的 User-Agent
func generateUserAgent(chromeVer string) string {
winVersions := []string{
"Windows NT 10.0; Win64; x64",
"Windows NT 10.0; WOW64",
}
winVer := winVersions[rand.Intn(len(winVersions))]
// generateUserAgent 根据指纹生成 User-Agent
func generateUserAgent(fp BrowserFingerprint) string {
winVersions := []string{"Windows NT 10.0; Win64; x64", "Windows NT 11.0; Win64; x64"}
macVersions := []string{"10_15_7", "11_0_0", "12_0_0", "13_0_0", "14_0", "14_5", "15_0", "15_2"}
linuxVersions := []string{"X11; Linux x86_64", "X11; Ubuntu; Linux x86_64", "X11; Fedora; Linux x86_64"}
return "Mozilla/5.0 (" + winVer + ") AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + chromeVer + ".0.0.0 Safari/537.36"
version := fp.Version
switch fp.Browser {
case "chrome":
switch fp.Platform {
case "Windows":
return fmt.Sprintf("Mozilla/5.0 (%s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s.0.0.0 Safari/537.36",
winVersions[rand.Intn(len(winVersions))], version)
case "macOS":
return fmt.Sprintf("Mozilla/5.0 (Macintosh; Intel Mac OS X %s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s.0.0.0 Safari/537.36",
macVersions[rand.Intn(len(macVersions))], version)
case "Linux":
return fmt.Sprintf("Mozilla/5.0 (%s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s.0.0.0 Safari/537.36",
linuxVersions[rand.Intn(len(linuxVersions))], version)
case "Android":
return fmt.Sprintf("Mozilla/5.0 (Linux; Android 14; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s.0.0.0 Mobile Safari/537.36", version)
}
case "firefox":
switch fp.Platform {
case "Windows":
return fmt.Sprintf("Mozilla/5.0 (%s; rv:%s.0) Gecko/20100101 Firefox/%s.0",
winVersions[rand.Intn(len(winVersions))], version, version)
case "macOS":
return fmt.Sprintf("Mozilla/5.0 (Macintosh; Intel Mac OS X %s; rv:%s.0) Gecko/20100101 Firefox/%s.0",
macVersions[rand.Intn(len(macVersions))], version, version)
case "Linux":
return fmt.Sprintf("Mozilla/5.0 (%s; rv:%s.0) Gecko/20100101 Firefox/%s.0",
linuxVersions[rand.Intn(len(linuxVersions))], version, version)
}
case "safari":
if fp.Mobile {
iosVersions := map[string]string{"18.5": "18_5", "18.0": "18_0", "17.0": "17_0", "16.0": "16_0", "15.6": "15_6", "15.5": "15_5"}
iosVer := iosVersions[version]
if iosVer == "" {
iosVer = "18_0"
}
if fp.Platform == "iPadOS" {
return fmt.Sprintf("Mozilla/5.0 (iPad; CPU OS %s like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/%s Mobile/15E148 Safari/604.1", iosVer, version)
}
return fmt.Sprintf("Mozilla/5.0 (iPhone; CPU iPhone OS %s like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/%s Mobile/15E148 Safari/604.1", iosVer, version)
}
return fmt.Sprintf("Mozilla/5.0 (Macintosh; Intel Mac OS X %s) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/%s Safari/605.1.15",
macVersions[rand.Intn(len(macVersions))], version)
case "opera":
chromeVer := map[string]string{"91": "118", "90": "117", "89": "116"}[version]
if chromeVer == "" {
chromeVer = "118"
}
return fmt.Sprintf("Mozilla/5.0 (%s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s.0.0.0 Safari/537.36 OPR/%s.0.0.0",
winVersions[rand.Intn(len(winVersions))], chromeVer, version)
case "okhttp":
return "okhttp/4.12.0"
}
// 默认 Chrome
return fmt.Sprintf("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s.0.0.0 Safari/537.36", version)
}
// getDefaultHeaders 获取默认请求头
// getDefaultHeaders 获取默认请求头(根据浏览器类型返回不同的头)
func (c *TLSClient) getDefaultHeaders() map[string]string {
secChUa := `"Chromium";v="` + c.chromeVer + `", "Not(A:Brand";v="99", "Google Chrome";v="` + c.chromeVer + `"`
return map[string]string{
"User-Agent": c.userAgent,
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Language": c.acceptLang,
"Accept-Encoding": "gzip, deflate, br, zstd",
"Cache-Control": "max-age=0",
"Sec-Ch-Ua": secChUa,
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": `"Windows"`,
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
headers := map[string]string{
"User-Agent": c.userAgent,
"Accept-Language": c.acceptLang,
"Accept-Encoding": "gzip, deflate, br",
}
fp := c.fingerprint
switch fp.Browser {
case "firefox":
headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
headers["Upgrade-Insecure-Requests"] = "1"
headers["Sec-Fetch-Dest"] = "document"
headers["Sec-Fetch-Mode"] = "navigate"
headers["Sec-Fetch-Site"] = "none"
headers["Sec-Fetch-User"] = "?1"
headers["DNT"] = "1"
case "safari":
headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
case "okhttp":
headers["Accept"] = "*/*"
headers["Accept-Encoding"] = "gzip"
default: // chrome, opera
secChUa := c.generateSecChUa()
headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
headers["Cache-Control"] = "max-age=0"
headers["Sec-Ch-Ua"] = secChUa
headers["Sec-Ch-Ua-Mobile"] = "?0"
if fp.Mobile {
headers["Sec-Ch-Ua-Mobile"] = "?1"
}
headers["Sec-Ch-Ua-Platform"] = c.getPlatformHeader()
headers["Sec-Fetch-Dest"] = "document"
headers["Sec-Fetch-Mode"] = "navigate"
headers["Sec-Fetch-Site"] = "none"
headers["Sec-Fetch-User"] = "?1"
headers["Upgrade-Insecure-Requests"] = "1"
}
return headers
}
// generateSecChUa 生成 Sec-Ch-Ua 头
func (c *TLSClient) generateSecChUa() string {
ver := c.fingerprint.Version
switch c.fingerprint.Browser {
case "opera":
return fmt.Sprintf(`"Opera";v="%s", "Chromium";v="118", "Not(A:Brand";v="99"`, ver)
default:
notABrands := []string{`"Not(A:Brand";v="99"`, `"Not A(Brand";v="99"`, `"Not/A)Brand";v="99"`}
return fmt.Sprintf(`"Chromium";v="%s", %s, "Google Chrome";v="%s"`, ver, notABrands[rand.Intn(len(notABrands))], ver)
}
}
// getPlatformHeader 获取平台头
func (c *TLSClient) getPlatformHeader() string {
switch c.fingerprint.Platform {
case "macOS":
return `"macOS"`
case "Linux":
return `"Linux"`
case "iOS", "iPadOS":
return `"iOS"`
case "Android":
return `"Android"`
default:
return `"Windows"`
}
}
// GetFingerprintInfo 获取指纹信息字符串(用于日志输出)
func (c *TLSClient) GetFingerprintInfo() string {
fp := c.fingerprint
return fmt.Sprintf("%s/%s (%s)", fp.Browser, fp.Version, fp.Platform)
}
// Do 执行 HTTP 请求