Files
codexautopool/backend/internal/register/chatgpt.go

409 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 重试机制)
func APIRegister(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] + "..."
}