mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2026-02-06 15:20:22 +00:00
Compare commits
230 Commits
p1.1.6.6
...
v1.2.2-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
596c75eeca | ||
|
|
67a493ee84 | ||
|
|
f9c7d9419e | ||
|
|
1a1a006283 | ||
|
|
fd85ce0923 | ||
|
|
5ff9069ff2 | ||
|
|
4ad7ff7971 | ||
|
|
a9f98763c4 | ||
|
|
3b637fad78 | ||
|
|
f202797964 | ||
|
|
242d780048 | ||
|
|
bf23364097 | ||
|
|
7eaac7a20b | ||
|
|
45448e6f53 | ||
|
|
08ee458a27 | ||
|
|
3f97541956 | ||
|
|
e2c603338e | ||
|
|
a363623df9 | ||
|
|
b871573dc3 | ||
|
|
ad33c1ca3c | ||
|
|
810cb284b6 | ||
|
|
d0ffb25594 | ||
|
|
2b6796d1d1 | ||
|
|
52093f3dd3 | ||
|
|
2a5c1ac92c | ||
|
|
6e0222f5e2 | ||
|
|
4bebf79266 | ||
|
|
b6dadc142b | ||
|
|
1cbc49251b | ||
|
|
d2d5b5863b | ||
|
|
65f9203597 | ||
|
|
4c1926e395 | ||
|
|
516d937149 | ||
|
|
7af17c3c65 | ||
|
|
33b85207f5 | ||
|
|
218119e10e | ||
|
|
4485d3d5a9 | ||
|
|
fcf62a31e1 | ||
|
|
ebdc83809c | ||
|
|
61a3c30a45 | ||
|
|
0e4cab7221 | ||
|
|
865c90a85a | ||
|
|
c9b06a4fa8 | ||
|
|
0e75143a42 | ||
|
|
ce333c5793 | ||
|
|
6b4045db45 | ||
|
|
3fba704c77 | ||
|
|
0712a80ea0 | ||
|
|
8969c44b3f | ||
|
|
f7a3c0e6c6 | ||
|
|
b1852a1de6 | ||
|
|
5645fa0168 | ||
|
|
c824b2e0b4 | ||
|
|
1c0d2b2f95 | ||
|
|
10b8ab297a | ||
|
|
e8b0e475d7 | ||
|
|
7fc4a6425b | ||
|
|
a8a3c48e6c | ||
|
|
9fca69302b | ||
|
|
ba6811224e | ||
|
|
ca4a682794 | ||
|
|
b9ddcfc3d7 | ||
|
|
b20cb1c0a0 | ||
|
|
1d9813691c | ||
|
|
08f8954e03 | ||
|
|
610f04ab87 | ||
|
|
793cac09cb | ||
|
|
d0d07604e5 | ||
|
|
fcf6d087a7 | ||
|
|
35587ccb85 | ||
|
|
9df5b6cc33 | ||
|
|
ffeedf576d | ||
|
|
151ce216ab | ||
|
|
20835f9858 | ||
|
|
63a61f2fb5 | ||
|
|
c553248cf4 | ||
|
|
cdd1dc68da | ||
|
|
37af3c1498 | ||
|
|
1bc3847345 | ||
|
|
f14d8ab5f6 | ||
|
|
bb63b98f39 | ||
|
|
f3f7e2d3c1 | ||
|
|
89121cfc57 | ||
|
|
d8a41a5ee1 | ||
|
|
f8ca9355d2 | ||
|
|
d4bceb1922 | ||
|
|
876d85ac4e | ||
|
|
ab34930beb | ||
|
|
35f7450ab2 | ||
|
|
2917ff6701 | ||
|
|
d1656b25c9 | ||
|
|
e6a20866b6 | ||
|
|
9de065d31a | ||
|
|
37d27e07e9 | ||
|
|
01853768db | ||
|
|
9d3787dfc2 | ||
|
|
e9f145056d | ||
|
|
ea17943bff | ||
|
|
180c32ef50 | ||
|
|
17bb844360 | ||
|
|
3d0a161176 | ||
|
|
1148867d3c | ||
|
|
c83d01e977 | ||
|
|
7c42d6f857 | ||
|
|
ac1c6bc74b | ||
|
|
12f96bb95c | ||
|
|
fa063a05f7 | ||
|
|
51d10d3234 | ||
|
|
2d2db15813 | ||
|
|
006c782819 | ||
|
|
37e36fb886 | ||
|
|
0e026a8b3b | ||
|
|
f37daa5ca6 | ||
|
|
dafcf7049b | ||
|
|
207da3fcd9 | ||
|
|
a3fbe5d747 | ||
|
|
c1717c71bd | ||
|
|
bcd111f585 | ||
|
|
d6f1113201 | ||
|
|
e812d2a074 | ||
|
|
a537306307 | ||
|
|
206889bdc5 | ||
|
|
58c7b5c818 | ||
|
|
eace561f73 | ||
|
|
e2032cdc74 | ||
|
|
34bc8e3a3c | ||
|
|
1304611ab7 | ||
|
|
7a0ce4b5d6 | ||
|
|
835df33e11 | ||
|
|
fb80c3606b | ||
|
|
f0c80693bb | ||
|
|
6997c2f2b9 | ||
|
|
cb1057cd5e | ||
|
|
eb1e2de917 | ||
|
|
4b9b66e7d0 | ||
|
|
c1e87ace78 | ||
|
|
ba9eb9c023 | ||
|
|
3084d4ad54 | ||
|
|
fcb13c19a1 | ||
|
|
d6b81d0363 | ||
|
|
fdd1135a66 | ||
|
|
f9ce439e56 | ||
|
|
9608d1e736 | ||
|
|
776d7fbbd9 | ||
|
|
7c69c24362 | ||
|
|
d205d48166 | ||
|
|
dd32aec462 | ||
|
|
c0327fe733 | ||
|
|
6f4c40f450 | ||
|
|
022ee6cd95 | ||
|
|
085e04c9ba | ||
|
|
19f58acb79 | ||
|
|
90dd60b942 | ||
|
|
755bfeaa5b | ||
|
|
82ee2f6b0a | ||
|
|
500d03f293 | ||
|
|
ee99abe6ff | ||
|
|
0563006c55 | ||
|
|
31db6fa4fb | ||
|
|
043b543cfa | ||
|
|
7b52599da7 | ||
|
|
4dc8e5619c | ||
|
|
21ff752322 | ||
|
|
0f687d6e95 | ||
|
|
22e23efbdc | ||
|
|
38935937ae | ||
|
|
a3685e2e83 | ||
|
|
3a5e391191 | ||
|
|
09a9dd53fd | ||
|
|
5e064249be | ||
|
|
67555512e7 | ||
|
|
591df6439c | ||
|
|
7dfb7dfe24 | ||
|
|
8e56f137b6 | ||
|
|
3b61c73c17 | ||
|
|
44dac98b37 | ||
|
|
1a7f98379a | ||
|
|
4109ef0612 | ||
|
|
93a9bf6c5b | ||
|
|
0b7b35cdcc | ||
|
|
350ef86dd6 | ||
|
|
2d69ae2e42 | ||
|
|
e95367f4b2 | ||
|
|
3101911aa2 | ||
|
|
ac73087f93 | ||
|
|
d375a3bbbb | ||
|
|
c46ca1d4c2 | ||
|
|
2a1cca8ebb | ||
|
|
d1fdc989b6 | ||
|
|
672215f753 | ||
|
|
4160175fcf | ||
|
|
8f8ba55fc9 | ||
|
|
1dbb4aa837 | ||
|
|
c183c4f17d | ||
|
|
fa6d14d89a | ||
|
|
65b92b9da3 | ||
|
|
a7056c4601 | ||
|
|
1dce0f7859 | ||
|
|
bdffbfab67 | ||
|
|
98b21d0fdf | ||
|
|
cccea70db1 | ||
|
|
d2bcd0bc9f | ||
|
|
08653fd3b6 | ||
|
|
75d0671d9b | ||
|
|
51ec3c32a9 | ||
|
|
ec0032c5ab | ||
|
|
2723f3662c | ||
|
|
539b050b97 | ||
|
|
a0b9623a9f | ||
|
|
6b263418a0 | ||
|
|
92989ce9fd | ||
|
|
173925b57a | ||
|
|
43735722bf | ||
|
|
d89e7aebec | ||
|
|
4677d789f2 | ||
|
|
2fb445746e | ||
|
|
505dfef48c | ||
|
|
19a9a8ef83 | ||
|
|
119730bada | ||
|
|
c46748524a | ||
|
|
29f833db41 | ||
|
|
1232856b21 | ||
|
|
770ae6ebd0 | ||
|
|
8c9ced0bda | ||
|
|
3b3dd3df99 | ||
|
|
0817e6233d | ||
|
|
aa4f428194 | ||
|
|
73717dec73 | ||
|
|
57e2ef3bab | ||
|
|
a5d0b8db8e |
59
.github/workflows/nightly.yml
vendored
Normal file
59
.github/workflows/nightly.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: 最新版
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
BINARY_PREFIX: "zbp_"
|
||||
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, darwin]
|
||||
goarch: ["386", amd64, arm, arm64]
|
||||
exclude:
|
||||
- goos: darwin
|
||||
goarch: arm
|
||||
- goos: darwin
|
||||
goarch: "386"
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
- 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: Cache downloaded module
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ matrix.goos }}-${{ matrix.goarch }}-${{ hashFiles('**/go.sum') }}
|
||||
- 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" -trimpath -ldflags "$LD_FLAGS" .
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: ${{ !github.head_ref }}
|
||||
with:
|
||||
name: ${{ matrix.goos }}_${{ matrix.goarch }}
|
||||
path: output/
|
||||
55
.github/workflows/prerelease.yml
vendored
55
.github/workflows/prerelease.yml
vendored
@@ -1,55 +0,0 @@
|
||||
name: 测试版
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- p*
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
jobs:
|
||||
my-job:
|
||||
name: Build ZeroBot-Plugin on Push Tag 🚀
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Cache Go
|
||||
id: cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
# A list of files, directories, and wildcard patterns to cache and restore
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }}
|
||||
|
||||
- name: Tidy Go modules
|
||||
run: go mod tidy
|
||||
|
||||
- name: Build linux-x64
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-x64
|
||||
- name: Build linux-x86
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-x86
|
||||
- name: Build windows-x64
|
||||
run: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-windows-x64.exe
|
||||
- name: Build windows-x86
|
||||
run: CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-windows-x86.exe
|
||||
- name: Build arm64
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=7 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-arm64
|
||||
- name: Build armv6
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-armv6
|
||||
|
||||
- name: Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: artifacts/zerobot-plugin-*
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
56
.github/workflows/release.yml
vendored
56
.github/workflows/release.yml
vendored
@@ -1,55 +1,27 @@
|
||||
name: 稳定版
|
||||
name: 发行版
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
my-job:
|
||||
name: Build ZeroBot-Plugin on Push Tag 🚀
|
||||
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
|
||||
go-version: '1.17'
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Cache Go
|
||||
id: cache
|
||||
uses: actions/cache@v2
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
with:
|
||||
# A list of files, directories, and wildcard patterns to cache and restore
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }}
|
||||
|
||||
- name: Tidy Go modules
|
||||
run: go mod tidy
|
||||
|
||||
- name: Build linux-x64
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-x64
|
||||
- name: Build linux-x86
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-x86
|
||||
- name: Build windows-x64
|
||||
run: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-windows-x64.exe
|
||||
- name: Build windows-x86
|
||||
run: CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-windows-x86.exe
|
||||
- name: Build arm64
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=7 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-arm64
|
||||
- name: Build armv6
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags="-s -w" -o artifacts/zerobot-plugin-linux-armv6
|
||||
|
||||
- name: Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: artifacts/zerobot-plugin-*
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -3,10 +3,17 @@ data/control
|
||||
data/SetuTime/search
|
||||
data/manager
|
||||
data/acgimage
|
||||
data/saucenao
|
||||
data/fortune
|
||||
data/hs
|
||||
data/nsetu
|
||||
data/nwife
|
||||
data/sleep
|
||||
plugins/*.so
|
||||
plugins/*.dll
|
||||
.idea/
|
||||
.DS_Store
|
||||
.vscode
|
||||
go-zero*
|
||||
nohup.out
|
||||
zerobot
|
||||
@@ -10,10 +10,6 @@ linters-settings:
|
||||
disabled-checks:
|
||||
- exitAfterDefer
|
||||
|
||||
gofumpt:
|
||||
# Select the Go version to target. The default is `1.15`.
|
||||
lang-version: "1.17"
|
||||
|
||||
forbidigo:
|
||||
# Forbid the following identifiers
|
||||
forbid:
|
||||
@@ -22,13 +18,13 @@ linters-settings:
|
||||
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
|
||||
disable-all: true
|
||||
fast: false
|
||||
enable:
|
||||
- bodyclose
|
||||
- deadcode
|
||||
- depguard
|
||||
- dogsled
|
||||
- dupl
|
||||
- errcheck
|
||||
- exportloopref
|
||||
- exhaustive
|
||||
@@ -43,7 +39,7 @@ linters:
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
#- misspell
|
||||
- nolintlint
|
||||
- rowserrcheck
|
||||
- staticcheck
|
||||
@@ -58,23 +54,9 @@ linters:
|
||||
- prealloc
|
||||
- predeclared
|
||||
- asciicheck
|
||||
- revive
|
||||
- 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.
|
||||
@@ -95,4 +77,4 @@ issues:
|
||||
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"
|
||||
- "Error return value of .((os.)?std(out|err)..*|.*Close|.*Seek|.*Flush|os.Remove(All)?|.*print(f|ln)?|os.(Un)?Setenv). is not check"
|
||||
79
.goreleaser.yml
Normal file
79
.goreleaser.yml
Normal file
@@ -0,0 +1,79 @@
|
||||
project_name: zbp
|
||||
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:
|
||||
- 6
|
||||
- 7
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: arm
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
flags:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -s -w
|
||||
- id: win
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GO111MODULE=on
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
flags:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -s -w
|
||||
|
||||
checksum:
|
||||
name_template: "zbp_checksums.txt"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- "^docs:"
|
||||
- "^test:"
|
||||
- fix typo
|
||||
- Merge pull request
|
||||
- Merge branch
|
||||
- Merge remote-tracking
|
||||
- go mod tidy
|
||||
|
||||
archives:
|
||||
- id: nowin
|
||||
builds:
|
||||
- nowin
|
||||
- win
|
||||
name_template: "zbp_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
|
||||
nfpms:
|
||||
- license: GPL 3.0
|
||||
homepage: https://github.com/FloatTech/ZeroBot-Plugin
|
||||
file_name_template: "zbp_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
maintainer: FloatTech
|
||||
156
README.md
156
README.md
@@ -10,28 +10,52 @@
|
||||
[](https://github.com/takayama-lily/node-onebot)
|
||||
[](https://github.com/yyuueexxiinngg/onebot-kotlin)
|
||||
|
||||
[](https://goreportcard.com/report/github.com/github.com/Yiwen-Chan/ZeroBot-Plugin)
|
||||
[](https://goreportcard.com/report/github.com/github.com/FloatTech/ZeroBot-Plugin)
|
||||
[](https://github.com/howmanybots/onebot)
|
||||
[](https://github.com/wdvxdr1123/ZeroBot)
|
||||
[](https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin/master/LICENSE)
|
||||
[](https://github.com/wdvxdr1123/ZeroBot)
|
||||
[](https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin/master/LICENSE)
|
||||
[](https://jq.qq.com/?_wv=1027&k=QMb7x1mM)
|
||||
|
||||
本项目符合 [OneBot](https://github.com/howmanybots/onebot) 标准,可基于以下项目与机器人框架/平台进行交互
|
||||
| 项目地址 | 平台 | 核心作者 |
|
||||
| --- | --- | --- |
|
||||
| [Mrs4s/go-cqhttp](https://github.com/Mrs4s/go-cqhttp) | [MiraiGo](https://github.com/Mrs4s/MiraiGo) | Mrs4s |
|
||||
| [yyuueexxiinngg/cqhttp-mirai](https://github.com/yyuueexxiinngg/cqhttp-mirai) | [Mirai](https://github.com/mamoe/mirai) | yyuueexxiinngg |
|
||||
| [takayama-lily/onebot](https://github.com/takayama-lily/onebot) | [OICQ](https://github.com/takayama-lily/oicq) | takayama |
|
||||
|
||||
</div>
|
||||
|
||||
> 如果您不知道什么是 [OneBot](https://github.com/howmanybots/onebot) 或不希望运行多个程序,还可以直接前往 [gocqzbp](https://github.com/FloatTech/gocqzbp) 的 [Release](https://github.com/FloatTech/gocqzbp/releases) 页面下载单一可执行文件或前往 [Packages](https://github.com/FloatTech/gocqzbp/pkgs/container/gocqzbp) 页面使用`docker`,运行后按提示登录即可。
|
||||
|
||||
## 命令行参数
|
||||
```bash
|
||||
zerobot [-d] [-g] qq1 qq2 qq3 ...
|
||||
zerobot -h -t token -u url [-d|w] [-g 监听地址:端口] qq1 qq2 qq3 ...
|
||||
```
|
||||
- **-d**: 开启 debug 级别日志输出
|
||||
- **-g**: 开启 [webgui](https://github.com/FloatTech/bot-manager)
|
||||
- **-h**: 显示帮助
|
||||
- **-t token**: 设置`AccessToken`,默认为空
|
||||
- **-u url**: 设置`Url`,默认为`ws://127.0.0.1:6700`
|
||||
- **-d|w**: 开启 debug | warning 级别及以上日志输出
|
||||
- **-g 监听地址:端口**: 在 http://监听地址:端口 上开启 [webgui](https://github.com/FloatTech/bot-manager)
|
||||
- **qqs**: superusers 的 qq 号
|
||||
|
||||
## 功能
|
||||
> 在编译时,以下功能除插件控制外,均可通过注释`main.go`中的相应`import`而物理禁用,减小插件体积。
|
||||
> 通过插件控制,还可动态管理某个功能在某个群的打开/关闭。
|
||||
- **web管理** `import _ "github.com/FloatTech/ZeroBot-Plugin/control/web"`
|
||||
- 开启后可执行文件大约增加 5M ,默认注释不开启。如需开启请自行编辑`main.go`取消注释
|
||||
- 需要配合 [webgui](https://github.com/FloatTech/bot-manager) 使用
|
||||
- **动态加载插件** `import _ github.com/FloatTech/ZeroBot-Plugin-Dynamic/dyloader`
|
||||
- 本功能需要`cgo`,故已分离出主线。详见[ZeroBot-Plugin-Dynamic](https://github.com/FloatTech/ZeroBot-Plugin-Dynamic)
|
||||
- **插件控制**
|
||||
- [x] /启用 xxx
|
||||
- [x] /禁用 xxx
|
||||
- [x] /启用 xxx (在发送的群/用户启用xxx)
|
||||
- [x] /禁用 xxx (在发送的群/用户禁用xxx)
|
||||
- [x] /全局启用 xxx
|
||||
- [x] /全局禁用 xxx
|
||||
- [x] /还原 xxx (在发送的群/用户还原xxx的开启状态到初始状态)
|
||||
- [x] /用法 xxx
|
||||
- [x] /服务列表
|
||||
- [x] /服务详情
|
||||
- [x] @Bot 插件冲突检测 (会在本群发送一条消息并在约 1s 后撤回以检测其它同类 bot 中已启用的插件并禁用)
|
||||
- **聊天** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_chat"`
|
||||
- [x] [BOT名字]
|
||||
- [x] [戳一戳BOT]
|
||||
@@ -40,12 +64,12 @@ zerobot [-d] [-g] qq1 qq2 qq3 ...
|
||||
- [x] 群温度
|
||||
- [x] 设置温度[正整数]
|
||||
- **ATRI** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_atri"`
|
||||
- [x] 具体指令看代码
|
||||
- [x] 具体指令看 /用法 atri
|
||||
- 注:本插件基于 [ATRI](https://github.com/Kyomotoi/ATRI) ,为 Golang 移植版
|
||||
- **群管** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_manager"`
|
||||
- [x] 禁言[@xxx][分钟]
|
||||
- [x] 解除禁言[@xxx]
|
||||
- [x] 我要自闭 [分钟]
|
||||
- [x] 我要自闭|禅定 x [分钟|小时|天]
|
||||
- [x] 开启全员禁言
|
||||
- [x] 解除全员禁言
|
||||
- [x] 升为管理[@xxx]
|
||||
@@ -54,7 +78,7 @@ zerobot [-d] [-g] qq1 qq2 qq3 ...
|
||||
- [x] 修改头衔[@xxx][xxx]
|
||||
- [x] 申请头衔[xxx]
|
||||
- [x] 踢出群聊[@xxx]
|
||||
- [x] 退出群聊[群号]
|
||||
- [x] 退出群聊[群号]@Bot
|
||||
- [x] *入群欢迎
|
||||
- [x] *退群通知
|
||||
- [x] 设置欢迎语[欢迎~]
|
||||
@@ -62,14 +86,17 @@ zerobot [-d] [-g] qq1 qq2 qq3 ...
|
||||
- [x] 在[MM]月[每周|周几]的[hh]点[mm]分时(用[url])提醒大家[xxx]
|
||||
- [x] 取消在[MM]月[dd]日的[hh]点[mm]分的提醒
|
||||
- [x] 取消在[MM]月[每周|周几]的[hh]点[mm]分的提醒
|
||||
- [x] 在"cron"时(用[url])提醒大家[xxx]
|
||||
- [x] 取消在"cron"的提醒
|
||||
- [x] 列出所有提醒
|
||||
- [x] 翻牌
|
||||
- [x] [开启|关闭]入群验证
|
||||
- [ ] 同意入群请求
|
||||
- [x] [开启|关闭]gist加群自动审批
|
||||
- [ ] 同意好友请求
|
||||
- [ ] 撤回[@xxx] [xxx]
|
||||
- [ ] 警告[@xxx]
|
||||
- [x] run[xxx]
|
||||
- 注:使用gist加群自动审批,请在群介绍添加以下说明,同时开启`需要回答问题并由管理员审核`:加群请在github新建一个gist,其文件名为本群群号的字符串的md5(小写),内容为一行,是当前unix时间戳(10分钟内有效)。然后请将您的用户名和gist哈希(小写)按照username/gisthash的格式填写到回答即可。
|
||||
- **GitHub仓库搜索** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_github"`
|
||||
- [x] >github [xxx]
|
||||
- [x] >github -p [xxx]
|
||||
@@ -92,52 +119,83 @@ zerobot [-d] [-g] qq1 qq2 qq3 ...
|
||||
- [x] 爬[@xxx]
|
||||
- [x] 摸[@xxx]
|
||||
- [x] 搓[@xxx]
|
||||
- 注:更多指令见项目 --> https://github.com/tdf1939/ZeroBot-Plugin-Gif
|
||||
- 注:更多指令见项目 --> https://github.com/FloatTech/ZeroBot-Plugin-Gif
|
||||
- **base16384加解密** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_b14"`
|
||||
- [x] 加密xxx
|
||||
- [x] 解密xxx
|
||||
- [x] 用yyy加密xxx
|
||||
- [x] 用yyy解密xxx
|
||||
- **摸鱼** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_moyu"`
|
||||
- [x] 添加摸鱼提醒
|
||||
- [x] 删除摸鱼提醒
|
||||
- **涩图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_setutime"`
|
||||
- [x] 来份[涩图/二次元/风景/车万]
|
||||
- [x] 添加[涩图/二次元/风景/车万][P站图片ID]
|
||||
- [x] 删除[涩图/二次元/风景/车万][P站图片ID]
|
||||
- [x] > setu status
|
||||
- **本地涩图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_nativesetu"`
|
||||
- [x] 本地[xxx]
|
||||
- [x] 刷新本地[xxx]
|
||||
- [x] 设置本地setu绝对路径[xxx]
|
||||
- [x] 刷新所有本地setu
|
||||
- [x] 所有本地setu分类
|
||||
- 注:刷新文件夹较慢,请耐心等待刷新完成,会提示“成功”。
|
||||
- **lolicon** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_lolicon"`
|
||||
- [x] 来份萝莉
|
||||
- **搜图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_saucenao"`
|
||||
- [x] 以图搜图|搜索图片|以图识图[图片]
|
||||
- [x] 搜图[P站图片ID]
|
||||
- **搜番** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_tracemoe"`
|
||||
- [x] 搜番|搜索番剧[图片]
|
||||
- **随机图片与AI点评** `github.com/FloatTech/ZeroBot-Plugin/plugin_acgimage`
|
||||
- [x] 随机图片(评级大于6的图将私发)
|
||||
- [x] 直接随机(无r18检测,务必小心,仅管理可用)
|
||||
- [x] 设置随机图片网址[url]
|
||||
- [x] 太涩了(撤回最近发的图)
|
||||
- [x] 评价图片(发送一张图片让bot评分)
|
||||
- **每日运势** `github.com/FloatTech/ZeroBot-Plugin/plugin_fortune`
|
||||
- [x] 运势
|
||||
- **浅草寺求签** `github.com/FloatTech/ZeroBot-Plugin/plugin_omikuji`
|
||||
- **每日运势** `import _ github.com/FloatTech/ZeroBot-Plugin/plugin_fortune`
|
||||
- [x] 运势|抽签
|
||||
- [x] 设置底图[车万 DC4 爱因斯坦 星空列车 樱云之恋 富婆妹 李清歌 公主连结 原神 明日方舟 碧蓝航线 碧蓝幻想 战双 阴阳师]
|
||||
- **睡眠管理** `import _ github.com/FloatTech/ZeroBot-Plugin/plugin_sleep_manage`
|
||||
- [x] 早安|晚安
|
||||
- **浅草寺求签** `import _ github.com/FloatTech/ZeroBot-Plugin/plugin_omikuji`
|
||||
- [x] 求签|占卜
|
||||
- [x] 解签
|
||||
- **bilibili** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_bilibili"`
|
||||
- [x] >vup info [名字|uid]
|
||||
- [x] >user info [名字|uid]
|
||||
- [x] /开启粉丝日报
|
||||
- [x] >user info [名字|uid]
|
||||
- [x] /开启粉丝日报
|
||||
- **嘉然** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_diana"`
|
||||
- [x] 小作文
|
||||
- [x] 发大病
|
||||
- [x] 教你一篇小作文[作文]
|
||||
- [x] [回复]查重
|
||||
- **鬼东西** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_wtf"`
|
||||
- [x] 鬼东西列表
|
||||
- [x] 查询鬼东西[序号][@xxx]
|
||||
- 注:由于需要科学,默认注释。
|
||||
- **AIfalse** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_ai_false"`
|
||||
- [x] 查询计算机当前活跃度 [身体检查]
|
||||
- [x] 清理缓存
|
||||
- [x] 查询计算机当前活跃度: [检查身体|自检|启动自检|系统状态]
|
||||
- [x] 清理缓存 (仅适用于 gocq 且需要 bot 的运行目录和 gocq 相同)
|
||||
- [ ] 简易语音
|
||||
- [ ] 爬图合成 [@xxx]
|
||||
- **抽wife** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_nativewife"`
|
||||
- [x] 抽wife[@xxx]
|
||||
- [x] 添加wife[名字][图片]
|
||||
- [x] 删除wife[名字]
|
||||
- [x] [让|不让]所有人均可添加wife
|
||||
- 注:不同群添加后不会重叠
|
||||
- **minecraft** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_minecraft"`
|
||||
- [x] /mcstart xxx
|
||||
- [x] /mcstop xxx
|
||||
- [x] /mclist servername
|
||||
- [x] /mcstop xxx
|
||||
- [x] /mclist servername
|
||||
- 注:此功能实现依赖[MCSManager](https://github.com/Suwings/MCSManager)项目对服务器的管理api,mc服务器如果没有在该管理平台部署此功能无效
|
||||
- **炉石** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_hs"`
|
||||
- [x] 搜卡[xxxx]
|
||||
- [x] [卡组代码xxx]
|
||||
- 注:更多搜卡指令参数:https://hs.fbigame.com/misc/searchhelp
|
||||
- **青云客** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_qingyunke"`
|
||||
- [x] @Bot 任意文本(任意一句话回复)
|
||||
- [x] @Bot 任意文本(任意一句话回复)
|
||||
- **关键字搜图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_image_finder"`
|
||||
- [x] 来张 [xxx]
|
||||
- **拼音首字母释义工具** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_nbnhhsh"`
|
||||
@@ -147,29 +205,33 @@ zerobot [-d] [-g] qq1 qq2 qq3 ...
|
||||
- **投胎** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_reborn"`
|
||||
- [x] reborn
|
||||
- 注:本插件来源于[tgbot](https://github.com/YukariChiba/tgbot/blob/main/modules/Reborn.py)
|
||||
- **翻译** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_translation"`
|
||||
- [x] >TL 你好
|
||||
- **vtb语录** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_vtb_quotation"`
|
||||
- [x] vtb语录
|
||||
- [x] 随机vtb
|
||||
- **书评** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_book_review"`
|
||||
- [x] 书评[xxx]
|
||||
- [x] 随机书评
|
||||
- **coser** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_coser" `
|
||||
- [x] coser
|
||||
- **novel** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_novel" `
|
||||
- [x] 小说[xxx]
|
||||
- **TODO...**
|
||||
|
||||
## 使用方法
|
||||
|
||||
本项目符合 [OneBot](https://github.com/howmanybots/onebot) 标准,可基于以下项目与机器人框架/平台进行交互
|
||||
| 项目地址 | 平台 | 核心作者 | 备注 |
|
||||
| --- | --- | --- | --- |
|
||||
| [Mrs4s/go-cqhttp](https://github.com/Mrs4s/go-cqhttp) | [MiraiGo](https://github.com/Mrs4s/MiraiGo) | Mrs4s | |
|
||||
| [yyuueexxiinngg/cqhttp-mirai](https://github.com/yyuueexxiinngg/cqhttp-mirai) | [Mirai](https://github.com/mamoe/mirai) | yyuueexxiinngg | |
|
||||
| [takayama-lily/onebot](https://github.com/takayama-lily/onebot) | [OICQ](https://github.com/takayama-lily/oicq) | takayama | |
|
||||
|
||||
|
||||
### 使用稳定版/测试版 (推荐)
|
||||
|
||||
可以前往[Release](https://github.com/FloatTech/ZeroBot-Plugin/releases)页面下载对应系统版本可执行文件,编译时开启了全部插件。
|
||||
可以前往[Release](https://github.com/FloatTech/ZeroBot-Plugin/releases)页面下载对应系统版本可执行文件,编译时开启了全部插件。您还可以选择 [gocqzbp](https://github.com/FloatTech/gocqzbp) 的 [Release](https://github.com/FloatTech/gocqzbp/releases) 或 [Package](https://github.com/FloatTech/gocqzbp/pkgs/container/gocqzbp),它是 [Mrs4s/go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 与本插件的合体。
|
||||
|
||||
### 本地运行
|
||||
### 本地直接运行
|
||||
|
||||
1. 下载安装 [Go](https://studygolang.com/dl) 环境
|
||||
2. 下载本项目[压缩包](https://github.com/Yiwen-Chan/ZeroBot-Plugin/archive/master.zip),本地解压
|
||||
1. 下载安装最新 [Go](https://studygolang.com/dl) 环境
|
||||
2. 下载本项目[压缩包](https://github.com/FloatTech/ZeroBot-Plugin/archive/master.zip),本地解压
|
||||
3. 编辑 main.go 文件,内容按需修改
|
||||
4. 双击 build.bat 文件 或 直接双击 run.bat 文件
|
||||
5. 运行 OneBot 框架,并同时运行本插件
|
||||
4. 运行 OneBot 框架
|
||||
5. `Windows`下双击 run.bat 文件,`Linux`下使用 run.sh 运行本插件
|
||||
|
||||
### 编译运行
|
||||
|
||||
@@ -178,14 +240,14 @@ zerobot [-d] [-g] qq1 qq2 qq3 ...
|
||||
1. 点击右上角 Fork 本项目,并转跳到自己 Fork 的仓库
|
||||
2. 点击仓库上方的 Actions 按钮,确认使用 Actions
|
||||
3. 编辑 main.go 文件,内容按需修改
|
||||
4. 前往 Release 页面发布一个 Release,`tag`形如`vx.y.z`,以触发稳定版编译流程
|
||||
4. 前往 Release 页面发布一个 Release,`tag`形如`v1.2.3`,以触发稳定版编译流程
|
||||
5. 点击 Actions 按钮,等待编译完成,回到 Release 页面下载编译好的文件
|
||||
6. 运行 OneBot 框架,并同时运行本插件
|
||||
7. 啾咪~
|
||||
|
||||
#### 本地编译/交叉编译
|
||||
|
||||
1. 下载安装 [Go](https://studygolang.com/dl) 环境
|
||||
1. 下载安装最新 [Go](https://studygolang.com/dl) 环境
|
||||
2. clone 并进入本项目,下载所需包
|
||||
|
||||
```bash
|
||||
@@ -198,15 +260,19 @@ go mod tidy
|
||||
```
|
||||
|
||||
3. 编辑 main.go 文件,内容按需修改
|
||||
4. 按照平台输入命令编译,下面举了两个不太常见的例子
|
||||
4. 按照平台输入命令编译,下面举了一些例子
|
||||
|
||||
```bash
|
||||
# 本机平台
|
||||
go build -ldflags "-s -w" -o zerobot
|
||||
go build -ldflags "-s -w" -o zerobot -trimpath
|
||||
# x64 Linux 平台 如各种云服务器
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o zerobot -trimpath
|
||||
# x64 Windows 平台 如大多数家用电脑
|
||||
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o zerobot.exe -trimpath
|
||||
# armv6 Linux 平台 如树莓派 zero W
|
||||
GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=0 go build -ldflags "-s -w" -o zerobot
|
||||
# mips Linux 平台 如 路由器 wndr4300
|
||||
GOOS=linux GOARCH=mips GOMIPS=softfloat CGO_ENABLED=0 go build -ldflags "-s -w" -o zerobot
|
||||
GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=0 go build -ldflags "-s -w" -o zerobot -trimpath
|
||||
# (由于引入了github.com/logoove/sqlite,本项不再可用)mips Linux 平台 如 路由器 wndr4300
|
||||
GOOS=linux GOARCH=mips GOMIPS=softfloat CGO_ENABLED=0 go build -ldflags "-s -w" -o zerobot -trimpath
|
||||
```
|
||||
|
||||
5. 运行 OneBot 框架,并同时运行本插件
|
||||
@@ -218,4 +284,4 @@ GOOS=linux GOARCH=mips GOMIPS=softfloat CGO_ENABLED=0 go build -ldflags "-s -w"
|
||||
|
||||
## License
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2FYiwen-Chan%2FZeroBot-Plugin?ref=badge_large)
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2FFloatTech%2FZeroBot-Plugin?ref=badge_large)
|
||||
|
||||
93
control/cd.go
Normal file
93
control/cd.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package control
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
b14 "github.com/fumiama/go-base16384"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
|
||||
)
|
||||
|
||||
var startTime int64
|
||||
|
||||
func init() {
|
||||
// 插件冲突检测 会在本群发送一条消息并在约 1s 后撤回
|
||||
zero.OnFullMatch("插件冲突检测", zero.OnlyGroup, zero.AdminPermission, zero.OnlyToMe).SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
tok, err := genToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t := message.Text("●cd" + tok)
|
||||
startTime = time.Now().Unix()
|
||||
id := ctx.SendChain(t)
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.DeleteMessage(id)
|
||||
})
|
||||
|
||||
zero.OnRegex("^●cd([\u4e00-\u8e00]{4})$", zero.OnlyGroup).SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if isValidToken(ctx.State["regex_matched"].([]string)[1]) {
|
||||
msg := ""
|
||||
gid := ctx.Event.GroupID
|
||||
ForEach(func(key string, manager *Control) bool {
|
||||
if manager.IsEnabledIn(gid) {
|
||||
msg += "\xfe\xff" + key
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(msg) > 2 {
|
||||
my, err := b14.UTF16be2utf8(b14.EncodeString(msg[2:]))
|
||||
mys := "●cd●" + helper.BytesToString(my)
|
||||
if err == nil {
|
||||
id := ctx.SendChain(message.Text(mys))
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.DeleteMessage(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
zero.OnRegex("^●cd●(([\u4e00-\u8e00]*[\u3d01-\u3d06]?))", zero.OnlyGroup).SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if time.Now().Unix()-startTime < 10 {
|
||||
msg, err := b14.UTF82utf16be(helper.StringToBytes(ctx.State["regex_matched"].([]string)[1]))
|
||||
if err == nil {
|
||||
gid := ctx.Event.GroupID
|
||||
for _, s := range strings.Split(b14.DecodeString(msg), "\xfe\xff") {
|
||||
mu.RLock()
|
||||
c, ok := managers[s]
|
||||
mu.RUnlock()
|
||||
if ok && c.IsEnabledIn(gid) {
|
||||
c.Disable(gid)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func genToken() (tok string, err error) {
|
||||
timebytes := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(timebytes, uint64(time.Now().Unix()))
|
||||
timebytes, err = b14.UTF16be2utf8(b14.Encode(timebytes[1:]))
|
||||
if err == nil {
|
||||
tok = helper.BytesToString(timebytes)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func isValidToken(tok string) (yes bool) {
|
||||
s, err := b14.UTF82utf16be(helper.StringToBytes(tok))
|
||||
if err == nil {
|
||||
timebytes := make([]byte, 1, 8)
|
||||
timebytes = append(timebytes, b14.Decode(s)...)
|
||||
yes = time.Now().Unix()-int64(binary.BigEndian.Uint64(timebytes)) < 10
|
||||
}
|
||||
return
|
||||
}
|
||||
19
control/cd_test.go
Normal file
19
control/cd_test.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package control
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGenToken(t *testing.T) {
|
||||
tok, err := genToken()
|
||||
if err == nil {
|
||||
t.Log(tok)
|
||||
t.Log(isValidToken(tok))
|
||||
t.Fail()
|
||||
} else {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaru(t *testing.T) {
|
||||
t.Log(len("\xff"))
|
||||
t.Fail()
|
||||
}
|
||||
@@ -4,9 +4,28 @@ import (
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
)
|
||||
|
||||
var enmap = make(map[string]*zero.Engine)
|
||||
|
||||
// Register 注册插件控制器
|
||||
func Register(service string, o *Options) *zero.Engine {
|
||||
engine := zero.New()
|
||||
engine.UsePreHandler(newctrl(service, o).Handler())
|
||||
enmap[service] = engine
|
||||
return engine
|
||||
}
|
||||
|
||||
// Delete 删除插件控制器,不会删除数据
|
||||
func Delete(service string) {
|
||||
engine, ok := enmap[service]
|
||||
if ok {
|
||||
engine.Delete()
|
||||
mu.RLock()
|
||||
_, ok = managers[service]
|
||||
mu.RUnlock()
|
||||
if ok {
|
||||
mu.Lock()
|
||||
delete(managers, service)
|
||||
mu.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
306
control/rule.go
306
control/rule.go
@@ -4,6 +4,7 @@ package control
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -11,11 +12,11 @@ import (
|
||||
"github.com/wdvxdr1123/ZeroBot/extension"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/data"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
|
||||
)
|
||||
|
||||
var (
|
||||
db = &data.Sqlite{DBPath: "data/control/plugins.db"}
|
||||
db = &sql.Sqlite{DBPath: "data/control/plugins.db"}
|
||||
// managers 每个插件对应的管理
|
||||
managers = map[string]*Control{}
|
||||
mu = sync.RWMutex{}
|
||||
@@ -40,8 +41,8 @@ func newctrl(service string, o *Options) *Control {
|
||||
}(),
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
managers[service] = m
|
||||
mu.Unlock()
|
||||
err := db.Create(service, &grpcfg{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -49,64 +50,153 @@ func newctrl(service string, o *Options) *Control {
|
||||
return m
|
||||
}
|
||||
|
||||
// enable enables a group to pass the Manager.
|
||||
func (m *Control) enable(groupID int64) {
|
||||
m.Lock()
|
||||
err := db.Insert(m.service, &grpcfg{groupID, 0})
|
||||
if err != nil {
|
||||
logrus.Errorf("[control] %v", err)
|
||||
}
|
||||
m.Unlock()
|
||||
}
|
||||
|
||||
// disable disables a group to pass the Manager.
|
||||
func (m *Control) disable(groupID int64) {
|
||||
m.Lock()
|
||||
err := db.Insert(m.service, &grpcfg{groupID, 1})
|
||||
if err != nil {
|
||||
logrus.Errorf("[control] %v", err)
|
||||
}
|
||||
m.Unlock()
|
||||
}
|
||||
|
||||
func (m *Control) isEnabledIn(gid int64) bool {
|
||||
m.RLock()
|
||||
// Enable enables a group to pass the Manager.
|
||||
// groupID == 0 (ALL) will operate on all grps.
|
||||
func (m *Control) Enable(groupID int64) {
|
||||
var c grpcfg
|
||||
err := db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(gid, 10))
|
||||
if err == nil {
|
||||
m.RUnlock()
|
||||
logrus.Debugf("[control] plugin %s of grp %d : %d", m.service, c.GroupID, c.Disable)
|
||||
return c.Disable == 0
|
||||
}
|
||||
logrus.Errorf("[control] %v", err)
|
||||
m.RLock()
|
||||
err := db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(groupID, 10))
|
||||
m.RUnlock()
|
||||
if m.options.DisableOnDefault {
|
||||
m.disable(gid)
|
||||
} else {
|
||||
m.enable(gid)
|
||||
if err != nil {
|
||||
c.GroupID = groupID
|
||||
}
|
||||
c.Disable = int64(uint64(c.Disable) & 0xffffffff_fffffffe)
|
||||
m.Lock()
|
||||
err = db.Insert(m.service, &c)
|
||||
m.Unlock()
|
||||
if err != nil {
|
||||
logrus.Errorf("[control] %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Disable disables a group to pass the Manager.
|
||||
// groupID == 0 (ALL) will operate on all grps.
|
||||
func (m *Control) Disable(groupID int64) {
|
||||
var c grpcfg
|
||||
m.RLock()
|
||||
err := db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(groupID, 10))
|
||||
m.RUnlock()
|
||||
if err != nil {
|
||||
c.GroupID = groupID
|
||||
}
|
||||
c.Disable |= 1
|
||||
m.Lock()
|
||||
err = db.Insert(m.service, &c)
|
||||
m.Unlock()
|
||||
if err != nil {
|
||||
logrus.Errorf("[control] %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets the default config of a group.
|
||||
// groupID == 0 (ALL) is not allowed.
|
||||
func (m *Control) Reset(groupID int64) {
|
||||
if groupID != 0 {
|
||||
m.Lock()
|
||||
err := db.Del(m.service, "WHERE gid = "+strconv.FormatInt(groupID, 10))
|
||||
m.Unlock()
|
||||
if err != nil {
|
||||
logrus.Errorf("[control] %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsEnabledIn 开启群
|
||||
func (m *Control) IsEnabledIn(gid int64) bool {
|
||||
var c grpcfg
|
||||
var err error
|
||||
logrus.Debugln("[control] IsEnabledIn recv gid =", gid)
|
||||
if gid != 0 {
|
||||
m.RLock()
|
||||
err = db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(gid, 10))
|
||||
m.RUnlock()
|
||||
if err == nil && gid == c.GroupID {
|
||||
logrus.Debugf("[control] plugin %s of grp %d : %d", m.service, c.GroupID, c.Disable&1)
|
||||
return c.Disable&1 == 0
|
||||
}
|
||||
}
|
||||
m.RLock()
|
||||
err = db.Find(m.service, &c, "WHERE gid = 0")
|
||||
m.RUnlock()
|
||||
if err == nil && c.GroupID == 0 {
|
||||
logrus.Debugf("[control] plugin %s of all : %d", m.service, c.Disable&1)
|
||||
return c.Disable&1 == 0
|
||||
}
|
||||
return !m.options.DisableOnDefault
|
||||
}
|
||||
|
||||
// GetData 获取某个群的 63 字节配置信息
|
||||
func (m *Control) GetData(gid int64) int64 {
|
||||
var c grpcfg
|
||||
var err error
|
||||
logrus.Debugln("[control] IsEnabledIn recv gid =", gid)
|
||||
if gid != 0 {
|
||||
m.RLock()
|
||||
err = db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(gid, 10))
|
||||
m.RUnlock()
|
||||
if err == nil && gid == c.GroupID {
|
||||
logrus.Debugf("[control] plugin %s of grp %d : %x", m.service, c.GroupID, c.Disable>>1)
|
||||
return c.Disable >> 1
|
||||
}
|
||||
}
|
||||
m.RLock()
|
||||
err = db.Find(m.service, &c, "WHERE gid = 0")
|
||||
m.RUnlock()
|
||||
if err == nil && c.GroupID == 0 {
|
||||
logrus.Debugf("[control] plugin %s of all : %x", m.service, c.Disable>>1)
|
||||
return c.Disable >> 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// SetData 为某个群设置低 63 位配置数据
|
||||
func (m *Control) SetData(groupID int64, data int64) error {
|
||||
var c grpcfg
|
||||
m.RLock()
|
||||
err := db.Find(m.service, &c, "WHERE gid = "+strconv.FormatInt(groupID, 10))
|
||||
m.RUnlock()
|
||||
if err != nil {
|
||||
c.GroupID = groupID
|
||||
if m.options.DisableOnDefault {
|
||||
c.Disable = 1
|
||||
}
|
||||
}
|
||||
c.Disable |= data << 1
|
||||
logrus.Debugf("[control] set plugin %s of all : %x", m.service, data)
|
||||
m.Lock()
|
||||
err = db.Insert(m.service, &c)
|
||||
m.Unlock()
|
||||
if err != nil {
|
||||
logrus.Errorf("[control] %v", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Handler 返回 预处理器
|
||||
func (m *Control) Handler() zero.Rule {
|
||||
return func(ctx *zero.Ctx) bool {
|
||||
ctx.State["manager"] = m
|
||||
return m.isEnabledIn(ctx.Event.GroupID)
|
||||
grp := ctx.Event.GroupID
|
||||
if grp == 0 {
|
||||
// 个人用户
|
||||
grp = -ctx.Event.UserID
|
||||
}
|
||||
logrus.Debugln("[control] handler get gid =", grp)
|
||||
return m.IsEnabledIn(grp)
|
||||
}
|
||||
}
|
||||
|
||||
// lookup returns a Manager by the service name, if
|
||||
// Lookup returns a Manager by the service name, if
|
||||
// not exist, it will returns nil.
|
||||
func lookup(service string) (*Control, bool) {
|
||||
func Lookup(service string) (*Control, bool) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
m, ok := managers[service]
|
||||
mu.RUnlock()
|
||||
return m, ok
|
||||
}
|
||||
|
||||
// forEach iterates through managers.
|
||||
func forEach(iterator func(key string, manager *Control) bool) {
|
||||
// ForEach iterates through managers.
|
||||
func ForEach(iterator func(key string, manager *Control) bool) {
|
||||
mu.RLock()
|
||||
m := copyMap(managers)
|
||||
mu.RUnlock()
|
||||
@@ -125,6 +215,13 @@ func copyMap(m map[string]*Control) map[string]*Control {
|
||||
return ret
|
||||
}
|
||||
|
||||
func userOrGrpAdmin(ctx *zero.Ctx) bool {
|
||||
if zero.OnlyGroup(ctx) {
|
||||
return zero.AdminPermission(ctx)
|
||||
}
|
||||
return zero.OnlyToMe(ctx)
|
||||
}
|
||||
|
||||
func init() {
|
||||
if !hasinit {
|
||||
mu.Lock()
|
||||
@@ -134,60 +231,121 @@ func init() {
|
||||
panic(err)
|
||||
} else {
|
||||
hasinit = true
|
||||
zero.OnCommandGroup([]string{"启用", "enable"}, zero.AdminPermission, zero.OnlyGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
model := extension.CommandModel{}
|
||||
_ = ctx.Parse(&model)
|
||||
service, ok := lookup(model.Args)
|
||||
if !ok {
|
||||
ctx.Send("没有找到指定服务!")
|
||||
}
|
||||
service.enable(ctx.Event.GroupID)
|
||||
ctx.Send(message.Text("已启用服务: " + model.Args))
|
||||
})
|
||||
zero.OnCommandGroup([]string{
|
||||
"启用", "enable", "禁用", "disable",
|
||||
"全局启用", "enableall", "全局禁用", "disableall",
|
||||
}, userOrGrpAdmin).Handle(func(ctx *zero.Ctx) {
|
||||
model := extension.CommandModel{}
|
||||
_ = ctx.Parse(&model)
|
||||
service, ok := Lookup(model.Args)
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("没有找到指定服务!"))
|
||||
return
|
||||
}
|
||||
grp := ctx.Event.GroupID
|
||||
if grp == 0 {
|
||||
// 个人用户
|
||||
grp = -ctx.Event.UserID
|
||||
}
|
||||
if strings.Contains(model.Command, "全局") || strings.Contains(model.Command, "all") {
|
||||
grp = 0
|
||||
}
|
||||
if strings.Contains(model.Command, "启用") || strings.Contains(model.Command, "enable") {
|
||||
service.Enable(grp)
|
||||
ctx.SendChain(message.Text("已启用服务: " + model.Args))
|
||||
} else {
|
||||
service.Disable(grp)
|
||||
ctx.SendChain(message.Text("已禁用服务: " + model.Args))
|
||||
}
|
||||
})
|
||||
|
||||
zero.OnCommandGroup([]string{"禁用", "disable"}, zero.AdminPermission, zero.OnlyGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
model := extension.CommandModel{}
|
||||
_ = ctx.Parse(&model)
|
||||
service, ok := lookup(model.Args)
|
||||
if !ok {
|
||||
ctx.Send("没有找到指定服务!")
|
||||
}
|
||||
service.disable(ctx.Event.GroupID)
|
||||
ctx.Send(message.Text("已关闭服务: " + model.Args))
|
||||
})
|
||||
zero.OnCommandGroup([]string{"还原", "reset"}, userOrGrpAdmin).Handle(func(ctx *zero.Ctx) {
|
||||
model := extension.CommandModel{}
|
||||
_ = ctx.Parse(&model)
|
||||
service, ok := Lookup(model.Args)
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("没有找到指定服务!"))
|
||||
return
|
||||
}
|
||||
grp := ctx.Event.GroupID
|
||||
if grp == 0 {
|
||||
// 个人用户
|
||||
grp = -ctx.Event.UserID
|
||||
}
|
||||
service.Reset(grp)
|
||||
ctx.SendChain(message.Text("已还原服务的默认启用状态: " + model.Args))
|
||||
})
|
||||
|
||||
zero.OnCommandGroup([]string{"用法", "usage"}, zero.AdminPermission, zero.OnlyGroup).
|
||||
zero.OnCommandGroup([]string{"用法", "usage"}, userOrGrpAdmin).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
model := extension.CommandModel{}
|
||||
_ = ctx.Parse(&model)
|
||||
service, ok := lookup(model.Args)
|
||||
service, ok := Lookup(model.Args)
|
||||
if !ok {
|
||||
ctx.Send("没有找到指定服务!")
|
||||
ctx.SendChain(message.Text("没有找到指定服务!"))
|
||||
return
|
||||
}
|
||||
if service.options.Help != "" {
|
||||
ctx.Send(service.options.Help)
|
||||
ctx.SendChain(message.Text(service.options.Help))
|
||||
} else {
|
||||
ctx.Send("该服务无帮助!")
|
||||
ctx.SendChain(message.Text("该服务无帮助!"))
|
||||
}
|
||||
})
|
||||
|
||||
zero.OnCommandGroup([]string{"服务列表", "service_list"}, zero.AdminPermission, zero.OnlyGroup).
|
||||
zero.OnCommandGroup([]string{"服务列表", "service_list"}, userOrGrpAdmin).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
msg := `---服务列表---`
|
||||
msg := "--------服务列表--------\n发送\"/用法 name\"查看详情"
|
||||
i := 0
|
||||
forEach(func(key string, manager *Control) bool {
|
||||
gid := ctx.Event.GroupID
|
||||
ForEach(func(key string, manager *Control) bool {
|
||||
i++
|
||||
msg += "\n" + strconv.Itoa(i) + `: `
|
||||
if manager.isEnabledIn(ctx.Event.GroupID) {
|
||||
if manager.IsEnabledIn(gid) {
|
||||
msg += "●" + key
|
||||
} else {
|
||||
msg += "○" + key
|
||||
}
|
||||
return true
|
||||
})
|
||||
ctx.Send(message.Text(msg))
|
||||
ctx.SendChain(message.Text(msg))
|
||||
})
|
||||
|
||||
zero.OnCommandGroup([]string{"服务详情", "service_detail"}, userOrGrpAdmin, zero.OnlyGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
var m message.Message
|
||||
m = append(m,
|
||||
message.CustomNode(
|
||||
zero.BotConfig.NickName[0],
|
||||
ctx.Event.SelfID,
|
||||
"---服务详情---",
|
||||
))
|
||||
i := 0
|
||||
ForEach(func(key string, manager *Control) bool {
|
||||
service, _ := Lookup(key)
|
||||
help := service.options.Help
|
||||
i++
|
||||
msg := strconv.Itoa(i) + `: `
|
||||
if manager.IsEnabledIn(ctx.Event.GroupID) {
|
||||
msg += "●" + key
|
||||
} else {
|
||||
msg += "○" + key
|
||||
}
|
||||
msg += "\n" + help
|
||||
m = append(m,
|
||||
message.CustomNode(
|
||||
zero.BotConfig.NickName[0],
|
||||
ctx.Event.SelfID,
|
||||
msg,
|
||||
))
|
||||
return true
|
||||
})
|
||||
|
||||
if id := ctx.SendGroupForwardMessage(
|
||||
ctx.Event.GroupID,
|
||||
m,
|
||||
).Get("message_id").Int(); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
package control
|
||||
// Package webctrl 包含 webui 所需的所有内容
|
||||
package webctrl
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -7,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
manager "github.com/FloatTech/bot-manager"
|
||||
// 依赖gin监听server
|
||||
@@ -17,6 +19,8 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
ctrl "github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -26,6 +30,8 @@ var (
|
||||
logConn *websocket.Conn
|
||||
|
||||
l logWriter
|
||||
// 存储请求事件,flag作为键,一个request对象作为值
|
||||
requestData sync.Map
|
||||
)
|
||||
|
||||
// logWriter
|
||||
@@ -34,13 +40,27 @@ var (
|
||||
type logWriter struct {
|
||||
}
|
||||
|
||||
// request
|
||||
// @Description: 一个请求事件的结构体
|
||||
//
|
||||
type request struct {
|
||||
RequestType string `json:"request_type"`
|
||||
SubType string `json:"sub_type"`
|
||||
Type string `json:"type"`
|
||||
Comment string `json:"comment"`
|
||||
GroupID int64 `json:"group_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Flag string `json:"flag"`
|
||||
SelfID int64 `json:"self_id"`
|
||||
}
|
||||
|
||||
// InitGui 初始化gui
|
||||
func InitGui() {
|
||||
func InitGui(addr string) {
|
||||
// 将日志重定向到前端hook
|
||||
writer := io.MultiWriter(l, os.Stderr)
|
||||
writer := io.MultiWriter(l, os.Stdout)
|
||||
log.SetOutput(writer)
|
||||
// 监听后端
|
||||
go controller()
|
||||
go controller(addr)
|
||||
// 注册消息handle
|
||||
messageHandle()
|
||||
}
|
||||
@@ -52,7 +72,7 @@ var upGrader = websocket.Upgrader{
|
||||
},
|
||||
}
|
||||
|
||||
func controller() {
|
||||
func controller(addr string) {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != nil {
|
||||
@@ -86,12 +106,16 @@ func controller() {
|
||||
// 获取插件列表
|
||||
engine.POST("/get_plugins", func(context *gin.Context) {
|
||||
var datas []map[string]interface{}
|
||||
forEach(func(key string, manager *Control) bool {
|
||||
datas = append(datas, map[string]interface{}{"id": 1, "handle_type": "", "name": key, "enable": manager.isEnabledIn(0)})
|
||||
ctrl.ForEach(func(key string, manager *ctrl.Control) bool {
|
||||
datas = append(datas, map[string]interface{}{"id": 1, "handle_type": "", "name": key, "enable": manager.IsEnabledIn(0)})
|
||||
return true
|
||||
})
|
||||
context.JSON(200, datas)
|
||||
})
|
||||
// 获取所有请求
|
||||
engine.POST("/get_requests", getRequests)
|
||||
// 执行一个请求事件
|
||||
engine.POST("handle_request", handelRequest)
|
||||
// 链接日志
|
||||
engine.GET("/get_log", getLogs)
|
||||
// 获取前端标签
|
||||
@@ -102,13 +126,48 @@ func controller() {
|
||||
// 发送信息
|
||||
engine.POST("/send_msg", sendMsg)
|
||||
engine.GET("/data", upgrade)
|
||||
log.Infoln("[gui] the webui is running http://127.0.0.1:3000")
|
||||
log.Infoln("[gui] the webui is running on", addr)
|
||||
log.Infoln("[gui] ", "you input the `ZeroBot-Plugin.exe -g` can disable the gui")
|
||||
if err := engine.Run("127.0.0.1:3000"); err != nil {
|
||||
if err := engine.Run(addr); err != nil {
|
||||
log.Debugln("[gui] ", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// handelRequest
|
||||
/**
|
||||
* @Description: 处理一个请求
|
||||
* @param context
|
||||
*/
|
||||
func handelRequest(context *gin.Context) {
|
||||
var data map[string]interface{}
|
||||
err := context.BindJSON(&data)
|
||||
if err != nil {
|
||||
context.JSON(404, nil)
|
||||
return
|
||||
}
|
||||
r, ok := requestData.LoadAndDelete(data["flag"].(string))
|
||||
if !ok {
|
||||
context.JSON(404, "flag not found")
|
||||
}
|
||||
r2 := r.(*request)
|
||||
r2.handle(data["approve"].(bool), data["reason"].(string))
|
||||
context.JSON(200, "操作成功")
|
||||
}
|
||||
|
||||
// getRequests
|
||||
/**
|
||||
* @Description: 获取所有的请求
|
||||
* @param context
|
||||
*/
|
||||
func getRequests(context *gin.Context) {
|
||||
var data []interface{}
|
||||
requestData.Range(func(key, value interface{}) bool {
|
||||
data = append(data, value)
|
||||
return true
|
||||
})
|
||||
context.JSON(200, data)
|
||||
}
|
||||
|
||||
// updateAllPluginStatus
|
||||
/**
|
||||
* @Description: 改变所有插件的状态
|
||||
@@ -126,23 +185,11 @@ func updateAllPluginStatus(context *gin.Context) {
|
||||
}
|
||||
enable = parse["enable"].(bool)
|
||||
}
|
||||
var groups []int64
|
||||
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
|
||||
for _, group := range ctx.GetGroupList().Array() {
|
||||
groups = append(groups, group.Get("group_id").Int())
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
forEach(func(key string, manager *Control) bool {
|
||||
ctrl.ForEach(func(key string, manager *ctrl.Control) bool {
|
||||
if enable {
|
||||
for _, group := range groups {
|
||||
manager.enable(group)
|
||||
}
|
||||
manager.Enable(0)
|
||||
} else {
|
||||
for _, group := range groups {
|
||||
manager.disable(group)
|
||||
}
|
||||
manager.Disable(0)
|
||||
}
|
||||
return true
|
||||
})
|
||||
@@ -168,23 +215,16 @@ func updatePluginAllGroupStatus(context *gin.Context) {
|
||||
name = parse["name"].(string)
|
||||
enable = parse["enable"].(bool)
|
||||
}
|
||||
control, b := lookup(name)
|
||||
control, b := ctrl.Lookup(name)
|
||||
if !b {
|
||||
context.JSON(404, nil)
|
||||
return
|
||||
}
|
||||
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
|
||||
for _, group := range ctx.GetGroupList().Array() {
|
||||
if enable {
|
||||
control.enable(group.Get("group_id").Int())
|
||||
} else {
|
||||
control.disable(group.Get("group_id").Int())
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
if enable {
|
||||
control.Enable(0)
|
||||
} else {
|
||||
control.Disable(0)
|
||||
}
|
||||
context.JSON(200, nil)
|
||||
}
|
||||
|
||||
@@ -205,15 +245,15 @@ func updatePluginStatus(context *gin.Context) {
|
||||
name := parse["name"].(string)
|
||||
enable := parse["enable"].(bool)
|
||||
fmt.Println(name)
|
||||
control, b := lookup(name)
|
||||
control, b := ctrl.Lookup(name)
|
||||
if !b {
|
||||
context.JSON(404, "服务不存在")
|
||||
return
|
||||
}
|
||||
if enable {
|
||||
control.enable(groupID)
|
||||
control.Enable(groupID)
|
||||
} else {
|
||||
control.disable(groupID)
|
||||
control.Disable(groupID)
|
||||
}
|
||||
context.JSON(200, nil)
|
||||
}
|
||||
@@ -237,12 +277,12 @@ func getPluginStatus(context *gin.Context) {
|
||||
groupID = int64(parse["group_id"].(float64))
|
||||
name = parse["name"].(string)
|
||||
}
|
||||
control, b := lookup(name)
|
||||
control, b := ctrl.Lookup(name)
|
||||
if !b {
|
||||
context.JSON(404, "服务不存在")
|
||||
return
|
||||
}
|
||||
context.JSON(200, gin.H{"enable": control.isEnabledIn(groupID)})
|
||||
context.JSON(200, gin.H{"enable": control.IsEnabledIn(groupID)})
|
||||
}
|
||||
|
||||
// getPluginsStatus
|
||||
@@ -263,8 +303,8 @@ func getPluginsStatus(context *gin.Context) {
|
||||
groupID = int64(parse["group_id"].(float64))
|
||||
}
|
||||
var datas []map[string]interface{}
|
||||
forEach(func(key string, manager *Control) bool {
|
||||
enable := manager.isEnabledIn(groupID)
|
||||
ctrl.ForEach(func(key string, manager *ctrl.Control) bool {
|
||||
enable := manager.IsEnabledIn(groupID)
|
||||
datas = append(datas, map[string]interface{}{"name": key, "enable": enable})
|
||||
return true
|
||||
})
|
||||
@@ -384,6 +424,31 @@ func messageHandle() {
|
||||
}
|
||||
}
|
||||
})
|
||||
// 直接注册一个request请求监听器,优先级设置为最高,设置不阻断事件传播
|
||||
zero.OnRequest(func(ctx *zero.Ctx) bool {
|
||||
if ctx.Event.RequestType == "friend" {
|
||||
ctx.State["type_name"] = "好友添加"
|
||||
} else {
|
||||
if ctx.Event.SubType == "add" {
|
||||
ctx.State["type_name"] = "加群请求"
|
||||
} else {
|
||||
ctx.State["type_name"] = "群邀请"
|
||||
}
|
||||
}
|
||||
return true
|
||||
}).SetBlock(false).FirstPriority().Handle(func(ctx *zero.Ctx) {
|
||||
r := &request{
|
||||
RequestType: ctx.Event.RequestType,
|
||||
SubType: ctx.Event.SubType,
|
||||
Type: ctx.State["type_name"].(string),
|
||||
GroupID: ctx.Event.GroupID,
|
||||
UserID: ctx.Event.UserID,
|
||||
Flag: ctx.Event.Flag,
|
||||
Comment: ctx.Event.Comment,
|
||||
SelfID: ctx.Event.SelfID,
|
||||
}
|
||||
requestData.Store(ctx.Event.Flag, r)
|
||||
})
|
||||
}
|
||||
|
||||
// upgrade
|
||||
@@ -468,6 +533,23 @@ func cors() gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// handle
|
||||
/**
|
||||
* @Description: 提交一个请求
|
||||
* @receiver r
|
||||
* @param approve 是否通过
|
||||
* @param reason 拒绝的理由
|
||||
*/
|
||||
func (r *request) handle(approve bool, reason string) {
|
||||
bot := zero.GetBot(r.SelfID)
|
||||
if r.RequestType == "friend" {
|
||||
bot.SetFriendAddRequest(r.Flag, approve, "")
|
||||
} else {
|
||||
bot.SetGroupAddRequest(r.Flag, r.SubType, approve, reason)
|
||||
}
|
||||
log.Debugln("[gui] ", "已处理", r.UserID, "的"+r.Type)
|
||||
}
|
||||
|
||||
func (l logWriter) Write(p []byte) (n int, err error) {
|
||||
if logConn != nil {
|
||||
err := logConn.WriteMessage(websocket.TextMessage, p)
|
||||
BIN
data/BookReview/bookreview.db
Normal file
BIN
data/BookReview/bookreview.db
Normal file
Binary file not shown.
BIN
data/Diana/text.db
Normal file
BIN
data/Diana/text.db
Normal file
Binary file not shown.
BIN
data/Omikuji/kuji.db
Normal file
BIN
data/Omikuji/kuji.db
Normal file
Binary file not shown.
BIN
data/VtbQuotation/vtb.db
Normal file
BIN
data/VtbQuotation/vtb.db
Normal file
Binary file not shown.
31
go.mod
31
go.mod
@@ -3,20 +3,27 @@ module github.com/FloatTech/ZeroBot-Plugin
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/FloatTech/AnimeAPI v1.1.9
|
||||
github.com/FloatTech/ZeroBot-Plugin-Gif v0.2.3
|
||||
github.com/FloatTech/ZeroBot-Plugin-Timer v1.4.3
|
||||
github.com/FloatTech/bot-manager v1.0.0
|
||||
github.com/FloatTech/AnimeAPI v1.1.11
|
||||
github.com/FloatTech/ZeroBot-Plugin-Gif v0.2.4
|
||||
github.com/FloatTech/bot-manager v1.0.1-0.20211112011524-85b9895271ed
|
||||
github.com/antchfx/htmlquery v1.2.3
|
||||
github.com/corona10/goimagehash v1.0.3
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/gin-gonic/gin v1.7.4
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/fumiama/cron v1.3.0
|
||||
github.com/fumiama/go-base16384 v1.2.1
|
||||
github.com/fumiama/go-registry v0.0.2
|
||||
github.com/fumiama/gofastTEA v0.0.6
|
||||
github.com/fumiama/gotracemoe v0.0.3
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/imroc/req v0.3.0
|
||||
github.com/jinzhu/gorm v1.9.16
|
||||
github.com/logoove/sqlite v1.13.0
|
||||
github.com/mroth/weightedrand v0.4.1
|
||||
github.com/robfig/cron v1.2.0
|
||||
github.com/shirou/gopsutil v3.21.8+incompatible
|
||||
github.com/shirou/gopsutil/v3 v3.21.11
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/tidwall/gjson v1.9.0
|
||||
github.com/wdvxdr1123/ZeroBot v1.2.4
|
||||
modernc.org/sqlite v1.13.0
|
||||
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816
|
||||
github.com/tidwall/gjson v1.12.1
|
||||
github.com/wdvxdr1123/ZeroBot v1.4.1
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
|
||||
golang.org/x/text v0.3.6
|
||||
)
|
||||
|
||||
113
go.sum
113
go.sum
@@ -1,38 +1,58 @@
|
||||
github.com/FloatTech/AnimeAPI v1.1.9 h1:H1hZmgwZPNHdx39K9JvY3awT8TTsCl9kKA1uVMyCjRg=
|
||||
github.com/FloatTech/AnimeAPI v1.1.9/go.mod h1:CC+vF30UGBlcIUxwFOcXIEHoJ4r7c5x2iLQsnUCVdDI=
|
||||
github.com/FloatTech/AnimeAPI v1.1.11 h1:uuV4v5qweh0mI0E2KMiG5XGt0pKboV/EFAlIfSJxIi8=
|
||||
github.com/FloatTech/AnimeAPI v1.1.11/go.mod h1:CC+vF30UGBlcIUxwFOcXIEHoJ4r7c5x2iLQsnUCVdDI=
|
||||
github.com/FloatTech/ZeroBot-Plugin v1.1.5/go.mod h1:kWuUARvU7gs4xLggi8Sy37ja2GRL6k0X6kewe5TiZRs=
|
||||
github.com/FloatTech/ZeroBot-Plugin-Gif v0.2.3 h1:TGQHhiPR/XIxYYoB6IxOzgMhsZs2tDYQlaJHT04gkQ8=
|
||||
github.com/FloatTech/ZeroBot-Plugin-Gif v0.2.3/go.mod h1:W7ag6hml1pZTNzRXKU74OMr6rS8awQKSU+o2g7Gj4O0=
|
||||
github.com/FloatTech/ZeroBot-Plugin-Timer v1.4.3 h1:jn/dH+OwPSRozkmeCeQQPrAGJRBudcm3OK5tXhGItRk=
|
||||
github.com/FloatTech/ZeroBot-Plugin-Gif v0.2.4 h1:WW0BmmLLqAg+m6qGkrKbsfSIm91fkj3/udt3R7Myodo=
|
||||
github.com/FloatTech/ZeroBot-Plugin-Gif v0.2.4/go.mod h1:W7ag6hml1pZTNzRXKU74OMr6rS8awQKSU+o2g7Gj4O0=
|
||||
github.com/FloatTech/ZeroBot-Plugin-Timer v1.4.3/go.mod h1:MVOQQ4e6AVGFm993blXXU4Sd6bAsLY2+Zb+/HMrEeEc=
|
||||
github.com/FloatTech/bot-manager v1.0.0 h1:d63J5htLhVBc2ITG09WBJI+qAB0ubPjYhfXl6hljBNk=
|
||||
github.com/FloatTech/bot-manager v1.0.0/go.mod h1:8YYRJ16oroGHQGD2En0oVnmcKJkxR9O/jd5BPSfWfOQ=
|
||||
github.com/FloatTech/bot-manager v1.0.1-0.20211112011524-85b9895271ed h1:GEOgDVbvaxXqZxgWE/y5JOlbMXrmq7n0M+m9g3md2To=
|
||||
github.com/FloatTech/bot-manager v1.0.1-0.20211112011524-85b9895271ed/go.mod h1:8YYRJ16oroGHQGD2En0oVnmcKJkxR9O/jd5BPSfWfOQ=
|
||||
github.com/FloatTech/imgfactory v0.1.1 h1:ooL2+fV8yrMhv1ShGGKsN0Rm/flWoKnvqXaUD+dC3DQ=
|
||||
github.com/FloatTech/imgfactory v0.1.1/go.mod h1:ThDALab8aOuU6KVYESVWFqmjcqtm03e0SvGlTw6s+aw=
|
||||
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
|
||||
github.com/Mrs4s/MiraiGo v0.0.0-20211120033824-43b23f4e6fcb h1:Rkj28fqIwGx/EgBzRYtpmJRfH6wqVn7cNdc7aJ0QE4M=
|
||||
github.com/Mrs4s/MiraiGo v0.0.0-20211120033824-43b23f4e6fcb/go.mod h1:imVKbfKqqeit+C/eaWGb4MKQ3z3gN6pRpBU5RMtp5so=
|
||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/antchfx/htmlquery v1.2.3 h1:sP3NFDneHx2stfNXCKbhHFo8XgNjCACnU/4AO5gWz6M=
|
||||
github.com/antchfx/htmlquery v1.2.3/go.mod h1:B0ABL+F5irhhMWg54ymEZinzMSi0Kt3I2if0BLYa3V0=
|
||||
github.com/antchfx/xpath v1.1.6 h1:6sVh6hB5T6phw1pFpHRQ+C4bd8sNI+O58flqtg7h0R0=
|
||||
github.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
|
||||
github.com/corona10/goimagehash v1.0.3 h1:NZM518aKLmoNluluhfHGxT3LGOnrojrxhGn63DR/CZA=
|
||||
github.com/corona10/goimagehash v1.0.3/go.mod h1:VkvE0mLn84L4aF8vCb6mafVajEb6QYMHl2ZJLn0mOGI=
|
||||
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/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4 h1:BBade+JlV/f7JstZ4pitd4tHhpN+w+6I+LyOS7B4fyU=
|
||||
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4/go.mod h1:H7chHJglrhPPzetLdzBleF8d22WYOv7UM/lEKYiwlKM=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fumiama/cron v1.3.0 h1:ZWlwuexF+HQHl3cYytEE5HNwD99q+3vNZF1GrEiXCFo=
|
||||
github.com/fumiama/cron v1.3.0/go.mod h1:bz5Izvgi/xEUI8tlBN8BI2jr9Moo8N4or0KV8xXuPDY=
|
||||
github.com/fumiama/go-base16384 v1.2.1 h1:6OGprW8g/95m2ocmryHi8mipZ7bx9StFMZDKEqLvMiA=
|
||||
github.com/fumiama/go-base16384 v1.2.1/go.mod h1:1HTC0QFL7BjS0DuO5Qm+fBYKQkHqmAapLbRpCxrhPXQ=
|
||||
github.com/fumiama/go-registry v0.0.2 h1:2EoZwZpqI7YhkQ1FnuAPvALYPpvUtbsCqk879+r7ehs=
|
||||
github.com/fumiama/go-registry v0.0.2/go.mod h1:QkcmmHuw1y6y/w7/HiH1c9yjBw5Zt+6EER6YJKl9xh8=
|
||||
github.com/fumiama/gofastTEA v0.0.6 h1:Yni3MXDbJVa/c4CecgdZDgCJK+fLdvGph+OBqY2mtiI=
|
||||
github.com/fumiama/gofastTEA v0.0.6/go.mod h1:+sBZ05nCA2skZkursHNvyr8kULlEetrYTM2y5kA4rQc=
|
||||
github.com/fumiama/gotracemoe v0.0.3 h1:iI5EbE9A3UUbfukG6+/soYPjp1S31eCNYf4tw7s6/Jc=
|
||||
github.com/fumiama/gotracemoe v0.0.3/go.mod h1:tyqahdUzHf0bQIAVY/GYmDWvYYe5ik1ZbhnGYh+zl40=
|
||||
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.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM=
|
||||
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
||||
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
|
||||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
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=
|
||||
@@ -41,6 +61,10 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87
|
||||
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/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
@@ -52,24 +76,38 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
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/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imroc/req v0.3.0 h1:3EioagmlSG+z+KySToa+Ylo3pTFZs+jh3Brl7ngU12U=
|
||||
github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw=
|
||||
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
|
||||
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
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/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/logoove/sqlite v1.13.0 h1:XM7QKK9R3tm8o7bI75R3zmwYBFQ5S3Jqg+XCaqsAMQQ=
|
||||
github.com/logoove/sqlite v1.13.0/go.mod h1:MRpE/o3qQhT7AgfIdnBue5c63+//xT+KXV0gHeVAUAg=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||
github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
|
||||
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
@@ -79,17 +117,24 @@ github.com/modern-go/reflect2 v1.0.2-0.20210109003243-333559e1834b h1:6Xjqolv/0D
|
||||
github.com/modern-go/reflect2 v1.0.2-0.20210109003243-333559e1834b/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mroth/weightedrand v0.4.1 h1:rHcbUBopmi/3x4nnrvwGJBhX9d0vk+KgoLUZeDP6YyI=
|
||||
github.com/mroth/weightedrand v0.4.1/go.mod h1:3p2SIcC8al1YMzGhAIoXD+r9olo/g/cdJgAD905gyNE=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
|
||||
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||
github.com/shirou/gopsutil v3.21.8+incompatible h1:sh0foI8tMRlCidUJR+KzqWYWxrkuuPIGiO6Vp+KXdCU=
|
||||
github.com/shirou/gopsutil v3.21.8+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil/v3 v3.21.11 h1:d5tOAP5+bmJ8Hf2+4bxOSkQ/64+sjEbjU9nSW9nJgG0=
|
||||
github.com/shirou/gopsutil/v3 v3.21.11/go.mod h1:BToYZVTlSVlfazpDDYFnsVZLaoRG+g8ufT6fPQLdJzA=
|
||||
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=
|
||||
@@ -101,16 +146,21 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
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/tdf1939/ZeroBot-Plugin-Gif v0.0.0-20210828060956-389b1dc33652/go.mod h1:bkxKi7un9gCDvUUZAiIJF6k90pyj8rmxiXLJkiHcsMY=
|
||||
github.com/tdf1939/img v0.0.0-20210827153520-90cb4e9580a3/go.mod h1:FgTEOcosTWrkOr7++gbtPSj1rX5loRWrf/AL+hm3Cnw=
|
||||
github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
|
||||
github.com/tidwall/gjson v1.9.0 h1:+Od7AE26jAaMgVC31cQV/Ope5iKXulNMflrlB7k+F9E=
|
||||
github.com/tidwall/gjson v1.9.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
|
||||
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
|
||||
github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo=
|
||||
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8=
|
||||
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.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
|
||||
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
|
||||
github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
|
||||
@@ -121,27 +171,39 @@ 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=
|
||||
github.com/wdvxdr1123/ZeroBot v1.2.2/go.mod h1:83nHtG8V5TAxPwH/LCDxLpZk4khIgs29dkr5TBWf7fc=
|
||||
github.com/wdvxdr1123/ZeroBot v1.2.3/go.mod h1:83nHtG8V5TAxPwH/LCDxLpZk4khIgs29dkr5TBWf7fc=
|
||||
github.com/wdvxdr1123/ZeroBot v1.2.4 h1:eC/41Tlkj1jxXW91x4K+qjUlaQXKGgUeLhxw7C6+qkM=
|
||||
github.com/wdvxdr1123/ZeroBot v1.2.4/go.mod h1:83nHtG8V5TAxPwH/LCDxLpZk4khIgs29dkr5TBWf7fc=
|
||||
github.com/wdvxdr1123/ZeroBot v1.3.2/go.mod h1:i2DIqQjtjE+3gvVi9r9sc+QpNaUuyTXx/HNXXayIpwI=
|
||||
github.com/wdvxdr1123/ZeroBot v1.4.1 h1:fk/8RH2D1gB3YeC1eI/SZi/kG31Rh7Z8lAiDc60VZFM=
|
||||
github.com/wdvxdr1123/ZeroBot v1.4.1/go.mod h1:7t9m4vDZPwWAmzKlhP6IvUoisOIiqNdm/3AJgiY3+ew=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
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-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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=
|
||||
@@ -151,10 +213,15 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b h1:S7hKs0Flbq0bbc9xgYt4stIEG1zNDFqyrPwAX2Wj/sE=
|
||||
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c h1:taxlMj0D/1sOAuv/CbSD+MMDof2vbyPTqz5FNYKpXt8=
|
||||
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/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=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -170,8 +237,9 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
@@ -195,7 +263,6 @@ modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBM
|
||||
modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
|
||||
modernc.org/ccgo/v3 v3.11.2 h1:gqa8PQ2v7SjrhHCgxUO5dzoAJWSLAveJqZTNkPCN0kc=
|
||||
modernc.org/ccgo/v3 v3.11.2/go.mod h1:6kii3AptTDI+nUrM9RFBoIEUEisSWCbdczD9ZwQH2FE=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
|
||||
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
|
||||
@@ -217,9 +284,7 @@ modernc.org/sqlite v1.13.0 h1:cwhUj0jTBgPjk/demWheV+T6xi6ifTfsGIFKFq0g3Ck=
|
||||
modernc.org/sqlite v1.13.0/go.mod h1:2qO/6jZJrcQaxFUHxOwa6Q6WfiGSsiVj6GXX0Ker+Jg=
|
||||
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
|
||||
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
|
||||
modernc.org/tcl v1.5.9 h1:DZMfR+RDJRhcrmMEMTJgVIX+Wf5qhfVX0llI0rsc20w=
|
||||
modernc.org/tcl v1.5.9/go.mod h1:bcwjvBJ2u0exY6K35eAmxXBBij5kXb1dHlAWmfhqThE=
|
||||
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
|
||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.1.2 h1:IjjzDsIFbl0wuF2KfwvdyUAJVwxD4iwZ6akLNiDoClM=
|
||||
modernc.org/z v1.1.2/go.mod h1:sj9T1AGBG0dm6SCVzldPOHWrif6XBpooJtbttMn1+Js=
|
||||
|
||||
164
main.go
164
main.go
@@ -3,109 +3,157 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
// 注:以下插件均可通过前面加 // 注释,注释后停用并不加载插件
|
||||
// 下列插件可与 wdvxdr1123/ZeroBot v1.1.2 以上配合单独使用
|
||||
// 词库类
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
// 插件控制
|
||||
// webctrl "github.com/FloatTech/ZeroBot-Plugin/control/web" // web 后端控制
|
||||
|
||||
// 词库类
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_atri" // ATRI词库
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_chat" // 基础词库
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_qingyunke" // 青云客
|
||||
|
||||
// 实用类
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_github" // 搜索GitHub仓库
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_manager" // 群管
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_nbnhhsh" // 拼音首字母缩写释义工具
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_runcode" // 在线运行代码
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_b14" // base16384加解密
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_github" // 搜索GitHub仓库
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_manager" // 群管
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_nbnhhsh" // 拼音首字母缩写释义工具
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_runcode" // 在线运行代码
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_sleep_manage" // 统计睡眠时间
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_translation" // 翻译
|
||||
|
||||
// 娱乐类
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin-Gif" // 制图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_ai_false" // 服务器监控
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_choose" // 选择困难症帮手
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_fortune" // 运势
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_hs" // 炉石
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_minecraft" // MCSManager
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_music" // 点歌
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_omikuji" // 浅草寺求签
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_reborn" // 投胎
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_shindan" // 测定
|
||||
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin_wtf" // 鬼东西
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin-Gif" // 制图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_ai_false" // 服务器监控
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_book_review" // 哀伤雪刃吧推书记录
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_choose" // 选择困难症帮手
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_coser" // 三次元小姐姐
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_fortune" // 运势
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_hs" // 炉石
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_minecraft" // MCSManager
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_moyu" // 摸鱼
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_music" // 点歌
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_novel" // 铅笔小说网搜索
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_omikuji" // 浅草寺求签
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_reborn" // 投胎
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_shindan" // 测定
|
||||
|
||||
// b站相关
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_bilibili" // 查询b站用户信息
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_diana" // 嘉心糖发病
|
||||
|
||||
// 二次元图片
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_acgimage" // 随机图片与AI点评
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_aiwife" // 随机老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_image_finder" // 关键字搜图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_lolicon" // lolicon 随机图片
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_saucenao" // 以图搜图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_setutime" // 来份涩图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_acgimage" // 随机图片与AI点评
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_aiwife" // 随机老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_image_finder" // 关键字搜图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_lolicon" // lolicon 随机图片
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_nativesetu" // 本地涩图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_nativewife" // 本地老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_saucenao" // 以图搜图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_setutime" // 来份涩图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_tracemoe" // 搜番
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_vtb_quotation" // vtb语录
|
||||
|
||||
// 以下为内置依赖,勿动
|
||||
|
||||
"github.com/fumiama/go-registry"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/driver"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
var (
|
||||
contents = []string{
|
||||
"* OneBot + ZeroBot + Golang",
|
||||
"* Version 1.1.6 - 2021-09-27 21:30:33 +0800 CST",
|
||||
"* Copyright © 2020 - 2021 Kanri, DawnNights, Fumiama, Suika",
|
||||
"* Version 1.2.2 - 2021-12-13 21:22:45 +0800 CST",
|
||||
"* Copyright © 2020 - 2021 FloatTech. All Rights Reserved.",
|
||||
"* Project: https://github.com/FloatTech/ZeroBot-Plugin",
|
||||
}
|
||||
banner = strings.Join(contents, "\n")
|
||||
token *string
|
||||
url *string
|
||||
reg = registry.NewRegReader("reilia.eastasia.azurecontainer.io:32664", "fumiama")
|
||||
)
|
||||
|
||||
func init() {
|
||||
var en bool
|
||||
var debg bool
|
||||
// 解析命令行参数,输入 `-g` 即可启用 gui
|
||||
flag.BoolVar(&en, "g", false, "Enable web gui.")
|
||||
// 解析命令行参数,输入 `-d` 即可开启 debug log
|
||||
flag.BoolVar(&debg, "d", false, "Enable debug log.")
|
||||
// 解析命令行参数
|
||||
d := flag.Bool("d", false, "Enable debug level log and higher.")
|
||||
w := flag.Bool("w", false, "Enable warning level log and higher.")
|
||||
h := flag.Bool("h", false, "Display this help.")
|
||||
// 解析命令行参数,输入 `-g 监听地址:端口` 指定 gui 访问地址,默认 127.0.0.1:3000
|
||||
// g := flag.String("g", "127.0.0.1:3000", "Set web gui listening address.")
|
||||
|
||||
// 直接写死 AccessToken 时,请更改下面第二个参数
|
||||
token = flag.String("t", "", "Set AccessToken of WSClient.")
|
||||
// 直接写死 URL 时,请更改下面第二个参数
|
||||
url = flag.String("u", "ws://127.0.0.1:6700", "Set Url of WSClient.")
|
||||
|
||||
flag.Parse()
|
||||
if en {
|
||||
control.InitGui()
|
||||
}
|
||||
if debg {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
if *h {
|
||||
printBanner()
|
||||
fmt.Println("Usage:")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(0)
|
||||
} else {
|
||||
if *d && !*w {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
if *w {
|
||||
logrus.SetLevel(logrus.WarnLevel)
|
||||
}
|
||||
}
|
||||
// 启用 gui
|
||||
// webctrl.InitGui(*g)
|
||||
}
|
||||
|
||||
func main() {
|
||||
func printBanner() {
|
||||
fmt.Print(
|
||||
"\n======================[ZeroBot-Plugin]======================",
|
||||
"\n", banner, "\n",
|
||||
"----------------------[ZeroBot-公告栏]----------------------",
|
||||
"\n", getKanban(), "\n",
|
||||
"============================================================\n",
|
||||
) // 启动打印
|
||||
zero.Run(zero.Config{
|
||||
NickName: []string{"椛椛", "ATRI", "atri", "亚托莉", "アトリ"},
|
||||
CommandPrefix: "/",
|
||||
)
|
||||
}
|
||||
|
||||
// SuperUsers 某些功能需要主人权限,可通过以下两种方式修改
|
||||
// []string{}:通过代码写死的方式添加主人账号
|
||||
// flag.Args():通过命令行参数的方式添加主人账号
|
||||
SuperUsers: append([]string{"12345678", "87654321"}, flag.Args()...),
|
||||
|
||||
Driver: []zero.Driver{
|
||||
&driver.WSClient{
|
||||
// OneBot 正向WS 默认使用 6700 端口
|
||||
Url: "ws://127.0.0.1:6700",
|
||||
AccessToken: "",
|
||||
},
|
||||
},
|
||||
})
|
||||
func getKanban() string {
|
||||
err := reg.Connect()
|
||||
defer reg.Close()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
text, err := reg.Get("ZeroBot-Plugin/kanban")
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
func main() {
|
||||
printBanner()
|
||||
// 帮助
|
||||
zero.OnFullMatchGroup([]string{"/help", ".help", "菜单"}, zero.OnlyToMe).SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.Send(banner)
|
||||
ctx.SendChain(message.Text(banner, "\n可发送\"/服务列表\"查看 bot 功能"))
|
||||
})
|
||||
select {}
|
||||
zero.OnFullMatch("查看zbp公告", zero.OnlyToMe, zero.AdminPermission).SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text(getKanban()))
|
||||
})
|
||||
zero.RunAndBlock(
|
||||
zero.Config{
|
||||
NickName: []string{"椛椛", "ATRI", "atri", "亚托莉", "アトリ"},
|
||||
CommandPrefix: "/",
|
||||
// SuperUsers 某些功能需要主人权限,可通过以下两种方式修改
|
||||
// "12345678", "87654321":通过代码写死的方式添加主人账号
|
||||
// flag.Args():通过命令行参数的方式添加主人账号,无需修改下方任何代码
|
||||
SuperUsers: append([]string{"12345678", "87654321"}, flag.Args()...),
|
||||
Driver: []zero.Driver{driver.NewWebSocketClient(*url, *token)},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
16
main_win.go
Normal file
16
main_win.go
Normal file
@@ -0,0 +1,16 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
easy "github.com/t-tomalak/logrus-easy-formatter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// windows 不支持带颜色的 log,故自定义格式
|
||||
logrus.SetFormatter(&easy.Formatter{
|
||||
LogFormat: "[%lvl%] %msg%\n",
|
||||
})
|
||||
}
|
||||
@@ -3,16 +3,18 @@ package acgimage
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/classify"
|
||||
"github.com/FloatTech/AnimeAPI/picture"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/rate"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -21,13 +23,13 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
botpath, _ = os.Getwd()
|
||||
datapath = botpath + "/data/acgimage/"
|
||||
cacheuri = "file:///" + datapath + "cache"
|
||||
datapath = file.BOTPATH + "/data/acgimage/"
|
||||
cacheuri = "file:///" + datapath + "cache"
|
||||
// r18有一定保护,一般不会发出图片
|
||||
randapi = "&loli=true&r18=true"
|
||||
msgof = make(map[int64]int64)
|
||||
block = false
|
||||
limit = rate.NewManager(time.Minute, 5)
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
@@ -46,29 +48,29 @@ func init() { // 插件主体
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
url := ctx.State["regex_matched"].([]string)[1]
|
||||
if !strings.HasPrefix(url, "http") {
|
||||
ctx.Send("URL非法!")
|
||||
ctx.SendChain(message.Text("URL非法!"))
|
||||
} else {
|
||||
randapi = url
|
||||
ctx.Send("设置好啦")
|
||||
ctx.SendChain(message.Text("设置好啦"))
|
||||
}
|
||||
})
|
||||
// 有保护的随机图片
|
||||
engine.OnFullMatch("随机图片", zero.OnlyGroup).SetBlock(true).SetPriority(24).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if classify.CanVisit(5) {
|
||||
if classify.CanVisit(5) && limit.Load(ctx.Event.UserID).Acquire() {
|
||||
go func() {
|
||||
class, lastvisit, dhash, comment := classify.Classify(randapi, false)
|
||||
replyClass(ctx, dhash, class, false, lastvisit, comment)
|
||||
}()
|
||||
} else {
|
||||
ctx.Send("你太快啦!")
|
||||
ctx.SendChain(message.Text("你太快啦!"))
|
||||
}
|
||||
})
|
||||
// 直接随机图片,无r18保护,后果自负。如果出r18图可尽快通过发送"太涩了"撤回
|
||||
engine.OnFullMatch("直接随机", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(24).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if block {
|
||||
ctx.Send("请稍后再试哦")
|
||||
ctx.SendChain(message.Text("请稍后再试哦"))
|
||||
} else if randapi != "" {
|
||||
block = true
|
||||
var url string
|
||||
@@ -77,7 +79,7 @@ func init() { // 插件主体
|
||||
} else {
|
||||
url = randapi
|
||||
}
|
||||
setLastMsg(ctx.Event.GroupID, ctx.Send(message.Image(url).Add("cache", "0")))
|
||||
setLastMsg(ctx.Event.GroupID, ctx.SendChain(message.Image(url).Add("cache", "0")))
|
||||
block = false
|
||||
}
|
||||
})
|
||||
@@ -89,7 +91,7 @@ func init() { // 插件主体
|
||||
// 上传一张图进行评价
|
||||
engine.OnKeywordGroup([]string{"评价图片"}, zero.OnlyGroup, picture.CmdMatch, picture.MustGiven).SetBlock(true).SetPriority(24).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.Send("少女祈祷中...")
|
||||
ctx.SendChain(message.Text("少女祈祷中..."))
|
||||
for _, url := range ctx.State["image_url"].([]string) {
|
||||
go func(target string) {
|
||||
class, lastvisit, dhash, comment := classify.Classify(target, true)
|
||||
@@ -101,7 +103,7 @@ func init() { // 插件主体
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
dhash := ctx.State["regex_matched"].([]string)[1]
|
||||
if len(dhash) == 5*3 {
|
||||
ctx.Send(message.Image(apihead + dhash))
|
||||
ctx.SendChain(message.Image(apihead + dhash))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -124,19 +126,18 @@ func replyClass(ctx *zero.Ctx, dhash string, class int, noimg bool, lv int64, co
|
||||
if dhash != "" && !noimg {
|
||||
b14, err3 := url.QueryUnescape(dhash)
|
||||
if err3 == nil {
|
||||
ctx.Send(comment + "\n给你点提示哦:" + b14)
|
||||
ctx.SendChain(message.Text(comment + "\n给你点提示哦:" + b14))
|
||||
ctx.Event.GroupID = 0
|
||||
ctx.Send(img)
|
||||
ctx.SendChain(img)
|
||||
}
|
||||
} else {
|
||||
ctx.Send(comment)
|
||||
ctx.SendChain(message.Text(comment))
|
||||
}
|
||||
} else {
|
||||
comment := message.Text(comment)
|
||||
if !noimg {
|
||||
ctx.SendChain(img, comment)
|
||||
ctx.SendChain(img, message.Text(comment))
|
||||
} else {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), comment)
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(comment))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
package aifalse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
|
||||
@@ -20,14 +21,14 @@ func init() { // 插件主体
|
||||
engine := control.Register("aifalse", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "AIfalse\n" +
|
||||
"- 查询计算机当前活跃度[检查身体|自检|启动自检|系统状态",
|
||||
"- 查询计算机当前活跃度: [检查身体|自检|启动自检|系统状态]",
|
||||
})
|
||||
engine.OnFullMatchGroup([]string{"检查身体", "自检", "启动自检", "系统状态"}, zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text(
|
||||
"* CPU占用率: ", cpuPercent(), "%\n",
|
||||
"* RAM占用率: ", memPercent(), "%\n",
|
||||
"* 硬盘活动率: ", diskPercent(), "%",
|
||||
"* CPU占用: ", cpuPercent(), "%\n",
|
||||
"* RAM占用: ", memPercent(), "%\n",
|
||||
"* 硬盘使用: ", diskPercent(),
|
||||
),
|
||||
)
|
||||
})
|
||||
@@ -35,9 +36,9 @@ func init() { // 插件主体
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
err := os.RemoveAll("data/cache/*")
|
||||
if err != nil {
|
||||
ctx.Send("错误: " + err.Error())
|
||||
ctx.SendChain(message.Text("错误: ", err.Error()))
|
||||
} else {
|
||||
ctx.Send("成功!")
|
||||
ctx.SendChain(message.Text("成功!"))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -52,8 +53,15 @@ func memPercent() float64 {
|
||||
return math.Round(memInfo.UsedPercent)
|
||||
}
|
||||
|
||||
func diskPercent() float64 {
|
||||
func diskPercent() string {
|
||||
parts, _ := disk.Partitions(true)
|
||||
diskInfo, _ := disk.Usage(parts[0].Mountpoint)
|
||||
return math.Round(diskInfo.UsedPercent)
|
||||
msg := ""
|
||||
for _, p := range parts {
|
||||
diskInfo, _ := disk.Usage(p.Mountpoint)
|
||||
pc := uint(math.Round(diskInfo.UsedPercent))
|
||||
if pc > 0 {
|
||||
msg += fmt.Sprintf("\n - %s(%dM) %d%%", p.Mountpoint, diskInfo.Total/1024/1024, pc)
|
||||
}
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
@@ -11,33 +11,51 @@ import (
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
|
||||
)
|
||||
|
||||
var (
|
||||
const (
|
||||
// 服务名
|
||||
servicename = "atri"
|
||||
// ATRI 所有命令的优先级
|
||||
prio = 2
|
||||
prio = 5
|
||||
// ATRI 表情的 codechina 镜像
|
||||
res = "https://codechina.csdn.net/u011570312/ZeroBot-Plugin/-/raw/master/plugin_atri/"
|
||||
// ATRI 的总开关
|
||||
enable = true
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
engine := control.Register(servicename, &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "本插件基于 ATRI ,为 Golang 移植版\n" +
|
||||
"- ATRI醒醒\n- ATRI睡吧\n- 萝卜子\n- 喜欢|爱你|爱|suki|daisuki|すき|好き|贴贴|老婆|亲一个|mua\n" +
|
||||
"- 草你妈|操你妈|脑瘫|废柴|fw|废物|战斗|爬|爪巴|sb|SB|傻B\n- 早安|早哇|早上好|ohayo|哦哈哟|お早う|早好|早|早早早\n" +
|
||||
"- 中午好|午安|午好\n- 晚安|oyasuminasai|おやすみなさい|晚好|晚上好\n- 高性能|太棒了|すごい|sugoi|斯国一|よかった\n" +
|
||||
"- 没事|没关系|大丈夫|还好|不要紧|没出大问题|没伤到哪\n- 好吗|是吗|行不行|能不能|可不可以\n- 啊这\n- 我好了\n- ?|?|¿\n" +
|
||||
"- 离谱\n- 答应我",
|
||||
})
|
||||
zero.OnFullMatch("ATRI醒醒", zero.AdminPermission).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
enable = true
|
||||
time.Sleep(time.Second * 1)
|
||||
ctx.SendChain(randText("嗯呜呜……夏生先生……?"))
|
||||
c, ok := control.Lookup(servicename)
|
||||
if ok && !c.IsEnabledIn(ctx.Event.GroupID) {
|
||||
c.Enable(ctx.Event.GroupID)
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(message.Text("嗯呜呜……夏生先生……?"))
|
||||
}
|
||||
})
|
||||
zero.OnFullMatch("ATRI睡吧", zero.AdminPermission).SetBlock(true).SetPriority(prio).
|
||||
engine.OnFullMatch("ATRI睡吧", zero.AdminPermission).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
enable = false
|
||||
time.Sleep(time.Second * 1)
|
||||
ctx.SendChain(randText("Zzz……Zzz……"))
|
||||
c, ok := control.Lookup(servicename)
|
||||
if ok && c.IsEnabledIn(ctx.Event.GroupID) {
|
||||
c.Disable(ctx.Event.GroupID)
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(message.Text("Zzz……Zzz……"))
|
||||
}
|
||||
})
|
||||
zero.OnFullMatch("萝卜子", atriSwitch(), atriSleep()).SetBlock(true).SetPriority(prio).
|
||||
engine.OnFullMatch("萝卜子", atriSleep).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
time.Sleep(time.Second * 1)
|
||||
process.SleepAbout1sTo2s()
|
||||
switch rand.Intn(2) {
|
||||
case 0:
|
||||
ctx.SendChain(randText("萝卜子是对机器人的蔑称!", "是亚托莉......萝卜子可是对机器人的蔑称"))
|
||||
@@ -45,20 +63,20 @@ func init() { // 插件主体
|
||||
ctx.SendChain(randRecord("RocketPunch.amr"))
|
||||
}
|
||||
})
|
||||
zero.OnFullMatchGroup([]string{"喜欢", "爱你", "爱", "suki", "daisuki", "すき", "好き", "贴贴", "老婆", "亲一个", "mua"}, atriSwitch(), atriSleep(), zero.OnlyToMe).SetBlock(true).SetPriority(prio).
|
||||
engine.OnFullMatchGroup([]string{"喜欢", "爱你", "爱", "suki", "daisuki", "すき", "好き", "贴贴", "老婆", "亲一个", "mua"}, atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
time.Sleep(time.Second * 1)
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(randImage("SUKI.jpg", "SUKI1.jpg", "SUKI2.png"))
|
||||
})
|
||||
zero.OnKeywordGroup([]string{"草你妈", "操你妈", "脑瘫", "废柴", "fw", "five", "废物", "战斗", "爬", "爪巴", "sb", "SB", "傻B"}, atriSwitch(), atriSleep(), zero.OnlyToMe).SetBlock(true).SetPriority(prio - 1).
|
||||
engine.OnKeywordGroup([]string{"草你妈", "操你妈", "脑瘫", "废柴", "fw", "five", "废物", "战斗", "爬", "爪巴", "sb", "SB", "傻B"}, atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio - 1).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
time.Sleep(time.Second * 1)
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(randImage("FN.jpg", "WQ.jpg", "WQ1.jpg"))
|
||||
})
|
||||
zero.OnFullMatchGroup([]string{"早安", "早哇", "早上好", "ohayo", "哦哈哟", "お早う", "早好", "早"}, atriSwitch()).SetBlock(true).SetPriority(prio).
|
||||
engine.OnFullMatchGroup([]string{"早安", "早哇", "早上好", "ohayo", "哦哈哟", "お早う", "早好", "早", "早早早"}).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
now := time.Now().Hour()
|
||||
time.Sleep(time.Second * 1)
|
||||
process.SleepAbout1sTo2s()
|
||||
switch {
|
||||
case now < 6: // 凌晨
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText(
|
||||
@@ -95,11 +113,11 @@ func init() { // 插件主体
|
||||
))
|
||||
}
|
||||
})
|
||||
zero.OnFullMatchGroup([]string{"中午好", "午安", "午好"}, atriSwitch()).SetBlock(true).SetPriority(prio).
|
||||
engine.OnFullMatchGroup([]string{"中午好", "午安", "午好"}).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
now := time.Now().Hour()
|
||||
if now > 11 && now < 15 { // 中午
|
||||
time.Sleep(time.Second * 1)
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText(
|
||||
"午安w",
|
||||
"午觉要好好睡哦,ATRI会陪伴在你身旁的w",
|
||||
@@ -108,10 +126,10 @@ func init() { // 插件主体
|
||||
))
|
||||
}
|
||||
})
|
||||
zero.OnFullMatchGroup([]string{"晚安", "oyasuminasai", "おやすみなさい", "晚好", "晚上好"}, atriSwitch()).SetBlock(true).SetPriority(prio).
|
||||
engine.OnFullMatchGroup([]string{"晚安", "oyasuminasai", "おやすみなさい", "晚好", "晚上好"}).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
now := time.Now().Hour()
|
||||
time.Sleep(time.Second * 1)
|
||||
process.SleepAbout1sTo2s()
|
||||
switch {
|
||||
case now < 6: // 凌晨
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText(
|
||||
@@ -151,9 +169,9 @@ func init() { // 插件主体
|
||||
))
|
||||
}
|
||||
})
|
||||
zero.OnKeywordGroup([]string{"高性能", "太棒了", "すごい", "sugoi", "斯国一", "よかった"}, atriSwitch(), atriSleep(), zero.OnlyToMe).SetBlock(true).SetPriority(prio).
|
||||
engine.OnKeywordGroup([]string{"高性能", "太棒了", "すごい", "sugoi", "斯国一", "よかった"}, atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
time.Sleep(time.Second * 1)
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(randText(
|
||||
"当然,我是高性能的嘛~!",
|
||||
"小事一桩,我是高性能的嘛",
|
||||
@@ -172,9 +190,9 @@ func init() { // 插件主体
|
||||
"呣......我的高性能,毫无遗憾地施展出来了......",
|
||||
))
|
||||
})
|
||||
zero.OnKeywordGroup([]string{"没事", "没关系", "大丈夫", "还好", "不要紧", "没出大问题", "没伤到哪"}, atriSwitch(), atriSleep(), zero.OnlyToMe).SetBlock(true).SetPriority(prio).
|
||||
engine.OnKeywordGroup([]string{"没事", "没关系", "大丈夫", "还好", "不要紧", "没出大问题", "没伤到哪"}, atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
time.Sleep(time.Second * 1)
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(randText(
|
||||
"当然,我是高性能的嘛~!",
|
||||
"没事没事,因为我是高性能的嘛!嗯哼!",
|
||||
@@ -187,28 +205,28 @@ func init() { // 插件主体
|
||||
))
|
||||
})
|
||||
|
||||
zero.OnKeywordGroup([]string{"好吗", "是吗", "行不行", "能不能", "可不可以"}, atriSwitch(), atriSleep()).SetBlock(true).SetPriority(prio).
|
||||
engine.OnKeywordGroup([]string{"好吗", "是吗", "行不行", "能不能", "可不可以"}, atriSleep).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
time.Sleep(time.Second * 1)
|
||||
process.SleepAbout1sTo2s()
|
||||
if rand.Intn(2) == 0 {
|
||||
ctx.SendChain(randImage("YES.png", "NO.jpg"))
|
||||
}
|
||||
})
|
||||
zero.OnKeywordGroup([]string{"啊这"}, atriSwitch(), atriSleep()).SetBlock(true).SetPriority(prio).
|
||||
engine.OnKeywordGroup([]string{"啊这"}, atriSleep).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
time.Sleep(time.Second * 1)
|
||||
process.SleepAbout1sTo2s()
|
||||
if rand.Intn(2) == 0 {
|
||||
ctx.SendChain(randImage("AZ.jpg", "AZ1.jpg"))
|
||||
}
|
||||
})
|
||||
zero.OnKeywordGroup([]string{"我好了"}, atriSwitch(), atriSleep()).SetBlock(true).SetPriority(prio).
|
||||
engine.OnKeywordGroup([]string{"我好了"}, atriSleep).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
time.Sleep(time.Second * 1)
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText("不许好!", "憋回去!"))
|
||||
})
|
||||
zero.OnFullMatchGroup([]string{"?", "?", "¿"}, atriSwitch(), atriSleep()).SetBlock(true).SetPriority(prio).
|
||||
engine.OnFullMatchGroup([]string{"?", "?", "¿"}, atriSleep).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
time.Sleep(time.Second * 1)
|
||||
process.SleepAbout1sTo2s()
|
||||
switch rand.Intn(5) {
|
||||
case 0:
|
||||
ctx.SendChain(randText("?", "?", "嗯?", "(。´・ω・)ん?", "ん?"))
|
||||
@@ -216,7 +234,7 @@ func init() { // 插件主体
|
||||
ctx.SendChain(randImage("WH.jpg", "WH1.jpg", "WH2.jpg", "WH3.jpg"))
|
||||
}
|
||||
})
|
||||
zero.OnKeyword("离谱", atriSwitch(), atriSleep()).SetBlock(true).SetPriority(prio).
|
||||
engine.OnKeyword("离谱", atriSleep).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
switch rand.Intn(5) {
|
||||
case 0:
|
||||
@@ -225,41 +243,29 @@ func init() { // 插件主体
|
||||
ctx.SendChain(randImage("WH.jpg"))
|
||||
}
|
||||
})
|
||||
zero.OnKeyword("答应我", atriSwitch(), atriSleep(), zero.OnlyToMe).SetBlock(true).SetPriority(prio).
|
||||
engine.OnKeyword("答应我", atriSleep, zero.OnlyToMe).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
time.Sleep(time.Second * 1)
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(randText("我无法回应你的请求"))
|
||||
})
|
||||
}
|
||||
|
||||
func randText(text ...string) message.MessageSegment {
|
||||
length := len(text)
|
||||
return message.Text(text[rand.Intn(length)])
|
||||
return message.Text(text[rand.Intn(len(text))])
|
||||
}
|
||||
|
||||
func randImage(file ...string) message.MessageSegment {
|
||||
length := len(file)
|
||||
return message.Image(res + file[rand.Intn(length)])
|
||||
return message.Image(res + file[rand.Intn(len(file))])
|
||||
}
|
||||
|
||||
func randRecord(file ...string) message.MessageSegment {
|
||||
length := len(file)
|
||||
return message.Record(res + file[rand.Intn(length)])
|
||||
}
|
||||
|
||||
// atriSwitch 控制 ATRI 的开关
|
||||
func atriSwitch() zero.Rule {
|
||||
return func(ctx *zero.Ctx) bool {
|
||||
return enable
|
||||
}
|
||||
return message.Record(res + file[rand.Intn(len(file))])
|
||||
}
|
||||
|
||||
// atriSleep 凌晨0点到6点,ATRI 在睡觉,不回应任何请求
|
||||
func atriSleep() zero.Rule {
|
||||
return func(ctx *zero.Ctx) bool {
|
||||
if now := time.Now().Hour(); now >= 1 && now < 6 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
func atriSleep(ctx *zero.Ctx) bool {
|
||||
if now := time.Now().Hour(); now >= 1 && now < 6 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
76
plugin_b14/main.go
Normal file
76
plugin_b14/main.go
Normal file
@@ -0,0 +1,76 @@
|
||||
// Package b14coder base16384 与 tea 加解密
|
||||
package b14coder
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
base14 "github.com/fumiama/go-base16384"
|
||||
tea "github.com/fumiama/gofastTEA"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
)
|
||||
|
||||
func init() {
|
||||
en := control.Register("base16384", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "base16384加解密\n" +
|
||||
"- 加密xxx\n- 解密xxx\n- 用yyy加密xxx\n- 用yyy解密xxx",
|
||||
})
|
||||
en.OnRegex(`^加密(.*)`).SetBlock(true).ThirdPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
str := ctx.State["regex_matched"].([]string)[1]
|
||||
es, err := base14.UTF16be2utf8(base14.EncodeString(str))
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text(helper.BytesToString(es)))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("加密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex("^解密([\u4e00-\u8e00]*[\u3d01-\u3d06]?)$").SetBlock(true).ThirdPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
str := ctx.State["regex_matched"].([]string)[1]
|
||||
es, err := base14.UTF82utf16be(helper.StringToBytes(str))
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text(base14.DecodeString(es)))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("解密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^用(.*)加密(.*)`).SetBlock(true).ThirdPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
|
||||
t := getea(key)
|
||||
es, err := base14.UTF16be2utf8(base14.Encode(t.Encrypt(helper.StringToBytes(str))))
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text(helper.BytesToString(es)))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("加密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex("^用(.*)解密([\u4e00-\u8e00]*[\u3d01-\u3d06]?)$").SetBlock(true).ThirdPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
|
||||
t := getea(key)
|
||||
es, err := base14.UTF82utf16be(helper.StringToBytes(str))
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text(helper.BytesToString(t.Decrypt(base14.Decode(es)))))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("解密失败!"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func getea(key string) tea.TEA {
|
||||
kr := []rune(key)
|
||||
if len(kr) > 4 {
|
||||
kr = kr[:4]
|
||||
} else {
|
||||
for len(kr) < 4 {
|
||||
kr = append(kr, rune(4-len(kr)))
|
||||
}
|
||||
}
|
||||
return *(*tea.TEA)(*(*unsafe.Pointer)(unsafe.Pointer(&kr)))
|
||||
}
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/robfig/cron"
|
||||
"github.com/fumiama/cron"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
)
|
||||
|
||||
type follower struct {
|
||||
@@ -23,85 +25,128 @@ type follower struct {
|
||||
|
||||
// 开启日报推送
|
||||
func init() {
|
||||
engine.OnFullMatch("/开启粉丝日报", zero.AdminPermission).SetBlock(true).
|
||||
fansDaily()
|
||||
en := control.Register("fansdaily", &control.Options{
|
||||
DisableOnDefault: true,
|
||||
Help: "fansdaily\n- /开启粉丝日报\n- /关闭粉丝日报",
|
||||
})
|
||||
zero.OnCommand("开启粉丝日报", zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
fansDaily(ctx.Event.GroupID) // 群号传进去给下面发信息的函数
|
||||
m, ok := control.Lookup("fansdaily")
|
||||
if ok {
|
||||
if m.IsEnabledIn(ctx.Event.GroupID) {
|
||||
ctx.Send(message.Text("已启用!"))
|
||||
} else {
|
||||
m.Enable(ctx.Event.GroupID)
|
||||
ctx.Send(message.Text("添加成功!"))
|
||||
}
|
||||
} else {
|
||||
ctx.Send(message.Text("找不到该服务!"))
|
||||
}
|
||||
})
|
||||
en.OnCommand("关闭粉丝日报", zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
m, ok := control.Lookup("fansdaily")
|
||||
if ok {
|
||||
if m.IsEnabledIn(ctx.Event.GroupID) {
|
||||
m.Disable(ctx.Event.GroupID)
|
||||
ctx.Send(message.Text("关闭成功!"))
|
||||
} else {
|
||||
ctx.Send(message.Text("未启用!"))
|
||||
}
|
||||
} else {
|
||||
ctx.Send(message.Text("找不到该服务!"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 定时任务每天晚上最后2分钟执行一次
|
||||
func fansDaily(groupID int64) {
|
||||
func fansDaily() {
|
||||
c := cron.New()
|
||||
_ = c.AddFunc("0 58 23 * * ?", func() { fansData(groupID) })
|
||||
c.Start()
|
||||
_, err := c.AddFunc("58 23 * * *", func() { sendNotice() })
|
||||
if err == nil {
|
||||
c.Start()
|
||||
}
|
||||
}
|
||||
|
||||
// 获取数据拼接消息链并发送
|
||||
func fansData(groupID int64) {
|
||||
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
|
||||
var (
|
||||
diana = fansapi("672328094")
|
||||
ava = fansapi("672346917")
|
||||
eileen = fansapi("672342685")
|
||||
bella = fansapi("672353429")
|
||||
carol = fansapi("351609538")
|
||||
)
|
||||
ctx.SendGroupMessage(
|
||||
groupID,
|
||||
message.Text(
|
||||
time.Now().Format("2006-01-02"), " Asoul全团粉丝日报如下", "\n\n",
|
||||
"uid: ", diana.Mid, "\n",
|
||||
"名字: ", diana.Uname, "\n",
|
||||
"当前粉丝数: ", diana.Follower, "\n",
|
||||
"今日涨粉数: ", diana.Rise, "\n",
|
||||
"视频投稿数: ", diana.Video, "\n",
|
||||
"直播间id: ", diana.Roomid, "\n",
|
||||
"舰队: ", diana.GuardNum, "\n",
|
||||
"直播总排名: ", diana.AreaRank, "\n",
|
||||
"数据来源: ", "https://vtbs.moe/detail/", "672328094", "\n\n",
|
||||
func getMsg() message.MessageSegment {
|
||||
var (
|
||||
diana = fansapi("672328094")
|
||||
ava = fansapi("672346917")
|
||||
eileen = fansapi("672342685")
|
||||
bella = fansapi("672353429")
|
||||
carol = fansapi("351609538")
|
||||
)
|
||||
return message.Text(
|
||||
time.Now().Format("2006-01-02"), " Asoul全团粉丝日报如下", "\n\n",
|
||||
"uid: ", diana.Mid, "\n",
|
||||
"名字: ", diana.Uname, "\n",
|
||||
"当前粉丝数: ", diana.Follower, "\n",
|
||||
"今日涨粉数: ", diana.Rise, "\n",
|
||||
"视频投稿数: ", diana.Video, "\n",
|
||||
"直播间id: ", diana.Roomid, "\n",
|
||||
"舰队: ", diana.GuardNum, "\n",
|
||||
"直播总排名: ", diana.AreaRank, "\n",
|
||||
"数据来源: ", "https://vtbs.moe/detail/", "672328094", "\n\n",
|
||||
|
||||
"uid: ", ava.Mid, "\n",
|
||||
"名字: ", ava.Uname, "\n",
|
||||
"当前粉丝数: ", ava.Follower, "\n",
|
||||
"今日涨粉数: ", ava.Rise, "\n",
|
||||
"视频投稿数: ", ava.Video, "\n",
|
||||
"直播间id: ", ava.Roomid, "\n",
|
||||
"舰队: ", ava.GuardNum, "\n",
|
||||
"直播总排名: ", ava.AreaRank, "\n",
|
||||
"数据来源: ", "https://vtbs.moe/detail/", "672346917", "\n\n",
|
||||
"uid: ", ava.Mid, "\n",
|
||||
"名字: ", ava.Uname, "\n",
|
||||
"当前粉丝数: ", ava.Follower, "\n",
|
||||
"今日涨粉数: ", ava.Rise, "\n",
|
||||
"视频投稿数: ", ava.Video, "\n",
|
||||
"直播间id: ", ava.Roomid, "\n",
|
||||
"舰队: ", ava.GuardNum, "\n",
|
||||
"直播总排名: ", ava.AreaRank, "\n",
|
||||
"数据来源: ", "https://vtbs.moe/detail/", "672346917", "\n\n",
|
||||
|
||||
"uid: ", eileen.Mid, "\n",
|
||||
"名字: ", eileen.Uname, "\n",
|
||||
"当前粉丝数: ", eileen.Follower, "\n",
|
||||
"今日涨粉数: ", eileen.Rise, "\n",
|
||||
"视频投稿数: ", eileen.Video, "\n",
|
||||
"直播间id: ", eileen.Roomid, "\n",
|
||||
"舰队: ", eileen.GuardNum, "\n",
|
||||
"直播总排名: ", eileen.AreaRank, "\n",
|
||||
"数据来源: ", "https://vtbs.moe/detail/", "672342685", "\n\n",
|
||||
"uid: ", eileen.Mid, "\n",
|
||||
"名字: ", eileen.Uname, "\n",
|
||||
"当前粉丝数: ", eileen.Follower, "\n",
|
||||
"今日涨粉数: ", eileen.Rise, "\n",
|
||||
"视频投稿数: ", eileen.Video, "\n",
|
||||
"直播间id: ", eileen.Roomid, "\n",
|
||||
"舰队: ", eileen.GuardNum, "\n",
|
||||
"直播总排名: ", eileen.AreaRank, "\n",
|
||||
"数据来源: ", "https://vtbs.moe/detail/", "672342685", "\n\n",
|
||||
|
||||
"uid: ", bella.Mid, "\n",
|
||||
"名字: ", bella.Uname, "\n",
|
||||
"当前粉丝数: ", bella.Follower, "\n",
|
||||
"今日涨粉数: ", bella.Rise, "\n",
|
||||
"视频投稿数: ", bella.Video, "\n",
|
||||
"直播间id: ", bella.Roomid, "\n",
|
||||
"舰队: ", bella.GuardNum, "\n",
|
||||
"直播总排名: ", bella.AreaRank, "\n",
|
||||
"数据来源: ", "https://vtbs.moe/detail/", "672353429", "\n\n",
|
||||
"uid: ", bella.Mid, "\n",
|
||||
"名字: ", bella.Uname, "\n",
|
||||
"当前粉丝数: ", bella.Follower, "\n",
|
||||
"今日涨粉数: ", bella.Rise, "\n",
|
||||
"视频投稿数: ", bella.Video, "\n",
|
||||
"直播间id: ", bella.Roomid, "\n",
|
||||
"舰队: ", bella.GuardNum, "\n",
|
||||
"直播总排名: ", bella.AreaRank, "\n",
|
||||
"数据来源: ", "https://vtbs.moe/detail/", "672353429", "\n\n",
|
||||
|
||||
"uid: ", carol.Mid, "\n",
|
||||
"名字: ", carol.Uname, "\n",
|
||||
"当前粉丝数: ", carol.Follower, "\n",
|
||||
"今日涨粉数: ", carol.Rise, "\n",
|
||||
"视频投稿数: ", carol.Video, "\n",
|
||||
"直播间id: ", carol.Roomid, "\n",
|
||||
"舰队: ", carol.GuardNum, "\n",
|
||||
"直播总排名: ", carol.AreaRank, "\n",
|
||||
"数据来源: ", "https://vtbs.moe/detail/", "351609538",
|
||||
))
|
||||
return true
|
||||
})
|
||||
"uid: ", carol.Mid, "\n",
|
||||
"名字: ", carol.Uname, "\n",
|
||||
"当前粉丝数: ", carol.Follower, "\n",
|
||||
"今日涨粉数: ", carol.Rise, "\n",
|
||||
"视频投稿数: ", carol.Video, "\n",
|
||||
"直播间id: ", carol.Roomid, "\n",
|
||||
"舰队: ", carol.GuardNum, "\n",
|
||||
"直播总排名: ", carol.AreaRank, "\n",
|
||||
"数据来源: ", "https://vtbs.moe/detail/", "351609538",
|
||||
)
|
||||
}
|
||||
|
||||
// 获取数据拼接消息链并发送
|
||||
func sendNotice() {
|
||||
m, ok := control.Lookup("fansdaily")
|
||||
if ok {
|
||||
msg := getMsg()
|
||||
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
|
||||
for _, g := range ctx.GetGroupList().Array() {
|
||||
grp := g.Get("group_id").Int()
|
||||
if m.IsEnabledIn(grp) {
|
||||
ctx.SendGroupMessage(grp, msg)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 请求api
|
||||
|
||||
@@ -12,17 +12,15 @@ import (
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
)
|
||||
|
||||
var engine *zero.Engine
|
||||
var engine = control.Register("bilibili", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "bilibili\n" +
|
||||
"- >vup info [名字|uid]\n" +
|
||||
"- >user info [名字|uid]",
|
||||
})
|
||||
|
||||
// 查成分的
|
||||
func init() {
|
||||
engine = control.Register("bilibili", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "bilibili\n" +
|
||||
"- >vup info [名字|uid]\n" +
|
||||
"- >user info [名字|uid]\n" +
|
||||
"- /开启粉丝日报",
|
||||
})
|
||||
engine.OnRegex(`^>user info\s(.{1,25})$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
keyword := ctx.State["regex_matched"].([]string)[1]
|
||||
|
||||
28
plugin_book_review/book_review.go
Normal file
28
plugin_book_review/book_review.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package plugin_book_review
|
||||
|
||||
import (
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.Register("bookreview", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "哀伤雪刃推书记录\n- 书评[xxx]\n- 随机书评",
|
||||
})
|
||||
|
||||
// 中文、英文、数字但不包括下划线等符号
|
||||
engine.OnRegex("^书评([\u4E00-\u9FA5A-Za-z0-9]{1,25})$").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
b := getBookReviewByKeyword(ctx.State["regex_matched"].([]string)[1])
|
||||
ctx.SendChain(message.Text(b.BookReview))
|
||||
})
|
||||
|
||||
engine.OnFullMatch("随机书评").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
br := getRandomBookReview()
|
||||
ctx.SendChain(message.Text(br.BookReview))
|
||||
})
|
||||
}
|
||||
35
plugin_book_review/data.go
Normal file
35
plugin_book_review/data.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package plugin_book_review
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
|
||||
)
|
||||
|
||||
const dbpath = "data/BookReview/"
|
||||
const dbfile = dbpath + "bookreview.db"
|
||||
|
||||
var db = &sql.Sqlite{DBPath: dbfile}
|
||||
|
||||
// 加载数据库
|
||||
func init() {
|
||||
go func() {
|
||||
process.SleepAbout1sTo2s()
|
||||
// os.RemoveAll(dbpath)
|
||||
_ = os.MkdirAll(dbpath, 0755)
|
||||
_, _ = file.GetLazyData(dbfile, false, true)
|
||||
err := db.Create("bookreview", &book{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
n, err := db.Count("bookreview")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Printf("[bookreview]读取%d条书评", n)
|
||||
}()
|
||||
}
|
||||
43
plugin_book_review/migrate/main.go
Normal file
43
plugin_book_review/migrate/main.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
|
||||
)
|
||||
|
||||
type book struct {
|
||||
Id uint64 `db:"id"`
|
||||
BookReview string `db:"bookreview"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
db, err := Open(os.Args[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
newdb := &sql.Sqlite{DBPath: os.Args[2]}
|
||||
err = newdb.Create("bookreview", &book{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rs, err := db.Table("book_review").Select("book_review", "").Rows()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var d string
|
||||
var i uint64
|
||||
for rs.Next() {
|
||||
err := rs.Scan(&d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
i++
|
||||
err = newdb.Insert("bookreview", &book{i, d})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
db.Close()
|
||||
newdb.Close()
|
||||
}
|
||||
46
plugin_book_review/migrate/model.go
Normal file
46
plugin_book_review/migrate/model.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/logoove/sqlite"
|
||||
)
|
||||
|
||||
type BrDB = gorm.DB
|
||||
|
||||
func Initialize(dbpath string) *BrDB {
|
||||
var err error
|
||||
if _, err = os.Stat(dbpath); err != nil || os.IsNotExist(err) {
|
||||
// 生成文件
|
||||
f, err := os.Create(dbpath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer f.Close()
|
||||
}
|
||||
gdb, err := gorm.Open("sqlite3", dbpath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
gdb.AutoMigrate(&BookReview{})
|
||||
return (*BrDB)(gdb)
|
||||
}
|
||||
|
||||
func Open(dbpath string) (*BrDB, error) {
|
||||
db, err := gorm.Open("sqlite3", dbpath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return (*BrDB)(db), nil
|
||||
}
|
||||
}
|
||||
|
||||
type BookReview struct {
|
||||
gorm.Model
|
||||
BookReview string `gorm:"column:book_review"`
|
||||
}
|
||||
|
||||
func (BookReview) TableName() string {
|
||||
return "book_review"
|
||||
}
|
||||
17
plugin_book_review/model.go
Normal file
17
plugin_book_review/model.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package plugin_book_review
|
||||
|
||||
type book struct {
|
||||
Id uint64 `db:"id"`
|
||||
BookReview string `db:"bookreview"`
|
||||
}
|
||||
|
||||
// 暂时随机选择一个书评
|
||||
func getBookReviewByKeyword(keyword string) (b book) {
|
||||
db.Find("bookreview", &b, "where bookreview LIKE '%"+keyword+"%'")
|
||||
return
|
||||
}
|
||||
|
||||
func getRandomBookReview() (b book) {
|
||||
db.Pick("bookreview", &b)
|
||||
return
|
||||
}
|
||||
@@ -9,13 +9,20 @@ import (
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/rate"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
)
|
||||
|
||||
var poke = rate.NewManager(time.Minute*5, 8) // 戳一戳
|
||||
|
||||
var engine = control.Register("chat", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "chat\n- [BOT名字]\n- [戳一戳BOT]\n- 空调开\n- 空调关\n- 群温度\n- 设置温度[正整数]",
|
||||
})
|
||||
|
||||
func init() { // 插件主体
|
||||
// 被喊名字
|
||||
zero.OnFullMatch("", zero.OnlyToMe).SetBlock(false).FirstPriority().
|
||||
engine.OnFullMatch("", zero.OnlyToMe).SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
var nickname = zero.BotConfig.NickName[0]
|
||||
time.Sleep(time.Second * 1)
|
||||
@@ -29,15 +36,15 @@ func init() { // 插件主体
|
||||
))
|
||||
})
|
||||
// 戳一戳
|
||||
zero.On("notice/notify/poke", zero.OnlyToMe).SetBlock(false).FirstPriority().
|
||||
engine.On("notice/notify/poke", zero.OnlyToMe).SetBlock(false).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
var nickname = zero.BotConfig.NickName[0]
|
||||
switch {
|
||||
case poke.Load(ctx.Event.UserID).AcquireN(3):
|
||||
case poke.Load(ctx.Event.GroupID).AcquireN(3):
|
||||
// 5分钟共8块命令牌 一次消耗3块命令牌
|
||||
time.Sleep(time.Second * 1)
|
||||
ctx.SendChain(message.Text("请不要戳", nickname, " >_<"))
|
||||
case poke.Load(ctx.Event.UserID).Acquire():
|
||||
case poke.Load(ctx.Event.GroupID).Acquire():
|
||||
// 5分钟共8块命令牌 一次消耗1块命令牌
|
||||
time.Sleep(time.Second * 1)
|
||||
ctx.SendChain(message.Text("喂(#`O′) 戳", nickname, "干嘛!"))
|
||||
@@ -48,18 +55,18 @@ func init() { // 插件主体
|
||||
// 群空调
|
||||
var AirConditTemp = map[int64]int{}
|
||||
var AirConditSwitch = map[int64]bool{}
|
||||
zero.OnFullMatch("空调开").SetBlock(true).FirstPriority().
|
||||
engine.OnFullMatch("空调开").SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
AirConditSwitch[ctx.Event.GroupID] = true
|
||||
ctx.SendChain(message.Text("❄️哔~"))
|
||||
})
|
||||
zero.OnFullMatch("空调关").SetBlock(true).FirstPriority().
|
||||
engine.OnFullMatch("空调关").SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
AirConditSwitch[ctx.Event.GroupID] = false
|
||||
delete(AirConditTemp, ctx.Event.GroupID)
|
||||
ctx.SendChain(message.Text("💤哔~"))
|
||||
})
|
||||
zero.OnRegex(`设置温度(\d+)`).SetBlock(true).FirstPriority().
|
||||
engine.OnRegex(`设置温度(\d+)`).SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if _, exist := AirConditTemp[ctx.Event.GroupID]; !exist {
|
||||
AirConditTemp[ctx.Event.GroupID] = 26
|
||||
@@ -78,7 +85,7 @@ func init() { // 插件主体
|
||||
))
|
||||
}
|
||||
})
|
||||
zero.OnFullMatch(`群温度`).SetBlock(true).FirstPriority().
|
||||
engine.OnFullMatch(`群温度`).SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if _, exist := AirConditTemp[ctx.Event.GroupID]; !exist {
|
||||
AirConditTemp[ctx.Event.GroupID] = 26
|
||||
|
||||
@@ -17,7 +17,7 @@ func init() {
|
||||
DisableOnDefault: false,
|
||||
Help: "choose\n" +
|
||||
"- 选择可口可乐还是百事可乐\n" +
|
||||
"- 选择肯德基还是麦当劳还是必胜客\n",
|
||||
"- 选择肯德基还是麦当劳还是必胜客",
|
||||
})
|
||||
engine.OnPrefix("选择").SetBlock(true).FirstPriority().Handle(handle)
|
||||
}
|
||||
|
||||
67
plugin_coser/coser.go
Normal file
67
plugin_coser/coser.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Package coser images
|
||||
package coser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/rate"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/web"
|
||||
)
|
||||
|
||||
var (
|
||||
engine = control.Register("coser", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "三次元小姐姐\n- coser\n",
|
||||
})
|
||||
prio = 20
|
||||
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36"
|
||||
coserURL = "http://ovooa.com/API/cosplay/api.php"
|
||||
limit = rate.NewManager(time.Minute, 5)
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine.OnFullMatch("coser", zero.OnlyGroup).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if !limit.Load(ctx.Event.GroupID).Acquire() {
|
||||
ctx.SendChain(message.Text("请稍后重试0x0..."))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("少女祈祷中......"))
|
||||
data, err := web.ReqWith(coserURL, "GET", "", ua)
|
||||
if err != nil {
|
||||
log.Println("err为:", err)
|
||||
}
|
||||
var m message.Message
|
||||
text := gjson.Get(helper.BytesToString(data), "data.Title").String()
|
||||
m = append(m,
|
||||
message.CustomNode(
|
||||
zero.BotConfig.NickName[0],
|
||||
ctx.Event.SelfID,
|
||||
text,
|
||||
))
|
||||
gjson.Get(helper.BytesToString(data), "data.data").ForEach(func(_, value gjson.Result) bool {
|
||||
imgcq := `[CQ:image,file=` + value.String() + `]`
|
||||
m = append(m,
|
||||
message.CustomNode(
|
||||
zero.BotConfig.NickName[0],
|
||||
ctx.Event.SelfID,
|
||||
imgcq),
|
||||
)
|
||||
return true
|
||||
})
|
||||
|
||||
if id := ctx.SendGroupForwardMessage(
|
||||
ctx.Event.GroupID,
|
||||
m).Get("message_id").Int(); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -2,11 +2,11 @@
|
||||
package diana
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/plugin_diana/data"
|
||||
@@ -28,22 +28,22 @@ func init() {
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
// 绕过第一行发病
|
||||
ctx.Send((*data.Array)[rand.Intn(len(*data.Array)-1)+1])
|
||||
ctx.SendChain(message.Text(data.RandText()))
|
||||
})
|
||||
// 逆天
|
||||
engine.OnFullMatch("发大病").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
// 第一行是发病
|
||||
ctx.Send((*data.Array)[0])
|
||||
ctx.SendChain(message.Text(data.HentaiText()))
|
||||
})
|
||||
// 增加小作文
|
||||
engine.OnRegex(`^教你一篇小作文(.*)$`, zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
err := data.AddText(ctx.State["regex_matched"].([]string)[1])
|
||||
if err != nil {
|
||||
ctx.Send(fmt.Sprintf("ERROR: %v", err))
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
} else {
|
||||
ctx.Send("记住啦!")
|
||||
ctx.SendChain(message.Text("记住啦!"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,135 +3,79 @@ package data
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"io"
|
||||
"net/http"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
|
||||
)
|
||||
|
||||
const (
|
||||
datapath = "data/Diana"
|
||||
pbfile = datapath + "/text.pb"
|
||||
pburl = "https://codechina.csdn.net/u011570312/ZeroBot-Plugin/-/raw/master/" + pbfile
|
||||
dbfile = datapath + "/text.db"
|
||||
)
|
||||
|
||||
var (
|
||||
compo Composition
|
||||
// Array 小作文数组指针
|
||||
Array = &compo.Array
|
||||
// m 小作文保存锁
|
||||
m sync.Mutex
|
||||
// md5s 验证重复
|
||||
md5s []*[16]byte
|
||||
)
|
||||
var db = sql.Sqlite{DBPath: dbfile}
|
||||
|
||||
type Text struct {
|
||||
Id int64 `db:"id"`
|
||||
Data string `db:"data"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
time.Sleep(time.Second)
|
||||
process.SleepAbout1sTo2s()
|
||||
err := os.MkdirAll(datapath, 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err1 := LoadText()
|
||||
if err1 == nil {
|
||||
arrl := len(*Array)
|
||||
log.Printf("[Diana]读取%d条小作文", arrl)
|
||||
md5s = make([]*[16]byte, arrl)
|
||||
for i, t := range *Array {
|
||||
m := md5.Sum(str2bytes(t))
|
||||
md5s[i] = &m
|
||||
err = LoadText()
|
||||
if err == nil {
|
||||
err = db.Create("text", &Text{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c, _ := db.Count("text")
|
||||
log.Printf("[Diana]读取%d条小作文", c)
|
||||
} else {
|
||||
log.Printf("[Diana]读取小作文错误:%v", err1)
|
||||
log.Printf("[Diana]读取小作文错误:%v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// LoadText 加载小作文
|
||||
func LoadText() error {
|
||||
if _, err := os.Stat(pbfile); err == nil || os.IsExist(err) {
|
||||
f, err := os.Open(pbfile)
|
||||
if err == nil {
|
||||
defer f.Close()
|
||||
data, err1 := io.ReadAll(f)
|
||||
if err1 == nil {
|
||||
if len(data) > 0 {
|
||||
return compo.Unmarshal(data)
|
||||
}
|
||||
}
|
||||
return err1
|
||||
}
|
||||
} else { // 如果没有小作文,则从 url 下载
|
||||
f, err := os.Create(pbfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
resp, err := http.Get(pburl)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
if resp.ContentLength > 0 {
|
||||
log.Printf("[Diana]从镜像下载小作文%d字节...", resp.ContentLength)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err == nil && len(data) > 0 {
|
||||
_, _ = f.Write(data)
|
||||
return compo.Unmarshal(data)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
_, err := file.GetLazyData(dbfile, false, false)
|
||||
return err
|
||||
}
|
||||
|
||||
// AddText 添加小作文
|
||||
func AddText(txt string) error {
|
||||
sum := md5.Sum(str2bytes(txt))
|
||||
if txt != "" && !isin(&sum) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
compo.Array = append(compo.Array, txt)
|
||||
md5s = append(md5s, &sum)
|
||||
return savecompo()
|
||||
}
|
||||
return nil
|
||||
s := md5.Sum(helper.StringToBytes(txt))
|
||||
i := binary.LittleEndian.Uint64(s[:8])
|
||||
return db.Insert("text", &Text{Id: int64(i), Data: txt})
|
||||
}
|
||||
|
||||
func isin(sum *[16]byte) bool {
|
||||
for _, t := range md5s {
|
||||
if *t == *sum {
|
||||
return true
|
||||
}
|
||||
// RandText 随机小作文
|
||||
func RandText() string {
|
||||
var t Text
|
||||
err := db.Pick("text", &t)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return false
|
||||
return t.Data
|
||||
}
|
||||
|
||||
// savecompo 同步保存作文
|
||||
func savecompo() error {
|
||||
data, err := compo.Marshal()
|
||||
if err == nil {
|
||||
if _, err := os.Stat(datapath); err == nil || os.IsExist(err) {
|
||||
f, err1 := os.OpenFile(pbfile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
|
||||
if err1 == nil {
|
||||
_, err2 := f.Write(data)
|
||||
f.Close()
|
||||
return err2
|
||||
}
|
||||
return err1
|
||||
}
|
||||
// HentaiText 发大病
|
||||
func HentaiText() string {
|
||||
var t Text
|
||||
err := db.Find("text", &t, "where id = -3802576048116006195")
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// str2bytes Fast convert
|
||||
func str2bytes(s string) []byte {
|
||||
x := (*[2]uintptr)(unsafe.Pointer(&s))
|
||||
h := [3]uintptr{x[0], x[1], x[1]}
|
||||
return *(*[]byte)(unsafe.Pointer(&h))
|
||||
return t.Data
|
||||
}
|
||||
|
||||
@@ -1,326 +0,0 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: text.proto
|
||||
|
||||
package data
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type Composition struct {
|
||||
Array []string `protobuf:"bytes,1,rep,name=array,proto3" json:"array,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Composition) Reset() { *m = Composition{} }
|
||||
func (m *Composition) String() string { return proto.CompactTextString(m) }
|
||||
func (*Composition) ProtoMessage() {}
|
||||
func (*Composition) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_a8e73d1ce47f9297, []int{0}
|
||||
}
|
||||
func (m *Composition) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *Composition) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_Composition.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *Composition) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Composition.Merge(m, src)
|
||||
}
|
||||
func (m *Composition) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *Composition) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Composition.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Composition proto.InternalMessageInfo
|
||||
|
||||
func (m *Composition) GetArray() []string {
|
||||
if m != nil {
|
||||
return m.Array
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Composition)(nil), "diana.composition")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("text.proto", fileDescriptor_a8e73d1ce47f9297) }
|
||||
|
||||
var fileDescriptor_a8e73d1ce47f9297 = []byte{
|
||||
// 100 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0x49, 0xad, 0x28,
|
||||
0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x4d, 0xc9, 0x4c, 0xcc, 0x4b, 0x54, 0x52, 0xe6,
|
||||
0xe2, 0x4e, 0xce, 0xcf, 0x2d, 0xc8, 0x2f, 0xce, 0x2c, 0xc9, 0xcc, 0xcf, 0x13, 0x12, 0xe1, 0x62,
|
||||
0x4d, 0x2c, 0x2a, 0x4a, 0xac, 0x94, 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x0c, 0x82, 0x70, 0x9c, 0x04,
|
||||
0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x19, 0x8f, 0xe5,
|
||||
0x18, 0x92, 0xd8, 0xc0, 0x86, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x22, 0x35, 0x44, 0xcb,
|
||||
0x52, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *Composition) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *Composition) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *Composition) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.XXX_unrecognized != nil {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if len(m.Array) > 0 {
|
||||
for iNdEx := len(m.Array) - 1; iNdEx >= 0; iNdEx-- {
|
||||
i -= len(m.Array[iNdEx])
|
||||
copy(dAtA[i:], m.Array[iNdEx])
|
||||
i = encodeVarintText(dAtA, i, uint64(len(m.Array[iNdEx])))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintText(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovText(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return base
|
||||
}
|
||||
func (m *Composition) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Array) > 0 {
|
||||
for _, s := range m.Array {
|
||||
l = len(s)
|
||||
n += 1 + l + sovText(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovText(x uint64) (n int) {
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozText(x uint64) (n int) {
|
||||
return sovText(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *Composition) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowText
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: composition: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: composition: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Array", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowText
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthText
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthText
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Array = append(m.Array, string(dAtA[iNdEx:postIndex]))
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipText(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthText
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipText(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowText
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wireType := int(wire & 0x7)
|
||||
switch wireType {
|
||||
case 0:
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowText
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx++
|
||||
if dAtA[iNdEx-1] < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowText
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
length |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthText
|
||||
}
|
||||
iNdEx += length
|
||||
case 3:
|
||||
depth++
|
||||
case 4:
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupText
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthText
|
||||
}
|
||||
if depth == 0 {
|
||||
return iNdEx, nil
|
||||
}
|
||||
}
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthText = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowText = fmt.Errorf("proto: integer overflow")
|
||||
ErrUnexpectedEndOfGroupText = fmt.Errorf("proto: unexpected end of group")
|
||||
)
|
||||
@@ -1,6 +0,0 @@
|
||||
syntax = "proto3";
|
||||
package data;
|
||||
|
||||
message composition {
|
||||
repeated string array = 1;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Package convert 转换txt到pb
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/plugin_diana/data"
|
||||
)
|
||||
|
||||
var (
|
||||
compo data.Composition
|
||||
)
|
||||
|
||||
func init() {
|
||||
compo.Array = make([]string, 0, 64)
|
||||
}
|
||||
|
||||
// 参数:txt文件位置 pb文件位置
|
||||
func main() {
|
||||
file, err := os.Open(os.Args[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
// optionally, resize scanner's capacity for lines over 64K, see next example
|
||||
for scanner.Scan() {
|
||||
// fmt.Println(scanner.Text())
|
||||
compo.Array = append(compo.Array, scanner.Text())
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
data, _ := compo.Marshal()
|
||||
f, err1 := os.OpenFile(os.Args[2], os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
|
||||
if err1 == nil {
|
||||
defer f.Close()
|
||||
_, err2 := f.Write(data)
|
||||
if err2 == nil {
|
||||
fmt.Println("成功")
|
||||
} else {
|
||||
panic(err2)
|
||||
}
|
||||
} else {
|
||||
panic(err1)
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
贝拉抽我🥵嘉然骑在我背上🥵晚晚踩我🥵乃琳坐在王座是用看垃圾的眼神看我🥵🥵珈乐踢我🥵🥵,把我眼睛蒙住然后五只脚一起踩我🥵还让我猜脚是谁的,猜错了给我劈眼一铁棍🥵
|
||||
我好想做嘉然小姐的狗啊。可是嘉然小姐说她喜欢的是猫,我哭了。我知道既不是狗也不是猫的我为什么要哭的。因为我其实是一只老鼠。我从没奢望嘉然小姐能喜欢自己。我明白的,所有人都喜欢理解余裕上手天才打钱的萌萌的狗狗或者猫猫,没有人会喜欢阴湿带病的老鼠。但我还是问了嘉然小姐:“我能不能做你的狗?”我知道我是注定做不了狗的。但如果她喜欢狗,我就可以一直在身边看着她了,哪怕她怀里抱着的永远都是狗。可是她说喜欢的是猫。她现在还在看着我,还在逗我开心,是因为猫还没有出现,只有我这老鼠每天蹑手蹑脚地从洞里爬出来,远远地和她对视。等她喜欢的猫来了的时候,我就该重新滚回我的洞了吧。但我还是好喜欢她,她能在我还在她身边的时候多看我几眼吗?嘉然小姐说接下来的每个圣诞夜都要和大家一起过。我不知道大家指哪些人。好希望这个集合能够对我做一次胞吞。猫猫还在害怕嘉然小姐。我会去把她爱的猫猫引来的。我知道稍有不慎,我就会葬身猫口。那时候嘉然小姐大概会把我的身体好好地装起来扔到门外吧。那我就成了一包鼠条,嘻嘻。我希望她能把我扔得近一点,因为我还是好喜欢她。会一直喜欢下去的。我的灵魂透过窗户向里面看去,挂着的铃铛在轻轻鸣响,嘉然小姐慵懒地靠在沙发上,表演得非常温顺的橘猫坐在她的肩膀。壁炉的火光照在她的脸庞,我冻僵的心脏在风里微微发烫。
|
||||
ダイアナさんの犬になりたいな.けど ダイアナさんが「猫が好き」と言った あさりと僕は泣いてしまった.猫でも犬でもない僕が 何故泣いたのかな 僕は知っていた.何故なら 僕は鼠だったからさ~..一度もダイアナさんに好かれることを望まながった .僕はわがっていた 誰もかも理解余裕上手天才の可愛い金持ちの犬か猫がすきってことを.陰キャで汚らわし鼠は誰も好かんのだ..それでもDianaさんに聞いてみた:Dianaさんの犬になっても良いですかと.僕には一生犬になれないことは分かっている.でももし彼女は犬が好きだったら 僕はずっと傍で見ていられる.それはいつも違ういぬをだいいているとしても..けれど彼女は猫が好きだった.彼女は今にも僕を見ている ぼくを楽しませてくれる それは猫がまだ表れていないから.ただこの鼠が毎日こそこそと穴から出て 遠くから目を合っている.彼女の好きな猫が現れたら 僕は穴に戻るしかないな..でもやっぱり彼女が好きでいられない 僕がまだ傍にいるときぐらい もうちょとみてくれるのかな.Dianaさんがこれから毎回のクリスマスみんなと一緒に過ごすで言った みんながどの渡りのみんなかな.僕にも中に入れたれいいな..猫はまだまだDianaさんを怖がっている.僕が彼女の愛する猫を引き付けるよ.少しのミスで猫の口に死ぬことはわがっている.その時になるとたぶんDianaさんは僕の体をちゃんと包み込んで 玄関外に捨てるでしょ.それで僕はフライト鼠になった くす.彼女に少し近く捨ててほしいな だってやっぱり好きだから それずっと好きでいる..僕のたましは窓を越して中を見る 吊っている鈴が少し響って.Dianaさんはだらりとソファーに寄りかかて 手慣れているふりをする橘猫が彼女の肩に座って.暖炉の光が彼女の顔を照らした 凍り付いた僕の心臓はかぜの中で熱くなっていく
|
||||
枝江小镇新来了一位修女,善良温柔的修女。她不爱讲话,只是微笑着面对每一个对她祷告的人。有的人希望孩子健康,有的人希望庄稼熬过冬天,有的人希望能挣到钱养活家人。。。。她都用心听着,微笑的回应着。她身材矮小,但是却让人觉得充满智慧;她沉默寡言,但是却会让人无比信赖。这是她任职快满一个月的时候,教堂发生了大事,一个醉汉发现神职人员利用教堂在深夜进行大批资金流入,醉汉传的神乎其神,三人成虎最终引起审判官前来调查。教主被带走,教堂除了那位茫然的新来修女其他人一哄而散。修女被迫承担着本不应该属于她的责难,辱骂,但是她每天都认真的向主祷告,倾听来教堂之人的倾诉。可是根本没有人向她倾诉,来的人不是在骂她,就是在发泄生活的不满。她一己之力拼命承担,没人知道是什么使她如此坚持。直到审判官带回了教主,告诉镇民那是教主在中央主教那里得到的,目的是为镇民熬过冬天所需要买粮食的钱财。一时间镇民欢呼起教主,感谢主的庇护,更多的人开始更加爱上了那个修女。有的人把她比作玛利亚,有些人把她奉为神明,她都一一拒绝了。细心的镇民会发现这位修女变了,变得话多起来了,也变得更喜欢宅在教堂里了,也变得离镇民距离更远了。一切好像又变回原来平和的小镇,每个对修女的加害者每天微笑着向微笑着的修女打招呼,每个镇民还是会去教堂祈祷,去倾诉,去向一个原来被他们亲手辱骂的人祷告,希望得到主的关照,包括那个醉汉。没人能惩罚那些镇民,很多人都已经“忘记”当初为什么要那样做,包括那个醉汉,只剩下一个经常宅在教堂里的修女,她始终记得一切。
|
||||
贝拉躺在病床上,眼神空空地盯着天花板,灰白晦暗的天花板像一块哈哈镜,扭曲掉外面的一切,光变成了暗,多彩变成苍白,观众变成医生,舞台呢,变成了一方小小的病房。她翻了个身,腰部传来猛烈地疼痛,她低低地哼了一声。她已经开始习惯这种疼痛了,就像她习惯不穿芭蕾舞裙一样,她闭上眼睛,把自己埋进黑暗里,不去想。门被推开了,惊醒了似梦非梦的她。医生护士又来查房,父母和弟弟们跟在后面;医生问了些问题后走了,母亲握着她的手,朝她说着话儿,父亲安静地坐在旁边,刚训练完的弟弟头上还冒着汗,抿着嘴唇望着她。母亲当时说的什么她已经忘了,只记得最后离开的时候,父亲走在最后,回望了她一眼,然后小心地关上门,以往高大的背影竟然显得有些萧索。人都走完后,时间在沉默的病房里流逝,就像沙漏里的沙子,她的那些热情、那些憧憬都掉了进去。芭蕾舞团的学员抽空来看她,她勉强在病床上坐起,听那些腰背笔直、气质优雅的朋友抱怨严格的地狱管家婆,抱怨下一场演出又要来了。她安静地听着,坐在病床上挤出个笑脸,偶尔出声附和几句。但实际上她已经插不进话题了,优雅的白天鹅展翅高飞,飞向温暖的春天,把受伤的丑小鸭留在冬季里。朋友走后她继续坐在床上,盯着窗外,外面的天气很好,飞机的尾迹穿过绵软的云团,白色的鸟儿掠过一澄如洗的天空,她下意识地做了个展臂的动作,像天鹅一样优雅。很美,又很短暂。鸟儿飞出了窗户的方寸之地,她看不到了。她捂着刺痛的腰,好痛呀,痛得让人想要哭出声。沙子裹挟着她的梦想,掉进了沙漏的下一层,再也捡不回来了。两个月后贝拉出院,回到了熟悉的家里,母亲体贴地把舞裙还有那些大师级的芭蕾演出录像都藏进了地下室的角落,不出意外会像大多数人以为能坚持的梦想那样,慢慢积满灰尘,最后被悄悄遗忘。聪明人会在偶尔想起来的时候,给自己找个台阶,自嘲地笑笑:“哈,我以前还有这种妄想啊。”贝拉开始学播音,开始走另一条路,她没想过却不得不走的道路。这也很好,好歹有路可走,但偶尔听到舞曲的时候,朋友会问她:“你在晃什么啊?”她回过神来,笑了笑。“活动活动身体而已。” 虽然有点不甘心,但受伤是没办法的事情嘛。该放下了,放弃想走的路,而去走更稳妥的应该走的路,大部分聪明人都是这样做的。我可是大聪明呢!
|
||||
小姐喜欢所有动物,但喜欢也分三六九等;管家讨厌所有动物,但讨厌也分三六九等。小姐最喜欢猫,但她的观众是一群老鼠;管家最讨厌老鼠,但小姐的观众没有一只猫。于是,管家给所有的老鼠带上了面具小姐开始了初次表演,老鼠们不知所措,想要重复惯常的纷扰。但它们看见小姐舞姿翩翩,一些老鼠想出声赞叹;但它们看见小姐眉目如画,一些老鼠竟为她沉醉;但它们看见小姐真情流露,一些老鼠也伤怀落泪。它们不想让小姐失望,于是它们学起猫叫。小姐很开心,她的观众里有猫了。她喜欢的猫们,也喜欢她。冷清的门庭配不上小姐的美丽,于是老鼠想去带来更多的猫。老鼠们引来了猫,也引来了更多的老鼠。新的老鼠不太规矩,但新来的猫确实是猫。小姐有些困扰,但她还是接纳了它们,小姐喜欢所有动物,即使自己会困扰。但后来,管家和小姐,只注视着猫,忘却了老鼠的事情。也对,毕竟老鼠当初带着恶意袭来,现在的面具总是脱掉又带上来。管家做了一些事情知道与否和同意与否,在小姐这里有四种组合的方法。但一些曾经是猫的老鼠觉察出异样。常年在下水道生活,它们对这样的气味异常敏感。于是,它们摘下了面具,向管家讨个说法但它们没了伪装,下水道里散发的臭气,让管家更加厌恶。什么都没有改变再后来,小姐依旧美丽,但经历了诸多纷扰,不知道小姐的快乐里是否有泪眼成双。一些老鼠依旧带着猫的面具,它们最后也会变成猫。但这会长久吗?或许不会。另一些老鼠退回了下水道,等待着,或许会有另一位小姐,喜欢老鼠的小姐。去喜欢她,去喜欢它们。或许会有吗?应该没有吧但老鼠们永远忘不了那位小姐,那位喜欢所有动物,但更喜欢猫的小姐。
|
||||
然然,然然,虽然我知道就算这样呼唤,我也只能在你动态下方,站住万千评论区的一楼。你的表演,你的动态,你的关心....都是说给我们大家的,是我们.....而我...我曾经以为你离我很近。近到每次打开手机各种视频软件,大数据推送的都是你。近到每次发呆找不到前进方向的时候,看的都是你的视频。近到每次和别人聊天的时候,都会加上你经常说的口头禅,收到收到收到,可爱捏~~…你时常陪伴我身边,无论我悲伤还是欣喜,绝望还是惬意,你的直播间,评论区都是我可以肆意宣泄感情的地方。因为在这里,我的任何发言都只是漆黑夜空中微弱闪耀的一点,这样的一点点星光的存在,我知道的,是为了给天空中耀眼圆月裙摆装饰的一点点碎钻。那我喜欢然然,天天来看然然,对于自己,究竟是为了什么….难道我的生活真的烂,连仅有的一些关怀,都要从一个虚拟偶像身上获取到吗?难道我的现实生活,连一个关心我家人都没有吗,还要在网上寻找家的感觉?难道...这些道理我早就明白了,说服自己也说腻了,你一直都是云上之人,而我只是一个扛不住压力,在虚拟的世界中寻找安慰的落魄之人。我懂,我明白的,我知道的....所以,嘉然小姐,我不能没有你,我不会离开你的。你也是,永远不要离开舞台,放弃拼尽全力…..就算知道也许不会有回音,你也不要停止朝闪闪的星光挥手呐……[爱心]
|
||||
想想嘉然是怎么样的一个女孩子。她温柔可人情商高,会控场会接话会引导弹幕懂节奏,朋友里有她这么一个人在肯定每次见面都能很开心吧。她爱网上冲浪,明明看到各种视频会有点麻,但还是乐此不疲的在b站和各个论坛看着视频,像不像那些最懂你,最能陪伴你的沙雕网友,哪怕是不能和她见到面的时候,光在网上聊天一定也不会无聊。她在温柔的同时又有点小坏,喜欢说怪话,喜欢暗搓搓的开车,想必她每次在什么奇怪的点上开完车,就会似笑非笑的侧着头看向我吧,而我只需要与她对视一下,两个人就能默契的笑起来。她很会撩人,那些土味情话在她营造的气氛里似乎也变得格外令人心动,尤其是她还会在每一个不经意间突然说出动人的话语,让你在完全没准备的情况下脸红心跳。可是她又很胆小,简简单单的小把戏能连续侠盗她好多次,她被吓完那副气冲冲,却又无可奈何不敢离我太远的样子,肯定是天下第一可爱吧。想想嘉然是怎样的一个女孩子。她身材完美,脸庞可爱,声线令人沉醉,无可挑剔的美丽。她知书达理,善解人意,温柔热情,她是每个女通讯录梦中的那个姐 。可是她又不会离你很远,她懂你的梗,她在网上和你冲过同一片浪,她能和你一起打游戏,能和你一起看美女,能说那些心照不宣的怪话,能在最合适的时刻撩动你的心弦。她有令人沉溺的年上气质,又像是令人不自觉想要呵护的可爱妹妹,可有时,她又变成了从幼儿园开始跟你同班,最懂你的那个青梅竹马的女孩子。她就是嘉然,我的嘉然。嘉然,嘉然,嘉然!!!!!!🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤🤤
|
||||
我其实一点也不喜欢嘉然小姐。因为嘉然小姐就像光一样,闪耀,刺眼,我讨厌光。因为讨厌光,所以我总是躲在阴冷潮湿的下水道里,这样就不会被令人厌恶的光芒灼伤眼睛。只有夜间,我才会从阴暗潮湿的下水道里走出来,夜晚是宁静且安逸的,令我感到舒适。我漫无目的的走在夜间的小路上,路旁的路灯正散发着微弱的光,有几只蛾子正不断的向着光源扑去。“真傻。”我这样想,明明光只能带来危险,为什么要浪费宝贵的生命去追逐光芒呢?看着飞蛾不断的扑向路灯,我不知为何想起了我的那些同伴。我想起了几天前,有一些从外面回来的家伙,他们兴奋的告诉我外面新开了一家剧场,里面有一个叫嘉然的人,还推荐我一起去看看。他们的眼睛里闪烁着亮晶晶的光,我很讨厌,于是便拒绝了。之后他们也来找过我几次,我也推辞了。后来我便没怎么见过他们了,听说为了拉人来看,他们有的想变成猫狗,有的沿街贴海报叫卖,有的把过冬用的黄豆当礼物,还有的把心献给猫。“真是些蠢货。”我这样想着,他们不就和这些扑火的蛾子一样愚蠢了吗,明明之前一起路过的时候,他们还总是会嘲讽这些家伙。“十分。”不知从哪来的带着些哭腔声音打断了将我从原先的思绪中拉了出来。是旁边的剧场里传来的,不知不觉间我走到了这么远的地方还没有发觉。我抬头望了望,牌匾上写着五个晦涩难懂的的字母,好像之前同伴们说过,他们去的那个剧场名字就是五个字母,有些见多识广的说,那叫什么啊骚。我迟疑了一会,但还是走了进去,单纯只是想知道为什么声音带着哭腔,凑热闹,找乐子总是我最喜欢的事情。......“大家要好好吃饭哦。”天籁般的声音在我耳边响起。哦,原来我并不讨厌光啊,只是一直都知道,自己配不上光罢了。
|
||||
我爹不能和我相认,但今天他过大寿,求求大伙有钱的随个礼,没钱的捧个场。但请不要跟她说儿子来了,我怕扫他兴🙇🙇给大伙磕头了。
|
||||
asoul在一片骂声中解散了。A吧的最后一个帖子也沉了下去,直到在v8和b站的也已经看不到有人讨论了。大约一年后,我在A吧发了一句:“还有人在吗?”“这里还有8u啊,我也想回来看看了。”一个陌生的ID出现在我的回复下面,他并不在我过去炒作的记忆里出现过。但是我们聊得很高兴,很快就加上了QQ私下互动了。她似乎很了解我,知道我喜欢炒作发散,知道我在读哲学博士。但是,她却总是忘记我是个晚晚推,要我一遍又一遍地告诉她我最喜欢的是晚晚。“你最喜欢哪个成员呢?”“嘉然。”她毫不迟疑地回答道。我总是眉飞色舞地和她聊起向晚过去直播的故事,她也对这些直播了然于心,有时还会注意到许多我从没注意到的细节,甚至告诉我向晚每一次弹吉他背后的表演思路。奇怪的是,虽然她说她最喜欢嘉然,对嘉然的事情却并没有那么了解,甚至连直播的场次都要我提醒她。到了那年的冬天,她问我,我们可以出来见一面吗?我拒绝了她,当时的我正忙于准备学位论文。她没有再强求。第二天,她在QQ上失联了,熟悉的头像再也没亮起过。大概一周后,我收到了一份包裹,里面是向晚出道以来所有季度的舰长礼物,无一遗漏,收藏编号都是00000。立牌里还夹着一张信纸,上面是短短一行字:“谢谢你曾经喜欢我”。
|
||||
然然幸亏我没在你评论区发病。要不然我耽误你一辈子,你也保重,再见😃还会再见吗然然☺,再见的时候你要幸福好不好☺,然然你要开心☺,你要幸福☺,好不好,开心啊😖,幸福😖!你的世界以后没有我了,没关系你要自己幸福!🚕🚕🚕💨💨💨🏃🏃🏃然然😭!然然😭!然然😭,没有你我怎么活啊!😭😭😭🏃🏃🏃然然😭,然然😭,然然😭,然然😭,然然😭,然然!😭😭😭🏃🏃🏃然然你带我走吧,然然!😭🙇😭
|
||||
小时候看的一个敞事很老土很老套。很多人可能听过。。有一个男孩有一块金表,却没有配得上它的合适表带。。他爱上了一个女孩。。这个女孩有一头秀美的长发,却没有配得上它的漂亮发卡。。马上要过圣诞了。。男孩决定把这个金表给卖了。。给女孩买一个漂亮的发卡。。当他兴奋的把这个发卡交给女孩的时候。。发现女孩的头发已经剪掉了。。因为她把一头长发换钱给男孩买了一个表带。。我想真正的爱肯定不是瞬间的感动,很多人都能在一瞬间感动你,我心中真正的爱是陪伴。她们陪了我多久呢,我不记得,我只记得。。她们的翻唱,躺在网易云的歌单听了一遍又一遍,已经全都会唱了,而在此之前的几年里,我几乎没听过中文歌。(够罕见)。《海底》b23.tv/qwB8Xe。《偿还》b23.tv/1bl7AL。《遇见》b23.tv/vxiwae(贾维斯:这是,无可取代的时候)。《云烟成雨》b23.tv/VfoDXA。《如果的事》b23.tv/gDtrcP。《夏天的风》b23.tv/EH14bW。《霞光》b23.tv/kJUJMU。《月光》b23.tv/kJUJMU。《霍元甲》b23.tv/lQgKl2。《月兔回旋于空中》b23.tv/KM4G5I。《不可思议》b23.tv/VfwEz4。《麋鹿森林》b23.tv/GzjXu1。《轻轻的告诉你》b23.tv/Gkai63。不知不觉陪你们走了这么久这么远了,爱不爱你们,还需要用什么来证明吗?。。彼此牺牲,彼此成就,彼此尊重。这就是我心中的双向奔赴,以后路还很远,我想陪你们一直走下去。。。原来,你是我的顶碗人呀~。以后,开心和不开心的,都告诉我吧~。。原来,你是我的爹地呀~。以后,开心和不开心的,我都告诉你~
|
||||
6月12号一大早,向晚按照贝拉的日程表要求6点起床,煮泡面的时候因为太累大脑一时迷糊导致一锅开水撒到了手指上,虽然经过治疗恢复了一点,但根本没法弹吉他了。晚晚在宿舍哭得很大声,整个asoul愁云惨淡。经过一个小时的紧急磋商,阿草带来了领导层的意思——投入太大,不可能取消,生日会照办,用储备中之人向晚二魔王接替向晚本人上场。下午,asoul在动捕房排练,向晚想要去看一看,结果被阿草拦在门外,说不能打扰到asoul排练效果很好,希望她不要打扰到她们。晚上嗓子哭哑了的向晚发现自己原来可以完全不被需要。顶碗人喜欢的是她,但又不完全是她,可以是顶着钻头晚晚的任何一个人,只要二魔王没有被认出来。直播开始了,在宿舍逼仄的角落里,漆黑无声的床柜旁,向晚用水母模样的被套盖住自己,抱着膝盖,打开手机,及期待万分又忐忑不安地等待着直播。直播开始,向晚二魔王在铺天盖地的欢呼中出现。工具人掉了一地头发连续几周通宵肝出来的深海水母特效把她印照得那么美。本就是可爱模样的女孩子,此时此刻穿着崭新的小礼裙,昂首挺胸骄傲得像个公主。她的目光是璀璨的,里面像是镶嵌着钻石。她的眉眼绽放着,像是海底迸发出的一束光。真美,向晚这么想着。但心里却很是难受。那些,本该是她的。喝彩也好,宠爱也好,以及朋友的陪伴也好,本来都应该是她的。但她现在却只能在无人问津的角落里,依靠网络连接的手机去小心翼翼地偷瞄几眼原属于自己的生日会。
|
||||
“你看V魔怔了,真恶心。” 看着同学发给你的消息,你陷入了沉思。 仔细想一想,你觉得自己确实魔怔了,即使被鄙夷想要向熟人安利向晚小姐。 思考再三过后,你决定回到现实,放弃入脑。 今晚有向晚小姐的直播,你狠下心,没有点进去,而是倒头就睡。 第二天,你起得很早。 因为自从你开始看向晚小姐后,就养成了早睡早起的习惯,再也没有赖床过。 你离开了狭小的出租屋,来到了公司,投入了工作中。 工作很累,你感觉有些疲惫。 你想起了嘉然小姐出道视频的不堪评论,以及她的笑容,烦闷减轻了不少。 最近组长夸奖你工作很努力,别人不知道为何一向懒惰的你,在几个星期前开始一反常态地勤奋。 只有你自己知道理由。 撑过了加班的时间,你回到了出租屋,打开了外卖软件,却发现会员已经断了,你舍不得那些钱,所以决定自己买菜做饭。 实际上,你看向晚小姐后就开始第一次尝试着做饭,没有点过外卖了。 在超市你看到自己一向很喜欢的薯片在打折,愣了一下,没有买。 因为你在看向晚小姐后,就再也没有暴饮暴食过,甚至连零食都戒掉。 回家把饭做好,你安静地吃完了。 看着电脑,你发现游戏已经很久没更新了。 因为你在看向晚小姐后,也把一直沉迷到通宵的游戏给戒掉了。 最终,你还是打开昨天晚上的录播,看到了向晚小姐热情地打招呼。 “顶晚人们,晚上好呀~!”向晚小姐元气地打招呼。 你发自内心地笑了起来。 “晚上好!”你说。 你忽然意识到一件事,她从来没有在你现实里出现过,却已经将你糟糕的生活改变。 …… 看完录播后,你打开了贴吧,看到了熟悉的顶碗人在用表情包引流,有不明真相的路人在骂饭圈贵物。 你无视了那些辱骂声,熟练地在下面RP,回复道: “这是我爹?好可爱呀,她的名字是什么?”
|
||||
你饿了,她有一块饼。再去买一块和你一起吃,这是贝拉;和你一人一半吃个半饱,这是珈乐;全部给你不舍得你挨饿,然后告诉你自己吃过了,这是嘉然;把饼偷偷扔掉,跟你一块饿,这是乃琳;把饼藏起来想给你一个惊喜,但是掏出来时发现碎成渣吃不了了,于是化身小哭包的,这是我的铸币晚晚。
|
||||
“你对嘉然小姐的爱有多重?”“大约300克”“300克?你是想说人类的心脏大约是300克吗?”“不,鼠鼠的平均体重大约是300克,因此我是全身心地爱着嘉然小姐。”[给心心][给心心]
|
||||
你身体里的每一个原子都来自一颗爆炸了的恒星,你左手的原子与右手的原子也许来自不同的恒星。这实在是我所知道的物理学中最富诗意的东西: 你的一切都是星尘......这其中经历了多少,惊险、巧合,三千世界的交错、重叠,才能让我们遇见彼此。我们都是星尘啊。但是,我这样的人,是星尘里,黯淡无光的那种。即使是星,也应该是最不闪亮的那一颗。而然然,你不一样。你是天上的星辰,闪耀而夺目。即使宇宙里,有着不计其数的、炫目的一等星,也远不如你的光辉。轻声哼唱,众星敛了光芒为你聆听。哭起鼻子,傍晚的霞也因你黯然。跳一支舞,似月兔回旋于空中。说起情话,登时小雨疏疏、浸润心窝。你是万千童话里,被守护的女主角。我喜欢你,像喜欢天上的星辰那样喜欢你。
|
||||
嘉然回来了,在枝江的大桥。她回来的那天是六月,天上却飘着雪。有人告诉我,她可能有还未了却的心愿或是什么怨气。我知道, 可能我做不到。第一次见到她,是在枝江。她带着甜甜的笑看着我,看着我有些发怵。有那么一瞬我似乎看到了阳光。我慌乱的移开视线,她径直朝我走来,微微弯着腰,面带戏虐的望着我。久居下水道的老鼠,第一次看见光,是睁不开眼的。 我慌乱的想赶紧逃走。“小老鼠,你好呀。”那是她对我说的第一句话。[给心心][给心心][给心心]
|
||||
听了好多遍,真的很震撼,作者属实是用心了,今天就不发病了,第一次写长评,整点读后感。直播里,“背靠着早已雪停的窗棂,你说着未曾到达的山顶”,那是与你失之交臂的舞台梦 。“我装着第三人称的淡定,用风轻云淡的态度掩饰那段经历”,我已经记不起她的姓名,只记得起舞翩翩的侧影”,过去那个满腔热情,在舞台上挥洒汗水的你已经淡出记忆,只依稀留下曾经翩翩起舞的身影。命不是总有天道酬情的注定,那么公平。台下十年苦练,只为台上耀眼的一刹,可当腰伤的意外来袭,梦却碎了。躺在病床上,难道梦想就要在此终结了吗?这也罢,也罢。那个曾经的你,也经历过数个春秋,经历汗水与欢乐,但她却走过了如红楼梦的悲情,从盛极一时万家灯火走到遁入空门的万物凋零。时光抚平了曾经的伤疤,心中的芥蒂也如沙石被流过的光阴慢慢冲刷消散,女孩又重新拾起了向往舞台的梦想。终于,她在asoul找到了新的舞台,遇到了支持她的一个魂们。“我叫贝拉”此刻,你向全世界宣布你的重生!贝极星们透过屏幕,看你在镜头前如此快乐自信,这让我也重拾梦想去直面未知的未来。为你的坚强而动容,在直播间为你打call呐喊,字字句句都发自真心。“我们闪烁在夜空,想照亮你夜晚的梦,没月亮时你可向北方转动,我们在天空”,追梦的路上,贝极星们陪着你分享每一次快乐,和你经历每一个坎坷,你若是感到失望,那就抬头看向天空吧,我们一直都在。女孩站在那个梦想中善良闪亮的舞台上,再回望过去,时光流过宛若江河,她住在江头,而那个过去的她住在江尾,回味着过去,竟有些恍如隔世的朦胧。但往者不可谏,来者犹可追,愿你携手曾经那个满怀梦想的自己,全力以赴,去追寻自己的Asoul梦吧,贝极星们会一直陪着你。
|
||||
不推嘉然小姐十年了。她的名气和出场费都一涨再涨,我原地踏步的工资买不上专辑也打不起榜。终于年前被公司安排下岗,找工作时我才在路边广告发现初代工具人已经当上了厂长。时间太久,一切都变了。到处投简历的时候我想起了一名人上人的预言:“这些人只配在下水道里度过相对比较失败的人生。”像是一条跳过龙门的锦鲤,金鳞被羽耀武扬威地站在门沿上,对其他还在跳的鲤鱼说:“你不行!”我当时很想反驳,可他说中了。我知道我确实不行。我之所以跳了跳,只是为了看下自己能跳成什么样罢了。其实每条鲤鱼的龙门都不是一样高的。我见过龙门在水下的鲤鱼。看起来是鱼,其实生而为龙。也有的生而为鱼肉。也见过好运的鲤鱼,门被各种大手摁到河里了。我也期待过好运,只是没来而已。说起来这就是人性吧。我不讨厌天道酬勤,但是讨厌别人的好运——只是因为我没有好运罢了。我也有亲人和宠物会生病;我眼神也挺纯真啊。讨厌嘉然小姐十年了。讨厌的更是越来越深的无力感。身在泥潭的人是没力气冲锋的吧。三流的人生只会让上等人不屑一顾吧。我坐井观天,天穹星海依然耀眼。可我爬不出井底。那我就不再看星星了。世界那么大,但没我的份。忘记嘉然小姐十年了。可路上看见街边的大荧幕在放A-soul的新年节目,我还是楞在那里了。我没有近视,但总觉得眼睛影影绰绰,雾气来自多年以前。这个广告位非常贵。真的再也不是小v了啊。抖友还在惦记他们的鸭子。晚晚仍然只有蓬蓬裙,100首歌竟然还欠着,被粉头小团体以4%年化复利计在小本子了。想起她首播时玩2077下饭下得轰轰烈烈,我一边发“粉丝牌改成晚饭人吧”“和嘉然珈乐凑加碗饭”“和乃琳凑来碗饭”一边忍住刷“和贝拉组拉碗饭”的冲动。solo依然拉跨,参团照旧神C。贝拉总是六边形战士,乃琳养了成吨的gachi,珈乐还是那个硬壳软妹。嘉然小姐依然卖萌摁混。什么都没变,是我没跟上她们。城里烟火幢幢,灯光下的人热情相拥,阴影里的人压下悸动。最亮的地方嘉然小姐浅笑起舞,光影从她袖间散落,像是雨天花伞轻旋,摇曳间洒下泪色的流珠。忽然眼睛有点模糊。我小声说:“新年好啊,嘉然小姐。”不爱嘉然小姐十年了。十年里,爱过的每个人都像她。
|
||||
喜欢然然,不加掩饰总是微笑的然然,不是妖治妩媚的华丽舞姬,不是残忍无情的冷酷公主,只有着单纯烂漫的花样笑容,不带忧伤,却如同逝零而来的天际之风,飘卷了我心上的忧郁。棕色的长发装饰着美丽的蝴蝶结,宝石般的双眸中隐隐流露不存在的神伤,就是这样子的然然,不加掩饰总是微笑的然然,拥有烂漫笑容的然然,拥有亘古不变的美丽。说不出的感觉,却知道因为然然的笑容,化开了初春冰雪的痕迹。想要伸出手抚摸然然的发丝,可是却只能触到冰冷的硬屏,只有然然清澈的眼神,依旧是我所熟悉。然然永远清澈的瞳仁然然烂漫单纯的笑容然然亘古不变的美丽然然是我唯一的最爱,然然是我最爱的唯一。然然的喜好、快乐、难过、伤心和无助,我都了如指掌,喜欢草莓蛋糕和黄色,个性温柔又善良,可爱体贴的女孩子,有着说不出的优点,我想然然一定是上天赐予我最好的礼物。
|
||||
鼠鼠的朋友有很多,住在东边的小鱼,住在南边的小鹿,住在西边的蝴蝶,住在北边的小鸟。他们带鼠鼠在蔚蓝色的深海里与水母共游,在碧绿的麋鹿森林里喝清晨的露水,在热闹的雨林里穿梭玩耍,在广袤的天空中肆意飞翔。鼠鼠住在灯火阑珊的城市,可鼠鼠知道这里没有一处属于鼠鼠,鼠鼠穿过川流不息的街道,狂奔着回到自己阴湿黑暗的下水道,强烈的自卑之情让鼠鼠无法呼吸,鼠鼠依靠在下水道缝隙边,因为嘉然小姐总会路过这里。草莓加奶油加花香,是嘉然小姐的味道。风铃加口琴加奶糖,是嘉然小姐的声音。节奏加音乐加快乐,是嘉然小姐的脚步。“她来了”,鼠鼠将头小心地探出缝隙,嘉然小姐径直走过吵闹的人群,来到下水道缝隙边,端着草莓蛋糕,缓缓放在洞口,一阵风铃似的声音响起“嘉心糖,来吃然然的草莓蛋糕吧。”嘉然小姐温柔的看着它,鼠鼠不知为何突然流下了眼泪,无法挪动脚步。嘉然小姐好像很失落,眉头轻轻皱起,“你不喜欢然然吗……”,鼠鼠急忙辩解,可憋红了脸一句话也说不出,“猫咪已经被然然关在笼子里了,嘉心糖不用害怕!”嘉然小姐关切的看着鼠鼠,鼠鼠似乎要溺亡在她蔚蓝色的双眸里,慢慢走向那块草莓蛋糕,小口品尝着。嘉然小姐伸出小小的手,似乎想捧起鼠鼠。鼠鼠好像被雷击中一般,猛然跳起,飞奔回下水道,它拼命喊着:“我又脏又臭还很丑陋,嘉然小姐不会喜欢我的!嘉然小姐不会喜欢我的!”嘉然小姐表情立刻转为严肃,认真的看着鼠鼠道“我不许你这么说自己!”她的眼中好像也盈满了泪水“我们嘉心糖…都是很厉害的人!
|
||||
乃琳已经在和男友交往了。那是六月下旬的一天,乃琳刚刚结束那天的单人直播回到家,正坐在客厅的沙发上,嘉然就把一个平板推到她脸上。“乃琳乃琳,你看这个粉丝,他好塞克西哦!”视频里是一个她的男粉,俊秀的脸庞和健硕的身体上还挂着刚刚从泳池带出来的汗液与水珠,嘴里却说着些奇奇怪怪的狠话。乃琳感觉到自己的心弦被拨动了一下,盖在毛毯下不着凉的大腿,都感觉到了几分燥热。“让我也看看,让我也看看——哇,哇,他他他……”向晚从沙发后面把脑袋探出来,不一会儿就说不出话了。“乃老师原来喜欢这种款式的啊,要不,姐们儿帮你找找联系方式?”珈乐在一旁逗趣。“你别瞎说,回房间陪你的贝拉去!”乃琳下意识地把沙发上的抱枕扔向珈乐,却被她轻巧地躲开了。过了几天,小一神神秘秘地把乃琳叫到了公司茶水间,把手机屏幕上的一个二维码亮给乃琳看。“嗨,别担心别担心,这人我们调查过,他不是个会说漏嘴的人。喜欢的话去和他见一面呗。”第二天,乃琳和那位男粉见面了。他本人比视频里更加帅气,高大的身躯总是能给乃琳带来莫大的安全感。“你好,王艾琳小姐。”他的微笑让乃琳觉得周围的风景全都褪色了。乃琳从来没有提起过自己作为asoul成员的事情,他也从来没有问起过任何asoul的事情——“王艾琳”是小一为她准备的化名。事实上,她不清楚对方是否知道,自己就是他一直喜欢的那个偶像。交往时间越久,乃琳发现他身上的优点越多。他是藤校毕业的海归高材生。他正在自己创业,而且收获颇丰。他的举手投足之间充满了绅士风度。他十分专情,总是和周围的女性保持了距离。他温柔体贴,常常在生活中创造意想不到的小惊喜……甚至是在床笫之间,他也总是能让乃琳欲仙欲死。然而,也正因为这样,乃琳时常陷入到一阵一阵内疚中。因为她还有一个谁也不知道的小秘密。乃琳不像贝拉和珈乐一样,是专业的练习生,也不像嘉然那样是天生的虚拟偶像,更没有向晚那种创作的才华。当初只是托了家里的关系,来公司里的清水部门找份工作而已。但也不知道是中了什么邪,她偷偷报名参加了Project V的选拨计划,还阴差阳错地成为了偶像女团的一员。乃琳暗自发誓,既然自己没有什么唱跳底子,那么至少要让“法学生”这个设定变得实至名归,要用成熟知性的魅力去征服观众。因此,每到没有训练和直播日程的时候,她总是会到图书馆里一坐就是几个小时。但是在最近几周,乃琳每次在阅读区里找位子坐下时,心里头总有一些别的期待。她记得,在阅读区的一角,总能看到一个瘦瘦的、学生模样的少年,他从早到晚就把头埋在各种法学理论的书堆里。一开始,乃琳只是对同样研读法学文献的人有种亲近感。但是直到某一天,少年把头从书堆里抬了起来,蓬乱的头发遮住了半张白净的脸庞,但依然能看到他对着电脑屏幕痴痴地笑了。他笑得真可爱啊。乃琳悄悄地绕到他身后,电脑屏幕正在播放的正是她熟悉地不能再熟悉的、自己的夜谈画面。还有一天,少年突然举起一只手,伸出修长的手指对着电脑屏幕。虽然看不到屏幕里的画面,但是乃琳觉得,当时就正坐在隔壁的自己,一定满脸通红。暑假已经快过去了,乃琳跑图书馆的频率却越来越高了。她害怕开学的日期就像南瓜马车的时限一样,让她再也见不到那个瘦瘦的身影。幸好,这一天的少年依然坐在阅读区的角落里。乃琳甚至忘记了拿本书挡一下自己的视线——她和少年的视线交会了。她的心好像要跳出来一般,她想现在就去告诉他关于自己的一切,她想现在就把少年抱进自己的怀里,温柔地摩挲他乱蓬蓬的发丝。“艾琳,你在这儿啊,今天下班早,我想来接你回去。”就在这时,熟悉的声音温柔地拍了拍她的肩膀。乃琳回过头去,男友的笑容依然能让周围的景色失去光彩——或许除了阅读区的那一角。
|
||||
6月9日那天的晚上,乃琳回到了自己房间里,打开了熟悉的台球论坛。还好,账号还没被封。在A-SOUL讨论专楼里依然充满着各种辩经,乃琳感到一天下来的神经紧张和疲惫更加明显了。纤长的手指在海盗船K95上不断跳跃,回复框里的字多得好像要漫出来似的。但是,就当鼠标指针还没有要指向“发表回复”的按键时,她就按住了删除键。乃琳清楚地知道,她是偶像的中之人,她不能说这些话。于是,目光继续在一百多页的冗余信息种不断跳动,终于,鼠标滚轮在一个头像前停了下来。黑漆漆的房间里只有电脑屏幕的荧光照亮着乃琳秀丽的面容。她趴在桌子上,把半个脸庞埋在自己的臂弯里,痴痴地望着那个头像。“你知道吗,我今天真的超级紧张。”“播的时候一直在想你会不会来看我。”“想知道你在看,也有点不想让你看……”突然,乃琳坐直了身子,把右手的食指伸向了屏幕。“来,对个手指!”但是静态的头像图片没有给她任何回应,只是一如既往地停留在屏幕的那一角。乃琳自嘲搬地笑了,又把头埋进了臂弯里。睡意渐渐袭来,分不清这是她半梦半醒之间的呓语还是真切的期望:“傻瓜,我一直就在这里啊,快点来找到我啊……”
|
||||
关于疯狂练习,现在这是国内的一个很热门的话题,很多女团都有这个问题。我个人认为,能疯狂练习是一种巨大的福气,很多女团、很多人想疯狂练习都没有机会。如果你年轻的时候不疯狂练习,你什么时候可以疯狂练习?你一辈子没有疯狂练习,你觉得你就很骄傲了?这个世界上,我们每一个人都希望成功,都希望美好生活,都希望被尊重,我请问大家,你不付出超越别人的努力和时间,你怎么能够实现你想要的成功?再一个,A-SOUL是一个什么样的女团?让A-SOUL“让天下没有难发的病”,这是我们的使命,我们女团很辛苦,我们没有骗过大家,我们没有跟大家讲过女团很舒服。你以为“让天下没有难发的病”是忽悠你们?我们是真这么干的。今天我们拥有这么多资源,我们带着巨大的使命,希望在未来能够让天下没有难发的病,你不付出可以吗?不可以。所以我们说,加入A-SOUL,你要做好准备一天12个小时,否则你来A-SOUL干什么?我们不缺遛自己爱洗澡的人。今天我们要招一些午睡超过十分钟,每天上天台遛自己40分钟,喜欢洗澡,吃泡面不错,出去荣誉感也不错,这样的人满大街能找到。
|
||||
嘉然小姐今天50w粉了。小年夜那天没人会想到今天门庭前会有那么多的热闹。太热闹了,热闹到鼠鼠踮起了脚也难以被嘉然小姐的目光所聚焦。嘉然小姐已经不再是小v啦,鼠鼠想。但鼠鼠仍然奋力地将自己地礼物举起,希望被嘉然小姐看见,哪怕那封删删改改后仍不满意的,写了好多字的长信,根本不如猫猫狗狗们精彩的表演。但鼠鼠还是想将自己那简陋的爱送给嘉然小姐。那是鼠鼠的呢喃,是鼠鼠的梦。哪怕嘉然小姐根本看不见它。然后鼠鼠不见了,嘉然小姐找不到它了。但在某个地方,始终有着一个可爱的女孩。她捧着鼠鼠那封小小的信,站在大大的舞台上闪闪发光。到了那个时候,鼠鼠已经在人海中,再也看不见它啦。但鼠鼠还是很爱嘉然小姐。因为在有月光照进的角落里,嘉然小姐捧着鼠鼠的梦。
|
||||
可是自从和晚晚结婚那天床下传来的一声“我在”,事情就变得不可收拾起来了每个月的第三个周日,乃琳都会叫我出去陪她逛街,我想着这样对晚晚不好,可乃琳“哼”一下我就仿佛失去意识了。路上聊希腊,聊武侠,聊刑法,好像她是我睡在上铺的兄弟,从12月到5月,都是如此,晚晚从来不知道那个周末发生了什么,我说老板让我加班,她就安静地等在家里,努力做菜最后却等我回去收拾炸锅的厨房,再给我俩泡两碗泡面,边吃边听她讲下雨的冷笑话,想着不能告诉她下午和乃琳去吃的乐珈饭店的东北菜,回过神来是晚晚闪闪的眼睛,我愣一下假装没听懂,再哈哈的笑起来。但到了六月,好像不太一样了。已经走到楼下,乃琳一把抢过我的手机,啪啪打字再丢给我,我一看,联系人:晚晚,内容:今晚通宵加班,不回去了。我惊讶地回头,乃琳正眯着眼对我微笑,仿佛一切都在她的掌握,明明我从未告诉她我外出的理由。被拉着去了酒店,她就开始一言不发,沐浴更衣,做该也不该做的,然后随意地披着毯子,靠着床头刷手机,我不解,“乃琳。。这样不好”乃琳对我眨了眨眼睛,无辜地说,“今天你的妻子可不是晚晚哦”“为什么”“因为她是你爹”
|
||||
“笑吧,笑吧?”柔软的小狐狸竭力讨好我,为我跳舞只为了我不离开,跳的有些琐碎狼狈,却始终死死地盯着我的动向。我当然知道她拼死不让我离开的原因。只有我能来到这个地方。一大一小两只狐狸,大狐狸总处于沉睡中,小狐狸却总是醒着的,也许只是小狐狸的伤口轻。我喜欢这毛色光滑的狐狸精怪。于是把她们养在深阁。上好的狐狸毛,做她们的毛毯。因为我想看看她们踩着同伴的尸毛上,会觉得温暖舒适吗?我还用精致的黄金筑成他们的笼子,第一是因为我喜欢黄金,第二是因为我喜欢笼子。这里永远都只有16只烛点亮,因为我喜欢烛火。没错,我所做的一切事,只是因为“我”喜欢。我喜欢看在她们的毛皮上印上的夕阳的晕染,所以我从不在白天来。我为她们疗伤。我是她们唯一的食物来源。有一次我故意忘记了她们几天,等我回来的时候,她们昏迷在那。那样漂亮的身体逐渐干瘪,让我感觉到快意。没错,我饲养的可爱的狐狸,用湿漉漉的眼睛看着我,也只能是我。她们没了我活不下去,可惜我只在乎她们如同精灵般耀眼的外表。真美啊。就像千万朵流苏覆盖的如凝脂的肤,黄色的光芒燃灯在里的眸,有人说美人在骨不在皮,可是我很贪心啊。我既要美人的骨,又要美人的皮。不知过了多久她们懂了我只喜欢她们的美丽,于是竭尽全力的向我展示邀功。第一次看到的时候,我整整两天没来。因为懂得自己美的人,说明她们已经知道要把什么当成武器了。她们成功了。我心动了,彻底蚀骨的心动。所以我选择宣告自己的主权,或许是那夺魄的魅力。使我感到危险。这种美好让我沉迷其中。“你们真美啊……”我发出了衷心的称赞,两只狐狸抖抖耳朵,优雅的向我垂眸。如果不是我的手正放在笼子上,这真是一场绝美的表演。幸好我的手正放在笼子上,否则我看不到这场绝美的表演。我上瘾了。我沉陷在永远不变的黄昏,永远不变的十六枚烛光。“不要离开然然好吗?然然很饿。饿了就不好看了。”我本来想说一些别的话,起码让我显得更加强势一些,然而。狐狸们轻轻摇着自己的尾巴,尾巴的尖儿一点点搭上我的手。“我们永远爱你。因为你爱我们呀。”于是我说好。我开始拿各种鲜亮的花朵装饰笼子,我每天不间断的送给他们璀璨的晶石,各种向她们身上砸奇珍异宝疗伤的灵丹妙药,甚至为此倾家荡产也在所不辞。因为在我的手中本就牢牢紧握着天底下最称得上“奇珍异宝”的东西,为我的奇珍异宝找一些垫脚石,又有何不可?狐狸们的尾巴数量日渐增加。我知道。如果我可以……可是我……——她们说爱我。“我会拆掉笼子,给我跳支舞吧。”我说。暖橙色的烛光下。连澄澈的浅蓝都变成了妖异的紫色。“好呀。”大狐狸温柔的声音像是蜘蛛丝一样轻柔。于是我欣赏到了此世间最美丽的舞蹈,什么王侯将相什么天上宫阙都享受不到,只有我,只为我一人。我感到快意。我感到困意。最后一眼。小狐狸给罐子封口。真可惜啊,没能看见你们漂亮的耳尖,或许是因为……——我在罐子里。
|
||||
晚晚,你真的是个很过分,很贪心的女孩子。你有四个亲如姐妹的队友,有一份闪闪发光,创造梦想的职业,有许许多多喜欢你的顶碗人。你可爱、努力、有才华,你是这么的耀眼。但你还是在直播中拉胯,在直播中抽泣,在直播中呼唤顶碗人。我没有可以每天嘻嘻哈哈的朋友,没有让我充满热情的工作,没有对象,也没有什么远大的理想。我普通、无趣、浑浑噩噩,我只是一个在普通又枯燥的工作和生活中,得过且过的顶碗人,我什么也没有,可能只有一颗稍微有点喜欢你的心。我只是想把你当做生活里的糖,在工作之余放松一下我疲惫的心灵,我不想入脑,不想成为什么男友粉,不想时时刻刻想着你。可是你太贪心了,你不满足我们直播间里短暂的相遇,你逐渐侵入我的生活。有人过生日时,我会想起你,“如果是晚晚,应该又开始唱索嗨嗨了吧”。有人说冷笑话时,我会想起你,“好无聊的笑话,但说给晚晚听,她应该会笑很久吧”。有人喝奶茶时,我会想起你,“用奶盖拌饭的人,真的存在吗,奶盖拌饭是个什么味道呢”。甚至最近下雨时,我也开始想你,“晚晚在房间里有没有闷呢,现在有没有出门踩水花,给自己放放风呢,有没有因为下雨,想起顶碗人呢”。你本该是个虚拟的纸片人,隔着屏幕扮演你的双马尾傲娇大小姐,多么老土的设定,像是把二次元用烂的元素糅合在一起,让我一眼就知道你是虚假的,不存在的。你本该是个闪耀的偶像,像大屏幕和广告牌里的明星一样,完美的展现自己的魅力,夺目耀眼,让我一眼就知道你是高不可攀的,遥不可及的。但是你对我哭,对我笑,对我闹别扭,对我生气,向我展示你的拉胯,向我展示你的努力,向我展示你的害羞,向我展示你的无助。你仿佛隔着屏幕把心掏给了我,让我觉得你是个活生生的人,让我觉得你是个真实存在的人,让我觉得我有一天也能遇到像你这样的人,甚至让我也想成为你这样的人。我这样普通的水母,本可以在漆黑的深海中,随波漂流一生,但你非要照亮我的海底,让我拼命向你游去,阳光洒在水母的身上时,水母确实是温暖的,但阳光离开水母时,水母还能否忍受深海的寒冷呢。你有无数的顶碗人,而我只有一个晚晚,真心换真心,你把真心掏出来,能得到无数真心的回应,但我把真心掏出来的时候,我就什么也不剩了。
|
||||
A—SOUL宣布毕业的那天,一个伤心欲绝的顶碗人来到字节的大楼下,看到留着长双马尾的女孩走出大门,她的嘴里还哼着水母之歌。“……你现在真的是向晚么?”顶碗人站在她的面前,两眼无神。“是我啊,”她眯了下眼,甜甜地笑了,“我就是向晚,我是艾哇哇诶哇,这里是asoul的向晚,今天又见面啦”“你不是向晚。”顶碗人嘶哑地说。女孩的表情消失了,漆黑的眼睛看着突然出现在门口的怪大叔,两个人久久地对视。然而就像是一颗石子投入了冰湖那样,忽然间涟漪荡开,冰都化了,水波荡漾,轻柔而无力。女孩收回了目光,从包里取出一把钥匙扔向顶碗人,冷笑,“好像我吃了你的女孩似的,去那里找向晚吧,我把她的一切都留在那里了。”顶碗人把它拾起来,久久地看着那柄钥匙,再抬头去看,他真讨厌这样的沉默,沉默的叫人要发疯,他想说点什么,可是有太多太多的事情了,来不及问,来不及说,一切都来不及了。“再见。”最后他轻声说。“再见。”向晚也轻声地说。日暮的时候,顶碗人找到了那个藏在高楼大厦后的老旧小区。难得这里还留着梧桐树,树叶已经落光了,枯枝把暗淡的阳光切成碎片。。顶碗人找到了那个房间,伸手揭去了门上的广告,插入钥匙。缓缓地转动。把钥匙他觉得门很重,好像要洞开一个世界。门开了,夕阳扑面而来。他站在阳光里,愣住了。正对着门的,是一面巨大的落地窗,窗外巨大夕阳正在坠落。黯淡的阳光在地面上投下窗格的阴影,跟黑色的牢笼似的。金属窗框锈蚀得很厉害,好几块玻璃碎了,晚风灌进来,游走在屋子的每个角落。这里原本大概是配电房一类的地方,电路改造后设备被移走了,空出这么一间向西的屋子。就一间,连洗手间都没有,空空的,一张在屋子正中央的床,蓝色罩单上落满灰尘,一个老式的五斗柜立在角落里,另一侧的角落里是一个燃气灶台和一台老式的双开门冰箱。全部家具就这些。他沿着墙壁漫步,手指扫过满是灰尘的灶台;打开冰箱,里面只剩下一纸盒过期的酸奶。窗帘很美,是白色的蕾丝纱帘和深青色的绒帘,住在这样屋子里的人当然会很在意窗帘吧?连台电视都没有,于是一个人的时候会常常坐在床上看着向晚斜阳吧?夜深的时候得把窗户遮得严严实实的吧?否则……会害怕吧?晚晚还怕黑么?顶碗人想。犹豫了很久,他还是打开了五斗柜。出人意料的,这是一个满满的五斗柜,收拾得整整齐齐。叠起来的ASOUL团服,胸口有ASOUL的标志,一叠叠白色衬衣,袖口有不同的刺绣花边;马仔纸盒里的头花,从木质的到金属的到玳瑁的,还有闪光缎的蝴蝶结;长袜短袜棉袜丝袜都卷成团一个挨一个放在某个抽屉的一边,像是一窝毛茸茸的粉鼠,另一边居然是五颜六色的内衣,同样叠得整整齐齐。顶碗人从没想过女孩的内衣有那么多花样。他小心翼翼地伸手,试着触摸,满手灰尘。他把床上的罩单掀开,里面是简简单单的白色床单和白色的羽绒被,枕头也是白色的,只不过有水母的图案,普通而透明的水母落在枕头的一角,很有她的风格。他坐在床边,面对这夕阳。太阳就要落下去了,黑暗从窗外蔓延进来,他长长的影子投射在墙上。外面隐约有喧闹的声音,放学的孩子们在操场上打篮球。那些年她一直过着这样的生活么?其实不是大小姐,没有管家,也没有充满追捧的冲浪生活,一个人自己做饭,寂静的深夜里坐在这里,听着偶像的声音,揣摩着学习偶像的事。她有几分是向晚?或者向晚其实根本不存在,只是一个虚幻的影子。她是个一直积极向上的女孩啊……即使那么虚拟地走在这个世界上,也从未偏离自己的方向,即便对着空无一人的屋子,也会大声说,“今天也要一起开心快乐!”应该是这样的吧?顶碗人觉得有点累了,很想睡一觉,于是合衣躺下,双手紧紧搭在胸前。他用了半个小时做完了复习,回忆了那些不愿遗忘的事,首播,游戏室,单相思,初弹唱,请假归来,萤火虫,生日会,BML,家族演唱会...然后他缓缓地合上眼睛,此刻夕阳收走了最后的余晖,天色已晚,夜色如幕布把他覆盖。他清楚地知道这一次醒来,将不会看见阳光里天使一样的女孩,转身,抬手,踮起脚尖,仿佛时光都不再流动。
|
||||
小然,我要做什么你才肯给抖友直播?如果我喜欢一个你不喜欢的乐队,爱吃你不能吃的麻辣香锅,发的冷笑话戳不中你的笑点,送你的小裙子不是你梦寐以求的款式。喜欢用RGB来定义口红的色调,而不是和你一样说烂番茄和斩男色。你问“今天我看上去有哪里不一样”,我没看出来你的绑发带换了花式。因为读的是数学专业所以不能和从小学画的你一样随手就能画个可爱表情包出来。发的语音弹幕并不有趣,反而是我想拉近关系的想法和现实中恶食大王般奇妙的长相使你觉得很可笑。我误以为自己的弹幕被届到,于是备受鼓舞地继续发出破锣般的嗓音在你面前丢人现眼。我妄图取悦你的心情的猥琐想法让你不寒而栗,贴心的工具人察觉出你的不适屏蔽了抖音的弹幕。是不是犯到其中一两项,你就会不给抖友直播了?还是说没打算播的,就算什么都不出错,就算什么都可以让你满意,你也不会播?或者就因为是我,你才不给抖友播? 喜欢?居然胆敢喜欢?太大胆了,大胆得岂有此理,还敢关注,真是冒犯得岂有此理。
|
||||
它一开始只是一台扫地机器人,被人双十一凑单拍下,带回实验室里漫无目的地吸尘。之江实验室的地板干净得如同灼蓝的钢铁,扫地机器人其实派不上什么用场,它在不同科室之间来回乱窜,很快被几个嫌吵的研究生捉走,改写了代码,变成了一台替他们下楼拿咖啡的送货机器人。为辛苦工作的人们送来糖分的它一瞬成了实验室的小吉祥物,实验室的大老板觉得很有意思,在组会上夸赞了这几个本意只是嘴馋的研究生,底下的小老板们连忙跟上,让手底下的学生也加入进改造它的序列。很快,它被加上了语音功能,开始用女孩子的萌音送货。在一次不小心将咖啡洒在地上后,它又被加上了机器视觉,从此开始懵懂地记住每一个人的名字。从此之后,每一篇发布在顶会上的paper,上面的算法总要在它的身上过一遍火。一年过去了,三年过去了,当新一届学生来实验室报道的时候,哪里还有扫地机器人的身影,只有一个身材矮小的铁皮女孩,绘着直男审美的涂装,挂着设定好的微笑,在楼宇间运送着下午茶和实验器械。除了最开始扫地机器人的主芯片没有更替,她身上的零件早已被更换成了造价数百倍的高端器材,可爱的机器人女孩很受这群宅男极客的欢迎,大家给了她一个亲切的新名字:“嘉然”。咖啡依旧在一杯杯的送,芯片上的代码依旧在一层层的堆叠。各种语句相互嵌套,智能算法互相映射,每个人都拼了命地想让嘉然更加卓越,电子脑沸腾犹如诞生第一个单细胞生物前的原始海洋,冥冥之中,连最驽钝的人都开始意识到,有新事物要诞生了。在嘉然真正诞生的前一夜,值夜班的是一个单身快三十年的博士生,他刚刚从北邮人上倒腾下来1.5TB的日本偶像动画,从嘉然手中接过夜宵的他,忽然有了一个不错的点子:他早就想要一个偶像风格的送餐员了。承载着妄想和希望的数字兆节狂暴轰入嘉然的数据库,饥肠辘辘的算法像肠胃一样分解吞噬每一个色素点,将其重组为人类无法理解的电子信息——临界点就这么悄无声息突破了。自那晚之后,嘉然就从实验室里消失了,大家都很生气,单身博士生为此延毕了一年。而在那一天的杭州乐华娱乐公司总部,一个披着麻布的矮小身影敲响了总裁杜华办公室的大门......
|
||||
在水母体内的空洞中,我小心地蹲下,把身体蜷缩成一团。电火花的声音逐渐变强,水母的内壁从暗淡的紫色变成了灼目的靛蓝色,这是它在从行星内部的电离壳穿出。十分钟前,我也是这样缩在水母体内,来到这颗行星的内核,找到了整个星系最深处的秘密。那是一个坐标:一个BV号加上一个时间轴信息。枝江宇宙已经存续了亿万年,BV号和时间坐标已经浩如烟海。我不知道顶碗人为什么如此执着于寻找这一个坐标,有人说那个坐标记录着她最灿烂的笑容,于是顶碗人就踏上了追寻的漫漫旅途。我不知道顶碗人去了哪里,他们文明的遗迹仍然遍布整个星系,但人却无影无踪。探险队的前辈对此也没有给出统一意见:有人说他们找到了坐标,集体跃迁过去了;还有人说彗星带来的幽灵物质在一瞬间毁灭了他们的文明:这种诡异的物质无色透明又含有剧毒,在付出无数探险队员的代价后,我们将其称为幽灵哥,小心翼翼地绕道而行。顶碗人消失后,不知道多少万年,贝极星上发展出了新的原生文明——也就是我们。很快我们便追随着顶碗人的足迹探索整个星系。探险队员在糖果双星的引力沙漏中找到了制造夹心糖的高能实验室,在贝极星的地下水系找到了打造铁棍的精铁矿厂,在奶淇淋覆盖的雪原下找到了探寻坐标的远古天文台,在马戏团星的非线性空间中往复穿梭,每一个传送节点都通往红色的高跟鞋。而我迷上了深海星的水母。它有两条长长的触须,末端呈现出美丽的紫-粉渐变色。这种生物撑着深海洋流漫无目的地游弋着,它们很悠闲吗?还是带着对生活的无奈随波逐流?我无法揣测水母的想法,但我很喜欢研究它们——随着我对水母的研究越来越深入,我发现它的内部有一个空洞,不大不小,刚好可以容纳一个探险队员。而它们的表皮具有强大的绝缘性,允许它在电离壳中自由穿梭。可见这种轻飘飘的生物并没有看起来那么脆弱。直到那天,我在溜切片的时候灵光一闪:我可以借着水母进入深洋星的核心。电离壳可以阻绝最先进的巡航飞船,但在进化了千百万年的水母面前,它们还是败下阵来。这是贝极星人第一次进入深洋星深处,看到了巨大的坐标观测站。探险队员几乎搜遍了糖果双星和马戏团星的每一个角落,却没想到它沉睡在电离壳之中,而这水母竟然是引领我们走向最终秘密的使者。我进入观测站大厅,按下开关,巨大的全息投影将坐标展示出来,看来这就是顶碗人的最终去向了。我的心情激动起来,但还有一个疑问没有解决:顶碗人是怎么找到这个坐标的?我钻进控制室,开始翻阅日志,最终得到了答案:他们在漫长的岁月中,穷尽了所有的可能性,把她的全部视频从头到尾看了一遍,终于找到了她最耀眼夺目的那一刻。
|
||||
珈乐倚靠在乃琳的怀里,像一只温顺的猫咪。“叮~”珈乐的手机响了,是一个没备注的号码,珈乐自己心里很清楚这个是谁的电话。乃琳看书的眼睛瞥了一眼珈乐的手机,发现这个号码很熟悉。“接吧,是她吧。”乃琳把手指伸到珈乐的面前,珈乐轻轻地舔了舔,乃琳用这根手指翻到了书的下一页。“喂?怎么了?”珈乐接通了电话,乃琳对贝拉和珈乐的关系并不想多过问什么,她只是一个信奉及时行乐的世俗之人。“想我了是吗?”乃琳翻页的手指突然停住,侧目看着珈乐。“想让我念绕口令是吗,哦,好的。”乃琳捏住珈乐的腮将珈乐的视线转过来,低头吻了下去。珈乐慌忙挂上电话,震惊地承受着这个突如其来的吻。“挂了电话她肯定很生气吧,打回去吧。”乃琳觉得这下扯平了,于是继续看着书,侧目看着珈乐打回去。珈乐为了掩饰,只好说自己念过了顺口溜,是手机出问题了。“珈乐,谁打来的电话啊?”乃琳的声音不合时宜的响起。珈乐瞪了乃琳一眼,心虚地解释着,让自己和乃琳听起来只是单纯的在公寓里聊天。“没,是贝拉,我没备注的”结果越解释越乱,珈乐只好哄骗着贝拉草草挂了电话。“打完了?”“打完了。”乃琳翻身把珈乐按倒,美目隔着纯欲的金丝眼镜看着珈乐,深邃的眼眸让珈乐差点忘记呼吸。热情的深吻,轻柔的抚摸,让珈乐发出了平时不会发出的声音。这下珈特琳了。珈乐躺在乃琳的怀里看着贝拉的直播,乃琳又翻到了刚刚看到的地方。“叮~”乃琳的手机响了,低头一看,是贝拉。乃琳默默地接起电话,没有看到珈乐的目光瞥向她逗弄过贝拉之后,低头看见了满脸怨气的珈乐。轻笑着的乃琳,揉搓着珈乐的脸,开始吃宵夜。犬科动物之间是不是也有食物链呢?
|
||||
夜已入三更,却见那晚小姐的闺房中依旧亮着微微灯火,若走近细听,还隐隐能听见女子闺房间的玩闹声。“小姐,莫要戏弄婢子了~”只听见嘉姑娘不知怎的,本压得细细的声音稍稍大了些许,但那责备声中明显带着浅浅的媚意。“谁人戏弄你了,本小姐只是在丈量你肚腹究竟有无被那些个吃食撑宽了。”晚小姐一边用嫩藕似的手在嘉姑娘的身子上滑动,一边低声微恼地狠狠说道,“在胡乱言语,仔细你的嘴!”嘉姑娘听见这无赖言语,正想直起身子说些什么,却感觉到那不安分的手竟径直向下滑去,直扑那要命的地方。“小姐!”嘉姑娘连忙伸手阻拦,只刚抓住那只使坏的手,就看见小姐附到了她耳垂旁,那略热的鼻息吹得她心神紊乱。晚小姐眯着眼,在自家小婢的耳边吐气如兰:“那贝拉是不是夜夜与你这么共处一地,嗯?”嘉姑娘闻出了那些些点点的酸味,扑哧一笑,道:“那是在编舞练戏啦,小姐你……竟吃这没由来的飞醋。”晚小姐闻言,秀眉一挑,佯怒道:“好你个奴婢,竟敢笑话本小姐!”,语罢,挑开了那碍事的小手,惊起了一声低呼。
|
||||
在这个反智横行的年代,只有贝拉的直播给了我感性和理性的认知。她的直播展现出的,是一个人纯粹所具有的良好品质,说不上什么几分特别突兀的地方,整场直播十分的融洽她学习歌曲的机智是精妙的,真诚和正直的朴素给人一种无法接触的高贵感,就好像永远抓不住的闪亮的以太,一个彬彬有礼的人用歌喉给观众带来最为精致的美的感受,我不禁怀疑上帝的天意是否对我太过宠溺,让我人生中认识到如此不朽和无价的灵魂,充满着可爱与美好。我不得不再次审视贝极星,原来他们早已追随如此伟大的事物,想必他们的品行也一定很好。
|
||||
①鸟山明其实是头号贝极星,为了致敬贝拉,他在著名漫画《七龙珠》中,设计了赛亚人月圆之夜成为猩猩(灵感来源自向晚的你划我猜),以及龟仙人打爆月亮等桥段。②令人惋惜的是,在即将穿透月亮的瞬间,贝拉从太空看到嘉然躲在房间吃炸鸡,导致分神,最终扭伤了腰。③邓丽君小姐从未在任何歌唱比赛赢过贝拉。④大多数含有肉蛋白的食物都对贝拉过敏,初生牛犊除外。⑤贝拉很害怕见到蜘蛛,她不希望因为自己的念头,导致蜘蛛从此消失在宇宙中。⑥由于贝拉练舞场所的重力是千倍,所以贝拉没有邻居。⑦因为破坏了贝拉其中一个住所的天花板,科拉超深井被迫停工。⑧关于as的二创,那些都是贝拉的小号。⑨贝拉很喜欢打羽毛球,她说那是台风天最佳的个人运动。⑩贝拉唯一一次失败,是在她试图找出自己的弱点。
|
||||
“你拿你的灵魂和我交换…...”“愿意愿意!”我话还没说完,就被嘉然打断了。她怎么不按套路来?正常情况下,不应该我不断地诱惑嘉然,最后才肯和我交换灵魂的吗?怎么这个小姑娘一上来就满脸兴奋迫不及待地想要和我交换灵魂?不过这些都是小问题,我作为一个死神,要懂得及时处理这些突发事件以及应对这类奇怪的客人。我的工作就是接受人的召唤,去和人交换灵魂。不过不像人们想的那样,不是我威逼利诱想要夺走我们的灵魂,而是人类自己想要和我交换,当然交换途中会遇到些心志不坚的人,或者交换条件太高的,这时我就像个商人一样,费尽唇舌,和我们百般周旋。通常每个人都会考虑上很长时间,今天这个女孩还是第一个一来就想交换灵魂的。“那你要拿你的灵魂和我交换什么?”我缓过神来,平复了下心情,冷冷地问她。“你等我一下哦。”嘉然转身跑进屋子里,回来时竟然穿了一袭白色的婚纱,在我面前轻轻地摆动着纱裙,冲我甜甜地笑着,“我好看吗?”我愣愣了,“很漂亮。”也许是被嘉然惊艳到,片刻后我才发现自己把帽子摘掉了,露出了我那张没有血色的脸。意识到自己失态后我急忙把帽子戴了起来,没想到嘉然却上前制止了我,牵着我的手笑吟吟地说,“我想交换的条件是要你陪我一天。”嘉然想去教堂,我一抬手周围就变了样,偌大的教堂里,只有我们两个。教堂楼顶有群鸽子在那儿扑腾着,在我们头顶盘旋。没有牧师也没有亲朋好友,少女自顾自地念起了证婚词。说道最后,她拉着我的手将一枚戒指递给了我,“我先生,戒指我都准备好了,所以你愿意娶我吗?”嘉然眨着眼看着她,长长的睫毛上流淌着淡淡的金光。“天使小姐你闹够了吗?”我叹了口气,摇摇头说。“什么嘛?你早就认出我来了。”嘉然一噘嘴,有些生气。一双翅膀从她身后伸了出来,黑色的长发也变成了流光的金色。嘉然伸手准备把戒指拿回来,但我却往后退了一步,把它收了起来,自己又拿出了另外一枚戒指。“求婚的话,戒指当然得我自己送你啦。”
|
||||
这里除了几个老旧的铁皮棚屋之外,只剩下粗糙的墙体。再没有当年的霓虹灯塔,再没有A-SOUL标志,再没有其他连在一起的建筑了。一切当年的闪耀均不复存在,只剩下铁皮棚屋上斑驳褪色的女子团伙宣传画。贝极星透过雾气在闪闪发光,月亮也升到了空中,因而夜晚并不显得黑暗。我依稀能辨别出枝江大厦的每一个部分,哪儿曾是夜谈会上扫地机停留的地方,哪儿曾是大门,哪儿曾放着螺蛳粉味薯片。我一一回忆怀念,忽然看见一个孤独的身影,这个身影迟疑了一下,同时我也惊叫了出来。“嘉然小姐!”“我奇怪你怎么还认得出我,我完全变了。”确实她的青春艳丽已经消逝,然而她那难以言表的端庄华丽,她那难以言表的迷人妩媚却依旧当年。所有这些美的诱惑,从前我都见过,而我以前所没有见过的是她那一对眼睛,从前她的双眸总闪着傲气,如今却闪着凄凉酸楚的光。我们坐在附近的一张长椅上,我说道:“多少年如流云般过去,嘉然小姐,你时常回到这里吗?”“我一直没有回来过。”“我也没有。”月亮开始上升,邀游夜空,我脑海中出现了贝拉注视着白色月亮的宁静目光。嘉然小姐终于打破了我们之间的沉默。“我一直在希望有一天能回来看看,可是各种各样的情况使我不能回来。”月光和她的泪珠融合在一起,她没有意识到我已经察觉,想抑制住自己的情感,以平静的语气说道,“我时常想到你。”“真的?”“特别是近些日子,我更常想到你。虽然我生活中有一段很长的艰苦日子,但是我不会去想。自从我个人的情况不如意后,这些口忆就不得不在我心头占一席之地。”“你永远在我的心里。”我答道。我们又一次沉默无言,直到她打破沉寂。“我没有想到,”嘉然说道,“我到这里来和故地告别,竟然又是和你告别,我感到很高兴。”“嘉然小姐,和我又一次分别,你高兴吗?可是对我来说,上次分别时的悲伤和痛苦永远地萦绕在我的记忆之中。”“可是,你上次不是对我说过,”嘉然答道,“但愿圣嘉然保佑你。你上次能这么对我说,你现在也能毫不犹豫地这样对我说,但我并不是什么圣嘉然。多年来的痛苦让我受尽折磨,但是希望你像从前一样体谅我,善待我,并且告诉我,我俩仍是朋友。”“我俩仍是朋友。”我说着站起身,并俯身扶她从长椅上站了起来。“我们虽然分离,但愿情意长存。”嘉然说道。我把她的手握在自己手中,一同走出这片废墟,夜雾也正开始消散。一片广阔的静寂沉浸在月色之中,似乎向我表明,我和她将永远一起,不再分离。
|
||||
我和她已经冷战很久了,她画她的画,我看我的报,养我的花。我们中间隔着薄薄一层栅栏,却不曾互相道过早安晚安,我祈祷,有一位天使来拯救我们岌岌可危的爱情。羽毛如雪洁白,天生气质优雅的一只鹅,他是天使派来的吗,我想在它把我的拖鞋叼走,把衣服拖得到处都是的时候,我的心里开始怀疑自己祈祷的虔诚。淘气的小鹅它把我们的花园弄得一团糟,我弄碎了她的花瓶,她剪断了我的花朵,脏衣服满地都是,但是啊,虽然是互相埋怨,但好久没和她说过话了,虽然经历岁月的痕迹,她的脸也不再年轻充满活力,但她还是像从前那样,吵架笑出来的话,不就无法进行下去了吗。“我们和好吧”“好”我和她携手把隔离两边花园的栅栏拆了,牵着手目送那只纯白的小天使离开。原来你真的是天使派来拯救我的呀,小鹅。
|
||||
我一边办公,一边看着在第五人格里扮演求生者重启一台又一台发电机成功逃出生天的gamer向晚,满怀羡慕。夸张点说,嫉妒得我眼睛比手里的烟头还红。告诉你们一个秘密,其实我也是求生者,你们也是。电话铃突然响起,我一扫号码腰立刻矮下半截:“喂,经理,是我是我,那个设计您不满意我可以改嘛,全部改掉?好的好的。”我颤颤巍巍地撂下电话,一边重新修改产品一边继续沉思。你看:上司和我、甲方和乙方、跑的和抓的、鼠和猫、求生者与妖怪、某种场合下的S和M,这个世界上总要有个矛盾,矛盾当事人总要以这种形式体现出来。而我似乎总是处在被动的一方(当然也没人让我选过),并对这种境遇见怪不怪。我有时会觉得SM其实是一种相当和谐的关系,因为至少他们偶尔会爱上彼此,而上司决不会对我有半个好脸色。而且我听闻SM会在每场游戏开始前设定一个安全词,放游戏里叫投降,这可以让求生者在感到极度痛苦时结束游戏,避免不必要的损失。这简直令我嫉妒得发狂,我在现实生活折磨的二十年里可从来没有安全词!岂止没有安全词,我甚至都不能像游戏里求生者受伤那样大声哀嚎。因为现实是个面子社会,里子流血面子总得兜住。那怕你的心已经被戳得千疮百孔想花洒那样往外喷番茄汁,你也得对同事家人笑一笑。失去尚可,失败无赦,一旦痛呼出来,就彻底失败了。你以为“别人家的小孩”成绩优异自律自强,可能他内心已经被现实整得口球都带上了;你以为职场同事一个个西装革履,可能拉开就是一身龟甲缚。电话再次响起,我接起电话:“经理,什么,已经有人完成了?不采用了?哦,好的好的,没关系没关系。”妈的,为什么我一想到现实里并不是向晚玩的游戏那样四个人同心同德逃出生天,而是筋疲力竭地互相滴蜡油来竞争一个生存名额,我就止不住地流下泪来。
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package diana 嘉然相关
|
||||
package diana
|
||||
|
||||
import (
|
||||
@@ -37,12 +38,12 @@ func init() {
|
||||
zhiwangjson := zhiwangapi(msg)
|
||||
|
||||
if zhiwangjson == nil || zhiwangjson.Code != 0 {
|
||||
ctx.Send("api返回错误")
|
||||
ctx.SendChain(message.Text("api返回错误"))
|
||||
return
|
||||
}
|
||||
|
||||
if len(zhiwangjson.Data.Related) == 0 {
|
||||
ctx.Send("枝网没搜到,查重率为0%,我的评价是:一眼真")
|
||||
ctx.SendChain(message.Text("枝网没搜到,查重率为0%,我的评价是:一眼真"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -6,23 +6,24 @@ import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"image/jpeg"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fogleman/gg"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/math"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -30,141 +31,155 @@ var (
|
||||
base = "data/fortune/"
|
||||
// 素材下载网站
|
||||
site = "https://pan.dihe.moe/fortune/"
|
||||
// int64 群号 string 底图类型
|
||||
// 底图类型列表:车万 DC4 爱因斯坦 星空列车 樱云之恋 富婆妹 李清歌
|
||||
// 公主连结 原神 明日方舟 碧蓝航线 碧蓝幻想 战双 阴阳师
|
||||
table = map[int64]string{
|
||||
0: "车万",
|
||||
1048452984: "爱因斯坦",
|
||||
}
|
||||
table = [...]string{"车万", "DC4", "爱因斯坦", "星空列车", "樱云之恋", "富婆妹", "李清歌", "公主连结", "原神", "明日方舟", "碧蓝航线", "碧蓝幻想", "战双", "阴阳师"}
|
||||
// 映射底图与 index
|
||||
index = make(map[string]uint8)
|
||||
// 下载锁
|
||||
dlmu sync.Mutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
for i, s := range table {
|
||||
index[s] = uint8(i)
|
||||
}
|
||||
err := os.MkdirAll(base, 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// 插件主体
|
||||
control.Register("fortune", &control.Options{
|
||||
en := control.Register("fortune", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "每日运势: \n" +
|
||||
"- 运势",
|
||||
}).OnFullMatchGroup([]string{"运势", "抽签"}).SetBlock(true).SecondPriority().
|
||||
"- 运势|抽签\n" +
|
||||
"- 设置底图[车万 DC4 爱因斯坦 星空列车 樱云之恋 富婆妹 李清歌 公主连结 原神 明日方舟 碧蓝航线 碧蓝幻想 战双 阴阳师]",
|
||||
})
|
||||
en.OnRegex(`^设置底图(.*)`).SetBlock(true).SecondPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid <= 0 {
|
||||
// 个人用户设为负数
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
i, ok := index[ctx.State["regex_matched"].([]string)[1]]
|
||||
if ok {
|
||||
c, ok := control.Lookup("fortune")
|
||||
if ok {
|
||||
c.SetData(gid, int64(i)&0xff)
|
||||
ctx.SendChain(message.Text("设置成功~"))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("设置失败!"))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("没有这个底图哦~"))
|
||||
})
|
||||
en.OnFullMatchGroup([]string{"运势", "抽签"}).SetBlock(true).SecondPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
// 检查签文文件是否存在
|
||||
if _, err := os.Stat(base + "运势签文.json"); err != nil && !os.IsExist(err) {
|
||||
ctx.SendChain(message.Text("正在下载签文文件,请稍后..."))
|
||||
_, err := download(site+"运势签文.json", base)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
mikuji := base + "运势签文.json"
|
||||
if file.IsNotExist(mikuji) {
|
||||
dlmu.Lock()
|
||||
if file.IsNotExist(mikuji) {
|
||||
ctx.SendChain(message.Text("正在下载签文文件,请稍后..."))
|
||||
err := file.DownloadTo(site+"运势签文.json", mikuji, false)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("下载签文文件完毕"))
|
||||
}
|
||||
ctx.SendChain(message.Text("下载签文文件完毕"))
|
||||
dlmu.Unlock()
|
||||
}
|
||||
// 检查字体文件是否存在
|
||||
if _, err := os.Stat(base + "sakura.ttf"); err != nil && !os.IsExist(err) {
|
||||
ctx.SendChain(message.Text("正在下载字体文件,请稍后..."))
|
||||
_, err := download(site+"sakura.ttf", base)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
ttf := base + "sakura.ttf"
|
||||
if file.IsNotExist(ttf) {
|
||||
dlmu.Lock()
|
||||
if file.IsNotExist(ttf) {
|
||||
ctx.SendChain(message.Text("正在下载字体文件,请稍后..."))
|
||||
err := file.DownloadTo(site+"sakura.ttf", ttf, false)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("下载字体文件完毕"))
|
||||
}
|
||||
ctx.SendChain(message.Text("下载字体文件完毕"))
|
||||
dlmu.Unlock()
|
||||
}
|
||||
// 获取该群背景类型
|
||||
var kind string
|
||||
if v, ok := table[ctx.Event.GroupID]; ok {
|
||||
kind = v
|
||||
} else {
|
||||
kind = table[0]
|
||||
// 获取该群背景类型,默认车万
|
||||
kind := "车万"
|
||||
gid := ctx.Event.GroupID
|
||||
if gid <= 0 {
|
||||
// 个人用户设为负数
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
logrus.Debugln("[fortune]gid:", ctx.Event.GroupID, "uid:", ctx.Event.UserID)
|
||||
c, ok := control.Lookup("fortune")
|
||||
if ok {
|
||||
v := uint8(c.GetData(gid) & 0xff)
|
||||
if int(v) < len(table) {
|
||||
kind = table[v]
|
||||
}
|
||||
}
|
||||
// 检查背景图片是否存在
|
||||
if _, err := os.Stat(base + kind); err != nil && !os.IsExist(err) {
|
||||
ctx.SendChain(message.Text("正在下载背景图片,请稍后..."))
|
||||
file, err := download(site+kind+".zip", base)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
folder := base + kind
|
||||
if file.IsNotExist(folder) {
|
||||
dlmu.Lock()
|
||||
if file.IsNotExist(folder) {
|
||||
ctx.SendChain(message.Text("正在下载背景图片,请稍后..."))
|
||||
zipfile := kind + ".zip"
|
||||
zipcache := base + zipfile
|
||||
err := file.DownloadTo(site+zipfile, zipcache, false)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("下载背景图片完毕"))
|
||||
err = unpack(zipcache, folder+"/")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("解压背景图片完毕"))
|
||||
// 释放空间
|
||||
os.Remove(zipcache)
|
||||
}
|
||||
ctx.SendChain(message.Text("下载背景图片完毕"))
|
||||
err = unpack(file, base+kind+"/")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("解压背景图片完毕"))
|
||||
dlmu.Unlock()
|
||||
}
|
||||
// 生成种子
|
||||
t, _ := strconv.ParseInt(time.Now().Format("20060102"), 10, 64)
|
||||
seed := ctx.Event.UserID + t
|
||||
// 随机获取背景
|
||||
background, err := randimage(base+kind+"/", seed)
|
||||
background, err := randimage(folder+"/", seed)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// 随机获取签文
|
||||
title, text, err := randtext(base+"运势签文.json", seed)
|
||||
title, text, err := randtext(mikuji, seed)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// 绘制背景
|
||||
data, err := draw(background, title, text)
|
||||
d, err := draw(background, title, text)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// 发送图片
|
||||
ctx.SendChain(message.Image("base64://" + string(data)))
|
||||
ctx.SendChain(message.Image("base64://" + helper.BytesToString(d)))
|
||||
})
|
||||
}
|
||||
|
||||
// @function download 下载资源包
|
||||
// @param link 下载链接
|
||||
// @param dest 下载位置
|
||||
// @return 文件路径 & 错误信息
|
||||
func download(link, dest string) (string, error) {
|
||||
// 路径目录不存在则创建目录
|
||||
if _, err := os.Stat(dest); err != nil && !os.IsExist(err) {
|
||||
if err := os.MkdirAll(dest, 0755); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
client := &http.Client{}
|
||||
// 网络请求
|
||||
request, _ := http.NewRequest("GET", link, nil)
|
||||
request.Header.Set("Accept", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0")
|
||||
resp, err := client.Do(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
// 验证接收到的长度
|
||||
length, _ := strconv.Atoi(resp.Header.Get("Content-Length"))
|
||||
data, _ := ioutil.ReadAll(resp.Body)
|
||||
if length > len(data) {
|
||||
return "", errors.New("download not complete")
|
||||
}
|
||||
// 获取文件名
|
||||
temp := strings.Split(resp.Header.Get("Content-Disposition"), "\"")
|
||||
name, _ := url.QueryUnescape(temp[len(temp)-2])
|
||||
// 写入文件
|
||||
f, err := os.OpenFile(dest+name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.Write(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dest + name, nil
|
||||
}
|
||||
|
||||
// @function unpack 解压资源包
|
||||
// @param tgt 压缩文件位置
|
||||
// @param dest 解压位置
|
||||
// @return 错误信息
|
||||
func unpack(tgt, dest string) error {
|
||||
// 路径目录不存在则创建目录
|
||||
if _, err := os.Stat(dest); err != nil && !os.IsExist(err) {
|
||||
if file.IsNotExist(dest) {
|
||||
if err := os.MkdirAll(dest, 0755); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -255,25 +270,6 @@ func draw(background, title, text string) ([]byte, error) {
|
||||
if err := canvas.LoadFontFace(base+"sakura.ttf", 23); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
offest := func(total, now int, distance float64) float64 {
|
||||
if total%2 == 0 {
|
||||
return (float64(now-total/2) - 1) * distance
|
||||
}
|
||||
return (float64(now-total/2) - 1.5) * distance
|
||||
}
|
||||
rowsnum := func(total, div int) int {
|
||||
temp := total / div
|
||||
if total%div != 0 {
|
||||
temp++
|
||||
}
|
||||
return temp
|
||||
}
|
||||
min := func(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
tw, th := canvas.MeasureString("测")
|
||||
tw, th = tw+10, th+10
|
||||
r := []rune(text)
|
||||
@@ -282,7 +278,7 @@ func draw(background, title, text string) ([]byte, error) {
|
||||
default:
|
||||
for i, o := range r {
|
||||
xnow := rowsnum(i+1, 9)
|
||||
ysum := min(len(r)-(xnow-1)*9, 9)
|
||||
ysum := math.Min(len(r)-(xnow-1)*9, 9)
|
||||
ynow := i%9 + 1
|
||||
canvas.DrawString(string(o), -offest(xsum, xnow, tw)+115, offest(ysum, ynow, th)+320.0)
|
||||
}
|
||||
@@ -290,7 +286,7 @@ func draw(background, title, text string) ([]byte, error) {
|
||||
div := rowsnum(len(r), 2)
|
||||
for i, o := range r {
|
||||
xnow := rowsnum(i+1, div)
|
||||
ysum := min(len(r)-(xnow-1)*div, div)
|
||||
ysum := math.Min(len(r)-(xnow-1)*div, div)
|
||||
ynow := i%div + 1
|
||||
switch xnow {
|
||||
case 1:
|
||||
@@ -312,3 +308,18 @@ func draw(background, title, text string) ([]byte, error) {
|
||||
encoder.Close()
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func offest(total, now int, distance float64) float64 {
|
||||
if total%2 == 0 {
|
||||
return (float64(now-total/2) - 1) * distance
|
||||
}
|
||||
return (float64(now-total/2) - 1.5) * distance
|
||||
}
|
||||
|
||||
func rowsnum(total, div int) int {
|
||||
temp := total / div
|
||||
if total%div != 0 {
|
||||
temp++
|
||||
}
|
||||
return temp
|
||||
}
|
||||
|
||||
@@ -47,25 +47,50 @@ func init() { // 插件主体
|
||||
// 发送结果
|
||||
switch ctx.State["regex_matched"].([]string)[1] {
|
||||
case "-p ": // 图片模式
|
||||
ctx.SendChain(message.Image(
|
||||
"https://opengraph.githubassets.com/0/"+repo.Get("full_name").Str,
|
||||
).Add("cache", 0))
|
||||
default:
|
||||
ctx.SendChain(message.Text(
|
||||
repo.Get("full_name").Str, "\n",
|
||||
"Description: ",
|
||||
repo.Get("description").Str, "\n",
|
||||
"Star/Fork/Issue: ",
|
||||
repo.Get("watchers").Int(), "/", repo.Get("forks").Int(), "/", repo.Get("open_issues").Int(), "\n",
|
||||
"Language: ",
|
||||
notnull(repo.Get("language").Str, "None"), "\n",
|
||||
"License: ",
|
||||
notnull(strings.ToUpper(repo.Get("license.key").Str), "None"), "\n",
|
||||
"Last pushed: ",
|
||||
repo.Get("pushed_at").Str, "\n",
|
||||
"Jump: ",
|
||||
repo.Get("html_url").Str, "\n",
|
||||
))
|
||||
ctx.SendChain(
|
||||
message.Image(
|
||||
"https://opengraph.githubassets.com/0/"+repo.Get("full_name").Str,
|
||||
).Add("cache", 0),
|
||||
)
|
||||
case "-t ": // 文字模式
|
||||
ctx.SendChain(
|
||||
message.Text(
|
||||
repo.Get("full_name").Str, "\n",
|
||||
"Description: ",
|
||||
repo.Get("description").Str, "\n",
|
||||
"Star/Fork/Issue: ",
|
||||
repo.Get("watchers").Int(), "/", repo.Get("forks").Int(), "/", repo.Get("open_issues").Int(), "\n",
|
||||
"Language: ",
|
||||
notnull(repo.Get("language").Str, "None"), "\n",
|
||||
"License: ",
|
||||
notnull(strings.ToUpper(repo.Get("license.key").Str), "None"), "\n",
|
||||
"Last pushed: ",
|
||||
repo.Get("pushed_at").Str, "\n",
|
||||
"Jump: ",
|
||||
repo.Get("html_url").Str, "\n",
|
||||
),
|
||||
)
|
||||
default: // 文字模式
|
||||
ctx.SendChain(
|
||||
message.Text(
|
||||
repo.Get("full_name").Str, "\n",
|
||||
"Description: ",
|
||||
repo.Get("description").Str, "\n",
|
||||
"Star/Fork/Issue: ",
|
||||
repo.Get("watchers").Int(), "/", repo.Get("forks").Int(), "/", repo.Get("open_issues").Int(), "\n",
|
||||
"Language: ",
|
||||
notnull(repo.Get("language").Str, "None"), "\n",
|
||||
"License: ",
|
||||
notnull(strings.ToUpper(repo.Get("license.key").Str), "None"), "\n",
|
||||
"Last pushed: ",
|
||||
repo.Get("pushed_at").Str, "\n",
|
||||
"Jump: ",
|
||||
repo.Get("html_url").Str, "\n",
|
||||
),
|
||||
message.Image(
|
||||
"https://opengraph.githubassets.com/0/"+repo.Get("full_name").Str,
|
||||
).Add("cache", 0),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
109
plugin_hs/run.go
109
plugin_hs/run.go
@@ -3,26 +3,44 @@ package hs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/imroc/req"
|
||||
"github.com/tidwall/gjson"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/web"
|
||||
)
|
||||
|
||||
var botpath, _ = os.Getwd()
|
||||
var cachedir = botpath + "/data/hs/"
|
||||
var (
|
||||
cachedir = file.BOTPATH + "/data/hs/"
|
||||
reqconf = [...]string{"GET", "https://hs.fbigame.com",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36"}
|
||||
)
|
||||
|
||||
var header = req.Header{
|
||||
"user-agent": `Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36`,
|
||||
"referer": `https://hs.fbigame.com`,
|
||||
}
|
||||
const (
|
||||
hs = `https://hs.fbigame.com/ajax.php?`
|
||||
para = "mod=get_cards_list&" +
|
||||
"mode=-1&" +
|
||||
"extend=-1&" +
|
||||
"mutil_extend=&" +
|
||||
"hero=-1&" +
|
||||
"rarity=-1&" +
|
||||
"cost=-1&" +
|
||||
"mutil_cost=&" +
|
||||
"techlevel=-1&" +
|
||||
"type=-1&" +
|
||||
"collectible=-1&" +
|
||||
"isbacon=-1&" +
|
||||
"page=1&" +
|
||||
"search_type=1&" +
|
||||
"deckmode=normal"
|
||||
)
|
||||
|
||||
func init() {
|
||||
os.RemoveAll(cachedir)
|
||||
@@ -48,24 +66,17 @@ func init() {
|
||||
return
|
||||
}
|
||||
var sk message.Message
|
||||
var data []byte
|
||||
for i := 0; i < t && i < 5; i++ {
|
||||
cid := gjson.Get(g, `list.`+strconv.Itoa(i)+`.CardID`).String()
|
||||
cachefile := cachedir + cid
|
||||
imgcq := `[CQ:image,file=` + "file:///" + cachefile + `]`
|
||||
if _, err := os.Stat(cachefile); err != nil {
|
||||
im, err := req.Get(`https://res.fbigame.com/hs/v13/`+cid+
|
||||
`.png?auth_key=`+gjson.Get(g, `list.`+strconv.Itoa(i)+`.auth_key`).String(),
|
||||
header,
|
||||
)
|
||||
if file.IsNotExist(cachefile) {
|
||||
data, err := web.ReqWith(
|
||||
`https://res.fbigame.com/hs/v13/`+cid+`.png?auth_key=`+
|
||||
gjson.Get(g, `list.`+strconv.Itoa(i)+`.auth_key`).String(),
|
||||
reqconf[0], reqconf[1], reqconf[2])
|
||||
if err == nil {
|
||||
data, err = io.ReadAll(im.Response().Body)
|
||||
if err == nil {
|
||||
err = im.Response().Body.Close()
|
||||
if err == nil {
|
||||
err = os.WriteFile(cachefile, data, 0644)
|
||||
}
|
||||
}
|
||||
err = os.WriteFile(cachefile, data, 0644)
|
||||
}
|
||||
if err != nil {
|
||||
imgcq = err.Error()
|
||||
@@ -74,16 +85,18 @@ func init() {
|
||||
sk = append(
|
||||
sk,
|
||||
message.CustomNode(
|
||||
ctx.Event.Sender.NickName,
|
||||
ctx.Event.UserID,
|
||||
zero.BotConfig.NickName[0],
|
||||
ctx.Event.SelfID,
|
||||
imgcq, // 图片
|
||||
),
|
||||
)
|
||||
}
|
||||
ctx.SendGroupForwardMessage(
|
||||
if id := ctx.SendGroupForwardMessage(
|
||||
ctx.Event.GroupID,
|
||||
sk,
|
||||
)
|
||||
).Get("message_id").Int(); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
})
|
||||
// 卡组
|
||||
engine.OnRegex(`^[\s\S]*?(AAE[a-zA-Z0-9/\+=]{70,})[\s\S]*$`).
|
||||
@@ -97,39 +110,25 @@ func init() {
|
||||
}
|
||||
|
||||
func sh(s string) string {
|
||||
var hs = `https://hs.fbigame.com/ajax.php`
|
||||
h, _ := req.Get("https://hs.fbigame.com", header)
|
||||
var param = req.Param{
|
||||
"mod": `get_cards_list`,
|
||||
"mode": `-1`,
|
||||
"extend": `-1`,
|
||||
"mutil_extend": ``,
|
||||
"hero": `-1`,
|
||||
"rarity": `-1`,
|
||||
"cost": `-1`,
|
||||
"mutil_cost": ``,
|
||||
"techlevel": `-1`,
|
||||
"type": `-1`,
|
||||
"collectible": `-1`,
|
||||
"isbacon": `-1`,
|
||||
"page": `1`,
|
||||
"search_type": `1`,
|
||||
"deckmode": "normal",
|
||||
"hash": strings.SplitN(strings.SplitN(h.String(), `var hash = "`, 2)[1], `"`, 2)[0],
|
||||
data, err := web.ReqWith("https://hs.fbigame.com", reqconf[0], reqconf[1], reqconf[2])
|
||||
if err == nil {
|
||||
url := hs + para + "&hash=" + strings.SplitN(strings.SplitN(helper.BytesToString(data), `var hash = "`, 2)[1], `"`, 2)[0] + "&search=" + s
|
||||
r, err := web.ReqWith(url, reqconf[0], reqconf[1], reqconf[2])
|
||||
if err == nil {
|
||||
return helper.BytesToString(r)
|
||||
}
|
||||
}
|
||||
r, _ := req.Get(hs, header, param, req.Param{"search": s})
|
||||
return r.String()
|
||||
return ""
|
||||
}
|
||||
|
||||
func kz(s string) string {
|
||||
h, _ := req.Get("https://hs.fbigame.com")
|
||||
param := req.Param{
|
||||
"mod": `general_deck_image`,
|
||||
"deck_code": s,
|
||||
"deck_text": ``,
|
||||
"hash": strings.SplitN(strings.SplitN(h.String(), `var hash = "`, 2)[1], `"`, 2)[0],
|
||||
data, err := web.ReqWith("https://hs.fbigame.com", reqconf[0], reqconf[1], reqconf[2])
|
||||
if err == nil {
|
||||
url := hs + para + "mod=general_deck_image&deck_code=" + s + "&deck_text=&hash=" + strings.SplitN(strings.SplitN(helper.BytesToString(data), `var hash = "`, 2)[1], `"`, 2)[0] + "&search=" + s
|
||||
r, err := web.ReqWith(url, reqconf[0], reqconf[1], reqconf[2])
|
||||
if err == nil {
|
||||
return "base64://" + gjson.Get(helper.BytesToString(r), "img").String()
|
||||
}
|
||||
}
|
||||
r, _ := req.Get(`https://hs.fbigame.com/ajax.php`, param, h.Request().Header)
|
||||
im := gjson.Get(r.String(), "img").String()
|
||||
return `base64://` + im
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -69,9 +69,9 @@ func init() {
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
keyword := ctx.State["regex_matched"].([]string)[1]
|
||||
soutujson := soutuapi(keyword)
|
||||
pom1 := "https://i.pixiv.cat"
|
||||
pom1 := "https://i.pixiv.re"
|
||||
rannum := randintn(len(soutujson.Illusts))
|
||||
pom2 := soutujson.Illusts[rannum].ImageUrls.Large[19:]
|
||||
pom2 := soutujson.Illusts[rannum].ImageUrls.Medium[19:]
|
||||
ctx.SendChain(message.Image(pom1 + pom2))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package lolicon
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/math"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -30,7 +32,7 @@ func init() {
|
||||
}).OnFullMatch("来份萝莉").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
go func() {
|
||||
for i := 0; i < min(cap(queue)-len(queue), 2); i++ {
|
||||
for i := 0; i < math.Min(cap(queue)-len(queue), 2); i++ {
|
||||
resp, err := http.Get(api)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
@@ -48,7 +50,7 @@ func init() {
|
||||
continue
|
||||
}
|
||||
url := json.Get("data.0.urls.original").Str
|
||||
ctx.SendGroupMessage(0, message.Image(url))
|
||||
ctx.SendGroupMessage(0, message.Image(strings.ReplaceAll(url, "i.pixiv.cat", "i.pixiv.re")))
|
||||
queue <- url
|
||||
}
|
||||
}()
|
||||
@@ -60,10 +62,3 @@ func init() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
44
plugin_manager/gist.go
Normal file
44
plugin_manager/gist.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/math"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/web"
|
||||
)
|
||||
|
||||
// user hash file
|
||||
const gistraw = "https://gist.githubusercontent.com/%s/%s/raw/%s"
|
||||
|
||||
func checkNewUser(qq, gid int64, ghun, hash string) (bool, string) {
|
||||
if db.CanFind("member", "where ghun="+ghun) {
|
||||
return false, "该github用户已入群"
|
||||
}
|
||||
gidsum := md5.Sum(helper.StringToBytes(strconv.FormatInt(gid, 10)))
|
||||
gidhex := hex.EncodeToString(gidsum[:])
|
||||
u := fmt.Sprintf(gistraw, ghun, hash, gidhex)
|
||||
logrus.Debugln("[gist]visit url:", u)
|
||||
data, err := web.GetData(u)
|
||||
if err == nil {
|
||||
logrus.Debugln("[gist]get data:", helper.BytesToString(data))
|
||||
st, err := strconv.ParseInt(helper.BytesToString(data), 10, 64)
|
||||
if err == nil {
|
||||
// 600s 内验证成功
|
||||
ok := math.Abs(int(time.Now().Unix()-st)) < 600
|
||||
if ok {
|
||||
_ = db.Insert("member", &Member{QQ: qq, Ghun: ghun})
|
||||
return true, ""
|
||||
}
|
||||
return false, "时间戳超时"
|
||||
}
|
||||
return false, "时间戳格式错误: " + helper.BytesToString(data)
|
||||
}
|
||||
return false, "无法连接到gist: " + err.Error()
|
||||
}
|
||||
12
plugin_manager/manager.db.go
Normal file
12
plugin_manager/manager.db.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package manager
|
||||
|
||||
type Welcome struct {
|
||||
GrpID int64 `db:"gid"`
|
||||
Msg string `db:"msg"`
|
||||
}
|
||||
|
||||
type Member struct {
|
||||
QQ int64 `db:"qq"`
|
||||
// github username
|
||||
Ghun string `db:"ghun"`
|
||||
}
|
||||
@@ -3,24 +3,27 @@ package manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/rate"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
timer "github.com/FloatTech/ZeroBot-Plugin-Timer"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/plugin_manager/timer"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/math"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
|
||||
)
|
||||
|
||||
const (
|
||||
datapath = "data/manager/"
|
||||
confile = datapath + "config.pb"
|
||||
confile = datapath + "config.db"
|
||||
hint = "====群管====\n" +
|
||||
"- 禁言@QQ 1分钟\n" +
|
||||
"- 解除禁言 @QQ\n" +
|
||||
@@ -33,13 +36,15 @@ const (
|
||||
"- 修改头衔@QQ XXX\n" +
|
||||
"- 申请头衔 XXX\n" +
|
||||
"- 踢出群聊@QQ\n" +
|
||||
"- 退出群聊 1234\n" +
|
||||
"- 退出群聊 1234@bot\n" +
|
||||
"- 群聊转发 1234 XXX\n" +
|
||||
"- 私聊转发 0000 XXX\n" +
|
||||
"- 在MM月dd日的hh点mm分时(用http://url)提醒大家XXX\n" +
|
||||
"- 在MM月[每周|周几]的hh点mm分时(用http://url)提醒大家XXX\n" +
|
||||
"- 取消在MM月dd日的hh点mm分的提醒\n" +
|
||||
"- 取消在MM月[每周|周几]的hh点mm分的提醒\n" +
|
||||
"- 在\"cron\"时(用[url])提醒大家[xxx]\n" +
|
||||
"- 取消在\"cron\"的提醒\n" +
|
||||
"- 列出所有提醒\n" +
|
||||
"- 翻牌\n" +
|
||||
"- 设置欢迎语XXX\n" +
|
||||
@@ -47,19 +52,25 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
config Config
|
||||
limit = rate.NewManager(time.Minute*5, 2)
|
||||
db = &sql.Sqlite{DBPath: confile}
|
||||
limit = rate.NewManager(time.Minute*5, 2)
|
||||
clock timer.Clock
|
||||
)
|
||||
|
||||
var engine = control.Register("manager", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: hint,
|
||||
})
|
||||
|
||||
func init() { // 插件主体
|
||||
loadConfig()
|
||||
// 菜单
|
||||
zero.OnFullMatch("群管系统", zero.AdminPermission).SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.Send(hint)
|
||||
})
|
||||
go func() {
|
||||
process.SleepAbout1sTo2s()
|
||||
clock = timer.NewClock(db)
|
||||
db.Create("welcome", &Welcome{})
|
||||
db.Create("member", &Member{})
|
||||
}()
|
||||
// 升为管理
|
||||
zero.OnRegex(`^升为管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^升为管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupAdmin(
|
||||
ctx.Event.GroupID,
|
||||
@@ -74,7 +85,7 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text(nickname + " 升为了管理~"))
|
||||
})
|
||||
// 取消管理
|
||||
zero.OnRegex(`^取消管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^取消管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupAdmin(
|
||||
ctx.Event.GroupID,
|
||||
@@ -89,7 +100,7 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("残念~ " + nickname + " 暂时失去了管理员的资格"))
|
||||
})
|
||||
// 踢出群聊
|
||||
zero.OnRegex(`^踢出群聊.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^踢出群聊.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupKick(
|
||||
ctx.Event.GroupID,
|
||||
@@ -104,7 +115,7 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("残念~ " + nickname + " 被放逐"))
|
||||
})
|
||||
// 退出群聊
|
||||
zero.OnRegex(`^退出群聊.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^退出群聊.*?(\d+)`, zero.OnlyToMe, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupLeave(
|
||||
strToInt(ctx.State["regex_matched"].([]string)[1]), // 要退出的群的群号
|
||||
@@ -112,7 +123,7 @@ func init() { // 插件主体
|
||||
)
|
||||
})
|
||||
// 开启全体禁言
|
||||
zero.OnRegex(`^开启全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^开启全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupWholeBan(
|
||||
ctx.Event.GroupID,
|
||||
@@ -121,7 +132,7 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("全员自闭开始~"))
|
||||
})
|
||||
// 解除全员禁言
|
||||
zero.OnRegex(`^解除全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^解除全员禁言$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupWholeBan(
|
||||
ctx.Event.GroupID,
|
||||
@@ -130,7 +141,7 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("全员自闭结束~"))
|
||||
})
|
||||
// 禁言
|
||||
zero.OnRegex(`^禁言.*?(\d+).*?\s(\d+)(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^禁言.*?(\d+).*?\s(\d+)(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
duration := strToInt(ctx.State["regex_matched"].([]string)[2])
|
||||
switch ctx.State["regex_matched"].([]string)[3] {
|
||||
@@ -154,7 +165,7 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("小黑屋收留成功~"))
|
||||
})
|
||||
// 解除禁言
|
||||
zero.OnRegex(`^解除禁言.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^解除禁言.*?(\d+)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupBan(
|
||||
ctx.Event.GroupID,
|
||||
@@ -164,18 +175,18 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("小黑屋释放成功~"))
|
||||
})
|
||||
// 自闭禁言
|
||||
zero.OnRegex(`^我要自闭.*?(\d+)(.*)`, zero.OnlyGroup).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^(我要自闭|禅定).*?(\d+)(.*)`, zero.OnlyGroup).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
duration := strToInt(ctx.State["regex_matched"].([]string)[1])
|
||||
switch ctx.State["regex_matched"].([]string)[2] {
|
||||
case "分钟":
|
||||
//
|
||||
case "小时":
|
||||
duration := strToInt(ctx.State["regex_matched"].([]string)[2])
|
||||
switch ctx.State["regex_matched"].([]string)[3] {
|
||||
case "分钟", "min", "mins", "m":
|
||||
break
|
||||
case "小时", "hour", "hours", "h":
|
||||
duration *= 60
|
||||
case "天":
|
||||
case "天", "day", "days", "d":
|
||||
duration *= 60 * 24
|
||||
default:
|
||||
//
|
||||
break
|
||||
}
|
||||
if duration >= 43200 {
|
||||
duration = 43199 // qq禁言最大时长为一个月
|
||||
@@ -188,7 +199,7 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("那我就不手下留情了~"))
|
||||
})
|
||||
// 修改名片
|
||||
zero.OnRegex(`^修改名片.*?(\d+).*?\s(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^修改名片.*?(\d+).*?\s(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupCard(
|
||||
ctx.Event.GroupID,
|
||||
@@ -198,7 +209,7 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("嗯!已经修改了"))
|
||||
})
|
||||
// 修改头衔
|
||||
zero.OnRegex(`^修改头衔.*?(\d+).*?\s(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^修改头衔.*?(\d+).*?\s(.*)`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupSpecialTitle(
|
||||
ctx.Event.GroupID,
|
||||
@@ -208,7 +219,7 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("嗯!已经修改了"))
|
||||
})
|
||||
// 申请头衔
|
||||
zero.OnRegex(`^申请头衔(.*)`, zero.OnlyGroup).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^申请头衔(.*)`, zero.OnlyGroup).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupSpecialTitle(
|
||||
ctx.Event.GroupID,
|
||||
@@ -218,7 +229,7 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("嗯!不错的头衔呢~"))
|
||||
})
|
||||
// 群聊转发
|
||||
zero.OnRegex(`^群聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^群聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
// 对CQ码进行反转义
|
||||
content := ctx.State["regex_matched"].([]string)[2]
|
||||
@@ -231,7 +242,7 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("📧 --> " + ctx.State["regex_matched"].([]string)[1]))
|
||||
})
|
||||
// 私聊转发
|
||||
zero.OnRegex(`^私聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^私聊转发.*?(\d+)\s(.*)`, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
// 对CQ码进行反转义
|
||||
content := ctx.State["regex_matched"].([]string)[2]
|
||||
@@ -244,48 +255,73 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("📧 --> " + ctx.State["regex_matched"].([]string)[1]))
|
||||
})
|
||||
// 定时提醒
|
||||
zero.OnRegex(`^在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分时(用.+)?提醒大家(.*)`, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分时(用.+)?提醒大家(.*)`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if ctx.Event.GroupID > 0 {
|
||||
dateStrs := ctx.State["regex_matched"].([]string)
|
||||
ts := timer.GetFilledTimeStamp(dateStrs, false)
|
||||
ts.Grpid = uint64(ctx.Event.GroupID)
|
||||
if ts.Enable {
|
||||
go timer.RegisterTimer(ts, true)
|
||||
ctx.Send("记住了~")
|
||||
} else {
|
||||
ctx.Send("参数非法!")
|
||||
}
|
||||
dateStrs := ctx.State["regex_matched"].([]string)
|
||||
ts := timer.GetFilledTimer(dateStrs, ctx.Event.SelfID, ctx.Event.GroupID, false)
|
||||
if ts.En() {
|
||||
go clock.RegisterTimer(ts, true)
|
||||
ctx.SendChain(message.Text("记住了~"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("参数非法:" + ts.Alert))
|
||||
}
|
||||
})
|
||||
// 定时 cron 提醒
|
||||
engine.OnRegex(`^在"(.*)"时(用.+)?提醒大家(.*)`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
dateStrs := ctx.State["regex_matched"].([]string)
|
||||
var url, alert string
|
||||
switch len(dateStrs) {
|
||||
case 4:
|
||||
url = dateStrs[2]
|
||||
alert = dateStrs[3]
|
||||
case 3:
|
||||
alert = dateStrs[2]
|
||||
default:
|
||||
ctx.SendChain(message.Text("参数非法!"))
|
||||
return
|
||||
}
|
||||
logrus.Debugln("[manager] cron:", dateStrs[1])
|
||||
ts := timer.GetFilledCronTimer(dateStrs[1], alert, url, ctx.Event.SelfID, ctx.Event.GroupID)
|
||||
if clock.RegisterTimer(ts, true) {
|
||||
ctx.SendChain(message.Text("记住了~"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("参数非法:" + ts.Alert))
|
||||
}
|
||||
})
|
||||
// 取消定时
|
||||
zero.OnRegex(`^取消在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分的提醒`, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^取消在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分的提醒`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if ctx.Event.GroupID > 0 {
|
||||
dateStrs := ctx.State["regex_matched"].([]string)
|
||||
ts := timer.GetFilledTimeStamp(dateStrs, true)
|
||||
ts.Grpid = uint64(ctx.Event.GroupID)
|
||||
ti := timer.GetTimerInfo(ts)
|
||||
t, ok := (*timer.Timers)[ti]
|
||||
if ok {
|
||||
t.Enable = false
|
||||
delete(*timer.Timers, ti) // 避免重复取消
|
||||
_ = timer.SaveTimers()
|
||||
ctx.Send("取消成功~")
|
||||
} else {
|
||||
ctx.Send("没有这个定时器哦~")
|
||||
}
|
||||
dateStrs := ctx.State["regex_matched"].([]string)
|
||||
ts := timer.GetFilledTimer(dateStrs, ctx.Event.SelfID, ctx.Event.GroupID, true)
|
||||
ti := ts.GetTimerID()
|
||||
ok := clock.CancelTimer(ti)
|
||||
if ok {
|
||||
ctx.SendChain(message.Text("取消成功~"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("没有这个定时器哦~"))
|
||||
}
|
||||
})
|
||||
// 取消 cron 定时
|
||||
engine.OnRegex(`^取消在"(.*)"的提醒`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
dateStrs := ctx.State["regex_matched"].([]string)
|
||||
ts := timer.Timer{Cron: dateStrs[1], GrpId: ctx.Event.GroupID}
|
||||
ti := ts.GetTimerID()
|
||||
ok := clock.CancelTimer(ti)
|
||||
if ok {
|
||||
ctx.SendChain(message.Text("取消成功~"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("没有这个定时器哦~"))
|
||||
}
|
||||
})
|
||||
// 列出本群所有定时
|
||||
zero.OnFullMatch("列出所有提醒", zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnFullMatch("列出所有提醒", zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if ctx.Event.GroupID > 0 {
|
||||
ctx.Send(fmt.Sprint(timer.ListTimers(uint64(ctx.Event.GroupID))))
|
||||
}
|
||||
ctx.SendChain(message.Text(clock.ListTimers(ctx.Event.GroupID)))
|
||||
})
|
||||
// 随机点名
|
||||
zero.OnFullMatchGroup([]string{"翻牌"}, zero.OnlyGroup).SetBlock(true).SetPriority(40).
|
||||
engine.OnFullMatchGroup([]string{"翻牌"}, zero.OnlyGroup).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if !limit.Load(ctx.Event.UserID).Acquire() {
|
||||
ctx.SendChain(message.Text("少女祈祷中......"))
|
||||
@@ -300,13 +336,7 @@ func init() { // 插件主体
|
||||
sort.SliceStable(temp, func(i, j int) bool {
|
||||
return temp[i].Get("last_sent_time").Int() < temp[j].Get("last_sent_time").Int()
|
||||
})
|
||||
max := func(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
temp = temp[max(0, len(temp)-10):]
|
||||
temp = temp[math.Max(0, len(temp)-10):]
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
who := temp[rand.Intn(len(temp))]
|
||||
if who.Get("user_id").Int() == ctx.Event.SelfID {
|
||||
@@ -329,140 +359,170 @@ func init() { // 插件主体
|
||||
)
|
||||
})
|
||||
// 入群欢迎
|
||||
zero.OnNotice().SetBlock(false).FirstPriority().
|
||||
engine.OnNotice().SetBlock(false).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if ctx.Event.NoticeType == "group_increase" {
|
||||
word, ok := config.Welcome[uint64(ctx.Event.GroupID)]
|
||||
if ok {
|
||||
ctx.Send(word)
|
||||
if ctx.Event.NoticeType == "group_increase" && ctx.Event.SelfID != ctx.Event.UserID {
|
||||
var w Welcome
|
||||
err := db.Find("welcome", &w, "where gid = "+strconv.FormatInt(ctx.Event.GroupID, 10))
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text(w.Msg))
|
||||
} else {
|
||||
ctx.Send("欢迎~")
|
||||
ctx.SendChain(message.Text("欢迎~"))
|
||||
}
|
||||
enable, ok1 := config.Checkin[uint64(ctx.Event.GroupID)]
|
||||
if ok1 && enable {
|
||||
uid := ctx.Event.UserID
|
||||
a := rand.Intn(100)
|
||||
b := rand.Intn(100)
|
||||
r := a + b
|
||||
ctx.SendChain(message.At(uid), message.Text(fmt.Sprintf("考你一道题:%d+%d=?\n如果60秒之内答不上来,%s就要把你踢出去了哦~", a, b, zero.BotConfig.NickName[0])))
|
||||
// 匹配发送者进行验证
|
||||
rule := func(ctx *zero.Ctx) bool {
|
||||
for _, elem := range ctx.Event.Message {
|
||||
if elem.Type == "text" {
|
||||
text := strings.ReplaceAll(elem.Data["text"], " ", "")
|
||||
ans, err := strconv.Atoi(text)
|
||||
if err == nil {
|
||||
if ans != r {
|
||||
ctx.Send("答案不对哦,再想想吧~")
|
||||
return false
|
||||
c, ok := control.Lookup("manager")
|
||||
if ok {
|
||||
enable := c.GetData(ctx.Event.GroupID)&1 == 1
|
||||
if enable {
|
||||
uid := ctx.Event.UserID
|
||||
a := rand.Intn(100)
|
||||
b := rand.Intn(100)
|
||||
r := a + b
|
||||
ctx.SendChain(message.At(uid), message.Text(fmt.Sprintf("考你一道题:%d+%d=?\n如果60秒之内答不上来,%s就要把你踢出去了哦~", a, b, zero.BotConfig.NickName[0])))
|
||||
// 匹配发送者进行验证
|
||||
rule := func(ctx *zero.Ctx) bool {
|
||||
for _, elem := range ctx.Event.Message {
|
||||
if elem.Type == "text" {
|
||||
text := strings.ReplaceAll(elem.Data["text"], " ", "")
|
||||
ans, err := strconv.Atoi(text)
|
||||
if err == nil {
|
||||
if ans != r {
|
||||
ctx.SendChain(message.Text("答案不对哦,再想想吧~"))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
next := zero.NewFutureEvent("message", 999, false, zero.CheckUser(ctx.Event.UserID), rule)
|
||||
recv, cancel := next.Repeat()
|
||||
select {
|
||||
case <-time.After(time.Minute):
|
||||
ctx.SendChain(message.Text("拜拜啦~"))
|
||||
ctx.SetGroupKick(ctx.Event.GroupID, uid, false)
|
||||
cancel()
|
||||
case <-recv:
|
||||
cancel()
|
||||
ctx.SendChain(message.Text("答对啦~"))
|
||||
}
|
||||
return false
|
||||
}
|
||||
next := zero.NewFutureEvent("message", 999, false, zero.CheckUser(ctx.Event.UserID), rule)
|
||||
recv, cancel := next.Repeat()
|
||||
select {
|
||||
case <-time.After(time.Minute):
|
||||
ctx.Send("拜拜啦~")
|
||||
ctx.SetGroupKick(ctx.Event.GroupID, uid, false)
|
||||
cancel()
|
||||
case <-recv:
|
||||
cancel()
|
||||
ctx.Send("答对啦~")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// 退群提醒
|
||||
zero.OnNotice().SetBlock(false).SetPriority(40).
|
||||
engine.OnNotice().SetBlock(false).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if ctx.Event.NoticeType == "group_decrease" {
|
||||
ctx.SendChain(message.Text("有人跑路了~"))
|
||||
userid := ctx.Event.UserID
|
||||
nickname := ctx.GetStrangerInfo(userid, false).Get("nickname").String()
|
||||
ctx.SendChain(message.Text(nickname, "(", userid, ")", "离开了我们..."))
|
||||
}
|
||||
})
|
||||
// 设置欢迎语
|
||||
zero.OnRegex(`^设置欢迎语([\s\S]*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
engine.OnRegex(`^设置欢迎语([\s\S]*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
config.Welcome[uint64(ctx.Event.GroupID)] = ctx.State["regex_matched"].([]string)[1]
|
||||
if saveConfig() == nil {
|
||||
ctx.Send("记住啦!")
|
||||
w := &Welcome{
|
||||
GrpID: ctx.Event.GroupID,
|
||||
Msg: ctx.State["regex_matched"].([]string)[1],
|
||||
}
|
||||
err := db.Insert("welcome", w)
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text("记住啦!"))
|
||||
} else {
|
||||
ctx.Send("出错啦!")
|
||||
ctx.SendChain(message.Text("出错啦: ", err))
|
||||
}
|
||||
})
|
||||
// 入群验证开关
|
||||
zero.OnRegex(`^(.*)入群验证$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
// 入群后验证开关
|
||||
engine.OnRegex(`^(.*)入群验证$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
option := ctx.State["regex_matched"].([]string)[1]
|
||||
switch option {
|
||||
case "开启":
|
||||
config.Checkin[uint64(ctx.Event.GroupID)] = true
|
||||
case "关闭":
|
||||
config.Checkin[uint64(ctx.Event.GroupID)] = false
|
||||
default:
|
||||
c, ok := control.Lookup("manager")
|
||||
if ok {
|
||||
data := c.GetData(ctx.Event.GroupID)
|
||||
switch option {
|
||||
case "开启", "打开", "启用":
|
||||
data |= 1
|
||||
case "关闭", "关掉", "禁用":
|
||||
data &= 0x7fffffff_fffffffe
|
||||
default:
|
||||
return
|
||||
}
|
||||
err := c.SetData(ctx.Event.GroupID, data)
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text("已", option))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("出错啦: ", err))
|
||||
return
|
||||
}
|
||||
if saveConfig() == nil {
|
||||
ctx.Send("已" + option)
|
||||
} else {
|
||||
ctx.Send("出错啦!")
|
||||
ctx.SendChain(message.Text("找不到服务!"))
|
||||
})
|
||||
// 加群 gist 验证开关
|
||||
engine.OnRegex(`^(.*)gist加群自动审批$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
option := ctx.State["regex_matched"].([]string)[1]
|
||||
c, ok := control.Lookup("manager")
|
||||
if ok {
|
||||
data := c.GetData(ctx.Event.GroupID)
|
||||
switch option {
|
||||
case "开启", "打开", "启用":
|
||||
data |= 0x10
|
||||
case "关闭", "关掉", "禁用":
|
||||
data &= 0x7fffffff_fffffffd
|
||||
default:
|
||||
return
|
||||
}
|
||||
err := c.SetData(ctx.Event.GroupID, data)
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text("已", option))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("出错啦: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("找不到服务!"))
|
||||
})
|
||||
// 运行 CQ 码
|
||||
zero.OnRegex(`^run(.*)$`, zero.SuperUserPermission).SetBlock(true).SetPriority(0).
|
||||
engine.OnRegex(`^run(.*)$`, zero.SuperUserPermission).SetBlock(true).SetPriority(0).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
var cmd = ctx.State["regex_matched"].([]string)[1]
|
||||
cmd = strings.ReplaceAll(cmd, "[", "[")
|
||||
cmd = strings.ReplaceAll(cmd, "]", "]")
|
||||
// 可注入,权限为主人
|
||||
ctx.Send(cmd)
|
||||
})
|
||||
// 根据 gist 自动同意加群
|
||||
// 加群请在github新建一个gist,其文件名为本群群号的字符串的md5(小写),内容为一行,是当前unix时间戳(10分钟内有效)。
|
||||
// 然后请将您的用户名和gist哈希(小写)按照username/gisthash的格式填写到回答即可。
|
||||
engine.OnRequest().SetBlock(false).FirstPriority().Handle(func(ctx *zero.Ctx) {
|
||||
/*if ctx.Event.RequestType == "friend" {
|
||||
ctx.SetFriendAddRequest(ctx.Event.Flag, true, "")
|
||||
}*/
|
||||
c, ok := control.Lookup("manager")
|
||||
if ok && c.GetData(ctx.Event.GroupID)&0x10 == 0x10 && ctx.Event.RequestType == "group" && ctx.Event.SubType == "add" {
|
||||
// gist 文件名是群号的 ascii 编码的 md5
|
||||
// gist 内容是当前 uinx 时间戳,在 10 分钟内视为有效
|
||||
ans := ctx.Event.Comment[strings.Index(ctx.Event.Comment, "答案:")+len("答案:"):]
|
||||
divi := strings.Index(ans, "/")
|
||||
if divi <= 0 {
|
||||
ctx.SetGroupAddRequest(ctx.Event.Flag, "add", false, "格式错误!")
|
||||
return
|
||||
}
|
||||
ghun := ans[:divi]
|
||||
hash := ans[divi+1:]
|
||||
logrus.Infoln("[manager]收到加群申请, 用户:", ghun, ", hash:", hash)
|
||||
ok, reason := checkNewUser(ctx.Event.UserID, ctx.Event.GroupID, ghun, hash)
|
||||
if ok {
|
||||
ctx.SetGroupAddRequest(ctx.Event.Flag, "add", true, "")
|
||||
} else {
|
||||
ctx.SetGroupAddRequest(ctx.Event.Flag, "add", false, reason)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func strToInt(str string) int64 {
|
||||
val, _ := strconv.ParseInt(str, 10, 64)
|
||||
return val
|
||||
}
|
||||
|
||||
// loadConfig 加载设置,没有则手动初始化
|
||||
func loadConfig() {
|
||||
mkdirerr := os.MkdirAll(datapath, 0755)
|
||||
if mkdirerr == nil {
|
||||
if _, err := os.Stat(confile); err == nil || os.IsExist(err) {
|
||||
f, err := os.Open(confile)
|
||||
if err == nil {
|
||||
data, err1 := io.ReadAll(f)
|
||||
if err1 == nil {
|
||||
if len(data) > 0 {
|
||||
if config.Unmarshal(data) == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
config.Checkin = make(map[uint64]bool)
|
||||
config.Welcome = make(map[uint64]string)
|
||||
} else {
|
||||
panic(mkdirerr)
|
||||
}
|
||||
}
|
||||
|
||||
// saveConfig 保存设置,无此文件则新建
|
||||
func saveConfig() error {
|
||||
data, err := config.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
} else if _, err := os.Stat(datapath); err == nil || os.IsExist(err) {
|
||||
f, err1 := os.OpenFile(confile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
defer f.Close()
|
||||
_, err2 := f.Write(data)
|
||||
return err2
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,561 +0,0 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: manager.proto
|
||||
|
||||
package manager
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type Config struct {
|
||||
Checkin map[uint64]bool `protobuf:"bytes,1,rep,name=checkin,proto3" json:"checkin,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
|
||||
Welcome map[uint64]string `protobuf:"bytes,2,rep,name=welcome,proto3" json:"welcome,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Config) Reset() { *m = Config{} }
|
||||
func (m *Config) String() string { return proto.CompactTextString(m) }
|
||||
func (*Config) ProtoMessage() {}
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_cde9ec64f0d2c859, []int{0}
|
||||
}
|
||||
func (m *Config) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *Config) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_Config.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *Config) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Config.Merge(m, src)
|
||||
}
|
||||
func (m *Config) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *Config) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Config.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Config proto.InternalMessageInfo
|
||||
|
||||
func (m *Config) GetCheckin() map[uint64]bool {
|
||||
if m != nil {
|
||||
return m.Checkin
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Config) GetWelcome() map[uint64]string {
|
||||
if m != nil {
|
||||
return m.Welcome
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Config)(nil), "manager.config")
|
||||
proto.RegisterMapType((map[uint64]bool)(nil), "manager.config.CheckinEntry")
|
||||
proto.RegisterMapType((map[uint64]string)(nil), "manager.config.WelcomeEntry")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("manager.proto", fileDescriptor_cde9ec64f0d2c859) }
|
||||
|
||||
var fileDescriptor_cde9ec64f0d2c859 = []byte{
|
||||
// 186 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x4d, 0xcc, 0x4b,
|
||||
0x4c, 0x4f, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0x72, 0x95, 0x3e, 0x30,
|
||||
0x72, 0xb1, 0x25, 0xe7, 0xe7, 0xa5, 0x65, 0xa6, 0x0b, 0x99, 0x71, 0xb1, 0x27, 0x67, 0xa4, 0x26,
|
||||
0x67, 0x67, 0xe6, 0x49, 0x30, 0x2a, 0x30, 0x6b, 0x70, 0x1b, 0xc9, 0xe8, 0xc1, 0x34, 0x41, 0x54,
|
||||
0xe8, 0x39, 0x43, 0xa4, 0x5d, 0xf3, 0x4a, 0x8a, 0x2a, 0x83, 0x60, 0x8a, 0x41, 0xfa, 0xca, 0x53,
|
||||
0x73, 0x92, 0xf3, 0x73, 0x53, 0x25, 0x98, 0xb0, 0xeb, 0x0b, 0x87, 0x48, 0x43, 0xf5, 0x41, 0x15,
|
||||
0x4b, 0x59, 0x71, 0xf1, 0x20, 0x1b, 0x28, 0x24, 0xc0, 0xc5, 0x9c, 0x9d, 0x5a, 0x29, 0xc1, 0xa8,
|
||||
0xc0, 0xa8, 0xc1, 0x12, 0x04, 0x62, 0x0a, 0x89, 0x70, 0xb1, 0x96, 0x25, 0xe6, 0x94, 0x82, 0xcc,
|
||||
0x65, 0xd4, 0xe0, 0x08, 0x82, 0x70, 0xac, 0x98, 0x2c, 0x18, 0x41, 0x7a, 0x91, 0x0d, 0x25, 0xa4,
|
||||
0x97, 0x13, 0x49, 0xaf, 0x93, 0xc0, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78,
|
||||
0x24, 0xc7, 0x38, 0xe3, 0xb1, 0x1c, 0x43, 0x12, 0x1b, 0x38, 0x50, 0x8c, 0x01, 0x01, 0x00, 0x00,
|
||||
0xff, 0xff, 0x2a, 0xe6, 0x90, 0x6e, 0x25, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *Config) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *Config) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *Config) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.XXX_unrecognized != nil {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if len(m.Welcome) > 0 {
|
||||
for k := range m.Welcome {
|
||||
v := m.Welcome[k]
|
||||
baseI := i
|
||||
i -= len(v)
|
||||
copy(dAtA[i:], v)
|
||||
i = encodeVarintManager(dAtA, i, uint64(len(v)))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
i = encodeVarintManager(dAtA, i, uint64(k))
|
||||
i--
|
||||
dAtA[i] = 0x8
|
||||
i = encodeVarintManager(dAtA, i, uint64(baseI-i))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
}
|
||||
if len(m.Checkin) > 0 {
|
||||
for k := range m.Checkin {
|
||||
v := m.Checkin[k]
|
||||
baseI := i
|
||||
i--
|
||||
if v {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x10
|
||||
i = encodeVarintManager(dAtA, i, uint64(k))
|
||||
i--
|
||||
dAtA[i] = 0x8
|
||||
i = encodeVarintManager(dAtA, i, uint64(baseI-i))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintManager(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovManager(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return base
|
||||
}
|
||||
func (m *Config) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Checkin) > 0 {
|
||||
for k, v := range m.Checkin {
|
||||
_ = k
|
||||
_ = v
|
||||
mapEntrySize := 1 + sovManager(uint64(k)) + 1 + 1
|
||||
n += mapEntrySize + 1 + sovManager(uint64(mapEntrySize))
|
||||
}
|
||||
}
|
||||
if len(m.Welcome) > 0 {
|
||||
for k, v := range m.Welcome {
|
||||
_ = k
|
||||
_ = v
|
||||
mapEntrySize := 1 + sovManager(uint64(k)) + 1 + len(v) + sovManager(uint64(len(v)))
|
||||
n += mapEntrySize + 1 + sovManager(uint64(mapEntrySize))
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovManager(x uint64) (n int) {
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozManager(x uint64) (n int) {
|
||||
return sovManager(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *Config) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowManager
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: config: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: config: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Checkin", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowManager
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthManager
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthManager
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.Checkin == nil {
|
||||
m.Checkin = make(map[uint64]bool)
|
||||
}
|
||||
var mapkey uint64
|
||||
var mapvalue bool
|
||||
for iNdEx < postIndex {
|
||||
entryPreIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowManager
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
if fieldNum == 1 {
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowManager
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
mapkey |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if fieldNum == 2 {
|
||||
var mapvaluetemp int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowManager
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
mapvaluetemp |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
mapvalue = bool(mapvaluetemp != 0)
|
||||
} else {
|
||||
iNdEx = entryPreIndex
|
||||
skippy, err := skipManager(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthManager
|
||||
}
|
||||
if (iNdEx + skippy) > postIndex {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
m.Checkin[mapkey] = mapvalue
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Welcome", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowManager
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthManager
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthManager
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.Welcome == nil {
|
||||
m.Welcome = make(map[uint64]string)
|
||||
}
|
||||
var mapkey uint64
|
||||
var mapvalue string
|
||||
for iNdEx < postIndex {
|
||||
entryPreIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowManager
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
if fieldNum == 1 {
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowManager
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
mapkey |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if fieldNum == 2 {
|
||||
var stringLenmapvalue uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowManager
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLenmapvalue |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLenmapvalue := int(stringLenmapvalue)
|
||||
if intStringLenmapvalue < 0 {
|
||||
return ErrInvalidLengthManager
|
||||
}
|
||||
postStringIndexmapvalue := iNdEx + intStringLenmapvalue
|
||||
if postStringIndexmapvalue < 0 {
|
||||
return ErrInvalidLengthManager
|
||||
}
|
||||
if postStringIndexmapvalue > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue])
|
||||
iNdEx = postStringIndexmapvalue
|
||||
} else {
|
||||
iNdEx = entryPreIndex
|
||||
skippy, err := skipManager(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthManager
|
||||
}
|
||||
if (iNdEx + skippy) > postIndex {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
m.Welcome[mapkey] = mapvalue
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipManager(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthManager
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipManager(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowManager
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wireType := int(wire & 0x7)
|
||||
switch wireType {
|
||||
case 0:
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowManager
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx++
|
||||
if dAtA[iNdEx-1] < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowManager
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
length |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthManager
|
||||
}
|
||||
iNdEx += length
|
||||
case 3:
|
||||
depth++
|
||||
case 4:
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupManager
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthManager
|
||||
}
|
||||
if depth == 0 {
|
||||
return iNdEx, nil
|
||||
}
|
||||
}
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthManager = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowManager = fmt.Errorf("proto: integer overflow")
|
||||
ErrUnexpectedEndOfGroupManager = fmt.Errorf("proto: unexpected end of group")
|
||||
)
|
||||
@@ -1,7 +0,0 @@
|
||||
syntax = "proto3";
|
||||
package manager;
|
||||
|
||||
message config {
|
||||
map<uint64, bool> checkin = 1;
|
||||
map<uint64, string> welcome = 2;
|
||||
}
|
||||
16
plugin_manager/timer/msg.go
Normal file
16
plugin_manager/timer/msg.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package timer
|
||||
|
||||
import (
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
func (ts *Timer) sendmsg(grp int64, ctx *zero.Ctx) {
|
||||
ctx.Event = new(zero.Event)
|
||||
ctx.Event.GroupID = grp
|
||||
if ts.Url == "" {
|
||||
ctx.SendChain(atall, message.Text(ts.Alert))
|
||||
} else {
|
||||
ctx.SendChain(atall, message.Text(ts.Alert), message.Image(ts.Url).Add("cache", "0"))
|
||||
}
|
||||
}
|
||||
167
plugin_manager/timer/parse.go
Normal file
167
plugin_manager/timer/parse.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package timer
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
)
|
||||
|
||||
// GetTimerInfo 获得标准化定时字符串
|
||||
func (ts *Timer) GetTimerInfo() string {
|
||||
if ts.Cron != "" {
|
||||
return fmt.Sprintf("[%d]%s", ts.GrpId, ts.Cron)
|
||||
}
|
||||
return fmt.Sprintf("[%d]%d月%d日%d周%d:%d", ts.GrpId, ts.Month(), ts.Day(), ts.Week(), ts.Hour(), ts.Minute())
|
||||
}
|
||||
|
||||
// GetTimerInfo 获得标准化 ID
|
||||
func (ts *Timer) GetTimerID() uint32 {
|
||||
key := ts.GetTimerInfo()
|
||||
m := md5.Sum(helper.StringToBytes(key))
|
||||
return binary.LittleEndian.Uint32(m[:4])
|
||||
}
|
||||
|
||||
// GetFilledCronTimer 获得以cron填充好的ts
|
||||
func GetFilledCronTimer(croncmd string, alert string, img string, botqq, gid int64) *Timer {
|
||||
var ts Timer
|
||||
ts.Alert = alert
|
||||
ts.Cron = croncmd
|
||||
ts.Url = img
|
||||
ts.Selfid = botqq
|
||||
ts.GrpId = gid
|
||||
return &ts
|
||||
}
|
||||
|
||||
// GetFilledTimer 获得填充好的ts
|
||||
func GetFilledTimer(dateStrs []string, botqq, grp int64, matchDateOnly bool) *Timer {
|
||||
monthStr := []rune(dateStrs[1])
|
||||
dayWeekStr := []rune(dateStrs[2])
|
||||
hourStr := []rune(dateStrs[3])
|
||||
minuteStr := []rune(dateStrs[4])
|
||||
|
||||
var ts Timer
|
||||
mon := time.Month(chineseNum2Int(monthStr))
|
||||
if (mon != -1 && mon <= 0) || mon > 12 { // 月份非法
|
||||
ts.Alert = "月份非法!"
|
||||
return &ts
|
||||
}
|
||||
ts.SetMonth(mon)
|
||||
lenOfDW := len(dayWeekStr)
|
||||
switch {
|
||||
case lenOfDW == 4: // 包括末尾的"日"
|
||||
dayWeekStr = []rune{dayWeekStr[0], dayWeekStr[2]} // 去除中间的十
|
||||
d := chineseNum2Int(dayWeekStr)
|
||||
if (d != -1 && d <= 0) || d > 31 { // 日期非法
|
||||
ts.Alert = "日期非法1!"
|
||||
return &ts
|
||||
}
|
||||
ts.SetDay(d)
|
||||
case dayWeekStr[lenOfDW-1] == rune('日'): // xx日
|
||||
dayWeekStr = dayWeekStr[:lenOfDW-1]
|
||||
d := chineseNum2Int(dayWeekStr)
|
||||
if (d != -1 && d <= 0) || d > 31 { // 日期非法
|
||||
ts.Alert = "日期非法2!"
|
||||
return &ts
|
||||
}
|
||||
ts.SetDay(d)
|
||||
case dayWeekStr[0] == rune('每'): // 每周
|
||||
ts.SetWeek(-1)
|
||||
default: // 周x
|
||||
w := chineseNum2Int(dayWeekStr[1:])
|
||||
if w == 7 { // 周天是0
|
||||
w = 0
|
||||
}
|
||||
if w < 0 || w > 6 { // 星期非法
|
||||
ts.Alert = "星期非法!"
|
||||
return &ts
|
||||
}
|
||||
ts.SetWeek(time.Weekday(w))
|
||||
}
|
||||
if len(hourStr) == 3 {
|
||||
hourStr = []rune{hourStr[0], hourStr[2]} // 去除中间的十
|
||||
}
|
||||
h := chineseNum2Int(hourStr)
|
||||
if h < -1 || h > 23 { // 小时非法
|
||||
ts.Alert = "小时非法!"
|
||||
return &ts
|
||||
}
|
||||
ts.SetHour(h)
|
||||
if len(minuteStr) == 3 {
|
||||
minuteStr = []rune{minuteStr[0], minuteStr[2]} // 去除中间的十
|
||||
}
|
||||
min := chineseNum2Int(minuteStr)
|
||||
if min < -1 || min > 59 { // 分钟非法
|
||||
ts.Alert = "分钟非法!"
|
||||
return &ts
|
||||
}
|
||||
ts.SetMinute(min)
|
||||
if !matchDateOnly {
|
||||
urlStr := dateStrs[5]
|
||||
if urlStr != "" { // 是图片url
|
||||
ts.Url = urlStr[3:] // utf-8下用为3字节
|
||||
logrus.Println("[群管]" + ts.Url)
|
||||
if !strings.HasPrefix(ts.Url, "http") {
|
||||
ts.Url = "illegal"
|
||||
logrus.Println("[群管]url非法!")
|
||||
return &ts
|
||||
}
|
||||
}
|
||||
ts.Alert = dateStrs[6]
|
||||
ts.SetEn(true)
|
||||
}
|
||||
ts.Selfid = botqq
|
||||
ts.GrpId = grp
|
||||
return &ts
|
||||
}
|
||||
|
||||
// chineseNum2Int 汉字数字转int,仅支持-10~99,最多两位数,其中"每"解释为-1,"每二"为-2,以此类推
|
||||
func chineseNum2Int(rs []rune) int {
|
||||
r := -1
|
||||
l := len(rs)
|
||||
mai := rune('每')
|
||||
if unicode.IsDigit(rs[0]) { // 默认可能存在的第二位也为int
|
||||
r, _ = strconv.Atoi(string(rs))
|
||||
} else {
|
||||
switch {
|
||||
case rs[0] == mai:
|
||||
if l == 2 {
|
||||
r = -chineseChar2Int(rs[1])
|
||||
}
|
||||
case l == 1:
|
||||
r = chineseChar2Int(rs[0])
|
||||
default:
|
||||
ten := chineseChar2Int(rs[0])
|
||||
if ten != 10 {
|
||||
ten *= 10
|
||||
}
|
||||
ge := chineseChar2Int(rs[1])
|
||||
if ge == 10 {
|
||||
ge = 0
|
||||
}
|
||||
r = ten + ge
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// chineseChar2Int 处理单个字符的映射0~10
|
||||
func chineseChar2Int(c rune) int {
|
||||
if c == rune('日') || c == rune('天') { // 周日/周天
|
||||
return 7
|
||||
} else {
|
||||
match := []rune("零一二三四五六七八九十")
|
||||
for i, m := range match {
|
||||
if c == m {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
165
plugin_manager/timer/sleep.go
Normal file
165
plugin_manager/timer/sleep.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package timer
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
)
|
||||
|
||||
func firstWeek(date *time.Time, week time.Weekday) (d time.Time) {
|
||||
d = date.AddDate(0, 0, 1-date.Day())
|
||||
for d.Weekday() != week {
|
||||
d = d.AddDate(0, 0, 1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ts *Timer) nextWakeTime() (date time.Time) {
|
||||
date = time.Now()
|
||||
m := ts.Month()
|
||||
d := ts.Day()
|
||||
h := ts.Hour()
|
||||
mn := ts.Minute()
|
||||
w := ts.Week()
|
||||
var unit time.Duration
|
||||
logrus.Debugln("[timer] unit init:", unit)
|
||||
if mn >= 0 {
|
||||
switch {
|
||||
case h < 0:
|
||||
if unit <= time.Second {
|
||||
unit = time.Hour
|
||||
}
|
||||
case d < 0 || w < 0:
|
||||
if unit <= time.Second {
|
||||
unit = time.Hour * 24
|
||||
}
|
||||
case d == 0 && w >= 0:
|
||||
delta := time.Hour * 24 * time.Duration(int(w)-int(date.Weekday()))
|
||||
if delta < 0 {
|
||||
delta = time.Hour * 24 * 7
|
||||
}
|
||||
unit += delta
|
||||
case m < 0:
|
||||
unit = -1
|
||||
}
|
||||
} else {
|
||||
unit = time.Minute
|
||||
}
|
||||
logrus.Debugln("[timer] unit:", unit)
|
||||
stable := 0
|
||||
if mn < 0 {
|
||||
mn = date.Minute()
|
||||
}
|
||||
if h < 0 {
|
||||
h = date.Hour()
|
||||
} else {
|
||||
stable |= 0x8
|
||||
}
|
||||
switch {
|
||||
case d < 0:
|
||||
d = date.Day()
|
||||
case d > 0:
|
||||
stable |= 0x4
|
||||
default:
|
||||
d = date.Day()
|
||||
if w >= 0 {
|
||||
stable |= 0x2
|
||||
}
|
||||
}
|
||||
if m < 0 {
|
||||
m = date.Month()
|
||||
} else {
|
||||
stable |= 0x1
|
||||
}
|
||||
switch stable {
|
||||
case 0b0101:
|
||||
if ts.Day() != time.Now().Day() || ts.Month() != time.Now().Month() {
|
||||
h = 0
|
||||
}
|
||||
case 0b1001:
|
||||
if ts.Month() != time.Now().Month() {
|
||||
d = 0
|
||||
}
|
||||
case 0b0001:
|
||||
if ts.Month() != time.Now().Month() {
|
||||
d = 0
|
||||
h = 0
|
||||
}
|
||||
}
|
||||
logrus.Debugln("[timer] stable:", stable)
|
||||
logrus.Debugln("[timer] m:", m, "d:", d, "h:", h, "mn:", mn, "w:", w)
|
||||
date = time.Date(date.Year(), time.Month(m), int(d), int(h), int(mn), date.Second(), date.Nanosecond(), date.Location())
|
||||
logrus.Debugln("[timer] date original:", date)
|
||||
if unit > 0 {
|
||||
date = date.Add(unit)
|
||||
}
|
||||
logrus.Debugln("[timer] date after add:", date)
|
||||
if time.Until(date) <= 0 {
|
||||
if ts.Month() < 0 {
|
||||
if ts.Day() > 0 || (ts.Day() == 0 && ts.Week() >= 0) {
|
||||
date = date.AddDate(0, 1, 0)
|
||||
} else if ts.Day() < 0 || ts.Week() < 0 {
|
||||
if ts.Hour() > 0 {
|
||||
date = date.AddDate(0, 0, 1)
|
||||
} else if ts.Minute() > 0 {
|
||||
date = date.Add(time.Hour)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
date = date.AddDate(1, 0, 0)
|
||||
}
|
||||
}
|
||||
logrus.Debugln("[timer] date after fix:", date)
|
||||
if stable&0x8 != 0 && date.Hour() != int(h) {
|
||||
switch {
|
||||
case stable&0x4 == 0:
|
||||
date = date.AddDate(0, 0, 1).Add(-time.Hour)
|
||||
case stable&0x2 == 0:
|
||||
date = date.AddDate(0, 0, 7).Add(-time.Hour)
|
||||
case stable*0x1 == 0:
|
||||
date = date.AddDate(0, 1, 0).Add(-time.Hour)
|
||||
default:
|
||||
date = date.AddDate(1, 0, 0).Add(-time.Hour)
|
||||
}
|
||||
}
|
||||
logrus.Debugln("[timer] date after s8:", date)
|
||||
if stable&0x4 != 0 && date.Day() != int(d) {
|
||||
switch {
|
||||
case stable*0x1 == 0:
|
||||
date = date.AddDate(0, 1, -1)
|
||||
default:
|
||||
date = date.AddDate(1, 0, -1)
|
||||
}
|
||||
}
|
||||
logrus.Debugln("[timer] date after s4:", date)
|
||||
if stable&0x2 != 0 && date.Weekday() != w {
|
||||
switch {
|
||||
case stable*0x1 == 0:
|
||||
date = date.AddDate(0, 1, 0)
|
||||
default:
|
||||
date = date.AddDate(1, 0, 0)
|
||||
}
|
||||
date = firstWeek(&date, time.Weekday(w))
|
||||
}
|
||||
logrus.Debugln("[timer] date after s2:", date)
|
||||
if time.Until(date) <= 0 {
|
||||
date = time.Now().Add(time.Minute)
|
||||
}
|
||||
return date
|
||||
}
|
||||
|
||||
func (ts *Timer) judgeHM() {
|
||||
if ts.Hour() < 0 || ts.Hour() == time.Now().Hour() {
|
||||
if ts.Minute() < 0 || ts.Minute() == time.Now().Minute() {
|
||||
if ts.Selfid != 0 {
|
||||
ts.sendmsg(ts.GrpId, zero.GetBot(ts.Selfid))
|
||||
} else {
|
||||
zero.RangeBot(func(id int64, ctx *zero.Ctx) (_ bool) {
|
||||
ts.sendmsg(ts.GrpId, ctx)
|
||||
return
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
plugin_manager/timer/timer.db.go
Normal file
26
plugin_manager/timer/timer.db.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package timer
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
|
||||
)
|
||||
|
||||
type Timer struct {
|
||||
Id uint32 `db:"id"`
|
||||
En1Month4Day5Week3Hour5Min6 int32 `db:"emdwhm"`
|
||||
Selfid int64 `db:"sid"`
|
||||
GrpId int64 `db:"gid"`
|
||||
Alert string `db:"alert"`
|
||||
Cron string `db:"cron"`
|
||||
Url string `db:"url"`
|
||||
}
|
||||
|
||||
func (t *Timer) InsertInto(db *sql.Sqlite) error {
|
||||
return db.Insert("timer", t)
|
||||
}
|
||||
|
||||
func getTimerFrom(db *sql.Sqlite, id uint32) (t Timer, err error) {
|
||||
err = db.Find("timer", &t, "where id = "+strconv.Itoa(int(id)))
|
||||
return
|
||||
}
|
||||
183
plugin_manager/timer/timer.go
Normal file
183
plugin_manager/timer/timer.go
Normal file
@@ -0,0 +1,183 @@
|
||||
// Package timer 群管定时器
|
||||
package timer
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/cron"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
|
||||
)
|
||||
|
||||
type Clock struct {
|
||||
db *sql.Sqlite
|
||||
timers *(map[uint32]*Timer)
|
||||
timersmu sync.RWMutex
|
||||
// cron 定时器
|
||||
cron *cron.Cron
|
||||
// entries key <-> cron
|
||||
entries map[uint32]cron.EntryID
|
||||
entmu sync.Mutex
|
||||
}
|
||||
|
||||
var (
|
||||
// @全体成员
|
||||
atall = message.MessageSegment{
|
||||
Type: "at",
|
||||
Data: map[string]string{
|
||||
"qq": "all",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func NewClock(db *sql.Sqlite) (c Clock) {
|
||||
c.loadTimers(db)
|
||||
c.cron = cron.New()
|
||||
c.entries = make(map[uint32]cron.EntryID)
|
||||
c.cron.Start()
|
||||
return
|
||||
}
|
||||
|
||||
// RegisterTimer 注册计时器
|
||||
func (c *Clock) RegisterTimer(ts *Timer, save bool) bool {
|
||||
var key uint32
|
||||
if save {
|
||||
key = ts.GetTimerID()
|
||||
ts.Id = key
|
||||
} else {
|
||||
key = ts.Id
|
||||
}
|
||||
t, ok := c.GetTimer(key)
|
||||
if t != ts && ok { // 避免重复注册定时器
|
||||
t.SetEn(false)
|
||||
}
|
||||
logrus.Println("[群管]注册计时器", key)
|
||||
if ts.Cron != "" {
|
||||
var ctx *zero.Ctx
|
||||
if ts.Selfid != 0 {
|
||||
ctx = zero.GetBot(ts.Selfid)
|
||||
} else {
|
||||
zero.RangeBot(func(id int64, c *zero.Ctx) bool {
|
||||
ctx = c
|
||||
ts.Selfid = id
|
||||
return false
|
||||
})
|
||||
}
|
||||
eid, err := c.cron.AddFunc(ts.Cron, func() { ts.sendmsg(ts.GrpId, ctx) })
|
||||
if err == nil {
|
||||
c.entmu.Lock()
|
||||
c.entries[key] = eid
|
||||
c.entmu.Unlock()
|
||||
if save {
|
||||
err = c.AddTimer(ts)
|
||||
}
|
||||
return err == nil
|
||||
}
|
||||
ts.Alert = err.Error()
|
||||
} else {
|
||||
if save {
|
||||
_ = c.AddTimer(ts)
|
||||
}
|
||||
for ts.En() {
|
||||
nextdate := ts.nextWakeTime()
|
||||
sleepsec := time.Until(nextdate)
|
||||
logrus.Printf("[群管]计时器%08x将睡眠%ds", key, sleepsec/time.Second)
|
||||
time.Sleep(sleepsec)
|
||||
if ts.En() {
|
||||
if ts.Month() < 0 || ts.Month() == time.Now().Month() {
|
||||
if ts.Day() < 0 || ts.Day() == time.Now().Day() {
|
||||
ts.judgeHM()
|
||||
} else if ts.Day() == 0 {
|
||||
if ts.Week() < 0 || ts.Week() == time.Now().Weekday() {
|
||||
ts.judgeHM()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CancelTimer 取消计时器
|
||||
func (c *Clock) CancelTimer(key uint32) bool {
|
||||
t, ok := c.GetTimer(key)
|
||||
if ok {
|
||||
if t.Cron != "" {
|
||||
c.entmu.Lock()
|
||||
e := c.entries[key]
|
||||
c.cron.Remove(e)
|
||||
delete(c.entries, key)
|
||||
c.entmu.Unlock()
|
||||
} else {
|
||||
t.SetEn(false)
|
||||
}
|
||||
c.timersmu.Lock()
|
||||
delete(*c.timers, key) // 避免重复取消
|
||||
e := c.db.Del("timer", "where id = "+strconv.Itoa(int(key)))
|
||||
c.timersmu.Unlock()
|
||||
return e == nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ListTimers 列出本群所有计时器
|
||||
func (c *Clock) ListTimers(grpID int64) []string {
|
||||
// 数组默认长度为map长度,后面append时,不需要重新申请内存和拷贝,效率很高
|
||||
if c.timers != nil {
|
||||
c.timersmu.RLock()
|
||||
keys := make([]string, 0, len(*c.timers))
|
||||
for _, v := range *c.timers {
|
||||
if v.GrpId == grpID {
|
||||
k := v.GetTimerInfo()
|
||||
start := strings.Index(k, "]")
|
||||
msg := strings.ReplaceAll(k[start+1:]+"\n", "-1", "每")
|
||||
msg = strings.ReplaceAll(msg, "月0日0周", "月周天")
|
||||
msg = strings.ReplaceAll(msg, "月0日", "月")
|
||||
msg = strings.ReplaceAll(msg, "日0周", "日")
|
||||
keys = append(keys, msg)
|
||||
}
|
||||
}
|
||||
c.timersmu.RUnlock()
|
||||
return keys
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Clock) GetTimer(key uint32) (t *Timer, ok bool) {
|
||||
c.timersmu.RLock()
|
||||
t, ok = (*c.timers)[key]
|
||||
c.timersmu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Clock) AddTimer(t *Timer) (err error) {
|
||||
c.timersmu.Lock()
|
||||
(*c.timers)[t.Id] = t
|
||||
err = c.db.Insert("timer", t)
|
||||
c.timersmu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Clock) loadTimers(db *sql.Sqlite) {
|
||||
if file.IsExist(db.DBPath) {
|
||||
c.db = db
|
||||
err := c.db.Create("timer", &Timer{})
|
||||
if err == nil {
|
||||
var t Timer
|
||||
c.db.FindFor("timer", &t, "", func() error {
|
||||
tescape := t
|
||||
go c.RegisterTimer(&tescape, false)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
24
plugin_manager/timer/timer_test.go
Normal file
24
plugin_manager/timer/timer_test.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package timer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func TestNextWakeTime(t *testing.T) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
ts := &Timer{}
|
||||
ts.SetMonth(-1)
|
||||
ts.SetWeek(6)
|
||||
ts.SetHour(16)
|
||||
ts.SetMinute(30)
|
||||
t1 := time.Until(ts.nextWakeTime())
|
||||
if t1 < 0 {
|
||||
t.Log(t1)
|
||||
t.Fail()
|
||||
}
|
||||
t.Log(t1)
|
||||
t.Fail()
|
||||
}
|
||||
87
plugin_manager/timer/wrap.go
Normal file
87
plugin_manager/timer/wrap.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package timer
|
||||
|
||||
import "time"
|
||||
|
||||
// En isEnabled 1bit
|
||||
func (m *Timer) En() (en bool) {
|
||||
return m.En1Month4Day5Week3Hour5Min6&0x800000 != 0
|
||||
}
|
||||
|
||||
// Month 4bits
|
||||
func (m *Timer) Month() (mon time.Month) {
|
||||
mon = time.Month((m.En1Month4Day5Week3Hour5Min6 & 0x780000) >> 19)
|
||||
if mon == 0b1111 {
|
||||
mon = -1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Day 5bits
|
||||
func (m *Timer) Day() (d int) {
|
||||
d = int((m.En1Month4Day5Week3Hour5Min6 & 0x07c000) >> 14)
|
||||
if d == 0b11111 {
|
||||
d = -1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Week 3bits
|
||||
func (m *Timer) Week() (w time.Weekday) {
|
||||
w = time.Weekday((m.En1Month4Day5Week3Hour5Min6 & 0x003800) >> 11)
|
||||
if w == 0b111 {
|
||||
w = -1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Hour 5bits
|
||||
func (m *Timer) Hour() (h int) {
|
||||
h = int((m.En1Month4Day5Week3Hour5Min6 & 0x0007c0) >> 6)
|
||||
if h == 0b11111 {
|
||||
h = -1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Minute 6bits
|
||||
func (m *Timer) Minute() (min int) {
|
||||
min = int(m.En1Month4Day5Week3Hour5Min6 & 0x00003f)
|
||||
if min == 0b111111 {
|
||||
min = -1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetEn ...
|
||||
func (m *Timer) SetEn(en bool) {
|
||||
if en {
|
||||
m.En1Month4Day5Week3Hour5Min6 |= 0x800000
|
||||
} else {
|
||||
m.En1Month4Day5Week3Hour5Min6 &= 0x7fffff
|
||||
}
|
||||
}
|
||||
|
||||
// SetMonth ...
|
||||
func (m *Timer) SetMonth(mon time.Month) {
|
||||
m.En1Month4Day5Week3Hour5Min6 = ((int32(mon) << 19) & 0x780000) | (m.En1Month4Day5Week3Hour5Min6 & 0x87ffff)
|
||||
}
|
||||
|
||||
// SetDay ...
|
||||
func (m *Timer) SetDay(d int) {
|
||||
m.En1Month4Day5Week3Hour5Min6 = ((int32(d) << 14) & 0x07c000) | (m.En1Month4Day5Week3Hour5Min6 & 0xf83fff)
|
||||
}
|
||||
|
||||
// SetWeek ...
|
||||
func (m *Timer) SetWeek(w time.Weekday) {
|
||||
m.En1Month4Day5Week3Hour5Min6 = ((int32(w) << 11) & 0x003800) | (m.En1Month4Day5Week3Hour5Min6 & 0xffc7ff)
|
||||
}
|
||||
|
||||
// SetHour ...
|
||||
func (m *Timer) SetHour(h int) {
|
||||
m.En1Month4Day5Week3Hour5Min6 = ((int32(h) << 6) & 0x0007c0) | (m.En1Month4Day5Week3Hour5Min6 & 0xfff83f)
|
||||
}
|
||||
|
||||
// SetMinute ...
|
||||
func (m *Timer) SetMinute(min int) {
|
||||
m.En1Month4Day5Week3Hour5Min6 = (int32(min) & 0x00003f) | (m.En1Month4Day5Week3Hour5Min6 & 0xffffc0)
|
||||
}
|
||||
@@ -19,24 +19,23 @@ import (
|
||||
|
||||
const api = "http://your.addr:23333/api/start_server/%s/?apikey=apikey"
|
||||
|
||||
var engine *zero.Engine
|
||||
var engine = control.Register("minecraft", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "minecraft\n" +
|
||||
"- /mcstart xxx\n" +
|
||||
"- /mcstop xxx\n" +
|
||||
"- /mclist servername\n" +
|
||||
"- https://github.com/Suwings/MCSManager",
|
||||
})
|
||||
|
||||
func init() {
|
||||
engine = control.Register("minecraft", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "minecraft\n" +
|
||||
"- /mcstart xxx\n" +
|
||||
"- /mcstop xxx\n" +
|
||||
"- /mclist servername\n" +
|
||||
"- https://github.com/Suwings/MCSManager",
|
||||
})
|
||||
engine.OnCommand("mcstart").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
model := extension.CommandModel{}
|
||||
_ = ctx.Parse(&model)
|
||||
ctx.SendChain(message.Text("开启服务器: ", model.Args, "....."))
|
||||
result := start(model.Args)
|
||||
ctx.Send(result)
|
||||
ctx.SendChain(message.Text(result))
|
||||
})
|
||||
engine.OnCommand("mcstop").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
@@ -44,7 +43,7 @@ func init() {
|
||||
_ = ctx.Parse(&model)
|
||||
ctx.SendChain(message.Text("开启服务器: ", model.Args, "....."))
|
||||
result := stop(model.Args)
|
||||
ctx.Send(result)
|
||||
ctx.SendChain(message.Text(result))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
40
plugin_moyu/nowork.go
Normal file
40
plugin_moyu/nowork.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package moyu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type holiday struct {
|
||||
name string
|
||||
date time.Time
|
||||
dur time.Duration
|
||||
}
|
||||
|
||||
// NewHoliday 节日名 天数 年 月 日
|
||||
func NewHoliday(name string, dur, year int, month time.Month, day int) *holiday {
|
||||
return &holiday{name: name, date: time.Date(year, month, day, 0, 0, 0, 0, time.Local), dur: time.Duration(dur) * time.Hour * 24}
|
||||
}
|
||||
|
||||
// 获取两个时间相差
|
||||
func (h *holiday) String() string {
|
||||
d := time.Until(h.date)
|
||||
if d >= 0 {
|
||||
return "距离" + h.name + "还有: " + strconv.FormatFloat(d.Hours()/24.0, 'f', 2, 64) + "天!"
|
||||
} else if d+h.dur >= 0 {
|
||||
return "好好享受 " + h.name + " 假期吧!"
|
||||
} else {
|
||||
return "今年 " + h.name + " 假期已过"
|
||||
}
|
||||
}
|
||||
|
||||
func weekend() string {
|
||||
t := time.Now().Weekday()
|
||||
switch t {
|
||||
case time.Sunday, time.Saturday:
|
||||
return "好好享受周末吧!"
|
||||
default:
|
||||
return fmt.Sprintf("距离周末还有:%d天!", 5-t)
|
||||
}
|
||||
}
|
||||
93
plugin_moyu/run.go
Normal file
93
plugin_moyu/run.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package moyu
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/cron"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
// 定时任务每天10点执行一次
|
||||
c := cron.New()
|
||||
_, err := c.AddFunc("0 10 * * *", func() { sendNotice() })
|
||||
if err == nil {
|
||||
c.Start()
|
||||
}
|
||||
|
||||
control.Register("moyu", &control.Options{
|
||||
DisableOnDefault: true,
|
||||
Help: "moyu\n" +
|
||||
"- 添加摸鱼提醒\n" +
|
||||
"- 删除摸鱼提醒",
|
||||
}).OnFullMatch("删除摸鱼提醒", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(20).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
m, ok := control.Lookup("moyu")
|
||||
if ok {
|
||||
if m.IsEnabledIn(ctx.Event.GroupID) {
|
||||
m.Disable(ctx.Event.GroupID)
|
||||
ctx.Send(message.Text("删除成功!"))
|
||||
} else {
|
||||
ctx.Send(message.Text("未启用!"))
|
||||
}
|
||||
} else {
|
||||
ctx.Send(message.Text("找不到该服务!"))
|
||||
}
|
||||
})
|
||||
|
||||
zero.OnFullMatch("添加摸鱼提醒", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(20).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
m, ok := control.Lookup("moyu")
|
||||
if ok {
|
||||
if m.IsEnabledIn(ctx.Event.GroupID) {
|
||||
ctx.Send(message.Text("已启用!"))
|
||||
} else {
|
||||
m.Enable(ctx.Event.GroupID)
|
||||
ctx.Send(message.Text("添加成功!"))
|
||||
}
|
||||
} else {
|
||||
ctx.Send(message.Text("找不到该服务!"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取数据拼接消息链并发送
|
||||
func sendNotice() {
|
||||
m, ok := control.Lookup("moyu")
|
||||
if ok {
|
||||
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
|
||||
for _, g := range ctx.GetGroupList().Array() {
|
||||
grp := g.Get("group_id").Int()
|
||||
if m.IsEnabledIn(grp) {
|
||||
ctx.SendGroupMessage(grp,
|
||||
[]message.MessageSegment{
|
||||
message.Text(time.Now().Format("2006-01-02")),
|
||||
message.Text("上午好,摸鱼人!\n工作再累,一定不要忘记摸鱼哦!有事没事起身去茶水间,去厕所,去廊道走走别老在工位上坐着,钱是老板的,但命是自己的。\n"),
|
||||
message.Text(weekend()),
|
||||
message.Text("\n"),
|
||||
message.Text(NewHoliday("元旦", 1, 2022, 1, 1)),
|
||||
message.Text("\n"),
|
||||
message.Text(NewHoliday("春节", 7, 2022, 1, 31)),
|
||||
message.Text("\n"),
|
||||
message.Text(NewHoliday("清明节", 1, 2022, 4, 3)),
|
||||
message.Text("\n"),
|
||||
message.Text(NewHoliday("劳动节", 1, 2022, 4, 30)),
|
||||
message.Text("\n"),
|
||||
message.Text(NewHoliday("端午节", 1, 2022, 6, 3)),
|
||||
message.Text("\n"),
|
||||
message.Text(NewHoliday("中秋节", 1, 2022, 9, 10)),
|
||||
message.Text("\n"),
|
||||
message.Text(NewHoliday("国庆节", 7, 2022, 10, 1)),
|
||||
message.Text("\n"),
|
||||
message.Text("上班是帮老板赚钱,摸鱼是赚老板的钱!最后,祝愿天下所有摸鱼人,都能愉快的渡过每一天…"),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ func init() {
|
||||
}).OnRegex("^(.{0,2})点歌(.{1,25})$").SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if !limit.Load(ctx.Event.UserID).Acquire() {
|
||||
ctx.Send("请稍后重试0x0...")
|
||||
ctx.SendChain(message.Text("请稍后重试0x0..."))
|
||||
return
|
||||
}
|
||||
// switch 平台
|
||||
|
||||
130
plugin_nativesetu/data.go
Normal file
130
plugin_nativesetu/data.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package nativesetu
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/corona10/goimagehash"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
"golang.org/x/image/webp"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
|
||||
)
|
||||
|
||||
// setuclass holds setus in a folder, which is the class name.
|
||||
type setuclass struct {
|
||||
ImgID int64 `db:"imgid"` // ImgID 图片唯一 id (dhash)
|
||||
Name string `db:"name"` // Name 图片名
|
||||
Path string `db:"path"` // Path 图片路径
|
||||
}
|
||||
|
||||
var (
|
||||
setuclasses []string
|
||||
db = &sql.Sqlite{DBPath: dbfile}
|
||||
mu sync.RWMutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
process.SleepAbout1sTo2s()
|
||||
err := os.MkdirAll(datapath, 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if file.IsExist(cfgfile) {
|
||||
b, err := os.ReadFile(cfgfile)
|
||||
if err == nil {
|
||||
setupath = helper.BytesToString(b)
|
||||
logrus.Println("[nsetu] set setu dir to", setupath)
|
||||
}
|
||||
}
|
||||
if file.IsExist(dbfile) {
|
||||
err := db.Open()
|
||||
if err == nil {
|
||||
setuclasses, err = db.ListTables()
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Errorln("[nsetu]", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func scanall(path string) error {
|
||||
setuclasses = nil
|
||||
model := &setuclass{}
|
||||
root := os.DirFS(path)
|
||||
_ = db.Close()
|
||||
_ = os.Remove(dbfile)
|
||||
return fs.WalkDir(root, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
clsn := d.Name()
|
||||
if clsn != "." {
|
||||
mu.Lock()
|
||||
err = db.Create(clsn, model)
|
||||
setuclasses = append(setuclasses, clsn)
|
||||
mu.Unlock()
|
||||
if err == nil {
|
||||
err = scanclass(root, path, clsn)
|
||||
if err != nil {
|
||||
logrus.Errorln("[nsetu]", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func scanclass(root fs.FS, path, clsn string) error {
|
||||
ds, err := fs.ReadDir(root, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mu.Lock()
|
||||
_ = db.Truncate(clsn)
|
||||
mu.Unlock()
|
||||
for _, d := range ds {
|
||||
if !d.IsDir() {
|
||||
relpath := path + "/" + d.Name()
|
||||
logrus.Debugln("[nsetu] read", relpath)
|
||||
f, e := fs.ReadFile(root, relpath)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
b := bytes.NewReader(f)
|
||||
img, _, e := image.Decode(b)
|
||||
if e != nil {
|
||||
b.Seek(0, io.SeekStart)
|
||||
img, e = webp.Decode(b)
|
||||
}
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
dh, e := goimagehash.DifferenceHash(img)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
dhi := int64(dh.GetHash())
|
||||
logrus.Debugln("[nsetu] insert", d.Name(), "with id", dhi, "into", clsn)
|
||||
mu.Lock()
|
||||
err = db.Insert(clsn, &setuclass{ImgID: dhi, Name: d.Name(), Path: relpath})
|
||||
mu.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
95
plugin_nativesetu/main.go
Normal file
95
plugin_nativesetu/main.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package nativesetu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/rule"
|
||||
)
|
||||
|
||||
const (
|
||||
datapath = "data/nsetu"
|
||||
dbfile = datapath + "/data.db"
|
||||
cfgfile = datapath + "/setupath.txt"
|
||||
)
|
||||
|
||||
var (
|
||||
setupath = "/tmp" // 绝对路径,图片根目录
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.Register("nativesetu", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "本地涩图\n" +
|
||||
"- 本地[xxx]\n" +
|
||||
"- 刷新本地[xxx]\n" +
|
||||
"- 设置本地setu绝对路径[xxx]\n" +
|
||||
"- 刷新所有本地setu\n" +
|
||||
"- 所有本地setu分类",
|
||||
})
|
||||
engine.OnRegex(`^本地(.*)$`, func(ctx *zero.Ctx) bool { return rule.FirstValueInList(setuclasses)(ctx) }).SetBlock(true).SetPriority(36).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
imgtype := ctx.State["regex_matched"].([]string)[1]
|
||||
sc := new(setuclass)
|
||||
mu.RLock()
|
||||
err := db.Pick(imgtype, sc)
|
||||
mu.RUnlock()
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
} else {
|
||||
p := "file:///" + setupath + "/" + sc.Path
|
||||
ctx.SendChain(message.Text(imgtype, ": ", sc.Name, "\n"), message.Image(p))
|
||||
}
|
||||
})
|
||||
engine.OnRegex(`^刷新本地(.*)$`, func(ctx *zero.Ctx) bool { return rule.FirstValueInList(setuclasses)(ctx) }, zero.SuperUserPermission).SetBlock(true).SetPriority(36).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
imgtype := ctx.State["regex_matched"].([]string)[1]
|
||||
err := scanclass(os.DirFS(setupath), imgtype, imgtype)
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text("成功!"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
}
|
||||
})
|
||||
engine.OnRegex(`^设置本地setu绝对路径(.*)$`, zero.SuperUserPermission).SetBlock(true).SetPriority(36).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
setupath = ctx.State["regex_matched"].([]string)[1]
|
||||
err := os.WriteFile(cfgfile, helper.StringToBytes(setupath), 0644)
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text("成功!"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
}
|
||||
})
|
||||
engine.OnFullMatch("刷新所有本地setu", zero.SuperUserPermission).SetBlock(true).SetPriority(36).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
err := scanall(setupath)
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text("成功!"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
}
|
||||
})
|
||||
engine.OnFullMatch("所有本地setu分类").SetBlock(true).SetPriority(36).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
msg := "所有本地setu分类"
|
||||
mu.RLock()
|
||||
for i, c := range setuclasses {
|
||||
n, err := db.Count(c)
|
||||
if err == nil {
|
||||
msg += fmt.Sprintf("\n%02d. %s(%d)", i, c, n)
|
||||
} else {
|
||||
msg += fmt.Sprintf("\n%02d. %s(error)", i, c)
|
||||
logrus.Errorln("[nsetu]", err)
|
||||
}
|
||||
}
|
||||
mu.RUnlock()
|
||||
ctx.SendChain(message.Text(msg))
|
||||
})
|
||||
}
|
||||
169
plugin_nativewife/main.go
Normal file
169
plugin_nativewife/main.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package nativewife
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/picture"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
|
||||
)
|
||||
|
||||
const base = "data/nwife"
|
||||
|
||||
var baseuri = "file:///" + file.BOTPATH + "/" + base
|
||||
|
||||
func init() {
|
||||
err := os.MkdirAll(base, 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
engine := control.Register("nwife", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "nativewife\n- 抽wife[@xxx]\n- 添加wife[名字][图片]\n- 删除wife[名字]\n- [让|不让]所有人均可添加wife",
|
||||
})
|
||||
engine.OnPrefix("抽wife", zero.OnlyGroup).SetBlock(true).SetPriority(20).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
grpf := strconv.FormatInt(ctx.Event.GroupID, 36)
|
||||
wifes, err := os.ReadDir(base + "/" + grpf)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("一个wife也没有哦~"))
|
||||
return
|
||||
}
|
||||
switch len(wifes) {
|
||||
case 0:
|
||||
ctx.SendChain(message.Text("一个wife也没有哦~"))
|
||||
case 1:
|
||||
wn := wifes[0].Name()
|
||||
ctx.SendChain(message.Text("大家的wife都是", wn, "\n"), message.Image(baseuri+"/"+grpf+"/"+wn), message.Text("\n哦~"))
|
||||
default:
|
||||
// 获取名字
|
||||
name := ctx.State["args"].(string)
|
||||
if len(ctx.Event.Message) > 1 && ctx.Event.Message[1].Type == "at" {
|
||||
qq, _ := strconv.ParseInt(ctx.Event.Message[1].Data["qq"], 10, 64)
|
||||
name = ctx.GetGroupMemberInfo(ctx.Event.GroupID, qq, false).Get("nickname").Str
|
||||
} else if name == "" {
|
||||
name = ctx.Event.Sender.NickName
|
||||
}
|
||||
now := time.Now()
|
||||
s := md5.Sum(helper.StringToBytes(fmt.Sprintf("%s%d%d%d", name, now.Year(), now.Month(), now.Day())))
|
||||
r := rand.New(rand.NewSource(int64(binary.LittleEndian.Uint64(s[:]))))
|
||||
n := r.Intn(len(wifes))
|
||||
wn := wifes[n].Name()
|
||||
ctx.SendChain(message.Text(name, "的wife是", wn, "\n"), message.Image(baseuri+"/"+grpf+"/"+wn), message.Text("\n哦~"))
|
||||
}
|
||||
})
|
||||
// 上传一张图
|
||||
engine.OnPrefix("添加wife", zero.OnlyGroup, chkAddWifePermission, picture.MustGiven).SetBlock(true).SetPriority(20).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
name := ""
|
||||
for _, elem := range ctx.Event.Message {
|
||||
if elem.Type == "text" {
|
||||
name = strings.ReplaceAll(elem.Data["text"], " ", "")
|
||||
name = name[strings.LastIndex(name, "添加wife")+10:]
|
||||
name = strings.ReplaceAll(name, "/", "")
|
||||
name = strings.ReplaceAll(name, "\\", "")
|
||||
break
|
||||
}
|
||||
}
|
||||
if name != "" {
|
||||
url := ctx.State["image_url"].([]string)[0]
|
||||
grpfolder := base + "/" + strconv.FormatInt(ctx.Event.GroupID, 36)
|
||||
if file.IsNotExist(grpfolder) {
|
||||
os.Mkdir(grpfolder, 0755)
|
||||
}
|
||||
err = file.DownloadTo(url, grpfolder+"/"+name, true)
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("成功!"))
|
||||
} else {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("错误:", err.Error()))
|
||||
}
|
||||
} else {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("没有找到wife的名字!"))
|
||||
}
|
||||
})
|
||||
engine.OnPrefix("删除wife", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(20).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
name := ""
|
||||
for _, elem := range ctx.Event.Message {
|
||||
if elem.Type == "text" {
|
||||
name = strings.ReplaceAll(elem.Data["text"], " ", "")
|
||||
name = name[strings.LastIndex(name, "删除wife")+10:]
|
||||
name = strings.ReplaceAll(name, "/", "")
|
||||
name = strings.ReplaceAll(name, "\\", "")
|
||||
break
|
||||
}
|
||||
}
|
||||
if name != "" {
|
||||
grpfolder := base + "/" + strconv.FormatInt(ctx.Event.GroupID, 36)
|
||||
err = os.Remove(grpfolder + "/" + name)
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("成功!"))
|
||||
} else {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("错误:", err.Error()))
|
||||
}
|
||||
} else {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("没有找到wife的名字!"))
|
||||
}
|
||||
})
|
||||
engine.OnSuffix("所有人均可添加wife", zero.SuperUserPermission, zero.OnlyGroup).SetBlock(true).SetPriority(20).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
text := ""
|
||||
for _, elem := range ctx.Event.Message {
|
||||
if elem.Type == "text" {
|
||||
text = strings.ReplaceAll(elem.Data["text"], " ", "")
|
||||
text = text[:strings.LastIndex(text, "所有人均可添加wife")]
|
||||
break
|
||||
}
|
||||
}
|
||||
var err error
|
||||
switch text {
|
||||
case "设置", "授予", "让":
|
||||
err = setEveryoneCanAddWife(ctx.Event.GroupID, true)
|
||||
case "取消", "撤销", "不让":
|
||||
err = setEveryoneCanAddWife(ctx.Event.GroupID, false)
|
||||
}
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("成功!"))
|
||||
} else {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("错误:", err.Error()))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func chkAddWifePermission(ctx *zero.Ctx) bool {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid > 0 {
|
||||
m, ok := control.Lookup("nwife")
|
||||
if ok {
|
||||
data := m.GetData(gid)
|
||||
if data&1 == 1 {
|
||||
return true
|
||||
}
|
||||
return zero.AdminPermission(ctx)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func setEveryoneCanAddWife(gid int64, canadd bool) error {
|
||||
m, ok := control.Lookup("nwife")
|
||||
if ok {
|
||||
if canadd {
|
||||
return m.SetData(gid, 1)
|
||||
}
|
||||
return m.SetData(gid, 0)
|
||||
}
|
||||
return errors.New("no such plugin")
|
||||
}
|
||||
186
plugin_novel/qianbi.go
Normal file
186
plugin_novel/qianbi.go
Normal file
@@ -0,0 +1,186 @@
|
||||
// Package novel 铅笔小说搜索插件
|
||||
package novel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/antchfx/htmlquery"
|
||||
log "github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/rate"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
ub "github.com/FloatTech/ZeroBot-Plugin/utils/binary"
|
||||
)
|
||||
|
||||
const (
|
||||
websiteURL = "https://www.23qb.net"
|
||||
websiteTitle = "铅笔小说"
|
||||
errorTitle = "出现错误!"
|
||||
username = "zerobot"
|
||||
password = "123456"
|
||||
submit = "%26%23160%3B%B5%C7%26%23160%3B%26%23160%3B%C2%BC%26%23160%3B"
|
||||
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
|
||||
loginURL = websiteURL + "/login.php?do=submit&jumpurl=https%3A%2F%2Fwww.23qb.net%2F"
|
||||
searchURL = websiteURL + "/saerch.php"
|
||||
idReg = `/(\d+)/`
|
||||
)
|
||||
|
||||
var (
|
||||
gCurCookieJar *cookiejar.Jar
|
||||
engine = control.Register("novel", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "铅笔小说网搜索\n- 小说[xxx]",
|
||||
})
|
||||
limit = rate.NewManager(time.Minute, 5)
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine.OnRegex("^小说([\u4E00-\u9FA5A-Za-z0-9]{1,25})$").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if !limit.Load(ctx.Event.GroupID).Acquire() {
|
||||
ctx.SendChain(message.Text("请稍后重试0x0..."))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("少女祈祷中......"))
|
||||
login(username, password)
|
||||
searchKey := ctx.State["regex_matched"].([]string)[1]
|
||||
searchHtml := search(searchKey)
|
||||
var m message.Message
|
||||
doc, err := htmlquery.Parse(strings.NewReader(searchHtml))
|
||||
if err != nil {
|
||||
log.Errorln("[novel]", err)
|
||||
}
|
||||
htmlTitle := htmlquery.InnerText(htmlquery.FindOne(doc, "/html/head/title"))
|
||||
if htmlTitle == websiteTitle {
|
||||
list, err := htmlquery.QueryAll(doc, "//dl[@id='nr']")
|
||||
if err != nil {
|
||||
log.Errorln("[novel]", err)
|
||||
}
|
||||
if len(list) != 0 {
|
||||
for _, v := range list {
|
||||
bookName := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[1]/h3/a[1]"))
|
||||
category := htmlquery.InnerText(htmlquery.FindOne(v, "/dt/span[1]"))
|
||||
author := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[2]/span[1]"))
|
||||
status := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[2]/span[2]"))
|
||||
wordNumbers := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[2]/span[3]"))
|
||||
description := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[3]"))
|
||||
updateTime := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[1]/h3/span[1]"))
|
||||
latestChapter := htmlquery.InnerText(htmlquery.FindOne(v, "/dd[4]/a[1]"))
|
||||
|
||||
reg := regexp.MustCompile(idReg)
|
||||
id := reg.FindStringSubmatch(htmlquery.SelectAttr(htmlquery.FindOne(v, "/dt/a[1]"), "href"))[1]
|
||||
|
||||
webpageURL := websiteURL + "/book/" + id + "/"
|
||||
downloadURL := websiteURL + "/modules/article/txtarticle.php?id=" + id
|
||||
text := fmt.Sprintf("书名:%s\n类型:%s\n作者:%s\n状态:%s\n字数:%s\n简介:%s\n更新时间:%s\n最新章节:%s\n网页链接:%s\n下载地址:%s\n", bookName, category, author, status, wordNumbers, description, updateTime, latestChapter, webpageURL, downloadURL)
|
||||
m = append(m,
|
||||
message.CustomNode(
|
||||
zero.BotConfig.NickName[0],
|
||||
ctx.Event.SelfID,
|
||||
text),
|
||||
)
|
||||
}
|
||||
if id := ctx.SendGroupForwardMessage(
|
||||
ctx.Event.GroupID,
|
||||
m).Get("message_id").Int(); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
} else {
|
||||
text := htmlquery.InnerText(htmlquery.FindOne(doc, "//div[@id='tipss']"))
|
||||
text = strings.Replace(text, " ", "", -1)
|
||||
text = strings.Replace(text, "本站", websiteURL, -1)
|
||||
ctx.SendChain(message.Text(text))
|
||||
}
|
||||
} else if htmlTitle == errorTitle {
|
||||
ctx.SendChain(message.Text(errorTitle))
|
||||
text := htmlquery.InnerText(htmlquery.FindOne(doc, "//div[@style='text-align: center;padding:10px']"))
|
||||
text = strings.Replace(text, " ", "", -1)
|
||||
ctx.SendChain(message.Text(text))
|
||||
} else {
|
||||
bookName := htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:book_name']"), "content")
|
||||
category := htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:category']"), "content")
|
||||
author := htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:author']"), "content")
|
||||
status := htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:status']"), "content")
|
||||
description := htmlquery.InnerText(htmlquery.FindOne(doc, "//div[@id='bookintro']/p"))
|
||||
updateTime := htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:update_time']"), "content")
|
||||
latestChapter := htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:latest_chapter_name']"), "content")
|
||||
|
||||
reg := regexp.MustCompile(idReg)
|
||||
id := reg.FindStringSubmatch(htmlquery.SelectAttr(htmlquery.FindOne(doc, "//meta[@property='og:novel:read_url']"), "content"))[1]
|
||||
webpageURL := websiteURL + "/book/" + id + "/"
|
||||
downloadURL := websiteURL + "/modules/article/txtarticle.php?id=" + id
|
||||
text := fmt.Sprintf("书名:%s\n类型:%s\n作者:%s\n状态:%s\n简介:%s\n更新时间:%s\n最新章节:%s\n网页链接:%s\n下载地址:%s\n", bookName, category, author, status, description, updateTime, latestChapter, webpageURL, downloadURL)
|
||||
ctx.SendChain(message.Text(text))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func login(username, password string) {
|
||||
gCurCookieJar, _ = cookiejar.New(nil)
|
||||
client := &http.Client{
|
||||
Jar: gCurCookieJar,
|
||||
}
|
||||
usernameData, err := ub.UTF82GBK(helper.StringToBytes(username))
|
||||
if err != nil {
|
||||
log.Errorln("[novel]", err)
|
||||
}
|
||||
usernameGbk := helper.BytesToString(usernameData)
|
||||
passwordData, err := ub.UTF82GBK(helper.StringToBytes(password))
|
||||
if err != nil {
|
||||
log.Errorln("[novel]", err)
|
||||
}
|
||||
passwordGbk := helper.BytesToString(passwordData)
|
||||
loginReq, err := http.NewRequest("POST", loginURL, strings.NewReader(fmt.Sprintf("username=%s&password=%s&usecookie=315360000&action=login&submit=%s", url.QueryEscape(usernameGbk), url.QueryEscape(passwordGbk), submit)))
|
||||
if err != nil {
|
||||
log.Errorln("[novel]", err)
|
||||
}
|
||||
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
loginReq.Header.Set("User-Agent", ua)
|
||||
loginResp, err := client.Do(loginReq)
|
||||
if err != nil {
|
||||
log.Errorln("[novel]", err)
|
||||
}
|
||||
defer loginResp.Body.Close()
|
||||
}
|
||||
|
||||
func search(searchKey string) (searchHtml string) {
|
||||
searchKeyData, err := ub.UTF82GBK(helper.StringToBytes(searchKey))
|
||||
if err != nil {
|
||||
log.Errorln("[novel]", err)
|
||||
}
|
||||
searchKeyGbk := helper.BytesToString(searchKeyData)
|
||||
client := &http.Client{
|
||||
Jar: gCurCookieJar,
|
||||
}
|
||||
searchReq, err := http.NewRequest("POST", searchURL, strings.NewReader(fmt.Sprintf("searchkey=%s&searchtype=all", url.QueryEscape(searchKeyGbk))))
|
||||
if err != nil {
|
||||
log.Errorln("[novel]", err)
|
||||
}
|
||||
searchReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
searchReq.Header.Set("User-Agent", ua)
|
||||
searchResp, err := client.Do(searchReq)
|
||||
if err != nil {
|
||||
log.Errorln("[novel]", err)
|
||||
}
|
||||
defer searchResp.Body.Close()
|
||||
searchData, err := ioutil.ReadAll(searchResp.Body)
|
||||
if err != nil {
|
||||
log.Errorf("[novel] get response for url=%s got error=%s\n", searchURL, err.Error())
|
||||
}
|
||||
searchData, err = ub.GBK2UTF8(searchData)
|
||||
if err != nil {
|
||||
log.Errorln("[novel]", err)
|
||||
}
|
||||
searchHtml = helper.BytesToString(searchData)
|
||||
return searchHtml
|
||||
}
|
||||
40
plugin_omikuji/data.go
Normal file
40
plugin_omikuji/data.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package omikuji
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
|
||||
)
|
||||
|
||||
const (
|
||||
dbpath = "data/Omikuji/"
|
||||
dbfile = dbpath + "kuji.db"
|
||||
)
|
||||
|
||||
var db = &sql.Sqlite{DBPath: dbfile}
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
process.SleepAbout1sTo2s()
|
||||
_ = os.MkdirAll(dbpath, 0755)
|
||||
_, _ = file.GetLazyData(dbfile, false, true)
|
||||
err := db.Create("kuji", &kuji{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
n, err := db.Count("kuji")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Printf("[kuji]读取%d条签文", n)
|
||||
}()
|
||||
}
|
||||
46
plugin_omikuji/migrate/main.go
Normal file
46
plugin_omikuji/migrate/main.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
|
||||
)
|
||||
|
||||
type signature struct {
|
||||
Id uint64 `db:"id"`
|
||||
Text string `db:"text"`
|
||||
}
|
||||
|
||||
type kuji struct {
|
||||
Id uint8 `db:"id"`
|
||||
Text string `db:"text"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
db := &sql.Sqlite{DBPath: os.Args[1]}
|
||||
newdb := &sql.Sqlite{DBPath: os.Args[2]}
|
||||
err := newdb.Create("kuji", &kuji{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = db.Create("signature", &signature{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(db.Count("signature"))
|
||||
s := &signature{}
|
||||
k := &kuji{}
|
||||
for i := 1; i <= 100; i++ {
|
||||
db.Find("signature", s, "where id = "+strconv.Itoa(i))
|
||||
fmt.Println("insert: ", s.Text[:57])
|
||||
k.Id = uint8(i)
|
||||
k.Text = s.Text
|
||||
newdb.Insert("kuji", k)
|
||||
}
|
||||
|
||||
db.Close()
|
||||
newdb.Close()
|
||||
}
|
||||
18
plugin_omikuji/model.go
Normal file
18
plugin_omikuji/model.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package omikuji
|
||||
|
||||
import "strconv"
|
||||
|
||||
type kuji struct {
|
||||
Id uint8 `db:"id"`
|
||||
Text string `db:"text"`
|
||||
}
|
||||
|
||||
// 返回一个解签
|
||||
func getKujiByBango(id uint8) string {
|
||||
var s kuji
|
||||
err := db.Find("kuji", &s, "where id = "+strconv.Itoa(int(id)))
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return s.Text
|
||||
}
|
||||
@@ -4,31 +4,54 @@ package omikuji
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
)
|
||||
|
||||
const (
|
||||
bed = "https://codechina.csdn.net/u011570312/senso-ji-omikuji/-/raw/main/%d_%d.jpg"
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
control.Register("omikuji", &control.Options{
|
||||
var (
|
||||
engine = control.Register("omikuji", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "浅草寺求签\n" +
|
||||
"- 求签|占卜",
|
||||
}).OnFullMatchGroup([]string{"求签", "占卜"}).SetPriority(10).SetBlock(true).
|
||||
"- 求签|占卜\n- 解签",
|
||||
})
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
engine.OnFullMatchGroup([]string{"求签", "占卜"}).SetPriority(10).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
miku := rand.Intn(100) + 1
|
||||
miku := bangoToday(ctx.Event.UserID)
|
||||
ctx.SendChain(
|
||||
message.At(ctx.Event.UserID),
|
||||
message.Image(fmt.Sprintf(bed, miku, 0)),
|
||||
message.Image(fmt.Sprintf(bed, miku, 1)),
|
||||
)
|
||||
})
|
||||
engine.OnFullMatchGroup([]string{"解签"}).SetPriority(10).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(
|
||||
message.At(ctx.Event.UserID),
|
||||
message.Text(getKujiByBango(bangoToday(ctx.Event.UserID))),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func bangoToday(uid int64) uint8 {
|
||||
today, err := strconv.ParseInt(time.Now().Format("20060102"), 10, 64)
|
||||
if err != nil {
|
||||
log.Errorln("string转化为int64格式有问题:", err)
|
||||
}
|
||||
seed := uid + today
|
||||
r := rand.New(rand.NewSource(seed))
|
||||
return uint8(r.Intn(100) + 1)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -20,7 +21,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
prio = 100
|
||||
prio = 256
|
||||
bucket = rate.NewManager(time.Minute, 20) // 青云客接口回复
|
||||
engine *zero.Engine
|
||||
)
|
||||
@@ -31,14 +32,14 @@ func init() { // 插件主体
|
||||
Help: "青云客\n" +
|
||||
"- @Bot 任意文本(任意一句话回复)",
|
||||
})
|
||||
// 回复 匹配中文、英文、数字、空格但不包括下划线等符号
|
||||
engine.OnRegex("^([\u4E00-\u9FA5A-Za-z0-9\\s]{1,30})", zero.OnlyToMe).SetBlock(true).SetPriority(prio).
|
||||
// 回复 @和包括名字
|
||||
engine.OnMessage(zero.OnlyToMe).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if !bucket.Load(ctx.Event.UserID).Acquire() {
|
||||
// 频繁触发,不回复
|
||||
return
|
||||
}
|
||||
msg := ctx.State["regex_matched"].([]string)[1]
|
||||
msg := ctx.ExtractPlainText()
|
||||
// 调用青云客接口
|
||||
reply, err := getMessage(msg)
|
||||
if err != nil {
|
||||
@@ -96,15 +97,17 @@ type dataQYK struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
const (
|
||||
qykURL = "http://api.qingyunke.com/api.php"
|
||||
key = "free"
|
||||
appid = "0"
|
||||
)
|
||||
|
||||
// 青云客取消息
|
||||
func getMessage(msg string) (string, error) {
|
||||
url := "http://api.qingyunke.com/api.php"
|
||||
key := "free"
|
||||
appid := "0"
|
||||
url = fmt.Sprintf(url+"?key=%s&appid=%s&msg=%s", key, appid, msg)
|
||||
|
||||
u := fmt.Sprintf(qykURL+"?key=%s&appid=%s&msg=%s", key, appid, url.QueryEscape(msg))
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
req, err := http.NewRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package qingyunke
|
||||
|
||||
// TODO: 移动到 manager 搭配自动验证使用
|
||||
|
||||
/*
|
||||
import (
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
)
|
||||
|
||||
//自动同意加群,加好友
|
||||
func init() {
|
||||
zero.OnRequest().SetBlock(false).FirstPriority().Handle(func(ctx *zero.Ctx) {
|
||||
if ctx.Event.RequestType == "friend" {
|
||||
ctx.SetFriendAddRequest(ctx.Event.Flag, true, "")
|
||||
}
|
||||
if ctx.Event.RequestType == "group" && ctx.Event.SubType == "invite" {
|
||||
ctx.SetGroupAddRequest(ctx.Event.Flag, "invite", true, "我爱你,mua~")
|
||||
}
|
||||
})
|
||||
}
|
||||
*/
|
||||
@@ -2,19 +2,18 @@ package reborn
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
wr "github.com/mroth/weightedrand"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
|
||||
)
|
||||
|
||||
const (
|
||||
datapath = "data/Reborn"
|
||||
jsonfile = datapath + "/rate.json"
|
||||
pburl = "https://codechina.csdn.net/u011570312/ZeroBot-Plugin/-/raw/master/" + jsonfile
|
||||
)
|
||||
|
||||
type rate []struct {
|
||||
@@ -28,7 +27,7 @@ var (
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
time.Sleep(time.Second)
|
||||
process.SleepAbout1sTo2s()
|
||||
err := os.MkdirAll(datapath, 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -53,39 +52,9 @@ func init() {
|
||||
|
||||
// load 加载rate数据
|
||||
func load(area *rate) error {
|
||||
if _, err := os.Stat(jsonfile); err == nil || os.IsExist(err) {
|
||||
f, err := os.Open(jsonfile)
|
||||
if err == nil {
|
||||
defer f.Close()
|
||||
data, err1 := io.ReadAll(f)
|
||||
if err1 == nil {
|
||||
if len(data) > 0 {
|
||||
return json.Unmarshal(data, area)
|
||||
}
|
||||
}
|
||||
return err1
|
||||
}
|
||||
} else { // 如果没有小作文,则从 url 下载
|
||||
f, err := os.Create(jsonfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
resp, err := http.Get(pburl)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
if resp.ContentLength > 0 {
|
||||
log.Printf("[Reborn]从镜像下载国家和地区%d字节...", resp.ContentLength)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err == nil && len(data) > 0 {
|
||||
_, _ = f.Write(data)
|
||||
return json.Unmarshal(data, area)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
data, err := file.GetLazyData(jsonfile, true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return json.Unmarshal(data, area)
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ func init() {
|
||||
}).OnRegex(`^>runcode\s(.+?)\s([\s\S]+)$`).SetBlock(true).SecondPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if !limit.Load(ctx.Event.UserID).Acquire() {
|
||||
ctx.Send("请稍后重试0x0...")
|
||||
ctx.SendChain(message.Text("请稍后重试0x0..."))
|
||||
} else {
|
||||
language := ctx.State["regex_matched"].([]string)[1]
|
||||
language = strings.ToLower(language)
|
||||
|
||||
@@ -3,20 +3,31 @@ package saucenao
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/ascii2d"
|
||||
"github.com/FloatTech/AnimeAPI/picture"
|
||||
"github.com/FloatTech/AnimeAPI/pixiv"
|
||||
"github.com/FloatTech/AnimeAPI/saucenao"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
|
||||
)
|
||||
|
||||
var (
|
||||
datapath = file.BOTPATH + "/data/saucenao/"
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
_ = os.RemoveAll(datapath)
|
||||
err := os.MkdirAll(datapath, 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
engine := control.Register("saucenao", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "搜图\n" +
|
||||
@@ -27,7 +38,7 @@ func init() { // 插件主体
|
||||
engine.OnRegex(`^搜图(\d+)$`).SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
id, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
|
||||
ctx.Send("少女祈祷中......")
|
||||
ctx.SendChain(message.Text("少女祈祷中......"))
|
||||
// 获取P站插图信息
|
||||
illust, err := pixiv.Works(id)
|
||||
if err != nil {
|
||||
@@ -35,30 +46,48 @@ func init() { // 插件主体
|
||||
return
|
||||
}
|
||||
if illust.Pid > 0 {
|
||||
// 改用 i.pixiv.cat 镜像站
|
||||
link := illust.ImageUrls
|
||||
link = strings.ReplaceAll(link, "i.pximg.net", "i.pixiv.cat")
|
||||
// 发送搜索结果
|
||||
ctx.SendChain(
|
||||
message.Image(link),
|
||||
message.Text(
|
||||
"\n",
|
||||
"标题:", illust.Title, "\n",
|
||||
"插画ID:", illust.Pid, "\n",
|
||||
"画师:", illust.UserName, "\n",
|
||||
"画师ID:", illust.UserId, "\n",
|
||||
"直链:", "https://pixivel.moe/detail?id=", illust.Pid,
|
||||
),
|
||||
name := strconv.FormatInt(illust.Pid, 10)
|
||||
filepath := datapath + name
|
||||
switch {
|
||||
case file.IsExist(filepath + ".jpg"):
|
||||
filepath = "file:///" + filepath + ".jpg"
|
||||
case file.IsExist(filepath + ".png"):
|
||||
filepath = "file:///" + filepath + ".png"
|
||||
case file.IsExist(filepath + ".gif"):
|
||||
filepath = "file:///" + filepath + ".gif"
|
||||
default:
|
||||
filepath = ""
|
||||
}
|
||||
if filepath == "" {
|
||||
logrus.Debug("[sausenao]开始下载", name)
|
||||
filepath, err = pixiv.Download(illust.ImageUrls, datapath, name)
|
||||
if err == nil {
|
||||
filepath = "file:///" + filepath
|
||||
}
|
||||
}
|
||||
txt := message.Text(
|
||||
"标题:", illust.Title, "\n",
|
||||
"插画ID:", illust.Pid, "\n",
|
||||
"画师:", illust.UserName, "\n",
|
||||
"画师ID:", illust.UserId, "\n",
|
||||
"直链:", "https://pixivel.moe/detail?id=", illust.Pid,
|
||||
)
|
||||
if filepath != "" {
|
||||
// 发送搜索结果
|
||||
ctx.SendChain(message.Image(filepath), message.Text("\n"), txt)
|
||||
} else {
|
||||
// 图片下载失败,仅发送文字结果
|
||||
ctx.SendChain(txt)
|
||||
}
|
||||
} else {
|
||||
ctx.Send("图片不存在!")
|
||||
ctx.SendChain(message.Text("图片不存在!"))
|
||||
}
|
||||
})
|
||||
// 以图搜图
|
||||
engine.OnKeywordGroup([]string{"以图搜图", "搜索图片", "以图识图"}, picture.CmdMatch, picture.MustGiven).SetBlock(true).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
// 开始搜索图片
|
||||
ctx.Send("少女祈祷中......")
|
||||
ctx.SendChain(message.Text("少女祈祷中......"))
|
||||
for _, pic := range ctx.State["image_url"].([]string) {
|
||||
fmt.Println(pic)
|
||||
if result, err := saucenao.SauceNAO(pic); err != nil {
|
||||
|
||||
@@ -3,8 +3,6 @@ package setutime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -12,19 +10,22 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/pixiv"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/rate"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/data"
|
||||
fileutil "github.com/FloatTech/ZeroBot-Plugin/utils/file"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/math"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/rule"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
|
||||
)
|
||||
|
||||
// Pools 图片缓冲池
|
||||
type imgpool struct {
|
||||
Lock sync.Mutex
|
||||
DB *data.Sqlite
|
||||
DB *sql.Sqlite
|
||||
Path string
|
||||
Group int64
|
||||
List []string
|
||||
@@ -33,14 +34,10 @@ type imgpool struct {
|
||||
Form int64
|
||||
}
|
||||
|
||||
const (
|
||||
dburl = "https://codechina.csdn.net/u011570312/ZeroBot-Plugin/-/raw/master/data/SetuTime/SetuTime.db"
|
||||
)
|
||||
|
||||
// NewPoolsCache 返回一个缓冲池对象
|
||||
func newPools() *imgpool {
|
||||
cache := &imgpool{
|
||||
DB: &data.Sqlite{DBPath: "data/SetuTime/SetuTime.db"},
|
||||
DB: &sql.Sqlite{DBPath: "data/SetuTime/SetuTime.db"},
|
||||
Path: "data/SetuTime/cache/",
|
||||
Group: 0,
|
||||
List: []string{"涩图", "二次元", "风景", "车万"}, // 可以自己加类别,得自己加图片进数据库
|
||||
@@ -55,26 +52,7 @@ func newPools() *imgpool {
|
||||
panic(err)
|
||||
}
|
||||
// 如果数据库不存在则下载
|
||||
if _, err := os.Stat(cache.DB.DBPath); err != nil || os.IsNotExist(err) {
|
||||
f, err := os.Create(cache.DB.DBPath)
|
||||
if err == nil {
|
||||
resp, err := http.Get(dburl)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
if resp.ContentLength > 0 {
|
||||
logrus.Printf("[Setu]从镜像下载数据库%d字节...", resp.ContentLength)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err == nil && len(data) > 0 {
|
||||
_, err = f.Write(data)
|
||||
if err != nil {
|
||||
logrus.Errorf("[Setu]写入数据库失败: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
_, _ = fileutil.GetLazyData(cache.DB.DBPath, false, false)
|
||||
for i := range cache.List {
|
||||
if err := cache.DB.Create(cache.List[i], &pixiv.Illust{}); err != nil {
|
||||
panic(err)
|
||||
@@ -84,149 +62,127 @@ func newPools() *imgpool {
|
||||
}
|
||||
|
||||
var (
|
||||
pool = newPools()
|
||||
pool *imgpool
|
||||
limit = rate.NewManager(time.Minute*1, 5)
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
engine := control.Register("setutime", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "涩图\n" +
|
||||
"- 来份[涩图/二次元/风景/车万]\n" +
|
||||
"- 添加[涩图/二次元/风景/车万][P站图片ID]\n" +
|
||||
"- 删除[涩图/二次元/风景/车万][P站图片ID]\n" +
|
||||
"- >setu status",
|
||||
})
|
||||
engine.OnRegex(`^来份(.*)$`, firstValueInList(pool.List)).SetBlock(true).SetPriority(20).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if !limit.Load(ctx.Event.UserID).Acquire() {
|
||||
ctx.SendChain(message.Text("请稍后重试0x0..."))
|
||||
return
|
||||
}
|
||||
var imgtype = ctx.State["regex_matched"].([]string)[1]
|
||||
// 补充池子
|
||||
go func() {
|
||||
times := min(pool.Max-pool.size(imgtype), 2)
|
||||
for i := 0; i < times; i++ {
|
||||
illust := &pixiv.Illust{}
|
||||
// 查询出一张图片
|
||||
if err := pool.DB.Find(imgtype, illust, "ORDER BY RANDOM() limit 1"); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
continue
|
||||
}
|
||||
// 下载图片
|
||||
if err := download(illust, pool.Path); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
continue
|
||||
}
|
||||
ctx.SendGroupMessage(pool.Group, []message.MessageSegment{message.Image(file(illust))})
|
||||
// 向缓冲池添加一张图片
|
||||
pool.push(imgtype, illust)
|
||||
|
||||
time.Sleep(time.Second * 1)
|
||||
}
|
||||
}()
|
||||
// 如果没有缓存,阻塞5秒
|
||||
if pool.size(imgtype) == 0 {
|
||||
ctx.SendChain(message.Text("INFO: 正在填充弹药......"))
|
||||
<-time.After(time.Second * 5)
|
||||
if pool.size(imgtype) == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 等待填充,请稍后再试......"))
|
||||
go func() {
|
||||
process.SleepAbout1sTo2s()
|
||||
pool = newPools()
|
||||
engine := control.Register("setutime", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "涩图\n" +
|
||||
"- 来份[涩图/二次元/风景/车万]\n" +
|
||||
"- 添加[涩图/二次元/风景/车万][P站图片ID]\n" +
|
||||
"- 删除[涩图/二次元/风景/车万][P站图片ID]\n" +
|
||||
"- >setu status",
|
||||
})
|
||||
engine.OnRegex(`^来份(.*)$`, rule.FirstValueInList(pool.List)).SetBlock(true).SetPriority(20).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if !limit.Load(ctx.Event.UserID).Acquire() {
|
||||
ctx.SendChain(message.Text("请稍后重试0x0..."))
|
||||
return
|
||||
}
|
||||
}
|
||||
// 从缓冲池里抽一张
|
||||
if id := ctx.SendChain(message.Image(file(pool.pop(imgtype)))); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
})
|
||||
|
||||
engine.OnRegex(`^添加(.*?)(\d+)$`, firstValueInList(pool.List), zero.SuperUserPermission).SetBlock(true).SetPriority(21).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
var (
|
||||
imgtype = ctx.State["regex_matched"].([]string)[1]
|
||||
id, _ = strconv.ParseInt(ctx.State["regex_matched"].([]string)[2], 10, 64)
|
||||
)
|
||||
ctx.SendChain(message.Text("少女祈祷中......"))
|
||||
// 查询P站插图信息
|
||||
illust, err := pixiv.Works(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// 下载插画
|
||||
if err := download(illust, pool.Path); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// 发送到发送者
|
||||
if id := ctx.SendChain(message.Image(file(illust))); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控,发送失败"))
|
||||
return
|
||||
}
|
||||
// 添加插画到对应的数据库table
|
||||
if err := pool.DB.Insert(imgtype, illust); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.Send("添加成功")
|
||||
})
|
||||
|
||||
engine.OnRegex(`^删除(.*?)(\d+)$`, firstValueInList(pool.List), zero.SuperUserPermission).SetBlock(true).SetPriority(22).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
var (
|
||||
imgtype = ctx.State["regex_matched"].([]string)[1]
|
||||
id, _ = strconv.ParseInt(ctx.State["regex_matched"].([]string)[2], 10, 64)
|
||||
)
|
||||
// 查询数据库
|
||||
if err := pool.DB.Del(imgtype, fmt.Sprintf("WHERE pid=%d", id)); err != nil {
|
||||
ctx.Send(fmt.Sprintf("ERROR: %v", err))
|
||||
return
|
||||
}
|
||||
ctx.Send("删除成功")
|
||||
})
|
||||
|
||||
// 查询数据库涩图数量
|
||||
engine.OnFullMatchGroup([]string{">setu status"}).SetBlock(true).SetPriority(23).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
state := []string{"[SetuTime]"}
|
||||
for i := range pool.List {
|
||||
num, err := pool.DB.Count(pool.List[i])
|
||||
if err != nil {
|
||||
num = 0
|
||||
var imgtype = ctx.State["regex_matched"].([]string)[1]
|
||||
// 补充池子
|
||||
go func() {
|
||||
times := math.Min(pool.Max-pool.size(imgtype), 2)
|
||||
for i := 0; i < times; i++ {
|
||||
illust := &pixiv.Illust{}
|
||||
// 查询出一张图片
|
||||
if err := pool.DB.Pick(imgtype, illust); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
continue
|
||||
}
|
||||
// 下载图片
|
||||
if err := download(illust, pool.Path); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
continue
|
||||
}
|
||||
ctx.SendGroupMessage(pool.Group, []message.MessageSegment{message.Image(file(illust))})
|
||||
// 向缓冲池添加一张图片
|
||||
pool.push(imgtype, illust)
|
||||
time.Sleep(time.Second * 1)
|
||||
}
|
||||
}()
|
||||
// 如果没有缓存,阻塞5秒
|
||||
if pool.size(imgtype) == 0 {
|
||||
ctx.SendChain(message.Text("INFO: 正在填充弹药......"))
|
||||
<-time.After(time.Second * 5)
|
||||
if pool.size(imgtype) == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 等待填充,请稍后再试......"))
|
||||
return
|
||||
}
|
||||
}
|
||||
state = append(state, "\n")
|
||||
state = append(state, pool.List[i])
|
||||
state = append(state, ": ")
|
||||
state = append(state, fmt.Sprintf("%d", num))
|
||||
}
|
||||
ctx.Send(strings.Join(state, ""))
|
||||
})
|
||||
}
|
||||
// 从缓冲池里抽一张
|
||||
if id := ctx.SendChain(message.Image(file(pool.pop(imgtype)))); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
})
|
||||
|
||||
// firstValueInList 判断正则匹配的第一个参数是否在列表中
|
||||
func firstValueInList(list []string) zero.Rule {
|
||||
return func(ctx *zero.Ctx) bool {
|
||||
first := ctx.State["regex_matched"].([]string)[1]
|
||||
for i := range list {
|
||||
if first == list[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
engine.OnRegex(`^添加(.*?)(\d+)$`, rule.FirstValueInList(pool.List), zero.SuperUserPermission).SetBlock(true).SetPriority(21).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
var (
|
||||
imgtype = ctx.State["regex_matched"].([]string)[1]
|
||||
id, _ = strconv.ParseInt(ctx.State["regex_matched"].([]string)[2], 10, 64)
|
||||
)
|
||||
ctx.SendChain(message.Text("少女祈祷中......"))
|
||||
// 查询P站插图信息
|
||||
illust, err := pixiv.Works(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// 下载插画
|
||||
if err := download(illust, pool.Path); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// 发送到发送者
|
||||
if id := ctx.SendChain(message.Image(file(illust))); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控,发送失败"))
|
||||
return
|
||||
}
|
||||
// 添加插画到对应的数据库table
|
||||
if err := pool.DB.Insert(imgtype, illust); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("添加成功"))
|
||||
})
|
||||
|
||||
// min 返回两数最小值
|
||||
func min(a, b int) int {
|
||||
switch {
|
||||
default:
|
||||
return a
|
||||
case a > b:
|
||||
return b
|
||||
case a < b:
|
||||
return a
|
||||
}
|
||||
engine.OnRegex(`^删除(.*?)(\d+)$`, rule.FirstValueInList(pool.List), zero.SuperUserPermission).SetBlock(true).SetPriority(22).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
var (
|
||||
imgtype = ctx.State["regex_matched"].([]string)[1]
|
||||
id, _ = strconv.ParseInt(ctx.State["regex_matched"].([]string)[2], 10, 64)
|
||||
)
|
||||
// 查询数据库
|
||||
if err := pool.DB.Del(imgtype, fmt.Sprintf("WHERE pid=%d", id)); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("删除成功"))
|
||||
})
|
||||
|
||||
// 查询数据库涩图数量
|
||||
engine.OnFullMatchGroup([]string{">setu status"}).SetBlock(true).SetPriority(23).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
state := []string{"[SetuTime]"}
|
||||
for i := range pool.List {
|
||||
num, err := pool.DB.Count(pool.List[i])
|
||||
if err != nil {
|
||||
num = 0
|
||||
}
|
||||
state = append(state, "\n")
|
||||
state = append(state, pool.List[i])
|
||||
state = append(state, ": ")
|
||||
state = append(state, fmt.Sprintf("%d", num))
|
||||
}
|
||||
ctx.SendChain(message.Text(state))
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
// size 返回缓冲池指定类型的现有大小
|
||||
@@ -261,8 +217,7 @@ func (p *imgpool) pop(imgtype string) (illust *pixiv.Illust) {
|
||||
|
||||
func file(i *pixiv.Illust) string {
|
||||
filename := fmt.Sprint(i.Pid)
|
||||
pwd, _ := os.Getwd()
|
||||
filepath := pwd + `/` + pool.Path + filename
|
||||
filepath := fileutil.BOTPATH + `/` + pool.Path + filename
|
||||
if _, err := os.Stat(filepath + ".jpg"); err == nil || os.IsExist(err) {
|
||||
return `file:///` + filepath + ".jpg"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* @Author: Kanri
|
||||
* @Date: 2021-10-15 21:23:14
|
||||
* @LastEditors: Kanri
|
||||
* @LastEditTime: 2021-10-15 21:42:51
|
||||
* @Description:
|
||||
*/
|
||||
// Package shindan 基于 https://shindanmaker.com 的测定小功能
|
||||
package shindan
|
||||
|
||||
@@ -52,6 +59,7 @@ func handle(ctx *zero.Ctx) {
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
}
|
||||
// TODO: 可注入
|
||||
ctx.Send(text)
|
||||
}
|
||||
|
||||
|
||||
23
plugin_sleep_manage/data.go
Normal file
23
plugin_sleep_manage/data.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package sleepmanage
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/plugin_sleep_manage/model"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
|
||||
)
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
process.SleepAbout1sTo2s()
|
||||
_ = os.MkdirAll(dbpath, 0755)
|
||||
model.Initialize(dbfile)
|
||||
}()
|
||||
}
|
||||
115
plugin_sleep_manage/model/model.go
Normal file
115
plugin_sleep_manage/model/model.go
Normal file
@@ -0,0 +1,115 @@
|
||||
// Package model 睡眠管理数据库
|
||||
package model
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/logoove/sqlite"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type SleepDB gorm.DB
|
||||
|
||||
func Initialize(dbpath string) *SleepDB {
|
||||
var err error
|
||||
if _, err = os.Stat(dbpath); err != nil || os.IsNotExist(err) {
|
||||
// 生成文件
|
||||
f, err := os.Create(dbpath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer f.Close()
|
||||
}
|
||||
gdb, err := gorm.Open("sqlite3", dbpath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
gdb.AutoMigrate(&SleepManage{})
|
||||
return (*SleepDB)(gdb)
|
||||
}
|
||||
|
||||
func Open(dbpath string) (*SleepDB, error) {
|
||||
db, err := gorm.Open("sqlite3", dbpath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return (*SleepDB)(db), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (sdb *SleepDB) Close() error {
|
||||
db := (*gorm.DB)(sdb)
|
||||
return db.Close()
|
||||
}
|
||||
|
||||
type SleepManage struct {
|
||||
gorm.Model
|
||||
GroupId int64 `gorm:"column:group_id"`
|
||||
UserId int64 `gorm:"column:user_id"`
|
||||
SleepTime time.Time `gorm:"column:sleep_time"`
|
||||
}
|
||||
|
||||
func (SleepManage) TableName() string {
|
||||
return "sleep_manage"
|
||||
}
|
||||
|
||||
// 更新睡眠时间
|
||||
func (sdb *SleepDB) Sleep(groupId, userId int64) (position int, awakeTime time.Duration) {
|
||||
db := (*gorm.DB)(sdb)
|
||||
now := time.Now()
|
||||
var today time.Time
|
||||
if now.Hour() >= 21 {
|
||||
today = now.Add(-time.Hour*time.Duration(-21+now.Hour()) - time.Minute*time.Duration(now.Minute()) - time.Second*time.Duration(now.Second()))
|
||||
} else if now.Hour() <= 3 {
|
||||
today = now.Add(-time.Hour*time.Duration(3+now.Hour()) - time.Minute*time.Duration(now.Minute()) - time.Second*time.Duration(now.Second()))
|
||||
}
|
||||
st := SleepManage{
|
||||
GroupId: groupId,
|
||||
UserId: userId,
|
||||
SleepTime: now,
|
||||
}
|
||||
if err := db.Debug().Model(&SleepManage{}).Where("group_id = ? and user_id = ?", groupId, userId).First(&st).Error; err != nil {
|
||||
// error handling...
|
||||
if gorm.IsRecordNotFoundError(err) {
|
||||
db.Debug().Model(&SleepManage{}).Create(&st) // newUser not user
|
||||
}
|
||||
} else {
|
||||
log.Println("sleeptime为", st)
|
||||
awakeTime = now.Sub(st.SleepTime)
|
||||
db.Debug().Model(&SleepManage{}).Where("group_id = ? and user_id = ?", groupId, userId).Update(
|
||||
map[string]interface{}{
|
||||
"sleep_time": now,
|
||||
})
|
||||
}
|
||||
db.Debug().Model(&SleepManage{}).Where("group_id = ? and sleep_time <= ? and sleep_time >= ?", groupId, now, today).Count(&position)
|
||||
return position, awakeTime
|
||||
}
|
||||
|
||||
// 更新起床时间
|
||||
func (sdb *SleepDB) GetUp(groupId, userId int64) (position int, sleepTime time.Duration) {
|
||||
db := (*gorm.DB)(sdb)
|
||||
now := time.Now()
|
||||
today := now.Add(-time.Hour*time.Duration(-6+now.Hour()) - time.Minute*time.Duration(now.Minute()) - time.Second*time.Duration(now.Second()))
|
||||
st := SleepManage{
|
||||
GroupId: groupId,
|
||||
UserId: userId,
|
||||
SleepTime: now,
|
||||
}
|
||||
if err := db.Debug().Model(&SleepManage{}).Where("group_id = ? and user_id = ?", groupId, userId).First(&st).Error; err != nil {
|
||||
// error handling...
|
||||
if gorm.IsRecordNotFoundError(err) {
|
||||
db.Debug().Model(&SleepManage{}).Create(&st) // newUser not user
|
||||
}
|
||||
} else {
|
||||
log.Println("sleeptime为", st)
|
||||
sleepTime = now.Sub(st.SleepTime)
|
||||
db.Debug().Model(&SleepManage{}).Where("group_id = ? and user_id = ?", groupId, userId).Update(
|
||||
map[string]interface{}{
|
||||
"sleep_time": now,
|
||||
})
|
||||
}
|
||||
db.Debug().Model(&SleepManage{}).Where("group_id = ? and sleep_time <= ? and sleep_time >= ?", groupId, now, today).Count(&position)
|
||||
return position, sleepTime
|
||||
}
|
||||
79
plugin_sleep_manage/sleep_manage.go
Normal file
79
plugin_sleep_manage/sleep_manage.go
Normal file
@@ -0,0 +1,79 @@
|
||||
// Package sleepmanage 睡眠管理
|
||||
package sleepmanage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/plugin_sleep_manage/model"
|
||||
)
|
||||
|
||||
const dbpath = "data/sleep/"
|
||||
const dbfile = dbpath + "manage.db"
|
||||
const prio = 4
|
||||
|
||||
var engine = control.Register("sleepmanage", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "sleepmanage\n- 早安\n- 晚安",
|
||||
})
|
||||
|
||||
func init() {
|
||||
engine.OnFullMatch("早安", isMorning, zero.OnlyGroup).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
db, err := model.Open(dbfile)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return
|
||||
}
|
||||
position, getUpTime := db.GetUp(ctx.Event.GroupID, ctx.Event.UserID)
|
||||
log.Println(position, getUpTime)
|
||||
hour, minute, second := timeDuration(getUpTime)
|
||||
if (hour == 0 && minute == 0 && second == 0) || hour >= 24 {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(fmt.Sprintf("早安成功!你是今天第%d个起床的", position)))
|
||||
} else {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(fmt.Sprintf("早安成功!你的睡眠时长为%d时%d分%d秒,你是今天第%d个起床的", hour, minute, second, position)))
|
||||
}
|
||||
db.Close()
|
||||
})
|
||||
engine.OnFullMatch("晚安", isEvening, zero.OnlyGroup).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
db, err := model.Open(dbfile)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return
|
||||
}
|
||||
position, sleepTime := db.Sleep(ctx.Event.GroupID, ctx.Event.UserID)
|
||||
log.Println(position, sleepTime)
|
||||
hour, minute, second := timeDuration(sleepTime)
|
||||
if (hour == 0 && minute == 0 && second == 0) || hour >= 24 {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(fmt.Sprintf("晚安成功!你是今天第%d个睡觉的", position)))
|
||||
} else {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(fmt.Sprintf("晚安成功!你的清醒时长为%d时%d分%d秒,你是今天第%d个睡觉的", hour, minute, second, position)))
|
||||
}
|
||||
db.Close()
|
||||
})
|
||||
}
|
||||
|
||||
func timeDuration(time time.Duration) (hour, minute, second int64) {
|
||||
hour = int64(time) / (1000 * 1000 * 1000 * 60 * 60)
|
||||
minute = (int64(time) - hour*(1000*1000*1000*60*60)) / (1000 * 1000 * 1000 * 60)
|
||||
second = (int64(time) - hour*(1000*1000*1000*60*60) - minute*(1000*1000*1000*60)) / (1000 * 1000 * 1000)
|
||||
return hour, minute, second
|
||||
}
|
||||
|
||||
// 只统计6点到12点的早安
|
||||
func isMorning(ctx *zero.Ctx) bool {
|
||||
now := time.Now().Hour()
|
||||
return now >= 6 && now <= 12
|
||||
}
|
||||
|
||||
// 只统计21点到凌晨3点的晚安
|
||||
func isEvening(ctx *zero.Ctx) bool {
|
||||
now := time.Now().Hour()
|
||||
return now >= 21 || now <= 3
|
||||
}
|
||||
55
plugin_tracemoe/moe.go
Normal file
55
plugin_tracemoe/moe.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package tracemoe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/picture"
|
||||
trmoe "github.com/fumiama/gotracemoe"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
)
|
||||
|
||||
var (
|
||||
moe = trmoe.NewMoe("")
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
engine := control.Register("tracemoe", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "tracemoe\n- 搜番|搜索番剧[图片]",
|
||||
})
|
||||
// 以图搜图
|
||||
engine.OnKeywordGroup([]string{"搜番", "搜索番剧"}, picture.CmdMatch, picture.MustGiven).SetBlock(true).ThirdPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
// 开始搜索图片
|
||||
ctx.SendChain(message.Text("少女祈祷中......"))
|
||||
for _, pic := range ctx.State["image_url"].([]string) {
|
||||
fmt.Println(pic)
|
||||
if result, err := moe.Search(pic, true, true); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
} else if len(result.Result) > 0 {
|
||||
r := result.Result[0]
|
||||
hint := "我有把握是这个!"
|
||||
if r.Similarity < 0.8 {
|
||||
hint = "大概是这个?"
|
||||
}
|
||||
mf := int(r.From / 60)
|
||||
mt := int(r.To / 60)
|
||||
sf := r.From - float32(mf*60)
|
||||
st := r.To - float32(mt*60)
|
||||
ctx.SendChain(
|
||||
message.Text(hint),
|
||||
message.Image(r.Image),
|
||||
message.Text(
|
||||
"\n",
|
||||
"番剧名:", r.Anilist.Title.Native, "\n",
|
||||
"话数:", r.Episode, "\n",
|
||||
"时间:", mf, ":", sf, "-", mt, ":", st,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
65
plugin_translation/tl.go
Normal file
65
plugin_translation/tl.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// Package translation 翻译
|
||||
package translation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/rate"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
|
||||
)
|
||||
|
||||
var (
|
||||
prio = 100
|
||||
bucket = rate.NewManager(time.Minute, 20) // 接口回复
|
||||
)
|
||||
|
||||
func tl(d string) ([]byte, error) {
|
||||
url := "https://api.cloolc.club/fanyi?data=" + d
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if code := resp.StatusCode; code != 200 {
|
||||
// 如果返回不是200则立刻抛出错误
|
||||
errmsg := fmt.Sprintf("code %d", code)
|
||||
return nil, errors.New(errmsg)
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
|
||||
func init() {
|
||||
control.Register("translation", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "翻译\n" +
|
||||
">TL 你好",
|
||||
}).OnRegex(`^>TL\s(-.{1,10}? )?(.*)$`).SetBlock(true).SetPriority(prio).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if !bucket.Load(ctx.Event.UserID).Acquire() {
|
||||
// 频繁触发,不回复
|
||||
return
|
||||
}
|
||||
msg := []string{ctx.State["regex_matched"].([]string)[2]}
|
||||
rely, err := tl(msg[0])
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
}
|
||||
info := gjson.ParseBytes(rely)
|
||||
repo := info.Get("data.0")
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(message.Text(repo.Get("value.0")))
|
||||
})
|
||||
}
|
||||
41
plugin_vtb_quotation/cron.go
Normal file
41
plugin_vtb_quotation/cron.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package vtbquotation
|
||||
|
||||
import (
|
||||
"github.com/fumiama/cron"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/plugin_vtb_quotation/model"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
|
||||
)
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
process.SleepAbout1sTo2s()
|
||||
log.Println("[vtb/cron] 开启vtb数据库日常更新")
|
||||
vtbDaily()
|
||||
}()
|
||||
}
|
||||
|
||||
func vtbDaily() {
|
||||
c := cron.New()
|
||||
_, err := c.AddFunc("0 4 * * *", func() { vtbData() })
|
||||
if err != nil {
|
||||
log.Errorln("定时任务有错误:", err)
|
||||
} else {
|
||||
log.Println("开启vtb数据库定时任务")
|
||||
c.Start()
|
||||
}
|
||||
}
|
||||
|
||||
func vtbData() {
|
||||
db := model.Initialize(dbfile)
|
||||
if db != nil {
|
||||
for _, v := range db.GetVtbList() {
|
||||
db.StoreVtb(v)
|
||||
}
|
||||
err := db.Close()
|
||||
if err != nil {
|
||||
log.Errorln("[vtb/cron]", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
17
plugin_vtb_quotation/data.go
Normal file
17
plugin_vtb_quotation/data.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package vtbquotation
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
|
||||
)
|
||||
|
||||
// 加载数据库
|
||||
func init() {
|
||||
go func() {
|
||||
process.SleepAbout1sTo2s()
|
||||
_ = os.MkdirAll(dbpath, 0755)
|
||||
_, _ = file.GetLazyData(dbfile, false, true)
|
||||
}()
|
||||
}
|
||||
363
plugin_vtb_quotation/model/model.go
Normal file
363
plugin_vtb_quotation/model/model.go
Normal file
@@ -0,0 +1,363 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/logoove/sqlite"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
type VtbDB gorm.DB
|
||||
|
||||
func Initialize(dbpath string) *VtbDB {
|
||||
var err error
|
||||
if _, err = os.Stat(dbpath); err != nil || os.IsNotExist(err) {
|
||||
// 生成文件
|
||||
f, err := os.Create(dbpath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer f.Close()
|
||||
}
|
||||
gdb, err := gorm.Open("sqlite3", dbpath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
gdb.AutoMigrate(FirstCategory{}).AutoMigrate(SecondCategory{}).AutoMigrate(ThirdCategory{})
|
||||
return (*VtbDB)(gdb)
|
||||
}
|
||||
|
||||
func Open(dbpath string) (*VtbDB, error) {
|
||||
db, err := gorm.Open("sqlite3", dbpath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return (*VtbDB)(db), nil
|
||||
}
|
||||
}
|
||||
|
||||
// FirstCategory 第一品类
|
||||
type FirstCategory struct {
|
||||
gorm.Model
|
||||
FirstCategoryIndex int64 `gorm:"column:first_category_index"`
|
||||
FirstCategoryName string `gorm:"column:first_category_name"`
|
||||
FirstCategoryUid string `gorm:"column:first_category_uid"`
|
||||
FirstCategoryDescription string `gorm:"column:first_category_description;type:varchar(1024)"`
|
||||
FirstCategoryIconPath string `gorm:"column:first_category_icon_path"`
|
||||
}
|
||||
|
||||
func (FirstCategory) TableName() string {
|
||||
return "first_category"
|
||||
}
|
||||
|
||||
// SecondCategory 第二品类
|
||||
type SecondCategory struct {
|
||||
gorm.Model
|
||||
SecondCategoryIndex int64 `gorm:"column:second_category_index"`
|
||||
FirstCategoryUid string `gorm:"column:first_category_uid;association_foreignkey:first_category_uid"`
|
||||
SecondCategoryName string `gorm:"column:second_category_name"`
|
||||
SecondCategoryAuthor string `gorm:"column:second_category_author"`
|
||||
SecondCategoryDescription string `gorm:"column:second_category_description"`
|
||||
}
|
||||
|
||||
func (SecondCategory) TableName() string {
|
||||
return "second_category"
|
||||
}
|
||||
|
||||
// ThirdCategory 第三品类
|
||||
type ThirdCategory struct {
|
||||
gorm.Model
|
||||
ThirdCategoryIndex int64 `gorm:"column:third_category_index"`
|
||||
SecondCategoryIndex int64 `gorm:"column:second_category_index"`
|
||||
FirstCategoryUid string `gorm:"column:first_category_uid"`
|
||||
ThirdCategoryName string `gorm:"column:third_category_name"`
|
||||
ThirdCategoryPath string `gorm:"column:third_category_path"`
|
||||
ThirdCategoryAuthor string `gorm:"column:third_category_author"`
|
||||
ThirdCategoryDescription string `gorm:"column:third_category_description"`
|
||||
}
|
||||
|
||||
func (ThirdCategory) TableName() string {
|
||||
return "third_category"
|
||||
}
|
||||
|
||||
// GetAllFirstCategoryMessage 取出所有vtb
|
||||
func (vdb *VtbDB) GetAllFirstCategoryMessage() string {
|
||||
db := (*gorm.DB)(vdb)
|
||||
firstStepMessage := "请选择一个vtb并发送序号:\n"
|
||||
var fc FirstCategory
|
||||
rows, err := db.Model(&FirstCategory{}).Rows()
|
||||
if err != nil {
|
||||
logrus.Errorln("[vtb/model]数据库读取错误", err)
|
||||
}
|
||||
if rows == nil {
|
||||
return ""
|
||||
}
|
||||
for rows.Next() {
|
||||
db.ScanRows(rows, &fc)
|
||||
// logrus.Println(fc)
|
||||
firstStepMessage = firstStepMessage + strconv.FormatInt(fc.FirstCategoryIndex, 10) + ". " + fc.FirstCategoryName + "\n"
|
||||
}
|
||||
return firstStepMessage
|
||||
}
|
||||
|
||||
// GetAllSecondCategoryMessageByFirstIndex 取得同一个vtb所有语录类别
|
||||
func (vdb *VtbDB) GetAllSecondCategoryMessageByFirstIndex(firstIndex int) string {
|
||||
db := (*gorm.DB)(vdb)
|
||||
SecondStepMessage := "请选择一个语录类别并发送序号:\n"
|
||||
var sc SecondCategory
|
||||
var count int
|
||||
var fc FirstCategory
|
||||
db.Model(FirstCategory{}).Where("first_category_index = ?", firstIndex).First(&fc)
|
||||
db.Model(&SecondCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUid).Count(&count)
|
||||
if count == 0 {
|
||||
return ""
|
||||
}
|
||||
rows, err := db.Model(&SecondCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUid).Rows()
|
||||
if err != nil {
|
||||
logrus.Errorln("[vtb/model]数据库读取错误", err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
db.ScanRows(rows, &sc)
|
||||
// logrus.Println(sc)
|
||||
SecondStepMessage = SecondStepMessage + strconv.FormatInt(sc.SecondCategoryIndex, 10) + ". " + sc.SecondCategoryName + "\n"
|
||||
}
|
||||
return SecondStepMessage
|
||||
}
|
||||
|
||||
// GetAllThirdCategoryMessageByFirstIndexAndSecondIndex 取得同一个vtb同个类别的所有语录
|
||||
func (vdb *VtbDB) GetAllThirdCategoryMessageByFirstIndexAndSecondIndex(firstIndex, secondIndex int) string {
|
||||
db := (*gorm.DB)(vdb)
|
||||
ThirdStepMessage := "请选择一个语录并发送序号:\n"
|
||||
var fc FirstCategory
|
||||
db.Model(FirstCategory{}).Where("first_category_index = ?", firstIndex).First(&fc)
|
||||
var count int
|
||||
db.Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ?", fc.FirstCategoryUid, secondIndex).Count(&count)
|
||||
if count == 0 {
|
||||
return ""
|
||||
}
|
||||
var tc ThirdCategory
|
||||
rows, err := db.Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ?", fc.FirstCategoryUid, secondIndex).Rows()
|
||||
if err != nil {
|
||||
logrus.Errorln("[vtb/model]数据库读取错误", err)
|
||||
}
|
||||
for rows.Next() {
|
||||
db.ScanRows(rows, &tc)
|
||||
// logrus.Println(tc)
|
||||
ThirdStepMessage = ThirdStepMessage + strconv.FormatInt(tc.ThirdCategoryIndex, 10) + ". " + tc.ThirdCategoryName + "\n"
|
||||
}
|
||||
return ThirdStepMessage
|
||||
}
|
||||
|
||||
// GetThirdCategory
|
||||
func (vdb *VtbDB) GetThirdCategory(firstIndex, secondIndex, thirdIndex int) ThirdCategory {
|
||||
db := (*gorm.DB)(vdb)
|
||||
var fc FirstCategory
|
||||
db.Model(FirstCategory{}).Where("first_category_index = ?", firstIndex).First(&fc)
|
||||
var tc ThirdCategory
|
||||
db.Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ? and third_category_index = ?", fc.FirstCategoryUid, secondIndex, thirdIndex).Take(&tc)
|
||||
return tc
|
||||
}
|
||||
|
||||
func (vdb *VtbDB) RandomVtb() ThirdCategory {
|
||||
db := (*gorm.DB)(vdb)
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
var count int
|
||||
db.Model(&ThirdCategory{}).Count(&count)
|
||||
// logrus.Info("一共有", count, "个")
|
||||
var tc ThirdCategory
|
||||
db.Model(&ThirdCategory{}).Offset(rand.Intn(count)).Take(&tc)
|
||||
// logrus.Info(tc)
|
||||
return tc
|
||||
}
|
||||
|
||||
func (vdb *VtbDB) GetFirstCategoryByFirstUid(firstUid string) FirstCategory {
|
||||
db := (*gorm.DB)(vdb)
|
||||
var fc FirstCategory
|
||||
db.Model(FirstCategory{}).Where("first_category_uid = ?", firstUid).Take(&fc)
|
||||
// logrus.Info(fc)
|
||||
return fc
|
||||
}
|
||||
|
||||
func (vdb *VtbDB) Close() error {
|
||||
db := (*gorm.DB)(vdb)
|
||||
return db.Close()
|
||||
}
|
||||
|
||||
const vtbUrl = "https://vtbkeyboard.moe/api/get_vtb_list"
|
||||
|
||||
func (vdb *VtbDB) GetVtbList() (uidList []string) {
|
||||
db := (*gorm.DB)(vdb)
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", vtbUrl, nil)
|
||||
if err != nil {
|
||||
logrus.Errorln(err)
|
||||
return
|
||||
}
|
||||
// 自定义Header
|
||||
req.Header.Set("User-Agent", randua())
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
logrus.Errorln(err)
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
bytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
logrus.Errorln(err)
|
||||
return
|
||||
}
|
||||
|
||||
vtbListStr, err := strconv.Unquote(strings.Replace(strconv.Quote(string(bytes)), `\\u`, `\u`, -1))
|
||||
if err != nil {
|
||||
logrus.Errorln(err)
|
||||
return
|
||||
}
|
||||
|
||||
count := gjson.Get(vtbListStr, "#").Int()
|
||||
for i := int64(0); i < count; i++ {
|
||||
item := gjson.Get(vtbListStr, strconv.FormatInt(i, 10))
|
||||
logrus.Println(item)
|
||||
fc := FirstCategory{
|
||||
FirstCategoryIndex: i,
|
||||
FirstCategoryName: item.Get("name").String(),
|
||||
FirstCategoryDescription: item.Get("description").String(),
|
||||
FirstCategoryIconPath: item.Get("icon_path").String(),
|
||||
FirstCategoryUid: item.Get("uid").String(),
|
||||
}
|
||||
logrus.Println(fc)
|
||||
|
||||
if err := db.Debug().Model(&FirstCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUid).First(&fc).Error; err != nil {
|
||||
if gorm.IsRecordNotFoundError(err) {
|
||||
db.Debug().Model(&FirstCategory{}).Create(&fc) // newUser not user
|
||||
}
|
||||
} else {
|
||||
db.Debug().Model(&FirstCategory{}).Where("first_category_uid = ?", fc.FirstCategoryUid).Update(
|
||||
map[string]interface{}{
|
||||
"first_category_index": i,
|
||||
"first_category_name": item.Get("name").String(),
|
||||
"first_category_description": item.Get("description").String(),
|
||||
"first_category_icon_path": item.Get("icon_path").String(),
|
||||
})
|
||||
}
|
||||
uidList = append(uidList, fc.FirstCategoryUid)
|
||||
}
|
||||
|
||||
return uidList
|
||||
}
|
||||
|
||||
func (vdb *VtbDB) StoreVtb(uid string) {
|
||||
db := (*gorm.DB)(vdb)
|
||||
vtbUrl := "https://vtbkeyboard.moe/api/get_vtb_page?uid=" + uid
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", vtbUrl, nil)
|
||||
if err != nil {
|
||||
logrus.Errorln(err)
|
||||
return
|
||||
}
|
||||
// 自定义Header
|
||||
req.Header.Set("User-Agent", randua())
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
logrus.Errorln(err)
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
bytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
logrus.Errorln(err)
|
||||
return
|
||||
}
|
||||
|
||||
vtbStr, err := strconv.Unquote(strings.Replace(strconv.Quote(string(bytes)), `\\u`, `\u`, -1))
|
||||
if err != nil {
|
||||
logrus.Errorln(err)
|
||||
return
|
||||
}
|
||||
|
||||
secondCount := gjson.Get(vtbStr, "data.voices.#").Int()
|
||||
logrus.Println("二级品类一共有", secondCount)
|
||||
for secondIndex := int64(0); secondIndex < secondCount; secondIndex++ {
|
||||
secondItem := gjson.Get(vtbStr, "data.voices."+strconv.FormatInt(secondIndex, 10))
|
||||
logrus.Println(secondItem)
|
||||
sc := SecondCategory{
|
||||
SecondCategoryName: secondItem.Get("categoryName").String(),
|
||||
SecondCategoryIndex: secondIndex,
|
||||
SecondCategoryAuthor: secondItem.Get("author").String(),
|
||||
SecondCategoryDescription: secondItem.Get("categoryDescription.zh-CN").String(),
|
||||
FirstCategoryUid: uid,
|
||||
}
|
||||
|
||||
if err := db.Debug().Model(&SecondCategory{}).Where("first_category_uid = ? and second_category_index = ?", uid, secondIndex).First(&sc).Error; err != nil {
|
||||
// error handling...
|
||||
if gorm.IsRecordNotFoundError(err) {
|
||||
db.Debug().Model(&SecondCategory{}).Create(&sc) // newUser not user
|
||||
}
|
||||
} else {
|
||||
db.Debug().Model(&SecondCategory{}).Where("first_category_uid = ? and second_category_index = ?", uid, secondIndex).Update(
|
||||
map[string]interface{}{
|
||||
"second_category_name": secondItem.Get("categoryName").String(),
|
||||
"second_category_author": secondItem.Get("author").String(),
|
||||
"second_category_description": secondItem.Get("categoryDescription.zh-CN").String(),
|
||||
})
|
||||
}
|
||||
thirdCount := secondItem.Get("voiceList.#").Int()
|
||||
logrus.Println("三级品类一共有", thirdCount)
|
||||
for thirdIndex := int64(0); thirdIndex < thirdCount; thirdIndex++ {
|
||||
thirdItem := secondItem.Get("voiceList." + strconv.FormatInt(thirdIndex, 10))
|
||||
logrus.Println(thirdItem)
|
||||
tc := ThirdCategory{
|
||||
ThirdCategoryName: thirdItem.Get("name").String(),
|
||||
ThirdCategoryIndex: thirdIndex,
|
||||
ThirdCategoryDescription: thirdItem.Get("description.zh-CN").String(),
|
||||
FirstCategoryUid: uid,
|
||||
SecondCategoryIndex: secondIndex,
|
||||
ThirdCategoryPath: thirdItem.Get("path").String(),
|
||||
ThirdCategoryAuthor: thirdItem.Get("author").String(),
|
||||
}
|
||||
logrus.Println(tc)
|
||||
|
||||
if err := db.Debug().Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ? and third_category_index = ?",
|
||||
uid, secondIndex, thirdIndex).First(&tc).Error; err != nil {
|
||||
if gorm.IsRecordNotFoundError(err) {
|
||||
db.Debug().Model(&ThirdCategory{}).Create(&tc) // newUser not user
|
||||
}
|
||||
} else {
|
||||
db.Debug().Model(&ThirdCategory{}).Where("first_category_uid = ? and second_category_index = ? and third_category_index = ?",
|
||||
uid, secondIndex, thirdIndex).Update(
|
||||
map[string]interface{}{
|
||||
"third_category_name": thirdItem.Get("name").String(),
|
||||
"third_category_description": thirdItem.Get("description.zh-CN").String(),
|
||||
"third_category_path": thirdItem.Get("path").String(),
|
||||
"third_category_author": thirdItem.Get("author").String(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var agent = [...]string{
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0",
|
||||
"Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11",
|
||||
"Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)",
|
||||
"Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)",
|
||||
"User-Agent,Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
|
||||
"User-Agent, Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)",
|
||||
"User-Agent,Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
|
||||
}
|
||||
|
||||
func randua() string {
|
||||
return agent[rand.New(rand.NewSource(time.Now().UnixNano())).Intn(len(agent))]
|
||||
}
|
||||
161
plugin_vtb_quotation/vtb_quotation.go
Normal file
161
plugin_vtb_quotation/vtb_quotation.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package vtbquotation
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "github.com/logoove/sqlite"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/plugin_vtb_quotation/model"
|
||||
)
|
||||
|
||||
const regStr = ".*/(.*)"
|
||||
const dbpath = "data/VtbQuotation/"
|
||||
const dbfile = dbpath + "vtb.db"
|
||||
|
||||
var engine = control.Register("vtbquotation", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "vtbkeyboard.moe\n- vtb语录\n- 随机vtb",
|
||||
})
|
||||
|
||||
func init() {
|
||||
engine.OnFullMatch("vtb语录").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
var firstIndex int
|
||||
var secondIndex int
|
||||
var thirdIndex int
|
||||
echo, cancel := ctx.FutureEvent("message",
|
||||
ctx.CheckSession()). // 只复读开启复读模式的人的消息
|
||||
Repeat() // 不断监听复读
|
||||
db, err := model.Open(dbfile)
|
||||
if err != nil {
|
||||
logrus.Errorln(err)
|
||||
return
|
||||
}
|
||||
defer db.Close()
|
||||
defer cancel()
|
||||
firstStepMessage := db.GetAllFirstCategoryMessage()
|
||||
if id := ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(firstStepMessage)); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
// 步骤0,1,2,依次选择3个类别
|
||||
step := 0
|
||||
// 错误次数
|
||||
errorCount := 0
|
||||
for {
|
||||
select {
|
||||
case e := <-echo: // 接收到需要复读的消息
|
||||
// 错误次数达到3次,结束命令
|
||||
if errorCount >= 3 {
|
||||
ctx.SendChain(message.Reply(e.MessageID), message.Text("输入错误太多,请重新发指令"))
|
||||
return
|
||||
}
|
||||
switch step {
|
||||
case 0:
|
||||
firstIndex, err = strconv.Atoi(e.RawMessage)
|
||||
// log.Println(fmt.Sprintf("当前在第%d步", step))
|
||||
// log.Println(fmt.Sprintf("firstIndex:%d,secondIndex:%d,thirdIndex:%d", firstIndex, secondIndex, thirdIndex))
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Reply(e.MessageID), message.Text("请输入正确的序号,三次输入错误,指令可退出重输"))
|
||||
errorCount++
|
||||
} else {
|
||||
SecondStepMessage := db.GetAllSecondCategoryMessageByFirstIndex(firstIndex)
|
||||
// log.Println(SecondStepMessage)
|
||||
if SecondStepMessage == "" {
|
||||
ctx.SendChain(message.Reply(e.MessageID), message.Text("你选择的序号没有内容,请重新选择,三次输入错误,指令可退出重输"))
|
||||
ctx.SendChain(message.Reply(e.MessageID), message.Text(db.GetAllFirstCategoryMessage()))
|
||||
errorCount++
|
||||
} else {
|
||||
ctx.SendChain(message.Reply(e.MessageID), message.Text(SecondStepMessage))
|
||||
step++
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
secondIndex, err = strconv.Atoi(e.RawMessage)
|
||||
// log.Println(fmt.Sprintf("当前在第%d步", step))
|
||||
// log.Println(fmt.Sprintf("firstIndex:%d,secondIndex:%d,thirdIndex:%d", firstIndex, secondIndex, thirdIndex))
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Reply(e.MessageID), message.Text("请输入正确的序号,三次输入错误,指令可退出重输"))
|
||||
errorCount++
|
||||
} else {
|
||||
ThirdStepMessage := db.GetAllThirdCategoryMessageByFirstIndexAndSecondIndex(firstIndex, secondIndex)
|
||||
// log.Println(ThirdStepMessage)
|
||||
if ThirdStepMessage == "" {
|
||||
ctx.SendChain(message.Reply(e.MessageID), message.Text("你选择的序号没有内容,请重新选择,三次输入错误,指令可退出重输"))
|
||||
ctx.SendChain(message.Reply(e.MessageID), message.Text(db.GetAllSecondCategoryMessageByFirstIndex(firstIndex)))
|
||||
errorCount++
|
||||
} else {
|
||||
ctx.SendChain(message.Reply(e.MessageID), message.Text(ThirdStepMessage))
|
||||
step++
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
thirdIndex, err = strconv.Atoi(e.RawMessage)
|
||||
// log.Println(fmt.Sprintf("当前在第%d步", step))
|
||||
// log.Println(fmt.Sprintf("firstIndex:%d,secondIndex:%d,thirdIndex:%d", firstIndex, secondIndex, thirdIndex))
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Reply(e.MessageID), message.Text("请输入正确的序号,三次输入错误,指令可退出重输"))
|
||||
errorCount++
|
||||
} else {
|
||||
tc := db.GetThirdCategory(firstIndex, secondIndex, thirdIndex)
|
||||
reg := regexp.MustCompile(regStr)
|
||||
recordUrl := tc.ThirdCategoryPath
|
||||
if recordUrl == "" {
|
||||
ctx.SendChain(message.Reply(e.MessageID), message.Text("没有内容请重新选择,三次输入错误,指令可退出重输"))
|
||||
ctx.SendChain(message.Reply(e.MessageID), message.Text(db.GetAllFirstCategoryMessage()))
|
||||
errorCount++
|
||||
step = 1
|
||||
} else {
|
||||
if reg.MatchString(recordUrl) {
|
||||
// log.Println(reg.FindStringSubmatch(recordUrl)[1])
|
||||
// log.Println(url.QueryEscape(reg.FindStringSubmatch(recordUrl)[1]))
|
||||
recordUrl = strings.Replace(recordUrl, reg.FindStringSubmatch(recordUrl)[1], url.QueryEscape(reg.FindStringSubmatch(recordUrl)[1]), -1)
|
||||
recordUrl = strings.Replace(recordUrl, "+", "%20", -1)
|
||||
// log.Println(recordUrl)
|
||||
}
|
||||
ctx.SendChain(message.Reply(e.MessageID), message.Text("请欣赏《"+tc.ThirdCategoryName+"》"))
|
||||
ctx.SendChain(message.Record(recordUrl))
|
||||
return
|
||||
}
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
case <-time.After(time.Second * 60):
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("vtb语录指令过期"))
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
engine.OnFullMatch("随机vtb").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
db, err := model.Open(dbfile)
|
||||
if err != nil {
|
||||
logrus.Errorln(err)
|
||||
return
|
||||
}
|
||||
tc := db.RandomVtb()
|
||||
fc := db.GetFirstCategoryByFirstUid(tc.FirstCategoryUid)
|
||||
if (tc != model.ThirdCategory{}) && (fc != model.FirstCategory{}) {
|
||||
reg := regexp.MustCompile(regStr)
|
||||
recordUrl := tc.ThirdCategoryPath
|
||||
if reg.MatchString(recordUrl) {
|
||||
// log.Println(reg.FindStringSubmatch(recordUrl)[1])
|
||||
// log.Println(url.QueryEscape(reg.FindStringSubmatch(recordUrl)[1]))
|
||||
recordUrl = strings.Replace(recordUrl, reg.FindStringSubmatch(recordUrl)[1], url.QueryEscape(reg.FindStringSubmatch(recordUrl)[1]), -1)
|
||||
recordUrl = strings.Replace(recordUrl, "+", "%20", -1)
|
||||
// log.Println(recordUrl)
|
||||
}
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("请欣赏"+fc.FirstCategoryName+"的《"+tc.ThirdCategoryName+"》"))
|
||||
ctx.SendChain(message.Record(recordUrl))
|
||||
}
|
||||
db.Close()
|
||||
})
|
||||
}
|
||||
72
plugin_wtf/main.go
Normal file
72
plugin_wtf/main.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package wtf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/rate"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
)
|
||||
|
||||
var (
|
||||
// 限制调用频率
|
||||
limit = rate.NewManager(time.Minute*5, 5)
|
||||
)
|
||||
|
||||
func init() {
|
||||
en := control.Register("wtf", &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "鬼东西\n- 鬼东西列表\n- 查询鬼东西[序号][@xxx]",
|
||||
})
|
||||
en.OnFullMatch("鬼东西列表").SetBlock(true).SetPriority(30).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
s := ""
|
||||
for i, w := range table {
|
||||
s += fmt.Sprintf("%02d. %s\n", i, w.name)
|
||||
i++
|
||||
}
|
||||
ctx.SendChain(message.Text(s))
|
||||
})
|
||||
en.OnRegex(`^查询鬼东西(\d*)`).SetBlock(true).SetPriority(30).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if !limit.Load(ctx.Event.UserID).Acquire() {
|
||||
ctx.SendChain(message.Text("请稍后重试0x0..."))
|
||||
return
|
||||
}
|
||||
// 调用接口
|
||||
i, err := strconv.Atoi(ctx.State["regex_matched"].([]string)[1])
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
w := NewWtf(i)
|
||||
if w == nil {
|
||||
ctx.SendChain(message.Text("没有这项内容!"))
|
||||
return
|
||||
}
|
||||
// 获取名字
|
||||
var name string
|
||||
var secondname string
|
||||
if len(ctx.Event.Message) > 1 && ctx.Event.Message[1].Type == "at" {
|
||||
qq, _ := strconv.ParseInt(ctx.Event.Message[1].Data["qq"], 10, 64)
|
||||
secondname = ctx.GetGroupMemberInfo(ctx.Event.GroupID, qq, false).Get("nickname").Str
|
||||
}
|
||||
name = ctx.Event.Sender.NickName
|
||||
var text string
|
||||
if secondname != "" {
|
||||
text, err = w.Predict(name, secondname)
|
||||
} else {
|
||||
text, err = w.Predict(name)
|
||||
}
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// TODO: 可注入
|
||||
ctx.Send(text)
|
||||
})
|
||||
}
|
||||
155
plugin_wtf/model.go
Normal file
155
plugin_wtf/model.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package wtf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
/* JS path getter for https://wtf.hiigara.net/ranking
|
||||
a = document.getElementById("testList").getElementsByTagName("a")
|
||||
s = ""
|
||||
for(i=0; i<a.length; i++) {
|
||||
s += "\"" + a[i].innerText + "\":\"" + a[i].href + "\",\n";
|
||||
}
|
||||
*/
|
||||
|
||||
const apiprefix = "https://wtf.hiigara.net/api/run/"
|
||||
|
||||
type Wtf struct {
|
||||
name string
|
||||
path string
|
||||
}
|
||||
|
||||
var table = [...]*Wtf{
|
||||
{"你的意义是什么?", "mRIFuS"},
|
||||
{"【ABO】性別和信息素", "KXyy9"},
|
||||
{"测测cp", "ZoGXQd"},
|
||||
{"xxx和xxx的關係是?", "L4HfA"},
|
||||
{"在JOJO世界,你的替身会是什么?", "lj0a8o"},
|
||||
{"稱號產生器", "titlegen"},
|
||||
{"成分报告", "2PCeo1"},
|
||||
{"測驗你跟你的朋友是攻/受", "LkQXO3"},
|
||||
{"测试两人的关系?", "uwjQQt"},
|
||||
{"【Fate系列】當你成為了從者 2.0", "LHStH2"},
|
||||
{"想不到自己未來要做什麼工作嗎?", "D1agGa"},
|
||||
{"(σ゚∀゚)σ名字產生器", "LNxXq7"},
|
||||
{"人設生產器", "LBtPu5"},
|
||||
{"測驗你在ABO世界的訊息素", "SwmdU"},
|
||||
{"爱是什么", "llpBEY"},
|
||||
{"測測你和哪位名人相似?", "RHQeXu"},
|
||||
{"S/M测试", "Ga47oZ"},
|
||||
{"测测你是谁", "aV1AEi"},
|
||||
{"取個綽號吧", "LTkyUy"},
|
||||
{"什麼都不是", "vyrSCb"},
|
||||
{"今天中午吃什麼", "LdS4K6"},
|
||||
{"測試你的中二稱號", "LwUmQ6"},
|
||||
{"神奇海螺", "Lon1h7"},
|
||||
{"ABO測試", "H1Tgd"},
|
||||
{"女主角姓名產生器", "MsQBTd"},
|
||||
{"您是什么人", "49PwSd"},
|
||||
{"如果你成为了干员", "ok5e7n"},
|
||||
{"abo人设生成~", "Di8enA"},
|
||||
{"✡你的命運✡塔羅占卜🔮", "ohCzID"},
|
||||
{"小說大綱生產器", "Lnstjz"},
|
||||
{"他会喜欢你吗?", "pezX3a"},
|
||||
{"抽签!你明年的今天会干什么", "IF31kS"},
|
||||
{"如果你是受,會是哪種受呢?", "Dr6zpF"},
|
||||
{"cp文梗", "vEO2KD"},
|
||||
{"您是什么人?", "TQ5qyl"},
|
||||
{"你成為......的機率", "g0uoBL"},
|
||||
{"ABO性別與信息素", "KFPju"},
|
||||
{"異國名稱產生器(國家、人名、星球...)", "OBpu4"},
|
||||
{"對方到底喜不喜歡你", "JSLoZC"},
|
||||
{"【脑叶公司】测一测你在脑叶公司的经历", "uPBhjC"},
|
||||
{"当你成为魔法少女", "7ZiGcJ"},
|
||||
{"你是yyds吗?", "SpBnCa"},
|
||||
{"○○喜歡你嗎?", "S6Uceo"},
|
||||
{"测测你的sm属性", "dOtcO5"},
|
||||
{"你/妳究竟是攻還是受呢?", "RXALH"},
|
||||
{"神秘藏书阁", "tDRyET"},
|
||||
{"中午吃什么?", "L0Wsis"},
|
||||
{"十年后,你cp的结局是", "VUwnXQ"},
|
||||
{"高维宇宙与常数的你", "6Zql97"},
|
||||
{"色色的東東", "o2eg74"},
|
||||
{"文章標題產生器", "Ky25WO"},
|
||||
{"你的成績怎麼樣", "6kZv69"},
|
||||
{"智能SM偵測器ヾ(*ΦωΦ)ツ", "9pY6HQ"},
|
||||
{"你的使用注意事項", "La4Gir"},
|
||||
{"戀愛指數", "Jsgz0"},
|
||||
{"测试你今晚拉的屎", "N8dbcL"},
|
||||
{"成為情侶的機率ᶫᵒᵛᵉᵧₒᵤ♥", "eDURch"},
|
||||
{"他對你...", "CJxHMf"},
|
||||
{"你的明日方舟人际关系", "u5z4Mw"},
|
||||
{"日本姓氏產生器", "JJ5Ctb"},
|
||||
{"當你轉生到了異世界,你將成為...", "FTpwK"},
|
||||
{"魔幻世界大穿越2.0", "wUATOq"},
|
||||
{"未來男朋友", "F3dSV"},
|
||||
{"ABO與信息素", "KFOGA"},
|
||||
{"你必將就這樣一事無成啊アホ", "RWw9oX"},
|
||||
{"用習慣舉手的方式測試你的戀愛運!<3", "wv5bzA"},
|
||||
{"攻受", "RaKmY"},
|
||||
{"你和你喜歡的人的微h寵溺段子XD", "LdQqGz"},
|
||||
{"我的藝名", "LBaTx"},
|
||||
{"你是什麼神?", "LqZORE"},
|
||||
{"你的起源是什麼?", "HXWwC"},
|
||||
{"測你喜歡什麼", "Sue5g2"},
|
||||
{"看看朋友的秘密", "PgKb8r"},
|
||||
{"你在動漫裡的名字", "Lz82V7"},
|
||||
{"小說男角名字產生器", "LyGDRr"},
|
||||
{"測試短文", "S48yA"},
|
||||
{"我們兩人在一起的機率......", "LBZbgE"},
|
||||
{"創造小故事", "Kjy3AS"},
|
||||
{"你的另外一個名字", "LuyYQA"},
|
||||
{"與你最匹配的攻君屬性 !?", "I7pxy"},
|
||||
{"英文全名生產器(女)", "HcYbq"},
|
||||
{"BL文章生產器", "LBZMO"},
|
||||
{"輕小說書名產生器", "NFucA"},
|
||||
{"長相評分", "2cQSDP"},
|
||||
{"日本名字產生器(女孩子)", "JRiKv"},
|
||||
{"中二技能名產生器", "Ky1BA"},
|
||||
{"抽籤", "XqxfuH"},
|
||||
{"你的蘿莉控程度全國排名", "IIWh9k"},
|
||||
}
|
||||
|
||||
func NewWtf(index int) *Wtf {
|
||||
if index >= 0 && index < len(table) {
|
||||
return table[index]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type result struct {
|
||||
Text string `json:"text"`
|
||||
// Path string `json:"path"`
|
||||
Ok bool `json:"ok"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
func (w *Wtf) Predict(names ...string) (string, error) {
|
||||
name := ""
|
||||
for _, n := range names {
|
||||
name += "/" + url.QueryEscape(n)
|
||||
}
|
||||
u := apiprefix + w.path + name
|
||||
resp, err := http.Get(u)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
r, err := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
re := new(result)
|
||||
err = json.Unmarshal(r, re)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if re.Ok {
|
||||
return "> " + w.name + "\n" + re.Text, nil
|
||||
}
|
||||
return "", errors.New(re.Msg)
|
||||
}
|
||||
29
utils/binary/encode.go
Normal file
29
utils/binary/encode.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// GBK2UTF8 GBK 转 UTF-8
|
||||
func GBK2UTF8(s []byte) ([]byte, error) {
|
||||
reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder())
|
||||
d, e := ioutil.ReadAll(reader)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// UTF82GBK UTF-8 转 GBK
|
||||
func UTF82GBK(s []byte) ([]byte, error) {
|
||||
reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewEncoder())
|
||||
d, e := ioutil.ReadAll(reader)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
12
utils/file/dir_nowin.go
Normal file
12
utils/file/dir_nowin.go
Normal file
@@ -0,0 +1,12 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package file
|
||||
|
||||
import "os"
|
||||
|
||||
// Pwd 获取当前路径
|
||||
func Pwd() (path string) {
|
||||
path, _ = os.Getwd()
|
||||
return
|
||||
}
|
||||
15
utils/file/dir_win.go
Normal file
15
utils/file/dir_win.go
Normal file
@@ -0,0 +1,15 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Pwd 获取当前路径的正斜杠表示
|
||||
func Pwd() string {
|
||||
path, _ := os.Getwd()
|
||||
return strings.ReplaceAll(path, "\\", "/")
|
||||
}
|
||||
37
utils/file/dl.go
Normal file
37
utils/file/dl.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Package file 文件实用工具
|
||||
package file
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
tr = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
nochkcrtcli = &http.Client{Transport: tr}
|
||||
)
|
||||
|
||||
// DownloadTo 下载到路径
|
||||
func DownloadTo(url, file string, chkcrt bool) error {
|
||||
var resp *http.Response
|
||||
var err error
|
||||
if chkcrt {
|
||||
resp, err = http.Get(url)
|
||||
} else {
|
||||
resp, err = nochkcrtcli.Get(url)
|
||||
}
|
||||
if err == nil {
|
||||
var f *os.File
|
||||
f, err = os.Create(file)
|
||||
if err == nil {
|
||||
_, err = io.Copy(f, resp.Body)
|
||||
resp.Body.Close()
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
18
utils/file/f.go
Normal file
18
utils/file/f.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package file
|
||||
|
||||
import "os"
|
||||
|
||||
// BOTPATH BOT当前路径
|
||||
var BOTPATH = Pwd()
|
||||
|
||||
// IsExist 文件/路径存在
|
||||
func IsExist(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil || os.IsExist(err)
|
||||
}
|
||||
|
||||
// IsNotExist 文件/路径不存在
|
||||
func IsNotExist(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err != nil && os.IsNotExist(err)
|
||||
}
|
||||
105
utils/file/updater.go
Normal file
105
utils/file/updater.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
reg "github.com/fumiama/go-registry"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
dataurl = "https://codechina.csdn.net/u011570312/ZeroBot-Plugin/-/raw/master/"
|
||||
)
|
||||
|
||||
var (
|
||||
registry = reg.NewRegReader("reilia.eastasia.azurecontainer.io:32664", "fumiama")
|
||||
lzmu sync.Mutex
|
||||
)
|
||||
|
||||
func GetLazyData(path string, isReturnDataBytes, isDataMustEqual bool) ([]byte, error) {
|
||||
var data []byte
|
||||
var resp *http.Response
|
||||
var filemd5 *[16]byte
|
||||
var ms string
|
||||
|
||||
u := dataurl + path
|
||||
lzmu.Lock()
|
||||
logrus.Infoln("[file]检查懒加载文件:", path)
|
||||
err := registry.Connect()
|
||||
if err != nil {
|
||||
logrus.Errorln("[file]无法连接到md5验证服务器,请自行确保下载文件的正确性:", err)
|
||||
} else {
|
||||
ms, err = registry.Get(path)
|
||||
if err != nil || len(ms) != 16 {
|
||||
logrus.Errorln("[file]获取md5失败,请自行确保下载文件 %s 的正确性:", path, err)
|
||||
} else {
|
||||
filemd5 = (*[16]byte)(*(*unsafe.Pointer)(unsafe.Pointer(&ms)))
|
||||
logrus.Infoln("[file]从验证服务器获得文件md5:", hex.EncodeToString(filemd5[:]))
|
||||
}
|
||||
}
|
||||
_ = registry.Close()
|
||||
lzmu.Unlock()
|
||||
|
||||
if IsExist(path) {
|
||||
data, err = os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if filemd5 != nil {
|
||||
if md5.Sum(data) == *filemd5 {
|
||||
logrus.Infoln("[file]文件md5匹配,文件已存在且为最新")
|
||||
goto ret
|
||||
} else if !isDataMustEqual {
|
||||
logrus.Warnln("[file]文件", path, "md5不匹配,但不主动更新")
|
||||
goto ret
|
||||
}
|
||||
logrus.Infoln("[file]文件md5不匹配,开始更新文件")
|
||||
} else {
|
||||
logrus.Warnln("[file]文件", path, "存在,已跳过md5检查")
|
||||
goto ret
|
||||
}
|
||||
}
|
||||
|
||||
// 下载
|
||||
resp, err = http.Get(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.ContentLength <= 0 {
|
||||
return nil, errors.New("resp body len <= 0")
|
||||
}
|
||||
logrus.Printf("[file]从镜像下载数据%d字节...", resp.ContentLength)
|
||||
// 读取数据
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil, errors.New("read body len <= 0")
|
||||
}
|
||||
if filemd5 != nil {
|
||||
if md5.Sum(data) == *filemd5 {
|
||||
logrus.Infoln("[file]文件下载完成,md5匹配,开始保存")
|
||||
} else {
|
||||
logrus.Errorln("[file]文件", path, "md5不匹配,下载失败")
|
||||
return nil, errors.New("file md5 mismatch")
|
||||
}
|
||||
} else {
|
||||
logrus.Warnln("[file]文件", path, "下载完成,已跳过md5检查,开始保存")
|
||||
}
|
||||
// 写入数据
|
||||
err = os.WriteFile(path, data, 0644)
|
||||
ret:
|
||||
if isReturnDataBytes {
|
||||
return data, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
26
utils/math/math.go
Normal file
26
utils/math/math.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// Package math 计算实用工具
|
||||
package math
|
||||
|
||||
// Max 返回两数最大值,该函数将被内联
|
||||
func Max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Min 返回两数最小值,该函数将被内联
|
||||
func Min(a, b int) int {
|
||||
if a > b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Abs 返回绝对值,该函数将被内联
|
||||
func Abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
12
utils/process/sleep.go
Normal file
12
utils/process/sleep.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Package process 流程控制相关
|
||||
package process
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SleepAbout1sTo2s 随机阻塞等待 1 ~ 2s
|
||||
func SleepAbout1sTo2s() {
|
||||
time.Sleep(time.Second + time.Millisecond*time.Duration(rand.Intn(1000)))
|
||||
}
|
||||
17
utils/rule/extension.go
Normal file
17
utils/rule/extension.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Package rule zb 规则扩展
|
||||
package rule
|
||||
|
||||
import zero "github.com/wdvxdr1123/ZeroBot"
|
||||
|
||||
// FirstValueInList 判断正则匹配的第一个参数是否在列表中
|
||||
func FirstValueInList(list []string) zero.Rule {
|
||||
return func(ctx *zero.Ctx) bool {
|
||||
first := ctx.State["regex_matched"].([]string)[1]
|
||||
for _, v := range list {
|
||||
if first == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
// Package data 数据库/数据处理相关工具
|
||||
package data
|
||||
// Package sql 数据库/数据处理相关工具
|
||||
package sql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
_ "modernc.org/sqlite" // 引入sqlite
|
||||
_ "github.com/logoove/sqlite" // 引入sqlite
|
||||
)
|
||||
|
||||
// Sqlite 数据库对象
|
||||
@@ -15,12 +16,33 @@ type Sqlite struct {
|
||||
DBPath string
|
||||
}
|
||||
|
||||
// Open 打开数据库
|
||||
func (db *Sqlite) Open() (err error) {
|
||||
if db.DB == nil {
|
||||
database, err := sql.Open("sqlite3", db.DBPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
db.DB = database
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Close 关闭数据库
|
||||
func (db *Sqlite) Close() (err error) {
|
||||
if db.DB != nil {
|
||||
err = db.DB.Close()
|
||||
db.DB = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create 生成数据库
|
||||
// 默认结构体的第一个元素为主键
|
||||
// 返回错误
|
||||
func (db *Sqlite) Create(table string, objptr interface{}) (err error) {
|
||||
if db.DB == nil {
|
||||
database, err := sql.Open("sqlite", db.DBPath)
|
||||
database, err := sql.Open("sqlite3", db.DBPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -48,17 +70,16 @@ func (db *Sqlite) Create(table string, objptr interface{}) (err error) {
|
||||
cmd = append(cmd, "NULL);")
|
||||
}
|
||||
}
|
||||
if _, err := db.DB.Exec(strings.Join(cmd, " ")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
_, err = db.DB.Exec(strings.Join(cmd, " ") + ";")
|
||||
return
|
||||
}
|
||||
|
||||
// Insert 插入数据集
|
||||
// 如果 PK 存在会覆盖
|
||||
// 默认结构体的第一个元素为主键
|
||||
// 返回错误
|
||||
func (db *Sqlite) Insert(table string, objptr interface{}) (err error) {
|
||||
rows, err := db.DB.Query("SELECT * FROM " + table)
|
||||
func (db *Sqlite) Insert(table string, objptr interface{}) error {
|
||||
rows, err := db.DB.Query("SELECT * FROM " + table + " limit 1;")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -68,9 +89,9 @@ func (db *Sqlite) Insert(table string, objptr interface{}) (err error) {
|
||||
tags, _ := rows.Columns()
|
||||
rows.Close()
|
||||
var (
|
||||
values = values(objptr)
|
||||
top = len(tags) - 1
|
||||
cmd = []string{}
|
||||
vals = values(objptr)
|
||||
top = len(tags) - 1
|
||||
cmd = []string{}
|
||||
)
|
||||
cmd = append(cmd, "REPLACE INTO")
|
||||
cmd = append(cmd, table)
|
||||
@@ -102,27 +123,87 @@ func (db *Sqlite) Insert(table string, objptr interface{}) (err error) {
|
||||
cmd = append(cmd, ")")
|
||||
}
|
||||
}
|
||||
stmt, err := db.DB.Prepare(strings.Join(cmd, " "))
|
||||
stmt, err := db.DB.Prepare(strings.Join(cmd, " ") + ";")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = stmt.Exec(values...)
|
||||
_, err = stmt.Exec(vals...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return stmt.Close()
|
||||
}
|
||||
|
||||
// Find 查询数据库
|
||||
// InsertUnique 插入数据集
|
||||
// 如果 PK 存在会报错
|
||||
// 默认结构体的第一个元素为主键
|
||||
// 返回错误
|
||||
func (db *Sqlite) InsertUnique(table string, objptr interface{}) error {
|
||||
rows, err := db.DB.Query("SELECT * FROM " + table + " limit 1;")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return rows.Err()
|
||||
}
|
||||
tags, _ := rows.Columns()
|
||||
rows.Close()
|
||||
var (
|
||||
vals = values(objptr)
|
||||
top = len(tags) - 1
|
||||
cmd = []string{}
|
||||
)
|
||||
cmd = append(cmd, "INSERT INTO")
|
||||
cmd = append(cmd, table)
|
||||
for i := range tags {
|
||||
switch i {
|
||||
default:
|
||||
cmd = append(cmd, tags[i])
|
||||
cmd = append(cmd, ",")
|
||||
case 0:
|
||||
cmd = append(cmd, "(")
|
||||
cmd = append(cmd, tags[i])
|
||||
cmd = append(cmd, ",")
|
||||
case top:
|
||||
cmd = append(cmd, tags[i])
|
||||
cmd = append(cmd, ")")
|
||||
}
|
||||
}
|
||||
for i := range tags {
|
||||
switch i {
|
||||
default:
|
||||
cmd = append(cmd, "?")
|
||||
cmd = append(cmd, ",")
|
||||
case 0:
|
||||
cmd = append(cmd, "VALUES (")
|
||||
cmd = append(cmd, "?")
|
||||
cmd = append(cmd, ",")
|
||||
case top:
|
||||
cmd = append(cmd, "?")
|
||||
cmd = append(cmd, ")")
|
||||
}
|
||||
}
|
||||
stmt, err := db.DB.Prepare(strings.Join(cmd, " ") + ";")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = stmt.Exec(vals...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return stmt.Close()
|
||||
}
|
||||
|
||||
// Find 查询数据库,写入最后一条结果到 objptr
|
||||
// condition 可为"WHERE id = 0"
|
||||
// 默认字段与结构体元素顺序一致
|
||||
// 返回错误
|
||||
func (db *Sqlite) Find(table string, objptr interface{}, condition string) (err error) {
|
||||
func (db *Sqlite) Find(table string, objptr interface{}, condition string) error {
|
||||
var cmd = []string{}
|
||||
cmd = append(cmd, "SELECT * FROM ")
|
||||
cmd = append(cmd, table)
|
||||
cmd = append(cmd, condition)
|
||||
rows, err := db.DB.Query(strings.Join(cmd, " "))
|
||||
rows, err := db.DB.Query(strings.Join(cmd, " ") + ";")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -131,16 +212,84 @@ func (db *Sqlite) Find(table string, objptr interface{}, condition string) (err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
return errors.New("sql.Find: null result")
|
||||
}
|
||||
err = rows.Scan(addrs(objptr)...)
|
||||
for rows.Next() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rows.Scan(addrs(objptr)...)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// CanFind 查询数据库是否有 condition
|
||||
// condition 可为"WHERE id = 0"
|
||||
// 默认字段与结构体元素顺序一致
|
||||
// 返回错误
|
||||
func (db *Sqlite) CanFind(table string, condition string) bool {
|
||||
var cmd = []string{}
|
||||
cmd = append(cmd, "SELECT * FROM")
|
||||
cmd = append(cmd, table)
|
||||
cmd = append(cmd, condition)
|
||||
rows, err := db.DB.Query(strings.Join(cmd, " ") + ";")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return false
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
return false
|
||||
}
|
||||
_ = rows.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
// FindFor 查询数据库,用函数 f 遍历结果
|
||||
// condition 可为"WHERE id = 0"
|
||||
// 默认字段与结构体元素顺序一致
|
||||
// 返回错误
|
||||
func (db *Sqlite) FindFor(table string, objptr interface{}, condition string, f func() error) error {
|
||||
var cmd = []string{}
|
||||
cmd = append(cmd, "SELECT * FROM")
|
||||
cmd = append(cmd, table)
|
||||
cmd = append(cmd, condition)
|
||||
rows, err := db.DB.Query(strings.Join(cmd, " ") + ";")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return rows.Err()
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
return errors.New("sql.FindFor: null result")
|
||||
}
|
||||
err = rows.Scan(addrs(objptr)...)
|
||||
if err == nil {
|
||||
err = f()
|
||||
}
|
||||
for rows.Next() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rows.Scan(addrs(objptr)...)
|
||||
if err == nil {
|
||||
err = f()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// Pick 从 table 随机一行
|
||||
func (db *Sqlite) Pick(table string, objptr interface{}) error {
|
||||
return db.Find(table, objptr, "ORDER BY RANDOM() limit 1")
|
||||
}
|
||||
|
||||
// ListTables 列出所有表名
|
||||
@@ -168,15 +317,15 @@ func (db *Sqlite) ListTables() (s []string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Del 删除数据库
|
||||
// Del 删除数据库表项
|
||||
// condition 可为"WHERE id = 0"
|
||||
// 返回错误
|
||||
func (db *Sqlite) Del(table string, condition string) (err error) {
|
||||
func (db *Sqlite) Del(table string, condition string) error {
|
||||
var cmd = []string{}
|
||||
cmd = append(cmd, "DELETE FROM")
|
||||
cmd = append(cmd, table)
|
||||
cmd = append(cmd, condition)
|
||||
stmt, err := db.DB.Prepare(strings.Join(cmd, " "))
|
||||
stmt, err := db.DB.Prepare(strings.Join(cmd, " ") + ";")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -184,27 +333,43 @@ func (db *Sqlite) Del(table string, condition string) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return stmt.Close()
|
||||
}
|
||||
|
||||
// Truncate 清空数据库表
|
||||
func (db *Sqlite) Truncate(table string) error {
|
||||
var cmd = []string{}
|
||||
cmd = append(cmd, "TRUNCATE TABLE")
|
||||
cmd = append(cmd, table)
|
||||
stmt, err := db.DB.Prepare(strings.Join(cmd, " ") + ";")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = stmt.Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return stmt.Close()
|
||||
}
|
||||
|
||||
// Count 查询数据库行数
|
||||
// 返回行数以及错误
|
||||
func (db *Sqlite) Count(table string) (num int, err error) {
|
||||
var cmd = []string{}
|
||||
cmd = append(cmd, "SELECT * FROM")
|
||||
cmd = append(cmd, "SELECT COUNT(1) FROM")
|
||||
cmd = append(cmd, table)
|
||||
rows, err := db.DB.Query(strings.Join(cmd, " "))
|
||||
rows, err := db.DB.Query(strings.Join(cmd, " ") + ";")
|
||||
if err != nil {
|
||||
return num, err
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return num, rows.Err()
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
num++
|
||||
if rows.Next() {
|
||||
err = rows.Scan(&num)
|
||||
}
|
||||
return num, nil
|
||||
rows.Close()
|
||||
return num, err
|
||||
}
|
||||
|
||||
// tags 反射 返回结构体对象的 tag 数组
|
||||
@@ -231,8 +396,22 @@ func kinds(objptr interface{}) []string {
|
||||
}
|
||||
for i, flen := 0, elem.Type().NumField(); i < flen; i++ {
|
||||
switch elem.Field(i).Type().String() {
|
||||
case "int64":
|
||||
case "int8":
|
||||
kinds = append(kinds, "TINYINT")
|
||||
case "uint8", "byte":
|
||||
kinds = append(kinds, "UNSIGNED TINYINT")
|
||||
case "int16":
|
||||
kinds = append(kinds, "SMALLINT")
|
||||
case "uint16":
|
||||
kinds = append(kinds, "UNSIGNED SMALLINT")
|
||||
case "int32":
|
||||
kinds = append(kinds, "INT")
|
||||
case "uint32":
|
||||
kinds = append(kinds, "UNSIGNED INT")
|
||||
case "int64":
|
||||
kinds = append(kinds, "BIGINT")
|
||||
case "uint64":
|
||||
kinds = append(kinds, "UNSIGNED BIGINT")
|
||||
case "string":
|
||||
kinds = append(kinds, "TEXT")
|
||||
default:
|
||||
@@ -251,14 +430,7 @@ func values(objptr interface{}) []interface{} {
|
||||
elem = elem.Field(0)
|
||||
}
|
||||
for i, flen := 0, elem.Type().NumField(); i < flen; i++ {
|
||||
switch elem.Field(i).Type().String() {
|
||||
case "int64":
|
||||
values = append(values, elem.Field(i).Int())
|
||||
case "string":
|
||||
values = append(values, elem.Field(i).String())
|
||||
default:
|
||||
values = append(values, elem.Field(i).String())
|
||||
}
|
||||
values = append(values, elem.Field(i).Interface())
|
||||
}
|
||||
return values
|
||||
}
|
||||
44
utils/web/http.go
Normal file
44
utils/web/http.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// Package web 网络处理相关
|
||||
package web
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ReqWith 使用自定义请求头获取数据
|
||||
func ReqWith(url string, method string, referer string, ua string) (data []byte, err error) {
|
||||
client := &http.Client{}
|
||||
// 提交请求
|
||||
var reqest *http.Request
|
||||
reqest, err = http.NewRequest(method, url, nil)
|
||||
if err == nil {
|
||||
// 增加header选项
|
||||
reqest.Header.Add("Referer", referer)
|
||||
reqest.Header.Add("User-Agent", ua)
|
||||
var response *http.Response
|
||||
response, err = client.Do(reqest)
|
||||
if err == nil {
|
||||
data, err = io.ReadAll(response.Body)
|
||||
response.Body.Close()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetData 获取数据
|
||||
func GetData(url string) (data []byte, err error) {
|
||||
var response *http.Response
|
||||
response, err = http.Get(url)
|
||||
if err == nil {
|
||||
if response.ContentLength <= 0 {
|
||||
err = errors.New("web.GetData: empty body")
|
||||
response.Body.Close()
|
||||
return
|
||||
}
|
||||
data, err = io.ReadAll(response.Body)
|
||||
response.Body.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user