package main import ( "encoding/json" "math/rand" "os" "path/filepath" "sync" ) type Profile struct { UserAgent string SecChUa string SecChUaMobile string SecChUaPlatform string } // SavedProfile is the captured browser fingerprint persisted after a manual // captcha session. Reused for subsequent auto-solve attempts so VK sees a // consistent (browser_fp, device, UA) triple rather than a freshly-generated one. type SavedProfile struct { Profile DeviceJSON string BrowserFp string } const profileFileName = "vk_profile.json" var ( profilePathOnce sync.Once profilePathVal string ) // profileFilePath returns a writeable absolute path for the cached browser // profile. Order: $VK_PROFILE_PATH, os.UserCacheDir(), os.TempDir(), CWD. // CWD is last because on Android it's the read-only APK lib dir. func profileFilePath() string { profilePathOnce.Do(func() { if p := os.Getenv("VK_PROFILE_PATH"); p != "" { profilePathVal = p return } if dir, err := os.UserCacheDir(); err == nil { sub := filepath.Join(dir, "vk-turn-proxy") if mkErr := os.MkdirAll(sub, 0o755); mkErr == nil { profilePathVal = filepath.Join(sub, profileFileName) return } } if tmp := os.TempDir(); tmp != "" { profilePathVal = filepath.Join(tmp, profileFileName) return } profilePathVal = profileFileName }) return profilePathVal } func LoadProfileFromDisk() (*SavedProfile, error) { data, err := os.ReadFile(profileFilePath()) if err != nil { return nil, err } var sp SavedProfile if err := json.Unmarshal(data, &sp); err != nil { return nil, err } return &sp, nil } func SaveProfileToDisk(sp SavedProfile) error { data, err := json.MarshalIndent(sp, "", " ") if err != nil { return err } return os.WriteFile(profileFilePath(), data, 0o644) } // profile contains paired User-Agent and Client Hints strings to harden bot detection. var profile = []Profile{ // Windows Chrome { UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36", SecChUa: `"Chromium";v="146", "Not-A.Brand";v="24", "Google Chrome";v="146"`, SecChUaMobile: "?0", SecChUaPlatform: `"Windows"`, }, { UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36", SecChUa: `"Chromium";v="145", "Not-A.Brand";v="99", "Google Chrome";v="145"`, SecChUaMobile: "?0", SecChUaPlatform: `"Windows"`, }, { UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36", SecChUa: `"Chromium";v="144", "Not-A.Brand";v="8", "Google Chrome";v="144"`, SecChUaMobile: "?0", SecChUaPlatform: `"Windows"`, }, // Windows Edge { UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0", SecChUa: `"Chromium";v="146", "Not-A.Brand";v="24", "Microsoft Edge";v="146"`, SecChUaMobile: "?0", SecChUaPlatform: `"Windows"`, }, { UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0", SecChUa: `"Chromium";v="145", "Not-A.Brand";v="99", "Microsoft Edge";v="145"`, SecChUaMobile: "?0", SecChUaPlatform: `"Windows"`, }, // macOS Chrome { UserAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36", SecChUa: `"Chromium";v="146", "Not-A.Brand";v="24", "Google Chrome";v="146"`, SecChUaMobile: "?0", SecChUaPlatform: `"macOS"`, }, { UserAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36", SecChUa: `"Chromium";v="145", "Not-A.Brand";v="99", "Google Chrome";v="145"`, SecChUaMobile: "?0", SecChUaPlatform: `"macOS"`, }, // Linux Chrome { UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36", SecChUa: `"Chromium";v="146", "Not-A.Brand";v="24", "Google Chrome";v="146"`, SecChUaMobile: "?0", SecChUaPlatform: `"Linux"`, }, { UserAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36", SecChUa: `"Chromium";v="144", "Not-A.Brand";v="8", "Google Chrome";v="144"`, SecChUaMobile: "?0", SecChUaPlatform: `"Linux"`, }, } // getRandomProfile returns a paired User-Agent and Client Hints profile. func getRandomProfile() Profile { return profile[rand.Intn(len(profile))] }