commit 28d0a19069c8c2726085846a7cfe2176b21fd6a6 Author: johlanse Date: Fri Nov 12 15:46:33 2021 +0800 init code diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..cb76721 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: CI + +on: [push, pull_request] + +env: + BINARY_PREFIX: "study_xxqg_" + BINARY_SUFFIX: "" + PR_PROMPT: "::warning:: Build artifact will not be uploaded due to the workflow is trigged by pull request." + LD_FLAGS: "-w -s" + +jobs: + build: + name: Build binary CI + runs-on: ubuntu-latest + strategy: + matrix: + # build and publish in parallel: linux/386, linux/amd64, windows/386, windows/amd64, darwin/amd64, darwin/arm64 + goos: [linux, windows] + goarch: [amd64] + exclude: + - goos: darwin + goarch: arm + - goos: darwin + goarch: "386" + - goos: windows + goarch: arm64 + fail-fast: true + steps: + - uses: actions/checkout@v2 + - name: Setup Go environment + uses: actions/setup-go@v2.1.3 + with: + go-version: 1.17 + - name: Build binary file + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + IS_PR: ${{ !!github.head_ref }} + run: | + if [ $GOOS = "windows" ]; then export BINARY_SUFFIX="$BINARY_SUFFIX.exe"; fi + if $IS_PR ; then echo $PR_PROMPT; fi + export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX" + export CGO_ENABLED=0 + go build -o "output/$BINARY_NAME" -ldflags "-w -s -X main.VERSION=action" + - name: Upload artifact + uses: actions/upload-artifact@v2 + if: ${{ !github.head_ref }} + with: + name: ${{ matrix.goos }}_${{ matrix.goarch }} + path: output/ + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ecc81d4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,41 @@ +name: release + +on: + push: + tags: + - 'v*' + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: '1.17.2' + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + version: latest + args: release --rm-dist + env: +# main: ./example/main.go + GITHUB_TOKEN: ${{ secrets.TOKEN }} + #- name: Checkout Dist + # uses: actions/checkout@v2 + # with: + # repository: 'gocq/dist' + # ref: master + # ssh-key: ${{ secrets.SSH_KEY }} + # path: upstream/dist + + #- name: Update Dist + # run: | + # chmod +x scripts/upload_dist.sh + # ./scripts/upload_dist.sh diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..1e62893 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,94 @@ +linters-settings: + errcheck: + ignore: fmt:.*,io/ioutil:^Read.* + ignoretests: true + + goimports: + local-prefixes: github.com/huoxue1/study_xxqg + + gocritic: + disabled-checks: + - exitAfterDefer + + forbidigo: + # Forbid the following identifiers + forbid: + - ^fmt\.Errorf$ # consider errors.Errorf in github.com/pkg/errors + +linters: + # please, do not use `enable-all`: it's deprecated and will be removed soon. + # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint + fast: true + enable: + - bodyclose + - deadcode + - depguard + - dogsled + - dupl + - errcheck + - exportloopref + - exhaustive + #- funlen + #- goconst + - gocritic + #- gocyclo + - gofmt + - goimports + - goprintffuncname + #- gosec + - gosimple + - govet + - ineffassign + - misspell + - nolintlint + - rowserrcheck + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - varcheck + - whitespace + - prealloc + - predeclared + - asciicheck + - forbidigo + - makezero + - revive + #- interfacer + + # don't enable: + # - scopelint + # - gochecknoglobals + # - gocognit + # - godot + # - godox + # - goerr113 + # - interfacer + # - maligned + # - nestif + # - testpackage + # - wsl + +run: + # default concurrency is a available CPU number. + # concurrency: 4 # explicitly omit this value to fully utilize available resources. + deadline: 5m + issues-exit-code: 1 + tests: false + +# output configuration options +output: + format: "colored-line-number" + print-issued-lines: true + print-linter-name: true + uniq-by-line: true + +issues: + # Fix found issues (if it's supported by the linter) + fix: true + exclude-use-default: false + exclude: + - "Error return value of .((os.)?std(out|err)..*|.*Close|.*Flush|os.Remove(All)?|.*print(f|ln)?|os.(Un)?Setenv). is not check" diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..d7a59b2 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,91 @@ +env: + - GO111MODULE=on +before: + hooks: + - go mod tidy +builds: + + - id: nowin + env: + - CGO_ENABLED=0 + - GO111MODULE=on + goos: + - linux +# - darwin + goarch: + - 386 + - amd64 +# - arm +# - arm64 +# goarm: +# - 7 +# ignore: +# - goos: darwin +# goarch: arm +# - goos: darwin +# goarch: 386 + mod_timestamp: "{{ .CommitTimestamp }}" + flags: + - -trimpath + ldflags: + - -s -w -X main.VERSION=v{{.Version}} + - id: win + env: + - CGO_ENABLED=0 + - GO111MODULE=on + goos: + - windows + goarch: + - 386 + - amd64 +# - arm + goarm: + - 7 + mod_timestamp: "{{ .CommitTimestamp }}" + flags: + - -trimpath + ldflags: + - -s -w -X main.VERSION=v{{.Version}} + +checksum: + name_template: "{{ .ProjectName }}_checksums.txt" +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" + - fix typo + - Merge pull request + - Merge branch + - Merge remote-tracking + - go mod tidy + +archives: + - id: binary + builds: + - win + - nowin + name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" + format_overrides: + - goos: windows + format: binary + - goos: linux + format: binary + - id: nowin + builds: + - nowin + - win + name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" + format_overrides: + - goos: windows + format: zip + +nfpms: + - license: AGPL 3.0 + homepage: https://github.com/huoxue1/study_xxqg + file_name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" + formats: + - deb + - rpm + maintainer: HuoXue1 \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..8c314c7 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +study_xxqg \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..9864b18 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/study_xxqg.iml b/.idea/study_xxqg.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/study_xxqg.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..712837a --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +### 学习强国自动化学习 + + + +## 使用 + ++ 从release下载对应版本压缩包 ++ 首次打开会在 ```config\config.yml```生成默认配置文件 ++ 再次打开即可运行 + + +## 申明,该项目仅用于学习。 + +## 鸣谢 + ++ ### [imkenf/XueQG](https://github.com/imkenf/XueQG) \ No newline at end of file diff --git a/config/config.yml b/config/config.yml new file mode 100644 index 0000000..7b35cd7 --- /dev/null +++ b/config/config.yml @@ -0,0 +1,5 @@ +# 刷课模式,默认为1, +# 1:只刷文章何视频 +# 2:只刷文章和视频和每日答题 +# 3:刷文章和视频和每日答题每周答题和专项答题 + model: 1 \ No newline at end of file diff --git a/config/user.json b/config/user.json new file mode 100644 index 0000000..ae74fe4 --- /dev/null +++ b/config/user.json @@ -0,0 +1 @@ +[{"cookies":[{"name":"_uab_collina","value":"163662103329486079242507","domain":"pc.xuexi.cn","path":"/points","expires":1951981033,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"__UID__","value":"5cdc19a0-42cd-11ec-b6bc-e3c5195d165e","domain":".xuexi.cn","path":"/","expires":1668157033,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"tmzw","value":"1636621034119","domain":".xuexi.cn","path":"/","expires":1636707434,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"zwfigprt","value":"297bce38be89bc8a076e6e29b6282c1c","domain":".xuexi.cn","path":"/","expires":1636707434,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"aliyungf_tc","value":"003c420ac17482ba6ad5550439dfb427add3b6076673bc476f0beeb76d7abdf6","domain":"pc-api.xuexi.cn","path":"/","expires":-1,"httpOnly":true,"secure":false,"same_site":"None"},{"name":"acw_tc","value":"2f6fc10416366210318943764e7c9b024dcd55ba787fb0c5064287171f5d3d","domain":"pc-api.xuexi.cn","path":"/","expires":1636622834,"httpOnly":true,"secure":false,"same_site":"None"},{"name":"aliyungf_tc","value":"171ba83785f1d8fb9d76af9bbb376840828ef4710300b5854145bf5e7fec3c1f","domain":"login.xuexi.cn","path":"/","expires":-1,"httpOnly":true,"secure":false,"same_site":"None"},{"name":"acw_tc","value":"2f6fc10416366210325768127e7c9adce9f228d7e2f62ece3cd09618ee90d6","domain":"login.xuexi.cn","path":"/","expires":1636622835,"httpOnly":true,"secure":false,"same_site":"None"},{"name":"_bl_uid","value":"nhkb8v0kuyzpFzw6Xptyftdj3b03","domain":"login.xuexi.cn","path":"/","expires":1652173035,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"_bl_uid","value":"bhkFmvUOuw7p1swt3pe4kCdf22g3","domain":"www.xuexi.cn","path":"/","expires":1652173036,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"_bl_uid","value":"94k6yvb2u6apejwFCpekkm5u6e79","domain":"pc.xuexi.cn","path":"/","expires":1652173036,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"token","value":"143ed9a00afe4c1fbcf1eae8acfdeca3","domain":".xuexi.cn","path":"/","expires":1636642691,"httpOnly":false,"secure":false,"same_site":"None"}],"nick":"苟江山","uid":"151142372517"}] \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..807bced --- /dev/null +++ b/go.mod @@ -0,0 +1,41 @@ +module github.com/huoxue1/study_xxqg + +go 1.17 + +replace github.com/willf/bitset v1.2.1 => github.com/bits-and-blooms/bitset v1.2.1 + +require ( + github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f + github.com/guonaihong/gout v0.2.9 + github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible + github.com/mxschmitt/playwright-go v0.1100.0 + github.com/sirupsen/logrus v1.8.1 + github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 + github.com/tidwall/gjson v1.11.0 + github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2 + gopkg.in/yaml.v2 v2.2.8 +) + +require ( + github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect + github.com/go-playground/locales v0.13.0 // indirect + github.com/go-playground/universal-translator v0.17.0 // indirect + github.com/go-playground/validator/v10 v10.4.1 // indirect + github.com/google/uuid v1.1.1 // indirect + github.com/jonboulle/clockwork v0.2.2 // indirect + github.com/leodido/go-urn v1.2.0 // indirect + github.com/lestrrat-go/strftime v1.0.5 // indirect + github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4 // indirect + github.com/mattn/go-colorable v0.1.11 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/willf/bitset v1.2.1 // indirect + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect + golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect + google.golang.org/protobuf v1.26.0 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7f0ad02 --- /dev/null +++ b/go.sum @@ -0,0 +1,123 @@ +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/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= +github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= +github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.7.0 h1:jGB9xAJQ12AIGNB4HguylppmDK1Am9ppF7XnGXXJuoU= +github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/guonaihong/gout v0.2.9 h1:8nU5hrtwP1qDwiadFvU+D+z3ud9WEk8iPSfxQDiebng= +github.com/guonaihong/gout v0.2.9/go.mod h1:H1JqEuZmK4h/urWUq/LnIOEzS1kxl5rK3NkFqZ6Rn48= +github.com/h2non/filetype v1.1.0/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= +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 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= +github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= +github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= +github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE= +github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= +github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4 h1:u9jwvcKbQpghIXgNl/EOL8hzhAFXh4ePrEP493W3tNA= +github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4/go.mod h1:kcRFpEzolcEklV6rD7W95mG49/sbdX/PlFmd7ni3RvA= +github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mxschmitt/playwright-go v0.1100.0 h1:GkI1TuXU50GlA988VqqdoTObLzi2bbeT8RmLtcxKQrc= +github.com/mxschmitt/playwright-go v0.1100.0/go.mod h1:a3SD3v+56XMA0sDDxXJXy+QGnCfXrNZ/+4gwR5ioSgU= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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/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= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 h1:J6v8awz+me+xeb/cUTotKgceAYouhIB3pjzgRd6IlGk= +github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816/go.mod h1:tzym/CEb5jnFI+Q0k4Qq3+LvRF4gO3E2pxS8fHP8jcA= +github.com/tidwall/gjson v1.11.0 h1:C16pk7tQNiH6VlCrtIXL1w8GaOsi1X3W8KDkE1BuYd4= +github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2 h1:BWVtt2VBY+lmVDu9MGKqLGKl04B+iRHcrW1Ptyi/8tg= +github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2/go.mod h1:lPnW9HVS0vJdeYyQtOvIvlXgZPNhUAhwz+z5r8AJk0Y= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/lib/config.go b/lib/config.go new file mode 100644 index 0000000..dd01a9b --- /dev/null +++ b/lib/config.go @@ -0,0 +1,44 @@ +package lib + +import ( + _ "embed" + "os" + + log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v2" +) + +type Config struct { + Model int `json:"model" yaml:"model"` + LogLevel string `json:"log_level" yaml:"log_level"` + ShowBrowser bool `json:"show_browser" yaml:"show_browser"` +} + +var ( + config = Config{ + Model: 1, + } +) + +//go:embed config_default.yml +var defaultConfig []byte + +func GetConfig() Config { + file, err := os.ReadFile("./config/config.yml") + if err != nil { + log.Warningln("检测到配置文件可能不存在") + err := os.WriteFile("./config/config.yml", defaultConfig, 0666) + if err != nil { + log.Errorln("写入到配置文件出现错误") + return Config{} + } + log.Infoln("成功写入到配置文件,请重启应用") + os.Exit(3) + } + err = yaml.Unmarshal(file, &config) + if err != nil { + log.Errorln(err.Error()) + return Config{} + } + return config +} diff --git a/lib/config_default.yml b/lib/config_default.yml new file mode 100644 index 0000000..df07a4b --- /dev/null +++ b/lib/config_default.yml @@ -0,0 +1,18 @@ +# 刷课模式,默认为1, +# 1:只刷文章何视频 +# 2:只刷文章和视频和每日答题 +# 3:刷文章和视频和每日答题每周答题和专项答题 +model: 1 + +# 日志等级 +# panic +# fatal +# error +# warn, warning +# info +# debug +# trace +log_level: "info" + +# 是否显示浏览器 +show_browser: true \ No newline at end of file diff --git a/lib/core.go b/lib/core.go new file mode 100644 index 0000000..4b0bd19 --- /dev/null +++ b/lib/core.go @@ -0,0 +1,227 @@ +package lib + +import ( + "bytes" + "encoding/base64" + "fmt" + "os" + "strings" + + qrcodeTerminal "github.com/Baozisoftware/qrcode-terminal-go" + "github.com/guonaihong/gout" + "github.com/mxschmitt/playwright-go" + log "github.com/sirupsen/logrus" + "github.com/tuotoo/qrcode" +) + +type Core struct { + pw *playwright.Playwright + browser playwright.Browser + context playwright.BrowserContext +} + +type cookie struct { + Name string `json:"name" yaml:"name"` + Value string `json:"value" yaml:"value"` + Domain string `json:"domain" yaml:"domain"` + Path string `json:"path" yaml:"path"` + Expires int `json:"expires" yaml:"expires"` + HTTPOnly bool `json:"httpOnly" yaml:"http_only"` + Secure bool `json:"secure" yaml:"secure"` + SameSite string `json:"same_site" yaml:"same_site"` +} + +func (c *Core) Init() { + pwt, err := playwright.Run() + if err != nil { + log.Errorln("[core]", "初始化playwright失败") + log.Errorln("[core] ", err.Error()) + + return + } + c.pw = pwt + browser, err := pwt.Chromium.Launch(playwright.BrowserTypeLaunchOptions{ + Args: []string{ + "--disable-extensions", + "--disable-gpu", + "--no-sandbox", + "--window-size=540,400", + "--start-maximized", + "--mute-audio", + "--window-position=0,0", + "--ignore-certificate-errors", + "--ignore-ssl-errors", + "--disable-features=RendererCodeIntegrity", + "--disable-blink-features", + "--disable-blink-features=AutomationControlled", + }, + Channel: nil, + ChromiumSandbox: nil, + Devtools: nil, + DownloadsPath: nil, + ExecutablePath: nil, + HandleSIGHUP: nil, + HandleSIGINT: nil, + HandleSIGTERM: nil, + Headless: playwright.Bool(false), + Proxy: nil, + SlowMo: nil, + Timeout: nil, + }) + if err != nil { + log.Errorln("[core] ", "初始化chrome失败") + log.Errorln("[core] ", err.Error()) + return + } + c.browser = browser + context, err := c.browser.NewContext() + if err != nil { + return + } + c.context = context +} + +func (c *Core) Quit() { + err := c.context.Close() + if err != nil { + return + } + err = c.browser.Close() + if err != nil { + return + } + err = c.pw.Stop() + if err != nil { + return + } +} + +func (c *Core) Login() ([]cookie, error) { + page, err := c.context.NewPage() + + if err != nil { + return nil, err + } + _, err = page.Goto("https://pc.xuexi.cn/points/login.html", playwright.PageGotoOptions{ + Referer: nil, + Timeout: playwright.Float(30000), + WaitUntil: playwright.WaitUntilStateDomcontentloaded, + }) + if err != nil { + log.Errorln("[core] ", "打开登录页面失败") + log.Errorln("[core] ", err.Error()) + + return nil, err + } + log.Infoln("[core] ", "正在等待二维码扫描") + + _, _ = page.WaitForSelector(`#app > div > div.login_content > div > div.login_qrcode `) + + _, err = page.Evaluate(`let h = document.body.scrollWidth/2;document.documentElement.scrollTop=h;`) + + if err != nil { + fmt.Println(err.Error()) + + return nil, err + } + + log.Infoln("[core] ", "加载验证码中,请耐心等待") + + frame := page.Frame(playwright.PageFrameOptions{ + Name: playwright.String(`ddlogin-iframe`), + URL: nil, + }) + if frame == nil { + log.Errorln("获取frame失败") + } + + selector, err := frame.QuerySelector(`img`) + + if err != nil { + log.Errorln(err.Error()) + + return nil, err + } + + img, err := selector.GetAttribute(`src`) + if err != nil { + log.Errorln(err.Error()) + + return nil, err + } + img = strings.ReplaceAll(img, "data:image/png;base64,", "") + go sendToQQ(img) + data, err := base64.StdEncoding.DecodeString(img) + if err != nil { + return nil, err + } + os.WriteFile("qrcode.png", data, 0666) + matrix, err := qrcode.Decode(bytes.NewReader(data)) + if err != nil { + return nil, err + } + + qrcodeTerminal.New().Get(matrix.Content).Print() + + _, err = page.WaitForNavigation(playwright.PageWaitForNavigationOptions{ + Timeout: playwright.Float(30 * 1000 * 5), + URL: nil, + WaitUntil: nil, + }) + if err != nil { + log.Errorln(err.Error()) + + return nil, err + } + cookies, err := c.context.Cookies() //nolint:wsl + if err != nil { + log.Errorln("[core] ", "获取cookie失败") + return nil, err + } + + var ( + cos []cookie + ) + + for _, c := range cookies { + co := cookie{} + co.Name = c.Name + co.Path = c.Path + co.Value = c.Value + co.Domain = c.Domain + co.Expires = c.Expires + co.HTTPOnly = c.HttpOnly + co.SameSite = c.SameSite + co.Secure = c.Secure + cos = append(cos, co) + } + info, nick, err := GetUserInfo(cos) + if err != nil { + return nil, err + } + err = SaveUser(User{ + Cookies: cos, + Nick: nick, + Uid: info, + }) + if err != nil { + return nil, err + } + + return cos, err +} + +func sendToQQ(img string) { + err := gout.POST("http://127.0.0.1:5700/send_private_msg").SetJSON(map[string]interface{}{ + "user_id": 3343780376, + "message": map[string]interface{}{ + "type": "image", + "data": map[string]interface{}{ + "file": "base64://" + img, + }, + }, + }).Do() + if err != nil { + return + } +} diff --git a/lib/core_test.go b/lib/core_test.go new file mode 100644 index 0000000..735dd30 --- /dev/null +++ b/lib/core_test.go @@ -0,0 +1,20 @@ +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) +} diff --git a/lib/respond.go b/lib/respond.go new file mode 100644 index 0000000..0e7cee9 --- /dev/null +++ b/lib/respond.go @@ -0,0 +1,489 @@ +package lib + +import ( + rand2 "math/rand" + "regexp" + "strings" + "time" + + "github.com/mxschmitt/playwright-go" + log "github.com/sirupsen/logrus" +) + +const ( + MyPointsUri = "https://pc.xuexi.cn/points/my-points.html" + + DailyBUTTON = `#app > div > div.layout-body > div > +div.my-points-section > div.my-points-content > div:nth-child(5) > div.my-points-card-footer > div.buttonbox > div` + WEEKEND = `#app > div > div.layout-body > +div > div.my-points-section > div.my-points-content > div:nth-child(6) > div.my-points-card-footer > div.buttonbox > div` + SPECIALBUTTON = `#app > div > div.layout-body > +div > div.my-points-section > div.my-points-content > div:nth-child(7) > div.my-points-card-footer > div.buttonbox > div` +) + +func (c *Core) RespondDaily(cookies []cookie, model string) { + defer func() { + err := recover() + if err != nil { + log.Errorln("答题模块异常结束") + } + }() + + // 获取用户成绩 + score, err := GetUserScore(cookies) + if err != nil { + log.Errorln("获取分数失败,停止每日答题", err.Error()) + + return + } + + page, err := c.context.NewPage() + if err != nil { + log.Errorln("创建页面失败" + err.Error()) + + return + } + err = c.context.AddCookies(cookieToParam(cookies)...) + if err != nil { + log.Errorln("添加cookie信息失败,已退出答题") + + return + } + log.Infoln("已加载每日答题模块") + + _, err = page.Goto(MyPointsUri, playwright.PageGotoOptions{ + Referer: playwright.String(MyPointsUri), + Timeout: playwright.Float(1000), + WaitUntil: playwright.WaitUntilStateDomcontentloaded, + }) + if err != nil { + log.Errorln("跳转页面失败") + + return + } + switch model { + case "daily": + { + // 检测是否已经完成 + if score.Content["daily"].CurrentScore >= score.Content["daily"].MaxScore { + log.Infoln("检测到每日答题已经完成,即将退出答题") + + return + } + err = page.Click(DailyBUTTON) + if err != nil { + log.Errorln("跳转到积分页面错误") + + return + } + } + case "weekly": + { + // 检测是否已经完成 + if score.Content["weekly"].CurrentScore >= score.Content["weekly"].MaxScore { + log.Infoln("检测到每周答题已经完成,即将退出答题") + + return + } + err = page.Click(WEEKEND) + if err != nil { + log.Errorln("跳转到积分页面错误") + + return + } + } + case "special": + { + // 检测是否已经完成 + if score.Content["special"].CurrentScore >= score.Content["special"].MaxScore { + log.Infoln("检测到特殊答题已经完成,即将退出答题") + + return + } + err = page.Click(SPECIALBUTTON) + if err != nil { + log.Errorln("跳转到积分页面错误") + + return + } + } + } + time.Sleep(5 * time.Second) + getAnswerPage(page, model) + + for true { + // 查看是否存在答题按钮,若按钮可用则重新提交答题 + btn, err := page.QuerySelector(`#app > div > div.layout-body > div > div.detail-body > div.action-row > button`) + if err != nil { + log.Infoln("获取提交按钮失败,本次答题结束" + err.Error()) + return + } + + enabled, err := btn.IsEnabled() + if err != nil { + log.Errorln(err.Error()) + continue + } + if enabled { + log.Infoln("检测到有答案未提交,将重新提交") + err := btn.Click() + if err != nil { + log.Errorln("提交答案失败") + } + } + switch model { + case "daily": + { + // 检测是否已经完成 + if score.Content["daily"].CurrentScore >= score.Content["daily"].MaxScore { + log.Infoln("检测到每日答题已经完成,即将退出答题") + + return + } + } + case "weekly": + { + // 检测是否已经完成 + if score.Content["weekly"].CurrentScore >= score.Content["weekly"].MaxScore { + log.Infoln("检测到每周答题已经完成,即将退出答题") + + return + } + } + case "special": + { + // 检测是否已经完成 + if score.Content["special"].CurrentScore >= score.Content["special"].MaxScore { + log.Infoln("检测到特殊答题已经完成,即将退出答题") + + return + } + } + } + + // 获取题目类型 + category, err := page.QuerySelector( + `#app > div > div.layout-body > div > div.detail-body > div.question > div.q-header`) + if err != nil { + log.Errorln("没有找到题目元素" + err.Error()) + + return + } + time.Sleep(1 * time.Second) + + // 获取题目 + question, err := page.QuerySelector( + `#app > div > div.layout-body > div > div.detail-body > div.question > div.q-body > div`) + if err != nil { + log.Errorln("未找到题目问题元素") + + return + } + + categoryText, err := category.TextContent() + if err != nil { + log.Errorln("获取题目元素失败" + err.Error()) + + return + } + log.Infoln("## 题目类型:" + categoryText) + + questionText, err := question.TextContent() + if err != nil { + log.Errorln("获取题目问题失败" + err.Error()) + return + } + log.Infoln("## 题目:" + questionText) + + // 获取答题帮助 + openTips, err := page.QuerySelector( + `#app > div > div.layout-body > div > div.detail-body > div.question > div.q-footer > span`) + if err != nil { + log.Errorln("为获取到题目提示信息" + err.Error()) + + return + } + err = openTips.Click() + if err != nil { + log.Errorln("点击打开提示信息按钮失败" + err.Error()) + + return + } + + content, err := page.Content() + if err != nil { + log.Errorln("获取网页全体内容失败" + err.Error()) + + return + } + err = openTips.Click() + if err != nil { + log.Errorln("点击打开提示信息按钮失败" + err.Error()) + + return + } + + tips := getTips(content) + log.Infoln("[提示信息]:", tips) + // 填空题 + switch { + case strings.Contains(categoryText, "填空题"): + err := FillBlank(page, tips) + if err != nil { + log.Errorln("填空题答题失败" + err.Error()) + + return + } + case strings.Contains(categoryText, "多选题"): + log.Infoln("读取到多选题") + options, err := getOptions(page) + if err != nil { + log.Errorln("获取选项失败" + err.Error()) + + return + } + log.Infoln("获取到选项答案:", options) + log.Infoln("[多选题选项]:", options) + var answer []string + for _, option := range options { + for _, tip := range tips { + if strings.Contains(option, tip) { + answer = append(answer, option) + } + } + } + err = radioCheck(page, answer) + if err != nil { + return + } + case strings.Contains(categoryText, "单选题"): + log.Infoln("读取到单选题") + options, err := getOptions(page) + if err != nil { + log.Errorln("获取选项失败" + err.Error()) + return + } + log.Infoln("获取到选项答案:", options) + log.Infoln("[多选题选项]:", options) + var answer []string + for _, option := range options { + for _, tip := range tips { + if strings.Contains(option, tip) { + answer = append(answer, option) + } + } + } + err = radioCheck(page, answer) + if err != nil { + return + } + } + score, _ = GetUserScore(cookies) + } +} + +func getAnswerPage(page playwright.Page, model string) { + selectPages, err := page.QuerySelectorAll(`#app .ant-pagination .ant-pagination-item`) + if err != nil { + log.Errorln("获取到页码失败") + + return + } + log.Infoln("共获取到", len(selectPages), "页") + modelName := "" + modelSlector := "" + switch model { + case "daily": + return + case "weekly": + modelName = "每周答题" + modelSlector = "button.ant-btn-primary" + case "special": + modelName = "专项答题" + modelSlector = "#app .items .item button" + } + for i := 1; i <= len(selectPages); i++ { + log.Infoln("获取到"+modelName, "第", i, "页") + err1 := selectPages[i-1].Click() + if err1 != nil { + log.Errorln("点击页码失败") + } + datas, err := page.QuerySelectorAll(modelSlector) + if err != nil { + log.Errorln("获取页面内容失败") + continue + } + for _, data := range datas { + time.Sleep(3 * time.Second) + content, err := data.TextContent() + if err != nil { + continue + } + if strings.Contains(content, "重新") || strings.Contains(content, "满分") { + continue + } else { + if strings.Contains(content, "电影试题") { + log.Infoln("发现有未答题的电影试题") + continue + } + enabled, err := data.IsEnabled() + if err != nil { + return + } + if enabled { + log.Infoln("按钮可用") + } + err = data.Click(playwright.ElementHandleClickOptions{ + Button: nil, + ClickCount: playwright.Int(2), + Delay: nil, + Force: nil, + Modifiers: nil, + NoWaitAfter: nil, + Position: nil, + Timeout: playwright.Float(100000), + }) + if err != nil { + log.Errorln("点击按钮失败" + err.Error()) + time.Sleep(2 * time.Second) + continue + } + time.Sleep(3 * time.Second) + return + } + } + } +} + +func radioCheck(page playwright.Page, answer []string) error { + radios, err := page.QuerySelectorAll(`.q-answer.choosable`) + if err != nil { + log.Errorln("获取选项失败") + + return err + } + log.Debugln("获取到", len(radios), "个按钮") + for _, radio := range radios { + textContent, err := radio.TextContent() + if err != nil { + return err + } + for _, s := range answer { + if textContent == s { + err := radio.Click() + if err != nil { + return err + } + r := rand2.Intn(2) + time.Sleep(time.Duration(r) * time.Second) + } + } + } + r := rand2.Intn(5) + time.Sleep(time.Duration(r) * time.Second) + checkNextBotton(page) + return nil +} + +func getOptions(page playwright.Page) ([]string, error) { + handles, err := page.QuerySelectorAll(`.q-answer.choosable`) + if err != nil { + log.Errorln("获取选项信息失败") + return nil, err + } + var options []string + for _, handle := range handles { + content, err := handle.TextContent() + if err != nil { + return nil, err + } + options = append(options, content) + } + return options, err +} + +func getTips(data string) []string { + data = strings.ReplaceAll(data, " ", "") + data = strings.ReplaceAll(data, "\n", "") + compile := regexp.MustCompile(`(.*?)`) + match := compile.FindAllStringSubmatch(data, -1) + var tips []string + for _, i := range match { + tips = append(tips, i[1]) + } + return tips +} + +func FillBlank(page playwright.Page, tips []string) error { + video := false + var answer []string + for _, tip := range tips { + if tip == "请观看视频" { + video = true + } + } + if video { + answer = append(answer, "不知道") + } else { + answer = tips + } + inouts, err := page.QuerySelectorAll(`div.q-body > div > input`) + if err != nil { + log.Errorln("获取输入框错误" + err.Error()) + return err + } + log.Debugln("获取到", len(inouts), "个填空") + if len(inouts) == 1 && len(tips) > 1 { + temp := "" + for _, tip := range tips { + temp += tip + } + answer = strings.Split(temp, ",") + log.Infoln("答案已合并处理" + err.Error()) + } + for i := 0; i < len(inouts); i++ { + err := inouts[i].Fill(answer[i]) + if err != nil { + log.Errorln("填充答案失败" + err.Error()) + continue + } + r := rand2.Intn(5) + time.Sleep(time.Duration(r) * time.Second) + } + r := rand2.Intn(2) + time.Sleep(time.Duration(r) * time.Second) + checkNextBotton(page) + return nil +} + +func checkNextBotton(page playwright.Page) { + btns, err := page.QuerySelectorAll(`#app .action-row > button`) + if err != nil { + log.Errorln("未检测到按钮" + err.Error()) + + return + } + if len(btns) <= 1 { + err := btns[0].Check() + if err != nil { + log.Errorln("点击吓一题按钮失败") + + return + } + time.Sleep(2 * time.Second) + _, err = btns[0].GetAttribute("disabled") + if err != nil { + log.Infoln("未检测到禁言属性") + + return + } + } else { + err := btns[1].Click() + if err != nil { + log.Errorln("提交试卷失败") + + return + } + log.Infoln("已成功提交试卷") + } +} diff --git a/lib/score.go b/lib/score.go new file mode 100644 index 0000000..f7c5163 --- /dev/null +++ b/lib/score.go @@ -0,0 +1,98 @@ +package lib + +import ( + "fmt" + + "github.com/guonaihong/gout" + log "github.com/sirupsen/logrus" + "github.com/tidwall/gjson" +) + +type Score struct { + TotalScore int + TodayScore int + Content map[string]Data +} + +type Data struct { + CurrentScore int + MaxScore int +} + +func GetUserScore(cookies []cookie) (Score, error) { + var score Score + var resp []byte + // 获取用户总分 + err := gout.GET(user_totalScore_url).SetCookies(cookieToJar(cookies)...).SetHeader(gout.H{ + "Cache-Control": "no-cache", + }).BindBody(&resp).Do() + if err != nil { + log.Errorln("获取用户总分错误" + err.Error()) + + return Score{}, err + } + log.Debugln(gjson.GetBytes(resp, "@this|@pretty")) + score.TotalScore = int(gjson.GetBytes(resp, "data.score").Int()) + + // 获取用户今日得分 + err = gout.GET(user_todayTotalScore_url).SetCookies(cookieToJar(cookies)...).SetHeader(gout.H{ + "Cache-Control": "no-cache", + }).BindBody(&resp).Do() + if err != nil { + log.Errorln("获取用户每日总分错误" + err.Error()) + + return Score{}, err + } + log.Debugln(gjson.GetBytes(resp, "@this|@pretty")) + score.TodayScore = int(gjson.GetBytes(resp, "data.score").Int()) + + err = gout.GET(user_rateScore_url).SetCookies(cookieToJar(cookies)...).SetHeader(gout.H{ + "Cache-Control": "no-cache", + }).BindBody(&resp).Do() + if err != nil { + log.Errorln("获取用户积分出现错误" + err.Error()) + return Score{}, err + } + log.Debugln(gjson.GetBytes(resp, "@this|@pretty")) + datas := gjson.GetBytes(resp, "data.taskProgress").Array() + m := make(map[string]Data, 7) + m["article"] = Data{ + CurrentScore: int(datas[0].Get("currentScore").Int()), + MaxScore: int(datas[0].Get("dayMaxScore").Int()), + } + m["video"] = Data{ + CurrentScore: int(datas[1].Get("currentScore").Int()), + MaxScore: int(datas[1].Get("dayMaxScore").Int()), + } + m["weekly"] = Data{ + CurrentScore: int(datas[2].Get("currentScore").Int()), + MaxScore: int(datas[2].Get("dayMaxScore").Int()), + } + m["video_time"] = Data{ + CurrentScore: int(datas[3].Get("currentScore").Int()), + MaxScore: int(datas[3].Get("dayMaxScore").Int()), + } + m["login"] = Data{ + CurrentScore: int(datas[4].Get("currentScore").Int()), + MaxScore: int(datas[4].Get("dayMaxScore").Int()), + } + m["special"] = Data{ + CurrentScore: int(datas[5].Get("currentScore").Int()), + MaxScore: int(datas[5].Get("dayMaxScore").Int()), + } + m["daily"] = Data{ + CurrentScore: int(datas[6].Get("currentScore").Int()), + MaxScore: int(datas[6].Get("dayMaxScore").Int()), + } + + score.Content = m + + return score, err +} + +func PrintScore(score Score) { + log.Infoln(fmt.Sprintf("当前学习总积分:%d 今日得分:%d", score.TodayScore, score.TodayScore)) + for s, data := range score.Content { + log.Infoln(s, ": ", data.CurrentScore, "/", data.MaxScore) + } +} diff --git a/lib/study.go b/lib/study.go new file mode 100644 index 0000000..0730162 --- /dev/null +++ b/lib/study.go @@ -0,0 +1,252 @@ +package lib + +import ( + "encoding/json" + "errors" + "fmt" + "math/rand" + "strconv" + "time" + + "github.com/guonaihong/gout" + "github.com/mxschmitt/playwright-go" + log "github.com/sirupsen/logrus" +) + +var ( + article_url_list = []string{ + "https://www.xuexi.cn/lgdata/35il6fpn0ohq.json", + "https://www.xuexi.cn/lgdata/45a3hac2bf1j.json", + "https://www.xuexi.cn/lgdata/1ajhkle8l72.json", + "https://www.xuexi.cn/lgdata/1ahjpjgb4n3.json", + "https://www.xuexi.cn/lgdata/1je1objnh73.json", + "https://www.xuexi.cn/lgdata/1kvrj9vvv73.json", + "https://www.xuexi.cn/lgdata/17qonfb74n3.json", + "https://www.xuexi.cn/lgdata/1i30sdhg0n3.json"} + + video_url_list = []string{ + "https://www.xuexi.cn/lgdata/3j2u3cttsii9.json", + "https://www.xuexi.cn/lgdata/1novbsbi47k.json", + "https://www.xuexi.cn/lgdata/31c9ca1tgfqb.json", + "https://www.xuexi.cn/lgdata/1oajo2vt47l.json", + "https://www.xuexi.cn/lgdata/18rkaul9h7l.json", + "https://www.xuexi.cn/lgdata/2qfjjjrprmdh.json", + "https://www.xuexi.cn/lgdata/3o3ufqgl8rsn.json", + "https://www.xuexi.cn/lgdata/525pi8vcj24p.json", + "https://www.xuexi.cn/lgdata/1742g60067k.json"} +) + +type Link struct { + Editor string `json:"editor"` + PublishTime string `json:"publishTime"` + ItemType string `json:"itemType"` + Author string `json:"author"` + CrossTime int `json:"crossTime"` + Source string `json:"source"` + NameB string `json:"nameB"` + Title string `json:"title"` + Type string `json:"type"` + Url string `json:"url"` + ShowSource string `json:"showSource"` + ItemId string `json:"itemId"` + ThumbImage string `json:"thumbImage"` + AuditTime string `json:"auditTime"` + ChannelNames []string `json:"channelNames"` + Producer string `json:"producer"` + ChannelIds []string `json:"channelIds"` + DataValid bool `json:"dataValid"` +} + +func getLinks(model string) ([]Link, error) { + UID := rand.Intn(20000000) + 10000000 + learnUrl := "" + if model == "article" { + learnUrl = article_url_list[rand.Intn(7)] + } else if model == "video" { + learnUrl = video_url_list[rand.Intn(7)] + } else { + return nil, errors.New("model选择出现错误") + } + var ( + resp []byte + ) + + err := gout.GET(learnUrl + "?_st=" + strconv.Itoa(UID)).BindBody(&resp).Do() + if err != nil { + log.Errorln("请求连接列表出现错误" + err.Error()) + return nil, err + } + + var links []Link + err = json.Unmarshal(resp, &links) + if err != nil { + log.Errorln("解析列表出现错误" + err.Error()) + return nil, err + } + return links, err +} + +func (c *Core) LearnArticle(cookies []cookie) { + defer func() { + err := recover() + if err != nil { + log.Errorln("文章学习模块异常结束") + } + }() + + score, err := GetUserScore(cookies) + if err != nil { + log.Errorln(err.Error()) + return + } + links, _ := getLinks("article") + if score.Content["article"].CurrentScore < score.Content["article"].MaxScore { + log.Infoln("开始加载文章学习模块") + page, err := c.context.NewPage() + if err != nil { + return + } + + err = c.context.AddCookies(cookieToParam(cookies)...) + if err != nil { + log.Errorln("添加cookie失败" + err.Error()) + return + } + tryCount := 0 + + for { + if tryCount < 20 { + PrintScore(score) + n := rand.Intn(len(links)) + _, err := page.Goto(links[n].Url, playwright.PageGotoOptions{ + Referer: playwright.String(links[rand.Intn(len(links))].Url), + Timeout: playwright.Float(10000), + WaitUntil: playwright.WaitUntilStateDomcontentloaded, + }) + if err != nil { + log.Errorln("页面跳转失败") + } + log.Infoln("正在学习文章:" + links[n].Title) + log.Infoln("文章发布时间:" + links[n].PublishTime) + log.Infoln("文章学习链接:" + links[n].Url) + learnTime := 50 + rand.Intn(5) + 10 + log.Infoln(fmt.Sprintf("正在进行阅读学习中,剩余%d篇,本篇剩余时间%d秒", score.Content["article"].MaxScore-score.Content["article"].CurrentScore, learnTime)) + for i := 0; i < learnTime; i++ { + if rand.Float32() > 0.5 { + go func() { + _, err = page.Evaluate(fmt.Sprintf(`let h = document.body.scrollHeight/120*%d;document.documentElement.scrollTop=h;`, i)) + if err != nil { + log.Errorln("文章滑动失败") + } + }() + } + time.Sleep(1 * time.Second) + } + + if score.Content["article"].CurrentScore >= score.Content["article"].MaxScore { + log.Infoln("检测到本次阅读学习分数已满,退出学习") + break + } + score, _ = GetUserScore(cookies) + tryCount++ + } else { + log.Errorln("阅读学习出现异常,稍后可重新学习") + } + } + } else { + log.Infoln("检测到文章学习已经完成") + } +} + +func (c *Core) LearnVideo(cookies []cookie) { + defer func() { + err := recover() + if err != nil { + log.Errorln("视频学习模块异常结束") + } + }() + score, err := GetUserScore(cookies) + if err != nil { + log.Errorln(err.Error()) + return + } + links, _ := getLinks("video") + if score.Content["video"].CurrentScore < score.Content["video"].MaxScore || score.Content["video_time"].CurrentScore < score.Content["video_time"].MaxScore { + log.Infoln("开始加载视频学习模块") + // core := Core{} + //core.Init() + + page, err := c.context.NewPage() + if err != nil { + return + } + + var resp string + err = gout.GET("http://1.15.144.22/stealth.min.js").BindBody(&resp).Do() + if err != nil { + return + } + err = page.AddInitScript(playwright.PageAddInitScriptOptions{ + Script: playwright.String(resp), + Path: nil, + }) + if err != nil { + return + } + err = c.context.AddCookies(cookieToParam(cookies)...) + if err != nil { + log.Errorln("添加cookie失败" + err.Error()) + return + } + tryCount := 0 + networkCookies, err := c.context.Cookies() + if err != nil { + return + } + for _, networkCookie := range networkCookies { + fmt.Println(networkCookie.Name) + } + + for { + if tryCount < 20 { + PrintScore(score) + n := rand.Intn(len(links)) + _, err := page.Goto(links[n].Url, playwright.PageGotoOptions{ + Referer: playwright.String(links[rand.Intn(len(links))].Url), + Timeout: playwright.Float(10000), + WaitUntil: playwright.WaitUntilStateDomcontentloaded, + }) + if err != nil { + log.Errorln("页面跳转失败") + } + log.Infoln("正在观看视频:" + links[n].Title) + log.Infoln("视频发布时间:" + links[n].PublishTime) + log.Infoln("视频学习链接:" + links[n].Url) + learnTime := 50 + rand.Intn(5) + 10 + log.Infoln(fmt.Sprintf("正在进行视频学习中,剩余%d个,当前剩余时间%d秒", score.Content["video"].MaxScore-score.Content["video"].CurrentScore, learnTime)) + for i := 0; i < learnTime; i++ { + if rand.Float32() > 0.5 { + go func() { + _, err := page.Evaluate(fmt.Sprintf(`let h = document.body.scrollHeight/120*%d;document.documentElement.scrollTop=h;`, i)) + if err != nil { + log.Errorln("视频滑动失败") + } + }() + } + time.Sleep(1 * time.Second) + } + + if score.Content["video"].CurrentScore >= score.Content["video"].MaxScore || score.Content["video_time"].CurrentScore >= score.Content["video_time"].MaxScore { + log.Infoln("检测到本次视频学习分数已满,退出学习") + break + } + score, _ = GetUserScore(cookies) + tryCount++ + } else { + log.Errorln("视频学习出现异常,稍后可重新学习") + } + } + } else { + log.Infoln("检测到视频学习已经完成") + } +} diff --git a/lib/study_test.go b/lib/study_test.go new file mode 100644 index 0000000..d71f35b --- /dev/null +++ b/lib/study_test.go @@ -0,0 +1,9 @@ +package lib + +import ( + "testing" +) + +func TestName1(t *testing.T) { + CheckUserCookie(User{}) +} diff --git a/lib/url.go b/lib/url.go new file mode 100644 index 0000000..077225c --- /dev/null +++ b/lib/url.go @@ -0,0 +1,8 @@ +package lib + +const ( + user_Info_url = "https://pc-api.xuexi.cn/open/api/user/info" + user_totalScore_url = "https://pc-api.xuexi.cn/open/api/score/get" + user_todayTotalScore_url = "https://pc-api.xuexi.cn/open/api/score/today/query" + user_rateScore_url = "https://pc-proxy-api.xuexi.cn/api/score/days/listScoreProgress?sence=score&deviceType=2" +) diff --git a/lib/user.go b/lib/user.go new file mode 100644 index 0000000..5cf25b8 --- /dev/null +++ b/lib/user.go @@ -0,0 +1,117 @@ +package lib + +import ( + "encoding/json" + "os" + + "github.com/guonaihong/gout" + log "github.com/sirupsen/logrus" + "github.com/tidwall/gjson" +) + +func init() { + _, err := os.Stat(`./config/user.json`) + if err != nil { + err := os.WriteFile(user_path, []byte("[]"), 0666) + if err != nil { + return + } + return + } +} + +const ( + user_path = "./config/user.json" +) + +type User struct { + Cookies []cookie `json:"cookies"` + Nick string `json:"nick"` + Uid string `json:"uid"` +} + +func GetUsers() ([]User, error) { + file, err := os.ReadFile(user_path) + if err != nil { + return nil, err + } + var users []User + err = json.Unmarshal(file, &users) + if err != nil { + return nil, err + } + var newUsers []User + for i := 0; i < len(users); i++ { + if CheckUserCookie(users[i]) { + newUsers = append(newUsers, users[i]) + continue + } + log.Infoln("用户" + users[i].Nick + "cookie已失效") + } + return users, err +} + +func SaveUser(user User) error { + users, err := GetUsers() + if err != nil { + log.Errorln("获取用户信息错误") + return err + } + a := false + for _, u := range users { + if u.Uid == user.Uid { + u.Cookies = user.Cookies + a = true + } + } + if !a { + users = append(users, user) + } + + data, err := json.Marshal(&users) + if err != nil { + log.Errorln("序列化用户失败") + return err + } + err = os.WriteFile(user_path, data, 0666) + if err != nil { + log.Errorln("写入用户信息到文件错误") + + return err + } + return err +} + +func GetUserInfo(cookies []cookie) (string, string, error) { + var resp []byte + err := gout.GET(user_Info_url). + SetCookies(cookieToJar(cookies)...). + SetHeader(gout.H{ + "Cache-Control": "no-cache", + }).BindBody(&resp).Do() + if err != nil { + log.Errorln("获取用户信息失败") + + return "", "", err + } + log.Debugln("[user] 用户信息:", gjson.GetBytes(resp, "@this|@pretty").String()) + uid := gjson.GetBytes(resp, "data.uid").String() + nick := gjson.GetBytes(resp, "data.nick").String() + + return uid, nick, err +} + +func CheckUserCookie(user User) bool { + var resp []byte + err := gout.GET(`https://pc-api.xuexi.cn/open/api/score/get?_t=1636607911602`).SetCookies(cookieToJar(user.Cookies)...).BindBody(&resp).Do() + if err != nil { + log.Errorln(err.Error()) + + return true + } + if gjson.GetBytes(resp, "code").Int() == 401 && gjson.GetBytes(resp, "message").String() == "token check failed" { + return true + } + + return false +} diff --git a/lib/user_test.go b/lib/user_test.go new file mode 100644 index 0000000..55c21f8 --- /dev/null +++ b/lib/user_test.go @@ -0,0 +1 @@ +package lib diff --git a/lib/utils.go b/lib/utils.go new file mode 100644 index 0000000..a909cbc --- /dev/null +++ b/lib/utils.go @@ -0,0 +1,47 @@ +package lib + +import ( + "net/http" + + "github.com/mxschmitt/playwright-go" +) + +func cookieToJar(cookies []cookie) []*http.Cookie { + var ( + cooks []*http.Cookie + ) + for _, c := range cookies { + cooks = append( + cooks, + &http.Cookie{ + Name: c.Name, + Value: c.Value, + Path: c.Path, + Domain: c.Domain, + Secure: c.Secure, + HttpOnly: c.HTTPOnly, + }, + ) + } + return cooks +} + +func cookieToParam(cookies []cookie) []playwright.SetNetworkCookieParam { + var ( + cooks []playwright.SetNetworkCookieParam + ) + for _, c := range cookies { + cooks = append(cooks, playwright.SetNetworkCookieParam{ + Name: c.Name, + Value: c.Value, + URL: playwright.String(""), + Domain: playwright.String(c.Domain), + Path: playwright.String(c.Path), + Expires: playwright.Int(c.Expires), + HttpOnly: playwright.Bool(c.HTTPOnly), + Secure: playwright.Bool(c.Secure), + SameSite: playwright.String(c.SameSite), + }) + } + return cooks +} diff --git a/main.exe b/main.exe new file mode 100644 index 0000000..41463d0 Binary files /dev/null and b/main.exe differ diff --git a/main.go b/main.go new file mode 100644 index 0000000..9d2649c --- /dev/null +++ b/main.go @@ -0,0 +1,70 @@ +package main + +import ( + "os" + "path" + "time" + + rotates "github.com/lestrrat-go/file-rotatelogs" + log "github.com/sirupsen/logrus" + easy "github.com/t-tomalak/logrus-easy-formatter" + + "github.com/huoxue1/study_xxqg/lib" +) + +func init() { + config = lib.GetConfig() + logFormatter := &easy.Formatter{ + TimestampFormat: "2006-01-02 15:04:05", + LogFormat: "[%time%] [%lvl%]: %msg% \n", + } + w, err := rotates.New(path.Join("logs", "%Y-%m-%d.log"), rotates.WithRotationTime(time.Hour*24)) + if err != nil { + log.Errorf("rotates init err: %v", err) + panic(err) + } + log.SetOutput(w) + log.SetFormatter(logFormatter) + level, err := log.ParseLevel(config.LogLevel) + + log.SetLevel(level) +} + +var ( + config lib.Config +) + +func init() { + _, err := os.Stat(`./config/`) + if err != nil { + os.Mkdir("./config/", 0666) + return + } +} + +func main() { + + log.Infoln(`// 刷课模式,默认为1, + 1:只刷文章何视频 + 2:只刷文章和视频和每日答题 + 3:刷文章和视频和每日答题每周答题和专项答题`) + log.Infoln("检测到模式", config.Model) + core := lib.Core{} + defer core.Quit() + core.Init() + login, err := core.Login() + if err != nil { + return + } + + core.LearnArticle(login) + core.LearnVideo(login) + if config.Model == 2 { + core.RespondDaily(login, "daily") + } else if config.Model == 3 { + core.RespondDaily(login, "daily") + core.RespondDaily(login, "weekly") + core.RespondDaily(login, "special") + } + +} diff --git a/qrcode.png b/qrcode.png new file mode 100644 index 0000000..c0badbf Binary files /dev/null and b/qrcode.png differ diff --git a/user.json b/user.json new file mode 100644 index 0000000..c0e8cde --- /dev/null +++ b/user.json @@ -0,0 +1 @@ +[{"cookies":[{"name":"_uab_collina","value":"163661069885993287694617","domain":"pc.xuexi.cn","path":"/points","expires":1951970698,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"__UID__","value":"4d0dda30-42b5-11ec-8eab-7b360a497c38","domain":".xuexi.cn","path":"/","expires":1668146699,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"tmzw","value":"1636610699718","domain":".xuexi.cn","path":"/","expires":1636697099,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"zwfigprt","value":"326756256aef95deeac0da98de19540a","domain":".xuexi.cn","path":"/","expires":1636697099,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"aliyungf_tc","value":"3212c84c4db8248eb50cafb336966f583ec86bde33da115a4561f52089443041","domain":"pc-api.xuexi.cn","path":"/","expires":-1,"httpOnly":true,"secure":false,"same_site":"None"},{"name":"acw_tc","value":"2f6fc10a16366106974298596e40c8422b249f6358963e35bdec6a06e7fffc","domain":"pc-api.xuexi.cn","path":"/","expires":1636612500,"httpOnly":true,"secure":false,"same_site":"None"},{"name":"aliyungf_tc","value":"e1ca5c9dbc630cf0cd3a4479a7cb7827b29e00e66d27bc969d7ede5f659a28ac","domain":"login.xuexi.cn","path":"/","expires":-1,"httpOnly":true,"secure":false,"same_site":"None"},{"name":"acw_tc","value":"2f6fc10a16366106983052977e40cad89c774137b66301bdc7f0766c8365f7","domain":"login.xuexi.cn","path":"/","expires":1636612501,"httpOnly":true,"secure":false,"same_site":"None"},{"name":"_bl_uid","value":"q2kIFvUvu68jwgrLg7LXxzt6sp2n","domain":"login.xuexi.cn","path":"/","expires":1652162702,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"_bl_uid","value":"qnkIdvOnu16j6LrUR7d9xjnosUR9","domain":"pc.xuexi.cn","path":"/","expires":1652162702,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"_bl_uid","value":"y5kOpvmgu6zjFnrqU8Is8atmzI1e","domain":"www.xuexi.cn","path":"/","expires":1652162702,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"token","value":"393807e803174851ac408d5a97ae0d97","domain":".xuexi.cn","path":"/","expires":1636632336,"httpOnly":false,"secure":false,"same_site":"None"}],"nick":"gjs","uid":"151172176463"},{"cookies":[{"name":"_uab_collina","value":"163661377781318630961955","domain":"pc.xuexi.cn","path":"/points","expires":1951973777,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"__UID__","value":"78569db0-42bc-11ec-9199-7dc30372e140","domain":".xuexi.cn","path":"/","expires":1668149778,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"tmzw","value":"1636613778733","domain":".xuexi.cn","path":"/","expires":1636700178,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"zwfigprt","value":"d90dcffd85a58b9228f41a3564b585f9","domain":".xuexi.cn","path":"/","expires":1636700178,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"aliyungf_tc","value":"e73d7582129f14b8e44805900fe16c6b3d1cb98cfbf3e92e2daaf5de778a0041","domain":"pc-api.xuexi.cn","path":"/","expires":-1,"httpOnly":true,"secure":false,"same_site":"None"},{"name":"acw_tc","value":"2f6fc10a16366137763874277e40f11af2eb6b2a22101afc55394b828f1b9e","domain":"pc-api.xuexi.cn","path":"/","expires":1636615579,"httpOnly":true,"secure":false,"same_site":"None"},{"name":"aliyungf_tc","value":"edf43c7511240e88c17b406bf729a9ee5fa1c286aab41995912ee012e478d4d6","domain":"login.xuexi.cn","path":"/","expires":-1,"httpOnly":true,"secure":false,"same_site":"None"},{"name":"acw_tc","value":"2f6fc10a16366137768314189e40f324b65cd4dd52322d849bd68424a12439","domain":"login.xuexi.cn","path":"/","expires":1636615579,"httpOnly":true,"secure":false,"same_site":"None"},{"name":"_bl_uid","value":"tqkLUvkhuFalRsl0d7n7pqw2X2nb","domain":"www.xuexi.cn","path":"/","expires":1652165781,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"_bl_uid","value":"U6kIsvCyud8lhglmb8ev1pk5pnak","domain":"login.xuexi.cn","path":"/","expires":1652165781,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"_bl_uid","value":"ItkwCv85uFqlyLl038zb1v6nzy2b","domain":"pc.xuexi.cn","path":"/","expires":1652165781,"httpOnly":false,"secure":false,"same_site":"None"},{"name":"token","value":"c18d4e0283514eeda1a4fe6cf63e3ac6","domain":".xuexi.cn","path":"/","expires":1636635418,"httpOnly":false,"secure":false,"same_site":"None"}],"nick":"苟江山","uid":"151142372517"}] \ No newline at end of file