package beatleader import ( "database/sql" "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.BLData{}, &service.BLRecordData{}) } var BLQuery = &blQuery{} type blQuery struct { } func (bl *blQuery) BindBL(qqId string, blId string) (reply string) { db := sqlite3.GetGormDB() tx := db.Begin() defer tx.Rollback() // blId为数字 if _, isNum := strconv.Atoi(blId); isNum != nil { return "blId格式错误,应当为一串数字(是您的beatleader主页链接中的末尾数字部分,一般和您的steamID相同)" } data, err := FetchPlayerData(blId) if data == nil { if err != nil { return "请求出错,报错如下,如果确定命令没问题可以重新试试:" + err.Error() } return "未找到玩家,请检查ID后重试" } //去重 if rows, err := tx.Select("ssid").Where("qqid = ?", qqId).Find(&service.SSBind{}).Rows(); err == nil { if rows.Next() { var currentBlId string err := rows.Scan(¤tBlId) if err != nil { log.Print(err) } rows.Close() // 获取当前绑定账号的信息 if currentData, err := FetchPlayerData(currentBlId); err == nil && currentData != nil { return fmt.Sprintf("您已绑定至bl账号:%s,请先输入\"解绑bl\"解绑", currentData.Name) } return "您已绑定过bl账号,请先输入\"解绑bl\"解绑" } else { rows.Close() } } if rows, err := tx.Select("ssid").Where("ssid = ?", blId).Find(&service.SSBind{}).Rows(); err == nil { if rows.Next() { return "该bl账号已绑定至其他用户" } rows.Close() } err = tx.Create(&service.SSBind{QQID: qqId, SSID: blId}).Commit().Error if err != nil { return "绑定失败,请稍后重试:" + err.Error() } return "和用户名为 " + data.Name + " 的用户绑定成功,同时也绑定了您的scoresaber账号。输入\"查bl\"查看个人数据" } func (bl *blQuery) UnbindBL(qqId string) (reply string) { db := sqlite3.GetGormDB() tx := db.Begin() defer tx.Rollback() //是否已绑定 if rows, err := tx.Select("ssid").Where("qqid = ?", qqId).Find(&service.SSBind{}).Rows(); err == nil { if !rows.Next() { return "您未绑定bl账号,输入\"绑定bl [blId]\"绑定" } rows.Close() } err := tx.Delete(&service.SSBind{QQID: qqId}).Commit().Error if err != nil { return "解绑失败" } return "解绑成功,重新绑定请输入\"绑定bl [blId]\"" } func (bl *blQuery) GetScore(qqId string) (currentData *PlayerDataLite, lastData *PlayerDataLite, err error) { db := sqlite3.GetGormDB() tx := db.Begin() defer tx.Rollback() blId, err := getBLID(qqId) if err != nil { return nil, nil, err } // 查询玩家数据 data, err := FetchPlayerData(blId) if data == nil && err == nil { return nil, nil, errors.New("查询出错,服务器返回了空数据") } if err != nil { log.Print(err) return nil, nil, err } // 构建 PlayerDataLite 结构体 dataLite := data.ToDataLite() // 查询最近的玩家数据 var lastDataLite PlayerDataLite err = tx.Select("*").Where("id = ?", dataLite.ID).Order("generated_time DESC").Limit(1).Find(&lastDataLite).Error if err != nil && err != sql.ErrNoRows { log.Print(err) return nil, nil, err } // 如果有新的数据,则插入 if lastDataLite.IsDiffFrom(dataLite) { //删掉旧数据 err = tx.Delete(&PlayerDataLite{ID: dataLite.ID}).Error if err != nil { log.Print(err) return nil, nil, err } err = tx.Create(&PlayerDataLite{ ID: dataLite.ID, Name: dataLite.Name, Country: dataLite.Country, PP: dataLite.PP, Rank: dataLite.Rank, CountryRank: dataLite.CountryRank, TotalScore: dataLite.TotalScore, TotalRankedScore: dataLite.TotalRankedScore, AverageRankedAccuracy: dataLite.AverageRankedAccuracy, TotalPlayCount: dataLite.TotalPlayCount, RankedPlayCount: dataLite.RankedPlayCount, ReplaysWatched: dataLite.ReplaysWatched, GeneratedTime: time.Now().Format("2006-01-02 15:04:05.999999999-07:00"), }).Error if err != nil { log.Print(err) return nil, nil, err } // 提交事务 err = tx.Commit().Error if err != nil { log.Print(err) return nil, nil, err } // 返回差异信息 return &dataLite, &lastDataLite, nil } // 如果没有新数据,直接提交事务 err = tx.Commit().Error if err != nil { log.Print(err) return nil, nil, err } // 返回当前数据的字符串表示 return &dataLite, nil, nil } func (bl *blQuery) GetScoreWithoutUpdate(qqId string) (currentData *PlayerDataLite, err error) { blId, err := getBLID(qqId) if err != nil { return nil, err } // 查询玩家数据 data, err := FetchPlayerData(blId) if err != nil { return nil, errors.New("查询出错,报错如下" + err.Error()) } if data.ID == "" { return nil, errors.New("未找到玩家,请检查ID后重试") } dataLite := data.ToDataLite() // 返回当前数据的字符串表示 return &dataLite, nil } func (bl *blQuery) GetRecentScores(count int, qqId string) ([]RecordDataLite, error) { // 查询绑定的 blId blId, err := getBLID(qqId) if err != nil { return nil, err } // return records, nil playerData, err := FetchPlayerData(blId) if err != nil { return nil, err } //从线上获取 historyUrl := "https://api.beatleader.xyz/player/%s/scores?leaderboardContext=general&page=1&sortBy=date&order=desc&includeIO=true&count=%d" fullUrl := fmt.Sprintf(historyUrl, blId, count) 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 } var response struct { Data []ScoreData `json:"data"` } err = json.Unmarshal(body, &response) if err != nil { return nil, err } scores := response.Data records := make([]RecordDataLite, 0) for _, score := range scores { dataLite := RecordDataLite{ ScoreID: score.ID, BlID: blId, Name: playerData.Name, Country: playerData.Country, SongName: score.Leaderboard.Song.Name, SongSubName: score.Leaderboard.Song.SubName, SongAuthorName: score.Leaderboard.Song.Author, SongHash: score.Leaderboard.Song.Hash, SongId: "", CoverImage: score.Leaderboard.Song.CoverImage, DifficultyRaw: score.Leaderboard.Difficulty.DifficultyName, PP: score.Pp, Stars: 0, Weight: score.Weight, Modifiers: score.Modifiers, Multiplier: float64(score.ModifiedScore) / float64(score.BaseScore), BadCuts: score.BadCuts, Score: score.ModifiedScore, Rank: score.Rank, MaxScore: int(float64(score.ModifiedScore) / score.Accuracy), FullCombo: score.FullCombo, DeviceHmd: GetHMDStr(score.HMD), DeviceControllerLeft: GetControllerStr(score.Controller), DeviceControllerRight: GetControllerStr(score.Controller), GeneratedTime: time.Unix(score.Timepost, 0).Format("2006-01-02 15:04:05.999999999-07:00"), } if score.Leaderboard.Difficulty.Stars != nil { dataLite.Stars = *score.Leaderboard.Difficulty.Stars } records = append(records, dataLite) } // 获取歌曲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 := 0; i < len(records); i++ { records[i].SongId = hashToSongId[records[i].SongHash] } return records, nil }