diff --git a/README.md b/README.md index 05828a3393..1b36f9df17 100644 --- a/README.md +++ b/README.md @@ -1009,22 +1009,6 @@ print("run[CQ:image,file="+j["img"]+"]") - 注:刷新文件夹较慢,请耐心等待刷新完成,会提示“成功”。 - -
- 抽wife - - `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativewife"` - - - [x] 抽wife[@xxx] - - - [x] 添加wife[名字][图片] - - - [x] 删除wife[名字] - - - [x] [让 | 不让]所有人均可添加wife - - - 注:不同群添加后不会重叠 -
拼音首字母释义工具 @@ -1067,6 +1051,42 @@ print("run[CQ:image,file="+j["img"]+"]") - [x] 当图片属于非 neutral 类别时自动发送评价(默认禁用,启用输入 /启用 nsfwauto) +
+
+ 抽wife + + `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/nwife"` + + - [x] 抽wife[@xxx] + + - [x] 添加wife[名字][图片] + + - [x] 删除wife[名字] + + - [x] [让 | 不让]所有人均可添加wife + + - 注:不同群添加后不会重叠 + +
+
+ 牛牛大作战 + +`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/niuniu" ` + +- [x] 打胶 + +- [x] jj[@xxx] + +- [x] 注册牛牛 + +- [x] 注销牛牛 + +- [x] 牛子长度排行 + +- [x] 牛子深度排行 + +- [x] 查看我的牛牛 +
浅草寺求签 diff --git a/main.go b/main.go index d21599c505..66f5d2b803 100644 --- a/main.go +++ b/main.go @@ -116,6 +116,7 @@ import ( _ "github.com/FloatTech/ZeroBot-Plugin/plugin/novel" // 铅笔小说网搜索 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/nsfw" // nsfw图片识别 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/nwife" // 本地老婆 + _ "github.com/FloatTech/ZeroBot-Plugin/plugin/niuniu" // 牛牛大作战 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/omikuji" // 浅草寺求签 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/poker" // 抽扑克 _ "github.com/FloatTech/ZeroBot-Plugin/plugin/qqwife" // 一群一天一夫一妻制群老婆 diff --git a/plugin/niuniu/main.go b/plugin/niuniu/main.go new file mode 100644 index 0000000000..5d04b218a1 --- /dev/null +++ b/plugin/niuniu/main.go @@ -0,0 +1,244 @@ +// Package niuniu 牛牛大作战 +package niuniu + +import ( + "fmt" + ctrl "github.com/FloatTech/zbpctrl" + "github.com/FloatTech/zbputils/control" + "github.com/FloatTech/zbputils/ctxext" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/extension/rate" + "github.com/wdvxdr1123/ZeroBot/message" + "math/rand" + "strconv" + "strings" + "time" +) + +var ( + en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{ + DisableOnDefault: false, + Brief: "牛牛大作战", + Help: "- 打胶\n" + + "- 注册牛牛\n" + + "- 注销牛牛\n" + + "- 查看我的牛牛\n" + + "- jj@xxx\n" + + "- 牛子长度排行\n" + + "- 牛子深度排行\n", + PrivateDataFolder: "niuniu", + }) + dajiaoLimiter = rate.NewManager[string](time.Second*90, 1) + jjLimiter = rate.NewManager[string](time.Second*150, 1) +) + +func init() { + en.OnFullMatch("牛子长度排行", zero.OnlyGroup, getdb).SetBlock(true).Handle(func(ctx *zero.Ctx) { + gid := ctx.Event.GroupID + niuniuList, err := db.readAllTable(gid) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + m := niuniuList.positive() + if m == nil { + ctx.SendChain(message.Text("暂时没有男孩子哦")) + return + } + var messages strings.Builder + messages.WriteString("牛子长度排行\n") + for i, user := range niuniuList.sort(true) { + messages.WriteString(fmt.Sprintf("第%d名 id:%s 长度:%.2fcm\n", i+1, + ctx.CardOrNickName(user.UID), user.Length)) + } + msg := ctxext.FakeSenderForwardNode(ctx, message.Text(&messages)) + if id := ctx.Send(message.Message{msg}).ID(); id == 0 { + ctx.Send(message.Text("发送排行失败")) + } + }) + en.OnFullMatch("牛子深度排行", zero.OnlyGroup, getdb).SetBlock(true).Handle(func(ctx *zero.Ctx) { + gid := ctx.Event.GroupID + niuniuList, err := db.readAllTable(gid) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return + } + m := niuniuList.negative() + if m == nil { + ctx.SendChain(message.Text("暂时没有女孩子哦")) + return + } + var messages strings.Builder + messages.WriteString("牛牛深度排行榜\n") + for i, user := range niuniuList.sort(false) { + messages.WriteString(fmt.Sprintf("第%d名 id:%s 长度:%.2fcm\n", i+1, + ctx.CardOrNickName(user.UID), user.Length)) + } + msg := ctxext.FakeSenderForwardNode(ctx, message.Text(&messages)) + if id := ctx.Send(message.Message{msg}).ID(); id == 0 { + ctx.Send(message.Text("发送排行失败")) + } + }) + en.OnFullMatch("查看我的牛牛", getdb, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) { + uid := ctx.Event.UserID + gid := ctx.Event.GroupID + niuniu, err := db.findniuniu(gid, uid) + if err != nil { + ctx.SendChain(message.Text("你还没有牛牛呢不能查看!")) + return + } + var result strings.Builder + sexLong := "长" + sex := "♂️" + if niuniu < 0 { + sexLong = "深" + sex = "♀️" + } + niuniuList, err := db.readAllTable(gid) + 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.At(uid), message.Text(&result)) + }) + en.OnFullMatchGroup([]string{"dj", "打胶"}, zero.OnlyGroup, + getdb).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 + }, func(ctx *zero.Ctx) { + timePass := int(time.Since(time.Unix(ctx.State["dajiao_last_touch"].(int64), 0)).Seconds()) + ctx.SendChain(message.Text(randomChoice([]string{ + fmt.Sprintf("才过去了%ds时间,你就又要打🦶了,身体受得住吗", timePass), + fmt.Sprintf("不行不行,你的身体会受不了的,歇%ds再来吧", 90-timePass), + fmt.Sprintf("休息一下吧,会炸膛的!%ds后再来吧", 90-timePass), + fmt.Sprintf("打咩哟,你的牛牛会爆炸的,休息%ds再来吧", 90-timePass), + }))) + }).Handle(func(ctx *zero.Ctx) { + // 获取群号和用户ID + gid := ctx.Event.GroupID + uid := ctx.Event.UserID + niuniu, err := db.findniuniu(gid, uid) + if err != nil { + ctx.SendChain(message.Text("请先注册牛牛!")) + dajiaoLimiter.Delete(fmt.Sprintf("%d_%d", gid, uid)) + return + } + messages, f := generateRandomStingTwo(niuniu) + u := userInfo{ + UID: uid, + Length: f, + } + ctx.SendChain(message.Text(messages)) + if err = db.insertniuniu(&u, gid); err != nil { + ctx.SendChain(message.Text("ERROR:", err)) + return + } + }) + en.OnFullMatch("注册牛牛", zero.OnlyGroup, getdb).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("你已经注册过了")) + return + } + //获取初始长度 + long := db.randLength() + u := userInfo{ + UID: uid, + Length: long, + UserCount: 0, + } + //添加数据进入表 + err := db.insertniuniu(&u, gid) + if err != nil { + err = db.createGIDTable(gid) + if err != nil { + ctx.SendChain(message.Text("ERROR:", err)) + return + } + err = db.insertniuniu(&u, gid) + if err != nil { + ctx.SendChain(message.Text("ERROR:", err)) + return + } + } + ctx.SendChain(message.Reply(ctx.Event.GroupID), + message.Text("注册成功,你的牛牛现在有", u.Length, "cm")) + }) + en.OnRegex(`jj\[CQ:at,qq=(\d+),name=[\s\S]*\]$`, getdb, + 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() + return lt + }, func(ctx *zero.Ctx) { + timePass := int(time.Since(time.Unix(ctx.State["jj_last_touch"].(int64), 0)).Seconds()) + ctx.SendChain(message.Text(randomChoice([]string{ + fmt.Sprintf("才过去了%ds时间,你就又要击剑了,真是饥渴难耐啊", timePass), + fmt.Sprintf("不行不行,你的身体会受不了的,歇%ds再来吧", 150-timePass), + fmt.Sprintf("你这种男同就应该被送去集中营!等待%ds再来吧", 150-timePass), + fmt.Sprintf("打咩哟!你的牛牛会炸的,休息%ds再来吧", 150-timePass), + }))) + }, + ).Handle(func(ctx *zero.Ctx) { + adduser, err := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64) + if err != nil { + ctx.SendChain(message.Text("ERROR:", err)) + return + } + uid := ctx.Event.UserID + gid := ctx.Event.GroupID + myniuniu, err := db.findniuniu(gid, uid) + if err != nil { + ctx.SendChain(message.Text("你还没有牛牛快去注册一个吧!")) + jjLimiter.Delete(fmt.Sprintf("%d_%d", gid, uid)) + return + } + adduserniuniu, err := db.findniuniu(gid, adduser) + if err != nil { + ctx.SendChain(message.At(uid), message.Text("对方还没有牛牛呢,不能🤺")) + jjLimiter.Delete(fmt.Sprintf("%d_%d", gid, uid)) + return + } + if uid == adduser { + ctx.SendChain(message.Text("你要和谁🤺?你自己吗?")) + jjLimiter.Delete(fmt.Sprintf("%d_%d", gid, uid)) + return + } + fencingResult, f, f1 := fencing(myniuniu, adduserniuniu) + err = db.insertniuniu(&userInfo{UID: uid, Length: f}, gid) + if err != nil { + ctx.SendChain(message.Text("ERROR:", err)) + return + } + err = db.insertniuniu(&userInfo{UID: adduser, Length: f1}, gid) + if err != nil { + ctx.SendChain(message.Text("ERROR:", err)) + return + } + ctx.SendChain(message.At(uid), message.Text(fencingResult)) + }) + en.OnFullMatch("注销牛牛", getdb, 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 + } + err = db.deleteniuniu(gid, uid) + if err != nil { + ctx.SendChain(message.Text("注销失败")) + return + } + ctx.SendChain(message.Text("注销成功,你已经没有牛牛了")) + }) +} + +func randomChoice(options []string) string { + return options[rand.Intn(len(options))] +} + diff --git a/plugin/niuniu/model.go b/plugin/niuniu/model.go new file mode 100644 index 0000000000..0f7462b1a2 --- /dev/null +++ b/plugin/niuniu/model.go @@ -0,0 +1,119 @@ +// Package niuniu 牛牛大作战 +package niuniu + +import ( + fcext "github.com/FloatTech/floatbox/ctxext" + sql "github.com/FloatTech/sqlite" + zero "github.com/wdvxdr1123/ZeroBot" + "github.com/wdvxdr1123/ZeroBot/message" + "math/rand" + "sort" + "strconv" + "sync" + "time" +) + +type model struct { + sql sql.Sqlite + sync.RWMutex +} + +type userInfo struct { + UID int64 + Length float64 + UserCount int +} +type users []*userInfo + +var ( + db = &model{} + getdb = fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool { + db.sql.DBPath = en.DataFolder() + "niuniu.db" + err := db.sql.Open(time.Hour * 24) + if err != nil { + ctx.SendChain(message.Text("ERROR: ", err)) + return false + } + return true + }) +) + +func (m users) positive() []userInfo { + var m1 []userInfo + for _, i2 := range m { + if i2.Length > 0 { + m1 = append(m1, *i2) + } + } + return m1 +} + +func (m users) negative() []userInfo { + var m1 []userInfo + for _, i2 := range m { + if i2.Length <= 0 { + m1 = append(m1, *i2) + } + } + return m1 +} + +func (m users) sort(isDesc bool) users { + t := func(i, j int) bool { + return m[i].UserCount < m[j].UserCount + } + if isDesc { + t = func(i, j int) bool { + return m[i].Length > m[j].Length + } + } + sort.Slice(m, t) + return m +} + +func (m users) ranking(niuniu float64, uid int64) int { + result := niuniu > 0 + for i, user := range m.sort(result) { + 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{}) +} + +func (db *model) findniuniu(gid, uid int64) (float64, error) { + db.RLock() + defer db.RUnlock() + u := userInfo{} + err := db.sql.Find(strconv.FormatInt(gid, 10), &u, "where UID = "+strconv.FormatInt(uid, 10)) + return u.Length, err +} + +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 = "+strconv.FormatInt(uid, 10)) +} + +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 new file mode 100644 index 0000000000..77575e5fa8 --- /dev/null +++ b/plugin/niuniu/utils.go @@ -0,0 +1,192 @@ +// Package niuniu 牛牛大作战 +package niuniu + +import ( + "fmt" + "math" + "math/rand" + "time" +) + +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 + } else { + 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) { + lossLimit := 0.25 + devourLimit := 0.27 + + probability := rand.Intn(100) + 1 + + switch { + case oppoLength <= -100 && myLength > 0 && 10 < probability && probability <= 20: + oppoLength *= 0.85 + change := -math.Min(math.Abs(lossLimit*myLength), math.Abs(1.5*myLength)) + myLength += change + return fmt.Sprintf("对方身为魅魔诱惑了你,你同化成魅魔!当前长度%.2fcm!", myLength), myLength, oppoLength + case oppoLength >= 100 && myLength > 0 && 10 < probability && probability <= 20: + oppoLength *= 0.85 + 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: + myLength *= 0.85 + change := math.Min(math.Abs(lossLimit*oppoLength), math.Abs(1.5*oppoLength)) + oppoLength -= change + return fmt.Sprintf("你身为魅魔诱惑了对方,吞噬了对方部分长度!当前长度%.2fcm!", myLength), myLength, oppoLength + + case myLength >= 100 && oppoLength > 0 && 10 < probability && probability <= 20: + myLength *= 0.85 + change := math.Min(math.Abs(devourLimit*oppoLength), math.Abs(1.5*oppoLength)) + oppoLength += change + 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, + 0 < probability && float64(probability) <= winProbability) +} + +// calculateWinProbability 计算胜率 +func calculateWinProbability(heightA, heightB float64) float64 { + var pA float64 + if heightA > heightB { + pA = 0.7 + 0.2*(heightA-heightB)/heightA + } else { + pA = 0.6 - 0.2*(heightB-heightA)/heightB + } + 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 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 -= float64(time.Now().UnixNano() % 10) + if rd > 1000000 { + return rd - rand.Float64()*rd + } + return float64(int(rd * rand.Float64())) +} + +func hitGlue(l float64) float64 { + return rand.Float64() * math.Log2(l) / 2 +} +