Browse Source

Merge pull request #25 from pppscn/main

将日志同时输出到控制台和文件
main
ztino 4 years ago
committed by GitHub
parent
commit
d38389d008
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .gitignore
  2. 9
      README.md
  3. 114
      cmd/jdTdudfp.go
  4. 4
      common/lib.go
  5. 9
      common/var.go
  6. 14
      conf.ini
  7. 4
      go.mod
  8. 4
      jd_seckill/seckill.go
  9. 15
      main.go
  10. 35
      service/dingtalk.go
  11. 22
      service/service.go

5
.gitignore

@ -15,3 +15,8 @@
# Dependency directories (remove the comment below to include it) # Dependency directories (remove the comment below to include it)
# vendor/ # vendor/
/*.log
/.idea
/qr_code.png
/cookie.txt
*.bak

9
README.md

@ -26,8 +26,6 @@ go get github.com/ztino/jd_seckill
## 待办 ## 待办
- 日志目前还未输出到本地日志文件保存
- 自动化抢购支持,无需设置抢购时间
- 跨平台桌面端支持,打算使用:https://github.com/therecipe/qt - 跨平台桌面端支持,打算使用:https://github.com/therecipe/qt
## 使用 ## 使用
@ -44,7 +42,8 @@ jd_seckill login
### 自动获取eid,fp ### 自动获取eid,fp
> ⚠依赖谷歌浏览器,请安装谷歌浏览器,获取到的eid和fp请手动填入配置文件 > ⚠依赖谷歌浏览器,请安装谷歌浏览器,windows下请将安全目录加入系统变量Path
> ⚠【重要提醒】自动获取eid和fp期间,建议鼠标跟随页面跳转,滑动到【加入购物车】【去购车结算】【去结算】按钮,但不要点击,可以提升获取成功率!(经验之谈!)
执行以下命令按照提示操作: 执行以下命令按照提示操作:
```shell ```shell
@ -113,9 +112,9 @@ $ zbarimg qr_code.png > qrcode.txt && qrencode -r qrcode.txt -o - -t UTF8 # 解
## 抢购流程/抢购结果 ## 抢购流程/抢购结果
- 程序开始抢购总时间为两分钟,不管有无抢购成功,都会停止,抢购详情请查阅日志和自己配置的第三方推送服务 - 程序开始抢购总时间为两分钟,不管有无抢购成功,都会停止,抢购详情请查阅日志和自己配置的第三方推送服务(目前支持:钉钉机器人、SMTP邮件、Server酱)
- 第二天抢购需要修改抢购时间和重新开始抢购任务。 - 抢购开始时间设定,格式:09:59:59,如果小于当前时间,则表示明天这个时间点开始;大于当前时间,则为今天
- 先写这么多。。。 - 先写这么多。。。

114
cmd/jdTdudfp.go

@ -2,16 +2,20 @@ package cmd
import ( import (
"context" "context"
"fmt"
"github.com/chromedp/cdproto/cdp" "github.com/chromedp/cdproto/cdp"
"github.com/chromedp/cdproto/network" "github.com/chromedp/cdproto/network"
"github.com/chromedp/cdproto/target" "github.com/chromedp/cdproto/target"
"github.com/chromedp/chromedp" "github.com/chromedp/chromedp"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
"github.com/unknwon/goconfig"
"github.com/ztino/jd_seckill/common" "github.com/ztino/jd_seckill/common"
"github.com/ztino/jd_seckill/jd_seckill" "github.com/ztino/jd_seckill/jd_seckill"
"log" "log"
"net/url" "net/url"
"os"
"strconv"
"time" "time"
) )
@ -26,22 +30,22 @@ var jdTdudfpCmd = &cobra.Command{
} }
func startJdTdudfp(cmd *cobra.Command, args []string) { func startJdTdudfp(cmd *cobra.Command, args []string) {
session:=jd_seckill.NewSession(common.CookieJar) session := jd_seckill.NewSession(common.CookieJar)
err:=session.CheckLoginStatus() err := session.CheckLoginStatus()
if err!=nil { if err != nil {
log.Println("自动获取eid和fp失败,请重新登录") log.Println("自动获取eid和fp失败,请重新登录")
}else { } else {
log.Println("开始自动获取eid和fp,如遇卡住请结束进程,重新启动") retryTimes, _ := strconv.Atoi(common.Config.MustValue("config", "retry_times", "5"))
options := []chromedp.ExecAllocatorOption{ options := []chromedp.ExecAllocatorOption{
chromedp.Flag("headless", false), chromedp.Flag("headless", false), //debug使用
//chromedp.Flag("blink-settings", "imagesEnabled=false"), chromedp.Flag("blink-settings", "imagesEnabled=false"), //禁用图片加载
chromedp.Flag("start-maximized", true), chromedp.Flag("start-maximized", true), //最大化窗口
chromedp.Flag("no-sandbox",true), chromedp.Flag("no-sandbox", true), //禁用沙盒, 性能优先
chromedp.Flag("disable-setuid-sandbox",true), chromedp.Flag("disable-setuid-sandbox", true), //禁用setuid沙盒, 性能优先
chromedp.Flag("no-default-browser-check",true), chromedp.Flag("no-default-browser-check", true), //不检查默认浏览器
chromedp.Flag("disable-plugins",true), chromedp.Flag("disable-plugins", true), //禁用扩展
chromedp.WindowSize(1920,1080), chromedp.UserAgent(common.Config.MustValue("config", "default_user_agent", "")),
chromedp.UserAgent(common.Config.MustValue("config","default_user_agent","")),
} }
options = append(chromedp.DefaultExecAllocatorOptions[:], options...) options = append(chromedp.DefaultExecAllocatorOptions[:], options...)
@ -49,9 +53,10 @@ func startJdTdudfp(cmd *cobra.Command, args []string) {
defer cc() defer cc()
ctx, cancel := chromedp.NewContext(c) ctx, cancel := chromedp.NewContext(c)
ch := addNewTabListener(ctx) _ = addNewTabListener(ctx)
defer cancel() defer cancel()
//设置cookie
u, _ := url.Parse("http://jd.com") u, _ := url.Parse("http://jd.com")
cookies := common.CookieJar.Cookies(u) cookies := common.CookieJar.Cookies(u)
err = chromedp.Run(ctx, err = chromedp.Run(ctx,
@ -67,60 +72,73 @@ func startJdTdudfp(cmd *cobra.Command, args []string) {
} }
return nil return nil
}), }),
chromedp.Navigate("https://jd.com"),
chromedp.Sleep(2*time.Second),
chromedp.Click(".cate_menu_lk"),
chromedp.Sleep(2*time.Second),
}, },
) )
if err != nil { if err != nil {
log.Println("设置cookie出错了")
log.Fatal(err) log.Fatal(err)
} }
newCtx, cancel2 := chromedp.NewContext(ctx, chromedp.WithTargetID(<-ch)) RETRY:
ch2 := addNewTabListener(newCtx) retryTimes--
defer cancel2() log.Println("【重要提醒】自动获取eid和fp期间,建议鼠标跟随页面跳转,滑动到【加入购物车】【去购车结算】【去结算】按钮,但不要点击,可以提升获取成功率!")
log.Println(fmt.Sprintf("开始自动获取eid和fp,如遇卡住请耐心等待,重试次数剩余: %v 次", retryTimes))
err = chromedp.Run(newCtx,
chromedp.Click(".goods_item_link"),
chromedp.Sleep(4*time.Second),
)
if err != nil {
log.Fatal(err)
}
newCtx2, cancel3 := chromedp.NewContext(ctx, chromedp.WithTargetID(<-ch2))
defer cancel3()
var res []byte var res []byte
err = chromedp.Run(newCtx2, testSkuId := common.Config.MustValue("config", "test_sku_id", "")
err = chromedp.Run(ctx,
chromedp.Tasks{
chromedp.Navigate(fmt.Sprintf("http://item.jd.com/%s.html", testSkuId)),
chromedp.WaitVisible("#InitCartUrl"), //加入购物车
chromedp.Sleep(2 * time.Second),
chromedp.Click("#InitCartUrl"), chromedp.Click("#InitCartUrl"),
chromedp.Sleep(2*time.Second), chromedp.WaitVisible(".btn-addtocart"), //去购车结算
chromedp.Sleep(2 * time.Second),
chromedp.Click(".btn-addtocart"), chromedp.Click(".btn-addtocart"),
chromedp.Sleep(2*time.Second), chromedp.WaitVisible(".common-submit-btn"), //去结算
chromedp.Sleep(2 * time.Second),
chromedp.Click(".common-submit-btn"), chromedp.Click(".common-submit-btn"),
chromedp.Sleep(3*time.Second), chromedp.Sleep(3 * time.Second),
chromedp.Evaluate("_JdTdudfp", &res), chromedp.Evaluate("_JdTdudfp", &res),
},
) )
if err != nil { if err != nil {
log.Println("chromedp 出错了")
log.Fatal(err) log.Fatal(err)
} }
value:=string(res) value := string(res)
if !gjson.Valid(value) || gjson.Get(value,"eid").String()=="" || gjson.Get(value,"fp").String()=="" { if !gjson.Valid(value) || gjson.Get(value, "eid").String() == "" || gjson.Get(value, "fp").String() == "" {
log.Println("获取失败,请重新尝试,返回信息:"+value) log.Println("获取失败,请重新尝试,返回信息:" + value)
}else{ if retryTimes > 0 {
log.Println("获取成功,请手动填入配置文件") goto RETRY
log.Println("eid:"+gjson.Get(value,"eid").String()) }
log.Println("fp:"+gjson.Get(value,"fp").String()) } else {
eid := gjson.Get(value, "eid").String()
fp := gjson.Get(value, "fp").String()
log.Println("eid:" + eid)
log.Println("fp:" + fp)
//修改配置文件
confFile := "./conf.ini"
cfg, err := goconfig.LoadConfigFile(confFile)
if err != nil {
log.Println("配置文件不存在,程序退出")
os.Exit(0)
}
cfg.SetValue("config", "eid", eid)
cfg.SetValue("config", "fp", fp)
if err := goconfig.SaveConfigFile(cfg, confFile); err != nil {
log.Println("保存配置文件失败,请手动填入配置文件")
} }
log.Println("eid, fp参数已经自动填入配置文件")
}
} }
} }
func addNewTabListener(ctx context.Context) <-chan target.ID { func addNewTabListener(ctx context.Context) <-chan target.ID {
/* mux := http.NewServeMux()
ts := httptest.NewServer(mux)
defer ts.Close()*/
return chromedp.WaitNewTarget(ctx, func(info *target.Info) bool { return chromedp.WaitNewTarget(ctx, func(info *target.Info) bool {
return true return true
}) })

4
common/lib.go

@ -109,3 +109,7 @@ func OpenImage(file string) {
} }
} }
} }
func Hour2Unix(hour string) (time.Time, error) {
return time.ParseInLocation(DateTimeFormatStr, time.Now().Format(DateFormatStr) + " " + hour, time.Local)
}

9
common/var.go

@ -5,9 +5,12 @@ import (
"github.com/unknwon/goconfig" "github.com/unknwon/goconfig"
) )
const SoftName = "jd_seckill" const (
SoftName = "jd_seckill"
const Version = "0.1.7" Version = "0.1.7"
DateTimeFormatStr = "2006-01-02 15:04:05"
DateFormatStr = "2006-01-02"
)
var Client *httpc.HttpClient var Client *httpc.HttpClient

14
conf.ini

@ -4,13 +4,16 @@
# 随意填写可能导致订单无法提交等问题 # 随意填写可能导致订单无法提交等问题
eid = eid =
fp = fp =
# 自动获取eid, fp的重试次数,测试商品sku_id
retry_times = 5
test_sku_id = 100016034372
# 商品id # 商品id
# 已经是茅台的sku_id了 # 已经是茅台的sku_id了
sku_id = 100012043978 sku_id = 100012043978
# 抢购数量 # 抢购数量
seckill_num = 2 seckill_num = 2
# 抢购开始时间设定 2021-01-01 09:59:59 # 抢购开始时间设定,格式:09:59:59,如果小于当前时间,则表示明天这个时间点开始;大于当前时间,则为今天。
buy_time = 2021-01-01 09:59:59 buy_time = 09:59:59
# 抢购总时间,单位:分钟,默认两分钟 # 抢购总时间,单位:分钟,默认两分钟
seckill_time = seckill_time =
# 抢购任务数量,默认5个 # 抢购任务数量,默认5个
@ -51,3 +54,10 @@ port =
email_user = email_user =
# 邮箱授权码(并不一定是邮箱密码) xxxxxxxxxxxxxxxx # 邮箱授权码(并不一定是邮箱密码) xxxxxxxxxxxxxxxx
email_pwd = email_pwd =
#钉钉机器人配置
[dingtalk]
# 机器人的Webhook地址(https://oapi.dingtalk.com/robot/send?access_token=XXXXXX)的access_token
access_token =
# 密钥,机器人安全设置页面,加签一栏下面显示的SEC开头的字符串
secret =

4
go.mod

@ -5,14 +5,18 @@ go 1.15
require ( require (
github.com/Albert-Zhan/httpc v0.0.0-20190712132051-aed72924b5e6 github.com/Albert-Zhan/httpc v0.0.0-20190712132051-aed72924b5e6
github.com/PuerkitoBio/goquery v1.6.0 github.com/PuerkitoBio/goquery v1.6.0
github.com/blinkbean/dingtalk v0.0.0-20201231030509-45a553a84503
github.com/chromedp/cdproto v0.0.0-20201204063249-be40c824ad18 github.com/chromedp/cdproto v0.0.0-20201204063249-be40c824ad18
github.com/chromedp/chromedp v0.5.4 github.com/chromedp/chromedp v0.5.4
github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/httphead v0.1.0 // indirect
github.com/json-iterator/go v1.1.10 // indirect
github.com/spf13/cobra v1.1.1 github.com/spf13/cobra v1.1.1
github.com/tidwall/gjson v1.6.7 github.com/tidwall/gjson v1.6.7
github.com/unknwon/goconfig v0.0.0-20200908083735-df7de6a44db8 github.com/unknwon/goconfig v0.0.0-20200908083735-df7de6a44db8
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 // indirect golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 // indirect
golang.org/x/text v0.3.4 golang.org/x/text v0.3.4
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
) )

4
jd_seckill/seckill.go

@ -63,7 +63,9 @@ func (this *Seckill) MakeReserve() {
reserveUrl := gjson.Get(body, "url").String() reserveUrl := gjson.Get(body, "url").String()
req = httpc.NewRequest(this.client) req = httpc.NewRequest(this.client)
_, _, _ = req.SetUrl("https:" + reserveUrl).SetMethod("get").Send().End() _, _, _ = req.SetUrl("https:" + reserveUrl).SetMethod("get").Send().End()
log.Println("预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约") msg := "预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约![我的预约](https://yushou.jd.com/member/qualificationList.action)"
_ = service.SendMessage(this.conf, "茅台抢购通知", msg)
log.Println(msg)
} }
} }

15
main.go

@ -5,12 +5,26 @@ import (
"github.com/unknwon/goconfig" "github.com/unknwon/goconfig"
"github.com/ztino/jd_seckill/cmd" "github.com/ztino/jd_seckill/cmd"
"github.com/ztino/jd_seckill/common" "github.com/ztino/jd_seckill/common"
"io"
"log" "log"
"os" "os"
"runtime" "runtime"
"time"
) )
func init() { func init() {
//将日志同时输出到控制台和文件
file := "./" + "jd_seckill_" + time.Now().Format("20060102") + ".log"
logFile, logErr := os.OpenFile(file, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if logErr != nil {
panic(logErr)
}
defer logFile.Close()
mw := io.MultiWriter(os.Stdout, logFile)
log.SetOutput(mw)
log.SetPrefix("[jd_seckill]")
log.SetFlags(log.LstdFlags | log.Lshortfile | log.LUTC)
//客户端设置初始化 //客户端设置初始化
common.Client=httpc.NewHttpClient() common.Client=httpc.NewHttpClient()
common.CookieJar=httpc.NewCookieJar() common.CookieJar=httpc.NewCookieJar()
@ -26,6 +40,7 @@ func init() {
//抢购状态管道 //抢购状态管道
common.SeckillStatus=make(chan bool) common.SeckillStatus=make(chan bool)
log.Println("jd_seckill 程序启动成功,祝您成功!")
} }
func main() { func main() {

35
service/dingtalk.go

@ -0,0 +1,35 @@
package service
import (
"github.com/blinkbean/dingtalk"
"github.com/unknwon/goconfig"
"log"
)
type Dingtalk struct {
conf *goconfig.ConfigFile
}
func NewDingtalk(conf *goconfig.ConfigFile) *Dingtalk {
return &Dingtalk{conf: conf}
}
func (this *Dingtalk) Send(title, msg string) error {
cli := dingtalk.InitDingTalkWithSecret(
this.conf.MustValue("dingtalk", "access_token", ""),
this.conf.MustValue("dingtalk", "secret", ""),
)
markdown := []string{
"### " + title,
"---------",
msg,
}
err := cli.SendMarkDownMessageBySlice(title, markdown)
if err != nil {
log.Println(err)
} else {
log.Println("钉钉机器人推送成功")
}
return nil
}

22
service/service.go

@ -2,18 +2,24 @@ package service
import "github.com/unknwon/goconfig" import "github.com/unknwon/goconfig"
func SendMessage(conf *goconfig.ConfigFile,title,msg string) error { func SendMessage(conf *goconfig.ConfigFile, title, msg string) error {
if conf.MustValue("messenger","enable","false")=="true" { if conf.MustValue("messenger", "enable", "false") == "true" {
//钉钉机器人
if conf.MustValue("messenger", "type", "none") == "dingtalk" {
dingtalk := NewDingtalk(conf)
err := dingtalk.Send(title, msg)
return err
}
//邮件发送 //邮件发送
if conf.MustValue("messenger","type","none")=="smtp" { if conf.MustValue("messenger", "type", "none") == "smtp" {
email:=NewEmail(conf) email := NewEmail(conf)
err:=email.Send([]string{conf.MustValue("messenger","email","")},title,msg) err := email.Send([]string{conf.MustValue("messenger", "email", "")}, title, msg)
return err return err
} }
//Server酱推送 //Server酱推送
if conf.MustValue("messenger","type","none")=="wechat" { if conf.MustValue("messenger", "type", "none") == "wechat" {
wechat:=NewWechat(conf) wechat := NewWechat(conf)
err:=wechat.Send(title,msg) err := wechat.Send(title, msg)
return err return err
} }
} }

Loading…
Cancel
Save