feat: Implement S2A OAuth authorization using chromedp and introduce new files for browser automation and team processing.
This commit is contained in:
@@ -902,25 +902,55 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// registerWithTimeout 带超时的注册
|
// registerWithTimeout 带超时的注册(遇到 403 会换指纹重试)
|
||||||
func registerWithTimeout(email, password, name, birthdate, proxy string) (*register.ChatGPTReg, error) {
|
func registerWithTimeout(email, password, name, birthdate, proxy string) (*register.ChatGPTReg, error) {
|
||||||
reg, err := register.New(proxy)
|
const maxInitRetries = 3
|
||||||
|
var reg *register.ChatGPTReg
|
||||||
|
var initErr error
|
||||||
|
|
||||||
|
// 初始化阶段:遇到 403 换指纹重试
|
||||||
|
for attempt := 0; attempt < maxInitRetries; attempt++ {
|
||||||
|
var err error
|
||||||
|
reg, err = register.New(proxy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := reg.InitSession(); err != nil {
|
if err := reg.InitSession(); err != nil {
|
||||||
|
initErr = err
|
||||||
|
// 检查是否是 403 错误,换指纹重试
|
||||||
|
if strings.Contains(err.Error(), "403") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("初始化失败: %v", err)
|
return nil, fmt.Errorf("初始化失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := reg.GetAuthorizeURL(email); err != nil {
|
if err := reg.GetAuthorizeURL(email); err != nil {
|
||||||
|
// 403 也可能在这里出现
|
||||||
|
if strings.Contains(err.Error(), "403") {
|
||||||
|
initErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("获取授权URL失败: %v", err)
|
return nil, fmt.Errorf("获取授权URL失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := reg.StartAuthorize(); err != nil {
|
if err := reg.StartAuthorize(); err != nil {
|
||||||
|
if strings.Contains(err.Error(), "403") {
|
||||||
|
initErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("启动授权失败: %v", err)
|
return nil, fmt.Errorf("启动授权失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化成功,跳出重试循环
|
||||||
|
initErr = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if initErr != nil {
|
||||||
|
return nil, fmt.Errorf("初始化失败(重试%d次): %v", maxInitRetries, initErr)
|
||||||
|
}
|
||||||
|
|
||||||
if err := reg.Register(email, password); err != nil {
|
if err := reg.Register(email, password); err != nil {
|
||||||
return nil, fmt.Errorf("注册失败: %v", err)
|
return nil, fmt.Errorf("注册失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"codex-pool/internal/proxyutil"
|
"codex-pool/internal/proxyutil"
|
||||||
|
|
||||||
|
"github.com/chromedp/cdproto/cdp"
|
||||||
"github.com/chromedp/cdproto/fetch"
|
"github.com/chromedp/cdproto/fetch"
|
||||||
"github.com/chromedp/cdproto/network"
|
"github.com/chromedp/cdproto/network"
|
||||||
"github.com/chromedp/cdproto/page"
|
"github.com/chromedp/cdproto/page"
|
||||||
@@ -226,7 +227,18 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(1500 * time.Millisecond)
|
// 等待页面跳转(等待 URL 变化或密码框出现,最多 10 秒)
|
||||||
|
passwordSelectors := []string{
|
||||||
|
`input[name="current-password"]`,
|
||||||
|
`input[autocomplete="current-password"]`,
|
||||||
|
`input[type="password"]`,
|
||||||
|
`input[name="password"]`,
|
||||||
|
`input[id="password"]`,
|
||||||
|
}
|
||||||
|
|
||||||
|
var passwordFound bool
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
if callbackURL != "" {
|
if callbackURL != "" {
|
||||||
logStep(StepComplete, "授权成功")
|
logStep(StepComplete, "授权成功")
|
||||||
@@ -239,16 +251,25 @@ func CompleteWithChromedpLogged(authURL, email, password, teamID string, headles
|
|||||||
return ExtractCodeFromCallbackURL(currentURL), nil
|
return ExtractCodeFromCallbackURL(currentURL), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
logStep(StepInputPassword, "查找密码框 | URL: %s", currentURL)
|
// 检查是否有密码输入框(页面已跳转)
|
||||||
|
for _, sel := range passwordSelectors {
|
||||||
// 密码输入框选择器
|
var nodes []*cdp.Node
|
||||||
passwordSelectors := []string{
|
if err := chromedp.Run(ctx, chromedp.Nodes(sel, &nodes, chromedp.ByQuery)); err == nil && len(nodes) > 0 {
|
||||||
`input[name="current-password"]`,
|
passwordFound = true
|
||||||
`input[autocomplete="current-password"]`,
|
break
|
||||||
`input[type="password"]`,
|
|
||||||
`input[name="password"]`,
|
|
||||||
`input[id="password"]`,
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if passwordFound {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 URL 已经变化(包含 password),也跳出
|
||||||
|
if strings.Contains(currentURL, "password") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logStep(StepInputPassword, "查找密码框 | URL: %s", currentURL)
|
||||||
|
|
||||||
// 使用短超时查找密码框(10秒)
|
// 使用短超时查找密码框(10秒)
|
||||||
findCtx2, findCancel2 := context.WithTimeout(ctx, 10*time.Second)
|
findCtx2, findCancel2 := context.WithTimeout(ctx, 10*time.Second)
|
||||||
|
|||||||
@@ -245,19 +245,41 @@ func (r *RodAuth) CompleteOAuthLogged(authURL, email, password, teamID string, l
|
|||||||
btn.MustClick()
|
btn.MustClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(1500 * time.Millisecond)
|
// 等待页面跳转(等待 URL 变化或密码框出现,最多 10 秒)
|
||||||
|
passwordSelector := "input[name='current-password'], input[autocomplete='current-password'], input[type='password'], input[name='password'], input[id='password']"
|
||||||
|
var passwordFound bool
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
if code := r.checkForCode(page); code != "" {
|
if code := r.checkForCode(page); code != "" {
|
||||||
logStep(StepComplete, "授权成功")
|
logStep(StepComplete, "授权成功")
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info, _ = page.Info()
|
||||||
|
// 检查是否有密码输入框(页面已跳转)
|
||||||
|
if pwdInput, _ := page.Timeout(100 * time.Millisecond).Element(passwordSelector); pwdInput != nil {
|
||||||
|
passwordFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 URL 已经变化(包含 password),也跳出
|
||||||
|
if strings.Contains(info.URL, "password") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取当前URL用于调试
|
// 获取当前URL用于调试
|
||||||
info, _ = page.Info()
|
info, _ = page.Info()
|
||||||
logStep(StepInputPassword, "查找密码框 | URL: %s", info.URL)
|
logStep(StepInputPassword, "查找密码框 | URL: %s", info.URL)
|
||||||
|
|
||||||
// 使用10秒超时查找密码输入框(优先使用 current-password)
|
// 使用10秒超时查找密码输入框(优先使用 current-password)
|
||||||
passwordInput, err := page.Timeout(10 * time.Second).Element("input[name='current-password'], input[autocomplete='current-password'], input[type='password'], input[name='password'], input[id='password']")
|
var passwordInput *rod.Element
|
||||||
|
if passwordFound {
|
||||||
|
passwordInput, err = page.Timeout(2 * time.Second).Element(passwordSelector)
|
||||||
|
} else {
|
||||||
|
passwordInput, err = page.Timeout(10 * time.Second).Element(passwordSelector)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
info, _ := page.Info()
|
info, _ := page.Info()
|
||||||
logError(StepInputPassword, "未找到密码输入框 | URL: %s", info.URL)
|
logError(StepInputPassword, "未找到密码输入框 | URL: %s", info.URL)
|
||||||
|
|||||||
Reference in New Issue
Block a user