第一版
This commit is contained in:
291
cmd/main.go
291
cmd/main.go
@@ -1,201 +1,156 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go-sccchina/libs"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/proto"
|
||||
"go-taobao/config"
|
||||
"go-taobao/libs"
|
||||
"go-taobao/services"
|
||||
"go-taobao/utils"
|
||||
)
|
||||
|
||||
var login = flag.Bool("login", false, "是否登录")
|
||||
var test = flag.Bool("time", false, "测试")
|
||||
var Browser libs.Browser
|
||||
var (
|
||||
mode = flag.String("mode", "", "运行模式: login(登录) | rush(抢购)")
|
||||
)
|
||||
|
||||
// var Chrome libs.Chrome
|
||||
func main() {
|
||||
flag.Parse()
|
||||
log.Println(*login)
|
||||
if *login {
|
||||
loginFun()
|
||||
}
|
||||
if *test {
|
||||
obj := Browser.GetBrowser(1, &libs.Fingerprint{})
|
||||
browser := obj.Browser
|
||||
page := obj.Page
|
||||
cookies, _ := loadCookiesFromFile("account_cookie.json")
|
||||
browser.SetCookies(cookies)
|
||||
page.MustNavigate("https://cart.taobao.com/cart.htm?spm=tbpc.mytb_index.leftnav.1.6db2782d0TVVip").MustWaitLoad()
|
||||
page.MustWaitNavigation()
|
||||
page.MustWaitLoad() // DOM 加载完成
|
||||
page.MustWaitIdle() // 基本静止(减少早期误判)
|
||||
page.MustActivate() // 前置以便扫码
|
||||
// // 获取当前课程列表
|
||||
// res := getApiResponse(page, "https://hnit.sccchina.net/student/student/coursestudy/getlist")
|
||||
// var dataBody map[string]any
|
||||
// json.Unmarshal([]byte(res), &dataBody)
|
||||
// list := dataBody["items"].([]interface{})
|
||||
// // pretty, _ := json.MarshalIndent(list, "", " ")
|
||||
// // 判断当前有多少个课程需要学习
|
||||
// for _, v := range list {
|
||||
// m := v.(map[string]interface{})
|
||||
// realCoursewarePlayNum := m["realCoursewarePlayNum"].(string)
|
||||
// realCoursewarePlayTime := m["realCoursewarePlayTime"].(string)
|
||||
// var showScore string = "0"
|
||||
// if m["showScore"] != nil {
|
||||
// if str, ok := m["showScore"].(string); ok {
|
||||
// showScore = str
|
||||
// }
|
||||
// }
|
||||
// courseVersionID := m["courseVersionID"].(string)
|
||||
// teachplanCourseVersionId := m["teachplanCourseVersionId"].(string)
|
||||
// sign := m["sign"].(string)
|
||||
// userAccountId := m["userAccountId"].(string)
|
||||
// versionName := m["versionName"].(string)
|
||||
// // 分割字符串 判断当前 课时 成绩
|
||||
// playTimeArr := strings.Split(realCoursewarePlayTime, " / ")
|
||||
// // 将第一个转为数字
|
||||
// activeTime, _ := strconv.ParseFloat(strings.TrimSpace(playTimeArr[0]), 64)
|
||||
// var endTime float64
|
||||
// if strings.TrimSpace(playTimeArr[1]) == "--" {
|
||||
// endTime = 360
|
||||
// } else {
|
||||
// TimeEnd, _ := strconv.ParseFloat(strings.TrimSpace(playTimeArr[1]), 64)
|
||||
// endTime = TimeEnd
|
||||
// }
|
||||
// // 判断有哪些课程需要观看
|
||||
// // var pages []*rod.Page
|
||||
// if showScore == "0" && activeTime < endTime {
|
||||
// log.Println(activeTime)
|
||||
// log.Println(endTime)
|
||||
// log.Println(realCoursewarePlayNum, realCoursewarePlayTime, showScore, courseVersionID, teachplanCourseVersionId, sign, userAccountId, versionName)
|
||||
// url := fmt.Sprintf(`https://hnit.sccchina.net/student/videolearning.html#Subpage/StudentVersionVideo?courseVersionId=%s&teachplanCourseVersionId=%s&sign=%s&userAccountId=%s&syncPracticePiwik=1&piwikSiteId=750&versionName=%s`, courseVersionID, teachplanCourseVersionId, sign, userAccountId, versionName)
|
||||
// log.Println(url)
|
||||
// time.Sleep(3 * time.Second)
|
||||
// p := browser.MustPage(url)
|
||||
// satrt := p.Timeout(5 * time.Second).MustElement(`#button-courseware`)
|
||||
// satrt.MustClick()
|
||||
// time.Sleep(2 * time.Second)
|
||||
// log.Println(1)
|
||||
// iframeEl := p.Timeout(10*time.Second).MustElementR(`iframe`, ".*")
|
||||
// log.Println(2)
|
||||
// log.Println(iframeEl)
|
||||
// if iframeEl != nil {
|
||||
// iframePage := iframeEl.MustFrame()
|
||||
// iframePage.MustWaitLoad()
|
||||
// // 再次进入内嵌的iframe
|
||||
// iframeEl2 := iframePage.Timeout(10 * time.Second).MustElement(`iframe#iframe`)
|
||||
// iframePage2 := iframeEl2.MustFrame()
|
||||
// iframePage2.MustWaitLoad()
|
||||
// cl := iframePage2.Timeout(5 * time.Second).MustElement(`em.pop_close`)
|
||||
// cl.MustClick()
|
||||
// }
|
||||
// time.Sleep(3 * time.Second)
|
||||
// // pages = append(pages, p)
|
||||
|
||||
// // go func() {
|
||||
// // browser.MustPage(url)
|
||||
// // }()
|
||||
// }
|
||||
// }
|
||||
// log.Println(string(pretty))
|
||||
select {}
|
||||
}
|
||||
cfg := config.DefaultConfig()
|
||||
|
||||
switch *mode {
|
||||
case "login":
|
||||
runLogin(cfg)
|
||||
case "rush":
|
||||
runRush(cfg)
|
||||
default:
|
||||
fmt.Println("淘宝抢购脚本")
|
||||
fmt.Println("用法:")
|
||||
fmt.Println(" -mode login 扫码登录并保存 Cookie")
|
||||
fmt.Println(" -mode rush 启动定时抢购")
|
||||
fmt.Printf("\n当前抢购目标时间: %s\n", cfg.TargetTime.Format("2006-01-02 15:04:05"))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func loginFun() {
|
||||
obj := Browser.GetBrowser(1, &libs.Fingerprint{})
|
||||
browser := obj.Browser
|
||||
page := obj.Page
|
||||
page.MustNavigate(`https://login.taobao.com/havanaone/login/login.htm`).MustWaitLoad()
|
||||
page.MustWaitNavigation()
|
||||
page.MustWaitLoad() // DOM 加载完成
|
||||
page.MustWaitIdle() // 基本静止(减少早期误判)
|
||||
page.MustActivate() // 前置以便扫码
|
||||
|
||||
var usl string = "https://login.taobao.com/havanaone/login/login.htm"
|
||||
for usl == "https://login.taobao.com/havanaone/login/login.htm" {
|
||||
uri := page.MustEval("() => window.location.href").String()
|
||||
log.Println("获取当前网页URL:", uri)
|
||||
usl = uri
|
||||
time.Sleep(5 * time.Second)
|
||||
// runLogin 登录模式:扫码登录并保存 Cookie
|
||||
func runLogin(cfg *config.RushConfig) {
|
||||
log.Println("========== 登录模式 ==========")
|
||||
auth := services.NewAuth(cfg)
|
||||
if err := auth.Login(); err != nil {
|
||||
log.Fatalf("[错误] 登录失败: %v", err)
|
||||
}
|
||||
page.MustNavigate("https://cart.taobao.com/cart.htm?spm=tbpc.mytb_index.leftnav.1.6db2782d0TVVip").MustWaitLoad()
|
||||
page.MustWaitNavigation()
|
||||
page.MustWaitLoad() // DOM 加载完成
|
||||
page.MustWaitIdle() // 基本静止(减少早期误判)
|
||||
page.MustActivate() // 前置以便扫码
|
||||
cookieArr := getPageCookie(page, "https://cart.taobao.com/cart.htm?spm=tbpc.mytb_index.leftnav.1.6db2782d0TVVip")
|
||||
saveCookiesToFile(cookieArr, "account_cookie.json")
|
||||
browser.Close()
|
||||
log.Println("[完成] 登录成功,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
|
||||
}
|
||||
// runRush 抢购模式:定时抢购主流程
|
||||
// 流程: 提前进购物车 → 全选 → 结算 → 进入订单确认页 → 到点刷新 → 检测可购买 → 提交
|
||||
func runRush(cfg *config.RushConfig) {
|
||||
log.Println("========== 抢购模式 ==========")
|
||||
log.Printf("[配置] 目标时间: %s", cfg.TargetTime.Format("2006-01-02 15:04:05"))
|
||||
log.Printf("[配置] 最大重试: %d 次", cfg.MaxRetry)
|
||||
log.Printf("[配置] 提前量: %v", cfg.SubmitAdvance)
|
||||
|
||||
func saveCookiesToFile(cookies []*proto.NetworkCookieParam, filename string) error {
|
||||
data, err := json.MarshalIndent(cookies, "", " ")
|
||||
// 1. 同步服务器时间
|
||||
log.Println("\n--- 第1步: 同步服务器时间 ---")
|
||||
timeSync, err := utils.NewTimeSync(5)
|
||||
if err != nil {
|
||||
return err
|
||||
log.Fatalf("[错误] 时间同步失败: %v", err)
|
||||
}
|
||||
return os.WriteFile(filename, data, 0644)
|
||||
}
|
||||
log.Printf("[时间] 本地时间: %s", time.Now().Format("15:04:05.000"))
|
||||
log.Printf("[时间] 服务器时间: %s", timeSync.Now().Format("15:04:05.000"))
|
||||
log.Printf("[时间] 偏移量: %v", timeSync.Offset)
|
||||
|
||||
func loadCookiesFromFile(filename string) ([]*proto.NetworkCookieParam, error) {
|
||||
data, err := os.ReadFile(filename)
|
||||
remaining := timeSync.UntilTarget(cfg.TargetTime)
|
||||
if remaining <= 0 {
|
||||
log.Println("[警告] 抢购时间已过!")
|
||||
return
|
||||
}
|
||||
log.Printf("[时间] 距离抢购还有: %v", remaining.Round(time.Second))
|
||||
|
||||
// 2. 加载 Cookie & 启动浏览器
|
||||
log.Println("\n--- 第2步: 启动浏览器 ---")
|
||||
auth := services.NewAuth(cfg)
|
||||
cookies, err := auth.LoadCookies()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.Fatalf("[错误] 加载 Cookie 失败: %v (请先运行 -mode login)", err)
|
||||
}
|
||||
var cookies []*proto.NetworkCookieParam
|
||||
if err := json.Unmarshal(data, &cookies); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cookies, nil
|
||||
}
|
||||
|
||||
// 获取指定接口的响应参数
|
||||
func getApiResponse(page *rod.Page, url string) string {
|
||||
// 定义一个通道接收消息
|
||||
ch := make(chan string)
|
||||
go page.EachEvent(func(e *proto.PageLoadEventFired) {
|
||||
log.Println("1234567890")
|
||||
}, func(e *proto.NetworkResponseReceived) {
|
||||
if strings.Contains(e.Response.URL, url) {
|
||||
reply, err := (proto.NetworkGetResponseBody{RequestID: e.RequestID}).Call(page)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
var browserMgr libs.Browser
|
||||
browserInfo := browserMgr.GetBrowser(cfg.AccountID, &libs.Fingerprint{})
|
||||
if err := auth.InjectCookies(browserInfo.Browser, cookies); err != nil {
|
||||
log.Fatalf("[错误] 注入 Cookie 失败: %v", err)
|
||||
}
|
||||
log.Println("[浏览器] Cookie 注入成功")
|
||||
|
||||
// 3. 提前打开购物车,全选商品,点击结算进入订单页
|
||||
log.Println("\n--- 第3步: 进入购物车并结算 ---")
|
||||
cart := services.NewCart(browserInfo.Page)
|
||||
cart.Open()
|
||||
cart.DisableAnimations()
|
||||
cart.SelectAll()
|
||||
|
||||
log.Println("[购物车] 点击结算,进入订单确认页...")
|
||||
if err := cart.ClickCheckout(); err != nil {
|
||||
log.Fatalf("[错误] 结算失败: %v", err)
|
||||
}
|
||||
|
||||
// 4. 等待进入订单确认页
|
||||
order := services.NewOrder(browserInfo.Page)
|
||||
if err := order.WaitForConfirmPage(15 * time.Second); err != nil {
|
||||
log.Fatalf("[错误] 进入订单确认页失败: %v", err)
|
||||
}
|
||||
log.Println("[订单] 已停留在订单确认页,等待抢购时间...")
|
||||
|
||||
// 5. 创建定时器 & 启动倒计时
|
||||
timer := services.NewTimer(cfg, timeSync)
|
||||
countdownDone := make(chan struct{})
|
||||
go timer.PrintCountdown(countdownDone)
|
||||
|
||||
// 6. 精确等待到抢购时间(目标时间 - 提前量)
|
||||
log.Println("\n--- 第4步: 等待抢购时间 ---")
|
||||
timer.WaitUntilRush()
|
||||
close(countdownDone)
|
||||
|
||||
// 7. 到点! 刷新订单页 → 检测是否可提交 → 提交
|
||||
log.Println("\n--- 第5步: 开始抢购! ---")
|
||||
|
||||
for attempt := 1; attempt <= cfg.MaxRetry; attempt++ {
|
||||
log.Printf("[抢购] 第 %d/%d 次尝试", attempt, cfg.MaxRetry)
|
||||
|
||||
// 刷新订单确认页
|
||||
order.Refresh()
|
||||
|
||||
// 检测是否可以提交
|
||||
if !order.CanSubmit() {
|
||||
log.Println("[抢购] 当前仍不可购买")
|
||||
if attempt < cfg.MaxRetry {
|
||||
time.Sleep(cfg.RetryInterval)
|
||||
}
|
||||
ch <- reply.Body
|
||||
// fmt.Println(reply.Body)
|
||||
continue
|
||||
}
|
||||
})()
|
||||
|
||||
select {
|
||||
case res := <-ch:
|
||||
return res
|
||||
case <-time.After(30 * time.Second):
|
||||
log.Println("等待上传响应超时")
|
||||
return ""
|
||||
// 可以购买,立即提交!
|
||||
result := order.Submit()
|
||||
if result.Success {
|
||||
log.Println("🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉")
|
||||
log.Printf("[成功] %s", result.Message)
|
||||
log.Println("🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉")
|
||||
log.Println("[提示] 浏览器保持打开,请尽快完成付款!")
|
||||
select {}
|
||||
}
|
||||
|
||||
log.Printf("[抢购] 提交失败: %s", result.Message)
|
||||
if attempt < cfg.MaxRetry {
|
||||
time.Sleep(cfg.RetryInterval)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("[结束] 已达最大重试次数")
|
||||
log.Println("[提示] 浏览器保持打开,你可以手动操作")
|
||||
select {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user