feat: 新插件 牛牛大作战 (#944)

This commit is contained in:
小宇宇 2024-08-30 17:59:51 +08:00 committed by GitHub
parent a7d2a31bb8
commit 3da55c4b75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 592 additions and 16 deletions

View File

@ -1009,22 +1009,6 @@ print("run[CQ:image,file="+j["img"]+"]")
- 注:刷新文件夹较慢,请耐心等待刷新完成,会提示“成功”。
</details>
<details>
<summary>抽wife</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativewife"`
- [x] 抽wife[@xxx]
- [x] 添加wife[名字][图片]
- [x] 删除wife[名字]
- [x] [让 | 不让]所有人均可添加wife
- 注:不同群添加后不会重叠
</details>
<details>
<summary>拼音首字母释义工具</summary>
@ -1067,6 +1051,42 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 当图片属于非 neutral 类别时自动发送评价(默认禁用,启用输入 /启用 nsfwauto)
</details>
<details>
<summary>抽wife</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/nwife"`
- [x] 抽wife[@xxx]
- [x] 添加wife[名字][图片]
- [x] 删除wife[名字]
- [x] [让 | 不让]所有人均可添加wife
- 注:不同群添加后不会重叠
</details>
<details>
<summary>牛牛大作战</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/niuniu" `
- [x] 打胶
- [x] jj[@xxx]
- [x] 注册牛牛
- [x] 注销牛牛
- [x] 牛子长度排行
- [x] 牛子深度排行
- [x] 查看我的牛牛
</details>
<details>
<summary>浅草寺求签</summary>

View File

@ -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" // 一群一天一夫一妻制群老婆

244
plugin/niuniu/main.go Normal file
View File

@ -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))]
}

119
plugin/niuniu/model.go Normal file
View File

@ -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
}

192
plugin/niuniu/utils.go Normal file
View File

@ -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
}