397 lines
11 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 scoresaber
import (
"fmt"
"log"
"strconv"
"strings"
"sync"
"git.lxtend.com/qqbot/action"
"git.lxtend.com/qqbot/constants"
"git.lxtend.com/qqbot/handler"
"git.lxtend.com/qqbot/message"
"git.lxtend.com/qqbot/model"
"git.lxtend.com/qqbot/service/scoresaber"
"git.lxtend.com/qqbot/util"
)
func init() {
handler.RegisterHandler("查ss", getMySSPic, constants.LEVEL_USER)
handler.RegisterHelpInform("查ss", "scoresaber", " 查看您的最新分数")
handler.RegisterHandler("查sss", getMySSPic, constants.LEVEL_USER)
handler.RegisterHelpInform("查sss", "scoresaber", "查sss 查看您的最新分数(仅取第一帧)")
handler.RegisterHandler("绑定ss", bindSS, constants.LEVEL_USER)
handler.RegisterHelpInform("绑定ss", "scoresaber", "绑定您的scoresaber账号")
handler.RegisterHandler("解绑ss", unbindSS, constants.LEVEL_USER)
handler.RegisterHelpInform("解绑ss", "scoresaber", "解绑您的scoresaber账号")
handler.RegisterHandler("最新ss", getMyRecentScore, constants.LEVEL_USER)
handler.RegisterHelpInform("最新ss", "scoresaber", "查看您的最新游戏记录")
handler.RegisterHandler("截ss", screenshotSS, constants.LEVEL_USER)
handler.RegisterHelpInform("ss+n", "scoresaber", "查看您需要打多少pp才能达到当前区服的第N名")
handler.RegisterFrontMatchHandler("ss+", ssPlusN, constants.LEVEL_USER)
handler.RegisterHelpInform("ss-n", "scoresaber", "查看落后您N名的玩家需要打多少pp才会超过您")
handler.RegisterFrontMatchHandler("ss-", ssPlusN, constants.LEVEL_USER)
handler.RegisterHandler("bs50", getBS50, constants.LEVEL_USER)
handler.RegisterHelpInform("bs50", "scoresaber", "查看您的B50分数")
}
func getBS50(msg model.Message) (reply *model.Reply) {
userIdStr := strconv.Itoa(int(msg.UserId))
userSSID, err := scoresaber.GetSSID(userIdStr)
if err != nil {
return &model.Reply{
ReplyMsg: err.Error(),
ReferOriginMsg: true,
FromMsg: msg,
}
}
if err := GenBs50(userSSID, util.GenTempFilePath("bs50.png")); err != nil {
return &model.Reply{
ReplyMsg: err.Error(),
ReferOriginMsg: true,
FromMsg: msg,
}
}
imageMsg := message.ImageMessage{
Type: "image",
Data: message.ImageMessageData{
File: util.GenTempFilePath("bs50.png"),
},
}
return &model.Reply{
ReplyMsg: imageMsg.ToCQString(),
ReferOriginMsg: true,
FromMsg: msg,
}
}
func ssPlusN(msg model.Message) (reply *model.Reply) {
var (
resultStr strings.Builder
err error
maxRetries = 5 // 最大重试次数
attempts = 0
)
var N int
var isPlus bool
if strings.HasPrefix(msg.RawMsg, "ss+") {
isPlus = true
} else if strings.HasPrefix(msg.RawMsg, "ss-") {
isPlus = false
} else {
return &model.Reply{
ReplyMsg: "请输入ss+或ss-",
ReferOriginMsg: true,
FromMsg: msg,
}
}
if len(msg.RawMsg) > len("ss+") {
N, err = strconv.Atoi(msg.RawMsg[len("ss+"):])
if err != nil || N <= 0 {
return &model.Reply{
ReplyMsg: "请输入一个整数",
ReferOriginMsg: true,
FromMsg: msg,
}
}
}
if !isPlus {
N = -N
}
// 获取当前用户在区中的排名
userIdStr := strconv.Itoa(int(msg.UserId))
userSSID, err := scoresaber.GetSSID(userIdStr)
if err != nil {
return &model.Reply{
ReplyMsg: err.Error(),
ReferOriginMsg: true,
FromMsg: msg,
}
}
var userInfo *scoresaber.PlayerData
for attempts < maxRetries {
err = nil
userInfo, err = scoresaber.FetchPlayerData(userSSID)
if err != nil {
break
}
attempts++
}
if err != nil {
return &model.Reply{
ReplyMsg: "获取您的分数时出现问题,请稍后重试。" + err.Error(),
ReferOriginMsg: true,
FromMsg: msg,
}
}
if userInfo.PP == 0 {
return &model.Reply{
ReplyMsg: "请先打几首Rank谱面再来使用此功能",
ReferOriginMsg: true,
FromMsg: msg,
}
}
resultStr.WriteString(fmt.Sprintf("您当前的ScoreSaber全区排名为%d", userInfo.CountryRank))
// 获取当前用户所在区对应+N位的玩家列表
leaderboard, err := scoresaber.FetchCountryLeaderboard(userInfo.Country, userInfo.CountryRank-N, userInfo.ID)
if err != nil {
return &model.Reply{
ReplyMsg: "获取排行榜时出现问题,请稍后重试。" + err.Error(),
ReferOriginMsg: true,
FromMsg: msg,
}
}
if userInfo.CountryRank-N <= 0 {
resultStr.WriteString("\n")
if userInfo.CountryRank == 1 {
resultStr.WriteString(fmt.Sprintf("注意:你已经是%s区Top1了。", userInfo.Country))
} else {
resultStr.WriteString(fmt.Sprintf("注意:你最多只需要提升%d名就是%s区Top1了。", userInfo.CountryRank-1, userInfo.Country))
}
}
if userInfo.CountryRank != 1 {
//寻找leaderboard中排名为userInfo.CountryRank-N的玩家
var targetPlayer scoresaber.PlayerData
targetRank := userInfo.CountryRank - N
if targetRank <= 0 {
targetRank = 1
}
if len(leaderboard.Players) == 0 {
resultStr.WriteString("\n")
resultStr.WriteString("未找到目标排名的玩家,请尝试更小的目标排名偏移量")
return &model.Reply{
ReplyMsg: resultStr.String(),
ReferOriginMsg: true,
FromMsg: msg,
}
}
var foundTargetPlayer bool
for _, player := range leaderboard.Players {
if player.CountryRank == targetRank {
targetPlayer = player
foundTargetPlayer = true
break
}
}
if !foundTargetPlayer {
resultStr.WriteString("\n")
resultStr.WriteString("未找到目标排名的玩家,请尝试更小的目标排名偏移量")
return &model.Reply{
ReplyMsg: resultStr.String(),
ReferOriginMsg: true,
FromMsg: msg,
}
}
resultStr.WriteString("\n")
if isPlus {
resultStr.WriteString(fmt.Sprintf("您只需要再打出%.2fpp就能超越%s达到%s区第%d名。", targetPlayer.PP-userInfo.PP, targetPlayer.Name, userInfo.Country, targetPlayer.CountryRank))
} else {
resultStr.WriteString(fmt.Sprintf("%s区的第%d名是%s对方只需要再打出%.2fpp就能超过你。", userInfo.Country, targetPlayer.CountryRank, targetPlayer.Name, userInfo.PP-targetPlayer.PP))
}
}
return &model.Reply{
ReplyMsg: resultStr.String(),
ReferOriginMsg: true,
FromMsg: msg,
}
}
func getMySSPic(msg model.Message) (reply *model.Reply) {
onlyFirstFrame := false
if strings.HasPrefix(msg.RawMsg, "查sss") {
onlyFirstFrame = true
}
var (
resultStr string
err error
maxRetries = 5 // 最大重试次数
attempts = 0
noUpdate = false
)
var userIdStr string
if len(msg.RawMsg) > len("查ss ") {
userIdStr = strings.Split(msg.RawMsg, " ")[1]
noUpdate = true
} else {
userIdStr = strconv.Itoa(int(msg.UserId))
}
var data *scoresaber.PlayerDataLite
var lastData *scoresaber.PlayerDataLite
for attempts < maxRetries {
if noUpdate {
data, err = scoresaber.SSQuery.GetScoreWithoutUpdate(userIdStr)
} else {
data, lastData, err = scoresaber.SSQuery.GetScore(userIdStr)
}
if err == nil {
break // 成功时退出循环
}
attempts++
log.Printf("获取分数时出错,第 %d 次重试: %v", attempts, err)
}
// 如果所有尝试都失败,返回适当的错误消息
if err != nil {
return &model.Reply{
ReplyMsg: "获取您的分数时出现问题,请稍后重试。" + err.Error(),
ReferOriginMsg: true,
FromMsg: msg,
}
}
if lastData != nil {
resultStr = data.LastDiffToImage(*lastData, onlyFirstFrame)
} else {
resultStr = data.LastDiffToImage(*data, onlyFirstFrame)
}
imageMsg := message.ImageMessage{
Type: "image",
Data: message.ImageMessageData{
File: resultStr,
},
}
return &model.Reply{
ReplyMsg: imageMsg.ToCQString(),
ReferOriginMsg: false,
FromMsg: msg,
}
}
func bindSS(msg model.Message) (reply *model.Reply) {
return &model.Reply{
ReplyMsg: scoresaber.SSQuery.BindSS(strconv.Itoa(int(msg.UserId)), msg.RawMsg[len("绑定ss "):]),
ReferOriginMsg: true,
FromMsg: msg,
}
}
func unbindSS(msg model.Message) (reply *model.Reply) {
return &model.Reply{
ReplyMsg: scoresaber.SSQuery.UnbindSS(strconv.Itoa(int(msg.UserId))),
ReferOriginMsg: true,
FromMsg: msg,
}
}
func getMyRecentScore(msg model.Message) (reply *model.Reply) {
count := 1
scoreMsg := []any{}
if len(msg.RawMsg) > len("最新ss ") {
var err error
count, err = strconv.Atoi(msg.RawMsg[len("最新ss "):])
if err != nil || count <= 0 {
return &model.Reply{
ReplyMsg: "",
ReferOriginMsg: true,
FromMsg: msg,
}
}
if count > 10 {
count = 10
}
}
var userName string
recordCount := 0
records, err := scoresaber.SSQuery.GetRecentScores(count, strconv.Itoa(int(msg.UserId)))
if err != nil {
return &model.Reply{
ReplyMsg: err.Error(),
ReferOriginMsg: true,
FromMsg: msg,
}
}
coverImageMap := make(map[string]string)
wg := sync.WaitGroup{}
for _, record := range records {
coverImageMap[record.SongHash] = record.CoverImage
wg.Add(1)
go func(songHash string) {
defer wg.Done()
//文件存在则跳过
filePath, err := util.DownloadFile(coverImageMap[songHash], constants.TempDir, true)
if err != nil {
log.Printf("下载图片失败: %v", err)
return
}
newPath, err := util.ResizeImageByMaxHeight2File(filePath, 20)
if err != nil {
log.Printf("缩放图片失败: %v", err)
}
coverImageMap[songHash] = newPath
}(record.SongHash)
}
wg.Wait()
for _, record := range records {
imageMsg := message.ImageMessage{
Type: "image",
Data: message.ImageMessageData{
File: coverImageMap[record.SongHash],
},
}
textMsg := message.TextMessage{
Type: "text",
Data: message.TextMessageData{
Text: record.ToString(),
},
}
scoreMsg = append(scoreMsg, imageMsg, textMsg)
userName = record.Name
recordCount++
}
if len(scoreMsg) <= 0 {
return &model.Reply{
ReplyMsg: "无最近游戏记录",
ReferOriginMsg: true,
FromMsg: msg,
}
}
//如果消息行数太多,使用合并转发
if len(records) > 5 {
nodeMsg := util.NewSelfNodeMessage(append(
[]any{
&message.TextMessage{
Type: "text",
Data: message.TextMessageData{
Text: "玩家 " + userName + " 的" + strconv.Itoa(recordCount) + "条最近记录为:\n",
},
},
},
scoreMsg...,
)...)
action.ActionManager.SendForward(
&model.Reply{
ReplyMsg: []any{nodeMsg},
ReferOriginMsg: false,
FromMsg: msg,
},
)
return nil
}
return &model.Reply{
ReplyMsg: append(
[]any{
message.TextMessage{
Type: "text",
Data: message.TextMessageData{
Text: "玩家 " + userName + " 的" + strconv.Itoa(recordCount) + "条最近记录为:",
},
},
},
scoreMsg...,
),
ReferOriginMsg: true,
FromMsg: msg,
}
}
func screenshotSS(msg model.Message) (reply *model.Reply) {
return &model.Reply{
ReplyMsg: "[CQ:image,file=file:///tmp/qqbot/" + scoresaber.GetSSPicture(strconv.Itoa(int(msg.UserId))) + "]",
ReferOriginMsg: true,
FromMsg: msg,
}
}