Files
chatgpt-creator/internal/register/batch.go
2026-03-10 23:25:12 +07:00

135 lines
3.7 KiB
Go

package register
import (
"fmt"
"os"
"sync"
"sync/atomic"
"time"
"github.com/verssache/chatgpt-creator/internal/email"
"github.com/verssache/chatgpt-creator/internal/util"
)
// registerOne handles a single account registration.
func registerOne(workerID int, tag string, proxy, outputFile, defaultPassword, defaultDomain string, printMu, fileMu *sync.Mutex) (bool, string, string) {
client, err := NewClient(proxy, tag, workerID, printMu, fileMu)
if err != nil {
return false, "", fmt.Sprintf("failed to create client: %v", err)
}
emailAddr, err := email.CreateTempEmail(defaultDomain)
if err != nil {
return false, "", fmt.Sprintf("failed to create temp email: %v", err)
}
password := defaultPassword
if password == "" {
password = util.GeneratePassword(14)
}
firstName, lastName := util.RandomName()
birthdate := util.RandomBirthdate()
client.print(fmt.Sprintf("Starting registration for %s", emailAddr))
err = client.RunRegister(emailAddr, password, firstName+" "+lastName, birthdate)
if err != nil {
return false, emailAddr, err.Error()
}
// Append to file
fileMu.Lock()
defer fileMu.Unlock()
f, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return false, emailAddr, fmt.Sprintf("failed to open output file: %v", err)
}
defer f.Close()
line := fmt.Sprintf("%s|%s\n", emailAddr, password)
if _, err := f.WriteString(line); err != nil {
return false, emailAddr, fmt.Sprintf("failed to write to output file: %v", err)
}
return true, emailAddr, ""
}
// RunBatch runs concurrent registration tasks with retry until target success count is reached.
func RunBatch(totalAccounts int, outputFile string, maxWorkers int, proxy, defaultPassword, defaultDomain string) {
var printMu sync.Mutex
var fileMu sync.Mutex
var remaining int64 = int64(totalAccounts)
var successCount int64
var failureCount int64
var attemptNum int64
startTime := time.Now()
var wg sync.WaitGroup
for w := 1; w <= maxWorkers; w++ {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
for {
// Claim a slot before starting work
if atomic.AddInt64(&remaining, -1) < 0 {
// No more slots needed, put it back and exit
atomic.AddInt64(&remaining, 1)
return
}
attempt := atomic.AddInt64(&attemptNum, 1)
tag := fmt.Sprintf("%d/%d", attempt, totalAccounts)
success, emailAddr, errStr := registerOne(workerID, tag, proxy, outputFile, defaultPassword, defaultDomain, &printMu, &fileMu)
if success {
atomic.AddInt64(&successCount, 1)
ts := time.Now().Format("15:04:05")
printMu.Lock()
fmt.Printf("[%s] [W%d] ✓ SUCCESS: %s\n", ts, workerID, emailAddr)
printMu.Unlock()
} else {
atomic.AddInt64(&failureCount, 1)
// Failed — return the slot so it gets retried
atomic.AddInt64(&remaining, 1)
ts := time.Now().Format("15:04:05")
printMu.Lock()
fmt.Printf("[%s] [W%d] ✗ FAILURE: %s | %s\n", ts, workerID, emailAddr, errStr)
printMu.Unlock()
}
}
}(w)
}
wg.Wait()
elapsed := time.Since(startTime)
elapsedStr := formatDuration(elapsed)
fmt.Printf("\n--- Batch Registration Summary ---\n")
fmt.Printf("Target: %d\n", totalAccounts)
fmt.Printf("Success: %d\n", successCount)
fmt.Printf("Attempts: %d\n", attemptNum)
fmt.Printf("Failures: %d\n", failureCount)
fmt.Printf("Elapsed: %s\n", elapsedStr)
fmt.Printf("----------------------------------\n")
}
func formatDuration(d time.Duration) string {
h := int(d.Hours())
m := int(d.Minutes()) % 60
s := int(d.Seconds()) % 60
if h > 0 {
return fmt.Sprintf("%dh %dm %ds", h, m, s)
}
if m > 0 {
return fmt.Sprintf("%dm %ds", m, s)
}
return fmt.Sprintf("%ds", s)
}