添加微信公众号支持

This commit is contained in:
johlanse 2022-07-31 20:33:39 +08:00
parent f73ec27d46
commit f0a0da68b7
12 changed files with 385 additions and 78 deletions

View File

@ -56,9 +56,13 @@ type Config struct {
} `json:"retry" yaml:"retry"` } `json:"retry" yaml:"retry"`
Wechat struct { Wechat struct {
Token string `json:"token" yaml:"token"` Enable bool `json:"enable" yaml:"enable"`
Secret string `json:"secret" yaml:"secret"` Token string `json:"token" yaml:"token"`
AppID string `json:"app_id" yaml:"app_id"` 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"` } `json:"wechat" yaml:"wechat"`
// 专项答题可接受的最小值 // 专项答题可接受的最小值
SpecialMinScore int `json:"special_min_score" yaml:"special_min_score"` SpecialMinScore int `json:"special_min_score" yaml:"special_min_score"`

View File

@ -51,6 +51,25 @@ web:
# 网页端登录密码 # 网页端登录密码
password: admin 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: retry:
# 重试次数 # 重试次数

View File

@ -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推送 ### web推送
> 适用于部署在服务器上或者家里有公网IP的设备上 > 适用于部署在服务器上或者家里有公网IP的设备上

9
go.mod
View File

@ -12,6 +12,7 @@ require (
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/guonaihong/gout v0.2.9 github.com/guonaihong/gout v0.2.9
github.com/imroc/req/v3 v3.8.2 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/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
github.com/klauspost/compress v1.15.5 github.com/klauspost/compress v1.15.5
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible 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/mxschmitt/playwright-go v0.1400.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/robfig/cron/v3 v3.0.0 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/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816
github.com/tidwall/gjson v1.12.1 github.com/tidwall/gjson v1.12.1
@ -29,7 +30,6 @@ require (
) )
require ( require (
github.com/beevik/etree v1.1.0 // indirect
github.com/codegangsta/negroni v1.0.0 // indirect github.com/codegangsta/negroni v1.0.0 // indirect
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
github.com/gin-contrib/sse v0.1.0 // 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/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // 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/jonboulle/clockwork v0.2.2 // indirect
github.com/json-iterator/go v1.1.10 // indirect github.com/json-iterator/go v1.1.10 // indirect
github.com/julienschmidt/httprouter v1.3.0 // 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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // 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/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect github.com/tidwall/pretty v1.2.0 // indirect
github.com/ugorji/go/codec v1.2.5 // 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/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect
golang.org/x/mod v0.3.0 // indirect golang.org/x/mod v0.3.0 // indirect
golang.org/x/net v0.0.0-20220111093109-d55c255bac03 // 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/text v0.3.7 // indirect
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 // indirect golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect

19
go.sum
View File

@ -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 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/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 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY=
github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= 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/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 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 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 h1:wFZ7B0dclCQyjClP5GwXRboUGIek5l0mCpodrGgT01c=
github.com/imroc/req/v3 v3.8.2/go.mod h1:3JIicOKEDHfCSYYNLb/ObZNpx64EV5y40VlHMwhUCzU= 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 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= 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= 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/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 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= 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.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 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.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 h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= 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= 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-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-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-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-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-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220111093109-d55c255bac03 h1:0FB83qp0AzVJm+0wcIlauAjJ+tNdh7jLuacRYCIVv7s= 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-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 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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/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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=

View File

@ -169,7 +169,7 @@ func (c *Core) GenerateCode() (string, string, error) {
return codeURL, g.Result, err 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 := req.C()
client.OnAfterResponse(func(client *req.Client, response *req.Response) error { client.OnAfterResponse(func(client *req.Client, response *req.Response) error {
return nil return nil
@ -218,6 +218,7 @@ func (c *Core) CheckQrCode(code string) (*model.User, bool, error) {
UID: uid, UID: uid,
Token: response.Cookies()[0].Value, Token: response.Cookies()[0].Value,
LoginTime: time.Now().Unix(), LoginTime: time.Now().Unix(),
PushId: pushID,
} }
err = model.AddUser(user) err = model.AddUser(user)
if err != nil { if err != nil {
@ -235,7 +236,7 @@ func (c *Core) CheckQrCode(code string) (*model.User, bool, error) {
* @return *model.User * @return *model.User
* @return error * @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() _, codeData, err := c.GenerateCode()
if err != nil { if err != nil {
return nil, err 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") 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++ { for i := 0; i < 150; i++ {
user, b, err := c.CheckQrCode(codeData) user, b, err := c.CheckQrCode(codeData, pushID)
if b && err == nil { if b && err == nil {
return user, err 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) time.Sleep(time.Duration(conf.GetConfig().Retry.Intervals) * time.Minute)
c.Push("text", fmt.Sprintf("登录超时,将进行第%d重新次登录", retryTimes)) c.Push("text", fmt.Sprintf("登录超时,将进行第%d重新次登录", retryTimes))
return c.L(retryTimes - 1) return c.L(retryTimes-1, pushID)
} }
func (c *Core) initWindows() { 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() dir, err := os.Getwd()
if err != nil { if err != nil {
return return

View File

@ -1,28 +1 @@
package lib 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()
}

View File

@ -178,7 +178,7 @@ func login(bot *Telegram, args []string) {
} }
core.Init() core.Init()
defer core.Quit() defer core.Quit()
_, err := core.L(config.Retry.Times) _, err := core.L(config.Retry.Times, "")
if err != nil { if err != nil {
bot.SendMsg(err.Error()) bot.SendMsg(err.Error())
return return

27
main.go
View File

@ -93,18 +93,22 @@ func main() {
os.Exit(1) os.Exit(1)
} }
if config.Web.Enable { engine := web.RouterInit()
engine := web.RouterInit() go func() {
go func() { h := http.NewServeMux()
h := http.NewServeMux() if config.Web.Enable {
h.Handle("/", engine) 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) err := http.ListenAndServe(fmt.Sprintf("%s:%d", config.Web.Host, config.Web.Port), h)
if err != nil { if err != nil {
return return
} }
}() }
} }()
if config.StartWait > 0 { if config.StartWait > 0 {
log.Infoln(fmt.Sprintf("将等待%d秒后启动程序", config.StartWait)) 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("已采用普通学习模式") log.Infoln("已采用普通学习模式")
do("normal") do("normal")
} else { } else {
@ -190,7 +194,6 @@ func do(m string) {
var user *model.User var user *model.User
users, _ := model.Query() users, _ := model.Query()
study := func(core2 *lib.Core, u *model.User) { study := func(core2 *lib.Core, u *model.User) {
defer func() { defer func() {
err := recover() err := recover()
if err != nil { if err != nil {
@ -229,7 +232,7 @@ func do(m string) {
// 用户小于1时自动登录 // 用户小于1时自动登录
if len(users) < 1 { if len(users) < 1 {
log.Infoln("未检测到有效用户信息,将采用登录模式") log.Infoln("未检测到有效用户信息,将采用登录模式")
u, err := core.L(config.Retry.Times) u, err := core.L(config.Retry.Times, "")
if err != nil { if err != nil {
log.Errorln(err.Error()) log.Errorln(err.Error())
return return
@ -242,7 +245,7 @@ func do(m string) {
study(core, u) study(core, u)
} }
if len(users) < 1 { if len(users) < 1 {
user, err := core.L(config.Retry.Times) user, err := core.L(config.Retry.Times, "")
if err != nil { if err != nil {
core.Push("msg", "登录超时") core.Push("msg", "登录超时")
return return
@ -278,7 +281,7 @@ func do(m string) {
} }
if i == 0 { if i == 0 {
u, err := core.L(config.Retry.Times) u, err := core.L(config.Retry.Times, "")
if err != nil { if err != nil {
log.Errorln(err.Error()) log.Errorln(err.Error())
return return

View File

@ -19,6 +19,7 @@ func init() {
log.Errorln("用户数据库打开失败请检查config目录权限") log.Errorln("用户数据库打开失败请检查config目录权限")
log.Panicln(err.Error()) log.Panicln(err.Error())
} }
_, _ = db.Exec(`create table user _, _ = db.Exec(`create table user
( (
nick TEXT, nick TEXT,

View File

@ -4,6 +4,7 @@ package model
import ( import (
"database/sql" "database/sql"
"fmt"
"math/rand" "math/rand"
"net/http" "net/http"
"time" "time"
@ -14,6 +15,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
"github.com/huoxue1/study_xxqg/conf"
"github.com/huoxue1/study_xxqg/push" "github.com/huoxue1/study_xxqg/push"
) )
@ -21,6 +23,14 @@ func init() {
go check() go check()
} }
var (
wechatPush func(id, message string)
)
func SetPush(push func(id, message string)) {
wechatPush = push
}
// User // User
/** /**
* @Description: * @Description:
@ -42,7 +52,7 @@ type User struct {
func Query() ([]*User, error) { func Query() ([]*User, error) {
var users []*User var users []*User
ping() ping()
var results, err = db.Query("select * from user") results, err := db.Query("select * from user")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -58,17 +68,50 @@ func Query() ([]*User, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// login := time.Unix(u.LoginTime, 0)
// sub := time.Now().Sub(login)
if CheckUserCookie(u) { if CheckUserCookie(u) {
// if lib.GetConfig().ForceExpiration != 0 && sub.Hours() > float64(lib.GetConfig().ForceExpiration) {
// log.Infoln("用户" + u.Nick + "cookie已强制失效")
// continue
// }
users = append(users, u) users = append(users, u)
} else { } else {
log.Infoln("用户" + u.Nick + "cookie已失效") 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) _ = DeleteUser(u.UID)
} }
} }

242
web/wx.go
View File

@ -1,42 +1,268 @@
package web package web
import ( import (
"encoding/json"
"fmt"
"net/http" "net/http"
"strings"
"sync"
"time"
"github.com/johlanse/wechat/mp"
"github.com/johlanse/wechat/mp/request"
log "github.com/sirupsen/logrus" 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/conf"
"github.com/huoxue1/study_xxqg/lib"
"github.com/huoxue1/study_xxqg/model"
) )
func init() { func init() {
//InitWechat() initWechat()
} }
var ( 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() config := conf.GetConfig()
if !config.Wechat.Enable {
return
}
log.Infoln(config.Wechat) log.Infoln(config.Wechat)
wx = mp.New(config.Wechat.Token, config.Wechat.AppID, config.Wechat.Secret, "123", "123") 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: "登录", Name: "登录",
Type: "click", Type: "click",
Key: "login", Key: login,
Url: "", Url: "",
MediaId: "", MediaId: "",
SubButtons: nil, 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) { 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) { func HandleWechat(rep http.ResponseWriter, req *http.Request) {
wx.ServeHTTP(rep, req) 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))
}
}