361 lines
11 KiB
Go
361 lines
11 KiB
Go
package scoresaber
|
||
|
||
import (
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"io"
|
||
"log"
|
||
"net/http"
|
||
"strconv"
|
||
"time"
|
||
|
||
"git.lxtend.com/lixiangwuxian/qqbot/service"
|
||
"git.lxtend.com/lixiangwuxian/qqbot/sqlite3"
|
||
"git.lxtend.com/lixiangwuxian/qqbot/util"
|
||
_ "github.com/mattn/go-sqlite3"
|
||
)
|
||
|
||
func init() {
|
||
// 使用GORM自动迁移替代手写SQL
|
||
sqlite3.AutoMigrate(&service.SSBind{}, &service.SSData{}, &service.SSRecordData{})
|
||
}
|
||
|
||
var SSQuery = &ssQuery{}
|
||
|
||
type ssQuery struct {
|
||
}
|
||
|
||
func (ss *ssQuery) BindSS(qqId string, ssId string) (reply string) {
|
||
tx := sqlite3.GetGormDB().Begin()
|
||
defer tx.Rollback()
|
||
// ssId为数字
|
||
if _, isNum := strconv.Atoi(ssId); isNum != nil {
|
||
return "ssId格式错误,应当为一串数字(是您的scoresaber主页链接中的末尾数字部分,一般和您的steamID相同)"
|
||
}
|
||
|
||
data, err := FetchPlayerData(ssId)
|
||
if data.ID == "" {
|
||
if err != nil {
|
||
return "请求出错,报错如下,如果确定命令没问题可以重新试试:" + err.Error()
|
||
}
|
||
return "未找到玩家,请检查ID后重试"
|
||
}
|
||
|
||
// 检查QQ是否已绑定
|
||
exists, currentSsId, err := service.CheckSSBindExists(qqId)
|
||
if err != nil {
|
||
log.Print(err)
|
||
return "检查绑定状态失败"
|
||
}
|
||
if exists {
|
||
// 获取当前绑定账号的信息
|
||
if currentData, err := FetchPlayerData(currentSsId); err == nil && currentData.ID != "" {
|
||
return fmt.Sprintf("您已绑定至ss账号:%s,请先输入\"解绑ss\"解绑", currentData.Name)
|
||
}
|
||
return "您已绑定过ss账号,请先输入\"解绑ss\"解绑"
|
||
}
|
||
|
||
// 检查SSID是否已绑定
|
||
ssidExists, err := service.CheckSSIDExists(ssId)
|
||
if err != nil {
|
||
log.Print(err)
|
||
return "检查SSID绑定状态失败"
|
||
}
|
||
if ssidExists {
|
||
return "该ss账号已绑定至其他用户"
|
||
}
|
||
|
||
// 创建绑定
|
||
err = service.CreateSSBind(qqId, ssId)
|
||
if err != nil {
|
||
return "绑定失败"
|
||
}
|
||
|
||
return "和用户名为 " + data.Name + " 的用户绑定成功,同时也绑定了您的beatleader账号。输入\"查ss\"查看个人数据"
|
||
}
|
||
|
||
func (ss *ssQuery) UnbindSS(qqId string) (reply string) {
|
||
// 检查是否已绑定
|
||
exists, _, err := service.CheckSSBindExists(qqId)
|
||
if err != nil {
|
||
log.Print(err)
|
||
return "检查绑定状态失败"
|
||
}
|
||
if !exists {
|
||
return "您未绑定ss账号,输入\"绑定ss [ssId]\"绑定"
|
||
}
|
||
|
||
// 删除绑定
|
||
err = service.DeleteSSBind(qqId)
|
||
if err != nil {
|
||
return "解绑失败"
|
||
}
|
||
|
||
return "解绑成功,重新绑定请输入\"绑定ss [ssId]\""
|
||
}
|
||
|
||
func (ss *ssQuery) GetScore(qqId string) (currentData *PlayerDataLite, lastData *PlayerDataLite, err error) {
|
||
tx := sqlite3.GetGormDB().Begin()
|
||
defer tx.Rollback()
|
||
|
||
ssId, err := GetSSID(qqId)
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
|
||
// 查询玩家数据
|
||
data, err := FetchPlayerData(ssId)
|
||
if data == nil && err == nil {
|
||
return nil, nil, errors.New("查询出错,服务器返回了空数据")
|
||
}
|
||
if err != nil {
|
||
log.Print(err)
|
||
return nil, nil, err
|
||
}
|
||
|
||
// 构建新的数据结构
|
||
newSSData := service.SSData{
|
||
ID: data.ID,
|
||
Name: data.Name,
|
||
Country: data.Country,
|
||
PP: data.PP,
|
||
Rank: data.Rank,
|
||
CountryRank: data.CountryRank,
|
||
TotalScore: int64(data.ScoreStats.TotalScore),
|
||
TotalRankedScore: int64(data.ScoreStats.TotalRankedScore),
|
||
AverageRankedAccuracy: data.ScoreStats.AverageRankedAccuracy,
|
||
TotalPlayCount: data.ScoreStats.TotalPlayCount,
|
||
RankedPlayCount: data.ScoreStats.RankedPlayCount,
|
||
ReplaysWatched: data.ScoreStats.ReplaysWatched,
|
||
GeneratedTime: time.Now(),
|
||
}
|
||
|
||
// 查询最近的玩家数据
|
||
lastSSData, err := service.GetLatestSSData(data.ID)
|
||
if err != nil {
|
||
log.Print(err)
|
||
return nil, nil, err
|
||
}
|
||
|
||
// 构建返回的PlayerDataLite结构
|
||
currentDataLite := PlayerDataLite{
|
||
ID: data.ID,
|
||
Name: data.Name,
|
||
ProfilePicture: data.ProfilePicture,
|
||
Country: data.Country,
|
||
PP: data.PP,
|
||
Rank: data.Rank,
|
||
CountryRank: data.CountryRank,
|
||
TotalScore: data.ScoreStats.TotalScore,
|
||
TotalRankedScore: data.ScoreStats.TotalRankedScore,
|
||
AverageRankedAccuracy: data.ScoreStats.AverageRankedAccuracy,
|
||
TotalPlayCount: data.ScoreStats.TotalPlayCount,
|
||
RankedPlayCount: data.ScoreStats.RankedPlayCount,
|
||
ReplaysWatched: data.ScoreStats.ReplaysWatched,
|
||
GeneratedTime: time.Now().Format("2006-01-02 15:04:05.999999999-07:00"),
|
||
}
|
||
|
||
// 如果有新的数据且与上次不同,则插入
|
||
if lastSSData == nil || hasDataChanged(lastSSData, &newSSData) {
|
||
err = tx.Delete(&service.SSData{ID: data.ID}).Error
|
||
if err != nil {
|
||
log.Print(err)
|
||
return nil, nil, err
|
||
}
|
||
err = tx.Create(&newSSData).Error
|
||
if err != nil {
|
||
log.Print(err)
|
||
return nil, nil, err
|
||
}
|
||
|
||
// 如果有历史数据,构建历史数据的PlayerDataLite
|
||
if lastSSData != nil {
|
||
lastDataLite := PlayerDataLite{
|
||
ID: lastSSData.ID,
|
||
Name: lastSSData.Name,
|
||
Country: lastSSData.Country,
|
||
PP: lastSSData.PP,
|
||
Rank: lastSSData.Rank,
|
||
CountryRank: lastSSData.CountryRank,
|
||
TotalScore: int(lastSSData.TotalScore),
|
||
TotalRankedScore: int(lastSSData.TotalRankedScore),
|
||
AverageRankedAccuracy: lastSSData.AverageRankedAccuracy,
|
||
TotalPlayCount: lastSSData.TotalPlayCount,
|
||
RankedPlayCount: lastSSData.RankedPlayCount,
|
||
ReplaysWatched: lastSSData.ReplaysWatched,
|
||
GeneratedTime: lastSSData.GeneratedTime.Format("2006-01-02 15:04:05.999999999-07:00"),
|
||
}
|
||
return ¤tDataLite, &lastDataLite, tx.Commit().Error
|
||
}
|
||
}
|
||
return ¤tDataLite, nil, tx.Commit().Error
|
||
}
|
||
|
||
// 辅助函数:检查数据是否有变化
|
||
func hasDataChanged(old *service.SSData, new *service.SSData) bool {
|
||
return old.PP != new.PP || old.Rank != new.Rank || old.CountryRank != new.CountryRank ||
|
||
old.TotalScore != new.TotalScore || old.TotalRankedScore != new.TotalRankedScore
|
||
}
|
||
|
||
func (ss *ssQuery) GetScoreWithoutUpdate(qqId string) (currentData *PlayerDataLite, err error) {
|
||
// 查询玩家数据
|
||
ssId, err := GetSSID(qqId)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
data, err := FetchPlayerData(ssId)
|
||
if err != nil {
|
||
return nil, errors.New("查询出错,报错如下" + err.Error())
|
||
}
|
||
if data.ID == "" {
|
||
return nil, errors.New("未找到玩家,请检查ID后重试")
|
||
}
|
||
dataLite := data.ToDataLite()
|
||
// 返回当前数据的字符串表示
|
||
return &dataLite, nil
|
||
}
|
||
|
||
func (ss *ssQuery) SaveRecord(cmdData CommandData) {
|
||
// 创建 SSRecordData 结构体实例
|
||
recordData := service.SSRecordData{
|
||
ScoreID: cmdData.Score.ID,
|
||
SSID: cmdData.Score.LeaderboardPlayerInfo.ID,
|
||
Name: cmdData.Score.LeaderboardPlayerInfo.Name,
|
||
Country: cmdData.Score.LeaderboardPlayerInfo.Country,
|
||
SongName: cmdData.Leaderboard.SongName,
|
||
SongSubName: cmdData.Leaderboard.SongSubName,
|
||
SongAuthorName: cmdData.Leaderboard.SongAuthorName,
|
||
SongHash: cmdData.Leaderboard.SongHash,
|
||
CoverImage: cmdData.Leaderboard.CoverImage,
|
||
DifficultyRaw: cmdData.Leaderboard.Difficulty.DifficultyRaw,
|
||
Stars: cmdData.Leaderboard.Stars,
|
||
PP: cmdData.Score.Pp,
|
||
Weight: cmdData.Score.Weight,
|
||
Modifiers: cmdData.Score.Modifiers,
|
||
Multiplier: cmdData.Score.Multiplier,
|
||
Rank: cmdData.Score.Rank,
|
||
BadCuts: cmdData.Score.BadCuts,
|
||
Score: cmdData.Score.ModifiedScore,
|
||
MaxScore: cmdData.Leaderboard.MaxScore,
|
||
MissedNotes: cmdData.Score.MissedNotes,
|
||
MaxCombo: cmdData.Score.MaxCombo,
|
||
FullCombo: cmdData.Score.FullCombo,
|
||
DeviceHmd: "",
|
||
DeviceControllerLeft: "",
|
||
DeviceControllerRight: "",
|
||
GeneratedTime: time.Now(),
|
||
}
|
||
|
||
// 检查设备信息并设置
|
||
if cmdData.Score.DeviceHmd != nil {
|
||
recordData.DeviceHmd = *cmdData.Score.DeviceHmd
|
||
}
|
||
if cmdData.Score.DeviceControllerLeft != nil {
|
||
recordData.DeviceControllerLeft = *cmdData.Score.DeviceControllerLeft
|
||
}
|
||
if cmdData.Score.DeviceControllerRight != nil {
|
||
recordData.DeviceControllerRight = *cmdData.Score.DeviceControllerRight
|
||
}
|
||
|
||
// 使用GORM保存记录
|
||
err := service.CreateSSRecordData(recordData)
|
||
if err != nil {
|
||
log.Print(err)
|
||
}
|
||
}
|
||
|
||
func (ss *ssQuery) GetRecentScores(count int, qqId string) ([]RecordDataLite, error) {
|
||
ssId, err := GetSSID(qqId)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
playerData, err := FetchPlayerData(ssId)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
//一页8条,超过8条则取两页
|
||
historyUrl := "https://scoresaber.com/api/player/%s/scores?page=%d&sort=recent"
|
||
var response struct {
|
||
Data []struct {
|
||
Score Score `json:"score"`
|
||
Leaderboard Leaderboard `json:"leaderboard"`
|
||
} `json:"playerScores"`
|
||
}
|
||
records := make([]RecordDataLite, 0)
|
||
for i := range (count-1)/8 + 1 {
|
||
fullUrl := fmt.Sprintf(historyUrl, ssId, i+1)
|
||
resp, err := http.Get(fullUrl)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer resp.Body.Close()
|
||
body, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
err = json.Unmarshal(body, &response)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
scores := response.Data
|
||
for _, score := range scores {
|
||
if len(records) >= count {
|
||
break
|
||
}
|
||
record := RecordDataLite{
|
||
ScoreID: score.Score.ID,
|
||
SsID: ssId,
|
||
Name: playerData.Name,
|
||
Country: playerData.Country,
|
||
SongName: score.Leaderboard.SongName,
|
||
SongSubName: score.Leaderboard.SongSubName,
|
||
SongAuthorName: score.Leaderboard.SongAuthorName,
|
||
SongHash: score.Leaderboard.SongHash,
|
||
SongId: "",
|
||
CoverImage: score.Leaderboard.CoverImage,
|
||
DifficultyRaw: score.Leaderboard.Difficulty.DifficultyRaw,
|
||
Stars: score.Leaderboard.Stars,
|
||
PP: score.Score.Pp,
|
||
Weight: score.Score.Weight,
|
||
Modifiers: score.Score.Modifiers,
|
||
Multiplier: score.Score.Multiplier,
|
||
Rank: score.Score.Rank,
|
||
BadCuts: score.Score.BadCuts,
|
||
Score: score.Score.ModifiedScore,
|
||
MaxScore: score.Leaderboard.MaxScore,
|
||
FullCombo: score.Score.FullCombo,
|
||
DeviceHmd: "",
|
||
DeviceControllerLeft: "",
|
||
DeviceControllerRight: "",
|
||
GeneratedTime: score.Score.TimeSet.Format("2006-01-02 15:04:05.999999999-07:00"),
|
||
}
|
||
// 检查设备信息并设置
|
||
if score.Score.DeviceHmd != nil {
|
||
record.DeviceHmd = *score.Score.DeviceHmd
|
||
}
|
||
if score.Score.DeviceControllerLeft != nil {
|
||
record.DeviceControllerLeft = *score.Score.DeviceControllerLeft
|
||
}
|
||
if score.Score.DeviceControllerRight != nil {
|
||
record.DeviceControllerRight = *score.Score.DeviceControllerRight
|
||
}
|
||
records = append(records, record)
|
||
}
|
||
}
|
||
// 获取歌曲ID
|
||
hashs := make([]string, 0)
|
||
for _, record := range records {
|
||
hashs = append(hashs, record.SongHash)
|
||
}
|
||
hashToSongId, err := util.GetSongIdsByHash(hashs)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
for i := range records {
|
||
records[i].SongId = hashToSongId[records[i].SongHash]
|
||
}
|
||
return records, nil
|
||
}
|