From f0a0da68b7743ed3f55602da5542a9e3ffc282a0 Mon Sep 17 00:00:00 2001 From: johlanse Date: Sun, 31 Jul 2022 20:33:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=BE=AE=E4=BF=A1=E5=85=AC?= =?UTF-8?q?=E4=BC=97=E5=8F=B7=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.go | 10 +- conf/config_default.yml | 19 ++++ docs/push.md | 29 +++++ go.mod | 9 +- go.sum | 19 ++-- lib/core.go | 19 +++- lib/core_test.go | 27 ----- lib/tg.go | 2 +- main.go | 27 +++-- model/model.go | 1 + model/user.go | 59 ++++++++-- web/wx.go | 242 ++++++++++++++++++++++++++++++++++++++-- 12 files changed, 385 insertions(+), 78 deletions(-) diff --git a/conf/config.go b/conf/config.go index 320dbb5..e62d59a 100644 --- a/conf/config.go +++ b/conf/config.go @@ -56,9 +56,13 @@ type Config struct { } `json:"retry" yaml:"retry"` Wechat struct { - Token string `json:"token" yaml:"token"` - Secret string `json:"secret" yaml:"secret"` - AppID string `json:"app_id" yaml:"app_id"` + Enable bool `json:"enable" yaml:"enable"` + Token string `json:"token" yaml:"token"` + Secret string `json:"secret" yaml:"secret"` + AppID string `json:"app_id" yaml:"app_id"` + LoginTempID string `json:"login_temp_id" yaml:"login_temp_id"` + NormalTempID string `json:"normal_temp_id" yaml:"normal_temp_id"` + PushLoginWarn bool `json:"push_login_warn" yaml:"push_login_warn"` } `json:"wechat" yaml:"wechat"` // 专项答题可接受的最小值 SpecialMinScore int `json:"special_min_score" yaml:"special_min_score"` diff --git a/conf/config_default.yml b/conf/config_default.yml index 5567942..32deb14 100644 --- a/conf/config_default.yml +++ b/conf/config_default.yml @@ -51,6 +51,25 @@ web: # 网页端登录密码 password: admin +# 微信公众号测试号配置 +wechat: + # 是否启用 + enable: false + # 开发者平台设置的token + token: "" + # 开发者平台的secret + secret: "" + # 开发者平台的appId + app_id: "" + # 发送登录消息需要使用的消息模板 + # 模板标题,随意 模板内容: 点我登录,然后在浏览器中打开!! + login_temp_id: "" + # 发送普通消息需要使用的消息模板 + # 模板标题:随意 模板内容: {{data.DATA}} + normal_temp_id: "-6Q" + # xxqg会每隔两小时左右检查所有用户的ck有效性,若开启该选项,会在检查失败时推送提醒消息 + push_login_warn: false + # 登录重试配置 retry: # 重试次数 diff --git a/docs/push.md b/docs/push.md index 26ddf5d..eb97c6c 100644 --- a/docs/push.md +++ b/docs/push.md @@ -1,5 +1,34 @@ ## 推送配置 +### 微信公众号推送 +配置config.yml的如下部分 +```yaml +# 微信公众号测试号配置 +wechat: + # 是否启用 + enable: false + # 开发者平台设置的token + token: "" + # 开发者平台的secret + secret: "" + # 开发者平台的appId + app_id: "" + # 发送登录消息需要使用的消息模板 + # 模板标题,随意 模板内容: 点我登录,然后在浏览器中打开!! + login_temp_id: "" + # 发送普通消息需要使用的消息模板 + # 模板标题:随意 模板内容: {{data.DATA}} + normal_temp_id: "" + # xxqg会每隔两小时左右检查所有用户的ck有效性,若开启该选项,会在检查失败时推送提醒消息 + push_login_warn: false +``` + ++ 前往微信[公众号开发者平台](http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login),手机微信扫码登录 ++ 配置url为**http:ip:port/wx**,ip和端口在web项配置中配置 ++ 设置token ++ 分别添加登录模板消息和普通模板消息,添加要求查看配置项注释 ++ 在配置文件中配置所有内容,启动程序 + ### web推送 > 适用于部署在服务器上或者家里有公网IP的设备上 diff --git a/go.mod b/go.mod index 3c4bcd8..13c0a11 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/google/uuid v1.3.0 github.com/guonaihong/gout v0.2.9 github.com/imroc/req/v3 v3.8.2 + github.com/johlanse/wechat v0.0.0-20220731103216-3c9e7b56434f github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/klauspost/compress v1.15.5 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible @@ -19,7 +20,7 @@ require ( github.com/mxschmitt/playwright-go v0.1400.0 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/robfig/cron/v3 v3.0.0 - github.com/sirupsen/logrus v1.8.1 + github.com/sirupsen/logrus v1.9.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 github.com/tidwall/gjson v1.12.1 @@ -29,7 +30,6 @@ require ( ) require ( - github.com/beevik/etree v1.1.0 // indirect github.com/codegangsta/negroni v1.0.0 // indirect github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect github.com/gin-contrib/sse v0.1.0 // indirect @@ -44,7 +44,6 @@ require ( github.com/gorilla/websocket v1.4.2 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/huoxue1/lorca v0.1.11 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/json-iterator/go v1.1.10 // indirect github.com/julienschmidt/httprouter v1.3.0 // indirect @@ -58,8 +57,6 @@ require ( github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pkg/errors v0.9.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect - github.com/rosbit/go-wget v1.2.5 // indirect - github.com/rosbit/go-wx-api v0.5.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/ugorji/go/codec v1.2.5 // indirect @@ -68,7 +65,7 @@ require ( golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect golang.org/x/mod v0.3.0 // indirect golang.org/x/net v0.0.0-20220111093109-d55c255bac03 // indirect - golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect + golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect diff --git a/go.sum b/go.sum index 75d8d83..d068cf1 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII= github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= -github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= -github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/codegangsta/negroni v1.0.0 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY= github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= @@ -55,10 +53,14 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/huoxue1/lorca v0.1.11 h1:B5x4Xta8rauGCERoCHY7nWg2z1GUrksf9ycZGHwLs4Q= -github.com/huoxue1/lorca v0.1.11/go.mod h1:eEI3aGkmMladREqFJwUhd8cLlSUgG13wvuNprVjaVaY= github.com/imroc/req/v3 v3.8.2 h1:wFZ7B0dclCQyjClP5GwXRboUGIek5l0mCpodrGgT01c= github.com/imroc/req/v3 v3.8.2/go.mod h1:3JIicOKEDHfCSYYNLb/ObZNpx64EV5y40VlHMwhUCzU= +github.com/johlanse/wechat v0.0.0-20220731101603-68047d1e93e2 h1:hbe3Fbjc+1Ne+fUzxRlxYCzZOi9kzwhacys9D1tXreo= +github.com/johlanse/wechat v0.0.0-20220731101603-68047d1e93e2/go.mod h1:Dwcvo5MQU6u7XKoClpxoUlJMBAJhES9Qz4d2TSSBrU8= +github.com/johlanse/wechat v0.0.0-20220731102434-854afb866cf2 h1:57tGW/IDYuqtbHZSXuVFRkwTRD6dmcOL/M86yK2MyTA= +github.com/johlanse/wechat v0.0.0-20220731102434-854afb866cf2/go.mod h1:Dwcvo5MQU6u7XKoClpxoUlJMBAJhES9Qz4d2TSSBrU8= +github.com/johlanse/wechat v0.0.0-20220731103216-3c9e7b56434f h1:wpe87qm/nBbQ8BT+mrYvEHnXRbn11+2iSYpjrj62QAQ= +github.com/johlanse/wechat v0.0.0-20220731103216-3c9e7b56434f/go.mod h1:dLGDxcVd4CFRQInD2S2aMm4CGobWAFixOEgFxXZJ6Sw= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -115,13 +117,11 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6O github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E= github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/rosbit/go-wget v1.2.5 h1:XQFUwTJR8HDwYvxYH8X0cq3BkN1BOMoADc92KKp9Pcs= -github.com/rosbit/go-wget v1.2.5/go.mod h1:8Tt92GKM/K9WI65yYkX0lwJDnRp64Vd082ojViWT6T4= -github.com/rosbit/go-wx-api v0.5.0 h1:aAjTHCd2o3b14O0JYx2b+qzbZh8ThlKWMD+QsQEK9vQ= -github.com/rosbit/go-wx-api v0.5.0/go.mod h1:+HyYx6Avz1sPXTN+JBGyW1mf/jToDkXoNZXHX7S2F8I= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -163,7 +163,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220111093109-d55c255bac03 h1:0FB83qp0AzVJm+0wcIlauAjJ+tNdh7jLuacRYCIVv7s= @@ -186,6 +185,8 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= diff --git a/lib/core.go b/lib/core.go index 0da55bc..32d2e88 100644 --- a/lib/core.go +++ b/lib/core.go @@ -169,7 +169,7 @@ func (c *Core) GenerateCode() (string, string, error) { return codeURL, g.Result, err } -func (c *Core) CheckQrCode(code string) (*model.User, bool, error) { +func (c *Core) CheckQrCode(code, pushID string) (*model.User, bool, error) { client := req.C() client.OnAfterResponse(func(client *req.Client, response *req.Response) error { return nil @@ -218,6 +218,7 @@ func (c *Core) CheckQrCode(code string) (*model.User, bool, error) { UID: uid, Token: response.Cookies()[0].Value, LoginTime: time.Now().Unix(), + PushId: pushID, } err = model.AddUser(user) if err != nil { @@ -235,7 +236,7 @@ func (c *Core) CheckQrCode(code string) (*model.User, bool, error) { * @return *model.User * @return error */ -func (c *Core) L(retryTimes int) (*model.User, error) { +func (c *Core) L(retryTimes int, pushID string) (*model.User, error) { _, codeData, err := c.GenerateCode() if err != nil { return nil, err @@ -247,7 +248,7 @@ func (c *Core) L(retryTimes int) (*model.User, error) { client.SetCommonHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36") for i := 0; i < 150; i++ { - user, b, err := c.CheckQrCode(codeData) + user, b, err := c.CheckQrCode(codeData, pushID) if b && err == nil { return user, err } @@ -258,10 +259,20 @@ func (c *Core) L(retryTimes int) (*model.User, error) { // 等待几分钟后重新执行 time.Sleep(time.Duration(conf.GetConfig().Retry.Intervals) * time.Minute) c.Push("text", fmt.Sprintf("登录超时,将进行第%d重新次登录", retryTimes)) - return c.L(retryTimes - 1) + return c.L(retryTimes-1, pushID) } func (c *Core) initWindows() { + _, err := os.Stat("C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe") + if err != nil { + if os.IsNotExist(err) && conf.GetConfig().EdgePath == "" { + log.Warningln("检测到edge浏览器不存在并且未配置edge_path,将再次运行时自动下载chrome浏览器") + c.initNotWindows() + return + } + err = nil + } + dir, err := os.Getwd() if err != nil { return diff --git a/lib/core_test.go b/lib/core_test.go index 99bd25a..55c21f8 100644 --- a/lib/core_test.go +++ b/lib/core_test.go @@ -1,28 +1 @@ package lib - -import ( - "fmt" - "testing" -) - -func TestName(t *testing.T) { - core := Core{} - core.Init() - cookies, err := core.Login() - if err != nil { - return - } - score, err := GetUserScore(cookies) - if err != nil { - return - } - fmt.Println(score) -} - -func TestLogin(t *testing.T) { - core := Core{} - core.Push = func(kind string, message string) { - fmt.Println(message) - } - core.L() -} diff --git a/lib/tg.go b/lib/tg.go index aa165d1..9ea5a58 100644 --- a/lib/tg.go +++ b/lib/tg.go @@ -178,7 +178,7 @@ func login(bot *Telegram, args []string) { } core.Init() defer core.Quit() - _, err := core.L(config.Retry.Times) + _, err := core.L(config.Retry.Times, "") if err != nil { bot.SendMsg(err.Error()) return diff --git a/main.go b/main.go index 219e70d..3eddcba 100644 --- a/main.go +++ b/main.go @@ -93,18 +93,22 @@ func main() { os.Exit(1) } - if config.Web.Enable { - engine := web.RouterInit() - go func() { - h := http.NewServeMux() + engine := web.RouterInit() + go func() { + h := http.NewServeMux() + if config.Web.Enable { h.Handle("/", engine) - //h.HandleFunc("/wx", web.HandleWechat) + } + if config.Wechat.Enable { + h.HandleFunc("/wx", web.HandleWechat) + } + if config.Web.Enable || config.Wechat.Enable { err := http.ListenAndServe(fmt.Sprintf("%s:%d", config.Web.Host, config.Web.Port), h) if err != nil { return } - }() - } + } + }() if config.StartWait > 0 { log.Infoln(fmt.Sprintf("将等待%d秒后启动程序", config.StartWait)) @@ -159,7 +163,7 @@ func main() { }() } - if !config.TG.Enable && config.Cron == "" { + if !config.TG.Enable && config.Cron == "" && !config.Wechat.Enable { log.Infoln("已采用普通学习模式") do("normal") } else { @@ -190,7 +194,6 @@ func do(m string) { var user *model.User users, _ := model.Query() study := func(core2 *lib.Core, u *model.User) { - defer func() { err := recover() if err != nil { @@ -229,7 +232,7 @@ func do(m string) { // 用户小于1时自动登录 if len(users) < 1 { log.Infoln("未检测到有效用户信息,将采用登录模式") - u, err := core.L(config.Retry.Times) + u, err := core.L(config.Retry.Times, "") if err != nil { log.Errorln(err.Error()) return @@ -242,7 +245,7 @@ func do(m string) { study(core, u) } if len(users) < 1 { - user, err := core.L(config.Retry.Times) + user, err := core.L(config.Retry.Times, "") if err != nil { core.Push("msg", "登录超时") return @@ -278,7 +281,7 @@ func do(m string) { } if i == 0 { - u, err := core.L(config.Retry.Times) + u, err := core.L(config.Retry.Times, "") if err != nil { log.Errorln(err.Error()) return diff --git a/model/model.go b/model/model.go index a323674..03c6967 100644 --- a/model/model.go +++ b/model/model.go @@ -19,6 +19,7 @@ func init() { log.Errorln("用户数据库打开失败,请检查config目录权限") log.Panicln(err.Error()) } + _, _ = db.Exec(`create table user ( nick TEXT, diff --git a/model/user.go b/model/user.go index 29378ba..7251b51 100644 --- a/model/user.go +++ b/model/user.go @@ -4,6 +4,7 @@ package model import ( "database/sql" + "fmt" "math/rand" "net/http" "time" @@ -14,6 +15,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" + "github.com/huoxue1/study_xxqg/conf" "github.com/huoxue1/study_xxqg/push" ) @@ -21,6 +23,14 @@ func init() { go check() } +var ( + wechatPush func(id, message string) +) + +func SetPush(push func(id, message string)) { + wechatPush = push +} + // User /** * @Description: @@ -42,7 +52,7 @@ type User struct { func Query() ([]*User, error) { var users []*User ping() - var results, err = db.Query("select * from user") + results, err := db.Query("select * from user") if err != nil { return nil, err } @@ -58,17 +68,50 @@ func Query() ([]*User, error) { if err != nil { return nil, err } - // login := time.Unix(u.LoginTime, 0) - // sub := time.Now().Sub(login) if CheckUserCookie(u) { - // if lib.GetConfig().ForceExpiration != 0 && sub.Hours() > float64(lib.GetConfig().ForceExpiration) { - // log.Infoln("用户" + u.Nick + "cookie已强制失效") - // continue - // } users = append(users, u) } else { log.Infoln("用户" + u.Nick + "cookie已失效") - push.PushMessage("", "用户"+u.UID+"已失效,请登录", "login", u.PushId) + _ = push.PushMessage("", "用户"+u.UID+"已失效,请登录", "login", u.PushId) + if conf.GetConfig().Wechat.PushLoginWarn { + wechatPush(u.PushId, fmt.Sprintf("用户%v已失效!!", u.Nick)) + } + _ = DeleteUser(u.UID) + } + } + return users, err +} + +// QueryByPushID +/** + * @Description: + * @return []*User + * @return error + */ +func QueryByPushID(pushID string) ([]*User, error) { + var users []*User + ping() + results, err := db.Query("select * from user where push_id = ?", pushID) + if err != nil { + return users, err + } + defer func(results *sql.Rows) { + err := results.Close() + if err != nil { + log.Errorln("关闭results失败" + err.Error()) + } + }(results) + for results.Next() { + u := new(User) + err := results.Scan(&u.Nick, &u.UID, &u.Token, &u.LoginTime, &u.PushId) + if err != nil { + return users, err + } + if CheckUserCookie(u) { + users = append(users, u) + } else { + log.Infoln("用户" + u.Nick + "cookie已失效") + _ = push.PushMessage("", "用户"+u.UID+"已失效,请登录", "login", u.PushId) _ = DeleteUser(u.UID) } } diff --git a/web/wx.go b/web/wx.go index 6f666dc..5062088 100644 --- a/web/wx.go +++ b/web/wx.go @@ -1,42 +1,268 @@ package web import ( + "encoding/json" + "fmt" "net/http" + "strings" + "sync" + "time" + "github.com/johlanse/wechat/mp" + "github.com/johlanse/wechat/mp/request" log "github.com/sirupsen/logrus" - "github.com/yujinliang/wechat/mp" - "github.com/yujinliang/wechat/mp/request" "github.com/huoxue1/study_xxqg/conf" + "github.com/huoxue1/study_xxqg/lib" + "github.com/huoxue1/study_xxqg/model" ) func init() { - //InitWechat() + initWechat() } var ( - wx *mp.WeiXin + wx *mp.WeiXin + lastNonce = "" + datas sync.Map ) -func InitWechat() { +const ( + login = "login" + StartStudy = "start_study" + getUser = "stop_study" + SCORE = "score" +) + +func initWechat() { config := conf.GetConfig() + if !config.Wechat.Enable { + return + } log.Infoln(config.Wechat) wx = mp.New(config.Wechat.Token, config.Wechat.AppID, config.Wechat.Secret, "123", "123") - wx.CreateMenu(&mp.Menu{Buttons: []mp.MenuButton{ + err := wx.CreateMenu(&mp.Menu{Buttons: []mp.MenuButton{ { Name: "登录", Type: "click", - Key: "login", + Key: login, Url: "", MediaId: "", SubButtons: nil, }, + { + Name: "学习管理", + Type: "click", + Key: "study", + Url: "", + MediaId: "", + SubButtons: []mp.MenuButton{ + { + Name: "开始学习", + Type: "click", + Key: StartStudy, + Url: "", + MediaId: "", + SubButtons: nil, + }, + { + Name: "获取用户", + Type: "click", + Key: getUser, + Url: "", + MediaId: "", + SubButtons: nil, + }, + { + Name: "积分查询", + Type: "click", + Key: SCORE, + Url: "", + MediaId: "", + SubButtons: nil, + }, + }, + }, }}) + if err != nil { + log.Errorln("设置自定义菜单出现异常" + err.Error()) + return + } + if conf.GetConfig().Wechat.PushLoginWarn { + model.SetPush(sendMsg) + } wx.HandleFunc("eventCLICK", func(wx *mp.WeiXin, w http.ResponseWriter, r *request.WeiXinRequest, timestamp, nonce string) { - log.Infoln(r.EventKey) + if lastNonce == nonce { + return + } + lastNonce = nonce + switch r.EventKey { + case login: + go handleLogin(r.FromUserName) + case StartStudy: + go handleStartStudy(r.FromUserName) + case getUser: + go handleGetUser(r.FromUserName) + case SCORE: + go handleScore(r.FromUserName) + } }) } +func sendMsg(id, message string) { + m := map[string]interface{}{ + "data": map[string]string{ + "value": message, + }, + } + data, _ := json.Marshal(m) + _, err := wx.SendTemplateMessage(&mp.TemplateMessage{ + ToUser: id, + TemplateId: conf.GetConfig().Wechat.NormalTempID, + URL: "", + TopColor: "", + RawJSONData: data, + }) + if err != nil { + return + } +} + +// HandleWechat +/* @Description: + * @param rep + * @param req + */ func HandleWechat(rep http.ResponseWriter, req *http.Request) { wx.ServeHTTP(rep, req) } + +func handleLogin(id string) { + defer func() { + err := recover() + if err != nil { + log.Errorln("处理微信事件错误") + log.Errorln(err) + } + }() + core := &lib.Core{Push: func(kind, message string) { + if kind == "flush" && strings.HasPrefix(message, "登录链接") { + l := strings.ReplaceAll(message, "登录链接:\r\n", "") + _, err := wx.SendTemplateMessage(&mp.TemplateMessage{ + ToUser: id, + TemplateId: conf.GetConfig().Wechat.LoginTempID, + URL: l, + TopColor: "", + RawJSONData: nil, + }) + if err != nil { + log.Errorln(err.Error()) + return + } + } + }} + _, err := core.L(0, id) + if err != nil { + return + } + sendMsg(id, "登录成功") +} + +func handleStartStudy(id string) { + defer func() { + err := recover() + if err != nil { + log.Errorln("处理微信事件错误") + log.Errorln(err) + } + }() + users, err := model.QueryByPushID(id) + if err != nil { + return + } + if users == nil { + log.Warningln("还未存在绑定的用户登录") + sendMsg(id, "你还没有已登陆的用户,请点击下方登录按钮登录!") + return + } + core := &lib.Core{ShowBrowser: conf.GetConfig().ShowBrowser, Push: func(kind, msg string) { + }} + core.Init() + defer core.Quit() + for i, user := range users { + _, ok := datas.Load(user.UID) + if ok { + log.Warningln("用户" + user.Nick + "已经在学习中了,跳过该用户") + continue + } else { + datas.Store(user.UID, "") + } + sendMsg(id, fmt.Sprintf("开始学习第%d个用户,用户名:%v", i+1, user.Nick)) + core.LearnArticle(user) + core.LearnVideo(user) + core.RespondDaily(user, "daily") + core.RespondDaily(user, "weekly") + core.RespondDaily(user, "special") + datas.Delete(user.UID) + score, _ := lib.GetUserScore(user.ToCookies()) + sendMsg(id, fmt.Sprintf("第%d个用户%v学习完成,学习积分\n%v", i+1, user.Nick, lib.FormatScore(score))) + } +} + +func handleGetUser(id string) { + defer func() { + err := recover() + if err != nil { + log.Errorln("处理微信事件错误") + log.Errorln(err) + } + }() + users, err := model.Query() + if err != nil { + return + } + if users == nil { + log.Warningln("还未存在绑定的用户登录") + sendMsg(id, "你还没有已登陆的用户,请点击下方登录按钮登录!") + return + } + message := "" + for _, user := range users { + message += fmt.Sprintf("%v ==> %v", user.Nick, time.Unix(user.LoginTime, 0).Format("2006-01-02 15:04:05")) + if user.PushId == id { + message += "(已绑定)\n" + } + } + sendMsg(id, message) +} + +func handleScore(id string) { + defer func() { + err := recover() + if err != nil { + log.Errorln("处理微信事件错误") + log.Errorln(err) + } + }() + + defer func() { + err := recover() + if err != nil { + log.Errorln("处理微信事件错误") + log.Errorln(err) + } + }() + users, err := model.Query() + if err != nil { + return + } + if users == nil { + log.Warningln("还未存在绑定的用户登录") + sendMsg(id, "你还没有已登陆的用户,请点击下方登录按钮登录!") + return + } + for _, user := range users { + score, _ := lib.GetUserScore(user.ToCookies()) + sendMsg(id, "用户:"+user.Nick+"\n"+lib.FormatScore(score)) + } +}