You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
233 lines
9.1 KiB
233 lines
9.1 KiB
package jd_seckill
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"github.com/Albert-Zhan/httpc"
|
|
"github.com/PuerkitoBio/goquery"
|
|
"github.com/tidwall/gjson"
|
|
"github.com/unknwon/goconfig"
|
|
"github.com/ztino/jd_seckill/common"
|
|
"github.com/ztino/jd_seckill/service"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Seckill struct {
|
|
client *httpc.HttpClient
|
|
conf *goconfig.ConfigFile
|
|
}
|
|
|
|
func NewSeckill(client *httpc.HttpClient,conf *goconfig.ConfigFile) *Seckill {
|
|
return &Seckill{client: client,conf: conf}
|
|
}
|
|
|
|
func (this *Seckill) getUserAgent() string {
|
|
return this.conf.MustValue("config","default_user_agent","")
|
|
}
|
|
|
|
func (this *Seckill) SkuTitle() (string,error) {
|
|
skuId:=this.conf.MustValue("config","sku_id","")
|
|
req:=httpc.NewRequest(this.client)
|
|
resp,body,err:=req.SetUrl(fmt.Sprintf("https://item.jd.com/%s.html",skuId)).SetMethod("get").Send().End()
|
|
if err!=nil || resp.StatusCode!=http.StatusOK {
|
|
log.Println("访问商品详情失败")
|
|
return "",errors.New("访问商品详情失败")
|
|
}
|
|
html:=strings.NewReader(body)
|
|
doc,_:=goquery.NewDocumentFromReader(html)
|
|
return strings.TrimSpace(doc.Find(".sku-name").Text()),nil
|
|
}
|
|
|
|
func (this *Seckill) MakeReserve() {
|
|
shopTitle,err:=this.SkuTitle()
|
|
if err!=nil {
|
|
log.Println("获取商品信息失败")
|
|
}else{
|
|
log.Println("商品名称:"+shopTitle)
|
|
}
|
|
skuId:=this.conf.MustValue("config","sku_id","")
|
|
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("https://yushou.jd.com/youshouinfo.action?callback=fetchJSON&sku="+skuId+"&_="+strconv.Itoa(int(time.Now().Unix()*1000))).SetMethod("get").Send().End()
|
|
if err!=nil || resp.StatusCode!=http.StatusOK {
|
|
log.Println("预约商品失败")
|
|
}else{
|
|
reserveUrl:=gjson.Get(body,"url").String()
|
|
req=httpc.NewRequest(this.client)
|
|
_,_,_=req.SetUrl("https:"+reserveUrl).SetMethod("get").Send().End()
|
|
log.Println("预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约")
|
|
}
|
|
}
|
|
|
|
func (this *Seckill) getSeckillUrl() (string,error) {
|
|
skuId:=this.conf.MustValue("config","sku_id","")
|
|
req:=httpc.NewRequest(this.client)
|
|
req.SetHeader("User-Agent",this.getUserAgent())
|
|
req.SetHeader("Host","itemko.jd.com")
|
|
req.SetHeader("Referer",fmt.Sprintf("https://item.jd.com/%s.html",skuId))
|
|
url:=""
|
|
for {
|
|
_,body,_:=req.SetUrl("https://itemko.jd.com/itemShowBtn?callback=jQuery{}"+strconv.Itoa(common.Rand(1000000,9999999))+"&skuId="+skuId+"&from=pc&_="+strconv.Itoa(int(time.Now().Unix()*1000))).SetMethod("get").Send().End()
|
|
if gjson.Get(body,"url").Exists() && gjson.Get(body,"url").String()!="" {
|
|
url=gjson.Get(body,"url").String()
|
|
break
|
|
}
|
|
log.Println("抢购链接获取失败,稍后自动重试")
|
|
time.Sleep(3*time.Millisecond)
|
|
}
|
|
//https://divide.jd.com/user_routing?skuId=8654289&sn=c3f4ececd8461f0e4d7267e96a91e0e0&from=pc
|
|
url=strings.ReplaceAll(url,"divide","marathon")
|
|
//https://marathon.jd.com/captcha.html?skuId=8654289&sn=c3f4ececd8461f0e4d7267e96a91e0e0&from=pc
|
|
url=strings.ReplaceAll(url,"user_routing","captcha.html")
|
|
log.Println("抢购链接获取成功:"+url)
|
|
return url,nil
|
|
}
|
|
|
|
func (this *Seckill) RequestSeckillUrl() {
|
|
user:=NewUser(this.client,this.conf)
|
|
userInfo,_:=user.GetUserInfo()
|
|
log.Println("用户:"+userInfo)
|
|
shopTitle,err:=this.SkuTitle()
|
|
if err!=nil {
|
|
log.Println("获取商品信息失败")
|
|
}else{
|
|
log.Println("商品名称:"+shopTitle)
|
|
}
|
|
url,_:=this.getSeckillUrl()
|
|
skuId:=this.conf.MustValue("config","sku_id","")
|
|
req:=httpc.NewRequest(this.client)
|
|
req.SetHeader("User-Agent",this.getUserAgent())
|
|
req.SetHeader("Host","marathon.jd.com")
|
|
req.SetHeader("Referer",fmt.Sprintf("https://item.jd.com/%s.html",skuId))
|
|
_,_,_=req.SetUrl(url).SetMethod("get").Send().End()
|
|
}
|
|
|
|
func (this *Seckill) SeckillPage() {
|
|
log.Println("访问抢购订单结算页面...")
|
|
skuId:=this.conf.MustValue("config","sku_id","")
|
|
seckillNum:=this.conf.MustValue("config","seckill_num","")
|
|
req:=httpc.NewRequest(this.client)
|
|
req.SetHeader("User-Agent",this.getUserAgent())
|
|
req.SetHeader("Host","marathon.jd.com")
|
|
req.SetHeader("Referer",fmt.Sprintf("https://item.jd.com/%s.html",skuId))
|
|
_,_,_=req.SetUrl("https://marathon.jd.com/seckill/seckill.action?skuId="+skuId+"&num="+seckillNum+"&rid="+strconv.Itoa(int(time.Now().Unix()))).SetMethod("get").Send().End()
|
|
}
|
|
|
|
func (this *Seckill) SeckillInitInfo() (string,error) {
|
|
log.Println("获取秒杀初始化信息...")
|
|
skuId:=this.conf.MustValue("config","sku_id","")
|
|
seckillNum:=this.conf.MustValue("config","seckill_num","")
|
|
req:=httpc.NewRequest(this.client)
|
|
req.SetHeader("User-Agent",this.getUserAgent())
|
|
req.SetHeader("Host","marathon.jd.com")
|
|
req.SetData("sku",skuId)
|
|
req.SetData("num",seckillNum)
|
|
req.SetData("isModifyAddress","false")
|
|
resp,body,err:=req.SetUrl("https://marathon.jd.com/seckillnew/orderService/pc/init.action").SetMethod("post").Send().End()
|
|
if err!=nil || resp.StatusCode!=http.StatusOK {
|
|
log.Println("初始化秒杀信息失败")
|
|
return "",errors.New("初始化秒杀信息失败")
|
|
}
|
|
if !gjson.Valid(body) {
|
|
log.Println("抢购失败,返回信息:"+body)
|
|
return "", errors.New("抢购失败,返回信息:"+body)
|
|
}
|
|
return body,nil
|
|
}
|
|
|
|
func (this *Seckill) SubmitSeckillOrder() bool {
|
|
eid:=this.conf.MustValue("config","eid","")
|
|
fp:=this.conf.MustValue("config","fp","")
|
|
skuId:=this.conf.MustValue("config","sku_id","")
|
|
seckillNum:=this.conf.MustValue("config","seckill_num","")
|
|
paymentPwd:=this.conf.MustValue("account","payment_pwd","")
|
|
initInfo,err:=this.SeckillInitInfo()
|
|
if err!=nil {
|
|
log.Println(fmt.Sprintf("抢购失败,无法获取生成订单的基本信息,接口返回:【%s】",err.Error()))
|
|
return false
|
|
}
|
|
address:=gjson.Get(initInfo,"addressList").Array()
|
|
defaultAddress:=address[0]
|
|
isinvoiceInfo:=gjson.Get(initInfo,"invoiceInfo").Exists()
|
|
invoiceTitle:="-1"
|
|
invoiceContentType:="-1"
|
|
invoicePhone:=""
|
|
invoicePhoneKey:=""
|
|
if isinvoiceInfo {
|
|
invoiceTitle=gjson.Get(initInfo,"invoiceInfo.invoiceTitle").String()
|
|
invoiceContentType=gjson.Get(initInfo,"invoiceInfo.invoiceContentType").String()
|
|
invoicePhone=gjson.Get(initInfo,"invoiceInfo.invoicePhone").String()
|
|
invoicePhoneKey=gjson.Get(initInfo,"invoiceInfo.invoicePhoneKey").String()
|
|
}
|
|
invoiceInfo:="false"
|
|
if isinvoiceInfo {
|
|
invoiceInfo="true"
|
|
}
|
|
token:=gjson.Get(initInfo,"token").String()
|
|
log.Println("提交抢购订单...")
|
|
req:=httpc.NewRequest(this.client)
|
|
req.SetHeader("User-Agent",this.getUserAgent())
|
|
req.SetHeader("Host","marathon.jd.com")
|
|
req.SetHeader("Referer",fmt.Sprintf("https://marathon.jd.com/seckill/seckill.action?skuId=%s&num=%s&rid=%d",skuId,seckillNum,int(time.Now().Unix())))
|
|
req.SetData("skuId",skuId)
|
|
req.SetData("num",seckillNum)
|
|
req.SetData("addressId",defaultAddress.Get("id").String())
|
|
req.SetData("yuShou","true")
|
|
req.SetData("isModifyAddress","false")
|
|
req.SetData("name",defaultAddress.Get("name").String())
|
|
req.SetData("provinceId",defaultAddress.Get("provinceId").String())
|
|
req.SetData("cityId",defaultAddress.Get("cityId").String())
|
|
req.SetData("countyId",defaultAddress.Get("countyId").String())
|
|
req.SetData("townId",defaultAddress.Get("townId").String())
|
|
req.SetData("addressDetail",defaultAddress.Get("addressDetail").String())
|
|
req.SetData("mobile",defaultAddress.Get("mobile").String())
|
|
req.SetData("mobileKey",defaultAddress.Get("mobileKey").String())
|
|
req.SetData("email",defaultAddress.Get("email").String())
|
|
req.SetData("postCode","")
|
|
req.SetData("invoiceTitle",invoiceTitle)
|
|
req.SetData("invoiceCompanyName","")
|
|
req.SetData("invoiceContent",invoiceContentType)
|
|
req.SetData("invoiceTaxpayerNO","")
|
|
req.SetData("invoiceEmail","")
|
|
req.SetData("invoicePhone",invoicePhone)
|
|
req.SetData("invoicePhoneKey",invoicePhoneKey)
|
|
req.SetData("invoice",invoiceInfo)
|
|
req.SetData("password",paymentPwd)
|
|
req.SetData("codTimeType","3")
|
|
req.SetData("paymentType","4")
|
|
req.SetData("areaCode","")
|
|
req.SetData("overseas","0")
|
|
req.SetData("phone","")
|
|
req.SetData("eid",eid)
|
|
req.SetData("fp",fp)
|
|
req.SetData("token",token)
|
|
req.SetData("pru","")
|
|
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,"茅台抢购通知","抢购失败,网络错误")
|
|
return false
|
|
}
|
|
if !gjson.Valid(body) {
|
|
log.Println("抢购失败,返回信息:"+body)
|
|
_=service.SendMessage(this.conf,"茅台抢购通知","抢购失败,返回信息:"+body)
|
|
return false
|
|
}
|
|
if gjson.Get(body,"success").Bool() {
|
|
orderId:=gjson.Get(body,"orderId").String()
|
|
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))
|
|
return true
|
|
}else{
|
|
log.Println("抢购失败,返回信息:"+body)
|
|
_=service.SendMessage(this.conf,"茅台抢购通知","抢购失败,返回信息:"+body)
|
|
return false
|
|
}
|
|
}
|