From 8fb25210dd5c28f41f677516393265eb63f886ab Mon Sep 17 00:00:00 2001 From: shudorcl <1985366171@qq.com> Date: Thu, 18 Sep 2025 16:07:01 +0800 Subject: [PATCH 1/5] update wife MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 增加角色作品名 --- data | 2 +- plugin/wife/main.go | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/data b/data index 328d7638e6..2b10767f59 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 328d7638e6947e8ac1bc8d3f5ecd6a351a4a3b6f +Subproject commit 2b10767f59a1661d81827c42ef9181223b4cfd78 diff --git a/plugin/wife/main.go b/plugin/wife/main.go index a353ee3a01..273109a2b9 100644 --- a/plugin/wife/main.go +++ b/plugin/wife/main.go @@ -3,7 +3,9 @@ package wife import ( "encoding/json" + "fmt" "os" + "regexp" "strings" fcext "github.com/FloatTech/floatbox/ctxext" @@ -51,14 +53,23 @@ func init() { ) return } + re := regexp.MustCompile(`^\[(.*?)\](.*)\..*$`) + match := re.FindStringSubmatch(card) + var msgText string + if len(match) == 3 { + work, name := match[1], match[2] + msgText = fmt.Sprintf("今天的二次元老婆是~来自【%s】的【%s】哒", work, name) + } else { + msgText = fmt.Sprintf("今天的二次元老婆是~【%s】哒", card) + } if id := ctx.SendChain( message.At(ctx.Event.UserID), - message.Text("今天的二次元老婆是~【", card, "】哒"), + message.Text(msgText), message.ImageBytes(data), ); id.ID() == 0 { ctx.SendChain( message.At(ctx.Event.UserID), - message.Text("今天的二次元老婆是~【", card, "】哒\n【图片发送失败, 请联系维护者】"), + message.Text(msgText, "\n【图片发送失败, 多半是被夹了,请联系维护者】"), ) } }) From 7c0d0b4874a9dc7aa01c9e2f11e567fefba34130 Mon Sep 17 00:00:00 2001 From: shudorcl <1985366171@qq.com> Date: Thu, 18 Sep 2025 17:27:38 +0800 Subject: [PATCH 2/5] update wife data & update wife\main.go --- data | 2 +- plugin/wife/main.go | 28 ++++++++++++++++++---------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/data b/data index 2b10767f59..c1878300e9 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 2b10767f59a1661d81827c42ef9181223b4cfd78 +Subproject commit c1878300e9c20a6f5649372d565d8f4868020595 diff --git a/plugin/wife/main.go b/plugin/wife/main.go index 273109a2b9..d460d9b179 100644 --- a/plugin/wife/main.go +++ b/plugin/wife/main.go @@ -17,6 +17,16 @@ import ( "github.com/wdvxdr1123/ZeroBot/message" ) +var re = regexp.MustCompile(`^\[(.*?)\](.*)\..*$`) + +func card2name(card string) (string, string) { + match := re.FindStringSubmatch(card) + if len(match) >= 3 { + return match[1], match[2] + } + return "", "" +} + func init() { engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{ DisableOnDefault: false, @@ -46,22 +56,20 @@ func init() { card := cards[fcext.RandSenderPerDayN(ctx.Event.UserID, len(cards))] data, err := engine.GetLazyData("wives/"+card, true) card, _, _ = strings.Cut(card, ".") + var msgText string + work, name := card2name(card) + if work != "" && name != "" { + msgText = fmt.Sprintf("今天的二次元老婆是~来自【%s】的【%s】哒", work, name) + } else { + msgText = fmt.Sprintf("今天的二次元老婆是~【%s】哒", card) + } if err != nil { ctx.SendChain( message.At(ctx.Event.UserID), - message.Text("今天的二次元老婆是~【", card, "】哒\n【图片下载失败: ", err, "】"), + message.Text(msgText, "\n【图片下载失败: ", err, "】"), ) return } - re := regexp.MustCompile(`^\[(.*?)\](.*)\..*$`) - match := re.FindStringSubmatch(card) - var msgText string - if len(match) == 3 { - work, name := match[1], match[2] - msgText = fmt.Sprintf("今天的二次元老婆是~来自【%s】的【%s】哒", work, name) - } else { - msgText = fmt.Sprintf("今天的二次元老婆是~【%s】哒", card) - } if id := ctx.SendChain( message.At(ctx.Event.UserID), message.Text(msgText), From e5084a3aa177ad080540da9eeb56b8bc415b1b89 Mon Sep 17 00:00:00 2001 From: shudorcl <1985366171@qq.com> Date: Fri, 19 Sep 2025 16:39:35 +0800 Subject: [PATCH 3/5] fix: card2name --- plugin/wife/main.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugin/wife/main.go b/plugin/wife/main.go index d460d9b179..4c06bb0a65 100644 --- a/plugin/wife/main.go +++ b/plugin/wife/main.go @@ -6,7 +6,6 @@ import ( "fmt" "os" "regexp" - "strings" fcext "github.com/FloatTech/floatbox/ctxext" ctrl "github.com/FloatTech/zbpctrl" @@ -55,7 +54,6 @@ func init() { Handle(func(ctx *zero.Ctx) { card := cards[fcext.RandSenderPerDayN(ctx.Event.UserID, len(cards))] data, err := engine.GetLazyData("wives/"+card, true) - card, _, _ = strings.Cut(card, ".") var msgText string work, name := card2name(card) if work != "" && name != "" { From 9067e16c624bd3201edc6c34eb4272b180f4f570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=E6=9F=B3=E7=85=9C?= <101934327+fangliuyu@users.noreply.github.com> Date: Fri, 19 Sep 2025 20:32:47 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E7=94=A8=E8=A0=A2=E5=8A=9E=E6=B3=95?= =?UTF-8?q?=E5=BC=80=E4=B8=AApr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugin/wife/main.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/plugin/wife/main.go b/plugin/wife/main.go index 4c06bb0a65..75bd1e8931 100644 --- a/plugin/wife/main.go +++ b/plugin/wife/main.go @@ -16,7 +16,16 @@ import ( "github.com/wdvxdr1123/ZeroBot/message" ) -var re = regexp.MustCompile(`^\[(.*?)\](.*)\..*$`) +var ( + cards = []string{} + re = regexp.MustCompile(`^\[(.*?)\](.*)\..*$`) + engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{ + DisableOnDefault: false, + Help: "- 抽老婆", + Brief: "从老婆库抽每日老婆", + PublicDataFolder: "Wife", + }).ApplySingle(ctxext.DefaultSingle) +) func card2name(card string) (string, string) { match := re.FindStringSubmatch(card) @@ -27,14 +36,7 @@ func card2name(card string) (string, string) { } func init() { - engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{ - DisableOnDefault: false, - Help: "- 抽老婆", - Brief: "从老婆库抽每日老婆", - PublicDataFolder: "Wife", - }).ApplySingle(ctxext.DefaultSingle) _ = os.MkdirAll(engine.DataFolder()+"wives", 0755) - cards := []string{} engine.OnFullMatch("抽老婆", fcext.DoOnceOnSuccess( func(ctx *zero.Ctx) bool { data, err := engine.GetLazyData("wife.json", true) From 5e503b9581619e769f227fa7e559b52af0345b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=E6=9F=B3=E7=85=9C?= <101934327+fangliuyu@users.noreply.github.com> Date: Fri, 19 Sep 2025 20:35:09 +0800 Subject: [PATCH 5/5] Add guess wife game --- plugin/wife/wifegame.go | 213 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 plugin/wife/wifegame.go diff --git a/plugin/wife/wifegame.go b/plugin/wife/wifegame.go new file mode 100644 index 0000000000..815dfb100f --- /dev/null +++ b/plugin/wife/wifegame.go @@ -0,0 +1,213 @@ +// Package wife 抽老婆 +package wife + +import ( + "errors" + "image/color" + "io/fs" + "math/rand" + "os" + "strings" + "time" + + "github.com/FloatTech/floatbox/file" + "github.com/FloatTech/gg" + ctrl "github.com/FloatTech/zbpctrl" + "github.com/FloatTech/zbputils/control" + "github.com/FloatTech/zbputils/ctxext" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/extension/single" + "github.com/wdvxdr1123/ZeroBot/message" + + zbmath "github.com/FloatTech/floatbox/math" + "github.com/FloatTech/imgfactory" +) + +var ( + sizeList = []int{0, 3, 5, 8} + enguess = control.Register("wifegame", &ctrl.Options[*zero.Ctx]{ + DisableOnDefault: false, + Help: "- 猜老婆", + Brief: "从老婆库猜老婆", + }).ApplySingle(single.New( + single.WithKeyFn(func(ctx *zero.Ctx) int64 { + if ctx.Event.GroupID != 0 { + return ctx.Event.GroupID + } + return -ctx.Event.UserID + }), + single.WithPostFn[int64](func(ctx *zero.Ctx) { + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.Text("已经有正在进行的游戏..."), + ), + ) + }), + )) +) + +func init() { + // _ = os.MkdirAll(engine.DataFolder()+"wives", 0755) + enguess.OnFullMatch("猜老婆").SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + var err error + class := 3 + + fileName, err := lottery() + if err != nil { + ctx.SendChain(message.Text("[猜老婆]error:\n", err)) + return + } + + work, name := card2name(fileName) + picFile := file.BOTPATH + "/" + engine.DataFolder() + "wives/" + fileName + pic, err := os.ReadFile(picFile) + if err != nil { + ctx.SendChain(message.Text("[猜老婆]error:\n", err)) + return + } + img, err := gg.LoadImage(picFile) + if err != nil { + ctx.SendChain(message.Text("[猜老婆]error:\n", err)) + return + } + dst := imgfactory.Size(img, img.Bounds().Dx(), img.Bounds().Dy()) + q, err := mosaic(dst, class) + if err != nil { + ctx.SendChain( + message.Reply(ctx.Event.MessageID), + message.Text("[猜老婆]图片生成失败:\n", err), + ) + return + } + if id := ctx.SendChain( + message.ImageBytes(q), + ); id.ID() != 0 { + ctx.SendChain(message.Text("请回答该二次元角色名字\n以“xxx酱”格式回答")) + } + var next *zero.FutureEvent + if ctx.Event.GroupID == 0 { + next = zero.NewFutureEvent("message", 999, false, zero.RegexRule(`(·)?.+酱$`), ctx.CheckSession()) + } else { + next = zero.NewFutureEvent("message", 999, false, zero.RegexRule(`(·)?.+酱$`), zero.CheckGroup(ctx.Event.GroupID)) + } + recv, cancel := next.Repeat() + defer cancel() + tick := time.NewTimer(105 * time.Second) + after := time.NewTimer(120 * time.Second) + for { + select { + case <-tick.C: + ctx.SendChain(message.Text("[猜老婆]你还有15s作答时间")) + case <-after.C: + ctx.Send( + message.ReplyWithMessage(ctx.Event.MessageID, + message.ImageBytes(pic), + message.Text("[猜老婆]倒计时结束,游戏结束...\n角色是:\n", name, "\n出自《", work, "》\n"), + ), + ) + return + case c := <-recv: + tick.Reset(105 * time.Second) + after.Reset(120 * time.Second) + msg := c.Event.Message.String() + msg, _, _ = strings.Cut(msg, "酱") + class-- + if strings.Contains(name, msg) { + if msgID := ctx.Send(message.ReplyWithMessage(c.Event.MessageID, + message.Text("太棒了,你猜对了!\n角色是:\n", name, "\n出自《", work, "》\n"), + message.ImageBytes(pic))); msgID.ID() == 0 { + ctx.SendChain(message.Text("太棒了,你猜对了!\n图片发送失败,可能被风控\n角色是:\n", name, "\n出自《", work, "》")) + } + return + } + if class < 1 { + if msgID := ctx.Send(message.ReplyWithMessage(c.Event.MessageID, + message.Text("很遗憾,次数到了,游戏结束!\n角色是:\n", name, "\n出自《", work, "》\n"), + message.ImageBytes(pic))); msgID.ID() == 0 { + ctx.SendChain(message.Text("很遗憾,次数到了,游戏结束!\n图片发送失败,可能被风控\n角色是:\n", name, "\n出自《", work, "》")) + } + return + } + q, err = mosaic(dst, class) + if err != nil { + ctx.SendChain( + message.Text("回答错误,你还有", class, "次机会\n请继续作答\n(提示:", work, ")"), + ) + continue + } + ctx.SendChain( + message.Text("回答错误,你还有", class, "次机会\n请继续作答(难度降低)\n"), + message.ImageBytes(q), + ) + continue + } + } + }) +} + +// 从本地图库随机抽取,规避网络问题 +func lottery() (fileName string, err error) { + path := engine.DataFolder() + "wives" + "/" + if file.IsNotExist(path) { + err = errors.New("图库文件夹不存在,请先发送“抽老婆”扩展图库") + return + } + files, err := os.ReadDir(path) + if err != nil { + return + } + // 如果本地列表为空 + if len(files) == 0 { + err = errors.New("本地数据为0,请先发送“抽老婆”扩展图库") + return + } + fileName = randPicture(files, 10) + if fileName == "" { + err = errors.New("抽取图库轮空了,请重试") + } + return +} + +func randPicture(files []fs.DirEntry, indexMax int) (fileName string) { + if len(files) > 1 { + picture := files[rand.Intn(len(files))] + // 如果是文件夹就递归 + if picture.IsDir() { + indexMax-- + if indexMax <= 0 { + return + } + fileName = randPicture(files, indexMax) + } else { + fileName = picture.Name() + } + } else { + music := files[0] + if !music.IsDir() { + fileName = files[0].Name() + } + } + return +} + +// 马赛克生成 +func mosaic(dst *imgfactory.Factory, level int) ([]byte, error) { + b := dst.Image().Bounds() + p := imgfactory.NewFactoryBG(dst.W(), dst.H(), color.NRGBA{255, 255, 255, 255}) + markSize := zbmath.Max(b.Max.X, b.Max.Y) * sizeList[level] / 200 + + for yOfMarknum := 0; yOfMarknum <= zbmath.Ceil(b.Max.Y, markSize); yOfMarknum++ { + for xOfMarknum := 0; xOfMarknum <= zbmath.Ceil(b.Max.X, markSize); xOfMarknum++ { + a := dst.Image().At(xOfMarknum*markSize+markSize/2, yOfMarknum*markSize+markSize/2) + cc := color.NRGBAModel.Convert(a).(color.NRGBA) + for y := 0; y < markSize; y++ { + for x := 0; x < markSize; x++ { + xOfPic := xOfMarknum*markSize + x + yOfPic := yOfMarknum*markSize + y + p.Image().Set(xOfPic, yOfPic, cc) + } + } + } + } + return imgfactory.ToBytes(p.Blur(3).Image()) +}