diff --git a/README.md b/README.md index d423a46a00..93e9f1ffff 100644 --- a/README.md +++ b/README.md @@ -1045,6 +1045,10 @@ print("run[CQ:image,file="+j["img"]+"]") - [x] 赎牛牛 +- [x] 牛牛拍卖行 + +- [x] 出售牛牛 + - [x] 牛牛商店 - [x] 牛牛背包 diff --git a/plugin/niuniu/draw.go b/plugin/niuniu/draw.go index cce5026a62..0fe284a38d 100644 --- a/plugin/niuniu/draw.go +++ b/plugin/niuniu/draw.go @@ -1,41 +1,34 @@ package niuniu import ( + "bytes" "fmt" "image" + "image/png" "net/http" + "github.com/FloatTech/AnimeAPI/niu" "github.com/FloatTech/floatbox/file" "github.com/FloatTech/rendercard" "github.com/FloatTech/zbputils/control" "github.com/FloatTech/zbputils/img/text" + zero "github.com/wdvxdr1123/ZeroBot" ) -type drawUserRanking struct { - name string - user *userInfo -} - -type drawer []drawUserRanking - -func (allUsers drawer) draw(t bool) (img image.Image, err error) { - fontbyte, err := file.GetLazyData(text.GlowSansFontFile, control.Md5File, true) +func processRankingImg(allUsers niu.BaseInfos, ctx *zero.Ctx, t bool) ([]byte, error) { + fontByte, err := file.GetLazyData(text.GlowSansFontFile, control.Md5File, true) if err != nil { return nil, err } - var ( - title string - s string - ) - title = "牛牛深度排行" - s = "牛牛深度" - if t { - title = "牛牛长度排行" - s = "牛牛长度" + s := "牛牛长度" + title := "牛牛长度排行" + if !t { + s = "牛牛深度" + title = "牛牛深度排行" } ri := make([]*rendercard.RankInfo, len(allUsers)) for i, user := range allUsers { - resp, err := http.Get(fmt.Sprintf("https://q1.qlogo.cn/g?b=qq&nk=%d&s=100", user.user.UID)) + resp, err := http.Get(fmt.Sprintf("https://q1.qlogo.cn/g?b=qq&nk=%d&s=100", user.UID)) if err != nil { return nil, err } @@ -46,11 +39,16 @@ func (allUsers drawer) draw(t bool) (img image.Image, err error) { } ri[i] = &rendercard.RankInfo{ Avatar: decode, - TopLeftText: user.name, - BottomLeftText: fmt.Sprintf("QQ:%d", user.user.UID), - RightText: fmt.Sprintf("%s:%.2fcm", s, user.user.Length), + TopLeftText: ctx.CardOrNickName(user.UID), + BottomLeftText: fmt.Sprintf("QQ:%d", user.UID), + RightText: fmt.Sprintf("%s:%.2fcm", s, user.Length), } } - img, err = rendercard.DrawRankingCard(fontbyte, title, ri) - return + img, err := rendercard.DrawRankingCard(fontByte, title, ri) + if err != nil { + return nil, err + } + var buf bytes.Buffer + err = png.Encode(&buf, img) + return buf.Bytes(), err } diff --git a/plugin/niuniu/main.go b/plugin/niuniu/main.go index d843b303f3..e1028dc36b 100644 --- a/plugin/niuniu/main.go +++ b/plugin/niuniu/main.go @@ -3,10 +3,11 @@ package niuniu import ( "fmt" + "math/rand" "strconv" - "strings" "time" + "github.com/FloatTech/AnimeAPI/niu" "github.com/FloatTech/AnimeAPI/wallet" ctrl "github.com/FloatTech/zbpctrl" "github.com/FloatTech/zbputils/control" @@ -23,11 +24,6 @@ type lastLength struct { Length float64 } -type propsCount struct { - Count int - TimeLimit time.Time -} - var ( en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{ DisableOnDefault: false, @@ -38,65 +34,60 @@ var ( "- 使用[道具名称]jj@xxx\n" + "- 注册牛牛\n" + "- 赎牛牛(cd:60分钟)\n" + + "- 出售牛牛\n" + + "- 牛牛拍卖行\n" + "- 牛牛商店\n" + "- 牛牛背包\n" + "- 注销牛牛\n" + "- 查看我的牛牛\n" + "- 牛子长度排行\n" + - "- 牛子深度排行\n", + "- 牛子深度排行\n" + + "\n ps : 出售后的牛牛都会进入牛牛拍卖行哦", PrivateDataFolder: "niuniu", }) dajiaoLimiter = rate.NewManager[string](time.Second*90, 1) jjLimiter = rate.NewManager[string](time.Second*150, 1) jjCount = syncx.Map[string, *lastLength]{} - prop = syncx.Map[string, *propsCount]{} + register = syncx.Map[string, *lastLength]{} ) func init() { - en.OnFullMatch("牛牛背包", zero.OnlyGroup, getdb).SetBlock(true).Handle(func(ctx *zero.Ctx) { + en.OnRegex(`^购买(\d+)牛牛$`, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { + n, err := strconv.Atoi(ctx.State["regex_matched"].([]string)[1]) + if err != nil { + ctx.SendChain(message.Text("ERROR:", err)) + return + } gid := ctx.Event.GroupID uid := ctx.Event.UserID - niu, err := db.findNiuNiu(gid, uid) + msg, err := niu.Auction(gid, uid, n) if err != nil { - ctx.SendChain(message.Text("你还没有牛牛呢快去注册一个吧!")) + ctx.SendChain(message.Text("ERROR:", err)) return } - ctx.SendChain(message.Text("当前牛牛背包如下", - "\n伟哥:", niu.WeiGe, - "\n媚药:", niu.Philter, - "\n击剑神器:", niu.Artifact, - "\n击剑神稽:", niu.ShenJi)) + ctx.SendChain(message.Reply(ctx.Event.Message), message.Text(msg)) }) - en.OnFullMatch("牛牛商店", zero.OnlyGroup, getdb).SetBlock(true).Handle(func(ctx *zero.Ctx) { + en.OnFullMatch("牛牛拍卖行", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { gid := ctx.Event.GroupID uid := ctx.Event.UserID - - if _, err := db.findNiuNiu(gid, uid); err != nil { - ctx.SendChain(message.Text("你还没有牛牛呢快去注册一个吧!")) + auction, err := niu.ShowAuction(gid) + if err != nil { + ctx.SendChain(message.Text("ERROR:", err)) return } var messages message.Message - messages = append(messages, ctxext.FakeSenderForwardNode(ctx, message.Text("牛牛商店当前售卖的物品如下"))) - messages = append(messages, - ctxext.FakeSenderForwardNode(ctx, - message.Text("商品1\n商品名:伟哥\n商品价格:300ATRI币\n商品描述:可以让你打胶每次都增长,有效5次"))) - messages = append(messages, - ctxext.FakeSenderForwardNode(ctx, - message.Text("商品2\n商品名:媚药\n商品价格:300ATRI币\n商品描述:可以让你打胶每次都减少,有效5次"))) - messages = append(messages, - ctxext.FakeSenderForwardNode(ctx, - message.Text("商品3\n商品名:击剑神器\n商品价格:500ATRI币\n商品描述:可以让你每次击剑都立于不败之地,有效2次"))) - messages = append(messages, - ctxext.FakeSenderForwardNode(ctx, - message.Text("商品4\n商品名:击剑神稽\n商品价格:500ATRI币\n商品描述:可以让你每次击剑都失败,有效2次"))) - + messages = append(messages, ctxext.FakeSenderForwardNode(ctx, message.Text("牛牛拍卖行有以下牛牛"))) + for _, info := range auction { + msg := fmt.Sprintf("商品序号: %d\n牛牛原所属: %d\n牛牛价格: %d%s\n牛牛大小: %.2fcm", + info.ID+1, info.UserID, info.Money, wallet.GetWalletName(), info.Length) + messages = append(messages, ctxext.FakeSenderForwardNode(ctx, message.Text(msg))) + } if id := ctx.Send(messages).ID(); id == 0 { - ctx.Send(message.Text("发送商店失败")) + ctx.Send(message.Text("发送拍卖行失败")) return } - - ctx.SendChain(message.Text("输入对应序号进行购买商品")) + ctx.SendChain(message.Reply(ctx.Event.Message), message.Text("请输入对应序号进行购买")) recv, cancel := zero.NewFutureEvent("message", 999, false, zero.CheckUser(uid), zero.CheckGroup(gid), zero.RegexRule(`^(\d+)$`)).Repeat() defer cancel() timer := time.NewTimer(120 * time.Second) @@ -115,29 +106,92 @@ func init() { return } - info, err := db.findNiuNiu(gid, uid) + msg, err := niu.Auction(gid, uid, n) if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) + ctx.SendChain(message.Text("ERROR:", err)) return } + ctx.SendChain(message.Reply(ctx.Event.Message), message.Text(msg)) + return + } + } - money, err := info.purchaseItem(n) - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } + }) + en.OnFullMatch("出售牛牛", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { + gid := ctx.Event.GroupID + uid := ctx.Event.UserID + sell, err := niu.Sell(gid, uid) + if err != nil { + ctx.SendChain(message.Text("ERROR:", err)) + return + } + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(sell)) + }) + en.OnFullMatch("牛牛背包", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { + gid := ctx.Event.GroupID + uid := ctx.Event.UserID + bag, err := niu.Bag(gid, uid) + if err != nil { + ctx.SendChain(message.Text("ERROR:", err)) + return + } + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(bag)) + }) + en.OnFullMatch("牛牛商店", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { + gid := ctx.Event.GroupID + uid := ctx.Event.UserID - if wallet.GetWalletOf(uid) < money { - ctx.SendChain(message.Text("你还没有足够的ATRI币呢,不能购买")) - return - } + if _, err := niu.GetWordNiuNiu(gid, uid); err != nil { + ctx.SendChain(message.Text(niu.ErrNoNiuNiu)) + return + } + + propMap := map[int]struct { + name string + cost int + scope string + description string + count int + }{ + 1: {"伟哥", 300, "打胶", "可以让你打胶每次都增长", 5}, + 2: {"媚药", 300, "打胶", "可以让你打胶每次都减少", 5}, + 3: {"击剑神器", 500, "jj", "可以让你每次击剑都立于不败之地", 2}, + 4: {"击剑神稽", 500, "jj", "可以让你每次击剑都失败", 2}, + } + + var messages message.Message + messages = append(messages, ctxext.FakeSenderForwardNode(ctx, message.Text("牛牛商店当前售卖的物品如下"))) + for id, _ := range propMap { + product := propMap[id] + productInfo := fmt.Sprintf("商品%d\n商品名: %s\n商品价格: %dATRI币\n商品作用域: %s\n商品描述: %s\n使用次数:%d", + id, product.name, product.cost, product.scope, product.description, product.count) + messages = append(messages, ctxext.FakeSenderForwardNode(ctx, message.Text(productInfo))) + } + if id := ctx.Send(messages).ID(); id == 0 { + ctx.Send(message.Text("发送商店失败")) + return + } - if err = wallet.InsertWalletOf(uid, -money); err != nil { + ctx.SendChain(message.Text("输入对应序号进行购买商品")) + recv, cancel := zero.NewFutureEvent("message", 999, false, zero.CheckUser(uid), zero.CheckGroup(gid), zero.RegexRule(`^(\d+)$`)).Repeat() + defer cancel() + timer := time.NewTimer(120 * time.Second) + answer := "" + defer timer.Stop() + for { + select { + case <-timer.C: + ctx.SendChain(message.At(uid), message.Text(" 超时,已自动取消")) + return + case r := <-recv: + answer = r.Event.Message.String() + n, err := strconv.Atoi(answer) + if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } - if err = db.insertNiuNiu(&info, gid); err != nil { + if err = niu.Store(gid, uid, n); err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } @@ -147,7 +201,7 @@ func init() { } } }) - en.OnFullMatch("赎牛牛", zero.OnlyGroup, getdb).SetBlock(true).Handle(func(ctx *zero.Ctx) { + en.OnFullMatch("赎牛牛", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { gid := ctx.Event.GroupID uid := ctx.Event.UserID last, ok := jjCount.Load(fmt.Sprintf("%d_%d", gid, uid)) @@ -157,7 +211,7 @@ func init() { return } - if time.Since(last.TimeLimit) > time.Minute*60 { + if time.Since(last.TimeLimit) > time.Hour { ctx.SendChain(message.Text("时间已经过期了,牛牛已被收回!")) jjCount.Delete(fmt.Sprintf("%d_%d", gid, uid)) return @@ -167,103 +221,74 @@ func init() { ctx.SendChain(message.Text("你还没有被厥够4次呢,不能赎牛牛")) return } + ctx.SendChain(message.Text("再次确认一下哦,这次赎牛牛,牛牛长度将会变成", last.Length, "cm\n还需要嘛【是|否】")) + recv, cancel := zero.NewFutureEvent("message", 999, false, zero.CheckUser(uid), zero.CheckGroup(gid), zero.RegexRule(`^(是|否)$`)).Repeat() + defer cancel() + timer := time.NewTimer(2 * time.Minute) + defer timer.Stop() + for { + select { + case <-timer.C: + ctx.SendChain(message.Text("操作超时,已自动取消")) + return + case c := <-recv: + answer := c.Event.Message.String() + if answer == "否" { + ctx.SendChain(message.Text("取消成功!")) + return + } - money := wallet.GetWalletOf(uid) - if money < 150 { - ctx.SendChain(message.Text("赎牛牛需要150ATRI币,快去赚钱吧")) - return - } - - if err := wallet.InsertWalletOf(uid, -150); err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - - niuniu, err := db.findNiuNiu(gid, uid) - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } + if err := niu.Redeem(gid, uid, last.Length); err == nil { + ctx.SendChain(message.Text("ERROR:", err)) + return + } - niuniu.Length = last.Length + jjCount.Delete(fmt.Sprintf("%d_%d", gid, uid)) - if err = db.insertNiuNiu(&niuniu, gid); err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return + ctx.SendChain(message.At(uid), message.Text(fmt.Sprintf("恭喜你!成功赎回牛牛,当前长度为:%.2fcm", last.Length))) + return + } } - - jjCount.Delete(fmt.Sprintf("%d_%d", gid, uid)) - ctx.SendChain(message.At(uid), message.Text(fmt.Sprintf("恭喜你!成功赎回牛牛,当前长度为:%.2fcm", last.Length))) }) - en.OnFullMatch("牛子长度排行", zero.OnlyGroup, getdb).SetBlock(true).Handle(func(ctx *zero.Ctx) { + en.OnFullMatch("牛子长度排行", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { gid := ctx.Event.GroupID - niuniuList, err := db.readAllTable(gid) + infos, err := niu.GetRankingInfo(gid, true) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } - m := niuniuList.positive() - if m == nil { - ctx.SendChain(message.Text("暂时没有男孩子哦")) - return - } - m.sort(true) - buf, err := m.setupDrawList(ctx, true) + img, err := processRankingImg(infos, ctx, true) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } - ctx.SendChain(message.ImageBytes(buf)) + ctx.SendChain(message.ImageBytes(img)) }) - en.OnFullMatch("牛子深度排行", zero.OnlyGroup, getdb).SetBlock(true).Handle(func(ctx *zero.Ctx) { + en.OnFullMatch("牛子深度排行", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { gid := ctx.Event.GroupID - niuniuList, err := db.readAllTable(gid) + infos, err := niu.GetRankingInfo(gid, false) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } - m := niuniuList.negative() - if m == nil { - ctx.SendChain(message.Text("暂时没有女孩子哦")) - return - } - m.sort(false) - buf, err := m.setupDrawList(ctx, false) + img, err := processRankingImg(infos, ctx, false) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } - - ctx.SendChain(message.ImageBytes(buf)) + ctx.SendChain(message.ImageBytes(img)) }) - en.OnFullMatch("查看我的牛牛", getdb, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { + en.OnFullMatch("查看我的牛牛", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { uid := ctx.Event.UserID gid := ctx.Event.GroupID - i, err := db.findNiuNiu(gid, uid) - if err != nil { - ctx.SendChain(message.Text("你还没有牛牛呢不能查看!")) - return - } - niuniu := i.Length - var result strings.Builder - sexLong := "长" - sex := "♂️" - if niuniu < 0 { - sexLong = "深" - sex = "♀️" - } - niuniuList, err := db.readAllTable(gid) + view, err := niu.View(gid, uid, ctx.CardOrNickName(uid)) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) return } - result.WriteString(fmt.Sprintf("\n📛%s<%s>的牛牛信息\n⭕性别:%s\n⭕%s度:%.2fcm\n⭕排行:%d\n⭕%s ", - ctx.CardOrNickName(uid), strconv.FormatInt(uid, 10), - sex, sexLong, niuniu, niuniuList.ranking(niuniu, uid), generateRandomString(niuniu))) - ctx.SendChain(message.Text(&result)) + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(view)) }) - en.OnRegex(`^(?:.*使用(.*))??打胶$`, zero.OnlyGroup, - getdb).SetBlock(true).Limit(func(ctx *zero.Ctx) *rate.Limiter { + en.OnRegex(`^(?:.*使用(.*))??打胶$`, zero.OnlyGroup).SetBlock(true).Limit(func(ctx *zero.Ctx) *rate.Limiter { lt := dajiaoLimiter.Load(fmt.Sprintf("%d_%d", ctx.Event.GroupID, ctx.Event.UserID)) ctx.State["dajiao_last_touch"] = lt.LastTouch() return lt @@ -279,58 +304,27 @@ func init() { // 获取群号和用户ID gid := ctx.Event.GroupID uid := ctx.Event.UserID - t := fmt.Sprintf("%d_%d", gid, uid) fiancee := ctx.State["regex_matched"].([]string) - updateMap(t, false) - niuniu, err := db.findNiuNiu(gid, uid) + msg, err := niu.HitGlue(gid, uid, fiancee[1]) if err != nil { - ctx.SendChain(message.Text("请先注册牛牛!")) - dajiaoLimiter.Delete(fmt.Sprintf("%d_%d", gid, uid)) - return - } - messages, err := niuniu.processNiuNiuAction(t, fiancee[1]) - if err != nil { - ctx.SendChain(message.Text(err)) - dajiaoLimiter.Delete(fmt.Sprintf("%d_%d", gid, uid)) - return - } - if err = db.insertNiuNiu(&niuniu, gid); err != nil { ctx.SendChain(message.Text("ERROR: ", err)) + dajiaoLimiter.Delete(fmt.Sprintf("%d_%d", ctx.Event.GroupID, ctx.Event.UserID)) return } - - ctx.SendChain(message.Text(messages)) + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(msg)) }) - en.OnFullMatch("注册牛牛", zero.OnlyGroup, getdb).SetBlock(true).Handle(func(ctx *zero.Ctx) { + en.OnFullMatch("注册牛牛", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { gid := ctx.Event.GroupID uid := ctx.Event.UserID - if _, err := db.findNiuNiu(gid, uid); err == nil { - ctx.SendChain(message.Text("你已经注册过了")) + msg, err := niu.Register(gid, uid) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) return } - // 获取初始长度 - length := db.randLength() - u := userInfo{ - UID: uid, - Length: length, - } - // 添加数据进入表 - if err := db.insertNiuNiu(&u, gid); err != nil { - if err = db.createGIDTable(gid); err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - - if err = db.insertNiuNiu(&u, gid); err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - } - ctx.SendChain(message.At(uid), - message.Text("注册成功,你的牛牛现在有", u.Length, "cm")) + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(msg)) }) - en.OnRegex(`^(?:.*使用(.*))??jj\s?(\[CQ:at,(?:\S*,)?qq=(\d+)(?:,\S*)?\]|(\d+))$`, getdb, + en.OnMessage(zero.NewPattern().Text(`^(?:.*使用(.*))??jj`).At().AsRule(), zero.OnlyGroup).SetBlock(true).Limit(func(ctx *zero.Ctx) *rate.Limiter { lt := jjLimiter.Load(fmt.Sprintf("%d_%d", ctx.Event.GroupID, ctx.Event.UserID)) ctx.State["jj_last_touch"] = lt.LastTouch() @@ -345,82 +339,55 @@ func init() { }))) }, ).Handle(func(ctx *zero.Ctx) { - fiancee := ctx.State["regex_matched"].([]string) - adduser, err := strconv.ParseInt(fiancee[3]+fiancee[4], 10, 64) + patternParsed := ctx.State[zero.KeyPattern].([]zero.PatternParsed) + adduser, err := strconv.ParseInt(patternParsed[1].At(), 10, 64) if err != nil { ctx.SendChain(message.Text("ERROR: ", err)) + jjLimiter.Delete(fmt.Sprintf("%d_%d", ctx.Event.GroupID, ctx.Event.UserID)) return } uid := ctx.Event.UserID gid := ctx.Event.GroupID - t := fmt.Sprintf("%d_%d", gid, uid) - updateMap(t, false) - myniuniu, err := db.findNiuNiu(gid, uid) - if err != nil { - ctx.SendChain(message.Text("你还没有牛牛快去注册一个吧!")) - jjLimiter.Delete(t) - return - } - adduserniuniu, err := db.findNiuNiu(gid, adduser) + msg, length, err := niu.JJ(gid, uid, adduser, patternParsed[0].Text()[1]) if err != nil { - ctx.SendChain(message.At(uid), message.Text("对方还没有牛牛呢,不能🤺")) - jjLimiter.Delete(t) - return - } - if uid == adduser { - ctx.SendChain(message.Text("你要和谁🤺?你自己吗?")) - jjLimiter.Delete(t) - return - } - fencingResult, err := myniuniu.processJJuAction(&adduserniuniu, t, fiancee[1]) - if err != nil { - ctx.SendChain(message.Text(err)) - jjLimiter.Delete(t) - return - } - - if err = db.insertNiuNiu(&myniuniu, gid); err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return - } - - if err = db.insertNiuNiu(&adduserniuniu, gid); err != nil { ctx.SendChain(message.Text("ERROR: ", err)) + jjLimiter.Delete(fmt.Sprintf("%d_%d", ctx.Event.GroupID, ctx.Event.UserID)) return } - - ctx.SendChain(message.At(uid), message.Text(" ", fencingResult)) + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(msg)) j := fmt.Sprintf("%d_%d", gid, adduser) count, ok := jjCount.Load(j) var c lastLength - // 按照第一次jj时的时间计算,超过45分钟则重置 + // 按照最后一次被jj时的时间计算,超过60分钟则重置 if !ok { c = lastLength{ TimeLimit: time.Now(), Count: 1, - Length: adduserniuniu.Length, + Length: length, } } else { c = lastLength{ - TimeLimit: c.TimeLimit, + TimeLimit: time.Now(), Count: count.Count + 1, Length: count.Length, } - if time.Since(c.TimeLimit) > time.Minute*60 { + if time.Since(c.TimeLimit) > time.Hour { c = lastLength{ TimeLimit: time.Now(), Count: 1, - Length: adduserniuniu.Length, + Length: length, } } } jjCount.Store(j, &c) if c.Count > 2 { - ctx.SendChain(message.Text(randomChoice([]string{fmt.Sprintf("你们太厉害了,对方已经被你们打了%d次了,你们可以继续找他🤺", c.Count), - "你们不要再找ta🤺啦!"}))) - // 保证只发送一次 - if c.Count < 4 { + ctx.SendChain(message.Text(randomChoice([]string{ + fmt.Sprintf("你们太厉害了,对方已经被你们打了%d次了,你们可以继续找他🤺", c.Count), + "你们不要再找ta🤺啦!"}, + ))) + + if c.Count >= 4 { id := ctx.SendPrivateMessage(adduser, message.Text(fmt.Sprintf("你在%d群里已经被厥冒烟了,快去群里赎回你原本的牛牛!\n发送:`赎牛牛`即可!", gid))) if id == 0 { @@ -429,19 +396,33 @@ func init() { } } }) - en.OnFullMatch("注销牛牛", getdb, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { + en.OnFullMatch("注销牛牛", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { uid := ctx.Event.UserID gid := ctx.Event.GroupID - _, err := db.findNiuNiu(gid, uid) - if err != nil { - ctx.SendChain(message.Text("你还没有牛牛呢,咋的你想凭空造一个啊")) - return + key := fmt.Sprintf("%d_%d", gid, uid) + data, ok := register.Load(key) + switch { + case !ok || time.Since(data.TimeLimit) > time.Hour*12: + data = &lastLength{ + TimeLimit: time.Now(), + Count: 1, + } + default: + if err := wallet.InsertWalletOf(uid, -data.Count*50); err != nil { + ctx.SendChain(message.Text("你的钱不够你注销牛牛了,这次注销需要", data.Count*50, wallet.GetWalletName())) + return + } } - err = db.deleteniuniu(gid, uid) + register.Store(key, data) + msg, err := niu.Cancel(gid, uid) if err != nil { - ctx.SendChain(message.Text("注销失败")) + ctx.SendChain(message.Text("ERROR: ", err)) return } - ctx.SendChain(message.Text("注销成功,你已经没有牛牛了")) + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(msg)) }) } + +func randomChoice(options []string) string { + return options[rand.Intn(len(options))] +} diff --git a/plugin/niuniu/model.go b/plugin/niuniu/model.go deleted file mode 100644 index ff9a95fdb3..0000000000 --- a/plugin/niuniu/model.go +++ /dev/null @@ -1,376 +0,0 @@ -// Package niuniu 牛牛大作战 -package niuniu - -import ( - "bytes" - "errors" - "fmt" - "image/png" - "math" - "math/rand" - "sort" - "strconv" - "sync" - "time" - - fcext "github.com/FloatTech/floatbox/ctxext" - sql "github.com/FloatTech/sqlite" - zero "github.com/wdvxdr1123/ZeroBot" - "github.com/wdvxdr1123/ZeroBot/message" -) - -type model struct { - sync.RWMutex - sql sql.Sqlite -} - -type userInfo struct { - UID int64 - Length float64 - UserCount int - WeiGe int // 伟哥 - Philter int // 媚药 - Artifact int // 击剑神器 - ShenJi int // 击剑神稽 - Buff1 int // 暂定 - Buff2 int // 暂定 - Buff3 int // 暂定 - Buff4 int // 暂定 - Buff5 int // 暂定 -} - -type users []*userInfo - -var ( - db = &model{} - getdb = fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { - db.sql = sql.New(en.DataFolder() + "niuniu.db") - err := db.sql.Open(time.Hour * 24) - if err != nil { - ctx.SendChain(message.Text("ERROR: ", err)) - return false - } - return true - }) -) - -// useWeiGe 使用道具伟哥 -func (u *userInfo) useWeiGe() (string, float64) { - niuniu := u.Length - reduce := math.Abs(hitGlue(niuniu)) - niuniu += reduce - return randomChoice([]string{ - fmt.Sprintf("哈哈,你这一用道具,牛牛就像是被激发了潜能,增加了%.2fcm!看来今天是个大日子呢!", reduce), - fmt.Sprintf("你这是用了什么神奇的道具?牛牛竟然增加了%.2fcm,简直是牛气冲天!", reduce), - fmt.Sprintf("使用道具后,你的牛牛就像是开启了加速模式,一下增加了%.2fcm,这成长速度让人惊叹!", reduce), - }), niuniu -} - -// usePhilter 使用道具媚药 -func (u *userInfo) usePhilter() (string, float64) { - niuniu := u.Length - reduce := math.Abs(hitGlue(niuniu)) - niuniu -= reduce - return randomChoice([]string{ - fmt.Sprintf("你使用媚药,咿呀咿呀一下使当前长度发生了一些变化,当前长度%.2f", niuniu), - fmt.Sprintf("看来你追求的是‘微观之美’,故意使用道具让牛牛凹进去了%.2fcm!", reduce), - fmt.Sprintf("缩小奇迹’在你身上发生了,牛牛凹进去了%.2fcm,你的选择真是独特!", reduce), - }), niuniu -} - -// useArtifact 使用道具击剑神器 -func (u *userInfo) useArtifact(adduserniuniu float64) (string, float64, float64) { - myLength := u.Length - difference := myLength - adduserniuniu - var ( - change float64 - ) - if difference > 0 { - change = hitGlue(myLength + adduserniuniu) - } else { - change = hitGlue((myLength + adduserniuniu) / 2) - } - myLength += change - return randomChoice([]string{ - fmt.Sprintf("凭借神秘道具的力量,你让对方在你的长度面前俯首称臣!你的长度增加了%.2fcm,当前长度达到了%.2fcm", change, myLength), - fmt.Sprintf("神器在手,天下我有!你使用道具后,长度猛增%.2fcm,现在的总长度是%.2fcm,无人能敌!", change, myLength), - fmt.Sprintf("这就是道具的魔力!你轻松增加了%.2fcm,让对手望尘莫及,当前长度为%.2fcm!", change, myLength), - fmt.Sprintf("道具一出,谁与争锋!你的长度因道具而增长%.2fcm,现在的长度是%.2fcm,霸气尽显!", change, myLength), - fmt.Sprintf("使用道具的你,如同获得神助!你的长度增长了%.2fcm,达到%.2fcm的惊人长度,胜利自然到手!", change, myLength), - }), myLength, adduserniuniu - change/1.3 -} - -// useShenJi 使用道具击剑神稽 -func (u *userInfo) useShenJi(adduserniuniu float64) (string, float64, float64) { - myLength := u.Length - difference := myLength - adduserniuniu - var ( - change float64 - ) - if difference > 0 { - change = hitGlue(myLength + adduserniuniu) - } else { - change = hitGlue((myLength + adduserniuniu) / 2) - } - myLength -= change - var r string - if myLength > 0 { - r = randomChoice([]string{ - fmt.Sprintf("哦吼!?看来你的牛牛因为使用了神秘道具而缩水了呢🤣🤣🤣!缩小了%.2fcm!", change), - fmt.Sprintf("哈哈,看来这个道具有点儿调皮,让你的长度缩水了%.2fcm!现在你的长度是%.2fcm,下次可得小心使用哦!", change, myLength), - fmt.Sprintf("使用道具后,你的牛牛似乎有点儿害羞,缩水了%.2fcm!现在的长度是%.2fcm,希望下次它能挺直腰板!", change, myLength), - fmt.Sprintf("哎呀,这个道具的效果有点儿意外,你的长度减少了%.2fcm,现在只有%.2fcm了!下次选道具可得睁大眼睛!", change, myLength), - }) - } else { - r = randomChoice([]string{ - fmt.Sprintf("哦哟,小姐姐真是玩得一手好游戏,使用道具后数值又降低了%.2fcm,小巧得更显魅力!", change), - fmt.Sprintf("看来小姐姐喜欢更加精致的风格,使用道具后,数值减少了%.2fcm,更加迷人了!", change), - fmt.Sprintf("小姐姐的每一次变化都让人惊喜,使用道具后,数值减少了%.2fcm,更加优雅动人!", change), - fmt.Sprintf("小姐姐这是在展示什么是真正的精致小巧,使用道具后,数值减少了%.2fcm,美得不可方物!", change), - }) - } - return r, myLength, adduserniuniu + 0.7*change -} - -func (u *userInfo) processNiuNiuAction(t string, props string) (string, error) { - var ( - messages string - info userInfo - err error - f float64 - ) - load, ok := prop.Load(t) - info = *u - if props != "" { - if contains(t, dajiaoProp) { - return "", errors.New("道具不存在") - } - if err = u.createUserInfoByProps(props); err != nil { - return "", err - } - } - switch { - case ok && load.Count > 1 && time.Since(load.TimeLimit) < time.Minute*8: - messages, f = generateRandomStingTwo(u.Length) - u.Length = f - errMessage := fmt.Sprintf("你使用道具次数太快了,此次道具不会生效,等待%d再来吧", time.Minute*8-time.Since(load.TimeLimit)) - err = errors.New(errMessage) - - case u.WeiGe-info.WeiGe != 0: - messages, f = u.useWeiGe() - u.Length = f - updateMap(t, true) - - case u.Philter-info.Philter != 0: - messages, f = u.usePhilter() - u.Length = f - updateMap(t, true) - - default: - messages, f = generateRandomStingTwo(u.Length) - u.Length = f - } - return messages, err -} - -func (u *userInfo) createUserInfoByProps(props string) error { - var ( - err error - ) - switch props { - case "伟哥": - if u.WeiGe > 0 { - u.WeiGe-- - } else { - err = errors.New("你还没有伟哥呢,不能使用") - } - case "媚药": - if u.Philter > 0 { - u.Philter-- - } else { - err = errors.New("你还没有媚药呢,不能使用") - } - case "击剑神器": - if u.Artifact > 0 { - u.Artifact-- - } else { - err = errors.New("你还没有击剑神器呢,不能使用") - } - case "击剑神稽": - if u.ShenJi > 0 { - u.ShenJi-- - } else { - err = errors.New("你还没有击剑神稽呢,不能使用") - } - default: - err = errors.New("道具不存在") - } - return err -} - -// 接收值依次是 被jj用户的信息 记录gid和uid的字符串 道具名称 -// 返回值依次是 要发送的消息 错误信息 -func (u *userInfo) processJJuAction(adduserniuniu *userInfo, t string, props string) (string, error) { - var ( - fencingResult string - f float64 - f1 float64 - info userInfo - err error - ) - v, ok := prop.Load(t) - info = *u - if props != "" { - if contains(t, jjProp) { - return "", errors.New("道具不存在") - } - if err = u.createUserInfoByProps(props); err != nil { - return "", err - } - } - switch { - case ok && v.Count > 1 && time.Since(v.TimeLimit) < time.Minute*8: - fencingResult, f, f1 = fencing(u.Length, adduserniuniu.Length) - u.Length = f - adduserniuniu.Length = f1 - errMessage := fmt.Sprintf("你使用道具次数太快了,此次道具不会生效,等待%d再来吧", time.Minute*8-time.Since(v.TimeLimit)) - err = errors.New(errMessage) - case u.ShenJi-info.ShenJi != 0: - fencingResult, f, f1 = u.useShenJi(adduserniuniu.Length) - u.Length = f - adduserniuniu.Length = f1 - updateMap(t, true) - case u.Artifact-info.Artifact != 0: - fencingResult, f, f1 = u.useArtifact(adduserniuniu.Length) - u.Length = f - adduserniuniu.Length = f1 - updateMap(t, true) - default: - fencingResult, f, f1 = fencing(u.Length, adduserniuniu.Length) - u.Length = f - adduserniuniu.Length = f1 - } - return fencingResult, err -} - -func (u *userInfo) purchaseItem(n int) (int, error) { - var ( - money int - err error - ) - switch n { - case 1: - money = 300 - u.WeiGe += 5 - case 2: - money = 300 - u.Philter += 5 - case 3: - money = 500 - u.Artifact += 2 - case 4: - money = 500 - u.ShenJi += 2 - default: - err = errors.New("无效的选择") - } - return money, err -} - -func (m users) setupDrawList(ctx *zero.Ctx, t bool) ([]byte, error) { - allUsers := make(drawer, len(m)) - for i, info := range m { - allUsers[i] = drawUserRanking{ - name: ctx.CardOrNickName(info.UID), - user: info, - } - } - image, err := allUsers.draw(t) - if err != nil { - return nil, err - } - var buf bytes.Buffer - err = png.Encode(&buf, image) - return buf.Bytes(), err -} - -func (m users) positive() users { - var m1 []*userInfo - for _, i2 := range m { - if i2.Length > 0 { - m1 = append(m1, i2) - } - } - return m1 -} - -func (m users) negative() users { - var m1 []*userInfo - for _, i2 := range m { - if i2.Length <= 0 { - m1 = append(m1, i2) - } - } - return m1 -} - -func (m users) sort(isDesc bool) { - t := func(i, j int) bool { - return m[i].Length < m[j].Length - } - if isDesc { - t = func(i, j int) bool { - return m[i].Length > m[j].Length - } - } - sort.Slice(m, t) -} - -func (m users) ranking(niuniu float64, uid int64) int { - m.sort(niuniu > 0) - for i, user := range m { - if user.UID == uid { - return i + 1 - } - } - return -1 -} - -func (db *model) randLength() float64 { - return float64(rand.Intn(9)+1) + (float64(rand.Intn(100)) / 100) -} - -func (db *model) createGIDTable(gid int64) error { - db.Lock() - defer db.Unlock() - return db.sql.Create(strconv.FormatInt(gid, 10), &userInfo{}) -} - -// findNiuNiu 返回一个用户的牛牛信息 -func (db *model) findNiuNiu(gid, uid int64) (userInfo, error) { - db.RLock() - defer db.RUnlock() - u := userInfo{} - err := db.sql.Find(strconv.FormatInt(gid, 10), &u, "WHERE UID = ?", uid) - return u, err -} - -// insertNiuNiu 更新一个用户的牛牛信息 -func (db *model) insertNiuNiu(u *userInfo, gid int64) error { - db.Lock() - defer db.Unlock() - return db.sql.Insert(strconv.FormatInt(gid, 10), u) -} - -func (db *model) deleteniuniu(gid, uid int64) error { - db.Lock() - defer db.Unlock() - return db.sql.Del(strconv.FormatInt(gid, 10), "WHERE UID = ?", uid) -} - -func (db *model) readAllTable(gid int64) (users, error) { - db.Lock() - defer db.Unlock() - a, err := sql.FindAll[userInfo](&db.sql, strconv.FormatInt(gid, 10), "WHERE UserCount = 0") - return a, err -} diff --git a/plugin/niuniu/utils.go b/plugin/niuniu/utils.go deleted file mode 100644 index 4c9fe3c957..0000000000 --- a/plugin/niuniu/utils.go +++ /dev/null @@ -1,250 +0,0 @@ -// Package niuniu 牛牛大作战 -package niuniu - -import ( - "fmt" - "math" - "math/rand" - "strings" - "time" -) - -var ( - jjProp = []string{"击剑神器", "击剑神稽"} - dajiaoProp = []string{"伟哥", "媚药"} -) - -// 检查字符串是否在切片中 -func contains(s string, array []string) bool { - for _, item := range array { - if strings.EqualFold(item, s) { - return true - } - } - return false -} - -func randomChoice(options []string) string { - return options[rand.Intn(len(options))] -} - -func updateMap(t string, d bool) { - value, ok := prop.Load(t) - if value == nil { - return - } - // 检查一次是否已经过期 - if !d { - if time.Since(value.TimeLimit) > time.Minute*8 { - prop.Delete(t) - } - return - } - if ok { - prop.Store(t, &propsCount{ - Count: value.Count + 1, - TimeLimit: value.TimeLimit, - }) - } else { - prop.Store(t, &propsCount{ - Count: 1, - TimeLimit: time.Now(), - }) - } - if time.Since(value.TimeLimit) > time.Minute*8 { - prop.Delete(t) - } -} - -func generateRandomStingTwo(niuniu float64) (string, float64) { - probability := rand.Intn(100 + 1) - reduce := math.Abs(hitGlue(niuniu)) - switch { - case probability <= 40: - niuniu += reduce - return randomChoice([]string{ - fmt.Sprintf("你嘿咻嘿咻一下,促进了牛牛发育,牛牛增加%.2fcm了呢!", reduce), - fmt.Sprintf("你打了个舒服痛快的🦶呐,牛牛增加了%.2fcm呢!", reduce), - }), niuniu - case probability <= 60: - return randomChoice([]string{ - "你打了个🦶,但是什么变化也没有,好奇怪捏~", - "你的牛牛刚开始变长了,可过了一会又回来了,什么变化也没有,好奇怪捏~", - }), niuniu - default: - niuniu -= reduce - if niuniu < 0 { - return randomChoice([]string{ - fmt.Sprintf("哦吼!?看来你的牛牛凹进去了%.2fcm呢!", reduce), - fmt.Sprintf("你突发恶疾!你的牛牛凹进去了%.2fcm!", reduce), - fmt.Sprintf("笑死,你因为打🦶过度导致牛牛凹进去了%.2fcm!🤣🤣🤣", reduce), - }), niuniu - } - return randomChoice([]string{ - fmt.Sprintf("阿哦,你过度打🦶,牛牛缩短%.2fcm了呢!", reduce), - fmt.Sprintf("你的牛牛变长了很多,你很激动地继续打🦶,然后牛牛缩短了%.2fcm呢!", reduce), - fmt.Sprintf("小打怡情,大打伤身,强打灰飞烟灭!你过度打🦶,牛牛缩短了%.2fcm捏!", reduce), - }), niuniu - } -} - -func generateRandomString(niuniu float64) string { - switch { - case niuniu <= -100: - return "wtf?你已经进化成魅魔了!魅魔在击剑时有20%的几率消耗自身长度吞噬对方牛牛呢。" - case niuniu <= -50: - return "嗯....好像已经穿过了身体吧..从另一面来看也可以算是凸出来的吧?" - case niuniu <= -25: - return randomChoice([]string{ - "这名女生,你的身体很健康哦!", - "WOW,真的凹进去了好多呢!", - "你已经是我们女孩子的一员啦!", - }) - case niuniu <= -10: - return randomChoice([]string{ - "你已经是一名女生了呢,", - "从女生的角度来说,你发育良好(,", - "你醒啦?你已经是一名女孩子啦!", - "唔...可以放进去一根手指了都...", - }) - case niuniu <= 0: - return randomChoice([]string{ - "安了安了,不要伤心嘛,做女生有什么不好的啊。", - "不哭不哭,摸摸头,虽然很难再长出来,但是请不要伤心啦啊!", - "加油加油!我看好你哦!", - "你醒啦?你现在已经是一名女孩子啦!", - }) - case niuniu <= 10: - return randomChoice([]string{ - "你行不行啊?细狗!", - "虽然短,但是小小的也很可爱呢。", - "像一只蚕宝宝。", - "长大了。", - }) - case niuniu <= 25: - return randomChoice([]string{ - "唔...没话说", - "已经很长了呢!", - }) - case niuniu <= 50: - return randomChoice([]string{ - "话说这种真的有可能吗?", - "厚礼谢!", - }) - case niuniu <= 100: - return randomChoice([]string{ - "已经突破天际了嘛...", - "唔...这玩意应该不会变得比我高吧?", - "你这个长度会死人的...!", - "你马上要进化成牛头人了!!", - "你是什么怪物,不要过来啊!!", - }) - default: - return "惊世骇俗!你已经进化成牛头人了!牛头人在击剑时有20%的几率消耗自身长度吞噬对方牛牛呢。" - } -} - -// fencing 击剑对决逻辑,返回对决结果和myLength的变化值 -func fencing(myLength, oppoLength float64) (string, float64, float64) { - devourLimit := 0.27 - - probability := rand.Intn(100) + 1 - - switch { - case oppoLength <= -100 && myLength > 0 && 10 < probability && probability <= 20: - change := hitGlue(oppoLength) + rand.Float64()*math.Log2(math.Abs(0.5*(myLength+oppoLength))) - myLength += change - return fmt.Sprintf("对方身为魅魔诱惑了你,你同化成魅魔!当前长度%.2fcm!", -myLength), -myLength, oppoLength - - case oppoLength >= 100 && myLength > 0 && 10 < probability && probability <= 20: - change := math.Min(math.Abs(devourLimit*myLength), math.Abs(1.5*myLength)) - myLength += change - return fmt.Sprintf("对方以牛头人的荣誉摧毁了你的牛牛!当前长度%.2fcm!", myLength), myLength, oppoLength - - case myLength <= -100 && oppoLength > 0 && 10 < probability && probability <= 20: - change := hitGlue(myLength+oppoLength) + rand.Float64()*math.Log2(math.Abs(0.5*(myLength+oppoLength))) - oppoLength -= change - myLength -= change - return fmt.Sprintf("你身为魅魔诱惑了对方,吞噬了对方部分长度!当前长度%.2fcm!", myLength), myLength, oppoLength - - case myLength >= 100 && oppoLength > 0 && 10 < probability && probability <= 20: - myLength -= oppoLength - oppoLength = 0.01 - return fmt.Sprintf("你以牛头人的荣誉摧毁了对方的牛牛!当前长度%.2fcm!", myLength), myLength, oppoLength - - default: - return determineResultBySkill(myLength, oppoLength) - } -} - -// determineResultBySkill 根据击剑技巧决定结果 -func determineResultBySkill(myLength, oppoLength float64) (string, float64, float64) { - probability := rand.Intn(100) + 1 - winProbability := calculateWinProbability(myLength, oppoLength) * 100 - return applySkill(myLength, oppoLength, - float64(probability) <= winProbability) -} - -// calculateWinProbability 计算胜率 -func calculateWinProbability(heightA, heightB float64) float64 { - pA := 0.9 - heightRatio := math.Max(heightA, heightB) / math.Min(heightA, heightB) - reductionRate := 0.1 * (heightRatio - 1) - reduction := pA * reductionRate - - adjustedPA := pA - reduction - return math.Max(adjustedPA, 0.01) -} - -// applySkill 应用击剑技巧并生成结果 -func applySkill(myLength, oppoLength float64, increaseLength1 bool) (string, float64, float64) { - reduce := fence(oppoLength) - // 兜底操作 - if reduce == 0 { - reduce = rand.Float64() + float64(rand.Intn(3)) - } - if increaseLength1 { - myLength += reduce - oppoLength -= 0.8 * reduce - if myLength < 0 { - return fmt.Sprintf("哦吼!?你的牛牛在长大欸!长大了%.2fcm!", reduce), myLength, oppoLength - } - return fmt.Sprintf("你以绝对的长度让对方屈服了呢!你的长度增加%.2fcm,当前长度%.2fcm!", reduce, myLength), myLength, oppoLength - } - myLength -= reduce - oppoLength += 0.8 * reduce - if myLength < 0 { - return fmt.Sprintf("哦吼!?看来你的牛牛因为击剑而凹进去了呢🤣🤣🤣!凹进去了%.2fcm!", reduce), myLength, oppoLength - } - return fmt.Sprintf("对方以绝对的长度让你屈服了呢!你的长度减少%.2fcm,当前长度%.2fcm!", reduce, myLength), myLength, oppoLength -} - -// fence 根据长度计算减少的长度 -func fence(rd float64) float64 { - rd = math.Abs(rd) - if rd == 0 { - rd = 1 - } - r := hitGlue(rd)*2 + rand.Float64()*math.Log2(rd) - - return float64(int(r * rand.Float64())) -} - -func hitGlue(l float64) float64 { - if l == 0 { - l = 0.1 - } - l = math.Abs(l) - switch { - case l > 1 && l <= 10: - return rand.Float64() * math.Log2(l*2) - case 10 < l && l <= 100: - return rand.Float64() * math.Log2(l*1.5) - case 100 < l && l <= 1000: - return rand.Float64() * (math.Log10(l*1.5) * 2) - case l > 1000: - return rand.Float64() * (math.Log10(l) * 2) - default: - return rand.Float64() - } -}