From e2c603338ef98a27da887296bb538418d25c9970 Mon Sep 17 00:00:00 2001 From: fumiama Date: Thu, 16 Dec 2021 15:27:47 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20=20drop=20all=20pb=20&=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20gist=20=E5=AE=A1=E6=89=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- go.mod | 2 - go.sum | 2 - plugin_diana/data/migrate/text.go | 68 --------- plugin_manager/gist.go | 43 ++++++ plugin_manager/manager.db.go | 12 ++ plugin_manager/manager.go | 235 ++++++++++++++++-------------- plugin_manager/manager.pb.go | 6 - plugin_manager/timer/timer.go | 10 +- utils/math/math.go | 8 + utils/web/http.go | 16 ++ 11 files changed, 209 insertions(+), 196 deletions(-) delete mode 100644 plugin_diana/data/migrate/text.go create mode 100644 plugin_manager/gist.go create mode 100644 plugin_manager/manager.db.go delete mode 100644 plugin_manager/manager.pb.go diff --git a/README.md b/README.md index 805efef3..b37d3b82 100644 --- a/README.md +++ b/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] diff --git a/go.mod b/go.mod index b05ffa2b..1d5cbf71 100644 --- a/go.mod +++ b/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 @@ -24,6 +23,5 @@ require ( github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 github.com/tidwall/gjson v1.12.1 github.com/wdvxdr1123/ZeroBot v1.4.1 - github.com/yusufpapurcu/wmi v1.2.2 // indirect golang.org/x/image v0.0.0-20211028202545-6944b10bf410 ) diff --git a/go.sum b/go.sum index 8580fc21..9e44f5df 100644 --- a/go.sum +++ b/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= diff --git a/plugin_diana/data/migrate/text.go b/plugin_diana/data/migrate/text.go deleted file mode 100644 index 87aa3073..00000000 --- a/plugin_diana/data/migrate/text.go +++ /dev/null @@ -1,68 +0,0 @@ -package main - -import ( - "crypto/md5" - "encoding/binary" - "fmt" - "os" - - "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(binary.LittleEndian.Uint64(s[:8])) - 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) -} diff --git a/plugin_manager/gist.go b/plugin_manager/gist.go new file mode 100644 index 00000000..79957958 --- /dev/null +++ b/plugin_manager/gist.go @@ -0,0 +1,43 @@ +package manager + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "strconv" + "time" + + "github.com/FloatTech/ZeroBot-Plugin/utils/math" + "github.com/FloatTech/ZeroBot-Plugin/utils/web" + "github.com/sirupsen/logrus" + "github.com/wdvxdr1123/ZeroBot/utils/helper" +) + +// 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() +} diff --git a/plugin_manager/manager.db.go b/plugin_manager/manager.db.go new file mode 100644 index 00000000..e602d546 --- /dev/null +++ b/plugin_manager/manager.db.go @@ -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"` +} diff --git a/plugin_manager/manager.go b/plugin_manager/manager.go index f9775ab4..d146e831 100644 --- a/plugin_manager/manager.go +++ b/plugin_manager/manager.go @@ -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). @@ -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 -} diff --git a/plugin_manager/manager.pb.go b/plugin_manager/manager.pb.go deleted file mode 100644 index 169f69f8..00000000 --- a/plugin_manager/manager.pb.go +++ /dev/null @@ -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"` -} diff --git a/plugin_manager/timer/timer.go b/plugin_manager/timer/timer.go index 9d6cf377..d9bffdde 100644 --- a/plugin_manager/timer/timer.go +++ b/plugin_manager/timer/timer.go @@ -37,8 +37,8 @@ var ( } ) -func NewClock(dbfile string) (c Clock) { - c.loadTimers(dbfile) +func NewClock(db *sql.Sqlite) (c Clock) { + c.loadTimers(db) c.cron = cron.New() c.entries = make(map[uint32]cron.EntryID) c.cron.Start() @@ -167,9 +167,9 @@ func (c *Clock) AddTimer(t *Timer) (err error) { return } -func (c *Clock) loadTimers(dbfile string) { - if file.IsExist(dbfile) { - c.db.DBPath = dbfile +func (c *Clock) loadTimers(db *sql.Sqlite) { + if file.IsExist(db.DBPath) { + c.db = db err := c.db.Create("timer", &Timer{}) if err == nil { var t Timer diff --git a/utils/math/math.go b/utils/math/math.go index ecb78445..abe6b0a3 100644 --- a/utils/math/math.go +++ b/utils/math/math.go @@ -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 +} diff --git a/utils/web/http.go b/utils/web/http.go index 8ba52b62..57e26084 100644 --- a/utils/web/http.go +++ b/utils/web/http.go @@ -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 +}