Files
go-account-register/services/bit_browser_service.go
2025-09-30 11:19:39 +08:00

438 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 Voices0随机1关闭
// "hardwareConcurrency": "4", // 硬件并发数
// "deviceMemory": "8", // 设备内存48不要传入大于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"]
}
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
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"]`)
// if err != nil {
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()
// }
// 获取邮箱输入框
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, "")
}
//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()
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()
}
// 删除浏览器
// 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"]
}