From 90d450074a5927423c58a265a13043e021d3427c Mon Sep 17 00:00:00 2001 From: pivst Date: Thu, 7 Jan 2021 13:00:11 +0800 Subject: [PATCH 1/7] fix eid ad fp acquire --- cmd/jdTdudfp.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/jdTdudfp.go b/cmd/jdTdudfp.go index 39077f1..76eaaaf 100644 --- a/cmd/jdTdudfp.go +++ b/cmd/jdTdudfp.go @@ -59,6 +59,8 @@ func startJdTdudfp(cmd *cobra.Command, args []string) { //商品链接 good_url,_:=cmd.Flags().GetString("good_url") + eid := "" + fp := "" var res []byte err = chromedp.Run(ctx, @@ -88,6 +90,8 @@ func startJdTdudfp(cmd *cobra.Command, args []string) { chromedp.Click(".common-submit-btn"), chromedp.Sleep(3 * time.Second), chromedp.Evaluate("_JdTdudfp", &res), + chromedp.Evaluate("_JdEid", &eid), + chromedp.Evaluate("_JdJrTdRiskFpInfo", &fp), ) if err != nil { log.Println("chromedp 出错了") From 70b2b075ef5a1ed47c72c622c9000f70a14d15ea Mon Sep 17 00:00:00 2001 From: pivst Date: Thu, 7 Jan 2021 13:23:42 +0800 Subject: [PATCH 2/7] add linux environment usage example --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 7f6c442..a3fc00c 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,22 @@ jd_seckill version (8)通知配置 > 目前支持email,wechat,dingtalk,具体可查看配置文件 +## Linux 无图形界面获取 eid 与 fp 方法参考 +(1) 安装无头 chrome +```shell +sudo apt install ./google-chrome-stable_current_amd64.deb +sudo apt-get -y install xorg xvfb gtk2-engines-pixbuf +sudo apt-get -y install dbus-x11 xfonts-base xfonts-100dpi xfonts-75dpi xfonts-cyrillic xfonts-scalable +sudo apt-get install -y xvfb + +Xvfb -ac :99 -screen 0 1280x1024x16 & export DISPLAY=:99 +``` +(2) 执行获取 eid 与 fp +```shell +#参数--good_url商品链接必须设置,链接地址是一个可以加入购物车的商品 +jd_seckill jdTdudfp --good_url https://item.jd.com/100007959916.html +``` + ## 感谢 ##### 非常感谢原作者 https://github.com/zhou-xiaojun/jd_mask 提供的代码 ##### 也非常感谢 https://github.com/wlwwu/jd_maotai 进行的优化 From 9d31938d1a3e252c0e46b2dfe046f59426558e38 Mon Sep 17 00:00:00 2001 From: ztino <> Date: Thu, 7 Jan 2021 20:58:17 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E4=BC=98=E5=8C=96eid=E5=92=8Cfp=E7=9A=84?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/jdTdudfp.go | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/cmd/jdTdudfp.go b/cmd/jdTdudfp.go index 76eaaaf..da1b394 100644 --- a/cmd/jdTdudfp.go +++ b/cmd/jdTdudfp.go @@ -59,6 +59,12 @@ func startJdTdudfp(cmd *cobra.Command, args []string) { //商品链接 good_url,_:=cmd.Flags().GetString("good_url") + + //返回的eid和fp + returnEid:="" + returnFp:="" + + //获取到的eid和fp eid := "" fp := "" @@ -99,13 +105,23 @@ func startJdTdudfp(cmd *cobra.Command, args []string) { } value := string(res) - if !gjson.Valid(value) || gjson.Get(value, "eid").String() == "" || gjson.Get(value, "fp").String() == "" { + //判断_JdTdudfp是否能获取到eid和fp,如果不能去获取_JdEid和_JdJrTdRiskFpInfo获取到的值 + if gjson.Valid(value) && gjson.Get(value, "eid").String() != "" && gjson.Get(value, "fp").String() != "" { + returnEid = gjson.Get(value, "eid").String() + returnFp = gjson.Get(value, "fp").String() + }else{ + if eid!="" && fp!=""{ + returnEid=eid + returnFp=fp + } + } + + //eid,fp合法性判断 + if returnEid=="" || returnFp=="" { log.Println("获取失败,请重新尝试,返回信息:" + value) - } else { - eid := gjson.Get(value, "eid").String() - fp := gjson.Get(value, "fp").String() - log.Println("eid:" + eid) - log.Println("fp:" + fp) + }else{ + log.Println("eid:" + returnEid) + log.Println("fp:" + returnFp) //修改配置文件 confFile := "./conf.ini" @@ -115,14 +131,13 @@ func startJdTdudfp(cmd *cobra.Command, args []string) { os.Exit(0) } - cfg.SetValue("config", "eid", eid) - cfg.SetValue("config", "fp", fp) + cfg.SetValue("config", "eid", returnEid) + cfg.SetValue("config", "fp", returnFp) if err := goconfig.SaveConfigFile(cfg, confFile); err != nil { log.Println("保存配置文件失败,请手动填入配置文件") + }else{ + log.Println("eid, fp参数已经自动填入配置文件") } - - log.Println("eid, fp参数已经自动填入配置文件") } - } } From ca827ce50ed3507140bcb5c586e1ec149a4318d0 Mon Sep 17 00:00:00 2001 From: ztino <76823311+ztino@users.noreply.github.com> Date: Thu, 7 Jan 2021 21:03:47 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index a3fc00c..a28df39 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ helloworld ======= + +### 不定时开放群链接:https://t.me/joinchat/GsDnhtkdKJ4nbwJh + > ⚠ 此项目是[python jd_seckill](https://github.com/huanghyw/jd_seckill) 的go版本实现,旨在降低使用门栏和相互学习而创建。 **go版本的jd_seckill,京东抢茅台神器,支持跨平台,使用者请在发布页下载可执行文件,欢迎pr。** From 2a3cb4bd46c332593c170cd6e7847e2d0bc399ec Mon Sep 17 00:00:00 2001 From: k3o6g Date: Thu, 7 Jan 2021 21:34:47 +0800 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0github=20action?= =?UTF-8?q?=20,=E6=89=93tag=E6=97=B6=E8=87=AA=E5=8A=A8=E6=89=93=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-release.yml | 28 +++++++++++++++++++ .goreleaser.yml | 43 +++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 .github/workflows/build-release.yml create mode 100644 .goreleaser.yml diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 0000000..f79fdea --- /dev/null +++ b/.github/workflows/build-release.yml @@ -0,0 +1,28 @@ +name: goreleaser + +on: + push: + tag: + - "*" +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - + name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.15 + - + name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + version: latest + args: release --rm-dist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..3ae1c67 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,43 @@ +# This is an example .goreleaser.yml file with some sane defaults. +# Make sure to check the documentation at http://goreleaser.com +before: + hooks: + # You may remove this if you don't use go modules. + - go mod download + # you may remove this if you don't need go generate + - go generate ./... +builds: + - env: + - CGO_ENABLED=0 + - GO111MODULE=on + - GOPROXY=https://goproxy.io,direct + binary: jd_seckill + ldflags: + - -s -w + goos: + - linux + - windows + - darwin + goarch: + - amd64 + - arm64 +archives: + - + replacements: + darwin: Darwin + linux: Linux + windows: Windows + amd64: amd64 + format_overrides: + - goos: windows + format: zip +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ .Tag }}-next" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' From 5cc18a4e6b0aed3753b88791f457f4cc97282e95 Mon Sep 17 00:00:00 2001 From: pppscn <35696959@qq.com> Date: Thu, 7 Jan 2021 23:49:23 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E6=89=AB=E7=A0=81=E5=90=8E=E4=BA=8C?= =?UTF-8?q?=E7=BB=B4=E7=A0=81=E8=87=AA=E5=8A=A8=E5=88=A0=E9=99=A4=EF=BC=8C?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=85=B3=E9=97=AD=E7=85=A7=E7=89=87=E6=9F=A5?= =?UTF-8?q?=E7=9C=8B=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/lib.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/common/lib.go b/common/lib.go index ddbcfe0..f7d9541 100644 --- a/common/lib.go +++ b/common/lib.go @@ -102,8 +102,18 @@ func Exists(path string) bool { func OpenImage(qrPath string) { if runtime.GOOS == "windows" {//windows - cmd := exec.Command("cmd", "/k", "start", qrPath) + cmd := exec.Command("cmd", "/c", "rundll32.exe", "C:\\Windows\\System32\\shimgvw.dll,ImageView_FullscreenA", qrPath) _ = cmd.Start() + //扫码后二维码自动删除,自动关闭照片查看器 + go func() { + for { + time.Sleep(time.Duration(1) * time.Second) + if !Exists(qrPath) { + _ = exec.Command("taskkill", "/F", "/T", "/PID", fmt.Sprint(cmd.Process.Pid)).Run() + break + } + } + }() }else if runtime.GOOS == "darwin" {//Macos cmd := exec.Command("open", qrPath) _ = cmd.Start() From 2af77d0720c619a64788c890c190f5c65bb8eea0 Mon Sep 17 00:00:00 2001 From: pppscn <35696959@qq.com> Date: Fri, 8 Jan 2021 00:02:23 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=8C=96=E9=A2=84?= =?UTF-8?q?=E7=BA=A6=E6=8A=A2=E8=B4=AD=E6=94=AF=E6=8C=81=EF=BC=8C=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E8=87=AA=E5=8A=A8=E5=8E=BB=E8=8C=85=E5=8F=B0=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E8=8E=B7=E5=8F=96=E4=B8=8B=E4=B8=80=E6=AC=A1=E6=8A=A2?= =?UTF-8?q?=E8=B4=AD=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、预约(jd_seckill reserve)成功后自动填写conf.ini中的buy_time 2、优化抢购(jd_seckill seckill)启动前的时间判断 --- README.md | 3 +- cmd/reserve.go | 14 ++--- cmd/seckill.go | 83 +++++++++++++++++---------- cmd/version.go | 2 +- common/lib.go | 19 ++++++- conf.ini | 2 +- jd_seckill/seckill.go | 128 ++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 202 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index a28df39..2a0d423 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ helloworld ### 不定时开放群链接:https://t.me/joinchat/GsDnhtkdKJ4nbwJh -> ⚠ 此项目是[python jd_seckill](https://github.com/huanghyw/jd_seckill) 的go版本实现,旨在降低使用门栏和相互学习而创建。 +> ⚠ 此项目是[python jd_seckill](https://github.com/huanghyw/jd_seckill) 的go版本实现,旨在降低使用门槛和相互学习而创建。 **go版本的jd_seckill,京东抢茅台神器,支持跨平台,使用者请在发布页下载可执行文件,欢迎pr。** @@ -28,7 +28,6 @@ go get github.com/ztino/jd_seckill ``` ## 待办 -- 自动化预约抢购支持,程序自动去茅台页面获取下一次抢购时间 - 跨平台桌面端支持,打算使用:https://github.com/therecipe/qt ## 使用 diff --git a/cmd/reserve.go b/cmd/reserve.go index 81ccef0..e6ac479 100644 --- a/cmd/reserve.go +++ b/cmd/reserve.go @@ -14,17 +14,17 @@ func init() { var reserveCmd = &cobra.Command{ Use: "reserve", Short: "Open JD Moutai buying appointment", - Run: startReserve, + Run: startReserve, } -func startReserve(cmd *cobra.Command, args []string) { - session:=jd_seckill.NewSession(common.CookieJar) - err:=session.CheckLoginStatus() - if err!=nil { +func startReserve(cmd *cobra.Command, args []string) { + session := jd_seckill.NewSession(common.CookieJar) + err := session.CheckLoginStatus() + if err != nil { log.Println("预约失败,请重新登录") - }else{ + } else { //开始预约,预约过的就重复预约 - seckill:=jd_seckill.NewSeckill(common.Client,common.Config) + seckill := jd_seckill.NewSeckill(common.Client, common.Config) seckill.MakeReserve() } } diff --git a/cmd/seckill.go b/cmd/seckill.go index 07c2a89..2490783 100644 --- a/cmd/seckill.go +++ b/cmd/seckill.go @@ -2,7 +2,6 @@ package cmd import ( "errors" - "fmt" "github.com/Albert-Zhan/httpc" "github.com/spf13/cobra" "github.com/tidwall/gjson" @@ -11,62 +10,88 @@ import ( "github.com/ztino/jd_seckill/log" "net/http" "os" + "regexp" "strconv" "time" ) func init() { rootCmd.AddCommand(seckillCmd) - seckillCmd.Flags().BoolP("run","r",false,"Run directly without waiting for the time to buy") + seckillCmd.Flags().BoolP("run", "r", false, "Run directly without waiting for the time to buy") } var seckillCmd = &cobra.Command{ Use: "seckill", Short: "Start panic buying procedure", - Run: startSeckill, + Run: startSeckill, } -func startSeckill(cmd *cobra.Command, args []string) { +func startSeckill(cmd *cobra.Command, args []string) { //获取是否直接运行抢购 - isRun,_:=cmd.Flags().GetBool("run") - session:=jd_seckill.NewSession(common.CookieJar) - err:=session.CheckLoginStatus() - if err!=nil { + isRun, _ := cmd.Flags().GetBool("run") + session := jd_seckill.NewSession(common.CookieJar) + err := session.CheckLoginStatus() + if err != nil { log.Println("抢购失败,请重新登录") - }else{ + } else { //活跃用户会话,当会话失效自动退出程序 - user:=jd_seckill.NewUser(common.Client,common.Config) + user := jd_seckill.NewUser(common.Client, common.Config) go KeepSession(user) + + seckill := jd_seckill.NewSeckill(common.Client, common.Config) //直接运行抢购跳过等待抢购时间 if !isRun { + //获取本地时间与京东云端时间差 + diffTime := seckill.GetDiffTime() + + //获取抢购时间 + buyDate := common.Config.MustValue("config", "buy_time", "") + buyTimeReg := regexp.MustCompile(`(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})`) + buyTimeArr := buyTimeReg.FindAllString(buyDate, 1) + if len(buyTimeArr) == 1 { + buyDate = buyTimeArr[0] + } else { + _, buyTimeArr, err := seckill.GetWareBusiness() + if err != nil || len(buyTimeArr) != 2 { + log.Println("请设置conf.ini中的抢购时间(buy_time)") + os.Exit(0) + } + buyDate = buyTimeArr[0] + ":00" + } + //计算抢购时间 - nowLocalTime:=time.Now().UnixNano()/1e6 - jdTime,_:=GetJdTime() - buyDate:=common.Config.MustValue("config","buy_time","") loc, _ := time.LoadLocation("Local") - t,_:=time.ParseInLocation("2006-01-02 15:04:05",buyDate,loc) - buyTime:=t.UnixNano()/1e6 - diffTime:=nowLocalTime-jdTime - log.Println(fmt.Sprintf("正在等待到达设定时间:%s,检测本地时间与京东服务器时间误差为【%d】毫秒",buyDate,diffTime)) - timerTime:=(buyTime+diffTime)-jdTime - if timerTime<=0 { - log.Println("请设置抢购时间") + t, _ := time.ParseInLocation("2006-01-02 15:04:05", buyDate, loc) + buyTime := t.UnixNano()/1e6 + diffTime + + //抢购总时间读取配置文件 + str := common.Config.MustValue("config", "seckill_time", "2") + seckillTime, err := strconv.Atoi(str) + if err != nil { + seckillTime = 2 + } + + timerTime := buyTime - time.Now().UnixNano()/1e6 + if timerTime >= 0 { //等待抢购 + log.Println("还没到达抢购时间:", buyDate, ",等待中...") + time.Sleep(time.Duration(timerTime) * time.Millisecond) + log.Println("时间到达,开始抢购……") + } else if timerTime <= int64(-seckillTime*6e4) { + log.Println("已经超过抢购时间(", buyDate, ")不止", seckillTime, "分钟,败局已定,下次请早!") os.Exit(0) + } else { + log.Println("您已经错过抢购时间,但还在抢购总时间(", seckillTime, "分钟)内,直接执行抢购,祝您好运!") } - //等待抢购 - time.Sleep(time.Duration(timerTime)*time.Millisecond) - //开始抢购 - log.Println("时间到达,开始执行……") - }else{ + } else { log.Println("开始执行……") } - seckill:=jd_seckill.NewSeckill(common.Client,common.Config) + //开启抢购任务,第二个参数为开启几个协程 //怕封号的可以减少协程数量,相反抢到的成功率也减低了 //抢购任务数读取配置文件 - str:=common.Config.MustValue("config","task_num","5") - taskNum,_:=strconv.Atoi(str) - Start(seckill,taskNum) + str := common.Config.MustValue("config", "task_num", "5") + taskNum, _ := strconv.Atoi(str) + Start(seckill, taskNum) } } diff --git a/cmd/version.go b/cmd/version.go index 05f3cf4..9c76c1b 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -15,6 +15,6 @@ var versionCmd = &cobra.Command{ Use: "version", Short: "Print the version number of jd_seckill", Run: func(cmd *cobra.Command, args []string) { - fmt.Println(fmt.Sprintf("%s version %s %s %s/%s",common.SoftName,common.SoftName,common.Version,runtime.GOOS,runtime.GOARCH)) + fmt.Println(fmt.Sprintf("%s version %s %s %s/%s", common.SoftName, common.SoftName, common.Version, runtime.GOOS, runtime.GOARCH)) }, } \ No newline at end of file diff --git a/common/lib.go b/common/lib.go index f7d9541..43d891a 100644 --- a/common/lib.go +++ b/common/lib.go @@ -101,7 +101,7 @@ func Exists(path string) bool { } func OpenImage(qrPath string) { - if runtime.GOOS == "windows" {//windows + if runtime.GOOS == "windows" { //windows cmd := exec.Command("cmd", "/c", "rundll32.exe", "C:\\Windows\\System32\\shimgvw.dll,ImageView_FullscreenA", qrPath) _ = cmd.Start() //扫码后二维码自动删除,自动关闭照片查看器 @@ -114,10 +114,10 @@ func OpenImage(qrPath string) { } } }() - }else if runtime.GOOS == "darwin" {//Macos + } else if runtime.GOOS == "darwin" { //Macos cmd := exec.Command("open", qrPath) _ = cmd.Start() - }else{ + } else { //linux或者其他系统 file, _ := os.Open(qrPath) img, _, _ := image.Decode(file) @@ -129,3 +129,16 @@ func OpenImage(qrPath string) { fmt.Println(qr.ToSmallString(false)) } } + +//指定位数随机数 +func RandomNumber(len int) string { + var container string + var str = "0123456789" + b := bytes.NewBufferString(str) + length := b.Len() + rand.Seed(time.Now().UnixNano()) + for i := 0; i < len; i++ { + container += string(str[rand.Intn(length)]) + } + return container +} diff --git a/conf.ini b/conf.ini index 146b408..c47dd52 100644 --- a/conf.ini +++ b/conf.ini @@ -9,7 +9,7 @@ fp = sku_id = 100012043978 # 抢购数量 seckill_num = 2 -# 抢购开始时间设定 2021-01-01 09:59:59 +# 抢购开始时间设定 2021-01-01 09:59:59 (PS.预约成功后会自动更新) buy_time = 2021-01-01 09:59:59 # 抢购总时间,单位:分钟,默认两分钟 seckill_time = diff --git a/jd_seckill/seckill.go b/jd_seckill/seckill.go index 8b8fb30..414a63b 100644 --- a/jd_seckill/seckill.go +++ b/jd_seckill/seckill.go @@ -11,6 +11,9 @@ import ( "github.com/ztino/jd_seckill/log" "github.com/ztino/jd_seckill/service" "net/http" + "net/url" + "os" + "regexp" "strconv" "strings" "time" @@ -42,7 +45,103 @@ func (this *Seckill) SkuTitle() (string, error) { return strings.TrimSpace(doc.Find(".sku-name").Text()), nil } +func (this *Seckill) GetDiffTime() int64 { + log.Println("获取本地时间与京东云端时间差") + localTime := time.Now().UnixNano() / 1e6 + log.Println("本地系统时间:", time.Unix(0, localTime*1e6)) + + jdTime := localTime + req := httpc.NewRequest(common.Client) + resp, body, err := req.SetUrl("https://a.jd.com//ajax/queryServerData.html").SetMethod("get").Send().End() + if err != nil || resp.StatusCode != http.StatusOK { + log.Println("获取京东服务器时间失败,以本地时间为准") + } else { + jdTime = gjson.Get(body, "serverTime").Int() + } + log.Println("京东云端时间:", time.Unix(0, jdTime*1e6)) + + delayTime := time.Now().UnixNano()/1e6 - localTime + log.Println("网络请求延时:", delayTime, "ms") + + diffTime := localTime - jdTime + delayTime/2 + log.Println("实际时间误差:", diffTime, "ms (本地时间-京东云端时间+网络请求延时/2)") + + return diffTime +} + +func (this *Seckill) GetWareBusiness() ([]string, []string, error) { + log.Println("获取商品的预约时间、抢购时间") + skuId := this.conf.MustValue("config", "sku_id", "") //商品ID + cat := "12259,12260,9435" //分类路径,TODO:适配其他商品时要调整 + area := "16_1303_3484_0" //配送至,TODO:适配其他商品时要调整成购买者实际地区 + shopId := "1000085463" //卖家ID + venderId := "1000085463" //供应商ID + paramJson := "{\"platform2\":\"1\",\"specialAttrStr\":\"p0pp1pppppppppppppppp\",\"skuMarkStr\":\"00\"}" //TODO:不知道干嘛用的? + num := this.conf.MustValue("config", "seckill_num", "1") //购买数量 + req := httpc.NewRequest(this.client) + req.SetHeader("User-Agent", this.getUserAgent()) + req.SetHeader("Referer", fmt.Sprintf("https://item.jd.com/%s.html", skuId)) + resp, body, err := req.SetUrl(fmt.Sprintf("https://item-soa.jd.com/getWareBusiness?callback=jQuery%s&skuId=%s&cat=%s&area=%s&shopId=%s&venderId=%s¶mJson=%s&num=%s&_=%s", + common.RandomNumber(7), + skuId, + cat, + area, + shopId, + venderId, + url.QueryEscape(paramJson), + num, + strconv.Itoa(int(time.Now().Unix()*1000)), + )).SetMethod("get").Send().End() + + var yuyueTimeArr []string + var buyTimeArr []string + if err != nil || resp.StatusCode != http.StatusOK { + log.Println("获取商品详情失败", resp, body, err) + return yuyueTimeArr, buyTimeArr, errors.New("访问商品详情失败") + } + if !gjson.Get(body, "yuyueInfo").Exists() || !gjson.Get(body, "yuyueInfo.yuyueTime").Exists() || !gjson.Get(body, "yuyueInfo.buyTime").Exists() { + log.Println("获取商品预约信息失败", body) + return yuyueTimeArr, buyTimeArr, errors.New("获取商品预约信息失败") + } + + yuyueTime := gjson.Get(body, "yuyueInfo.yuyueTime").String() + buyTime := gjson.Get(body, "yuyueInfo.buyTime").String() + log.Println("预约起止时间:", yuyueTime) + log.Println("购买起止时间:", buyTime) + + reg := regexp.MustCompile(`(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2})`) + yuyueTimeArr = reg.FindAllString(yuyueTime, 2) + buyTimeArr = reg.FindAllString(buyTime, 2) + + return yuyueTimeArr, buyTimeArr, nil +} + func (this *Seckill) MakeReserve() { + yuyueTimeArr, buyTimeArr, err := this.GetWareBusiness() + if err == nil && len(yuyueTimeArr) == 2 { + diffTime := this.GetDiffTime() + loc, _ := time.LoadLocation("Local") + yuyueTimeBegin, _ := time.ParseInLocation(common.DateTimeFormatStr, yuyueTimeArr[0]+":00", loc) + yuyueTimeEnd, _ := time.ParseInLocation(common.DateTimeFormatStr, yuyueTimeArr[1]+":59", loc) + + beginTime := yuyueTimeBegin.UnixNano()/1e6 + diffTime + endTime := yuyueTimeEnd.UnixNano()/1e6 + diffTime + + diffTime = beginTime - time.Now().UnixNano()/1e6 + if diffTime > 0 { + log.Println("还没到预约时间,等待", diffTime, "ms 后开始预约") + time.Sleep(time.Duration(diffTime) * time.Millisecond) + } + + diffTime = time.Now().UnixNano()/1e6 - endTime + if diffTime > 0 { + log.Println("您已经错过预约时间,下次请早!") + os.Exit(0) + } + } else { + log.Println("预约起始时间获取失败,立即尝试预约:", err, yuyueTimeArr) + } + user := NewUser(this.client, this.conf) userInfo, _ := user.GetUserInfo() log.Println("用户:" + userInfo) @@ -63,9 +162,26 @@ func (this *Seckill) MakeReserve() { reserveUrl := gjson.Get(body, "url").String() req = httpc.NewRequest(this.client) _, _, _ = req.SetUrl("https:" + reserveUrl).SetMethod("get").Send().End() - msg := "预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约![我的预约](https://yushou.jd.com/member/qualificationList.action)" - _ = service.SendMessage(this.conf, "茅台抢购通知", msg) + msg := "商品名称《" + shopTitle + "》预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约!\n\n[我的预约](https://yushou.jd.com/member/qualificationList.action)" + _ = service.SendMessage(this.conf, "京东秒杀通知", msg) log.Println(msg) + + //更新购买时间 + if len(buyTimeArr) == 2 { + confFile := "./conf.ini" + cfg, err := goconfig.LoadConfigFile(confFile) + if err != nil { + log.Println("配置文件不存在,程序退出") + os.Exit(0) + } + buyTime := buyTimeArr[0] + ":00" + cfg.SetValue("config", "buy_time", buyTime) + if err := goconfig.SaveConfigFile(cfg, confFile); err != nil { + log.Println("保存配置文件失败,请手动修改conf.ini,buy_time =", buyTime) + } + + log.Println("下一次抢购开始时间设定已经更新:", buyTime) + } } } @@ -237,13 +353,13 @@ func (this *Seckill) SubmitSeckillOrder() bool { resp, body, err := req.SetUrl("https://marathon.jd.com/seckillnew/orderService/pc/submitOrder.action?skuId=" + skuId).SetMethod("post").Send().End() if err != nil || resp.StatusCode != http.StatusOK { log.Println("抢购失败,网络错误") - _ = service.SendMessage(this.conf, "茅台抢购通知", "抢购失败,网络错误") + _ = service.SendMessage(this.conf, "京东秒杀通知", "抢购失败,网络错误") return false } if !gjson.Valid(body) { log.Println("抢购失败,返回信息:" + body) - _ = service.SendMessage(this.conf, "茅台抢购通知", "抢购失败,返回信息:"+body) + _ = service.SendMessage(this.conf, "京东秒杀通知", "抢购失败,返回信息:"+body) return false } if gjson.Get(body, "success").Bool() { @@ -251,11 +367,11 @@ func (this *Seckill) SubmitSeckillOrder() bool { totalMoney := gjson.Get(body, "totalMoney").String() payUrl := "https:" + gjson.Get(body, "pcUrl").String() log.Println(fmt.Sprintf("抢购成功,订单号:%s, 总价:%s, 电脑端付款链接:%s", orderId, totalMoney, payUrl)) - _ = service.SendMessage(this.conf, "茅台抢购通知", fmt.Sprintf("抢购成功,订单号:%s, 总价:%s, 电脑端付款链接:%s", orderId, totalMoney, payUrl)) + _ = service.SendMessage(this.conf, "京东秒杀通知", fmt.Sprintf("抢购成功,订单号:%s, 总价:%s, 电脑端付款链接:%s", orderId, totalMoney, payUrl)) return true } else { log.Println("抢购失败,返回信息:" + body) - _ = service.SendMessage(this.conf, "茅台抢购通知", "抢购失败,返回信息:"+body) + _ = service.SendMessage(this.conf, "京东秒杀通知", "抢购失败,返回信息:"+body) return false } }