package services import ( "encoding/json" "fmt" "go-account-register/libs" "go-account-register/models" "go-account-register/utils" "log" "strconv" "time" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/proto" ) type BitBrowserService struct { } var Browser libs.Browser var Chrome libs.Chrome 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{}{ "name": fmt.Sprintf(`推特注册%s`, utils.RandString(10)), "proxyMethod": 2, "proxyType": "noproxy", "host": "", "port": 0, "proxyUserName": "", "proxyPassword": "", "randomFingerprint": true, "syncTabs": false, "syncCookies": false, "browserFingerPrint": map[string]interface{}{ // "coreVersion": "130", // "ostype": "PC", // "os": "Win32", // "osVersion": "11,10", // "openWidth": 1920, // "openHeight": 1480, // "resolutionType": "0", // "userAgent": "", // ua,不填则自动生成 // "isIpCreateTimeZone": true, // 基于IP生成对应的时区 // "timeZone": "", // 时区,isIpCreateTimeZone 为false时,参考附录中的时区列表 // "timeZoneOffset": 0, // isIpCreateTimeZone 为false时设置,时区偏移量 // "webRTC": "3", //webrtc 0 => 替换, 1 => 允许, 2 => 禁止, 3 => 隐私 // "ignoreHttpsErrors": false, // 忽略https证书错误,true, false // "position": "1", //地理位置 0 => 询问, 1 => 允许, 2 => 禁止 // "isIpCreatePosition": true, // 是否基于IP生成对应的地理位置 // "lat": "", // 纬度 isIpCreatePosition 为false时设置 // "lng": "", // 经度 isIpCreatePosition 为false时设置 // "precisionData": "", //精度米 isIpCreatePosition 为false时设置 // "isIpCreateLanguage": true, // 是否基于IP生成对应国家的浏览器语言 // "languages": "", // isIpCreateLanguage 为false时设置,值参考附录 // "isIpCreateDisplayLanguage": true, // 是否基于IP生成对应国家的浏览器界面语言 // "displayLanguages": "", // isIpCreateDisplayLanguage 为false时设置,默认为空,即跟随系统,值参考附录 // "windowSizeLimit": true, // 分辨率类型为自定义,且ostype为PC时,此项有效,约束窗口最大尺寸不超过分辨率 // "devicePixelRatio": 1, // 显示缩放比例,默认1,填写时,建议 1, 1.5, 2, 2.5, 3 // "fontType": "2", // 字体生成类型 0 => 系统默认 | 2 => 随机 // "canvas": "0", //canvas 0随机|1关闭 // "webGL": "0", //webGL图像,0随机|1关闭 // "webGLMeta": "0", //webgl元数据 0自定义|1关闭 // "webGLManufacturer": "", // webGLMeta 自定义时,webGL厂商值,建议留空会自动生成 // "webGLRender": "", // webGLMeta自定义时,webGL渲染值,建议留空自动生成 // "audioContext": "0", // audioContext值,0随机|1关闭 // "mediaDevice": "0", // 媒体设备,0 随机 | 1 关闭 // "speechVoices": "0", // Speech Voices,0随机|1关闭 // "hardwareConcurrency": "4", // 硬件并发数 // "deviceMemory": "8", // 设备内存,4,8,不要传入大于8的值 // "doNotTrack": "1", // doNotTrack 1开启|0关闭 // "clientRectNoiseEnabled": true, // ClientRects true使用相匹配的值代替您真实的ClientRects | false每个浏览器使用当前电脑默认的ClientRects // "portScanProtect": "0", // 端口扫描保护 0开启|1关闭,注意默认开启保护,组织所有本地127的ws链接,比如某些打印机之类的,如有连接本地服务需求,建议关闭,或者在 portWhiteList 中,填写对应端口,加入白名单 // "portWhiteList": "", // 端口扫描保护开启时的白名单,逗号分隔 // "deviceInfoEnabled": true, // 自定义设备信息,默认开启 // "computerName": "", // deviceInfoEnabled 为true时设置,建议留空系统自动生成即可 // "macAddr": "", // deviceInfoEnabled 为true时设置,建议留空系统自动生成即可 // "disableSslCipherSuitesFlag": false, // ssl是否禁用特性,默认不禁用,注意开启后自定义设置时,有可能会导致某些网站无法访问 // "disableSslCipherSuites": nil, // ssl 禁用特性,序列化的ssl特性值,参考附录 // "enablePlugins": false, // 是否启用插件指纹 // "plugins": "", // enablePlugins为true时,序列化的插件值,插件指纹值参考附录 // "launchArgs": "", // 启动参数,如无痕模式打开,那么设置启动参数为 "--incognito", 多个启动参数用逗号分隔,如 "--incognito,--no-sandbox" }, } 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 { var proxyHandle utils.ProxyHandle protocol, username, password, ip, port, err2 := proxyHandle.ParseProxy(proxy.Type + "://" + proxy.Proxy) if err2 == nil { fingerprint["proxyType"] = protocol fingerprint["host"] = ip fingerprint["port"] = port fingerprint["proxyUserName"] = username fingerprint["proxyPassword"] = password } } bitBrowserRequest := utils.NewBitBrowserRequest() jsonStr, _ := bitBrowserRequest.CreateBrowser(fingerprint) var rawData map[string]interface{} if err := json.Unmarshal([]byte(jsonStr.Body()), &rawData); err != nil { panic(err) } success := rawData["success"].(bool) if success { // 如果成功则将数据存入数据库 data := rawData["data"].(map[string]interface{}) id := data["id"].(string) browserInstanceService := InitBrowserInstanceService() var params = &models.BrowserInstance{ BrowserId: id, Fingerprint: data, UpdateTime: time.Now().Unix(), CreateTime: time.Now().Unix(), } if proxy != nil && proxy.Type != "" { params.Proxy = proxy.Type + "://" + proxy.Proxy } browserInstanceService.Create(params) return id } return "" } // 获取当前浏览器的pid func (*BitBrowserService) GetPid(id string) int { // 获取当前 bitBrowserRequest := utils.NewBitBrowserRequest() jsonStr, _ := bitBrowserRequest.GetPids([]string{id}) var result struct { Data map[string]int `json:"data"` // 动态键名 → 值 Success bool `json:"success"` } if err := json.Unmarshal([]byte(jsonStr.Body()), &result); err != nil { panic(err) } success := result.Success if success { data := result.Data if value, exists := data[id]; exists { return value } else { return 0 } } return 0 } // 打开浏览器 func (*BitBrowserService) OpenBrowser(browserId string) string { // 获取当前 bitBrowserRequest := utils.NewBitBrowserRequest() data := map[string]interface{}{ "id": browserId, "queue": true, } jsonStr, _ := bitBrowserRequest.OpenBrowser(data) var rawData map[string]interface{} if err := json.Unmarshal([]byte(jsonStr.Body()), &rawData); err != nil { panic(err) } success := rawData["success"].(bool) if success { data := rawData["data"].(map[string]interface{}) ws := data["ws"].(string) browserInstanceService := InitBrowserInstanceService() browserInstanceService.Update(browserId, &models.BrowserInstance{ BrowserUrl: ws, }) return ws } return "" } // 删除浏览器 func (*BitBrowserService) DeleteBrowser(browserId string) bool { bitBrowserRequest := utils.NewBitBrowserRequest() // 先关闭浏览器 _, err := bitBrowserRequest.CloseBrowser(browserId) if err != nil { log.Println("关闭浏览器失败") } // 删除浏览器 _, err2 := bitBrowserRequest.DeleteBrowser(browserId) log.Println("删除浏览器,", err2 == nil) return err2 == nil } func (bit *BitBrowserService) Register() *libs.ErrorInfo { // 随机获取代理 proxyService := InitProxyService() proxy := proxyService.GetInfo() var browserId string // 创建新的浏览器 if proxy != nil { browserId = bit.CreateBrowser(proxy, nil) } else { browserId = bit.CreateBrowser(nil, nil) } // 打开浏览器 if browserId == "" { return libs.ErrorCode["BrowserCreationFailed"] } time.Sleep(10 * time.Second) ws := bit.OpenBrowser(browserId) if ws == "" { return libs.ErrorCode["BrowserCreationFailed"] } // 获取一个邮箱 emailService := InitEmailService() emailInfo := emailService.GetEmailOne() log.Println("当前邮箱信息", emailInfo) obj := Browser.GetBitBrowser(ws) page := obj.Page err := rod.Try(func() { 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() }) if err != nil { page.MustReload() time.Sleep(10 * time.Second) // 点击创建账号 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() } time.Sleep(5 * time.Second) // 判断当前是否是邮箱注册 // _, err := page.Element(`input[name="email"]`) // if err != nil { switchButton, switchButtonErr := page.Timeout(5 * time.Second).Element(`button.css-146c3p1.r-bcqeeo.r-qvutc0.r-37j5jr.r-1ff274t.r-a023e6.r-rjixqe.r-16dba41`) if switchButtonErr == nil { switchButton.MustClick() } emailButtonQ, switchButtonErr := page.Timeout(5 * time.Second).Element(`button.css-146c3p1.r-bcqeeo.r-qvutc0.r-1tl8opc.r-1ff274t.r-a023e6.r-rjixqe.r-16dba41`) if switchButtonErr == nil { emailButtonQ.MustClick() } // } // 获取邮箱输入框 inputEmail := page.Timeout(5 * time.Second).MustElement(`input[name="email"]`) inputEmail.MustFocus() inputEmail.MustInput(emailInfo.Email) // 获取名称输入框 inputName := page.Timeout(5 * time.Second).MustElement(`input[name="name"]`) inputName.MustFocus() nameStr := emailInfo.Name if emailInfo.Name == "" { nameStr = utils.RandString(12) } inputName.MustInput(nameStr) // 随机生成生日 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`) log.Println(dateSelects) fmt.Println(strconv.Itoa(dateParams.Month)) fmt.Println(strconv.Itoa(dateParams.Day)) fmt.Println(strconv.Itoa(dateParams.Year)) /// 月份 dateSelects[0].WaitVisible() opts, _ := dateSelects[0].Elements("option") for i, opt := range opts { txt, _ := opt.Text() val, _ := opt.Attribute("value") log.Printf("option[%d] text=%s value=%s\n", i, txt, *val) if i != 0 && i == dateParams.Month { dateSelects[0].MustSelect(txt) } } // 年份 dateSelects[2].WaitVisible() dateSelects[2].MustSelect(strconv.Itoa(dateParams.Year)) // 日期 dateSelects[1].WaitVisible() dateSelects[1].MustSelect(strconv.Itoa(dateParams.Day)) // dateSelects[0].MustClick() // monthOption := dateSelects[0].MustElement(fmt.Sprintf(`option[value="%d"]`, dateParams.Month)) // monthOption.MustClick() time.Sleep(2 * time.Second) // 点击下一步 ocfSignupNextLink := page.Timeout(5 * time.Second).MustElement(`button[data-testid="ocfSignupNextLink"]`) ocfSignupNextLink.MustClick() // 判断是否需要验证码 time.Sleep(4 * time.Second) _, err1 := page.Timeout(5 * time.Second).Element(`input[name="verfication_code"]`) if err1 != nil { // res := getApiResponse(page, "") } var codeInputVer *rod.Element = nil // 循环获取 验证码输入框 for codeInputVer == nil { t, err1 := page.Timeout(5 * time.Second).Element(`input[name="verfication_code"]`) if err1 == nil { codeInputVer = t } time.Sleep(5 * time.Second) } //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"] } // 获取邮箱验证码 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.MustClick() // // 点击验证按钮 // verifyButton := page.Timeout(5 * time.Second).MustElement(`button[data-theme="home.verifyButton"]`) // verifyButton.MustClick() // 输入密码 passwordInput, err2 := page.Timeout(5 * time.Second).Element(`input[name="password"]`) if err2 != nil { // res := getApiResponse(page, "") } passwordInput.MustInput(emailInfo.Password) time.Sleep(5 * time.Second) // 点击下一步 loginButton := page.Timeout(5 * time.Second).MustElement(`button[data-testid="LoginForm_Login_Button"]`) loginButton.MustClick() time.Sleep(5 * time.Second) page.MustNavigate("https://x.com").MustWaitLoad() time.Sleep(10 * time.Second) // 获取cookie cookieArr := GetPageCookie(page, "https://x.com") // 创建用户 twitterAccountService := InitTwitterAccountService() accountId := twitterAccountService.Create(&models.TwitterAccount{ Name: nameStr, Email: emailInfo.Email, Password: emailInfo.Password, Birthday: strconv.Itoa(dateParams.Year) + "-" + strconv.Itoa(dateParams.Month) + "-" + strconv.Itoa(dateParams.Day), Cookies: cookieArr, Status: "注册成功", LoginStatus: "登录成功", Proxy: func() string { if proxy != nil && proxy.Type != "" { return proxy.Type + "://" + proxy.Proxy } return "" }(), }) emailService.Update(emailInfo.Email, models.Email{ UseStatus: 1, }) // 设置fa2 page.MustNavigate("https://x.com/settings/account/login_verification").MustWaitLoad() // 点击fa2按钮 inputCheckbox := page.Timeout(5 * time.Second).MustElement(`input[aria-describedby="CHECKBOX_2_LABEL"]`) inputCheckbox.MustClick() // 等待2秒 time.Sleep(2 * time.Second) // 设置密码 inpurPassword, errPassword := page.Timeout(5 * time.Second).Element(`input[name="password"]`) if errPassword == nil { inpurPassword.MustInput(emailInfo.Password) // 点击确认 faConfirm := page.Timeout(5 * time.Second).MustElement(`button[data-testid="LoginForm_Login_Button"]`) faConfirm.MustClick() } // 等待2秒 time.Sleep(2 * time.Second) ActionListNextButton := page.Timeout(5 * time.Second).MustElement(`button[data-testid="ActionListNextButton"]`) ActionListNextButton.MustClick() // 点击无法扫描二维码 queryCode := page.Timeout(5 * time.Second).MustElement("div.css-175oi2r.r-1awozwy.r-w7s2jr>div.css-175oi2r.r-95jzfe>div.css-146c3p1.r-bcqeeo.r-1ttztb7.r-qvutc0.r-37j5jr.r-a023e6.r-rjixqe.r-16dba41>button.css-1jxf684.r-bcqeeo.r-qvutc0.r-poiln3.r-fdjqy7") queryCode.MustClick() fa2Code := page.Timeout(5 * time.Second).MustElement(`div.css-146c3p1.r-bcqeeo.r-qvutc0.r-37j5jr.r-1blvdjr.r-vrz42v.r-b88u0q.r-x572qd.r-ywje51.r-1oqcu8e.r-q4m81j.r-13qz1uu`) fa2CodeText := fa2Code.MustText() log.Println("当前账号的二步验证码,", fa2CodeText) ocfShowCodeNextLink := page.Timeout(5 * time.Second).MustElement(`button[data-testid="ocfShowCodeNextLink"]`) ocfShowCodeNextLink.MustClick() codeText, _ := utils.GetFA2Code(fa2CodeText) ocfEnterTextTextInput := page.Timeout(5 * time.Second).MustElement(`input[data-testid="ocfEnterTextTextInput"]`) ocfEnterTextTextInput.MustInput(codeText) ocfEnterTextNextButton := page.Timeout(5 * time.Second).MustElement(`button[data-testid="ocfEnterTextNextButton"]`) ocfEnterTextNextButton.MustClick() OCF_CallToAction_Button, errOCF := page.Timeout(5 * time.Second).Element(`button[data-testid="OCF_CallToAction_Button"]`) if errOCF == nil { OCF_CallToAction_Button.MustClick() } if accountId != 0 { twitterAccountService.Update(accountId, &models.TwitterAccount{Fa2: fa2CodeText}) } time.Sleep(2 * time.Second) page.MustNavigate("https://x.com/home").MustWaitLoad() // 删除浏览器 // bit.DeleteBrowser(browserId) return libs.ErrorCode["RegisterSuccessful"] } // 获取指定页面的cookie列表 func GetPageCookie(page *rod.Page, url string) []*proto.NetworkCookieParam { cookieArr, _ := page.Cookies([]string{url}) var cookies []*proto.NetworkCookieParam for _, cookie := range cookieArr { cookies = append(cookies, &proto.NetworkCookieParam{ Name: cookie.Name, Value: cookie.Value, Domain: cookie.Domain, Path: cookie.Path, Expires: cookie.Expires, // 过期时间 HTTPOnly: cookie.HTTPOnly, // 禁止 JS 访问 Secure: cookie.Secure, // 仅 HTTPS 传输 }) } 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"] }