第一版

This commit is contained in:
zyj
2026-02-09 16:05:56 +08:00
parent fcdc5b8f2d
commit 514d7f9b0e
11 changed files with 1137 additions and 365 deletions

View File

@@ -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 {}
}