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:
@@ -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 请求
|
||||
|
||||
Reference in New Issue
Block a user