From 587d3967ef71d5cd8dafa0671958339cb5d02f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=B2=E5=AE=9D=E5=9D=8F=E5=9D=8F=E5=9D=8F?= <2758988938@qq.com> Date: Sat, 25 Mar 2023 11:06:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=AD=BE=E5=88=B0=E9=A2=84?= =?UTF-8?q?=E8=AE=BE=E5=88=87=E6=8D=A2=20(#634)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugin/score/draw.go | 176 +++++++++++++++++++++++ plugin/score/model.go | 11 ++ plugin/score/sign_in.go | 299 +++++++++++++++++----------------------- 3 files changed, 317 insertions(+), 169 deletions(-) create mode 100644 plugin/score/draw.go diff --git a/plugin/score/draw.go b/plugin/score/draw.go new file mode 100644 index 0000000000..33ae9f3da5 --- /dev/null +++ b/plugin/score/draw.go @@ -0,0 +1,176 @@ +// Package score 签到,答题得分 +package score + +import ( + "bytes" + "fmt" + "image" + "image/color" + "strconv" + "time" + + "github.com/FloatTech/ZeroBot-Plugin/kanban/banner" + "github.com/FloatTech/floatbox/file" + "github.com/FloatTech/gg" + "github.com/FloatTech/imgfactory" + "github.com/FloatTech/zbputils/control" + "github.com/FloatTech/zbputils/img/text" + "github.com/disintegration/imaging" +) + +func drawScore16(a *scdata) (image.Image, error) { + // 绘图 + getAvatar, err := initPic(a.picfile, a.uid) + if err != nil { + return nil, err + } + back, err := gg.LoadImage(a.picfile) + if err != nil { + return nil, err + } + // 避免图片过大,最大 1280*720 + back = imgfactory.Limit(back, 1280, 720) + imgDX := back.Bounds().Dx() + imgDY := back.Bounds().Dy() + canvas := gg.NewContext(imgDX, imgDY) + // draw Aero Style + aeroStyle := gg.NewContext(imgDX-202, imgDY-202) + aeroStyle.DrawImage(imaging.Blur(back, 2.5), -100, -100) + // aero draw image. + aeroStyle.DrawRoundedRectangle(0, 0, float64(imgDX-200), float64(imgDY-200), 16) + // SideLine + aeroStyle.SetLineWidth(3) + aeroStyle.SetRGBA255(255, 255, 255, 100) + aeroStyle.StrokePreserve() + aeroStyle.SetRGBA255(255, 255, 255, 140) + // fill + aeroStyle.Fill() + // draw background + canvas.DrawImage(back, 0, 0) + // Aero style combine + canvas.DrawImage(aeroStyle.Image(), 100, 100) + canvas.Fill() + hourWord := getHourWord(time.Now()) + avatar, _, err := image.Decode(bytes.NewReader(getAvatar)) + if err != nil { + return nil, err + } + avatarf := imgfactory.Size(avatar, 200, 200) + canvas.DrawImage(avatarf.Circle(0).Image(), 120, 120) + // draw info(name,coin,etc) + canvas.SetRGB255(0, 0, 0) + data, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true) + if err != nil { + return nil, err + } + if err = canvas.ParseFontFace(data, 50); err != nil { + return nil, err + } + // draw head + canvas.DrawStringWrapped(a.nickname, 350, 180, 0.5, 0.5, 0.5, 0.5, gg.AlignLeft) + canvas.Fill() + // main draw + data, err = file.GetLazyData(text.FontFile, control.Md5File, true) + if err != nil { + return nil, err + } + if err = canvas.ParseFontFace(data, 30); err != nil { + return nil, err + } + canvas.DrawStringAnchored(hourWord, 350, 280, 0, 0) + canvas.DrawStringAnchored("ATRI币 + "+strconv.Itoa(a.inc), 350, 350, 0, 0) + canvas.DrawStringAnchored("当前ATRI币:"+strconv.Itoa(a.score), 350, 400, 0, 0) + canvas.DrawStringAnchored("LEVEL: "+strconv.Itoa(getrank(a.level)), 350, 450, 0, 0) + // draw Info(Time,etc.) + getTime := time.Now().Format("2006-01-02 15:04:05") + getTimeLengthWidth, getTimeLengthHight := canvas.MeasureString(getTime) + canvas.DrawStringAnchored(getTime, float64(imgDX)-100-20-getTimeLengthWidth/2, float64(imgDY)-100-getTimeLengthHight, 0.5, 0.5) // time + var nextrankScore int + if a.rank < 10 { + nextrankScore = rankArray[a.rank+1] + } else { + nextrankScore = SCOREMAX + } + nextLevelStyle := strconv.Itoa(a.level) + "/" + strconv.Itoa(nextrankScore) + getLevelLength, _ := canvas.MeasureString(nextLevelStyle) + canvas.DrawStringAnchored(nextLevelStyle, 100+getLevelLength, float64(imgDY)-100-getTimeLengthHight, 0.5, 0.5) // time + canvas.Fill() + canvas.SetRGB255(255, 255, 255) + if err = canvas.ParseFontFace(data, 20); err != nil { + return nil, err + } + canvas.DrawStringAnchored("Created By Zerobot-Plugin "+banner.Version, float64(imgDX)/2, float64(imgDY)-20, 0.5, 0.5) // zbp + canvas.SetRGB255(0, 0, 0) + canvas.DrawStringAnchored("Created By Zerobot-Plugin "+banner.Version, float64(imgDX)/2-3, float64(imgDY)-19, 0.5, 0.5) // zbp + canvas.SetRGB255(255, 255, 255) + // Gradient + grad := gg.NewLinearGradient(20, 320, 400, 20) + grad.AddColorStop(0, color.RGBA{G: 255, A: 255}) + grad.AddColorStop(1, color.RGBA{B: 255, A: 255}) + grad.AddColorStop(0.5, color.RGBA{R: 255, A: 255}) + canvas.SetStrokeStyle(grad) + canvas.SetLineWidth(4) + // level array with rectangle work. + gradLineLength := float64(imgDX-120) - 120 + renderLine := (float64(a.level) / float64(nextrankScore)) * gradLineLength + canvas.MoveTo(120, float64(imgDY)-102) + canvas.LineTo(120+renderLine, float64(imgDY)-102) + canvas.ClosePath() + canvas.Stroke() + return canvas.Image(), nil +} + +func drawScore15(a *scdata) (image.Image, error) { + // 绘图 + _, err := initPic(a.picfile, a.uid) + if err != nil { + return nil, err + } + back, err := gg.LoadImage(a.picfile) + if err != nil { + return nil, err + } + // 避免图片过大,最大 1280*720 + back = imgfactory.Limit(back, 1280, 720) + canvas := gg.NewContext(back.Bounds().Size().X, int(float64(back.Bounds().Size().Y)*1.7)) + canvas.SetRGB(1, 1, 1) + canvas.Clear() + canvas.DrawImage(back, 0, 0) + monthWord := time.Now().Format("01/02") + hourWord := getHourWord(time.Now()) + _, err = file.GetLazyData(text.BoldFontFile, control.Md5File, true) + if err != nil { + return nil, err + } + if err = canvas.LoadFontFace(text.BoldFontFile, float64(back.Bounds().Size().X)*0.1); err != nil { + return nil, err + } + canvas.SetRGB(0, 0, 0) + canvas.DrawString(hourWord, float64(back.Bounds().Size().X)*0.1, float64(back.Bounds().Size().Y)*1.2) + canvas.DrawString(monthWord, float64(back.Bounds().Size().X)*0.6, float64(back.Bounds().Size().Y)*1.2) + _, err = file.GetLazyData(text.FontFile, control.Md5File, true) + if err != nil { + return nil, err + } + if err = canvas.LoadFontFace(text.FontFile, float64(back.Bounds().Size().X)*0.04); err != nil { + return nil, err + } + canvas.DrawString(a.nickname+fmt.Sprintf(" ATRI币+%d", a.inc), float64(back.Bounds().Size().X)*0.1, float64(back.Bounds().Size().Y)*1.3) + canvas.DrawString("当前ATRI币:"+strconv.FormatInt(int64(a.score), 10), float64(back.Bounds().Size().X)*0.1, float64(back.Bounds().Size().Y)*1.4) + canvas.DrawString("LEVEL:"+strconv.FormatInt(int64(a.rank), 10), float64(back.Bounds().Size().X)*0.1, float64(back.Bounds().Size().Y)*1.5) + canvas.DrawRectangle(float64(back.Bounds().Size().X)*0.1, float64(back.Bounds().Size().Y)*1.55, float64(back.Bounds().Size().X)*0.6, float64(back.Bounds().Size().Y)*0.1) + canvas.SetRGB255(150, 150, 150) + canvas.Fill() + var nextrankScore int + if a.rank < 10 { + nextrankScore = rankArray[a.rank+1] + } else { + nextrankScore = SCOREMAX + } + canvas.SetRGB255(0, 0, 0) + canvas.DrawRectangle(float64(back.Bounds().Size().X)*0.1, float64(back.Bounds().Size().Y)*1.55, float64(back.Bounds().Size().X)*0.6*float64(a.level)/float64(nextrankScore), float64(back.Bounds().Size().Y)*0.1) + canvas.SetRGB255(102, 102, 102) + canvas.Fill() + canvas.DrawString(fmt.Sprintf("%d/%d", a.level, nextrankScore), float64(back.Bounds().Size().X)*0.75, float64(back.Bounds().Size().Y)*1.62) + return canvas.Image(), nil +} diff --git a/plugin/score/model.go b/plugin/score/model.go index 65145fbc2d..4c5ab6ef57 100644 --- a/plugin/score/model.go +++ b/plugin/score/model.go @@ -122,3 +122,14 @@ func (sdb *scoredb) GetScoreRankByTopN(n int) (st []scoretable, err error) { err = db.Model(&scoretable{}).Order("score desc").Limit(n).Find(&st).Error return } + +type scdata struct { + drawedfile string + picfile string + uid int64 + nickname string + inc int //增加币 + score int //钱包 + level int + rank int +} diff --git a/plugin/score/sign_in.go b/plugin/score/sign_in.go index 1b9aecb493..44974dbe85 100644 --- a/plugin/score/sign_in.go +++ b/plugin/score/sign_in.go @@ -2,25 +2,19 @@ package score import ( - "bytes" "image" - "image/color" "math" "math/rand" "os" "strconv" "time" - "github.com/disintegration/imaging" - - "github.com/FloatTech/ZeroBot-Plugin/kanban/banner" - "github.com/FloatTech/AnimeAPI/bilibili" "github.com/FloatTech/AnimeAPI/wallet" + fcext "github.com/FloatTech/floatbox/ctxext" "github.com/FloatTech/floatbox/file" "github.com/FloatTech/floatbox/process" "github.com/FloatTech/floatbox/web" - "github.com/FloatTech/gg" "github.com/FloatTech/imgfactory" ctrl "github.com/FloatTech/zbpctrl" "github.com/FloatTech/zbputils/control" @@ -37,7 +31,8 @@ const ( referer = "https://weibo.com/" signinMax = 1 // SCOREMAX 分数上限定为1200 - SCOREMAX = 1200 + SCOREMAX = 1200 + defKeyID int64 = -6 ) var ( @@ -48,6 +43,17 @@ var ( Help: "- 签到\n- 获得签到背景[@xxx] | 获得签到背景\n- 查看等级排名\n注:为跨群排名\n- 查看我的钱包\n- 查看钱包排名\n注:为本群排行,若群人数太多不建议使用该功能!!!", PrivateDataFolder: "score", }) + initDef = fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { + var defkey string + m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) + _ = m.Manager.Response(defKeyID) + _ = m.Manager.GetExtra(defKeyID, &defkey) + if defkey == "" { + _ = m.Manager.SetExtra(defKeyID, "1") + return true + } + return true + }) ) func init() { @@ -60,185 +66,119 @@ func init() { } sdb = initialize(engine.DataFolder() + "score.db") }() - engine.OnFullMatch("签到").Limit(ctxext.LimitByUser).SetBlock(true). - Handle(func(ctx *zero.Ctx) { - uid := ctx.Event.UserID - now := time.Now() - today := now.Format("20060102") - // 签到图片 - drawedFile := cachePath + strconv.FormatInt(uid, 10) + today + "signin.png" - picFile := cachePath + strconv.FormatInt(uid, 10) + today + ".png" - // 获取签到时间 - si := sdb.GetSignInByUID(uid) - siUpdateTimeStr := si.UpdatedAt.Format("20060102") - switch { - case si.Count >= signinMax && siUpdateTimeStr == today: - // 如果签到时间是今天 - ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("今天你已经签到过了!")) - if file.IsExist(drawedFile) { - ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile)) - } - return - case siUpdateTimeStr != today: - // 如果是跨天签到就清数据 - err := sdb.InsertOrUpdateSignInCountByUID(uid, 0) - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - } - // 更新签到次数 - err := sdb.InsertOrUpdateSignInCountByUID(uid, si.Count+1) - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - // 更新经验 - level := sdb.GetScoreByUID(uid).Score + 1 - if level > SCOREMAX { - level = SCOREMAX - ctx.SendChain(message.At(uid), message.Text("你的等级已经达到上限")) - } - err = sdb.InsertOrUpdateScoreByUID(uid, level) - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - // 更新钱包 - rank := getrank(level) - add := 1 + rand.Intn(10) + rank*5 // 等级越高获得的钱越高 - err = wallet.InsertWalletOf(uid, add) - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - score := wallet.GetWalletOf(uid) - // 绘图 - getAvatar, err := initPic(picFile, uid) - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return + engine.OnRegex(`^签到\s?(\d*)$`, initDef).Limit(ctxext.LimitByUser).SetBlock(true).Handle(func(ctx *zero.Ctx) { + //选择key + var key string + gid := ctx.Event.GroupID + if gid < 0 { + // 个人用户设为负数 + gid = -ctx.Event.UserID + } + if ctx.State["regex_matched"].([]string)[1] != "" { + key = ctx.State["regex_matched"].([]string)[1] + } else { + m := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]) + _ = m.Manager.GetExtra(gid, &key) + if key == "" { + _ = m.Manager.GetExtra(defKeyID, &key) } - back, err := gg.LoadImage(picFile) - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return + } + uid := ctx.Event.UserID + today := time.Now().Format("20060102") + // 签到图片 + drawedFile := cachePath + strconv.FormatInt(uid, 10) + today + "signin.png" + picFile := cachePath + strconv.FormatInt(uid, 10) + today + ".png" + // 获取签到时间 + si := sdb.GetSignInByUID(uid) + siUpdateTimeStr := si.UpdatedAt.Format("20060102") + switch { + case si.Count >= signinMax && siUpdateTimeStr == today: + // 如果签到时间是今天 + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("今天你已经签到过了!")) + if file.IsExist(drawedFile) { + ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile)) } - // 避免图片过大,最大 1280*720 - back = imgfactory.Limit(back, 1280, 720) - imgDX := back.Bounds().Dx() - imgDY := back.Bounds().Dy() - canvas := gg.NewContext(imgDX, imgDY) - // draw Aero Style - aeroStyle := gg.NewContext(imgDX-202, imgDY-202) - aeroStyle.DrawImage(imaging.Blur(back, 2.5), -100, -100) - // aero draw image. - aeroStyle.DrawRoundedRectangle(0, 0, float64(imgDX-200), float64(imgDY-200), 16) - // SideLine - aeroStyle.SetLineWidth(3) - aeroStyle.SetRGBA255(255, 255, 255, 100) - aeroStyle.StrokePreserve() - aeroStyle.SetRGBA255(255, 255, 255, 140) - // fill - aeroStyle.Fill() - // draw background - canvas.DrawImage(back, 0, 0) - // Aero style combine - canvas.DrawImage(aeroStyle.Image(), 100, 100) - canvas.Fill() - hourWord := getHourWord(now) - avatar, _, err := image.Decode(bytes.NewReader(getAvatar)) + return + case siUpdateTimeStr != today: + // 如果是跨天签到就清数据 + err := sdb.InsertOrUpdateSignInCountByUID(uid, 0) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } - avatarf := imgfactory.Size(avatar, 200, 200) - canvas.DrawImage(avatarf.Circle(0).Image(), 120, 120) - // draw info(name,coin,etc) - canvas.SetRGB255(0, 0, 0) - data, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true) + } + // 更新签到次数 + err := sdb.InsertOrUpdateSignInCountByUID(uid, si.Count+1) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + // 更新经验 + level := sdb.GetScoreByUID(uid).Score + 1 + if level > SCOREMAX { + level = SCOREMAX + ctx.SendChain(message.At(uid), message.Text("你的等级已经达到上限")) + } + err = sdb.InsertOrUpdateScoreByUID(uid, level) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + // 更新钱包 + rank := getrank(level) + add := 1 + rand.Intn(10) + rank*5 // 等级越高获得的钱越高 + err = wallet.InsertWalletOf(uid, add) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + alldata := scdata{ + drawedfile: drawedFile, + picfile: picFile, + uid: uid, + nickname: ctx.CardOrNickName(uid), + inc: add, + score: wallet.GetWalletOf(uid), + level: level, + rank: rank, + } + var drawimage image.Image + switch key { + case "1": + drawimage, err = drawScore16(&alldata) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } - if err = canvas.ParseFontFace(data, 50); err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - // draw head - nickName := ctx.CardOrNickName(uid) - canvas.DrawString(nickName, 350, 180) - canvas.Fill() - // main draw - data, err = file.GetLazyData(text.FontFile, control.Md5File, true) + case "2": + drawimage, err = drawScore15(&alldata) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } - if err = canvas.ParseFontFace(data, 30); err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - canvas.DrawStringAnchored(hourWord, 350, 280, 0, 0) - canvas.DrawStringAnchored("ATRI币 + "+strconv.Itoa(add), 350, 350, 0, 0) - canvas.DrawStringAnchored("当前ATRI币:"+strconv.Itoa(score), 350, 400, 0, 0) - canvas.DrawStringAnchored("LEVEL: "+strconv.Itoa(getrank(level)), 350, 450, 0, 0) - // draw Info(Time,etc.) - getTime := time.Now().Format("2006-01-02 15:04:05") - getTimeLengthWidth, getTimeLengthHight := canvas.MeasureString(getTime) - canvas.DrawStringAnchored(getTime, float64(imgDX)-100-20-getTimeLengthWidth/2, float64(imgDY)-100-getTimeLengthHight, 0.5, 0.5) // time - var nextrankScore int - if rank < 10 { - nextrankScore = rankArray[rank+1] - } else { - nextrankScore = SCOREMAX - } - nextLevelStyle := strconv.Itoa(level) + "/" + strconv.Itoa(nextrankScore) - getLevelLength, _ := canvas.MeasureString(nextLevelStyle) - canvas.DrawStringAnchored(nextLevelStyle, 100+getLevelLength, float64(imgDY)-100-getTimeLengthHight, 0.5, 0.5) // time - canvas.Fill() - canvas.SetRGB255(255, 255, 255) - if err = canvas.ParseFontFace(data, 20); err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - canvas.DrawStringAnchored("Created By Zerobot-Plugin "+banner.Version, float64(imgDX)/2, float64(imgDY)-20, 0.5, 0.5) // zbp - canvas.SetRGB255(0, 0, 0) - canvas.DrawStringAnchored("Created By Zerobot-Plugin "+banner.Version, float64(imgDX)/2-3, float64(imgDY)-19, 0.5, 0.5) // zbp - canvas.SetRGB255(255, 255, 255) - // Gradient - grad := gg.NewLinearGradient(20, 320, 400, 20) - grad.AddColorStop(0, color.RGBA{G: 255, A: 255}) - grad.AddColorStop(1, color.RGBA{B: 255, A: 255}) - grad.AddColorStop(0.5, color.RGBA{R: 255, A: 255}) - canvas.SetStrokeStyle(grad) - canvas.SetLineWidth(4) - // level array with rectangle work. - gradLineLength := float64(imgDX-120) - 120 - renderLine := (float64(level) / float64(nextrankScore)) * gradLineLength - canvas.MoveTo(120, float64(imgDY)-102) - canvas.LineTo(120+renderLine, float64(imgDY)-102) - canvas.ClosePath() - canvas.Stroke() - // done. - f, err := os.Create(drawedFile) - if err != nil { - data, err := imgfactory.ToBytes(canvas.Image()) - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - ctx.SendChain(message.ImageBytes(data)) - return - } - _, err = imgfactory.WriteTo(canvas.Image(), f) - _ = f.Close() + default: + ctx.SendChain(message.Text("未找到签到设定:", key)) + return + } + // done. + f, err := os.Create(drawedFile) + if err != nil { + data, err := imgfactory.ToBytes(drawimage) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } - ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile)) - }) + ctx.SendChain(message.ImageBytes(data)) + return + } + _, err = imgfactory.WriteTo(drawimage, f) + _ = f.Close() + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile)) + }) engine.OnPrefix("获得签到背景", zero.OnlyGroup).Limit(ctxext.LimitByGroup).SetBlock(true). Handle(func(ctx *zero.Ctx) { @@ -328,6 +268,27 @@ func init() { } ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile)) }) + engine.OnRegex(`^设置(默认)?签到预设\s?(\d*)$`, zero.SuperUserPermission).Limit(ctxext.LimitByUser).SetBlock(true).Handle(func(ctx *zero.Ctx) { + if ctx.State["regex_matched"].([]string)[2] == "" { + ctx.SendChain(message.Text("设置失败,数据为空")) + } else { + s := ctx.State["regex_matched"].([]string)[1] + key := ctx.State["regex_matched"].([]string)[2] + gid := ctx.Event.GroupID + if gid == 0 { + gid = -ctx.Event.UserID + } + if s != "" { + gid = defKeyID + } + err := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]).Manager.SetExtra(gid, key) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + ctx.SendChain(message.Text("设置成功,当前", s, "预设为:", key)) + } + }) } func getHourWord(t time.Time) string {