package register import ( "bytes" "encoding/json" "fmt" "math/rand" "net/http" "net/url" "strings" "time" "codex-pool/internal/client" "codex-pool/internal/logger" "codex-pool/internal/mail" ) // ChatGPTReg ChatGPT 注册器 type ChatGPTReg struct { Proxy string Client *client.TLSClient AuthSessionLoggingID string OAIDid string CSRFToken string AuthorizeURL string AccessToken string } // Result 注册结果 type Result struct { Email string `json:"email"` Password string `json:"password"` Name string `json:"name"` AccessToken string `json:"access_token"` } // New 创建注册器 func New(proxy string) (*ChatGPTReg, error) { c, err := client.New(proxy) if err != nil { return nil, err } return &ChatGPTReg{ Proxy: proxy, Client: c, AuthSessionLoggingID: GenerateUUID(), }, nil } // InitSession 初始化会话 func (r *ChatGPTReg) InitSession() error { resp, err := r.Client.Get("https://chatgpt.com") if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { return fmt.Errorf("初始化失败,状态码: %d", resp.StatusCode) } r.OAIDid = r.Client.GetCookie("https://chatgpt.com", "oai-did") csrfCookie := r.Client.GetCookie("https://chatgpt.com", "__Host-next-auth.csrf-token") if csrfCookie != "" { decoded, err := url.QueryUnescape(csrfCookie) if err == nil { parts := strings.Split(decoded, "|") if len(parts) > 0 { r.CSRFToken = parts[0] } } } if r.CSRFToken == "" { return fmt.Errorf("无法获取 CSRF token") } loginURL := fmt.Sprintf("https://chatgpt.com/auth/login?openaicom-did=%s", r.OAIDid) loginResp, err := r.Client.Get(loginURL) if err != nil { return err } defer loginResp.Body.Close() return nil } // GetAuthorizeURL 获取授权 URL func (r *ChatGPTReg) GetAuthorizeURL(email string) error { loginURL := fmt.Sprintf( "https://chatgpt.com/api/auth/signin/openai?prompt=login&ext-oai-did=%s&auth_session_logging_id=%s&screen_hint=login_or_signup&login_hint=%s", r.OAIDid, r.AuthSessionLoggingID, url.QueryEscape(email), ) data := url.Values{} data.Set("callbackUrl", "https://chatgpt.com/") data.Set("csrfToken", r.CSRFToken) data.Set("json", "true") req, err := http.NewRequest("POST", loginURL, strings.NewReader(data.Encode())) if err != nil { return err } req.Header.Set("Origin", "https://chatgpt.com") req.Header.Set("Content-Type", "application/x-www-form-urlencoded") resp, err := r.Client.Do(req) if err != nil { return err } defer resp.Body.Close() var result map[string]interface{} if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return err } if authURL, ok := result["url"].(string); ok && strings.Contains(authURL, "auth.openai.com") { r.AuthorizeURL = authURL return nil } return fmt.Errorf("无法获取授权 URL") } // StartAuthorize 开始授权流程 func (r *ChatGPTReg) StartAuthorize() error { resp, err := r.Client.Get(r.AuthorizeURL) if err != nil { return err } defer resp.Body.Close() finalURL := resp.Request.URL.String() if strings.Contains(finalURL, "create-account") || strings.Contains(finalURL, "log-in") { return nil } return fmt.Errorf("授权流程启动失败") } // Register 注册账户 func (r *ChatGPTReg) Register(email, password string) error { payload := map[string]string{ "password": password, "username": email, } jsonData, _ := json.Marshal(payload) req, err := http.NewRequest("POST", "https://auth.openai.com/api/accounts/user/register", bytes.NewReader(jsonData)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Origin", "https://auth.openai.com") resp, err := r.Client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { body, _ := client.ReadBodyString(resp) return fmt.Errorf("注册失败,状态码: %d, 响应: %s", resp.StatusCode, truncateStr(body, 200)) } return nil } // SendVerificationEmail 发送验证邮件 func (r *ChatGPTReg) SendVerificationEmail() error { resp, err := r.Client.Get("https://auth.openai.com/api/accounts/email-otp/send") if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { return fmt.Errorf("发送验证邮件失败,状态码: %d", resp.StatusCode) } return nil } // ValidateOTP 验证 OTP func (r *ChatGPTReg) ValidateOTP(code string) error { payload := map[string]string{"code": code} jsonData, _ := json.Marshal(payload) req, err := http.NewRequest("POST", "https://auth.openai.com/api/accounts/email-otp/validate", bytes.NewReader(jsonData)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Origin", "https://auth.openai.com") resp, err := r.Client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { return fmt.Errorf("OTP 验证失败,状态码: %d", resp.StatusCode) } return nil } // CreateAccount 创建账户 func (r *ChatGPTReg) CreateAccount(name, birthdate string) error { payload := map[string]string{ "name": name, "birthdate": birthdate, } jsonData, _ := json.Marshal(payload) req, err := http.NewRequest("POST", "https://auth.openai.com/api/accounts/create_account", bytes.NewReader(jsonData)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Origin", "https://auth.openai.com") resp, err := r.Client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { return fmt.Errorf("创建账户失败,状态码: %d", resp.StatusCode) } // 处理可能出现的继续 URL var result map[string]interface{} if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { if continueURL, ok := result["continue_url"].(string); ok && continueURL != "" { contResp, err := r.Client.Get(continueURL) if err == nil { contResp.Body.Close() } } } return nil } // GetSessionToken 获取 access token func (r *ChatGPTReg) GetSessionToken() error { resp, err := r.Client.Get("https://chatgpt.com/api/auth/session") if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { return fmt.Errorf("获取 session 失败,状态码: %d", resp.StatusCode) } var result map[string]interface{} if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return err } if token, ok := result["accessToken"].(string); ok { r.AccessToken = token return nil } return fmt.Errorf("响应中没有 accessToken") } // Run 执行单次注册(由 main.go 调用) func Run(email, password, realName, birthdate, proxy string) (*ChatGPTReg, error) { return APIRegister(email, password, realName, birthdate, proxy, "[RegTest]") } // APIRegister 使用 API 完成注册 (集成 403 重试机制,全局 5 分钟超时) func APIRegister(email, password, realName, birthdate, proxy string, logPrefix string) (*ChatGPTReg, error) { type regResult struct { reg *ChatGPTReg err error } resultCh := make(chan regResult, 1) go func() { reg, err := apiRegisterInternal(email, password, realName, birthdate, proxy, logPrefix) resultCh <- regResult{reg, err} }() select { case r := <-resultCh: return r.reg, r.err case <-time.After(5 * time.Minute): return nil, fmt.Errorf("注册超时 (5分钟)") } } // apiRegisterInternal APIRegister 的内部实现 func apiRegisterInternal(email, password, realName, birthdate, proxy string, logPrefix string) (*ChatGPTReg, error) { var reg *ChatGPTReg var lastErr error // 403 重试机制 - 最多重试 3 次,每次换新指纹 for retry := 0; retry < 3; retry++ { var err error reg, err = New(proxy) if err != nil { lastErr = err if retry < 2 { logger.Warning(fmt.Sprintf("%s 客户端创建失败,重试 %d/3...", logPrefix, retry+1), email, "register") continue } return nil, fmt.Errorf("客户端创建失败: %v", err) } fpInfo := reg.Client.GetFingerprintInfo() if retry == 0 { logger.Status(fmt.Sprintf("%s 初始化会话... [%s]", logPrefix, fpInfo), email, "register") } else { logger.Warning(fmt.Sprintf("%s 403 重试 %d/3,换指纹 [%s]", logPrefix, retry, fpInfo), email, "register") } if err := reg.InitSession(); err != nil { if strings.Contains(err.Error(), "403") { lastErr = err reg.Client.Close() continue // 403 则换指纹重试 } return nil, fmt.Errorf("初始化失败: %v", err) } // 初始化成功 lastErr = nil break } if lastErr != nil { return nil, fmt.Errorf("初始化失败: %v (已重试3次)", lastErr) } // 获取授权 URL if err := reg.GetAuthorizeURL(email); err != nil { return nil, fmt.Errorf("获取授权URL失败: %v", err) } // 开始授权流程 if err := reg.StartAuthorize(); err != nil { return nil, fmt.Errorf("授权流程失败: %v", err) } // 注册账户 if err := reg.Register(email, password); err != nil { return nil, fmt.Errorf("注册账号失败: %v", err) } // 发送验证邮件 if err := reg.SendVerificationEmail(); err != nil { return nil, fmt.Errorf("发送验证邮件失败: %v", err) } // 获取验证码 (带超时 60s) - 合并日志,使用 Status 显示等待状态 logger.Status(fmt.Sprintf("%s 验证邮箱中...", logPrefix), email, "register") otpCode, err := mail.GetVerificationCode(email, 60*time.Second) if err != nil { return nil, err } logger.Success(fmt.Sprintf("%s 验证码: %s", logPrefix, otpCode), email, "register") // 验证 OTP if err := reg.ValidateOTP(otpCode); err != nil { return nil, fmt.Errorf("验证OTP失败: %v", err) } // 创建账户 if err := reg.CreateAccount(realName, birthdate); err != nil { return nil, fmt.Errorf("创建账户失败: %v", err) } // 预热 session (可选) _ = reg.GetSessionToken() return reg, nil } // GenerateUUID 生成 UUID func GenerateUUID() string { b := make([]byte, 16) rand.Read(b) return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) } // GenerateName 随机生成姓名 func GenerateName() string { firstNames := []string{"James", "Mary", "John", "Patricia", "Robert", "Jennifer", "Michael", "Linda", "William", "Elizabeth"} lastNames := []string{"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"} return firstNames[rand.Intn(len(firstNames))] + " " + lastNames[rand.Intn(len(lastNames))] } // GenerateBirthdate 随机生成生日 func GenerateBirthdate() string { year := rand.Intn(20) + 1980 month := rand.Intn(12) + 1 day := rand.Intn(28) + 1 return fmt.Sprintf("%04d-%02d-%02d", year, month, day) } // GeneratePassword 随机生成密码 func GeneratePassword() string { chars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*" p := make([]byte, 12) for i := range p { p[i] = chars[rand.Intn(len(chars))] } return string(p) } func truncateStr(s string, max int) string { if len(s) <= max { return s } return s[:max] + "..." }