From 72390b4c093477d973eb20083038d9e96f2825e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Fri, 10 Jun 2022 19:58:48 +0800 Subject: [PATCH] add more --- README.md | 5 +- cd.go | 100 ++++++++++++++ context.go | 58 +++++++- engine.go | 14 +- event.go | 3 + example/echo/main.go | 9 +- example/main.go | 3 +- go.mod | 22 ++- go.sum | 194 +++++++++++++++++++++++++- lazy.go | 20 +++ manager.go | 84 +++++++++++ matcher.go | 24 ++++ rule.go | 321 +++++++++++++++++++++++++++++++++++++++++++ rules.go | 8 ++ 14 files changed, 845 insertions(+), 20 deletions(-) create mode 100644 cd.go create mode 100644 lazy.go create mode 100644 manager.go create mode 100644 rule.go diff --git a/README.md b/README.md index ee39047..34c335d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ This framework is a simple wrapper for [go-telegram-bot-api](https://github.com/go-telegram-bot-api/telegram-bot-api), aiming to make the event processing easier. ## Quick Start -> Here is a plugin-based example +> Here is a plugin-based example, see more in the `example` folder ![plugin-based example](https://user-images.githubusercontent.com/41315874/171567343-f61eba4e-2bc9-49b3-af05-6446f0a73c54.png) @@ -34,8 +34,7 @@ func main() { if args == "" { return } - msg := ctx.Value.(*tgba.Message) - ctx.Caller.Send(tgba.NewMessage(msg.Chat.ID, args)) + ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, args)) }) rei.Run(rei.Bot{ Token: "", diff --git a/cd.go b/cd.go new file mode 100644 index 0000000..8e93f0d --- /dev/null +++ b/cd.go @@ -0,0 +1,100 @@ +package rei + +import ( + "encoding/binary" + "strings" + "time" + + ctrl "github.com/FloatTech/zbpctrl" + binutils "github.com/FloatTech/zbputils/binary" + "github.com/FloatTech/zbputils/math" + "github.com/FloatTech/zbputils/process" + b14 "github.com/fumiama/go-base16384" + tgba "github.com/go-telegram-bot-api/telegram-bot-api/v5" +) + +var startTime int64 + +func init() { + // 插件冲突检测 会在本群发送一条消息并在约 1s 后撤回 + OnMessageFullMatch("插件冲突检测", OnlyGroup, AdminPermission, OnlyToMe).SetBlock(true).secondPriority(). + Handle(func(ctx *Ctx) { + tok := genToken() + if tok == "" || len([]rune(tok)) != 4 { + return + } + startTime = time.Now().Unix() + msg, err := ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "●cd"+tok)) + if err != nil { + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "ERROR: "+err.Error())) + return + } + process.SleepAbout1sTo2s() + _, _ = ctx.Caller.Send(tgba.NewDeleteMessage(ctx.Message.Chat.ID, msg.MessageID)) + }) + + OnMessageRegex("^●cd([\u4e00-\u8e00]{4})$", OnlyGroup).SetBlock(true).secondPriority(). + Handle(func(ctx *Ctx) { + if isValidToken(ctx.State["regex_matched"].([]string)[1]) { + gid := ctx.Message.Chat.ID + w := binutils.SelectWriter() + m.ForEach(func(key string, manager *ctrl.Control[*Ctx]) bool { + if manager.IsEnabledIn(gid) { + w.WriteString("\xfe\xff") + w.WriteString(key) + } + return true + }) + if w.Len() > 2 { + my, cl := binutils.OpenWriterF(func(wr *binutils.Writer) { + wr.WriteString("●cd●") + wr.WriteString(b14.EncodeString(w.String()[2:])) + }) + binutils.PutWriter(w) + msg, err := ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, binutils.BytesToString(my))) + cl() + if err != nil { + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "ERROR: "+err.Error())) + return + } + process.SleepAbout1sTo2s() + _, _ = ctx.Caller.Send(tgba.NewDeleteMessage(ctx.Message.Chat.ID, msg.MessageID)) + } + } + }) + + OnMessageRegex("^●cd●(([\u4e00-\u8e00]*[\u3d01-\u3d06]?))", OnlyGroup).SetBlock(true).secondPriority(). + Handle(func(ctx *Ctx) { + if time.Now().Unix()-startTime < 10 { + gid := ctx.Message.Chat.ID + for _, s := range strings.Split(b14.DecodeString(ctx.State["regex_matched"].([]string)[1]), "\xfe\xff") { + m.RLock() + c, ok := m.M[s] + m.RUnlock() + if ok && c.IsEnabledIn(gid) { + c.Disable(gid) + } + } + } + }) +} + +func genToken() (tok string) { + timebytes, cl := binutils.OpenWriterF(func(w *binutils.Writer) { + w.WriteUInt64(uint64(time.Now().Unix())) + }) + tok = b14.EncodeString(binutils.BytesToString(timebytes[1:])) + cl() + return +} + +func isValidToken(tok string) (yes bool) { + s := b14.DecodeString(tok) + timebytes, cl := binutils.OpenWriterF(func(w *binutils.Writer) { + _ = w.WriteByte(0) + w.WriteString(s) + }) + yes = math.Abs64(time.Now().Unix()-int64(binary.BigEndian.Uint64(timebytes))) < 10 + cl() + return +} diff --git a/context.go b/context.go index f135c7e..ba194fb 100644 --- a/context.go +++ b/context.go @@ -1,12 +1,64 @@ package rei -import tgba "github.com/go-telegram-bot-api/telegram-bot-api/v5" +import ( + "fmt" + "reflect" + "sync" + + tgba "github.com/go-telegram-bot-api/telegram-bot-api/v5" +) type Ctx struct { Event State - Caller *TelegramClient - ma *Matcher + Caller *TelegramClient + Message *tgba.Message + ma *Matcher +} + +// decoder 反射获取的数据 +type decoder []dec + +type dec struct { + index int + key string +} + +// decoder 缓存 +var decoderCache = sync.Map{} + +// Parse 将 Ctx.State 映射到结构体 +func (ctx *Ctx) Parse(model interface{}) (err error) { + var ( + rv = reflect.ValueOf(model).Elem() + t = rv.Type() + modelDec decoder + ) + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("parse state error: %v", r) + } + }() + d, ok := decoderCache.Load(t) + if ok { + modelDec = d.(decoder) + } else { + modelDec = decoder{} + for i := 0; i < t.NumField(); i++ { + t1 := t.Field(i) + if key, ok := t1.Tag.Lookup("zero"); ok { + modelDec = append(modelDec, dec{ + index: i, + key: key, + }) + } + } + decoderCache.Store(t, modelDec) + } + for _, d := range modelDec { // decoder类型非小内存,无法被编译器优化为快速拷贝 + rv.Field(d.index).Set(reflect.ValueOf(ctx.State[d.key])) + } + return nil } // CheckSession 判断会话连续性 diff --git a/engine.go b/engine.go index bb28e8e..661c8e0 100644 --- a/engine.go +++ b/engine.go @@ -1,7 +1,7 @@ package rei -// New 生成空引擎 -func NewEngine() *Engine { +// 生成空引擎 +func newEngine() *Engine { return &Engine{ preHandler: []Rule{}, midHandler: []Rule{}, @@ -9,7 +9,7 @@ func NewEngine() *Engine { } } -var defaultEngine = NewEngine() +var defaultEngine = newEngine() // Engine is the pre_handler, mid_handler, post_handler manager type Engine struct { @@ -17,6 +17,9 @@ type Engine struct { midHandler []Rule postHandler []Process matchers []*Matcher + prio int + service string + datafolder string } // Delete 移除该 Engine 注册的所有 Matchers @@ -59,6 +62,11 @@ func (e *Engine) ApplySingle(s *Single[int64]) *Engine { return e } +// DataFolder 本插件数据目录, 默认 data/rbp/ +func (e *Engine) DataFolder() string { + return e.datafolder +} + // On 添加新的指定消息类型的匹配器(默认Engine) func On(typ string, rules ...Rule) *Matcher { return defaultEngine.On(typ, rules...) } diff --git a/event.go b/event.go index f4aad5f..022cbb8 100644 --- a/event.go +++ b/event.go @@ -46,6 +46,9 @@ func (tc *TelegramClient) processEvent(update tgba.Update) { State: State{}, Caller: tc, } + if tp == "Message" { + ctx.Message = (*tgba.Message)(f.UnsafePointer()) + } match(ctx, matchers) continue } diff --git a/example/echo/main.go b/example/echo/main.go index 3296002..a0f4edf 100644 --- a/example/echo/main.go +++ b/example/echo/main.go @@ -1,18 +1,21 @@ package echo import ( + ctrl "github.com/FloatTech/zbpctrl" rei "github.com/fumiama/ReiBot" tgba "github.com/go-telegram-bot-api/telegram-bot-api/v5" ) func init() { - rei.OnMessagePrefix("echo").SetBlock(true). + rei.Register("echo", &ctrl.Options[*rei.Ctx]{ + DisableOnDefault: false, + Help: "- echo xxx", + }).OnMessagePrefix("echo").SetBlock(true). Handle(func(ctx *rei.Ctx) { args := ctx.State["args"].(string) if args == "" { return } - msg := ctx.Value.(*tgba.Message) - _, _ = ctx.Caller.Send(tgba.NewMessage(msg.Chat.ID, args)) + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, args)) }) } diff --git a/example/main.go b/example/main.go index 07d991f..0efc372 100644 --- a/example/main.go +++ b/example/main.go @@ -10,8 +10,7 @@ import ( func main() { rei.OnMessageFullMatch("help").SetBlock(true). Handle(func(ctx *rei.Ctx) { - msg := ctx.Value.(*tgba.Message) - _, _ = ctx.Caller.Send(tgba.NewMessage(msg.Chat.ID, "echo string")) + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "echo string")) }) rei.Run(rei.Bot{ Token: "", diff --git a/go.mod b/go.mod index 4fef86a..95fc317 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,34 @@ module github.com/fumiama/ReiBot go 1.18 require ( + github.com/FloatTech/zbpctrl v1.4.1-0.20220610074608-425160596f27 + github.com/FloatTech/zbputils v1.4.1-0.20220610074642-09004f278b03 github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c + github.com/fumiama/go-base16384 v1.5.2 github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.1 - github.com/wdvxdr1123/ZeroBot v1.5.1 + github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220610070647-9eeffcb277ee ) require ( + github.com/FloatTech/sqlite v0.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fogleman/gg v1.3.0 // indirect + github.com/fumiama/cron v1.3.0 // indirect + github.com/fumiama/go-registry v0.1.6 // indirect + github.com/fumiama/gofastTEA v0.0.10 // indirect + github.com/fumiama/sqlite3 v1.14.6 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect + golang.org/x/image v0.0.0-20220601225756-64ec528b34cd // indirect + golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect + golang.org/x/text v0.3.7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/libc v1.16.8 // indirect + modernc.org/mathutil v1.4.1 // indirect + modernc.org/memory v1.1.1 // indirect ) diff --git a/go.sum b/go.sum index f42c3c9..ae2560d 100644 --- a/go.sum +++ b/go.sum @@ -1,25 +1,211 @@ +github.com/FloatTech/sqlite v0.3.2 h1:iTg2ZKnzjjZAdlSN3hXmpCBn15odc4Ud484OoM3yXGA= +github.com/FloatTech/sqlite v0.3.2/go.mod h1:VFtLofV5qxw5eBneZRbWwD451SLSm50o9J3J43iB1iw= +github.com/FloatTech/zbpctrl v1.4.1-0.20220610074608-425160596f27 h1:C+D30vpxfgbJetTFXWAHzuU8GydbFb/A8Kv6E3PdRS4= +github.com/FloatTech/zbpctrl v1.4.1-0.20220610074608-425160596f27/go.mod h1:5FDkrlVaQCxUfeqH7XJPTfej0q+y9fzImhvZI4ofu9Y= +github.com/FloatTech/zbputils v1.4.1-0.20220610074642-09004f278b03 h1:OeVhJvlf3gMac+B5CMOQoJOM3I/uz/wCMgX6o6OgtFE= +github.com/FloatTech/zbputils v1.4.1-0.20220610074642-09004f278b03/go.mod h1:6Y34s+MbssTT4AWBKfJCMifFyFhtB3RGC1qo+8EVGZ4= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= 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/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +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/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.5.2 h1:cbxXTcDH92PNgG7bEBwiCEoWb5O+nwZKxKOG94ilFo8= +github.com/fumiama/go-base16384 v1.5.2/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM= +github.com/fumiama/go-registry v0.1.6 h1:Ee/tXCCIR/xt8celhbbw0W/xDMdhAXLwy2YFBB/LWFk= +github.com/fumiama/go-registry v0.1.6/go.mod h1:dIUVbiOgfk9oZcsgwDvNLC72i+ctibVukSXR/9bLviI= +github.com/fumiama/gofastTEA v0.0.10 h1:JJJ+brWD4kie+mmK2TkspDXKzqq0IjXm89aGYfoGhhQ= +github.com/fumiama/gofastTEA v0.0.10/go.mod h1:RIdbYZyB4MbH6ZBlPymRaXn3cD6SedlCu5W/HHfMPBk= +github.com/fumiama/sqlite3 v1.14.6 h1:+e+iygyiDXQJVi7xeXIviBvR7hAc5y20WA9hRwfKn10= +github.com/fumiama/sqlite3 v1.14.6/go.mod h1:Xx9a2/OtHuy9pBjow0N+bE/RhNeZ7zZz5xh25vqbA5A= github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc= github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8= +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/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/wdvxdr1123/ZeroBot v1.5.1 h1:riSAWc0kTy+ILgf+YnZMp+FfhVRGDrOMxK1e2wwWdus= -github.com/wdvxdr1123/ZeroBot v1.5.1/go.mod h1:K2vu0mslV8s4qhIAu/a03Z7YW24qjM0j3imIR+k21KI= +github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220610070647-9eeffcb277ee h1:b2f+KLhZv+BCMQZuwMJvhKQOrz5YXzOduHC3G1DjQR0= +github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220610070647-9eeffcb277ee/go.mod h1:LJ+VOf523i3IrykuLK53UEeWqnAclRL5d2wGT4sS4Zk= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/image v0.0.0-20220601225756-64ec528b34cd h1:9NbNcTg//wfC5JskFW4Z3sqwVnjmJKHxLAol1bW2qgw= +golang.org/x/image v0.0.0-20220601225756-64ec528b34cd/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 h1:z8Hj/bl9cOV2grsOpEaQFUaly0JWN3i97mo3jXKJNp0= +golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= +modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= +modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= +modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= +modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= +modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= +modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= +modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= +modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= +modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= +modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= +modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= +modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= +modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= +modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= +modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= +modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= +modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= +modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= +modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= +modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= +modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= +modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= +modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= +modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= +modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= +modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= +modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= +modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= +modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= +modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= +modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= +modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= +modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= +modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU= +modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko= +modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA= +modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4= +modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0= +modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8= +modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= +modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= +modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= +modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= +modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= +modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= +modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= +modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= +modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= +modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= +modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= +modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= +modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= +modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= +modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= +modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= +modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= +modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= +modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= +modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= +modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= +modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= +modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= +modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= +modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= +modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= +modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= +modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= +modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= +modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= +modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= +modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= +modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ= +modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c= +modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI= +modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ= +modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= +modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34= +modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ= +modernc.org/libc v1.14.5/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o= +modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= +modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= +modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/lazy.go b/lazy.go new file mode 100644 index 0000000..f49080b --- /dev/null +++ b/lazy.go @@ -0,0 +1,20 @@ +package rei + +import ( + "errors" + "strings" + "unicode" + + "github.com/FloatTech/zbputils/file" +) + +// 下载并获取本 engine 文件夹下的懒加载数据 +func (e *Engine) GetLazyData(filename string, isDataMustEqual bool) ([]byte, error) { + if e.datafolder == "" { + return nil, errors.New("datafolder is empty") + } + if !strings.HasSuffix(e.datafolder, "/") || !strings.HasPrefix(e.datafolder, "data/") || !unicode.IsUpper(rune(e.datafolder[5])) { + return nil, errors.New("invalid datafolder") + } + return file.GetLazyData(e.datafolder+filename, isDataMustEqual) +} diff --git a/manager.go b/manager.go new file mode 100644 index 0000000..43af90b --- /dev/null +++ b/manager.go @@ -0,0 +1,84 @@ +package rei + +import ( + "fmt" + "os" + "sync/atomic" + "time" + "unicode" + + ctrl "github.com/FloatTech/zbpctrl" + "github.com/FloatTech/zbputils/file" + "github.com/sirupsen/logrus" +) + +type Manager ctrl.Manager[*Ctx] + +var ( + enmap = make(map[string]*Engine) + priomap = make(map[int]string) // priomap is map[prio]service + foldermap = make(map[string]string) // foldermap is map[folder]service + prio uint64 + m = ctrl.NewManager[*Ctx]("data/control/plugins.db", 10*time.Second) +) + +// Register 注册插件控制器 +func Register(service string, o *ctrl.Options[*Ctx]) *Engine { + prio := int(atomic.AddUint64(&prio, 10)) + e := newEngine() + s, ok := priomap[prio] + if ok { + panic(fmt.Sprint("prio", prio, "is used by", s)) + } + priomap[prio] = service + logrus.Debugln("[control]插件", service, "已设置优先级", prio) + e.UsePreHandler(newctrl(service, o)) + e.prio = prio + e.service = service + switch { + case o.PublicDataFolder != "": + if unicode.IsLower([]rune(o.PublicDataFolder)[0]) { + panic("public data folder " + o.PublicDataFolder + " must start with an upper case letter") + } + e.datafolder = "data/" + o.PublicDataFolder + "/" + case o.PrivateDataFolder != "": + if unicode.IsUpper([]rune(o.PrivateDataFolder)[0]) { + panic("private data folder " + o.PrivateDataFolder + " must start with an lower case letter") + } + e.datafolder = "data/" + o.PrivateDataFolder + "/" + default: + e.datafolder = "data/rbp/" + } + if e.datafolder != "data/rbp/" { + s, ok := foldermap[e.datafolder] + if ok { + panic("folder " + e.datafolder + " has been required by service " + s) + } + foldermap[e.datafolder] = service + } + if file.IsNotExist(e.datafolder) { + err := os.MkdirAll(e.datafolder, 0755) + if err != nil { + panic(err) + } + } + logrus.Debugln("[control]插件", service, "已设置数据目录", e.datafolder) + enmap[service] = e + return e +} + +// Delete 删除插件控制器, 不会删除数据 +func Delete(service string) { + engine, ok := enmap[service] + if ok { + engine.Delete() + m.RLock() + _, ok = m.M[service] + m.RUnlock() + if ok { + m.Lock() + delete(m.M, service) + m.Unlock() + } + } +} diff --git a/matcher.go b/matcher.go index c359b76..c356b11 100644 --- a/matcher.go +++ b/matcher.go @@ -56,6 +56,30 @@ func (m *Matcher) SetBlock(block bool) *Matcher { return m } +// setPriority 设置当前 Matcher 优先级 +func (m *Matcher) setPriority(priority int) *Matcher { + matcherLock.Lock() + defer matcherLock.Unlock() + m.priority = priority + sortMatcher(m.Type) + return m +} + +// firstPriority 设置当前 Matcher 优先级 - 0 +func (m *Matcher) firstPriority() *Matcher { + return m.setPriority(0) +} + +// secondPriority 设置当前 Matcher 优先级 - 1 +func (m *Matcher) secondPriority() *Matcher { + return m.setPriority(1) +} + +// thirdPriority 设置当前 Matcher 优先级 - 2 +func (m *Matcher) thirdPriority() *Matcher { + return m.setPriority(2) +} + // Limit 限速器 // postfn 当请求被拒绝时的操作 func (m *Matcher) Limit(limiterfn func(*Ctx) *rate.Limiter, postfn ...func(*Ctx)) *Matcher { diff --git a/rule.go b/rule.go new file mode 100644 index 0000000..89463f3 --- /dev/null +++ b/rule.go @@ -0,0 +1,321 @@ +package rei + +import ( + "fmt" + "os" + "strconv" + "strings" + "time" + "unsafe" + + ctrl "github.com/FloatTech/zbpctrl" + "github.com/FloatTech/zbputils/img/text" + "github.com/FloatTech/zbputils/process" + tgba "github.com/go-telegram-bot-api/telegram-bot-api/v5" + "github.com/sirupsen/logrus" + "github.com/wdvxdr1123/ZeroBot/extension" +) + +func newctrl(service string, o *ctrl.Options[*Ctx]) Rule { + c := m.NewControl(service, o) + return func(ctx *Ctx) bool { + msg, ok := ctx.Value.(*tgba.Message) + if !ok { + return false + } + ctx.State["manager"] = c + ctx.Message = msg + var gid int64 = 0 + if !msg.Chat.IsPrivate() { + gid = msg.Chat.ID + } + return c.Handler(uintptr(unsafe.Pointer(ctx)), gid, msg.From.ID) + } +} + +func Lookup(service string) (*ctrl.Control[*Ctx], bool) { + return m.Lookup(service) +} + +func init() { + process.NewCustomOnce(&m).Do(func() { + err := os.MkdirAll("data/control", 0755) + if err != nil { + panic(err) + } + err = m.D.Open(time.Hour * 24) + if err != nil { + panic(err) + } + err = m.InitBlock() + if err != nil { + panic(err) + } + OnMessage(func(ctx *Ctx) bool { + return m.IsBlocked(ctx.Message.From.ID) + }).SetBlock(true).thirdPriority() + OnMessageCommandGroup([]string{ + "启用", "enable", "禁用", "disable", + }, UserOrGrpAdmin).SetBlock(true).secondPriority().Handle(func(ctx *Ctx) { + model := extension.CommandModel{} + _ = ctx.Parse(&model) + service, ok := Lookup(model.Args) + if !ok { + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "没有找到指定服务!")) + return + } + grp := ctx.Message.Chat.ID + if ctx.Message.Chat.IsPrivate() { + // 个人用户 + grp = -ctx.Message.From.ID + } + if strings.Contains(model.Command, "启用") || strings.Contains(model.Command, "enable") { + service.Enable(grp) + if service.Options.OnEnable != nil { + service.Options.OnEnable(ctx) + } else { + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "已启用服务: "+model.Args)) + } + } else { + service.Disable(grp) + if service.Options.OnDisable != nil { + service.Options.OnDisable(ctx) + } else { + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "已禁用服务: "+model.Args)) + } + } + }) + + OnMessageCommandGroup([]string{ + "全局启用", "allenable", "全局禁用", "alldisable", + }, OnlyToMe, SuperUserPermission).SetBlock(true).secondPriority().Handle(func(ctx *Ctx) { + model := extension.CommandModel{} + _ = ctx.Parse(&model) + service, ok := Lookup(model.Args) + if !ok { + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "没有找到指定服务!")) + return + } + if strings.Contains(model.Command, "启用") || strings.Contains(model.Command, "enable") { + service.Enable(0) + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "已全局启用服务: "+model.Args)) + } else { + service.Disable(0) + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "已全局禁用服务: "+model.Args)) + } + }) + + OnMessageCommandGroup([]string{"还原", "reset"}, UserOrGrpAdmin).SetBlock(true).secondPriority().Handle(func(ctx *Ctx) { + model := extension.CommandModel{} + _ = ctx.Parse(&model) + service, ok := Lookup(model.Args) + if !ok { + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "没有找到指定服务!")) + return + } + grp := ctx.Message.Chat.ID + if ctx.Message.Chat.IsPrivate() { + // 个人用户 + grp = -ctx.Message.From.ID + } + service.Reset(grp) + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "已还原服务的默认启用状态: "+model.Args)) + }) + + OnMessageCommandGroup([]string{ + "禁止", "ban", "允许", "permit", + }, AdminPermission).SetBlock(true).secondPriority().Handle(func(ctx *Ctx) { + model := extension.CommandModel{} + _ = ctx.Parse(&model) + args := strings.Split(model.Args, " ") + if len(args) >= 2 { + service, ok := Lookup(args[0]) + if !ok { + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "没有找到指定服务!")) + return + } + grp := ctx.Message.Chat.ID + if ctx.Message.Chat.IsPrivate() { + // 个人用户 + grp = -ctx.Message.From.ID + } + msg := "**" + args[0] + "报告**" + if strings.Contains(model.Command, "允许") || strings.Contains(model.Command, "permit") { + for _, usr := range args[1:] { + uid, err := strconv.ParseInt(usr, 10, 64) + if err == nil { + service.Permit(uid, grp) + msg += "\n+ 已允许" + usr + } + } + } else { + for _, usr := range args[1:] { + uid, err := strconv.ParseInt(usr, 10, 64) + if err == nil { + service.Ban(uid, grp) + msg += "\n- 已禁止" + usr + } + } + } + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, msg)) + return + } + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "参数错误!")) + }) + + OnMessageCommandGroup([]string{ + "全局禁止", "allban", "全局允许", "allpermit", + }, SuperUserPermission).SetBlock(true).secondPriority().Handle(func(ctx *Ctx) { + model := extension.CommandModel{} + _ = ctx.Parse(&model) + args := strings.Split(model.Args, " ") + if len(args) >= 2 { + service, ok := Lookup(args[0]) + if !ok { + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "没有找到指定服务!")) + return + } + msg := "**" + args[0] + "全局报告**" + if strings.Contains(model.Command, "允许") || strings.Contains(model.Command, "permit") { + for _, usr := range args[1:] { + uid, err := strconv.ParseInt(usr, 10, 64) + if err == nil { + service.Permit(uid, 0) + msg += "\n+ 已允许" + usr + } + } + } else { + for _, usr := range args[1:] { + uid, err := strconv.ParseInt(usr, 10, 64) + if err == nil { + service.Ban(uid, 0) + msg += "\n- 已禁止" + usr + } + } + } + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, msg)) + return + } + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "参数错误!")) + }) + + OnMessageCommandGroup([]string{ + "封禁", "block", "解封", "unblock", + }, SuperUserPermission).SetBlock(true).secondPriority().Handle(func(ctx *Ctx) { + model := extension.CommandModel{} + _ = ctx.Parse(&model) + args := strings.Split(model.Args, " ") + if len(args) >= 1 { + msg := "**报告**" + if strings.Contains(model.Command, "解") || strings.Contains(model.Command, "un") { + for _, usr := range args { + uid, err := strconv.ParseInt(usr, 10, 64) + if err == nil { + if m.DoUnblock(uid) == nil { + msg += "\n- 已解封" + usr + } + } + } + } else { + for _, usr := range args { + uid, err := strconv.ParseInt(usr, 10, 64) + if err == nil { + if m.DoBlock(uid) == nil { + msg += "\n+ 已封禁" + usr + } + } + } + } + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, msg)) + return + } + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "参数错误!")) + }) + + OnMessageCommandGroup([]string{ + "改变默认启用状态", "allflip", + }, SuperUserPermission).SetBlock(true).secondPriority().Handle(func(ctx *Ctx) { + model := extension.CommandModel{} + _ = ctx.Parse(&model) + service, ok := Lookup(model.Args) + if !ok { + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "没有找到指定服务!")) + return + } + err := service.Flip() + if err != nil { + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "ERROR: "+err.Error())) + return + } + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "已改变全局默认启用状态: "+model.Args)) + }) + + OnMessageCommandGroup([]string{"用法", "usage"}, UserOrGrpAdmin).SetBlock(true).secondPriority(). + Handle(func(ctx *Ctx) { + model := extension.CommandModel{} + _ = ctx.Parse(&model) + service, ok := Lookup(model.Args) + if !ok { + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "没有找到指定服务!")) + return + } + if service.Options.Help != "" { + gid := ctx.Message.Chat.ID + if ctx.Message.Chat.IsPrivate() { + // 个人用户 + gid = -ctx.Message.From.ID + } + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, service.EnableMarkIn(gid).String()+" "+service.String())) + } else { + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, "该服务无帮助!")) + } + }) + + OnMessageCommandGroup([]string{"服务列表", "service_list"}, UserOrGrpAdmin).SetBlock(true).secondPriority(). + Handle(func(ctx *Ctx) { + i := 0 + gid := ctx.Message.Chat.ID + if ctx.Message.Chat.IsPrivate() { + // 个人用户 + gid = -ctx.Message.From.ID + } + m.RLock() + msg := make([]any, 1, len(m.M)*4+1) + m.RUnlock() + msg[0] = "--------服务列表--------\n发送\"/用法 name\"查看详情" + m.ForEach(func(key string, manager *ctrl.Control[*Ctx]) bool { + i++ + msg = append(msg, "\n", i, ": ", manager.EnableMarkIn(gid), key) + return true + }) + _, _ = ctx.Caller.Send(tgba.NewMessage(ctx.Message.Chat.ID, fmt.Sprint(msg...))) + }) + + OnMessageCommandGroup([]string{"服务详情", "service_detail"}, UserOrGrpAdmin).SetBlock(true).secondPriority(). + Handle(func(ctx *Ctx) { + i := 0 + gid := ctx.Message.Chat.ID + if ctx.Message.Chat.IsPrivate() { + // 个人用户 + gid = -ctx.Message.From.ID + } + m.RLock() + msgs := make([]any, 1, len(m.M)*7+1) + m.RUnlock() + msgs[0] = "---服务详情---\n" + m.ForEach(func(key string, service *ctrl.Control[*Ctx]) bool { + i++ + msgs = append(msgs, i, ": ", service.EnableMarkIn(gid), key, "\n", service, "\n\n") + return true + }) + data, err := text.RenderToBase64(fmt.Sprint(msgs...), text.FontFile, 400, 20) + if err != nil { + logrus.Errorf("[control] %v", err) + } + _, _ = ctx.Caller.Send(tgba.NewPhoto(ctx.Message.Chat.ID, tgba.FileBytes{ + Name: "服务详情", + Bytes: data, + })) + }) + }) +} diff --git a/rules.go b/rules.go index ed237d5..4de7fa5 100644 --- a/rules.go +++ b/rules.go @@ -308,6 +308,14 @@ func AdminPermission(ctx *Ctx) bool { return m.IsCreator() || m.IsAdministrator() } +// UserOrGrpAdmin 允许用户单独使用或群管使用 +func UserOrGrpAdmin(ctx *Ctx) bool { + if OnlyPublic(ctx) { + return AdminPermission(ctx) + } + return OnlyToMe(ctx) +} + // IsPhoto 消息是图片返回 true func IsPhoto(ctx *Ctx) bool { msg, ok := ctx.Value.(*tgba.Message)