371 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 beatleader
import (
"database/sql"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"strconv"
"time"
"git.lxtend.com/qqbot/sqlite3"
_ "github.com/mattn/go-sqlite3"
)
func init() {
createScoreTableSQL := `CREATE TABLE IF NOT EXISTS blData (
id TEXT,
name TEXT,
country TEXT,
pp REAL,
rank INTEGER,
country_rank INTEGER,
total_score INTEGER,
total_ranked_score INTEGER,
average_ranked_accuracy REAL,
total_play_count INTEGER,
ranked_play_count INTEGER,
replays_watched INTEGER,
generated_time TEXT
);`
createRecordTableSQL := `CREATE TABLE IF NOT EXISTS blRecordData (
id INTEGER PRIMARY KEY AUTOINCREMENT,
score_id INT,
bl_id TEXT,
name TEXT,
country TEXT,
song_name VARCHAR(255),
song_sub_name VARCHAR(255),
song_author_name VARCHAR(255),
song_hash VARCHAR(64),
cover_image TEXT,
difficulty_raw VARCHAR(100),
pp REAL,
stars REAL,
weight REAL,
modifiers VARCHAR(255),
multiplier REAL,
bad_cuts INT,
missed_notes INT,
max_combo INT,
score INT,
rank INT,
max_score INT,
full_combo BOOLEAN,
device_hmd VARCHAR(100),
device_controller_left VARCHAR(100),
device_controller_right VARCHAR(100),
generated_time TEXT
);`
sqlite3.TryCreateTable(createScoreTableSQL)
sqlite3.TryCreateTable(createRecordTableSQL)
}
var BLQuery = &blQuery{}
type blQuery struct {
}
func (bl *blQuery) BindBL(qqId string, blId string) (reply string) {
tx, err := sqlite3.GetTran()
if err != nil {
log.Print(err)
}
defer tx.Rollback()
// blId为数字
if _, isNum := strconv.Atoi(blId); isNum != nil {
return "blId格式错误,应当为一串数字(大部分情况下是你的steamID)"
}
data, err := FetchPlayerData(blId)
if data == nil {
if err != nil {
return "未找到玩家,请检查ID后重试:" + err.Error()
}
return "未找到玩家,请检查ID后重试"
}
//去重
if rows, err := tx.Query("SELECT * FROM ssBind WHERE qqid = ?", qqId); err == nil {
if rows.Next() {
return "您已绑定过bl账号,请先输入\"解绑bl\"解绑"
}
rows.Close()
}
if rows, err := tx.Query("SELECT * FROM ssBind WHERE ssid = ?", blId); err == nil {
if rows.Next() {
return "该bl账号已绑定至其他用户"
}
rows.Close()
}
_, err = tx.Exec("INSERT INTO ssBind(qqid, ssid) VALUES(?, ?)", qqId, blId)
if err != nil {
return "绑定失败,请稍后重试:" + err.Error()
}
err = tx.Commit()
if err != nil {
return "无法提交事务"
}
return "和用户名为 " + data.Name + " 的用户绑定成功,输入\"查bl\"查看个人数据"
}
func (bl *blQuery) UnbindBL(qqId string) (reply string) {
tx, err := sqlite3.GetTran()
if err != nil {
log.Print(err)
}
defer tx.Rollback()
//是否已绑定
if rows, err := tx.Query("SELECT * FROM ssBind WHERE qqid = ?", qqId); err == nil {
if !rows.Next() {
return "您未绑定bl账号输入\"绑定bl [blId]\"绑定"
}
rows.Close()
}
_, err = tx.Exec("DELETE FROM ssBind WHERE qqid = ?", qqId)
if err != nil {
return "解绑失败"
}
err = tx.Commit()
if err != nil {
return "无法提交事务"
}
return "解绑成功,重新绑定请输入\"绑定bl [blId]\""
}
func (bl *blQuery) GetScore(qqId string) (reply string, err error) {
db := sqlite3.GetDB() // 假设 sqlite3.GetDB() 返回 *sqlx.DB
tx, err := db.Beginx()
if err != nil {
log.Print(err)
return "数据库连接失败,请稍后重试", err
}
defer tx.Rollback()
blId, err := getBLID(qqId)
if err != nil {
return err.Error(), nil
}
// 查询玩家数据
data, err := FetchPlayerData(blId)
if data == nil && err == nil {
return "查询出错,服务器返回了空数据", errors.New("查询出错,服务器返回了空数据")
}
if err != nil {
log.Print(err)
return "查询出错,服务器返回了空数据" + err.Error(), err
}
// 构建 PlayerDataLite 结构体
dataLite := PlayerDataLite{
ID: data.ID,
Name: data.Name,
Country: data.Country,
PP: data.AccPp + data.PassPp + data.TechPp,
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.WatchedReplays,
GeneratedTime: time.Now().Format("2006-01-02 15:04:05.999999999-07:00"),
}
// 查询最近的玩家数据
var lastDataLite PlayerDataLite
err = tx.Get(&lastDataLite, "SELECT * FROM blData WHERE id = ? ORDER BY generated_time DESC LIMIT 1", dataLite.ID)
if err != nil && err != sql.ErrNoRows {
log.Print(err)
return "查询历史数据时出错", err
}
// 如果有新的数据,则插入
if lastDataLite.IsDiffFrom(dataLite) {
_, err = tx.NamedExec(`INSERT INTO blData (id, name, country, pp, rank, country_rank, total_score,
total_ranked_score, average_ranked_accuracy, total_play_count, ranked_play_count, replays_watched, generated_time)
VALUES (:id, :name, :country, :pp, :rank, :country_rank, :total_score,
:total_ranked_score, :average_ranked_accuracy, :total_play_count, :ranked_play_count, :replays_watched, :generated_time)`, dataLite)
if err != nil {
log.Print(err)
return "插入新数据时出错", err
}
// 提交事务
err = tx.Commit()
if err != nil {
log.Print(err)
return "SQL事务提交失败请重试", err
}
// 返回差异信息
return data.LastDiffToString(lastDataLite), nil
}
// 如果没有新数据,直接提交事务
err = tx.Commit()
if err != nil {
log.Print(err)
return "SQL事务提交失败请重试", err
}
// 返回当前数据的字符串表示
return data.ToString(), nil
}
func (bl *blQuery) SaveRecord(scoreData ScoreData) {
db := sqlite3.GetDB() // 假设 sqlite3.GetDB() 返回 *sqlx.DB
tx, err := db.Beginx()
if err != nil {
log.Print(err)
return
}
defer tx.Rollback()
// 创建 RecordDataLite 结构体实例
dataLite := RecordDataLite{
ScoreID: scoreData.ID,
BlID: scoreData.Player.ID,
Name: scoreData.Player.Name,
Country: scoreData.Player.Country,
SongName: scoreData.Leaderboard.Song.Name,
SongSubName: scoreData.Leaderboard.Song.SubName,
SongAuthorName: scoreData.Leaderboard.Song.Author,
SongHash: scoreData.Leaderboard.Song.Hash,
CoverImage: scoreData.Leaderboard.Song.CoverImage,
DifficultyRaw: scoreData.Leaderboard.Difficulty.DifficultyName,
Stars: 0,
PP: scoreData.Pp,
Weight: scoreData.Weight,
Modifiers: scoreData.Modifiers,
Multiplier: float64(scoreData.ModifiedScore) / float64(scoreData.BaseScore),
BadCuts: scoreData.BadCuts,
Score: scoreData.ModifiedScore,
Rank: scoreData.Rank,
MaxScore: int(float64(scoreData.ModifiedScore) / scoreData.Accuracy),
MissedNotes: scoreData.MissedNotes,
MaxCombo: scoreData.MaxCombo,
FullCombo: scoreData.FullCombo,
DeviceHmd: GetHMDStr(scoreData.HMD),
DeviceControllerLeft: GetControllerStr(scoreData.Controller),
DeviceControllerRight: GetControllerStr(scoreData.Controller),
GeneratedTime: time.Now().Format("2006-01-02 15:04:05.999999999-07:00"),
}
if scoreData.Leaderboard.Difficulty.Stars != nil {
dataLite.Stars = *scoreData.Leaderboard.Difficulty.Stars
}
// 使用 NamedExec 插入数据
_, err = tx.NamedExec(`INSERT INTO blRecordData
(score_id, bl_id, name, country, song_name, song_sub_name, song_author_name,
song_hash, cover_image, difficulty_raw, pp, stars, weight, modifiers,
multiplier, bad_cuts, missed_notes, max_combo, score,rank, max_score,
full_combo, device_hmd, device_controller_left, device_controller_right,
generated_time)
VALUES (:score_id, :bl_id, :name, :country, :song_name, :song_sub_name,
:song_author_name, :song_hash, :cover_image, :difficulty_raw, :pp, :stars,
:weight, :modifiers, :multiplier, :bad_cuts, :missed_notes, :max_combo,
:score,:rank, :max_score, :full_combo, :device_hmd, :device_controller_left,
:device_controller_right, :generated_time)`, dataLite)
if err != nil {
log.Print(err)
return
}
// 提交事务
err = tx.Commit()
if err != nil {
log.Print(err)
}
}
func (bl *blQuery) GetRecentScores(count int, qqId string) ([]RecordDataLite, error) {
db := sqlite3.GetDB() // 假设 sqlite3.GetDB() 返回 *sqlx.DB
tx, err := db.Beginx()
if err != nil {
log.Print(err)
return nil, errors.New("数据库连接失败,请稍后重试")
}
defer tx.Rollback()
// 查询绑定的 blId
blId, err := getBLID(qqId)
if err != nil {
return nil, err
}
// // 查询记录
// var records []RecordDataLite
// err = tx.Select(&records, "SELECT * FROM blRecordData WHERE bl_id = ? ORDER BY generated_time DESC LIMIT ?", blId, count)
// if err != nil {
// if err == sql.ErrNoRows {
// return nil, errors.New("未查询到数据")
// }
// log.Println("查询数据出错:", err)
// return nil, errors.New("查询记录失败")
// }
// // 提交事务
// err = tx.Commit()
// if err != nil {
// log.Print(err)
// return nil, errors.New("提交事务失败")
// }
// return records, nil
//从线上获取
historyUrl := "https://api.beatleader.xyz/player/%s/scores"
fullUrl := fmt.Sprintf(historyUrl, blId)
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 scores []ScoreData
err = json.Unmarshal(body, &scores)
if err != nil {
return nil, err
}
records := make([]RecordDataLite, 0)
for _, score := range scores {
records = append(records, RecordDataLite{
ScoreID: score.ID,
BlID: score.Player.ID,
Name: score.Player.Name,
Country: score.Player.Country,
SongName: score.Leaderboard.Song.Name,
SongSubName: score.Leaderboard.Song.SubName,
SongAuthorName: score.Leaderboard.Song.Author,
SongHash: score.Leaderboard.Song.Hash,
SongId: score.Leaderboard.Song.ID,
CoverImage: score.Leaderboard.Song.CoverImage,
DifficultyRaw: score.Leaderboard.Difficulty.DifficultyName,
PP: score.Pp,
Stars: *score.Leaderboard.Difficulty.Stars,
Weight: score.Weight,
Modifiers: score.Modifiers,
Multiplier: float64(score.ModifiedScore) / float64(score.BaseScore),
BadCuts: score.BadCuts,
Score: score.ModifiedScore,
Rank: score.Rank,
MaxScore: score.ModifiedScore,
FullCombo: score.FullCombo,
DeviceHmd: GetHMDStr(score.HMD),
DeviceControllerLeft: GetControllerStr(score.Controller),
DeviceControllerRight: GetControllerStr(score.Controller),
GeneratedTime: time.Now().Format("2006-01-02 15:04:05.999999999-07:00"),
})
}
return records, nil
}