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

416 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/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)
}
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 完整的注册流程
func Run(email, password, name, birthdate, proxy string) (*ChatGPTReg, error) {
return RunWithRetry(email, password, name, birthdate, proxy, 3)
}
// RunWithRetry 带重试的注册流程
// 当验证码获取超过5秒就换新邮箱重新注册
func RunWithRetry(email, password, name, birthdate, proxy string, maxRetries int) (*ChatGPTReg, error) {
for attempt := 0; attempt < maxRetries; attempt++ {
if attempt > 0 {
// 重试时生成新邮箱
email = mail.GenerateEmail()
password = GeneratePassword()
fmt.Printf(" [Retry %d] New email: %s\n", attempt, email)
}
reg, err := runOnce(email, password, name, birthdate, proxy)
if err == nil {
return reg, nil
}
// 如果不是验证码超时错误,直接返回
if !strings.Contains(err.Error(), "验证码获取超时") {
return nil, err
}
fmt.Printf(" [!] OTP timeout, retrying with new email...\n")
}
return nil, fmt.Errorf("注册失败: 已重试 %d 次", maxRetries)
}
// runOnce 执行一次注册流程(使用短超时获取验证码)
func runOnce(email, password, name, birthdate, proxy string) (*ChatGPTReg, error) {
reg, err := New(proxy)
if err != nil {
return nil, err
}
// 初始化
if err := reg.InitSession(); err != nil {
return nil, fmt.Errorf("初始化失败: %v", err)
}
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)
}
// 先用5秒超时尝试获取验证码
otpCode, err := mail.GetVerificationCode(email, 5*time.Second)
if err != nil {
// 5秒内没获取到再等120秒总共等待更多时间
otpCode, err = mail.GetVerificationCode(email, 120*time.Second)
if err != nil {
return nil, fmt.Errorf("验证码获取超时")
}
}
if err := reg.ValidateOTP(otpCode); err != nil {
return nil, fmt.Errorf("OTP验证失败: %v", err)
}
// 创建账户
if err := reg.CreateAccount(name, birthdate); err != nil {
return nil, fmt.Errorf("创建账户失败: %v", err)
}
// 获取 Token
_ = reg.GetSessionToken()
return reg, nil
}
// ==================== 工具函数 ====================
// GenerateName 生成随机姓名
func GenerateName() string {
firstNames := []string{"James", "John", "Robert", "Michael", "David", "William", "Richard", "Joseph", "Thomas", "Charles"}
lastNames := []string{"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"}
return firstNames[rand.Intn(len(firstNames))] + " " + lastNames[rand.Intn(len(lastNames))]
}
// GenerateUUID 生成 UUID
func GenerateUUID() string {
b := make([]byte, 16)
rand.Read(b)
b[6] = (b[6] & 0x0f) | 0x40
b[8] = (b[8] & 0x3f) | 0x80
return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:16])
}
// GenerateBirthdate 生成随机生日
func GenerateBirthdate() string {
year := 2000 + rand.Intn(5)
month := 1 + rand.Intn(12)
day := 1 + rand.Intn(28)
return fmt.Sprintf("%d-%02d-%02d", year, month, day)
}
// GeneratePassword 生成随机密码
func GeneratePassword() string {
const (
upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
lower = "abcdefghijklmnopqrstuvwxyz"
digits = "0123456789"
special = "!@#$%"
)
b := make([]byte, 13)
for i := 0; i < 2; i++ {
b[i] = upper[rand.Intn(len(upper))]
}
for i := 2; i < 10; i++ {
b[i] = lower[rand.Intn(len(lower))]
}
for i := 10; i < 12; i++ {
b[i] = digits[rand.Intn(len(digits))]
}
b[12] = special[rand.Intn(len(special))]
return string(b)
}
func truncateStr(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen] + "..."
}