新增将账号导入到adspower的方法

This commit is contained in:
zyj
2025-11-21 10:52:22 +08:00
parent ecc99977d2
commit 87a077d40c
7 changed files with 648 additions and 0 deletions

View File

@@ -0,0 +1,475 @@
package services
import (
"context"
"encoding/json"
"errors"
"fmt"
"go-account-register/config"
"go-account-register/libs"
"go-account-register/models"
"go-account-register/utils"
"log"
"math/rand"
"os"
"strings"
"time"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/input"
"github.com/go-rod/rod/lib/proto"
)
type AdsPowerService struct {
}
func (*AdsPowerService) CreateBrowser(accountId int64) string {
TwitterAccount := InitTwitterAccountService()
user := TwitterAccount.GetInfo(accountId)
fingerprint := map[string]interface{}{
"name": fmt.Sprintf(`推特账号%d`, accountId),
"group_id": "0",
"platform": "",
"user_proxy_config": map[string]interface{}{
"proxy_soft": "no_proxy",
"proxy_type": "socks5",
},
"fingerprint_config": map[string]interface{}{
"webrtc": "proxy",
"random_ua": map[string]interface{}{
"ua_browser": []string{
"chrome",
},
"ua_system_version": []string{
"Windows 10",
"Windows 11",
"Mac OS X 15",
"Mac OS X 14",
},
},
},
}
if user.Cookies != nil {
fingerprint["cookie"] = user.Cookies
} else {
cookieObj := []map[string]interface{}{
{
"name": "auth_token",
"value": user.Token,
"domain": ".x.com",
"path": "/",
"httpOnly": false,
"secure": true,
"session": true,
"expires": proto.TimeSinceEpoch(time.Now().Add(24 * time.Hour).Unix()),
"sameSite": "unspecified",
},
}
cookieJson, _ := json.Marshal(cookieObj)
fingerprint["cookie"] = string(cookieJson)
}
if user.Proxy != "" {
var proxyHandle utils.ProxyHandle
protocol, username, password, ip, port, err2 := proxyHandle.ParseProxy(user.Proxy)
if err2 == nil {
fingerprint["user_proxy_config"] = map[string]interface{}{
"proxy_soft": "other",
"proxy_type": protocol,
"proxy_host": ip,
"proxy_port": port,
"proxy_user": username,
"proxy_password": password,
}
}
}
adsPowerRequest := utils.NewAdsPowerRequest()
jsonStr, _ := adsPowerRequest.CreateBrowser(fingerprint)
var rawData map[string]interface{}
if err := json.Unmarshal([]byte(jsonStr.Body()), &rawData); err != nil {
panic(err)
}
log.Println("创建浏览器返回数据", rawData)
success := rawData["msg"].(string)
if success == "Success" {
// 如果成功则将数据存入数据库
data := rawData["data"].(map[string]interface{})
id := data["profile_id"].(string)
// jsonData, _ := json.Marshal(data)
TwitterAccount.Update(accountId, &models.TwitterAccount{
BrowserId: id,
})
return id
}
return ""
}
func (*AdsPowerService) GetRunStatus(id string) int {
adsPowerRequest := utils.NewAdsPowerRequest()
jsonStr, _ := adsPowerRequest.GetRunStatus(id)
var rawData map[string]interface{}
if err := json.Unmarshal([]byte(jsonStr.Body()), &rawData); err != nil {
panic(err)
}
success := rawData["msg"].(string)
if success == "success" {
data := rawData["data"].(map[string]interface{})
status := data["status"].(string)
if status == "Active" {
return 1
}
return 0
}
return 0
}
// 打开浏览器
func (*AdsPowerService) OpenBrowser(accountId int, browserId string) string {
// 获取当前
adsPowerRequest := utils.NewAdsPowerRequest()
data := map[string]interface{}{
"profile_id": browserId,
"delete_cache": "1",
// "last_opened_tabs": "0",
}
jsonStr, _ := adsPowerRequest.OpenBrowser(data)
var rawData map[string]interface{}
if err := json.Unmarshal([]byte(jsonStr.Body()), &rawData); err != nil {
panic(err)
}
success := rawData["msg"].(string)
if success == "success" {
data := rawData["data"].(map[string]interface{})
ws := data["ws"].(map[string]interface{})
puppeteer := ws["puppeteer"].(string)
TwitterAccount := InitTwitterAccountService()
TwitterAccount.Update(int64(accountId), &models.TwitterAccount{
BrowserUrl: puppeteer,
})
return puppeteer
}
return ""
}
// 删除浏览器
func (*AdsPowerService) DeleteBrowser(browserId string) bool {
adsPowerRequest := utils.NewAdsPowerRequest()
// 先关闭浏览器
_, err := adsPowerRequest.CloseBrowser(browserId)
if err != nil {
log.Println("关闭浏览器失败")
}
// 删除浏览器
_, err2 := adsPowerRequest.DeleteBrowser(browserId)
log.Println("删除浏览器,", err2 == nil)
return err2 == nil
}
// 关闭浏览器
func (*AdsPowerService) CloseBrowser(browserId string) bool {
adsPowerRequest := utils.NewAdsPowerRequest()
jsonStr, _ := adsPowerRequest.CloseBrowser(browserId)
var result struct {
Data string `json:"data"` // 动态键名 → 值
Success bool `json:"success"`
}
if err := json.Unmarshal([]byte(jsonStr.Body()), &result); err != nil {
return false
}
return true
}
func randNum(min, max int) int {
return rand.Intn(max-min) + min
}
func getKey(id string) input.Key {
keys := map[string]input.Key{
"0": input.Numpad0,
"1": input.Numpad1,
"2": input.Numpad2,
"3": input.Numpad3,
"4": input.Numpad4,
"5": input.Numpad5,
"6": input.Numpad6,
"7": input.Numpad7,
"8": input.Numpad8,
"9": input.Numpad9,
"+": input.NumpadAdd,
}
return keys[id]
}
// 将指定元素滑动到顶部
func scrollElementLocationCenter(el *rod.Element) {
// 执行JS滚动到视口顶部平滑滚动
el.MustEval(`function() {
this.scrollIntoView({
behavior: "smooth",
block: "center", // 关键参数:垂直居中
inline: "center" // 水平居中
});
}`)
// 等待滚动完成
// time.Sleep(2 * time.Second)
}
// 发推
func (*AdsPowerService) SendPost(accountId int, Content string, Img []string) *libs.ErrorInfo {
// 获取账号状态
UserService := InitTwitterAccountService()
user := UserService.GetInfo(int64(accountId))
if user.LoginStatus != "登录成功" {
return libs.ErrorCode["AccountHasBeenTakenOffline"]
}
// 获取实例
obj := Browser.GetBitBrowser(user.BrowserUrl)
page := obj.Page
// 跳转到首页
page.MustNavigate("https://x.com/home").MustWaitLoad()
err := rod.Try(func() {
// 获取内容输入框
textInput := page.Timeout(10 * time.Second).MustElement(`div.notranslate.public-DraftEditor-content`)
textInput.MustInput(Content)
// 检测字符串是否包含标签
if strings.Contains(Content, "#") {
_, err := page.Timeout(5 * time.Second).Element(`div.css-175oi2r.r-14lw9ot.r-z2wwpe.r-1upvrn0.r-h3f8nf.r-11yh6sk.r-1rnoaur`)
time.Sleep(2 * time.Second)
if err == nil {
tagBuuton := page.Timeout(5 * time.Second).MustElement(`div.css-175oi2r.r-14lw9ot.r-z2wwpe.r-1upvrn0.r-h3f8nf.r-11yh6sk.r-1rnoaur div[data-testid="typeaheadResult"] button.css-175oi2r.r-x572qd.r-6dt33c.r-1loqt21.r-o7ynqc.r-6416eg.r-1ny4l3l`)
tagBuuton.MustClick()
time.Sleep(2 * time.Second)
}
}
// 循环保存图片到本地
if len(Img) > 0 {
var file libs.File
for _, v := range Img {
tempFile, _ := file.DownloadImage(v)
// 结束时删除临时文件
defer os.Remove(tempFile)
page.MustElement(`input[data-testid="fileInput"]`).MustSetFiles(tempFile)
time.Sleep(1 * time.Second)
}
time.Sleep(5 * time.Second)
}
})
if errors.Is(err, context.DeadlineExceeded) {
fmt.Println("请求空闲等待超时!")
return libs.ErrorCode["SendPostFailed"]
}
code := postButton(page)
if code == 200 {
return libs.ErrorCode["SendPostSuccessful"]
}
return libs.ErrorCode["SendPostFailed"]
}
// 模拟人类刷帖子 限制时间
func (*AdsPowerService) ViewPostsSpecifiedTime(accountId int) *libs.ErrorInfo {
UserService := InitTwitterAccountService()
user := UserService.GetInfo(int64(accountId))
if user.LoginStatus != "登录成功" {
return libs.ErrorCode["AccountHasBeenTakenOffline"]
}
// 获取实例
obj := Browser.GetBitBrowser(user.BrowserUrl)
page := obj.Page
// 跳转到首页
page.MustNavigate("https://x.com/home").MustWaitLoad()
forYou := page.Timeout(10 * time.Second).MustElement(`div[data-testid="ScrollSnap-List"] div.css-175oi2r.r-14tvyh0.r-cpa5s6.r-16y2uox:nth-child(1)`)
forYou.MustClick()
page.MustWaitNavigation()
// 是否检测到帖子列表
err := rod.Try(func() {
page.Timeout(5 * time.Second).MustElement(`article[data-testid="tweet"]`)
})
if errors.Is(err, context.DeadlineExceeded) {
fmt.Println("请求空闲等待超时!")
// 关闭浏览器
Browser.CancelBrowser(accountId)
return libs.ErrorCode["NetworkTimeout"]
}
var status chan string = make(chan string)
// 获取配置中的时间限制
appConfig, _ := config.LoadConfig()
randTime := appConfig.Limit.ViweTimeMin + rand.Intn(appConfig.Limit.ViweTimeMax-appConfig.Limit.ViweTimeMin)
go loopViewPosts(page, accountId, status, randTime)
select {
case res := <-status:
if res == "fali" {
return libs.ErrorCode["MaintainAccountFailed"]
}
if res == "success" {
return libs.ErrorCode["MaintainAccountSuccessful"]
}
case <-time.After(time.Duration(randTime) * time.Minute):
return libs.ErrorCode["MaintainAccountSuccessful"]
}
return libs.ErrorCode["MaintainAccountSuccessful"]
// // 获取配置中的时间限制
// appConfig, _ := config.LoadConfig()
// randTime := appConfig.Limit.ViweTimeMin + rand.Intn(appConfig.Limit.ViweTimeMax-appConfig.Limit.ViweTimeMin)
// // 获取当前时间戳
// startTime := time.Now().Unix()
// var activeTime int64 = time.Now().Unix()
// // 函数报错则向这个通道发送消息
// ch := make(chan string, 1)
// for activeTime-startTime < int64(randTime*60) {
// select {
// case <-ch:
// // 失败
// return libs.ErrorCode["MaintainAccountFailed"]
// default:
// viewPosts(page, ch, accountId)
// }
// activeTime = time.Now().Unix()
// }
// return libs.ErrorCode["MaintainAccountSuccessful"]
// timer := time.NewTimer(time.Duration(randTime) * time.Minute)
// defer timer.Stop()
// ticker := time.NewTicker(1 * time.Second)
// defer ticker.Stop()
// for {
// select {
// case <-timer.C:
// fmt.Println("30分钟已到自动返回")
// return libs.ErrorCode["MaintainAccountSuccessful"]
// case <-ch:
// // 失败
// return libs.ErrorCode["MaintainAccountFailed"]
// case <-ticker.C:
// viewPosts(page, ch, accountId)
// }
// }
}
func loopViewPosts(page *rod.Page, accountId int, status chan string, randTime int) {
// 获取当前时间戳
startTime := time.Now().Unix()
var activeTime int64 = time.Now().Unix()
// 函数报错则向这个通道发送消息
ch := make(chan string, 1)
for activeTime-startTime < int64(randTime*60) {
viewPosts(page, ch, accountId)
activeTime = time.Now().Unix()
}
status <- "success"
}
// 查看帖子
func viewPosts(page *rod.Page, ch chan string, accountId int) bool {
// 滑动滚动条
log.Println("开始滚动", accountId)
randomTrackScroll(page)
log.Println("结束滚动", accountId)
is := rand.Float64() // 生成随机数
if is < 0.5 {
err := rod.Try(func() {
// 获取视口内的帖子列表
list, err := getVisibleElements(page, `div[data-testid="tweetText"]`)
if err != nil || len(list) <= 0 {
return
}
// 等待两秒
time.Sleep(2 * time.Second)
// 随机选择一个点击进入帖子
tweetText := list[rand.Intn(len(list))]
log.Println("选择一个帖子:", accountId, tweetText)
scrollElementLocationCenter(tweetText)
// 禁用链接点击
_, err3 := page.Evaluate(&rod.EvalOptions{
JS: `(selector) => {
const el = document.querySelector(selector);
const links = el.querySelectorAll('a');
links.forEach(a => {
a.style.pointerEvents = 'none';
});
}`,
JSArgs: []interface{}{`div[data-testid="tweetText"]`},
})
if err3 != nil {
}
tweetText.MustClick()
// 恢复链接的点击行为(如果需要)
_, _ = page.Evaluate(&rod.EvalOptions{
JS: `(selector) => {
const el = document.querySelector(selector);
const links = el.querySelectorAll('a');
links.forEach(a => {
a.style.pointerEvents = '';
});
}`,
JSArgs: []interface{}{`div[data-testid="tweetText"]`},
})
page.MustWaitNavigation()
// 查看帖子 3-5分钟
timeMin := 3 + rand.Intn(2)
time.Sleep(time.Duration(timeMin) * time.Minute)
// 退回到上一步
// page.MustNavigateBack()
// 等待新页面加载完成
// page.MustWaitLoad()
page.MustNavigate("https://x.com/home").MustWaitLoad()
// 判断当前URL是否与预期的URL一致
url := page.MustEval("() => window.location.href").String()
if url != "https://x.com/home" {
// 如果不一致 就直接跳转
page.MustNavigate("https://x.com/home").MustWaitLoad()
}
})
if err != nil {
ch <- "error"
}
return true
}
return false
}
// 随机滚动
func randomTrackScroll(page *rod.Page) {
// height, _ := page.Eval(`() => document.body.scrollHeight`)
// totalHeight := int(height.Value.Num())
err := rod.Try(func() {
var totalHeight int = 1000 + rand.Intn(1000)
currentPos := 0
log.Println("获取随机滚动的参数:", totalHeight)
for currentPos < totalHeight {
// 随机水平偏移(模拟人类不精确的滑动)
xOffset := rand.Intn(20) - 10 // -10到10之间的随机值
yDistance := 50 + rand.Intn(150)
err := page.Mouse.Scroll(float64(xOffset), float64(yDistance), 1)
if err != nil {
log.Printf("滚动失败: %v", err)
}
currentPos += yDistance
// 随机等待和偶尔的回滑
if rand.Float32() < 0.1 { // 10%概率回滑一点
backStep := 10 + rand.Intn(30)
err := page.Mouse.Scroll(0, -float64(backStep), 1)
if err != nil {
log.Printf("回滑失败: %v", err)
}
currentPos -= backStep
}
time.Sleep(time.Millisecond * time.Duration(50+rand.Intn(300)))
log.Println("滚动完成")
}
})
if err != nil {
}
}

View File

@@ -108,3 +108,15 @@ func (u *TwitterAccountService) Update(id int64, params *models.TwitterAccount)
func (u *TwitterAccountService) Delete(id int64) {
u.db.Delete(&models.TwitterAccount{}, id)
}
// 获取可用账号
func (u *TwitterAccountService) GetAvailableImportAccount(params *models.TwitterAccount) []models.TwitterAccount {
var users []models.TwitterAccount
res := u.db.Model(&models.TwitterAccount{}).Where(`(browser_id = '' OR browser_id IS NULL) AND (login_status != "账号异常" OR login_status IS NULL)`).Where(params)
// if params.Username != "" {
// res.Where("username LIKE ?", "%"+params.Username+"%")
// }
res.Order("id DESC").Find(&users)
return users
}