ZeroBot-Plugin/plugin/wallet/wallet.go
vatebur 852c5f2f35
feat(wallet): 完善钱包管理和查询功能 (#954)
1. 新增“管理钱包余额”功能,仅超级用户可用
2. 重构“查看我的钱包功能”,可以查看任意群员的钱包
3. 增加“钱包转账”功能

有问题的地方:
1.任何人的钱包都可查,群友钱包余额泄露
2. ”管理钱包余额“和“钱包转账”代码高度相似,是否有必要优化

* chore: update readme
2024-08-19 19:46:45 +08:00

262 lines
7.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package wallet 钱包
package wallet
import (
"math"
"os"
"regexp"
"strconv"
"strings"
"time"
"github.com/FloatTech/AnimeAPI/wallet"
"github.com/FloatTech/floatbox/binary"
"github.com/FloatTech/floatbox/file"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/img/text"
"github.com/golang/freetype"
"github.com/wcharczuk/go-chart/v2"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
func init() {
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "钱包",
Help: "- 查看钱包排名\n" +
"- 设置硬币名称XX\n" +
"- 管理钱包余额[+金额|-金额][@xxx]\n" +
"- 查看我的钱包|查询钱包余额[@xxx]\n" +
"- 钱包转账[金额][@xxx]\n" +
"注:仅超级用户能“管理钱包余额”\n",
PrivateDataFolder: "wallet",
})
cachePath := en.DataFolder() + "cache/"
coinNameFile := en.DataFolder() + "coin_name.txt"
go func() {
_ = os.RemoveAll(cachePath)
err := os.MkdirAll(cachePath, 0755)
if err != nil {
panic(err)
}
// 更改硬币名称
var coinName string
if file.IsExist(coinNameFile) {
content, err := os.ReadFile(coinNameFile)
if err != nil {
panic(err)
}
coinName = binary.BytesToString(content)
} else {
// 旧版本数据
coinName = "ATRI币"
}
wallet.SetWalletName(coinName)
}()
en.OnFullMatch("查看钱包排名", zero.OnlyGroup).Limit(ctxext.LimitByGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
gid := strconv.FormatInt(ctx.Event.GroupID, 10)
today := time.Now().Format("20060102")
drawedFile := cachePath + gid + today + "walletRank.png"
if file.IsExist(drawedFile) {
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile))
return
}
// 无缓存获取群员列表
temp := ctx.GetThisGroupMemberListNoCache().Array()
usergroup := make([]int64, len(temp))
for i, info := range temp {
usergroup[i] = info.Get("user_id").Int()
}
// 获取钱包信息
st, err := wallet.GetGroupWalletOf(true, usergroup...)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if len(st) == 0 {
ctx.SendChain(message.Text("ERROR: 当前没人获取过", wallet.GetWalletName()))
return
} else if len(st) > 10 {
st = st[:10]
}
_, err = file.GetLazyData(text.FontFile, control.Md5File, true)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
b, err := os.ReadFile(text.FontFile)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
font, err := freetype.ParseFont(b)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
f, err := os.Create(drawedFile)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
var bars []chart.Value
for _, v := range st {
if v.Money != 0 {
bars = append(bars, chart.Value{
Label: ctx.CardOrNickName(v.UID),
Value: float64(v.Money),
})
}
}
err = chart.BarChart{
Font: font,
Title: wallet.GetWalletName() + "排名(1天只刷新1次)",
Background: chart.Style{
Padding: chart.Box{
Top: 40,
},
},
YAxis: chart.YAxis{
Range: &chart.ContinuousRange{
Min: 0,
Max: math.Ceil(bars[0].Value/10) * 10,
},
},
Height: 500,
BarWidth: 50,
Bars: bars,
}.Render(chart.PNG, f)
_ = f.Close()
if err != nil {
_ = os.Remove(drawedFile)
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile))
})
en.OnPrefix("设置硬币名称", zero.OnlyToMe, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
coinName := strings.TrimSpace(ctx.State["args"].(string))
err := os.WriteFile(coinNameFile, binary.StringToBytes(coinName), 0644)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
wallet.SetWalletName(coinName)
ctx.SendChain(message.Text("记住啦~"))
})
en.OnPrefix(`管理钱包余额`, zero.SuperUserPermission).SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
param := strings.TrimSpace(ctx.State["args"].(string))
// 捕获修改的金额
re := regexp.MustCompile(`^[+-]?\d+$`)
amount, err := strconv.Atoi(re.FindString(param))
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("输入的金额异常"))
return
}
// 捕获用户QQ号只支持@事件
var uidStr string
if len(ctx.Event.Message) > 1 && ctx.Event.Message[1].Type == "at" {
uidStr = ctx.Event.Message[1].Data["qq"]
} else {
// 没at就修改自己的钱包
uidStr = strconv.FormatInt(ctx.Event.UserID, 10)
}
uidInt, err := strconv.ParseInt(uidStr, 10, 64)
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("QQ号处理失败"))
return
}
if amount < wallet.GetWalletOf(uidInt) {
ctx.SendChain(message.Text("管理失败:对方钱包余额不足,扣款失败:"))
return
}
err = wallet.InsertWalletOf(uidInt, amount)
if err != nil {
ctx.SendChain(message.Text("[ERROR]:管理失败,钱包坏掉了:\n", err))
return
}
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("钱包余额修改成功,已修改用户:", uidStr, "的钱包,修改金额为:", amount))
})
// 保留用户习惯,兼容旧语法“查看我的钱包”
en.OnPrefixGroup([]string{`查询钱包余额`, `查看我的钱包`}).SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
param := ctx.State["args"].(string)
var uidStr string
if len(ctx.Event.Message) > 1 && ctx.Event.Message[1].Type == "at" {
uidStr = ctx.Event.Message[1].Data["qq"]
} else if param == "" {
uidStr = strconv.FormatInt(ctx.Event.UserID, 10)
}
uidInt, err := strconv.ParseInt(uidStr, 10, 64)
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("QQ号处理失败"))
return
}
money := wallet.GetWalletOf(uidInt)
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("QQ号", uidStr, ",的钱包有", money, wallet.GetWalletName()))
})
en.OnPrefix(`钱包转账`, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) {
param := strings.TrimSpace(ctx.State["args"].(string))
// 捕获修改的金额
re := regexp.MustCompile(`^[+-]?\d+$`)
amount, err := strconv.Atoi(re.FindString(param))
if err != nil || amount <= 0 {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("输入额异常请检查金额或at是否正常"))
return
}
// 捕获用户QQ号只支持@事件
var uidStr string
if len(ctx.Event.Message) > 1 && ctx.Event.Message[1].Type == "at" {
uidStr = ctx.Event.Message[1].Data["qq"]
} else {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("获取被转方信息失败"))
return
}
uidInt, err := strconv.ParseInt(uidStr, 10, 64)
if err != nil {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("QQ号处理失败"))
return
}
// 开始转账流程
if amount > wallet.GetWalletOf(ctx.Event.UserID) {
ctx.SendChain(message.Text("[ERROR]:钱包余额不足,转账失败"))
return
}
err = wallet.InsertWalletOf(ctx.Event.UserID, -amount)
if err != nil {
ctx.SendChain(message.Text("[ERROR]:转账失败,扣款异常:\n", err))
return
}
err = wallet.InsertWalletOf(uidInt, amount)
if err != nil {
ctx.SendChain(message.Text("[ERROR]:转账失败,转账时银行被打劫:\n", err))
return
}
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("转账成功:成功给"), message.At(uidInt), message.Text(",转账:", amount, wallet.GetWalletName()))
})
}