单个账号流注册流程
This commit is contained in:
@@ -3,16 +3,50 @@ package api
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go-account-register/libs"
|
"go-account-register/libs"
|
||||||
|
"go-account-register/services"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Twitter struct{}
|
type Twitter struct {
|
||||||
|
}
|
||||||
|
|
||||||
type RegisterParams struct {
|
type RegisterParams struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Twitter) Login(ctx *gin.Context) {
|
||||||
|
// 参数
|
||||||
|
var res *libs.ErrorInfo
|
||||||
|
accountId := ctx.Query("accountId")
|
||||||
|
id, _ := strconv.Atoi(accountId)
|
||||||
|
|
||||||
|
var TwitterService services.TwitterService
|
||||||
|
res = TwitterService.Login(id)
|
||||||
|
UserService := services.InitTwitterAccountService()
|
||||||
|
UserService.ChangeAccountLoginStatus(id, res.Msg)
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": res.Code,
|
||||||
|
"msg": res.Msg,
|
||||||
|
"data": res.Data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Twitter) Logout(ctx *gin.Context) {
|
||||||
|
accountId := ctx.Query("accountId")
|
||||||
|
id, _ := strconv.Atoi(accountId)
|
||||||
|
var TwitterService services.TwitterService
|
||||||
|
TwitterService.Logout(id)
|
||||||
|
UserService := services.InitTwitterAccountService()
|
||||||
|
UserService.ChangeAccountLoginStatus(id, "账号下线")
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "成功",
|
||||||
|
"data": "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (*Twitter) Register(ctx *gin.Context) {
|
func (*Twitter) Register(ctx *gin.Context) {
|
||||||
var res *libs.ErrorInfo
|
var res *libs.ErrorInfo
|
||||||
var paramsJson RegisterParams
|
var paramsJson RegisterParams
|
||||||
@@ -26,8 +60,8 @@ func (*Twitter) Register(ctx *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// var TwitterService services.TwitterAccountService
|
var BitBrowserService services.BitBrowserService
|
||||||
// res = TwitterService.Register()
|
res = BitBrowserService.Register()
|
||||||
ctx.JSON(http.StatusOK, gin.H{
|
ctx.JSON(http.StatusOK, gin.H{
|
||||||
"code": res.Code,
|
"code": res.Code,
|
||||||
"msg": res.Msg,
|
"msg": res.Msg,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"go-account-register/config"
|
"go-account-register/config"
|
||||||
"go-account-register/router"
|
"go-account-register/router"
|
||||||
|
"go-account-register/services"
|
||||||
"go-account-register/utils"
|
"go-account-register/utils"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@@ -37,7 +38,9 @@ func handleShutdownSignals() {
|
|||||||
// 阻塞等待信号
|
// 阻塞等待信号
|
||||||
sig := <-sigChan
|
sig := <-sigChan
|
||||||
log.Printf("接收到信号: %v, 关闭所有浏览器...", sig)
|
log.Printf("接收到信号: %v, 关闭所有浏览器...", sig)
|
||||||
|
services.Browser.CancelAllBrowser()
|
||||||
|
accountService := services.InitTwitterAccountService()
|
||||||
|
accountService.OfflineAllAccount()
|
||||||
// 强制退出程序
|
// 强制退出程序
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,4 +31,8 @@ var ErrorCode = map[string]*ErrorInfo{
|
|||||||
"RegisterFailed": {Code: 0, Data: "", Msg: "注册失败"},
|
"RegisterFailed": {Code: 0, Data: "", Msg: "注册失败"},
|
||||||
"RegisterSuccessful": {Code: 0, Data: "", Msg: "注册成功"},
|
"RegisterSuccessful": {Code: 0, Data: "", Msg: "注册成功"},
|
||||||
"BrowserCreationFailed": {Code: 0, Data: "", Msg: "浏览器创建失败"},
|
"BrowserCreationFailed": {Code: 0, Data: "", Msg: "浏览器创建失败"},
|
||||||
|
"MailboxUnavailable": {Code: 0, Data: "", Msg: "邮箱不可用"},
|
||||||
|
"AccountLocked": {Code: 0, Data: "", Msg: "账号锁定"},
|
||||||
|
"UnlockedSuccessfully": {Code: 200, Data: "", Msg: "解锁成功"},
|
||||||
|
"UnlockedFailed": {Code: 0, Data: "", Msg: "解锁失败"},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,5 +13,5 @@ type BrowserInstance struct {
|
|||||||
|
|
||||||
// 实现 TableName 方法指定表名
|
// 实现 TableName 方法指定表名
|
||||||
func (BrowserInstance) TableName() string {
|
func (BrowserInstance) TableName() string {
|
||||||
return "browser_instance"
|
return "ar_browser_instance"
|
||||||
}
|
}
|
||||||
|
|||||||
100
plugin/task-account-unlock-plugin.go
Normal file
100
plugin/task-account-unlock-plugin.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-account-register/config"
|
||||||
|
"go-account-register/models"
|
||||||
|
"go-account-register/services"
|
||||||
|
paramsTypes "go-account-register/types"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"runtime/debug"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TaskAccountUnlockPlugin struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*TaskAccountUnlockPlugin) Run(task *paramsTypes.Task) {
|
||||||
|
|
||||||
|
// 查询当前所有账号
|
||||||
|
accountService := services.InitTwitterAccountService()
|
||||||
|
// 判断是否是补偿任务
|
||||||
|
var accountList []models.TwitterAccount
|
||||||
|
accountList = accountService.GetAvailableAccount(&models.TwitterAccount{
|
||||||
|
Status: "账号锁定",
|
||||||
|
})
|
||||||
|
if task.Data.Compensate == 1 {
|
||||||
|
// 补偿任务
|
||||||
|
accountList = accountService.GetAccountListByStatus(&models.TwitterAccount{
|
||||||
|
Status: "解锁失败",
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 清空当前运行的账号状态
|
||||||
|
accountService.ClearAccountStatus(&models.TwitterAccount{
|
||||||
|
Status: "解锁成功",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取并发数
|
||||||
|
appConfig, _ := config.LoadConfig()
|
||||||
|
num := math.Ceil(float64(len(accountList)) / float64(appConfig.Limit.BrowserRunMax))
|
||||||
|
sem := make(chan struct{}, appConfig.Limit.BrowserRunMax) // 全局并发上限
|
||||||
|
var globalWait sync.WaitGroup
|
||||||
|
for i := 0; i < int(num); i++ {
|
||||||
|
select {
|
||||||
|
case <-task.Ctx.Done():
|
||||||
|
log.Println("中断任务")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
log.Println("批次:", i+1)
|
||||||
|
for j := 0; j < appConfig.Limit.BrowserRunMax; j++ {
|
||||||
|
log.Println(i*appConfig.Limit.BrowserRunMax + j)
|
||||||
|
taskIndex := i*appConfig.Limit.BrowserRunMax + j
|
||||||
|
if taskIndex >= len(accountList) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
globalWait.Add(1)
|
||||||
|
sem <- struct{}{} // 阻塞直到有空位
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
<-sem // 释放信号量
|
||||||
|
globalWait.Done()
|
||||||
|
}()
|
||||||
|
unlockWork(task, accountList[taskIndex].ID)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
globalWait.Wait()
|
||||||
|
|
||||||
|
task.Message <- "Success"
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockWork(task *paramsTypes.Task, id int64) {
|
||||||
|
AccountService := services.InitTwitterAccountService()
|
||||||
|
var TwitterService services.BitBrowserService
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Printf("Goroutine panic: %v\nStack: %s", r, debug.Stack())
|
||||||
|
// AccountService.ChangeAccountLoginStatus(int(id), "账号下线")
|
||||||
|
// TwitterService.Logout(int(id))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
res := TwitterService.AccountUnlock(id)
|
||||||
|
// 开始登录
|
||||||
|
// res := TwitterService.Login(int(id))
|
||||||
|
AccountService.ChangeAccountLoginStatus(int(id), res.Msg)
|
||||||
|
// if res.Code == 200 {
|
||||||
|
// // 开始拟人
|
||||||
|
// // res = TwitterService.ChangeAccountInfo(int(id))
|
||||||
|
// AccountService.ChangeAccountStatus(int(id), res.Msg)
|
||||||
|
// time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
|
// TwitterService.Logout(int(id))
|
||||||
|
// AccountService.ChangeAccountLoginStatus(int(id), "账号下线")
|
||||||
|
// }
|
||||||
|
|
||||||
|
log.Println(task.ID)
|
||||||
|
}
|
||||||
@@ -5,10 +5,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var task Task
|
var task Task
|
||||||
|
var twitter Twitter
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(app *gin.Engine) {
|
func Init(app *gin.Engine) {
|
||||||
task.TaskRouterInit(app)
|
task.TaskRouterInit(app)
|
||||||
|
twitter.TwitterRouterInit(app)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,4 +13,6 @@ var twitterApi api.Twitter
|
|||||||
func (*Twitter) TwitterRouterInit(app *gin.Engine) {
|
func (*Twitter) TwitterRouterInit(app *gin.Engine) {
|
||||||
twitter := app.Group("/twitter")
|
twitter := app.Group("/twitter")
|
||||||
twitter.POST("/register", twitterApi.Register)
|
twitter.POST("/register", twitterApi.Register)
|
||||||
|
twitter.GET("/login", twitterApi.Login) // 登录账号
|
||||||
|
twitter.GET("/logout", twitterApi.Logout) // 退出登录
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,20 @@ type BitBrowserService struct {
|
|||||||
var Browser libs.Browser
|
var Browser libs.Browser
|
||||||
var Chrome libs.Chrome
|
var Chrome libs.Chrome
|
||||||
|
|
||||||
func (*BitBrowserService) CreateBrowser(proxy *models.Proxy) string {
|
type BitCookieParam struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
ExpirationDate float64 `json:"expirationDate"` // 必须为 number
|
||||||
|
HttpOnly bool `json:"httpOnly"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Secure bool `json:"secure"`
|
||||||
|
Session bool `json:"session"`
|
||||||
|
StoreID *string `json:"storeId"` // 允许 null,用指针
|
||||||
|
Value string `json:"value"`
|
||||||
|
SameSite *string `json:"sameSite,omitempty"` // 可选字段,不填时省略
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*BitBrowserService) CreateBrowser(proxy *models.Proxy, cookies []*proto.NetworkCookieParam) string {
|
||||||
fingerprint := map[string]interface{}{
|
fingerprint := map[string]interface{}{
|
||||||
"name": fmt.Sprintf(`推特注册%s`, utils.RandString(10)),
|
"name": fmt.Sprintf(`推特注册%s`, utils.RandString(10)),
|
||||||
"proxyMethod": 2,
|
"proxyMethod": 2,
|
||||||
@@ -42,6 +55,25 @@ func (*BitBrowserService) CreateBrowser(proxy *models.Proxy) string {
|
|||||||
"resolutionType": "0",
|
"resolutionType": "0",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if cookies != nil {
|
||||||
|
var cookie []BitCookieParam
|
||||||
|
for _, v := range cookies {
|
||||||
|
cookie = append(cookie, BitCookieParam{
|
||||||
|
Domain: v.Domain,
|
||||||
|
ExpirationDate: float64(v.Expires),
|
||||||
|
HttpOnly: v.HTTPOnly,
|
||||||
|
Name: v.Name,
|
||||||
|
Path: v.Path,
|
||||||
|
Secure: v.Secure,
|
||||||
|
Session: false,
|
||||||
|
StoreID: nil,
|
||||||
|
Value: v.Value,
|
||||||
|
SameSite: (*string)(&v.SameSite),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
data, _ := json.Marshal(cookie)
|
||||||
|
fingerprint["cookie"] = data
|
||||||
|
}
|
||||||
if proxy != nil {
|
if proxy != nil {
|
||||||
var proxyHandle utils.ProxyHandle
|
var proxyHandle utils.ProxyHandle
|
||||||
protocol, username, password, ip, port, err2 := proxyHandle.ParseProxy(proxy.Type + "://" + proxy.Proxy)
|
protocol, username, password, ip, port, err2 := proxyHandle.ParseProxy(proxy.Type + "://" + proxy.Proxy)
|
||||||
@@ -72,7 +104,7 @@ func (*BitBrowserService) CreateBrowser(proxy *models.Proxy) string {
|
|||||||
UpdateTime: time.Now().Unix(),
|
UpdateTime: time.Now().Unix(),
|
||||||
CreateTime: time.Now().Unix(),
|
CreateTime: time.Now().Unix(),
|
||||||
}
|
}
|
||||||
if proxy != nil {
|
if proxy != nil && proxy.Type != "" {
|
||||||
params.Proxy = proxy.Type + "://" + proxy.Proxy
|
params.Proxy = proxy.Type + "://" + proxy.Proxy
|
||||||
}
|
}
|
||||||
browserInstanceService.Create(params)
|
browserInstanceService.Create(params)
|
||||||
@@ -81,6 +113,7 @@ func (*BitBrowserService) CreateBrowser(proxy *models.Proxy) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取当前浏览器的pid
|
||||||
func (*BitBrowserService) GetPid(id string) int {
|
func (*BitBrowserService) GetPid(id string) int {
|
||||||
// 获取当前
|
// 获取当前
|
||||||
bitBrowserRequest := utils.NewBitBrowserRequest()
|
bitBrowserRequest := utils.NewBitBrowserRequest()
|
||||||
@@ -151,9 +184,9 @@ func (bit *BitBrowserService) Register() *libs.ErrorInfo {
|
|||||||
var browserId string
|
var browserId string
|
||||||
// 创建新的浏览器
|
// 创建新的浏览器
|
||||||
if proxy != nil {
|
if proxy != nil {
|
||||||
browserId = bit.CreateBrowser(proxy)
|
browserId = bit.CreateBrowser(proxy, nil)
|
||||||
} else {
|
} else {
|
||||||
browserId = bit.CreateBrowser(nil)
|
browserId = bit.CreateBrowser(nil, nil)
|
||||||
}
|
}
|
||||||
// 打开浏览器
|
// 打开浏览器
|
||||||
if browserId == "" {
|
if browserId == "" {
|
||||||
@@ -167,15 +200,19 @@ func (bit *BitBrowserService) Register() *libs.ErrorInfo {
|
|||||||
emailService := InitEmailService()
|
emailService := InitEmailService()
|
||||||
|
|
||||||
emailInfo := emailService.GetEmailOne()
|
emailInfo := emailService.GetEmailOne()
|
||||||
|
log.Println("当前邮箱信息", emailInfo)
|
||||||
obj := Browser.GetBitBrowser(ws)
|
obj := Browser.GetBitBrowser(ws)
|
||||||
page := obj.Page
|
page := obj.Page
|
||||||
page.MustNavigate("https://x.com/i/flow/signup").MustWaitLoad()
|
page.MustNavigate("https://x.com/i/flow/signup").MustWaitLoad()
|
||||||
|
// 点击创建账号
|
||||||
|
createAccount := page.Timeout(5 * time.Second).MustElement(`button.css-175oi2r.r-sdzlij.r-1phboty.r-rs99b7.r-lrvibr.r-ywje51.r-184id4b.r-13qz1uu.r-2yi16.r-1qi8awa.r-3pj75a.r-1loqt21.r-o7ynqc.r-6416eg.r-1ny4l3l`)
|
||||||
|
createAccount.MustClick()
|
||||||
// 判断当前是否是邮箱注册
|
// 判断当前是否是邮箱注册
|
||||||
_, err := page.Element(`input[name="email"]`)
|
// _, err := page.Element(`input[name="email"]`)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
switchButton := page.Timeout(5 * time.Second).MustElement(`button.css-146c3p1.r-bcqeeo.r-qvutc0.r-1qd0xha.r-1ff274t.r-a023e6.r-rjixqe.r-16dba41`)
|
switchButton := page.Timeout(5 * time.Second).MustElement(`button.css-146c3p1.r-bcqeeo.r-qvutc0.r-37j5jr.r-1ff274t.r-a023e6.r-rjixqe.r-16dba41`)
|
||||||
switchButton.MustClick()
|
switchButton.MustClick()
|
||||||
}
|
// }
|
||||||
// 获取邮箱输入框
|
// 获取邮箱输入框
|
||||||
inputEmail := page.Timeout(5 * time.Second).MustElement(`input[name="email"]`)
|
inputEmail := page.Timeout(5 * time.Second).MustElement(`input[name="email"]`)
|
||||||
inputEmail.MustFocus()
|
inputEmail.MustFocus()
|
||||||
@@ -189,7 +226,7 @@ func (bit *BitBrowserService) Register() *libs.ErrorInfo {
|
|||||||
}
|
}
|
||||||
inputName.MustInput(nameStr)
|
inputName.MustInput(nameStr)
|
||||||
// 随机生成生日
|
// 随机生成生日
|
||||||
dateParams := utils.RandDate(18, 100)
|
dateParams := utils.RandDate(18, 80)
|
||||||
dateSelects, _ := page.Timeout(5 * time.Second).Elements(`select.r-30o5oe.r-1niwhzg.r-17gur6a.r-1yadl64.r-18jsvk2.r-1loqt21.r-1inkyih.r-rjixqe.r-crgep1.r-1wzrnnt.r-1ny4l3l.r-t60dpp.r-xd6kpl.r-is05cd.r-ttdzmv`)
|
dateSelects, _ := page.Timeout(5 * time.Second).Elements(`select.r-30o5oe.r-1niwhzg.r-17gur6a.r-1yadl64.r-18jsvk2.r-1loqt21.r-1inkyih.r-rjixqe.r-crgep1.r-1wzrnnt.r-1ny4l3l.r-t60dpp.r-xd6kpl.r-is05cd.r-ttdzmv`)
|
||||||
// 月份
|
// 月份
|
||||||
dateSelects[0].WaitVisible()
|
dateSelects[0].WaitVisible()
|
||||||
@@ -206,13 +243,24 @@ func (bit *BitBrowserService) Register() *libs.ErrorInfo {
|
|||||||
ocfSignupNextLink.MustClick()
|
ocfSignupNextLink.MustClick()
|
||||||
// 判断是否需要验证码
|
// 判断是否需要验证码
|
||||||
time.Sleep(4 * time.Second)
|
time.Sleep(4 * time.Second)
|
||||||
codeInput, err1 := page.Timeout(5 * time.Second).Element(`input[name="verfication_code"]`)
|
_, err1 := page.Timeout(5 * time.Second).Element(`input[name="verfication_code"]`)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
// res := getApiResponse(page, "")
|
// res := getApiResponse(page, "")
|
||||||
}
|
}
|
||||||
//TODO 对接获取邮箱信息接口
|
//TODO 对接获取邮箱信息接口
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
codeValue := LoopGetEmailCode("pop.dragonsmail.com:993", emailInfo.Email, emailInfo.Password)
|
||||||
|
if codeValue == "0" {
|
||||||
|
emailService.Update(emailInfo.Email, models.Email{
|
||||||
|
Status: 0,
|
||||||
|
})
|
||||||
|
return libs.ErrorCode["MailboxUnavailable"]
|
||||||
|
}
|
||||||
// 获取邮箱验证码
|
// 获取邮箱验证码
|
||||||
codeInput.MustInput("")
|
log.Println("获取当前验证码", codeValue)
|
||||||
|
codeInput1, _ := page.Timeout(5 * time.Second).Element(`input[name="verfication_code"]`)
|
||||||
|
codeInput1.MustInput(codeValue)
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
// 点击下一步
|
// 点击下一步
|
||||||
codeNextButton := page.Timeout(5 * time.Second).MustElement(`button.css-175oi2r.r-sdzlij.r-1phboty.r-rs99b7.r-lrvibr.r-19yznuf.r-64el8z.r-1fkl15p.r-1loqt21.r-o7ynqc.r-6416eg.r-1ny4l3l`)
|
codeNextButton := page.Timeout(5 * time.Second).MustElement(`button.css-175oi2r.r-sdzlij.r-1phboty.r-rs99b7.r-lrvibr.r-19yznuf.r-64el8z.r-1fkl15p.r-1loqt21.r-o7ynqc.r-6416eg.r-1ny4l3l`)
|
||||||
codeNextButton.MustClick()
|
codeNextButton.MustClick()
|
||||||
@@ -224,12 +272,15 @@ func (bit *BitBrowserService) Register() *libs.ErrorInfo {
|
|||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
// res := getApiResponse(page, "")
|
// res := getApiResponse(page, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordInput.MustInput(emailInfo.Password)
|
passwordInput.MustInput(emailInfo.Password)
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
// 点击下一步
|
// 点击下一步
|
||||||
loginButton := page.Timeout(5 * time.Second).MustElement(`button[data-testid="LoginForm_Login_Button"]`)
|
loginButton := page.Timeout(5 * time.Second).MustElement(`button[data-testid="LoginForm_Login_Button"]`)
|
||||||
loginButton.MustClick()
|
loginButton.MustClick()
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
page.MustNavigate("https://x.com").MustWaitLoad()
|
page.MustNavigate("https://x.com").MustWaitLoad()
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
// 获取cookie
|
// 获取cookie
|
||||||
cookieArr := getPageCookie(page, "https://x.com")
|
cookieArr := getPageCookie(page, "https://x.com")
|
||||||
// 创建用户
|
// 创建用户
|
||||||
@@ -243,13 +294,15 @@ func (bit *BitBrowserService) Register() *libs.ErrorInfo {
|
|||||||
Status: "注册成功",
|
Status: "注册成功",
|
||||||
LoginStatus: "登录成功",
|
LoginStatus: "登录成功",
|
||||||
Proxy: func() string {
|
Proxy: func() string {
|
||||||
if proxy != nil {
|
if proxy != nil && proxy.Type != "" {
|
||||||
return proxy.Type + "://" + proxy.Proxy
|
return proxy.Type + "://" + proxy.Proxy
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}(),
|
}(),
|
||||||
})
|
})
|
||||||
|
emailService.Update(emailInfo.Email, models.Email{
|
||||||
|
UseStatus: 1,
|
||||||
|
})
|
||||||
// 删除浏览器
|
// 删除浏览器
|
||||||
bit.DeleteBrowser(browserId)
|
bit.DeleteBrowser(browserId)
|
||||||
return libs.ErrorCode["RegisterSuccessful"]
|
return libs.ErrorCode["RegisterSuccessful"]
|
||||||
@@ -273,3 +326,20 @@ func getPageCookie(page *rod.Page, url string) []*proto.NetworkCookieParam {
|
|||||||
}
|
}
|
||||||
return cookies
|
return cookies
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 循环获取3次验证码
|
||||||
|
func LoopGetEmailCode(server, email, password string) string {
|
||||||
|
var emailImap utils.EmailImap
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
codeValue := emailImap.GetMailCode(server, email, password)
|
||||||
|
if codeValue != "0" {
|
||||||
|
return codeValue
|
||||||
|
}
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
}
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*BitBrowserService) AccountUnlock(id int64) *libs.ErrorInfo {
|
||||||
|
return libs.ErrorCode["UnlockedSuccessfully"]
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,9 +19,13 @@ func InitEmailService() *EmailService {
|
|||||||
|
|
||||||
func (e *EmailService) GetEmailOne() *models.Email {
|
func (e *EmailService) GetEmailOne() *models.Email {
|
||||||
var email models.Email
|
var email models.Email
|
||||||
err := e.db.Model(email).Where("use_status = ?", 0).First(&email).Error
|
err := e.db.Model(email).Where("use_status = ? and status=?", 0, 1).First(&email).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &email
|
return &email
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *EmailService) Update(email string, params models.Email) {
|
||||||
|
e.db.Model(&models.Email{}).Where("email = ?", email).Updates(params)
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,3 +22,62 @@ func (t *TwitterAccountService) Create(params *models.TwitterAccount) {
|
|||||||
params.UpdateTime = time.Now().Unix()
|
params.UpdateTime = time.Now().Unix()
|
||||||
t.db.Create(params)
|
t.db.Create(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *TwitterAccountService) GetInfo(id int64) *models.TwitterAccount {
|
||||||
|
var account models.TwitterAccount
|
||||||
|
result := t.db.Model(&models.TwitterAccount{}).Where("id = ?", id).First(&account)
|
||||||
|
if result.Error != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &account
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更改账号状态
|
||||||
|
func (u *TwitterAccountService) ChangeAccountStatus(accountId int, status string) bool {
|
||||||
|
u.db.Model(&models.TwitterAccount{}).Where("id = ?", accountId).Updates(map[string]interface{}{"status": status, "update_time": time.Now().Unix()})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更改账号登录状态
|
||||||
|
func (u *TwitterAccountService) ChangeAccountLoginStatus(accountId int, loginStatus string) bool {
|
||||||
|
u.db.Model(&models.TwitterAccount{}).Where("id = ?", accountId).Updates(map[string]interface{}{"login_status": loginStatus, "update_time": time.Now().Unix()})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下线所有账号
|
||||||
|
func (u *TwitterAccountService) OfflineAllAccount() bool {
|
||||||
|
u.db.Model(&models.TwitterAccount{}).Where("login_status = ?", "登录成功").Updates(&models.TwitterAccount{
|
||||||
|
LoginStatus: "账号下线",
|
||||||
|
UpdateTime: time.Now().Unix(),
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取可用账号
|
||||||
|
func (u *TwitterAccountService) GetAvailableAccount(params *models.TwitterAccount) []models.TwitterAccount {
|
||||||
|
var users []models.TwitterAccount
|
||||||
|
res := u.db.Model(&models.TwitterAccount{}).Where(`status = ? AND (login_status != "账号异常" OR login_status IS NULL)`, params.Status)
|
||||||
|
// if params.Username != "" {
|
||||||
|
// res.Where("username LIKE ?", "%"+params.Username+"%")
|
||||||
|
// }
|
||||||
|
|
||||||
|
res.Order("id DESC").Find(&users)
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取养号未运行或者运行失败的账号
|
||||||
|
func (u *TwitterAccountService) GetAccountListByStatus(params *models.TwitterAccount) []models.TwitterAccount {
|
||||||
|
var users []models.TwitterAccount
|
||||||
|
res := u.db.Model(&models.TwitterAccount{}).Where(`status = ? AND (login_status != "账号异常" OR login_status IS NULL)`, params.Status)
|
||||||
|
// if params.Username != "" {
|
||||||
|
// res.Where("username LIKE ?", "%"+params.Username+"%")
|
||||||
|
// }
|
||||||
|
res.Order("id DESC").Find(&users)
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空状态字段
|
||||||
|
func (u *TwitterAccountService) ClearAccountStatus(params *models.TwitterAccount) bool {
|
||||||
|
u.db.Model(&models.TwitterAccount{}).Where("status = ?", params.Status).Update("status", nil)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
88
services/twitter_service.go
Normal file
88
services/twitter_service.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go-account-register/libs"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-rod/rod"
|
||||||
|
"github.com/go-rod/rod/lib/cdp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TwitterService struct{}
|
||||||
|
|
||||||
|
// 登录
|
||||||
|
func (*TwitterService) Login(id int) *libs.ErrorInfo {
|
||||||
|
UserService := InitTwitterAccountService()
|
||||||
|
user := UserService.GetInfo(int64(id))
|
||||||
|
obj := Browser.GetBrowser(id, &libs.Fingerprint{
|
||||||
|
Proxy: user.Proxy,
|
||||||
|
})
|
||||||
|
browser := obj.Browser
|
||||||
|
page := obj.Page
|
||||||
|
|
||||||
|
err3 := rod.Try(func() {
|
||||||
|
// 创建 Cookie 对象
|
||||||
|
cookies := user.Cookies
|
||||||
|
log.Println("获取cookie", cookies)
|
||||||
|
// 全局设置
|
||||||
|
browser.SetCookies(cookies)
|
||||||
|
page.MustNavigate("https://x.com/home").MustWaitLoad()
|
||||||
|
page.MustWaitNavigation()
|
||||||
|
})
|
||||||
|
if e, ok := err3.(*cdp.Error); ok {
|
||||||
|
if e.Code == -32000 && e.Message == "Inspected target navigated or closed" {
|
||||||
|
Browser.CancelBrowser(id)
|
||||||
|
return libs.ErrorCode["LoginFailed"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
url := page.MustEval("() => window.location.href").String()
|
||||||
|
if url == "https://x.com/account/access" {
|
||||||
|
Browser.CancelBrowser(id)
|
||||||
|
return libs.ErrorCode["AccountLocked"]
|
||||||
|
}
|
||||||
|
log.Println(url)
|
||||||
|
err1 := rod.Try(func() {
|
||||||
|
page.Timeout(5 * time.Second).MustElement(`input[autocomplete="username"]`)
|
||||||
|
})
|
||||||
|
if err1 == nil {
|
||||||
|
Browser.CancelBrowser(id)
|
||||||
|
return libs.ErrorCode["LoginFailed"]
|
||||||
|
}
|
||||||
|
// 是否检测到侧边栏
|
||||||
|
err2 := rod.Try(func() {
|
||||||
|
page.Timeout(5 * time.Second).MustElement(`a[data-testid="AppTabBar_Profile_Link"]`)
|
||||||
|
})
|
||||||
|
|
||||||
|
if err2 != nil {
|
||||||
|
Browser.CancelBrowser(id)
|
||||||
|
return libs.ErrorCode["LoginFailed"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否检测到帖子列表
|
||||||
|
err := rod.Try(func() {
|
||||||
|
page.Timeout(5 * time.Second).MustElement(`article[data-testid="tweet"]`)
|
||||||
|
})
|
||||||
|
|
||||||
|
if errors.Is(err, context.DeadlineExceeded) {
|
||||||
|
fmt.Println("请求空闲等待超时!")
|
||||||
|
// 关闭浏览器
|
||||||
|
Browser.CancelBrowser(id)
|
||||||
|
return libs.ErrorCode["NetworkTimeout"]
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return libs.ErrorCode["LoginSuccessful"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 退出登录
|
||||||
|
func (*TwitterService) Logout(id int) {
|
||||||
|
Browser.CancelBrowser(id)
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@@ -18,7 +19,8 @@ type EmailImap struct {
|
|||||||
// 获取最新一封邮件的正文内容
|
// 获取最新一封邮件的正文内容
|
||||||
func (*EmailImap) GetLatestMailContent(server, username, password string) (string, error) {
|
func (*EmailImap) GetLatestMailContent(server, username, password string) (string, error) {
|
||||||
// 1. 连接 IMAP 服务器 (SSL/TLS, 端口 993)
|
// 1. 连接 IMAP 服务器 (SSL/TLS, 端口 993)
|
||||||
c, err := client.DialTLS(server, nil)
|
tlsConfig := &tls.Config{InsecureSkipVerify: true}
|
||||||
|
c, err := client.DialTLS(server, tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("连接失败: %v", err)
|
return "", fmt.Errorf("连接失败: %v", err)
|
||||||
}
|
}
|
||||||
@@ -66,7 +68,8 @@ func (*EmailImap) GetLatestMailContent(server, username, password string) (strin
|
|||||||
return "", fmt.Errorf("解析邮件失败: %v", err)
|
return "", fmt.Errorf("解析邮件失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var content string
|
var htmlContent, textContent string
|
||||||
|
|
||||||
for {
|
for {
|
||||||
p, err := mr.NextPart()
|
p, err := mr.NextPart()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@@ -76,16 +79,23 @@ func (*EmailImap) GetLatestMailContent(server, username, password string) (strin
|
|||||||
return "", fmt.Errorf("读取邮件失败: %v", err)
|
return "", fmt.Errorf("读取邮件失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch p.Header.(type) {
|
switch h := p.Header.(type) {
|
||||||
case *mail.InlineHeader:
|
case *mail.InlineHeader:
|
||||||
|
mediaType, _, _ := h.ContentType()
|
||||||
b, _ := io.ReadAll(p.Body)
|
b, _ := io.ReadAll(p.Body)
|
||||||
content = strings.TrimSpace(string(b))
|
if strings.HasPrefix(mediaType, "text/html") {
|
||||||
// 只取第一个正文即可
|
htmlContent = string(b)
|
||||||
return content, nil
|
} else if strings.HasPrefix(mediaType, "text/plain") {
|
||||||
|
textContent = string(b)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return content, nil
|
// 优先返回 HTML,没有就退回纯文本
|
||||||
|
if htmlContent != "" {
|
||||||
|
return htmlContent, nil
|
||||||
|
}
|
||||||
|
return textContent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EmailImap) GetMailCode(server, username, password string) string {
|
func (e *EmailImap) GetMailCode(server, username, password string) string {
|
||||||
@@ -95,7 +105,6 @@ func (e *EmailImap) GetMailCode(server, username, password string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
htmlStr := res
|
htmlStr := res
|
||||||
fmt.Println(htmlStr)
|
|
||||||
|
|
||||||
doc, err := goquery.NewDocumentFromReader(strings.NewReader(htmlStr))
|
doc, err := goquery.NewDocumentFromReader(strings.NewReader(htmlStr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -112,7 +121,7 @@ func (e *EmailImap) GetMailCode(server, username, password string) string {
|
|||||||
if code == "" {
|
if code == "" {
|
||||||
return "0"
|
return "0"
|
||||||
}
|
}
|
||||||
return code
|
return strings.TrimSpace(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test() {
|
func test() {
|
||||||
|
|||||||
Reference in New Issue
Block a user