diff --git a/main.go b/main.go index 444f5d8acd..b78bf0758e 100644 --- a/main.go +++ b/main.go @@ -80,6 +80,7 @@ import ( _ "github.com/FloatTech/ZeroBot-Plugin/plugin/chrev" // 英文字符翻转 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/coser" // 三次元小姐姐 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/cpstory" // cp短打 + _ "github.com/FloatTech/ZeroBot-Plugin/plugin/cybercat" // 云养猫 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/dailynews" // 今日早报 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/danbooru" // DeepDanbooru二次元图标签识别 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/diana" // 嘉心糖发病 diff --git a/plugin/cybercat/arena.go b/plugin/cybercat/arena.go new file mode 100644 index 0000000000..80e57d5f5f --- /dev/null +++ b/plugin/cybercat/arena.go @@ -0,0 +1,246 @@ +// Package cybercat 云养猫 +package cybercat + +import ( + "math" + "math/rand" + "strconv" + "strings" + "time" + + "github.com/FloatTech/AnimeAPI/wallet" + zbmath "github.com/FloatTech/floatbox/math" + "github.com/FloatTech/zbputils/ctxext" + "github.com/FloatTech/zbputils/img/text" + "github.com/fumiama/jieba/util/helper" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" +) + +func init() { + engine.OnRegex(`^(喵喵|猫猫)(PK|pk)\s*\[CQ:at,qq=(\d+).*`, zero.OnlyGroup, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + id := ctx.Event.MessageID + gidStr := "group" + strconv.FormatInt(ctx.Event.GroupID, 10) + uidStr := strconv.FormatInt(ctx.Event.UserID, 10) + if ctx.State["regex_matched"].([]string)[3] == uidStr { + ctx.SendChain(message.Reply(id), message.Text("猫猫歪头看着你表示咄咄怪事哦~")) + return + } + userInfo, err := catdata.find(gidStr, uidStr) + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + if userInfo == (catInfo{}) || userInfo.Name == "" { + ctx.SendChain(message.Reply(id), message.Text("铲屎官你还没有属于你的主子喔,快去买一只吧!")) + return + } + lastTime := time.Unix(userInfo.ArenaTime, 0) + if time.Since(lastTime).Hours() < 24 { + ctx.SendChain(message.Reply(id), message.Text(userInfo.Name, "已经PK过了,让它休息休息吧")) + return + } + duelStr := ctx.State["regex_matched"].([]string)[3] + duelInfo, err := catdata.find(gidStr, duelStr) + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + if duelInfo == (catInfo{}) || duelInfo.Name == "" { + ctx.SendChain(message.Reply(id), message.Text("他还没有属于他的猫猫,无法PK")) + return + } + lastTime = time.Unix(duelInfo.ArenaTime, 0) + if time.Since(lastTime).Hours() < 24 { + ctx.SendChain(message.Reply(id), message.Text(ctx.CardOrNickName(duelInfo.User), "的", duelInfo.Name, "已经PK过了,让它休息休息吧")) + return + } + /***************************************************************/ + ctx.SendChain(message.Text("等待对方回应。(发送“取消”撤回PK)\n请对方发送“去吧猫猫”接受PK或“拒绝”结束PK")) + recv, cancel := zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule("^(去吧猫猫|取消|拒绝)$"), zero.CheckGroup(ctx.Event.GroupID), zero.CheckUser(zbmath.Str2Int64(duelStr), userInfo.User)).Repeat() + defer cancel() + approve := false + over := time.NewTimer(60 * time.Second) + for { + select { + case <-over.C: + ctx.SendChain(message.Reply(id), message.Text("对方没回应,PK取消")) + return + case c := <-recv: + over.Stop() + switch { + case c.Event.Message.String() == "拒绝" && c.Event.UserID == duelInfo.User: + ctx.SendChain(message.Reply(id), message.Text("对方拒绝了你的PK")) + return + case c.Event.Message.String() == "取消" && c.Event.UserID == userInfo.User: + ctx.SendChain(message.Reply(id), message.Text("你取消了PK")) + return + case c.Event.Message.String() == "去吧猫猫" && c.Event.UserID == duelInfo.User: + approve = true + } + } + if approve { + break + } + } + /***************************************************************/ + now := time.Now().Unix() + winer := userInfo + loser := duelInfo + /***************************************************************/ + mood := false + switch { + case userInfo.Satiety > 50 && rand.Intn(100) > zbmath.Max(userInfo.Mood, 80): + mood = true + winer = duelInfo + loser = userInfo + case duelInfo.Satiety > 50 && rand.Intn(100) > zbmath.Max(duelInfo.Mood, 80): + mood = true + } + if mood { + ctx.SendChain(message.Text(loser.Name, "好像并没有心情PK\n", winer.Name, "获得了比赛胜利")) + money := 10 + rand.Intn(int(winer.Weight)) + if wallet.InsertWalletOf(winer.User, money) == nil { + ctx.SendChain(message.At(winer.User), message.Text("你家的喵喵为你赢得了", money)) + } + winer.ArenaTime = now + err = catdata.insert(gidStr, &winer) + if err == nil { + loser.ArenaTime = now + err = catdata.insert(gidStr, &loser) + } + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + } + return + } + /***************************************************************/ + winLine := math.Min(userInfo.Weight, duelInfo.Weight) + weightLine := (userInfo.Weight + duelInfo.Weight) * rand.Float64() + fatLine := false + if winLine > weightLine-winLine*0.1 && winLine < weightLine+winLine*0.1 { + fatLine = true + } + if fatLine { + ctx.SendChain(message.Reply(id), message.Text(duelInfo.Name, "和", userInfo.Name, "之间并没有PK的意愿呢\nPK结束")) + userInfo.ArenaTime = now + err = catdata.insert(gidStr, &userInfo) + if err == nil { + duelInfo.ArenaTime = now + err = catdata.insert(gidStr, &duelInfo) + } + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + } + } + /***************************************************************/ + winer, loser = pkweight(userInfo, duelInfo) + messageText := make(message.Message, 0, 3) + if rand.Intn(2) == 0 { + messageText = append(messageText, message.Text("天啊,", + strconv.FormatFloat(winer.Weight, 'f', 2, 64), "kg的", winer.Name, + "完美的借力打力,将", strconv.FormatFloat(loser.Weight, 'f', 2, 64), "kg的", loser.Name, "打趴下了")) + } else { + messageText = append(messageText, message.Text("精彩!", strconv.FormatFloat(winer.Weight, 'f', 2, 64), "kg的", winer.Name, + "利用了PK地形,让", strconv.FormatFloat(loser.Weight, 'f', 2, 64), "kg的", loser.Name, "认输了")) + } + money := 10 + rand.Intn(int(winer.Weight)) + if wallet.InsertWalletOf(winer.User, money) == nil { + messageText = append(messageText, message.Text("\n"), message.At(winer.User), message.Text("\n", winer.Name, "为你赢得了", money)) + } else { + messageText = append(messageText, message.Text("\n"), message.At(winer.User), message.Text("\n", winer.Name, "受伤了,所赚的钱全拿来疗伤了")) + } + if rand.Float64()*100 < math.Max(20, loser.Weight) { + loser.Weight -= math.Min(1, loser.Weight/10) * rand.Float64() + messageText = append(messageText, message.Text("\n"), message.At(loser.User), + message.Text("\n", loser.Name, "在PK中受伤了\n在医疗中心治愈过程中体重降低至", strconv.FormatFloat(loser.Weight, 'f', 2, 64))) + } + userInfo.ArenaTime = time.Now().Unix() + err = catdata.insert(gidStr, &winer) + if err == nil { + duelInfo.ArenaTime = time.Now().Unix() + err = catdata.insert(gidStr, &loser) + } + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + } + ctx.Send(messageText) + }) + engine.OnFullMatchGroup([]string{"猫猫排行榜", "喵喵排行榜"}, zero.OnlyGroup, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + gidStr := "group" + strconv.FormatInt(ctx.Event.GroupID, 10) + infoList, err := catdata.getGroupdata(gidStr) + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + if len(infoList) == 0 { + ctx.SendChain(message.Text("没有人养猫哦")) + return + } + messageText := make([]string, 0, 10) + for i, info := range infoList { + if i > 9 { + break + } + messageText = append(messageText, []string{ + strconv.Itoa(i+1) + "." + info.Name + "(" + info.Type + ")", + "体重:" + strconv.FormatFloat(info.Weight, 'f', 2, 64) + "斤", + "主人:" + ctx.CardOrNickName(info.User), "--------------------", + }...) + } + textPic, err := text.RenderToBase64(strings.Join(messageText, "\n"), text.BoldFontFile, 1080, 50) + if err != nil { + return + } + ctx.SendChain(message.Image("base64://" + helper.BytesToString(textPic))) + }) +} + +// PK参数权重 +// 系数= (属性 - 基准属性)/100 +// R = (体重 - 基准属性)*0.05 + (心情 - 心情)*K*0.4 + (饱食度 - 饱食度)*R*0.4 +/**********************体重绝对优势****************************** + // 纯比体重 + // 17,100,100 Vs 130,100,100 + R1 = (17 - 50) * 0.05 = -1.65 + R2 = (130 - 50) * 0.05 = 4 + R2 > R1,R2赢 + // 如果心情好 + // 17,80,100 Vs 130,40,100 + R1 = (17 - 50) * 0.05 + (80 - 40)*(80 - 50)/100 * 0.4 = -1.65 + 4.8 = 3.15 + R2 = (130 - 50) * 0.05 + (40 - 80)*(40 - 50)/100 * 0.4 = 4 + 1.6 = 5.6 + R2 > R1,R2赢 + // 如果肚子饿 + // 17,80,80 Vs 130,40,40 + R1 = (17 - 50) * 0.05 + (80 - 40)*(80 - 50)/100 * 0.4 + (80 - 40)*(80 - 50)/100 * 0.4 = -1.65 + 4.8 + 4.8 = 7.95 + R2 = (130 - 50) * 0.05 + (40 - 80)*(40 - 50)/100 * 0.4 + (80 - 40)*(80 - 50)/100 * 0.4 = 4 + 1.6 + 1.6 = 7.2 + R1 > R2,R1赢 +**********************体重均衡****************************** + // 纯比体重 + // 30,100,100 Vs 60,100,100 + R1 = (30 - 50) * 0.05 = -0.1 + R2 = (60 - 50) * 0.05 = 0.5 + R2 > R1,R2赢 + // 如果心情好 + // 30,80,100 Vs 60,40,100 + R1 = (30 - 50) * 0.05 + (80 - 40)*(80 - 50)/100 * 0.4 = -0.1 + 4.8 = 4.7 + R2 = (60 - 50) * 0.05 + (40 - 80)*(40 - 50)/100 * 0.4 = 0.5 + 1.6 = 2.3 + R1 > R2,R1赢 + // 如果肚子饿 + // 30,80,40 Vs 130,40,80 + R1 = (30 - 50) * 0.05 + (80 - 40)*(80 - 50)/100 * 0.4 + (80 - 40)*(80 - 50)/100 * 0.4 = 4.7 + 1.6= 6.3 + R2 = (130 - 50) * 0.05 + (40 - 80)*(40 - 50)/100 * 0.4 + (80 - 40)*(80 - 50)/100 * 0.4 = 2.3 + 4.8 = 7.1 + R2 > R1,R2赢 +*/ +func pkweight(player1, player2 catInfo) (winer, loser catInfo) { + weightOfplayer1 := (player1.Weight-50)*0.05 + + float64((player1.Mood-player2.Mood)*(player1.Mood-50))*0.4 + + (player1.Satiety-player2.Satiety)*(player1.Satiety-50)*0.4 + weightOfplayer2 := (player2.Weight-50)*0.05 + + float64((player2.Mood-player1.Mood)*(player2.Mood-50))*0.4 + + (player2.Satiety-player1.Satiety)*(player2.Satiety-50)*0.4 + if weightOfplayer1 > weightOfplayer2 { + return player1, player2 + } + return player2, player1 +} diff --git a/plugin/cybercat/catdata.go b/plugin/cybercat/catdata.go new file mode 100644 index 0000000000..195236eee1 --- /dev/null +++ b/plugin/cybercat/catdata.go @@ -0,0 +1,246 @@ +// Package cybercat 云养猫 +package cybercat + +import ( + "sync" + "time" + "os" + "fmt" + "path/filepath" + "path" + + + fcext "github.com/FloatTech/floatbox/ctxext" + "github.com/FloatTech/floatbox/web" + sql "github.com/FloatTech/sqlite" + ctrl "github.com/FloatTech/zbpctrl" + "github.com/FloatTech/zbputils/control" + "github.com/FloatTech/zbputils/ctxext" + "github.com/tidwall/gjson" + "github.com/FloatTech/floatbox/file" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" +) + +const apiURL = "https://api.thecatapi.com/v1/images/" + +var typeEN2ZH = map[string]string{ + "Abyssinian": "阿比西尼亚猫", "Aegean": "爱琴猫", "American Bobtail": "美国短尾猫", "American Curl": "美国卷耳猫", "American Shorthairs": "美洲短毛猫", "American Wirehair": "美国硬毛猫", + "Arabian Mau": "美英猫", "Australian Mist": "澳大利亚雾猫", "Balinese": "巴厘岛猫", "Bambino": "班比诺猫", "Bengal": "孟加拉虎", "Birman": "比尔曼猫", "Bombay": "孟买猫", "British Longhair": "英国长毛猫", + "British Shorthair": "英国短毛猫", "Burmese": "缅甸猫", "Burmilla": "博美拉猫", "California Spangled": "加州闪亮猫", "Chantilly-Tiffany": "查达利/蒂法尼猫", "Chartreux": "夏特鲁斯猫", "Chausie": "非洲狮子猫", + "Cheetoh": "奇多猫", "Colorpoint Shorthair": "重点色短毛猫", "Cornish Rex": "康沃尔-雷克斯猫", "Cymric": "威尔士猫", "Cyprus": "塞浦路斯猫", "Devon Rex": "德文狸猫", "Donskoy": "顿斯科伊猫", "Dragon Li": "中国狸花猫", + "Egyptian Mau": "埃及猫", "European Burmese": "欧洲缅甸猫", "Exotic Shorthair": "异国短毛猫", "Havana Brown": "哈瓦那褐猫", "Himalayan": "喜马拉雅猫", "Japanese Bobtail": "日本短尾猫", "Javanese": "爪哇猫", + "Khao Manee": "泰国御猫", "Korat": "呵叻猫", "Kurilian": "千岛短尾猫", "LaPerm": "拉邦猫", "Maine Coon": "缅因猫", "Malayan": "马来猫", "Manx": "马恩岛猫", "Munchkin": "曼基康猫", "Nebelung": "内华达猫", + "Norwegian Forest Cat": "挪威森林猫", "Ocicat": "欧西猫", "Oriental Shorthair": "东方短毛猫", "Persian": "波斯猫", "Pixie-bob": "北美洲短毛猫", "Ragamuffin": "褴褛猫", "Ragdoll": "布偶猫", + "Russian Blue": "俄罗斯蓝猫", "Savannah": "沙凡那猫", "Scottish Fold": "苏格兰折耳猫", "Selkirk Rex": "塞尔凯克卷毛猫", "Siamese": "暹罗猫", "Siberian": "西伯利亚猫", "Singapura": "新加坡猫", "Snowshoe": "雪鞋猫", + "Somali": "索马里猫", "Sphynx": "斯芬克斯猫", "Tonkinese": "东京猫", "Toyger": "玩具虎猫", "Turkish Angora": "土耳其安哥拉猫", + "Turkish Van": "土耳其梵猫", "York Chocolate": "约克巧克力猫", "Cymic": "金力克长毛猫"} + +var typeZH2Breeds = map[string]string{ + "阿比西尼亚猫": "abys", "爱琴猫": "aege", "美国短尾猫": "abob", "美国卷耳猫": "acur", "美洲短毛猫": "asho", "美国硬毛猫": "awir", "美英猫": "amau", "澳大利亚雾猫": "amis", "巴厘岛猫": "bali", + "班比诺猫": "bamb", "孟加拉虎": "beng", "比尔曼猫": "birm", "孟买猫": "bomb", "英国长毛猫": "bslo", "英国短毛猫": "bsho", "缅甸猫": "bure", "博美拉猫": "buri", "加州闪亮猫": "cspa", + "查达利/蒂法尼猫": "ctif", "夏特鲁斯猫": "char", "非洲狮子猫": "chau", "奇多猫": "chee", "重点色短毛猫": "csho", "康沃尔-雷克斯猫": "crex", "威尔士猫": "cymr", "塞浦路斯猫": "cypr", + "德文狸猫": "drex", "顿斯科伊猫": "dons", "中国狸花猫": "lihu", "埃及猫": "emau", "欧洲缅甸猫": "ebur", "异国短毛猫": "esho", "哈瓦那褐猫": "hbro", "喜马拉雅猫": "hima", "日本短尾猫": "jbob", + "爪哇猫": "java", "泰国御猫": "khao", "呵叻猫": "kora", "千岛短尾猫": "kuri", "拉邦猫": "lape", "缅因猫": "mcoo", "马来猫": "mala", "马恩岛猫": "manx", "曼基康猫": "munc", "内华达猫": "nebe", + "挪威森林猫": "norw", "欧西猫": "ocic", "东方短毛猫": "orie", "波斯猫": "pers", "北美洲短毛猫": "pixi", "褴褛猫": "raga", "布偶猫": "ragd", "俄罗斯蓝猫": "rblu", "沙凡那猫": "sava", + "苏格兰折耳猫": "sfol", "塞尔凯克卷毛猫": "srex", "暹罗猫": "siam", "西伯利亚猫": "sibe", "新加坡猫": "sing", "雪鞋猫": "snow", "索马里猫": "soma", "斯芬克斯猫": "sphy", "东京猫": "tonk", + "玩具虎猫": "toyg", "土耳其安哥拉猫": "tang", "土耳其梵猫": "tvvan", "约克巧克力猫": "ycho", "金力克长毛猫": "cymi"} + +type catdb struct { + db sql.Sqlite + sync.RWMutex +} + +type catInfo struct { + User int64 // 主人 + Name string // 喵喵名称 + Type string // 品种 + Satiety float64 // 饱食度 + Mood int // 心情 + Weight float64 // 体重 + LastTime int64 // 上次喂养时间 + Work int64 // 打工时间 + ArenaTime int64 // 上次PK时间 + Food float64 // 食物数量 + Picurl string // 猫猫图片 +} + +func (c *catInfo) avatar(Gid int64) string { + cache := "data/cybercat/cache" // 指定缓存路径 + cache = path.Join(engine.DataFolder(), "cache") + imgname := fmt.Sprintf("%d_%d", c.User, Gid) + imgfile := filepath.Join(cache, c.Type+imgname+".png") + aimgfile := filepath.Join(file.BOTPATH, imgfile) + + if _, err := os.Stat(cache); os.IsNotExist(err) { + err := os.MkdirAll(cache, 0755) + if err != nil { + fmt.Println("Error creating cache directory:", err) + return err.Error() + } + } + if file.IsNotExist(aimgfile) { + breed := c.Type + data, err := web.GetData(apiURL + "search?has_breeds=" + breed) + if err != nil { + fmt.Println("Error fetching avatar URL:", err) + return err.Error() // 返回错误信息 + } + imgurl := gjson.ParseBytes(data).Get("0.url").String() + imgdata, err := web.GetData(imgurl) + if err != nil { + return "错误:未能解析图片URL" + } + var f *os.File + f, err = os.Create(aimgfile) // 使用 aimgfile 作为文件路径 + if err != nil { + fmt.Println("Error creating file:", err) + return err.Error() // 返回错误信息 + } + defer f.Close() + _, err = f.Write([]byte(imgdata)) // 写入图片数据 + if err != nil { + fmt.Println("Error writing file:", err) + return err.Error() // 返回错误信息 + } + } + return "file:///" + aimgfile // 返回文件协议的完整路径 +} + +var ( + dbpath = "data/cybercat/catdata.db" + catdata = &catdb{db: sql.New(dbpath), + } + engine = control.Register("cybercat", &ctrl.Options[*zero.Ctx]{ + DisableOnDefault: false, + Brief: "云养猫", + Help: "一款既能能赚钱(?)又能看猫的养成类插件\n----------指令----------\n" + + "- 吸猫\n(随机返回一只猫)\n- 吸xxx猫\n(吸指定猫种的猫)\n- 买猫\n- 买xxx猫\n- 买n袋猫粮\n- 喂猫n斤猫粮\n" + + "- 猫猫打工[1-9]小时\n- 撸猫\n- 猫猫状态\n- 猫猫改名叫xxx\n" + + "- 喵喵pk@对方QQ\n- 猫猫排行榜\n" + + "\n---------注意事项---------" + + "\n1.科学养猪(划去)\n" + + "猫店开门时间为6点-21点\n喂猫时间为6点-9点、11点-14点、17点-20点;\n工作时间为6点-21点" + + "\n2.一袋有五斤猫粮" + + "\n3.猫猫心情影响吃饭饭和打工" + + "\n4.越重的猫猫饭量越大呢!" + + "\n5.一天只能打工一次,打工期间的猫猫无法喂养哦" + + "\n6.品种为猫娘的猫猫可以使用“上传猫猫照片”更换图片", + PrivateDataFolder: "cybercat", + }).ApplySingle(ctxext.DefaultSingle) + getdb = fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { + dbpath = engine.DataFolder() + "catdata.db" + catdata.db = sql.New(dbpath) + err := catdata.db.Open(time.Hour * 24) + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return false + } + return true + }) +) + +func init() { + engine.OnRegex(`^吸(.*猫)$`).SetBlock(true).Handle(func(ctx *zero.Ctx) { + typeOfcat := ctx.State["regex_matched"].([]string)[1] + if typeOfcat == "猫" { + typeName, temperament, description, url, err := suineko() + if err != nil { + ctx.SendChain(message.Text("[ERROR]: ", err)) + return + } + ctx.SendChain(message.Image(url), message.Text("品种: ", typeName, + "\n气质:\n", temperament, "\n描述:\n", description)) + return + } + breeds, ok := typeZH2Breeds[typeOfcat] + if !ok { + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("没有相关该品种的猫图")) + return + } + picurl, err := getPicByBreed(breeds) + if err != nil { + ctx.SendChain(message.Text("[ERROR]: ", err)) + return + } + ctx.SendChain(message.Text("品种: ", typeOfcat), message.Image(picurl)) + }) +} + +func suineko() (typeName, temperament, description, url string, err error) { + data, err := web.GetData(apiURL + "search?has_breeds=1") + if err != nil { + return + } + picID := gjson.ParseBytes(data).Get("0.id").String() + picdata, err := web.GetData(apiURL + picID) + if err != nil { + return + } + name := gjson.ParseBytes(picdata).Get("breeds.0.name").String() + return typeEN2ZH[name], gjson.ParseBytes(picdata).Get("breeds.0.temperament").String(), gjson.ParseBytes(picdata).Get("breeds.0.description").String(), gjson.ParseBytes(picdata).Get("url").String(), nil +} + +func getPicByBreed(catBreed string) (url string, err error) { + data, err := web.GetData(apiURL + "search?breed_ids=" + catBreed) + if err != nil { + return + } + return gjson.ParseBytes(data).Get("0.url").String(), nil +} + +func (sql *catdb) insert(gid string, dbInfo *catInfo) error { + sql.Lock() + defer sql.Unlock() + err := sql.db.Create(gid, &catInfo{}) + if err != nil { + return err + } + return sql.db.Insert(gid, dbInfo) +} + +func (sql *catdb) find(gid, uid string) (dbInfo catInfo, err error) { + sql.Lock() + defer sql.Unlock() + err = sql.db.Create(gid, &catInfo{}) + if err != nil { + return + } + if !sql.db.CanFind(gid, "where user = "+uid) { + return catInfo{}, nil // 规避没有该用户数据的报错 + } + err = sql.db.Find(gid, &dbInfo, "where user = "+uid) + return +} + +func (sql *catdb) del(gid, uid string) error { + sql.Lock() + defer sql.Unlock() + return sql.db.Del(gid, "where user = "+uid) +} + +func (sql *catdb) delcat(gid, uid string) error { + sql.Lock() + defer sql.Unlock() + dbInfo := catInfo{} + _ = sql.db.Find(gid, &dbInfo, "where user = "+uid) + newInfo := catInfo{ + User: dbInfo.User, + Food: dbInfo.Food, + } + return sql.db.Insert(gid, &newInfo) +} + +func (sql *catdb) getGroupdata(gid string) (list []catInfo, err error) { + sql.RLock() + defer sql.RUnlock() + info := catInfo{} + err = sql.db.FindFor(gid, &info, "order by Weight DESC", func() error { + if info.Name != "" { + list = append(list, info) + } + return nil + }) + return +} + diff --git a/plugin/cybercat/keepcat.go b/plugin/cybercat/keepcat.go new file mode 100644 index 0000000000..07b63ad644 --- /dev/null +++ b/plugin/cybercat/keepcat.go @@ -0,0 +1,371 @@ +// Package cybercat 云养猫 +package cybercat + +import ( + "math" + "math/rand" + "strconv" + "time" + + "github.com/FloatTech/AnimeAPI/wallet" + zbmath "github.com/FloatTech/floatbox/math" + "github.com/FloatTech/zbputils/ctxext" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" +) + +func init() { + engine.OnRegex(`^喂猫((\d+(.\d+)?)斤猫粮)?|猫猫状态$`, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + id := ctx.Event.MessageID + gidStr := "group" + strconv.FormatInt(ctx.Event.GroupID, 10) + uidStr := strconv.FormatInt(ctx.Event.UserID, 10) + userInfo, err := catdata.find(gidStr, uidStr) + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + if userInfo == (catInfo{}) || userInfo.Name == "" { + ctx.SendChain(message.Reply(id), message.Text("铲屎官你还没有属于你的主子喔,快去买一只吧!")) + return + } + /*************************判断指令类型**************************************/ + cmd := false // false表示在喂猫 + if ctx.State["regex_matched"].([]string)[0] == "猫猫状态" { + cmd = true + } + /**************************获取工作状态*************************************/ + stauts := "休闲中" + money, workEnd := userInfo.settleOfWork(gidStr) + switch { + case !cmd && !workEnd: + ctx.SendChain(message.Reply(id), message.Text(userInfo.Name, "还在努力打工,没有回来呢")) + return + case cmd && !workEnd: + overwork := time.Unix(userInfo.Work/10, 0).Add(time.Hour * time.Duration(userInfo.Work%10)) + stauts = overwork.Format("工作中\n(将在01月02日15:04下班)") + case cmd && money > 0: + stauts = "从工作回来休息中\n 为你赚了" + strconv.Itoa(money) + } + now := time.Now().Hour() + if !cmd && ((now < 6 || (now > 8 && now < 11) || (now > 14 && now < 17) || now > 21) && (userInfo.Satiety > 50 || rand.Intn(3) == 1)) { + if userInfo.Satiety > 50 { + ctx.SendChain(message.Text("猫猫拍了拍饱饱的肚子表示并不饿呢")) + return + } + ctx.SendChain(message.Text("猫猫只想和你一起吃传统早中晚饭咧")) + return + } + /****************************计算食物数量***********************************/ + food := 0.0 + if !cmd { + stauts = "刚刚的食物很美味" + // 如果没有指定猫粮就 (1 + 猫粮/5*x )斤猫粮 + if ctx.State["regex_matched"].([]string)[2] != "" { + food, _ = strconv.ParseFloat(ctx.State["regex_matched"].([]string)[2], 64) + } else { + food = math.Max(1.0+math.Max(userInfo.Food-1, 0)/5*rand.Float64(), (100-userInfo.Satiety)*userInfo.Weight/200) + } + switch { + case userInfo.Food == 0 || userInfo.Food < food: + ctx.SendChain(message.Reply(id), message.Text("铲屎官你已经没有足够的猫粮了")) + return + // 如果猫粮太多就只吃一点,除非太饿了 + case food > 5 && (rand.Intn(10) < 8 || userInfo.Satiety < 30): + food = 5 + stauts = "食物实在太多了!" + case food < 0.5: + ctx.SendChain(message.Reply(id), message.Text(userInfo.Name, "骂骂咧咧的走了")) + return + } + } + /****************************空闲时间猫体力的减少计算***********************************/ + subtime := 0.0 + if userInfo.LastTime != 0 { + lastTime := time.Unix(userInfo.LastTime, 0) + subtime = time.Since(lastTime).Hours() + userInfo.LastTime = time.Unix(userInfo.LastTime, 0).Add(time.Duration(subtime) * time.Hour).Unix() + } + // 频繁喂猫减少心情 + if !cmd && subtime < 8 { + userInfo.Mood -= 5 + if userInfo.Mood < 0 { + userInfo.Mood = 0 + } + if rand.Intn(10) < 6 && subtime < 2 && userInfo.Satiety > 90 { + if err = catdata.insert(gidStr, &userInfo); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + ctx.SendChain(message.Reply(id), message.Text(userInfo.Name, "肚子已经很饱了,吃不动了")) + return + } + } + // 当饱食度降到0以下,体力减少 + if subtime > 1 { + userInfo.Satiety -= subtime * 4 + userInfo = userInfo.settleOfWeight() + userInfo.Mood -= int(subtime) + userInfo.LastTime = time.Now().Unix() + } + /***************************太饿了偷吃************************************/ + userInfo.Food -= food + if userInfo.Food > 0 && (rand.Intn(10) == 1 || userInfo.Satiety < 10) { + eat := userInfo.Food / 5 * rand.Float64() + userInfo = userInfo.settleOfSatiety(eat) + userInfo.Mood += int(eat) + userInfo = userInfo.settleOfWeight() + } + /***************************整体结算,判断当前的心情是否继续************************************/ + userInfo = userInfo.settleOfData() + if !cmd && userInfo.Satiety > 80 && rand.Intn(100) > zbmath.Max(userInfo.Mood*2-userInfo.Mood/2, 50) { + ctx.SendChain(message.Reply(id), message.Text(userInfo.Name, "好像并没有心情吃东西")) + return + } + /****************************结算食物***********************************/ + userInfo = userInfo.settleOfSatiety(food) + userInfo = userInfo.settleOfWeight() + switch { + case userInfo.Mood <= 0 && rand.Intn(100) < 10: + if err = catdata.delcat(gidStr, uidStr); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + ctx.SendChain(message.Reply(id), message.Text("猫猫", userInfo.Name, "和你的感情淡了,选择了离家出走")) + return + case userInfo.Weight <= 0 && subtime > 72: // 三天不喂食就死 + if err = catdata.delcat(gidStr, uidStr); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + ctx.SendChain(message.Reply(id), message.Text("猫猫", userInfo.Name, "由于瘦骨如柴,已经难以存活去世了...")) + return + case userInfo.Weight >= 100: + if 100*rand.Float64() > math.Max(userInfo.Weight-100, 10) { // 越胖越容易成功 + if err = catdata.delcat(gidStr, uidStr); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + ctx.SendChain(message.Reply(id), message.Text("猫猫", userInfo.Name, "由于太胖了,已经难以存活去世了...")) + return + } + ctx.SendChain(message.Reply(id), message.Text("渡劫成功!", strconv.FormatFloat(userInfo.Weight, 'f', 2, 64), "kg的", + userInfo.Name, "进化成猫娘了!\n可以发送“上传猫猫照片”修改图像了喔")) + userInfo.Type = "猫娘" + userInfo.Weight = 3 + rand.Float64()*10 + } + /****************************保存数据***********************************/ + userInfo.LastTime = time.Now().Unix() + userInfo.Mood += int(userInfo.Satiety)/5 - int(userInfo.Weight)/10 + userInfo = userInfo.settleOfData() + avatarResult := userInfo.avatar(ctx.Event.GroupID) + if err = catdata.insert(gidStr, &userInfo); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + if !cmd && userInfo.Satiety < 80 { + stauts = "完全没有饱" + } + ctx.SendChain(message.Reply(id), message.Text(userInfo.Name, "当前信息如下:\n"), + message.Image(avatarResult), + message.Text("品种: "+userInfo.Type, + "\n饱食度: ", strconv.FormatFloat(userInfo.Satiety, 'f', 0, 64), + "\n心情: ", userInfo.Mood, + "\n体重: ", strconv.FormatFloat(userInfo.Weight, 'f', 2, 64), + "\n状态:", stauts, + "\n\n你的剩余猫粮(斤): ", strconv.FormatFloat(userInfo.Food, 'f', 2, 64))) + }) + engine.OnRegex(`^猫猫打工(([1-9])小时)?$`, zero.OnlyGroup, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + id := ctx.Event.MessageID + gidStr := "group" + strconv.FormatInt(ctx.Event.GroupID, 10) + uidStr := strconv.FormatInt(ctx.Event.UserID, 10) + userInfo, err := catdata.find(gidStr, uidStr) + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + if userInfo == (catInfo{}) || userInfo.Name == "" { + ctx.SendChain(message.Reply(id), message.Text("铲屎官你还没有属于你的主子喔,快去买一只吧!")) + return + } + /***************************************************************/ + _, workEnd := userInfo.settleOfWork(gidStr) + if !workEnd { + ctx.SendChain(message.Reply(id), message.Text(userInfo.Name, "还在努力打工,没有回来呢")) + return + } + if userInfo.Work > 0 && time.Unix(userInfo.Work/10, 0).Day() == time.Now().Day() && rand.Intn(100) < 10 { + ctx.SendChain(message.Reply(id), message.Text(userInfo.Name, "已经很累了,你不能这么资本")) + return + } + /***************************************************************/ + subtime := 0.0 + if userInfo.LastTime != 0 { + lastTime := time.Unix(userInfo.LastTime, 0) + subtime = time.Since(lastTime).Hours() + userInfo.LastTime = time.Unix(userInfo.LastTime, 0).Add(time.Duration(subtime) * time.Hour).Unix() + } + userInfo.Satiety -= subtime + userInfo.Mood -= int(subtime) + userInfo = userInfo.settleOfWeight() + if userInfo.Weight < 0 { + if err = catdata.delcat(gidStr, uidStr); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + ctx.SendChain(message.Reply(id), message.Text("由于你长时间没有喂猫猫,", userInfo.Name, "已经饿死了...")) + return + } + /***************************************************************/ + userInfo = userInfo.settleOfData() + if userInfo.Satiety > 90 && rand.Intn(100) > zbmath.Max(userInfo.Mood*2-userInfo.Mood/2, 50) { + ctx.SendChain(message.Reply(id), message.Text(userInfo.Name, "好像并没有心情去工作")) + return + } + /***************************************************************/ + workTime := 1 + rand.Intn(9) + if ctx.State["regex_matched"].([]string)[2] != "" { + workTime, _ = strconv.Atoi(ctx.State["regex_matched"].([]string)[2]) + } + userInfo.Work = time.Now().Unix()*10 + int64(workTime) + if err = catdata.insert(gidStr, &userInfo); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + ctx.SendChain(message.Reply(id), message.Text(userInfo.Name, "开始去打工了")) + }) + engine.OnFullMatchGroup([]string{"逗猫", "撸猫", "rua猫", "mua猫", "玩猫", "摸猫"}, zero.OnlyGroup, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + id := ctx.Event.MessageID + gidStr := "group" + strconv.FormatInt(ctx.Event.GroupID, 10) + uidStr := strconv.FormatInt(ctx.Event.UserID, 10) + userInfo, err := catdata.find(gidStr, uidStr) + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + if userInfo == (catInfo{}) || userInfo.Name == "" { + ctx.SendChain(message.Reply(id), message.Text("铲屎官你还没有属于你的主子喔,快去买一只吧!")) + return + } + _, workEnd := userInfo.settleOfWork(gidStr) + if !workEnd { + ctx.SendChain(message.Reply(id), message.Text(userInfo.Name, "还在努力打工,没有回来呢")) + return + } + /***************************************************************/ + subtime := 0.0 + if userInfo.LastTime != 0 { + lastTime := time.Unix(userInfo.LastTime, 0) + subtime = time.Since(lastTime).Hours() + userInfo.LastTime = time.Unix(userInfo.LastTime, 0).Add(time.Duration(subtime) * time.Hour).Unix() + } + userInfo.Satiety -= subtime + userInfo.Mood -= int(subtime) + userInfo = userInfo.settleOfWeight() + if userInfo.Weight < 0 { + if err = catdata.delcat(gidStr, uidStr); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + ctx.SendChain(message.Reply(id), message.Text("由于你长时间没有喂猫猫,", userInfo.Name, "已经饿死了...")) + return + } + /***************************************************************/ + choose := rand.Intn(2) + text := "被调教得屁股高跷呢!心情提高至" + switch choose { + case 0: + text = "不耐烦的走掉了,心情降低至" + userInfo.Mood -= rand.Intn(zbmath.Max(1, userInfo.Mood)) + case 1: + userInfo.Mood += rand.Intn(100) + } + userInfo = userInfo.settleOfData() + if err = catdata.insert(gidStr, &userInfo); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + ctx.SendChain(message.Reply(id), message.Text(userInfo.Name, text, userInfo.Mood)) + }) +} + +// 饱食度结算 +func (inf *catInfo) settleOfSatiety(food float64) catInfo { + if food > 0 && inf.Satiety < 30 && rand.Intn(100) <= inf.Mood/3 { + food *= 4 + } + inf.Satiety += (food * 100 / math.Max(1, inf.Weight/2)) + return *inf +} + +// 体重结算 +func (inf *catInfo) settleOfWeight() catInfo { + if inf.Weight < 0 { + satiety := math.Min((-inf.Weight)*7, inf.Satiety) + inf.Weight += satiety + inf.Satiety -= satiety + } + switch { + case inf.Satiety > 100: + inf.Weight += (inf.Satiety - 50) / 100 + case inf.Satiety < 0: + inf.Weight += inf.Satiety / 10 + if inf.Weight < 0 { + needFood := math.Min(-inf.Weight*5, inf.Food) + inf.Food -= needFood + inf.Weight += needFood / 5 + } + } + return *inf +} + +// 整体数据结算 +func (inf *catInfo) settleOfData() catInfo { + if inf.Satiety > 100 { + inf.Satiety = 100 + } else if inf.Satiety < 0 { + inf.Satiety = 0 + } + if inf.Mood > 100 { + inf.Mood = 100 + } else if inf.Mood < 0 { + inf.Mood = 0 + } + if inf.Weight < 0 { + inf.Weight = -5 + } + if inf.Food < 0 { + inf.Food = 0 + } + return *inf +} + +// 打工结算 +func (inf *catInfo) settleOfWork(gid string) (int, bool) { + workTime := inf.Work % 10 + if workTime <= 0 { + return 0, true + } + lastTime := time.Unix(inf.Work/10, 0) + subtime := time.Since(lastTime).Hours() + if subtime < float64(workTime) { + return 0, false + } + getFood := 5 * rand.Float64() + mood := rand.Intn(int(workTime)) + if rand.Intn(5) < 3 { // 60%受饿 + getFood = -(getFood + float64(workTime)*rand.Float64()) + mood *= -3 + } + inf.Satiety += getFood * 100 / math.Max(1, inf.Weight) + inf.Mood += mood + inf.Work = time.Now().Unix() * 10 + inf.LastTime = time.Unix(inf.LastTime, 0).Add(time.Duration(workTime) * time.Hour).Unix() + if catdata.insert(gid, inf) != nil { + return 0, true + } + getmoney := 10 + rand.Intn(10*int(workTime)) + if wallet.InsertWalletOf(inf.User, getmoney) != nil { + return 0, true + } + return getmoney, true +} diff --git a/plugin/cybercat/store.go b/plugin/cybercat/store.go new file mode 100644 index 0000000000..855883af80 --- /dev/null +++ b/plugin/cybercat/store.go @@ -0,0 +1,321 @@ +// Package cybercat 云养猫 +package cybercat + +import ( + "math/rand" + "strconv" + "strings" + "time" + + "github.com/FloatTech/AnimeAPI/wallet" + "github.com/FloatTech/floatbox/process" + "github.com/FloatTech/zbputils/ctxext" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" +) + +func init() { + engine.OnRegex(`^买(.*猫)$`, zero.OnlyGroup, func(ctx *zero.Ctx) bool { + if now := time.Now().Hour(); now >= 6 && now <= 20 { + return true + } + ctx.SendChain(message.Text("猫店已经关门了,早上六点后再来吧")) + return false + }, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + id := ctx.Event.MessageID + gidStr := "group" + strconv.FormatInt(ctx.Event.GroupID, 10) + uidStr := strconv.FormatInt(ctx.Event.UserID, 10) + userInfo, err := catdata.find(gidStr, uidStr) + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + /*******************************************************/ + if userInfo != (catInfo{}) && userInfo.Name != "" { + id = ctx.SendChain(message.Reply(id), message.Text("你居然背着你家喵喵出来找小三!")) + if rand.Intn(100) < 20 { + process.SleepAbout1sTo2s() + if rand.Intn(50) == 30 { + if catdata.del(gidStr, uidStr) == nil { + ctx.SendChain(message.Reply(id), message.Text("喔,天啊!你家喵喵带着所有猫粮离家出走了!\n你失去了所有!")) + } + } else if catdata.delcat(gidStr, uidStr) == nil { + ctx.SendChain(message.Reply(id), message.Text("喔,天啊!你家喵喵离家出走了!\n你失去了喵喵!")) + } + } + return + } + /*******************************************************/ + lastTime := time.Unix(userInfo.LastTime, 0).Day() + if lastTime != time.Now().Day() { + userInfo.Work = 0 + userInfo.LastTime = 0 + } + /*******************************************************/ + userInfo.User = ctx.Event.UserID + typeOfcat := ctx.State["regex_matched"].([]string)[1] + if userInfo.LastTime != 0 && typeOfcat == "猫" { + ctx.SendChain(message.Reply(id), message.Text("抱歉,一天只能去猫店一次")) + return + } else if userInfo.Work > 1 { + ctx.SendChain(message.Reply(id), message.Text("抱歉,一天只能选购两次")) + return + } + /*******************************************************/ + if typeOfcat == "猫" { + userInfo.LastTime = time.Now().Unix() + } else { + userInfo.Work++ + } + if err = catdata.insert(gidStr, &userInfo); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + /*******************************************************/ + if wallet.GetWalletOf(ctx.Event.UserID) < 100 { + ctx.SendChain(message.Reply(id), message.Text("一只喵喵官方售价100哦;\n你身上没有足够的钱,快去赚钱吧~")) + return + } + messageText := make(message.Message, 0, 3) + messageText = append(messageText, message.Reply(id)) + money := 100 + if rand.Intn(10) == 5 { + money = rand.Intn(50) + 50 + messageText = append(messageText, message.Text("你前往的喵喵店时发现正好有活动,\n一只喵喵现在只需要", money, "\n------------------------\n")) + } + /*******************************************************/ + if typeOfcat == "猫" { + nameMap := make([]string, 0, len(typeZH2Breeds)) + for zhName := range typeZH2Breeds { + nameMap = append(nameMap, zhName) + } + if rand.Intn(100) >= 90 { + nameMap = append(nameMap, "猫娘") + } + nameList := make([]int, 0, 5) + for i := 0; i < 5; i++ { + nameList = append(nameList, rand.Intn(len(nameMap))) + } + messageText = append(messageText, message.Text("当前猫店售卖以下几只猫:", + "\n1.", nameMap[nameList[0]], + "\n2.", nameMap[nameList[1]], + "\n3.", nameMap[nameList[2]], + "\n4.", nameMap[nameList[3]], + "\n5.", nameMap[nameList[4]], + "\n请发送对应序号进行购买或“取消”取消购买")) + ctx.Send(messageText) + typeRecv, typeCancel := zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule("^([1-5]|取消)$"), zero.CheckGroup(ctx.Event.GroupID), zero.CheckUser(userInfo.User)).Repeat() + defer typeCancel() + approve := false + over := time.NewTimer(60 * time.Second) + for { + select { + case <-over.C: + ctx.SendChain(message.Reply(id), message.Text("你考虑的时间太长了,喵喵店都关门了!下次再来买哦~")) + return + case c := <-typeRecv: + over.Stop() + switch c.Event.Message.String() { + case "取消": + ctx.SendChain(message.Reply(c.Event.MessageID), message.Text("欢迎你的下次光临")) + return + default: + index, _ := strconv.Atoi(c.Event.Message.String()) + typeOfcat = nameMap[nameList[index-1]] + approve = true + } + } + if approve { + break + } + } + } + /*******************************************************/ + picurl, _ := getPicByBreed(typeZH2Breeds[typeOfcat]) + satiety := 90 * rand.Float64() // 饱食度 + mood := 50 + rand.Intn(50) // 心情 + weight := 2 + 8*rand.Float64() // 体重 + /*******************************************************/ + messageText = message.Message{message.Reply(id)} + messageText = append(messageText, message.Text("经过询问后得知它当前的信息为:\n"), + message.Image(picurl), + message.Text("品种: ", typeOfcat, + "\n当前饱食度: ", strconv.FormatFloat(satiety, 'f', 0, 64), + "\n当前心情: ", mood, + "\n当前体重: ", strconv.FormatFloat(weight, 'f', 2, 64), + "\n\n你想要买这只猫猫,\n请发送“叫xxx”为它取个名字吧~\n(发送“否”取消购买)")) + ctx.Send(messageText) + recv, cancel := zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule("^(叫.*|否)$"), zero.CheckGroup(ctx.Event.GroupID), zero.CheckUser(userInfo.User)).Repeat() + defer cancel() + approve := false + over := time.NewTimer(90 * time.Second) + for { + select { + case <-over.C: + ctx.SendChain(message.Reply(id), message.Text("你考虑的时间太长了,喵喵店都关门了!下次再来买哦~")) + return + case c := <-recv: + over.Stop() + switch c.Event.Message.String() { + case "否": + ctx.SendChain(message.Reply(c.Event.MessageID), message.Text("欢迎你的下次光临")) + return + default: + id = c.Event.MessageID + userInfo.Name = strings.ReplaceAll(c.Event.Message.String(), "叫", "") + if userInfo.Name == "" || len(userInfo.Name) > 15 { + over.Reset(90 * time.Second) + ctx.SendChain(message.Reply(id), message.Text("请输入正确的猫名")) + continue + } + approve = true + } + } + if approve { + break + } + } + messageText = message.Message{message.Reply(id)} + if rand.Intn(5) == 1 { + mood += rand.Intn(30) + if mood > 100 { + mood = 100 + } + messageText = append(messageText, message.Text("这只喵喵好像很喜欢这个名字,\n")) + } + userInfo.Type = typeOfcat + userInfo.Satiety = satiety + userInfo.Mood = mood + userInfo.Weight = weight + userInfo.LastTime = 0 + userInfo.Work = 0 + userInfo.Picurl = picurl + if err = wallet.InsertWalletOf(ctx.Event.UserID, -money); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + if err = catdata.insert(gidStr, &userInfo); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + messageText = append(messageText, message.Text("恭喜你买了一只喵喵")) + ctx.Send(messageText) + }) + engine.OnRegex(`^买((\d+)袋)?猫粮$`, zero.OnlyGroup, func(ctx *zero.Ctx) bool { + if now := time.Now().Hour(); now >= 6 && now <= 20 { + return true + } + ctx.SendChain(message.Text("猫店已经关门了,早上六点后再来吧")) + return false + }, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + id := ctx.Event.MessageID + gidStr := "group" + strconv.FormatInt(ctx.Event.GroupID, 10) + uidStr := strconv.FormatInt(ctx.Event.UserID, 10) + /*******************************************************/ + mun := 1.0 + if ctx.State["regex_matched"].([]string)[2] != "" { + mun, _ = strconv.ParseFloat(ctx.State["regex_matched"].([]string)[2], 64) + if mun > 100 { + ctx.SendChain(message.Reply(id), message.Text("猫猫店库存只有100袋,无法供给")) + return + } + if mun < 1 { + ctx.SendChain(message.Reply(id), message.Text("请输入正确的数量")) + return + } + } + /*******************************************************/ + userInfo, err := catdata.find(gidStr, uidStr) + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + if userInfo.Food > 50 { + ctx.SendChain(message.Reply(id), message.Text("你家的猫粮已经装满仓库(上限50斤)了!")) + return + } + /*******************************************************/ + if wallet.GetWalletOf(ctx.Event.UserID) < 10*int(mun) { + ctx.SendChain(message.Reply(id), message.Text("一袋猫粮官方售价10哦;\n你身上没有足够的钱,快去赚钱吧~")) + return + } + messageText := make(message.Message, 0, 3) + messageText = append(messageText, message.Reply(id)) + userInfo.User = ctx.Event.UserID + foodmoney := 10 + if rand.Intn(10) < 3 { + foodmoney = rand.Intn(5) + 5 + messageText = append(messageText, message.Text("你前往的喵喵店时发现正好有活动,\n一袋猫粮现在只需要", foodmoney, ";\n")) + } + foodmoney *= int(mun) + userInfo.Food += 5 * mun + if wallet.InsertWalletOf(ctx.Event.UserID, -foodmoney) != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + if err = catdata.insert(gidStr, &userInfo); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + messageText = append(messageText, message.Text("你购买了", mun, "袋,共计", foodmoney, "\n当前猫粮有", strconv.FormatFloat(userInfo.Food, 'f', 2, 64), "斤")) + ctx.Send(messageText) + }) + engine.OnPrefixGroup([]string{"喵喵改名叫", "猫猫改名叫"}, zero.OnlyGroup, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + id := ctx.Event.MessageID + gidStr := "group" + strconv.FormatInt(ctx.Event.GroupID, 10) + uidStr := strconv.FormatInt(ctx.Event.UserID, 10) + userInfo, err := catdata.find(gidStr, uidStr) + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + if userInfo == (catInfo{}) || userInfo.Name == "" { + ctx.SendChain(message.Reply(id), message.Text("铲屎官你还没有属于你的主子喔,快去买一只吧!")) + return + } + newName := strings.TrimSpace(ctx.State["args"].(string)) + switch { + case newName == "": + userInfo.Name = userInfo.Type + case len(newName) > 6*3: + ctx.SendChain(message.Reply(id), message.Text("请输入正确的名字")) + return + default: + userInfo.Name = newName + } + if err = catdata.insert(gidStr, &userInfo); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + ctx.SendChain(message.Reply(id), message.Text("修改成功")) + }) + engine.OnPrefix("上传猫猫照片", zero.OnlyGroup, getdb, func(ctx *zero.Ctx) bool { + id := ctx.Event.MessageID + gidStr := "group" + strconv.FormatInt(ctx.Event.GroupID, 10) + uidStr := strconv.FormatInt(ctx.Event.UserID, 10) + userInfo, err := catdata.find(gidStr, uidStr) + if err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return false + } + if userInfo == (catInfo{}) || userInfo.Name == "" { + ctx.SendChain(message.Reply(id), message.Text("铲屎官你还没有属于你的主子喔,快去买一只吧!")) + return false + } + if userInfo.Type != "猫娘" { + ctx.SendChain(message.Reply(id), message.Text("只有猫娘才能资格更换图片喔")) + return false + } + return zero.MustProvidePicture(ctx) + }).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) { + gidStr := "group" + strconv.FormatInt(ctx.Event.GroupID, 10) + uidStr := strconv.FormatInt(ctx.Event.UserID, 10) + userInfo, _ := catdata.find(gidStr, uidStr) + userInfo.Picurl = ctx.State["image_url"].([]string)[0] + if err := catdata.insert(gidStr, &userInfo); err != nil { + ctx.SendChain(message.Text("[ERROR]:", err)) + return + } + ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("成功")) + }) +}