mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2026-02-07 15:40:26 +00:00
Compare commits
10 Commits
v1.2.2-bet
...
v1.2.2-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45448e6f53 | ||
|
|
08ee458a27 | ||
|
|
3f97541956 | ||
|
|
e2c603338e | ||
|
|
a363623df9 | ||
|
|
b871573dc3 | ||
|
|
ad33c1ca3c | ||
|
|
810cb284b6 | ||
|
|
d0ffb25594 | ||
|
|
2b6796d1d1 |
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/
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: compile
|
||||
name: 发行版
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
|
||||
@@ -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"
|
||||
15
README.md
15
README.md
@@ -91,11 +91,12 @@ zerobot -h -t token -u url [-d|w] [-g 监听地址:端口] qq1 qq2 qq3 ...
|
||||
- [x] 列出所有提醒
|
||||
- [x] 翻牌
|
||||
- [x] [开启|关闭]入群验证
|
||||
- [ ] 同意入群请求
|
||||
- [x] [开启|关闭]gist加群自动审批
|
||||
- [ ] 同意好友请求
|
||||
- [ ] 撤回[@xxx] [xxx]
|
||||
- [ ] 警告[@xxx]
|
||||
- [x] run[xxx]
|
||||
- 注:使用gist加群自动审批,请在群介绍添加以下说明,同时开启`需要回答问题并由管理员审核`:加群请在github新建一个gist,其文件名为本群群号的字符串的md5,内容为一行,是当前unix时间戳。然后请将您的用户名和gist哈希按照username/gisthash的格式填写到回答即可。
|
||||
- **GitHub仓库搜索** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_github"`
|
||||
- [x] >github [xxx]
|
||||
- [x] >github -p [xxx]
|
||||
@@ -212,6 +213,8 @@ zerobot -h -t token -u url [-d|w] [-g 监听地址:端口] qq1 qq2 qq3 ...
|
||||
- **书评** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_book_review"`
|
||||
- [x] 书评[关键字]
|
||||
- [x] 随机书评
|
||||
- **coser** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_coser" `
|
||||
- [x] coser
|
||||
- **TODO...**
|
||||
|
||||
## 使用方法
|
||||
@@ -259,15 +262,15 @@ go mod tidy
|
||||
|
||||
```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
|
||||
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
|
||||
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
|
||||
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
|
||||
GOOS=linux GOARCH=mips GOMIPS=softfloat CGO_ENABLED=0 go build -ldflags "-s -w" -o zerobot -trimpath
|
||||
```
|
||||
|
||||
5. 运行 OneBot 框架,并同时运行本插件
|
||||
|
||||
@@ -310,13 +310,13 @@ func init() {
|
||||
ctx.SendChain(message.Text(msg))
|
||||
})
|
||||
|
||||
zero.OnCommandGroup([]string{"服务详情", "service_detail"}, userOrGrpAdmin).
|
||||
zero.OnCommandGroup([]string{"服务详情", "service_detail"}, userOrGrpAdmin, zero.OnlyGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
var m message.Message
|
||||
m = append(m,
|
||||
message.CustomNode(
|
||||
ctx.Event.Sender.NickName,
|
||||
ctx.Event.UserID,
|
||||
zero.BotConfig.NickName[0],
|
||||
ctx.Event.SelfID,
|
||||
"---服务详情---",
|
||||
))
|
||||
i := 0
|
||||
@@ -333,17 +333,19 @@ func init() {
|
||||
msg += "\n" + help
|
||||
m = append(m,
|
||||
message.CustomNode(
|
||||
ctx.Event.Sender.NickName,
|
||||
ctx.Event.UserID,
|
||||
zero.BotConfig.NickName[0],
|
||||
ctx.Event.SelfID,
|
||||
msg,
|
||||
))
|
||||
return true
|
||||
})
|
||||
|
||||
ctx.SendGroupForwardMessage(
|
||||
if id := ctx.SendGroupForwardMessage(
|
||||
ctx.Event.GroupID,
|
||||
m,
|
||||
)
|
||||
).Get("message_id").Int(); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
1
go.mod
1
go.mod
@@ -6,7 +6,6 @@ require (
|
||||
github.com/FloatTech/AnimeAPI v1.1.10
|
||||
github.com/FloatTech/ZeroBot-Plugin-Gif v0.2.4
|
||||
github.com/FloatTech/bot-manager v1.0.1-0.20211112011524-85b9895271ed
|
||||
github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737
|
||||
github.com/corona10/goimagehash v1.0.3
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/fumiama/cron v1.3.0
|
||||
|
||||
2
go.sum
2
go.sum
@@ -12,8 +12,6 @@ github.com/FloatTech/imgfactory v0.1.1/go.mod h1:ThDALab8aOuU6KVYESVWFqmjcqtm03e
|
||||
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/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE=
|
||||
github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE=
|
||||
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=
|
||||
|
||||
1
main.go
1
main.go
@@ -32,6 +32,7 @@ import (
|
||||
_ "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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package b14coder base16384 与 tea 加解密
|
||||
package b14coder
|
||||
|
||||
import (
|
||||
|
||||
66
plugin_coser/coser.go
Normal file
66
plugin_coser/coser.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package plugin_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: 可能被风控了"))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"github.com/RomiChan/protobuf/proto"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/utils/sql"
|
||||
)
|
||||
|
||||
type Composition struct {
|
||||
Array []string `protobuf:"bytes,1,rep"`
|
||||
}
|
||||
|
||||
var (
|
||||
compo Composition
|
||||
)
|
||||
|
||||
type Text struct {
|
||||
Id int64 `db:"id"`
|
||||
Data string `db:"data"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := LoadText(os.Args[1])
|
||||
if err == nil {
|
||||
arrl := len(compo.Array)
|
||||
fmt.Printf("[Diana]读取%d条小作文\n", arrl)
|
||||
db := sql.Sqlite{DBPath: os.Args[2]}
|
||||
err = db.Create("text", &Text{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, d := range compo.Array {
|
||||
s := md5.Sum(helper.StringToBytes(d))
|
||||
i := *(*int64)(unsafe.Pointer(&s))
|
||||
fmt.Printf("[Diana]id: %d\n", i)
|
||||
err = db.Insert("text", &Text{
|
||||
Id: i,
|
||||
Data: d,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c, _ := db.Count("text")
|
||||
fmt.Println("[Diana]转化", c, "条小作文")
|
||||
}
|
||||
err = db.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadText 加载小作文
|
||||
func LoadText(pbfile string) error {
|
||||
data, err := os.ReadFile(pbfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return proto.Unmarshal(data, &compo)
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package data
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
@@ -56,8 +56,8 @@ func LoadText() error {
|
||||
// AddText 添加小作文
|
||||
func AddText(txt string) error {
|
||||
s := md5.Sum(helper.StringToBytes(txt))
|
||||
i := *(*int64)(unsafe.Pointer(&s))
|
||||
return db.Insert("text", &Text{Id: i, Data: txt})
|
||||
i := binary.LittleEndian.Uint64(s[:8])
|
||||
return db.Insert("text", &Text{Id: int64(i), Data: txt})
|
||||
}
|
||||
|
||||
// RandText 随机小作文
|
||||
|
||||
@@ -85,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]*$`).
|
||||
|
||||
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,15 +3,12 @@ package manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/RomiChan/protobuf/proto"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/rate"
|
||||
@@ -19,15 +16,15 @@ import (
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
||||
"github.com/FloatTech/ZeroBot-Plugin/plugin_manager/timer"
|
||||
"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/sql"
|
||||
)
|
||||
|
||||
const (
|
||||
datapath = "data/manager/"
|
||||
confile = datapath + "config.pb"
|
||||
timerfile = datapath + "timers.pb"
|
||||
hint = "====群管====\n" +
|
||||
datapath = "data/manager/"
|
||||
confile = datapath + "config.db"
|
||||
hint = "====群管====\n" +
|
||||
"- 禁言@QQ 1分钟\n" +
|
||||
"- 解除禁言 @QQ\n" +
|
||||
"- 我要自闭 1分钟\n" +
|
||||
@@ -55,20 +52,22 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
config Config
|
||||
limit = rate.NewManager(time.Minute*5, 2)
|
||||
clock timer.Clock
|
||||
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()
|
||||
go func() {
|
||||
time.Sleep(time.Second + time.Millisecond*time.Duration(rand.Intn(1000)))
|
||||
clock = timer.NewClock(timerfile)
|
||||
process.SleepAbout1sTo2s()
|
||||
clock = timer.NewClock(db)
|
||||
db.Create("welcome", &Welcome{})
|
||||
db.Create("member", &Member{})
|
||||
}()
|
||||
// 升为管理
|
||||
engine.OnRegex(`^升为管理.*?(\d+)`, zero.OnlyGroup, zero.SuperUserPermission).SetBlock(true).SetPriority(40).
|
||||
@@ -259,9 +258,9 @@ func init() { // 插件主体
|
||||
engine.OnRegex(`^在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分时(用.+)?提醒大家(.*)`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
dateStrs := ctx.State["regex_matched"].([]string)
|
||||
ts := timer.GetFilledTimer(dateStrs, ctx.Event.SelfID, false)
|
||||
ts := timer.GetFilledTimer(dateStrs, ctx.Event.SelfID, ctx.Event.GroupID, false)
|
||||
if ts.En() {
|
||||
go clock.RegisterTimer(ts, ctx.Event.GroupID, true)
|
||||
go clock.RegisterTimer(ts, true)
|
||||
ctx.SendChain(message.Text("记住了~"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("参数非法:" + ts.Alert))
|
||||
@@ -283,8 +282,8 @@ func init() { // 插件主体
|
||||
return
|
||||
}
|
||||
logrus.Debugln("[manager] cron:", dateStrs[1])
|
||||
ts := timer.GetFilledCronTimer(dateStrs[1], alert, url, ctx.Event.SelfID)
|
||||
if clock.RegisterTimer(ts, ctx.Event.GroupID, true) {
|
||||
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))
|
||||
@@ -294,8 +293,8 @@ func init() { // 插件主体
|
||||
engine.OnRegex(`^取消在(.{1,2})月(.{1,3}日|每?周.?)的(.{1,3})点(.{1,3})分的提醒`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
dateStrs := ctx.State["regex_matched"].([]string)
|
||||
ts := timer.GetFilledTimer(dateStrs, ctx.Event.SelfID, true)
|
||||
ti := ts.GetTimerInfo(ctx.Event.GroupID)
|
||||
ts := timer.GetFilledTimer(dateStrs, ctx.Event.SelfID, ctx.Event.GroupID, true)
|
||||
ti := ts.GetTimerID()
|
||||
ok := clock.CancelTimer(ti)
|
||||
if ok {
|
||||
ctx.SendChain(message.Text("取消成功~"))
|
||||
@@ -307,8 +306,8 @@ func init() { // 插件主体
|
||||
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]}
|
||||
ti := ts.GetTimerInfo(ctx.Event.GroupID)
|
||||
ts := timer.Timer{Cron: dateStrs[1], GrpId: ctx.Event.GroupID}
|
||||
ti := ts.GetTimerID()
|
||||
ok := clock.CancelTimer(ti)
|
||||
if ok {
|
||||
ctx.SendChain(message.Text("取消成功~"))
|
||||
@@ -363,46 +362,50 @@ func init() { // 插件主体
|
||||
engine.OnNotice().SetBlock(false).FirstPriority().
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if ctx.Event.NoticeType == "group_increase" && ctx.Event.SelfID != ctx.Event.UserID {
|
||||
word, ok := config.Welcome[ctx.Event.GroupID]
|
||||
if ok {
|
||||
ctx.SendChain(message.Text(word))
|
||||
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.SendChain(message.Text("欢迎~"))
|
||||
}
|
||||
enable, ok1 := config.Checkin[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.SendChain(message.Text("答案不对哦,再想想吧~"))
|
||||
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.SendChain(message.Text("拜拜啦~"))
|
||||
ctx.SetGroupKick(ctx.Event.GroupID, uid, false)
|
||||
cancel()
|
||||
case <-recv:
|
||||
cancel()
|
||||
ctx.SendChain(message.Text("答对啦~"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,30 +422,66 @@ func init() { // 插件主体
|
||||
// 设置欢迎语
|
||||
engine.OnRegex(`^设置欢迎语([\s\S]*)$`, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).SetPriority(40).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
config.Welcome[ctx.Event.GroupID] = ctx.State["regex_matched"].([]string)[1]
|
||||
if saveConfig() == nil {
|
||||
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.SendChain(message.Text("出错啦!"))
|
||||
ctx.SendChain(message.Text("出错啦: ", err))
|
||||
}
|
||||
})
|
||||
// 入群验证开关
|
||||
// 入群后验证开关
|
||||
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[ctx.Event.GroupID] = true
|
||||
case "关闭":
|
||||
config.Checkin[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.SendChain(message.Text("已", option))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("出错啦!"))
|
||||
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 码
|
||||
engine.OnRegex(`^run(.*)$`, zero.SuperUserPermission).SetBlock(true).SetPriority(0).
|
||||
@@ -453,61 +492,33 @@ func init() { // 插件主体
|
||||
// 可注入,权限为主人
|
||||
ctx.Send(cmd)
|
||||
})
|
||||
// 自动同意加好友,被邀请入群(从qingyunke移过来,先注释)
|
||||
/*
|
||||
engine.OnRequest().SetBlock(false).FirstPriority().Handle(func(ctx *zero.Ctx) {
|
||||
if ctx.Event.RequestType == "friend" {
|
||||
ctx.SetFriendAddRequest(ctx.Event.Flag, true, "")
|
||||
// 根据 gist 自动同意加群
|
||||
// 加群请在github新建一个gist,其文件名为本群群号的字符串的md5,内容为一行,是当前unix时间戳。
|
||||
// 然后请将您的用户名和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, "/")
|
||||
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)
|
||||
}
|
||||
if ctx.Event.RequestType == "group" && ctx.Event.SubType == "invite" {
|
||||
ctx.SetGroupAddRequest(ctx.Event.Flag, "invite", true, "我爱你,mua~")
|
||||
}
|
||||
})
|
||||
*/
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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 file.IsExist(confile) {
|
||||
f, err := os.Open(confile)
|
||||
if err == nil {
|
||||
data, err1 := io.ReadAll(f)
|
||||
if err1 == nil {
|
||||
if len(data) > 0 {
|
||||
if proto.Unmarshal(data, &config) == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
config.Checkin = make(map[int64]bool)
|
||||
config.Welcome = make(map[int64]string)
|
||||
} else {
|
||||
panic(mkdirerr)
|
||||
}
|
||||
}
|
||||
|
||||
// saveConfig 保存设置,无此文件则新建
|
||||
func saveConfig() error {
|
||||
data, err := proto.Marshal(&config)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if file.IsExist(datapath) {
|
||||
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,6 +0,0 @@
|
||||
package manager
|
||||
|
||||
type Config struct {
|
||||
Checkin map[int64]bool `protobuf:"bytes,1,rep" protobuf_key:"varint,1,opt" protobuf_val:"varint,2,opt"`
|
||||
Welcome map[int64]string `protobuf:"bytes,2,rep" protobuf_key:"varint,1,opt" protobuf_val:"bytes,2,opt"`
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package timer
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -8,28 +10,37 @@ import (
|
||||
"unicode"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
)
|
||||
|
||||
// GetTimerInfo 获得标准化定时字符串
|
||||
func (ts *Timer) GetTimerInfo(grp int64) string {
|
||||
func (ts *Timer) GetTimerInfo() string {
|
||||
if ts.Cron != "" {
|
||||
return fmt.Sprintf("[%d]%s", grp, ts.Cron)
|
||||
return fmt.Sprintf("[%d]%s", ts.GrpId, ts.Cron)
|
||||
}
|
||||
return fmt.Sprintf("[%d]%d月%d日%d周%d:%d", grp, ts.Month(), ts.Day(), ts.Week(), ts.Hour(), ts.Minute())
|
||||
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 int64) *Timer {
|
||||
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 int64, matchDateOnly bool) *Timer {
|
||||
func GetFilledTimer(dateStrs []string, botqq, grp int64, matchDateOnly bool) *Timer {
|
||||
monthStr := []rune(dateStrs[1])
|
||||
dayWeekStr := []rune(dateStrs[2])
|
||||
hourStr := []rune(dateStrs[3])
|
||||
@@ -43,7 +54,8 @@ func GetFilledTimer(dateStrs []string, botqq int64, matchDateOnly bool) *Timer {
|
||||
}
|
||||
ts.SetMonth(mon)
|
||||
lenOfDW := len(dayWeekStr)
|
||||
if lenOfDW == 4 { // 包括末尾的"日"
|
||||
switch {
|
||||
case lenOfDW == 4: // 包括末尾的"日"
|
||||
dayWeekStr = []rune{dayWeekStr[0], dayWeekStr[2]} // 去除中间的十
|
||||
d := chineseNum2Int(dayWeekStr)
|
||||
if (d != -1 && d <= 0) || d > 31 { // 日期非法
|
||||
@@ -51,7 +63,7 @@ func GetFilledTimer(dateStrs []string, botqq int64, matchDateOnly bool) *Timer {
|
||||
return &ts
|
||||
}
|
||||
ts.SetDay(d)
|
||||
} else if dayWeekStr[lenOfDW-1] == rune('日') { // xx日
|
||||
case dayWeekStr[lenOfDW-1] == rune('日'): // xx日
|
||||
dayWeekStr = dayWeekStr[:lenOfDW-1]
|
||||
d := chineseNum2Int(dayWeekStr)
|
||||
if (d != -1 && d <= 0) || d > 31 { // 日期非法
|
||||
@@ -59,9 +71,9 @@ func GetFilledTimer(dateStrs []string, botqq int64, matchDateOnly bool) *Timer {
|
||||
return &ts
|
||||
}
|
||||
ts.SetDay(d)
|
||||
} else if dayWeekStr[0] == rune('每') { // 每周
|
||||
case dayWeekStr[0] == rune('每'): // 每周
|
||||
ts.SetWeek(-1)
|
||||
} else { // 周x
|
||||
default: // 周x
|
||||
w := chineseNum2Int(dayWeekStr[1:])
|
||||
if w == 7 { // 周天是0
|
||||
w = 0
|
||||
@@ -105,6 +117,7 @@ func GetFilledTimer(dateStrs []string, botqq int64, matchDateOnly bool) *Timer {
|
||||
ts.SetEn(true)
|
||||
}
|
||||
ts.Selfid = botqq
|
||||
ts.GrpId = grp
|
||||
return &ts
|
||||
}
|
||||
|
||||
@@ -116,13 +129,14 @@ func chineseNum2Int(rs []rune) int {
|
||||
if unicode.IsDigit(rs[0]) { // 默认可能存在的第二位也为int
|
||||
r, _ = strconv.Atoi(string(rs))
|
||||
} else {
|
||||
if rs[0] == mai {
|
||||
switch {
|
||||
case rs[0] == mai:
|
||||
if l == 2 {
|
||||
r = -chineseChar2Int(rs[1])
|
||||
}
|
||||
} else if l == 1 {
|
||||
case l == 1:
|
||||
r = chineseChar2Int(rs[0])
|
||||
} else {
|
||||
default:
|
||||
ten := chineseChar2Int(rs[0])
|
||||
if ten != 10 {
|
||||
ten *= 10
|
||||
|
||||
@@ -56,11 +56,12 @@ func (ts *Timer) nextWakeTime() (date time.Time) {
|
||||
} else {
|
||||
stable |= 0x8
|
||||
}
|
||||
if d < 0 {
|
||||
switch {
|
||||
case d < 0:
|
||||
d = date.Day()
|
||||
} else if d > 0 {
|
||||
case d > 0:
|
||||
stable |= 0x4
|
||||
} else {
|
||||
default:
|
||||
d = date.Day()
|
||||
if w >= 0 {
|
||||
stable |= 0x2
|
||||
@@ -148,14 +149,14 @@ func (ts *Timer) nextWakeTime() (date time.Time) {
|
||||
return date
|
||||
}
|
||||
|
||||
func (ts *Timer) judgeHM(grp int64) {
|
||||
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(grp, zero.GetBot(ts.Selfid))
|
||||
ts.sendmsg(ts.GrpId, zero.GetBot(ts.Selfid))
|
||||
} else {
|
||||
zero.RangeBot(func(id int64, ctx *zero.Ctx) (_ bool) {
|
||||
ts.sendmsg(grp, ctx)
|
||||
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
|
||||
}
|
||||
@@ -2,34 +2,28 @@
|
||||
package timer
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/RomiChan/protobuf/proto"
|
||||
"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 {
|
||||
// 记录每个定时器以便取消
|
||||
timersmap TimersMap
|
||||
// 定时器map
|
||||
timers *(map[string]*Timer)
|
||||
db *sql.Sqlite
|
||||
timers *(map[uint32]*Timer)
|
||||
timersmu sync.RWMutex
|
||||
// 定时器存储位置
|
||||
pbfile *string
|
||||
// cron 定时器
|
||||
cron *cron.Cron
|
||||
// entries key <-> cron
|
||||
entries map[string]cron.EntryID
|
||||
entries map[uint32]cron.EntryID
|
||||
entmu sync.Mutex
|
||||
}
|
||||
|
||||
@@ -43,26 +37,27 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func NewClock(pbfile string) (c Clock) {
|
||||
c.loadTimers(pbfile)
|
||||
c.timers = &c.timersmap.Timers
|
||||
c.pbfile = &pbfile
|
||||
func NewClock(db *sql.Sqlite) (c Clock) {
|
||||
c.loadTimers(db)
|
||||
c.cron = cron.New()
|
||||
c.entries = make(map[string]cron.EntryID)
|
||||
c.entries = make(map[uint32]cron.EntryID)
|
||||
c.cron.Start()
|
||||
return
|
||||
}
|
||||
|
||||
// RegisterTimer 注册计时器
|
||||
func (c *Clock) RegisterTimer(ts *Timer, grp int64, save bool) bool {
|
||||
key := ts.GetTimerInfo(grp)
|
||||
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)
|
||||
}
|
||||
c.timersmu.Lock()
|
||||
(*c.timers)[key] = ts
|
||||
c.timersmu.Unlock()
|
||||
logrus.Println("[群管]注册计时器", key)
|
||||
if ts.Cron != "" {
|
||||
var ctx *zero.Ctx
|
||||
@@ -75,33 +70,33 @@ func (c *Clock) RegisterTimer(ts *Timer, grp int64, save bool) bool {
|
||||
return false
|
||||
})
|
||||
}
|
||||
eid, err := c.cron.AddFunc(ts.Cron, func() { ts.sendmsg(grp, ctx) })
|
||||
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 {
|
||||
c.SaveTimers()
|
||||
err = c.AddTimer(ts)
|
||||
}
|
||||
return true
|
||||
return err == nil
|
||||
}
|
||||
ts.Alert = err.Error()
|
||||
} else {
|
||||
if save {
|
||||
c.SaveTimers()
|
||||
_ = c.AddTimer(ts)
|
||||
}
|
||||
for ts.En() {
|
||||
nextdate := ts.nextWakeTime()
|
||||
sleepsec := time.Until(nextdate)
|
||||
logrus.Printf("[群管]计时器%s将睡眠%ds", key, sleepsec/time.Second)
|
||||
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(grp)
|
||||
ts.judgeHM()
|
||||
} else if ts.Day() == 0 {
|
||||
if ts.Week() < 0 || ts.Week() == time.Now().Weekday() {
|
||||
ts.judgeHM(grp)
|
||||
ts.judgeHM()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,8 +107,8 @@ func (c *Clock) RegisterTimer(ts *Timer, grp int64, save bool) bool {
|
||||
}
|
||||
|
||||
// CancelTimer 取消计时器
|
||||
func (c *Clock) CancelTimer(key string) bool {
|
||||
t, ok := (*c.timers)[key]
|
||||
func (c *Clock) CancelTimer(key uint32) bool {
|
||||
t, ok := c.GetTimer(key)
|
||||
if ok {
|
||||
if t.Cron != "" {
|
||||
c.entmu.Lock()
|
||||
@@ -126,41 +121,22 @@ func (c *Clock) CancelTimer(key string) bool {
|
||||
}
|
||||
c.timersmu.Lock()
|
||||
delete(*c.timers, key) // 避免重复取消
|
||||
e := c.db.Del("timer", "where id = "+strconv.Itoa(int(key)))
|
||||
c.timersmu.Unlock()
|
||||
_ = c.SaveTimers()
|
||||
return e == nil
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// SaveTimers 保存当前计时器
|
||||
func (c *Clock) SaveTimers() error {
|
||||
c.timersmu.RLock()
|
||||
data, err := proto.Marshal(&c.timersmap)
|
||||
c.timersmu.RUnlock()
|
||||
if err == nil {
|
||||
c.timersmu.Lock()
|
||||
defer c.timersmu.Unlock()
|
||||
f, err1 := os.OpenFile(*c.pbfile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
|
||||
if err1 != nil {
|
||||
return err1
|
||||
} else {
|
||||
_, err2 := f.Write(data)
|
||||
f.Close()
|
||||
return err2
|
||||
}
|
||||
}
|
||||
return err
|
||||
return false
|
||||
}
|
||||
|
||||
// ListTimers 列出本群所有计时器
|
||||
func (c *Clock) ListTimers(grpID int64) []string {
|
||||
// 数组默认长度为map长度,后面append时,不需要重新申请内存和拷贝,效率很高
|
||||
if c.timers != nil {
|
||||
g := strconv.FormatInt(grpID, 10)
|
||||
c.timersmu.RLock()
|
||||
keys := make([]string, 0, len(*c.timers))
|
||||
for k := range *c.timers {
|
||||
if strings.Contains(k, g) {
|
||||
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周", "月周天")
|
||||
@@ -176,35 +152,32 @@ func (c *Clock) ListTimers(grpID int64) []string {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Clock) GetTimer(key string) (t *Timer, ok bool) {
|
||||
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) loadTimers(pbfile string) {
|
||||
if file.IsExist(pbfile) {
|
||||
f, err := os.Open(pbfile)
|
||||
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 {
|
||||
data, err := io.ReadAll(f)
|
||||
if err == nil {
|
||||
if len(data) > 0 {
|
||||
err = proto.Unmarshal(data, &c.timersmap)
|
||||
if err == nil {
|
||||
for str, t := range c.timersmap.Timers {
|
||||
grp, err := strconv.ParseInt(str[1:strings.Index(str, "]")], 10, 64)
|
||||
if err == nil {
|
||||
go c.RegisterTimer(t, grp, false)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
logrus.Errorln("[群管]读取定时器文件失败,将在下一次保存时覆盖原文件。err:", err)
|
||||
logrus.Errorln("[群管]如不希望被覆盖,请运行源码plugin_manager/timers/migrate下的程序将timers.pb刷新为新版")
|
||||
}
|
||||
}
|
||||
var t Timer
|
||||
c.db.FindFor("timer", &t, "", func() error {
|
||||
tescape := t
|
||||
go c.RegisterTimer(&tescape, false)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
c.timersmap.Timers = make(map[string]*Timer)
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package timer
|
||||
|
||||
type Timer struct {
|
||||
Alert string `protobuf:"bytes,1,opt"`
|
||||
Cron string `protobuf:"bytes,2,opt"`
|
||||
En1Month4Day5Week3Hour5Min6 int32 `protobuf:"varint,4,opt"`
|
||||
Selfid int64 `protobuf:"varint,8,opt"`
|
||||
Url string `protobuf:"bytes,16,opt"`
|
||||
}
|
||||
|
||||
type TimersMap struct {
|
||||
Timers map[string]*Timer `protobuf:"bytes,1,rep" protobuf_key:"bytes,1,opt" protobuf_val:"bytes,2,opt"`
|
||||
}
|
||||
@@ -2,10 +2,12 @@ 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 {
|
||||
@@ -14,6 +16,7 @@ func (m *Timer) Month() (mon time.Month) {
|
||||
return
|
||||
}
|
||||
|
||||
// Day 5bits
|
||||
func (m *Timer) Day() (d int) {
|
||||
d = int((m.En1Month4Day5Week3Hour5Min6 & 0x07c000) >> 14)
|
||||
if d == 0b11111 {
|
||||
@@ -22,6 +25,7 @@ func (m *Timer) Day() (d int) {
|
||||
return
|
||||
}
|
||||
|
||||
// Week 3bits
|
||||
func (m *Timer) Week() (w time.Weekday) {
|
||||
w = time.Weekday((m.En1Month4Day5Week3Hour5Min6 & 0x003800) >> 11)
|
||||
if w == 0b111 {
|
||||
@@ -30,6 +34,7 @@ func (m *Timer) Week() (w time.Weekday) {
|
||||
return
|
||||
}
|
||||
|
||||
// Hour 5bits
|
||||
func (m *Timer) Hour() (h int) {
|
||||
h = int((m.En1Month4Day5Week3Hour5Min6 & 0x0007c0) >> 6)
|
||||
if h == 0b11111 {
|
||||
@@ -38,6 +43,7 @@ func (m *Timer) Hour() (h int) {
|
||||
return
|
||||
}
|
||||
|
||||
// Minute 6bits
|
||||
func (m *Timer) Minute() (min int) {
|
||||
min = int(m.En1Month4Day5Week3Hour5Min6 & 0x00003f)
|
||||
if min == 0b111111 {
|
||||
@@ -46,6 +52,7 @@ func (m *Timer) Minute() (min int) {
|
||||
return
|
||||
}
|
||||
|
||||
// SetEn ...
|
||||
func (m *Timer) SetEn(en bool) {
|
||||
if en {
|
||||
m.En1Month4Day5Week3Hour5Min6 |= 0x800000
|
||||
@@ -54,22 +61,27 @@ func (m *Timer) SetEn(en bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,9 @@ func init() {
|
||||
defer db.Close()
|
||||
defer cancel()
|
||||
firstStepMessage := db.GetAllFirstCategoryMessage()
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(firstStepMessage))
|
||||
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
|
||||
// 错误次数
|
||||
|
||||
@@ -16,3 +16,11 @@ func Min(a, b int) int {
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Abs 返回绝对值,该函数将被内联
|
||||
func Abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ func (db *Sqlite) Create(table string, objptr interface{}) (err error) {
|
||||
}
|
||||
|
||||
// Insert 插入数据集
|
||||
// 如果 PK 存在会覆盖
|
||||
// 默认结构体的第一个元素为主键
|
||||
// 返回错误
|
||||
func (db *Sqlite) Insert(table string, objptr interface{}) error {
|
||||
@@ -133,7 +134,67 @@ func (db *Sqlite) Insert(table string, objptr interface{}) error {
|
||||
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"
|
||||
// 默认字段与结构体元素顺序一致
|
||||
// 返回错误
|
||||
@@ -164,6 +225,68 @@ func (db *Sqlite) Find(table string, objptr interface{}, condition string) error
|
||||
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 err
|
||||
}
|
||||
|
||||
// Pick 从 table 随机一行
|
||||
func (db *Sqlite) Pick(table string, objptr interface{}) error {
|
||||
return db.Find(table, objptr, "ORDER BY RANDOM() limit 1")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
@@ -23,3 +24,18 @@ func ReqWith(url string, method string, referer string, ua string) (data []byte,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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