package cmd
import (
"errors"
"fmt"
"github.com/Albert-Zhan/httpc"
"github.com/spf13/cobra"
"github.com/tidwall/gjson"
"github.com/ztino/jd_seckill/common"
"github.com/ztino/jd_seckill/jd_seckill"
"github.com/ztino/jd_seckill/log"
"net/http"
"os"
"strconv"
"time"
)
func init ( ) {
rootCmd . AddCommand ( seckillCmd )
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 ,
}
func startSeckill ( cmd * cobra . Command , args [ ] string ) {
//获取是否直接运行抢购
isRun , _ := cmd . Flags ( ) . GetBool ( "run" )
session := jd_seckill . NewSession ( common . CookieJar )
err := session . CheckLoginStatus ( )
if err != nil {
log . Println ( "抢购失败,请重新登录" )
} else {
//活跃用户会话,当会话失效自动退出程序
user := jd_seckill . NewUser ( common . Client , common . Config )
go KeepSession ( user )
//直接运行抢购跳过等待抢购时间
if ! isRun {
//计算抢购时间
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 ( "请设置抢购时间" )
os . Exit ( 0 )
}
//等待抢购
time . Sleep ( time . Duration ( timerTime ) * time . Millisecond )
//开始抢购
log . Println ( "时间到达,开始执行……" )
} 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 )
}
}
func GetJdTime ( ) ( int64 , error ) {
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 ( "获取京东服务器时间失败" )
return 0 , errors . New ( "获取京东服务器时间失败" )
}
return gjson . Get ( body , "serverTime" ) . Int ( ) , nil
}
func Start ( seckill * jd_seckill . Seckill , taskNum int ) {
//抢购总时间读取配置文件
str := common . Config . MustValue ( "config" , "seckill_time" , "2" )
seckillTime , _ := strconv . Atoi ( str )
seckillTotalTime := time . Now ( ) . Add ( time . Duration ( seckillTime ) * time . Minute ) . Unix ( )
//抢购间隔时间读取配置文件
str = common . Config . MustValue ( "config" , "ticker_time" , "1500" )
tickerTime , _ := strconv . Atoi ( str )
//开始检测抢购状态
go CheckSeckillStatus ( )
//抢购总时间超时程序自动退出
for time . Now ( ) . Unix ( ) < seckillTotalTime {
for i := 1 ; i <= taskNum ; i ++ {
go task ( seckill )
}
//怕封号的可以增加间隔时间,相反抢到的成功率也减低了
time . Sleep ( time . Duration ( tickerTime ) * time . Millisecond )
}
log . Println ( "抢购结束,具体详情请查看日志" )
}
func task ( seckill * jd_seckill . Seckill ) {
seckill . RequestSeckillUrl ( )
seckill . SeckillPage ( )
flag := seckill . SubmitSeckillOrder ( )
//提前抢购成功的,直接结束程序
if flag {
//通知管道
common . SeckillStatus <- true
}
}
func CheckSeckillStatus ( ) {
for {
select {
case <- common . SeckillStatus :
//抢购成功,程序退出
os . Exit ( 0 )
}
}
}
func KeepSession ( user * jd_seckill . User ) {
//每30分钟检测一次
t := time . NewTicker ( 30 * time . Minute )
for {
select {
case <- t . C :
if err := user . RefreshStatus ( ) ; err != nil {
_ = os . Remove ( "./cookie.txt" )
log . Println ( "会话失效,程序自动退出" )
os . Exit ( 0 )
}
log . Println ( "活跃会话成功" )
}
}
}