From 6404f753a5ab38451ac02f304b5310d19b278121 Mon Sep 17 00:00:00 2001 From: zyj <18107291228@163.com> Date: Wed, 26 Nov 2025 19:55:55 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=99=BB=E5=BD=95=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E4=BF=A1=E6=81=AF=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/twitter.go | 10 ++ go.mod | 14 ++- go.sum | 21 +++++ models/twitter_account.go | 39 ++++---- plugin/task-get-plugin.go | 101 +++++++++++++++++++++ router/twitter.go | 1 + services/twitter_account_export_service.go | 83 +++++++++++++++++ services/twitter_account_service.go | 6 ++ services/twitter_service.go | 59 +++++++++++- task/scheduler.go | 6 +- 10 files changed, 314 insertions(+), 26 deletions(-) create mode 100644 plugin/task-get-plugin.go create mode 100644 services/twitter_account_export_service.go diff --git a/api/twitter.go b/api/twitter.go index 86f4811..6744ffc 100644 --- a/api/twitter.go +++ b/api/twitter.go @@ -102,3 +102,13 @@ func (*Twitter) Test(ctx *gin.Context) { "data": "", }) } + +func (t *Twitter) Export(ctx *gin.Context) { + var TwitterService services.TwitterService + code, msg, files := TwitterService.ExportAccounts() + ctx.JSON(http.StatusOK, gin.H{ + "code": code, + "msg": msg, + "files": files, + }) +} diff --git a/go.mod b/go.mod index 77eb435..ec0e984 100644 --- a/go.mod +++ b/go.mod @@ -49,17 +49,23 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/richardlehane/mscfb v1.0.4 // indirect + github.com/richardlehane/msoleps v1.0.4 // indirect + github.com/tiendc/go-deepcopy v1.7.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + github.com/xuri/efp v0.0.1 // indirect + github.com/xuri/excelize/v2 v2.10.0 // indirect + github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect github.com/ysmood/fetchup v0.2.3 // indirect github.com/ysmood/goob v0.4.0 // indirect github.com/ysmood/got v0.40.0 // indirect github.com/ysmood/gson v0.7.3 // indirect github.com/ysmood/leakless v0.9.0 // indirect golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.37.0 // indirect - golang.org/x/net v0.39.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/text v0.29.0 // indirect + golang.org/x/crypto v0.43.0 // indirect + golang.org/x/net v0.46.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/text v0.30.0 // indirect google.golang.org/protobuf v1.34.1 // indirect ) diff --git a/go.sum b/go.sum index 262dd92..3eb505b 100644 --- a/go.sum +++ b/go.sum @@ -96,6 +96,11 @@ github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/redis/go-redis/v9 v9.14.0 h1:u4tNCjXOyzfgeLN+vAZaW1xUooqWDqVEsZN0U01jfAE= github.com/redis/go-redis/v9 v9.14.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= +github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= +github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00= +github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -108,10 +113,18 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tiendc/go-deepcopy v1.7.1 h1:LnubftI6nYaaMOcaz0LphzwraqN8jiWTwm416sitff4= +github.com/tiendc/go-deepcopy v1.7.1/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8= +github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= +github.com/xuri/excelize/v2 v2.10.0 h1:8aKsP7JD39iKLc6dH5Tw3dgV3sPRh8uRVXu/fMstfW4= +github.com/xuri/excelize/v2 v2.10.0/go.mod h1:SC5TzhQkaOsTWpANfm+7bJCldzcnU/jrhqkTi/iBHBU= +github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE= +github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= @@ -141,6 +154,8 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/image v0.31.0 h1:mLChjE2MV6g1S7oqbXC0/UcKijjm5fnJLUYKIYrLESA= golang.org/x/image v0.31.0/go.mod h1:R9ec5Lcp96v9FTF+ajwaH3uGxPH4fKfHHAVbUILxghA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -159,6 +174,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -180,6 +197,8 @@ golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -201,6 +220,8 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/models/twitter_account.go b/models/twitter_account.go index 70fce03..4c56a12 100644 --- a/models/twitter_account.go +++ b/models/twitter_account.go @@ -3,24 +3,27 @@ package models import "github.com/go-rod/rod/lib/proto" type TwitterAccount struct { - ID int64 `gorm:"id;primary_key;auto_increment"` - Uid string `gorm:"uid"` - Name string `gorm:"name"` - Email string `gorm:"email"` - Password string `gorm:"password"` - EmailPassword string `gorm:"email_password"` - Birthday string `gorm:"birthday"` - Token string `gorm:"token"` - Fa2 string `gorm:"fa2"` - Cookies []*proto.NetworkCookieParam `gorm:"img;type:json;serializer:json"` - Type string `gorm:"type"` - Status string `gorm:"status"` - LoginStatus string `gorm:"login_status"` - Proxy string `gorm:"proxy"` - BrowserId string `gorm:"browser_id"` - BrowserUrl string `gorm:"browser_url"` - UpdateTime int64 `gorm:"update_time"` - CreateTime int64 `gorm:"create_time"` + ID int64 `gorm:"id;primary_key;auto_increment"` + Uid string `gorm:"uid"` + Name string `gorm:"name"` + Email string `gorm:"email"` + Password string `gorm:"password"` + EmailPassword string `gorm:"email_password"` + Birthday string `gorm:"birthday"` + Token string `gorm:"token"` + Fa2 string `gorm:"fa2"` + Cookies []*proto.NetworkCookieParam `gorm:"img;type:json;serializer:json"` + Type string `gorm:"type"` + Status string `gorm:"status"` + LoginStatus string `gorm:"login_status"` + Proxy string `gorm:"proxy"` + BrowserId string `gorm:"browser_id"` + BrowserUrl string `gorm:"browser_url"` + RegisterDate string `gorm:"register_date"` + RegisterAddress string `gorm:"register_address"` + RegisterIp string `gorm:"register_ip"` + UpdateTime int64 `gorm:"update_time"` + CreateTime int64 `gorm:"create_time"` } // 实现 TableName 方法指定表名 diff --git a/plugin/task-get-plugin.go b/plugin/task-get-plugin.go new file mode 100644 index 0000000..ade7907 --- /dev/null +++ b/plugin/task-get-plugin.go @@ -0,0 +1,101 @@ +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 TaskGetPlugin struct{} + +func (*TaskGetPlugin) Run(task *paramsTypes.Task) { + + // 查询当前所有账号 + userService := services.InitTwitterAccountService() + // 判断是否是补偿任务 + var userList []models.TwitterAccount + userList = userService.GetAvailableAccount(&models.TwitterAccount{ + Type: "get", + }) + + if task.Data.Compensate == 1 { + // 补偿任务 + userList = userService.GetAccountListByStatus(&models.TwitterAccount{ + Status: "成功", + Type: "get", + }) + } else { + // 清空当前运行的账号状态 + userService.ClearAccountStatus(&models.TwitterAccount{ + Type: "get", + }) + } + log.Println("可用账号数量:", len(userList)) + // 获取并发数 + appConfig, _ := config.LoadConfig() + num := math.Ceil(float64(len(userList)) / 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(userList) { + break + } + time.Sleep(5 * time.Second) + globalWait.Add(1) + sem <- struct{}{} // 阻塞直到有空位 + go func() { + defer func() { + <-sem // 释放信号量 + globalWait.Done() + }() + getAccountWork(task, userList[taskIndex].ID) + }() + } + } + globalWait.Wait() + + task.Message <- "Success" +} + +func getAccountWork(task *paramsTypes.Task, id int64) { + UserService := services.InitTwitterAccountService() + defer func() { + if r := recover(); r != nil { + log.Printf("Goroutine panic: %v\nStack: %s", r, debug.Stack()) + UserService.ChangeAccountLoginStatus(int(id), "账号下线") + TwitterService.Logout(int(id)) + } + }() + + // 开始登录 + + res := TwitterService.Login(int(id)) + UserService.ChangeAccountLoginStatus(int(id), res.Msg) + if res.Code == 200 { + // 开始拟人 + // res = TwitterService.LoopViewPost(int(id)) + + time.Sleep(2 * time.Second) + UserService.ChangeAccountStatus(int(id), "成功") + TwitterService.Logout(int(id)) + UserService.ChangeAccountLoginStatus(int(id), "账号下线") + } + + log.Println(task.ID) +} diff --git a/router/twitter.go b/router/twitter.go index 472d9cc..32b34e6 100644 --- a/router/twitter.go +++ b/router/twitter.go @@ -17,4 +17,5 @@ func (*Twitter) TwitterRouterInit(app *gin.Engine) { twitter.GET("/logout", twitterApi.Logout) // 退出登录 twitter.POST("/register1", twitterApi.Register1) twitter.GET("/test", twitterApi.Test) + twitter.GET("/export", twitterApi.Export) // 导出接口 } diff --git a/services/twitter_account_export_service.go b/services/twitter_account_export_service.go new file mode 100644 index 0000000..9c75ec6 --- /dev/null +++ b/services/twitter_account_export_service.go @@ -0,0 +1,83 @@ +package services + +import ( + "go-account-register/models" + "log" + + "github.com/xuri/excelize/v2" +) + +// 导出 Twitter 账号数据到 Excel +func ExportTwitterAccounts() ([]string, error) { + accountService := InitTwitterAccountService() + accounts := accountService.GetAll(&models.TwitterAccount{ + Type: "get", + }) + + // 分类 + countryMap := map[string][]models.TwitterAccount{ + "United States": {}, + "Singapore": {}, + "Japan": {}, + "Taiwan": {}, + "Other": {}, + } + for _, acc := range accounts { + addr := acc.RegisterAddress + if addr == "United States" || addr == "Singapore" || addr == "Japan" || addr == "Taiwan" { + countryMap[addr] = append(countryMap[addr], acc) + } else { + countryMap["Other"] = append(countryMap["Other"], acc) + } + } + + var files []string + for k, v := range countryMap { + if len(v) == 0 { + continue + } + file := excelize.NewFile() + titles := []string{"编号", "用户id", "用户名", "邮箱", "密码", "邮箱密码", "生日", "Token", "二步验证码", "Cookies", "类型", "状态", "登录状态", "代理", "浏览器ID", "浏览器地址", "注册时间", "注册地区", "注册IP", "更新时间", "创建时间"} + for i, t := range titles { + cell, _ := excelize.CoordinatesToCellName(i+1, 1) + file.SetCellValue("Sheet1", cell, t) + } + for row, acc := range v { + vals := []interface{}{ + acc.ID, + acc.Uid, + acc.Name, + acc.Email, + acc.Password, + acc.EmailPassword, + acc.Birthday, + acc.Token, + acc.Fa2, + acc.Cookies, + acc.Type, + acc.Status, + acc.LoginStatus, + acc.Proxy, + acc.BrowserId, + acc.BrowserUrl, + acc.RegisterDate, + acc.RegisterAddress, + acc.RegisterIp, + acc.UpdateTime, + acc.CreateTime, + } + for col, val := range vals { + cell, _ := excelize.CoordinatesToCellName(col+1, row+2) + file.SetCellValue("Sheet1", cell, val) + } + } + filename := "twitter_accounts_" + k + ".xlsx" + if err := file.SaveAs(filename); err != nil { + log.Println("保存文件失败:", filename, err) + } else { + files = append(files, filename) + log.Println("已导出:", filename) + } + } + return files, nil +} diff --git a/services/twitter_account_service.go b/services/twitter_account_service.go index cc77ea0..851dd43 100644 --- a/services/twitter_account_service.go +++ b/services/twitter_account_service.go @@ -17,6 +17,12 @@ func InitTwitterAccountService() *TwitterAccountService { return &TwitterAccountService{db: db} } +func (t *TwitterAccountService) GetAll(params *models.TwitterAccount) []models.TwitterAccount { + var accounts []models.TwitterAccount + t.db.Model(&models.TwitterAccount{}).Where(params).Find(&accounts) + return accounts +} + func (t *TwitterAccountService) Create(params *models.TwitterAccount) int64 { params.CreateTime = time.Now().Unix() params.UpdateTime = time.Now().Unix() diff --git a/services/twitter_service.go b/services/twitter_service.go index a5609f7..9bd92b1 100644 --- a/services/twitter_service.go +++ b/services/twitter_service.go @@ -110,6 +110,7 @@ func (*TwitterService) Login(id int) *libs.ErrorInfo { fmt.Println(err) } getAccountInfo(page, int64(id)) + getRegisterInfo(page, int64(id), user.Password) return libs.ErrorCode["LoginSuccessful"] } @@ -170,6 +171,53 @@ func getAccountInfo(page *rod.Page, id int64) { }) } +func getRegisterInfo(page *rod.Page, id int64, password string) { + // 进入账号信息页面 + page.MustNavigate("https://x.com/settings/your_twitter_data/account").MustWaitLoad() + page.MustWaitNavigation() + page.MustWaitLoad() // DOM 加载完成 + page.MustWaitIdle() // 基本静止(减少早期误判) + page.MustActivate() // 前置 + // 判断当前是否需要输入密码 + inpurPassword, err := page.Timeout(5 * time.Second).Element(`input[name="current_password"]`) + if err == nil { + if is, err1 := inpurPassword.Visible(); err1 == nil && is { + inpurPassword.MustInput(password) + } + ConfirmButton := page.Timeout(5 * time.Second).MustElement(`button.css-175oi2r.r-sdzlij.r-1phboty.r-rs99b7.r-lrvibr.r-ddtstp.r-1xpp3t0.r-2yi16.r-1qi8awa.r-3pj75a.r-1loqt21.r-o7ynqc.r-6416eg.r-1ny4l3l`) + ConfirmButton.MustClick() + } + time.Sleep(5 * time.Second) + // 获取创建时间 + dateElement := page.Timeout(10 * time.Second).MustElement(`#react-root > div > div > div.css-175oi2r.r-1f2l425.r-13qz1uu.r-417010.r-18u37iz > main > div > div > div > section:nth-child(2) > div.css-175oi2r.r-qocrb3.r-14lw9ot.r-1h0z5md.r-1jx8gzb.r-f8sm7e.r-13qz1uu.r-1ye8kvj > div > div:nth-child(6) > div:nth-child(2)`) + createdAt, _ := dateElement.Text() + // 获取注册IP + ipElement := page.Timeout(10 * time.Second).MustElement(`#react-root > div > div > div.css-175oi2r.r-1f2l425.r-13qz1uu.r-417010.r-18u37iz > main > div > div > div > section:nth-child(2) > div.css-175oi2r.r-qocrb3.r-14lw9ot.r-1h0z5md.r-1jx8gzb.r-f8sm7e.r-13qz1uu.r-1ye8kvj > div > div:nth-child(6) > div:nth-child(3)`) + ipAddr, _ := ipElement.Text() + + // 分离IP地址和地区 + var ip, region string + // ipAddr 格式: "68.132.165.85 (United States)" + if strings.Contains(ipAddr, "(") { + parts := strings.Split(ipAddr, " (") + ip = strings.TrimSpace(parts[0]) + if len(parts) > 1 { + region = strings.TrimRight(parts[1], ")") + } + } else { + ip = ipAddr + } + log.Println("注册IP:", ip) + log.Println("注册地区:", region) + log.Println("创建时间:", createdAt) + userService := InitTwitterAccountService() + userService.Update(id, &models.TwitterAccount{ + RegisterIp: ip, + RegisterDate: createdAt, + RegisterAddress: region, + }) +} + // 退出登录 func (*TwitterService) Logout(id int) { Browser.CancelBrowser(id) @@ -705,7 +753,16 @@ func (*TwitterService) LoopViewPost(id int) *libs.ErrorInfo { time.Sleep(time.Duration(s2) * time.Second) log.Println("浏览推文内容链接:", v.ContentLink) } - + } return libs.ErrorCode["ViewSuccessful"] } + +// 导出 Twitter 账号数据到 Excel +func (s *TwitterService) ExportAccounts() (code int, msg string, files []string) { + result, err := ExportTwitterAccounts() + if err != nil { + return 500, "导出失败", nil + } + return 200, "导出成功", result +} diff --git a/task/scheduler.go b/task/scheduler.go index 81a04ec..3616714 100644 --- a/task/scheduler.go +++ b/task/scheduler.go @@ -170,9 +170,9 @@ func (s *Scheduler) processQueue() { func (s *Scheduler) executeTask(task *paramsTypes.Task) { // 根据类型判断任务 switch task.Type { - // case 6: - // var taskNurturingClashPlugin plugin.TaskNurturingClashPlugin - // go taskNurturingClashPlugin.Run(task) + case 2: + var taskGetPlugin plugin.TaskGetPlugin + go taskGetPlugin.Run(task) case 1: var taskViewPostPlugin plugin.TaskViewPostPlugin go taskViewPostPlugin.Run(task)