diff --git a/README.md b/README.md index 17f19503..8124aecb 100644 --- a/README.md +++ b/README.md @@ -619,15 +619,9 @@ print("run[CQ:image,file="+j["img"]+"]") `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/drift_bottle"` - - [x] (在群xxx)丢漂流瓶(到频道xxx) [消息] + - [x] @Bot pick (随机捞一个漂流瓶) - - [x] (从频道xxx)捡漂流瓶 - - - [x] @BOT 创建频道 xxx - - - [x] 跳入(频道)海中 - - - [x] 注:不显式限制时,私聊发送可在所有群抽到,群聊发送仅可在本群抽到,默认频道为 global + - [x] @Bot throw xxx (投递内容xxx,支持图片文字,投递内容需要大于10个字符或者带有图片)
diff --git a/plugin/drift_bottle/data.go b/plugin/drift_bottle/data.go deleted file mode 100644 index 21535975..00000000 --- a/plugin/drift_bottle/data.go +++ /dev/null @@ -1,53 +0,0 @@ -package driftbottle - -import ( - "fmt" - "hash/crc64" - "strconv" - "sync" - - "github.com/FloatTech/floatbox/binary" - sql "github.com/FloatTech/sqlite" -) - -type bottle struct { - ID int64 `db:"id"` // ID qq_grp_name_msg 的 crc64 - QQ int64 `db:"qq"` // QQ 发送者 qq - Grp int64 `db:"grp"` // Grp 限制抽出的群 / 人(负数) - Name string `db:"name"` // Name 发送者 昵称 - Msg string `db:"msg"` // Msg 消息,纯文本 -} - -var sea = &sql.Sqlite{} -var seamu sync.RWMutex - -func newBottle(qq, grp int64, name, msg string) *bottle { - id := int64(crc64.Checksum(binary.StringToBytes(fmt.Sprintf("%d_%d_%s_%s", qq, grp, name, msg)), crc64.MakeTable(crc64.ISO))) - return &bottle{ID: id, QQ: qq, Grp: grp, Name: name, Msg: msg} -} - -func (b *bottle) throw(db *sql.Sqlite, channel string) error { - seamu.Lock() - defer seamu.Unlock() - return db.Insert(channel, b) -} - -func (b *bottle) destroy(db *sql.Sqlite, channel string) error { - seamu.Lock() - defer seamu.Unlock() - return db.Del(channel, "WHERE id="+strconv.FormatInt(b.ID, 10)) -} - -// fetchBottle grp != 0 -func fetchBottle(db *sql.Sqlite, channel string, grp int64) (*bottle, error) { - seamu.RLock() - defer seamu.RUnlock() - b := new(bottle) - return b, db.Find(channel, b, "WHERE grp=0 or grp="+strconv.FormatInt(grp, 10)+" ORDER BY RANDOM() limit 1") -} - -func createChannel(db *sql.Sqlite, channel string) error { - seamu.Lock() - defer seamu.Unlock() - return db.Create(channel, &bottle{}) -} diff --git a/plugin/drift_bottle/main.go b/plugin/drift_bottle/main.go index c9c53530..a37da47f 100644 --- a/plugin/drift_bottle/main.go +++ b/plugin/drift_bottle/main.go @@ -2,132 +2,107 @@ package driftbottle import ( + "fmt" + "hash/crc64" "strconv" - "strings" "sync" "time" + "unicode/utf8" + "github.com/FloatTech/floatbox/binary" + sql "github.com/FloatTech/sqlite" ctrl "github.com/FloatTech/zbpctrl" "github.com/FloatTech/zbputils/control" - "github.com/sirupsen/logrus" + "github.com/FloatTech/zbputils/ctxext" zero "github.com/wdvxdr1123/ZeroBot" "github.com/wdvxdr1123/ZeroBot/message" ) +type sea struct { + ID int64 `db:"id"` // ID qq_grp_name_msg 的 crc64 hashCheck. + QQ int64 `db:"qq"` // Get current user(Who sends this) + Name string `db:"Name"` // his or her name at that time:P + Msg string `db:"msg"` // What he or she sent to bot? + Grp int64 `db:"grp"` // which group sends this msg? + Time string `db:"time"` // we need to know the current time,master> +} + +var seaSide = &sql.Sqlite{} +var seaLocker sync.RWMutex + +// We need a container to inject what we need :( + func init() { en := control.Register("driftbottle", &ctrl.Options[*zero.Ctx]{ DisableOnDefault: false, - Help: "漂流瓶\n- (在群xxx)丢漂流瓶(到频道xxx) [消息]\n- (从频道xxx)捡漂流瓶\n- @BOT 创建频道 xxx\n- 跳入(频道)海中\n- 注:不显式限制时,私聊发送可在所有群抽到,群聊发送仅可在本群抽到,默认频道为 global", + Help: "简单的漂流瓶\n" + "- @bot pick" + "- @bot throw xxx (xxx为投递内容)", PrivateDataFolder: "driftbottle", }) - sea.DBPath = en.DataFolder() + "sea.db" - err := sea.Open(time.Hour * 24) + seaSide.DBPath = en.DataFolder() + "sea.db" + err := seaSide.Open(time.Hour * 24) if err != nil { panic(err) } - _ = createChannel(sea, "global") - en.OnRegex(`^(在群\d+)?丢漂流瓶(到频道\w+)?\s+(.*)$`).SetBlock(true). - Handle(func(ctx *zero.Ctx) { - msgs := ctx.State["regex_matched"].([]string) - grp := ctx.Event.GroupID - channel := "global" - msg := msgs[3] - var err error - if msgs[1] != "" { - grp, err = strconv.ParseInt(msgs[1][6:], 10, 64) - if err != nil { - ctx.SendChain(message.Text("群号非法!")) - return - } - } - if msgs[2] != "" { - channel = msgs[2][9:] - } - if msg == "" { - ctx.SendChain(message.Text("消息为空!")) - return - } - logrus.Debugln("[driftbottle]", grp, channel, msg) - err = newBottle( - ctx.Event.UserID, - grp, - ctx.CardOrNickName(ctx.Event.UserID), - msg, - ).throw(sea, channel) - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("你将它扔进大海,希望有人捞到吧~"))) - }) - en.OnRegex(`^(从频道\w+)?捡漂流瓶$`).SetBlock(true). - Handle(func(ctx *zero.Ctx) { - msgs := ctx.State["regex_matched"].([]string) - grp := ctx.Event.GroupID - if grp == 0 { - grp = -ctx.Event.UserID - } - if grp == 0 { - ctx.SendChain(message.Text("找不到对象!")) - return - } - channel := "global" - if msgs[1] != "" { - channel = msgs[1][9:] - } - logrus.Debugln("[driftbottle]", grp, channel) - b, err := fetchBottle(sea, channel, grp) - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - var wg sync.WaitGroup - wg.Add(1) - go func() { - err = b.destroy(sea, channel) - wg.Done() - }() - ctx.Send( - message.ReplyWithMessage( - ctx.Event.MessageID, - message.Text("你在海边捡到了一个来自 ", b.Name, " 的漂流瓶,打开瓶子,里面有一张纸条,写着:"), - message.Text(b.Msg), - ), - ) - wg.Wait() - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - }) - en.OnPrefix("创建频道", zero.SuperUserPermission, zero.OnlyToMe).SetBlock(true). - Handle(func(ctx *zero.Ctx) { - channel := strings.TrimRight(ctx.State["args"].(string), " ") - if channel == "" { - ctx.SendChain(message.Text("频道名为空!")) - return - } - err := createChannel(sea, channel) - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("成功~"))) - }) - en.OnRegex(`^跳入(\w+)?海中$`).SetBlock(true). - Handle(func(ctx *zero.Ctx) { - msgs := ctx.State["regex_matched"].([]string) - channel := "global" - if msgs[1] != "" { - channel = msgs[1] - } - seamu.RLock() - c, err := sea.Count(channel) - seamu.RUnlock() - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("你缓缓走入大海,感受着海浪轻柔地拍打着你的小腿,膝盖……\n波浪卷着你的腰腹,你感觉有些把握不住平衡了……\n……\n你沉入海中,", c, " 个物体与你一同沉浮。\n不知何处涌来一股暗流,你失去了意识。"))) - }) + + _ = createChannel(seaSide) + en.OnFullMatch("pick", zero.OnlyToMe, zero.OnlyGroup).Limit(ctxext.LimitByGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { + be, err := fetchBottle(seaSide) + if err != nil { + ctx.SendChain(message.Text("ERR:", err)) + } + idstr := strconv.Itoa(int(be.ID)) + qqstr := strconv.Itoa(int(be.QQ)) + grpstr := strconv.Itoa(int(be.Grp)) + botname := zero.BotConfig.NickName[0] + msg := message.Message{message.CustomNode(botname, ctx.Event.SelfID, botname+"试着帮你捞出来了这个~\nID:"+idstr+"\n投递人: "+be.Name+"("+qqstr+")"+"\n群号: "+grpstr+"\n时间: "+be.Time+"\n内容: \n"+be.Msg)} + ctx.Send(msg) + }) + + en.OnRegex(`throw.*?(.*)`, zero.OnlyToMe, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { + senderFormatTime := time.Unix(ctx.Event.Time, 0).Format("2006-01-02 15:04:05") + rawSenderMessage := ctx.State["regex_matched"].([]string)[1] + rawMessageCallBack := message.UnescapeCQCodeText(rawSenderMessage) + keyWordsNum := utf8.RuneCountInString(rawMessageCallBack) + if keyWordsNum < 10 { + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("需要投递的内容过少( ")) + return + } + // check current needs and prepare to throw drift_bottle. + err = globalbottle( + ctx.Event.UserID, + ctx.Event.GroupID, + senderFormatTime, + ctx.CardOrNickName(ctx.Event.UserID), + rawMessageCallBack, + ).throw(seaSide) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("已经帮你丢出去了哦~"))) + }) +} + +func globalbottle(qq, grp int64, time, name, msg string) *sea { // Check as if the User is available and collect information to store. + id := int64(crc64.Checksum(binary.StringToBytes(fmt.Sprintf("%d_%d_%s_%s_%s", grp, qq, time, name, msg)), crc64.MakeTable(crc64.ISO))) + return &sea{ID: id, Grp: grp, Time: time, QQ: qq, Name: name, Msg: msg} +} + +func (be *sea) throw(db *sql.Sqlite) error { + seaLocker.Lock() + defer seaLocker.Unlock() + return db.Insert("global", be) +} + +func fetchBottle(db *sql.Sqlite) (*sea, error) { + seaLocker.Lock() + defer seaLocker.Unlock() + be := new(sea) + return be, db.Pick("global", be) +} + +func createChannel(db *sql.Sqlite) error { + seaLocker.Lock() + defer seaLocker.Unlock() + return db.Create("global", &sea{}) } diff --git a/plugin/tarot/tarot.go b/plugin/tarot/tarot.go index 29996fe5..4e41157b 100644 --- a/plugin/tarot/tarot.go +++ b/plugin/tarot/tarot.go @@ -62,7 +62,11 @@ func init() { }).ApplySingle(ctxext.DefaultSingle) cache := engine.DataFolder() + "cache" - _ = os.MkdirAll(cache, 0755) + _ = os.RemoveAll(cache) + err := os.MkdirAll(cache, 0755) + if err != nil { + panic(err) + } getTarot := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { data, err := engine.GetLazyData("tarots.json", true) @@ -142,19 +146,18 @@ func init() { } imgurl := bed + reverse[p] + card.ImgURL imgname := "" - imgpath := cache + "/" + imgname + ".png" if p == 1 { - imgname = reverse[p][:len(reverse[p])-1] + card.Name + imgname = reverse[p][:len(reverse[p])-1] + name } else { - imgname = card.Name + imgname = name } - err := pool.SendImageFromPool(imgname, imgpath, func() error { + imgpath := cache + "/" + imgname + ".png" + err := pool.SendImageFromPool("pool"+imgname, imgpath, func() error { data, err := web.RequestDataWith(web.NewTLS12Client(), imgurl, "GET", "gitcode.net", web.RandUA()) if err != nil { return err } - var f *os.File - f, err = os.Create(imgpath) + f, err := os.Create(imgpath) if err != nil { return err } @@ -191,9 +194,9 @@ func init() { var imgmsg message.MessageSegment var err error if p == 1 { - imgmsg, err = poolimg(ctx, imgurl, reverse[p][:len(reverse[p])-1]+card.Name, cache) + imgmsg, err = poolimg(ctx, imgurl, reverse[p][:len(reverse[p])-1]+name, cache) } else { - imgmsg, err = poolimg(ctx, imgurl, card.Name, cache) + imgmsg, err = poolimg(ctx, imgurl, name, cache) } if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) @@ -285,9 +288,9 @@ func init() { var imgmsg message.MessageSegment var err error if p == 1 { - imgmsg, err = poolimg(ctx, imgurl, reverse[p][:len(reverse[p])-1]+card.Name, cache) + imgmsg, err = poolimg(ctx, imgurl, reverse[p][:len(reverse[p])-1]+name, cache) } else { - imgmsg, err = poolimg(ctx, imgurl, card.Name, cache) + imgmsg, err = poolimg(ctx, imgurl, name, cache) } if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) @@ -323,7 +326,7 @@ func init() { func poolimg(ctx *zero.Ctx, imgurl, imgname, cache string) (msg message.MessageSegment, err error) { imgfile := cache + "/" + imgname + ".png" aimgfile := file.BOTPATH + "/" + imgfile - m, err := pool.GetImage(imgname) + m, err := pool.GetImage("pool" + imgname) if err == nil { msg = message.Image(m.String()) if ctxext.SendToSelf(ctx)(msg) == 0 {