package scoresaber import ( "database/sql" "errors" "log" "strconv" "time" _ "github.com/mattn/go-sqlite3" ) func initDB() { db, err := sql.Open("sqlite3", "./bindss.db") if err != nil { log.Print(err) } defer db.Close() createBindTableSQL := `CREATE TABLE IF NOT EXISTS ssBind ( id INTEGER PRIMARY KEY AUTOINCREMENT, qqid TEXT UNIQUE, ssid TEXT UNIQUE );` createScoreTableSQL := `CREATE TABLE IF NOT EXISTS ssData ( 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 ssRecordData ( id INTEGER PRIMARY KEY AUTOINCREMENT, score_id INT, ss_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, full_combo BOOLEAN, device_hmd VARCHAR(100), device_controller_left VARCHAR(100), device_controller_right VARCHAR(100), generated_time TEXT );` _, err = db.Exec(createBindTableSQL) if err != nil { log.Print(err) } _, err = db.Exec(createScoreTableSQL) if err != nil { log.Print(err) } _, err = db.Exec(createRecordTableSQL) if err != nil { log.Print(err) } } var SSQuery = &ssQuery{} type ssQuery struct { db *sql.DB } func init() { initDB() db, err := sql.Open("sqlite3", "./bindss.db") if err != nil { log.Print(err) } SSQuery = &ssQuery{db: db} } func (ss *ssQuery) BindSS(qqId string, ssId string) (reply string) { tx, err := ss.db.Begin() if err != nil { log.Print(err) } defer tx.Rollback() // ssId为数字 if _, isNum := strconv.Atoi(ssId); isNum != nil { return "ssId格式错误,应当为一串数字" } data, _ := FetchPlayerData(ssId) if data == nil { return "未找到玩家" } //去重 if rows, err := tx.Query("SELECT * FROM ssBind WHERE qqid = ?", qqId); err == nil { if rows.Next() { return "您已绑定过ss账号,请先输入\"解绑ss\"解绑" } rows.Close() } _, err = tx.Exec("INSERT INTO ssBind(qqid, ssid) VALUES(?, ?)", qqId, ssId) if err != nil { return "绑定失败" } err = tx.Commit() if err != nil { return "无法提交事务" } return "和用户名为 " + data.Name + " 的用户绑定成功,输入\"查ss\"查看个人数据" } func (ss *ssQuery) UnbindSS(qqId string) (reply string) { tx, err := ss.db.Begin() 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 "您未绑定ss账号,输入\"绑定ss [ssId]\"绑定" } rows.Close() } _, err = tx.Exec("DELETE FROM ssBind WHERE qqid = ?", qqId) if err != nil { return "解绑失败" } err = tx.Commit() if err != nil { return "无法提交事务" } return "解绑成功,重新绑定请输入\"绑定ss [ssId]\"" } func (ss *ssQuery) GetScore(qqId string) (reply string) { tx, err := ss.db.Begin() 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 "您未绑定ss账号,输入\"绑定ss [ssId]\"绑定" } } var ssId string tx.QueryRow("SELECT ssid FROM ssBind WHERE qqid = ?", qqId).Scan(&ssId) data, _ := FetchPlayerData(ssId) if data == nil { return "查询出错,服务器返回了空数据" } dataLite := PlayerDataLite{ ID: data.ID, Name: data.Name, 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(), } var lastDataLite PlayerDataLite tx.QueryRow("SELECT * FROM ssData WHERE id = ? ORDER BY generated_time DESC LIMIT 1", dataLite.ID).Scan(&lastDataLite.ID, &lastDataLite.Name, &lastDataLite.Country, &lastDataLite.PP, &lastDataLite.Rank, &lastDataLite.CountryRank, &lastDataLite.TotalScore, &lastDataLite.TotalRankedScore, &lastDataLite.AverageRankedAccuracy, &lastDataLite.TotalPlayCount, &lastDataLite.RankedPlayCount, &lastDataLite.ReplaysWatched, &lastDataLite.GeneratedTime) if lastDataLite.TotalPlayCount != dataLite.TotalPlayCount { tx.Exec("INSERT INTO ssData(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(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13)", dataLite.ID, dataLite.Name, dataLite.Country, dataLite.PP, dataLite.Rank, dataLite.CountryRank, dataLite.TotalScore, dataLite.TotalRankedScore, dataLite.AverageRankedAccuracy, dataLite.TotalPlayCount, dataLite.RankedPlayCount, dataLite.ReplaysWatched, dataLite.GeneratedTime) err = tx.Commit() if err != nil { return "SQL事务提交失败,请重试" } return data.LastDiffToString(lastDataLite) } err = tx.Commit() if err != nil { return "SQL事务提交失败,请重试" } return data.ToString() } func (ss *ssQuery) SaveRecord(cmdData CommandData) { tx, err := ss.db.Begin() if err != nil { log.Print(err) } defer tx.Rollback() dataLite := RecordDataLite{ 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, BadCuts: cmdData.Score.BadCuts, Score: cmdData.Score.ModifiedScore, MissedNotes: cmdData.Score.MissedNotes, MaxCombo: cmdData.Score.MaxCombo, FullCombo: cmdData.Score.FullCombo, DeviceHmd: "", DeviceControllerLeft: "", DeviceControllerRight: "", GeneratedTime: time.Now().Format("2006-01-02 15:04:05.999999999-07:00"), } if cmdData.Score.DeviceHmd != nil { dataLite.DeviceHmd = *cmdData.Score.DeviceHmd } if cmdData.Score.DeviceControllerLeft != nil { dataLite.DeviceControllerLeft = *cmdData.Score.DeviceControllerLeft } if cmdData.Score.DeviceControllerRight != nil { dataLite.DeviceControllerRight = *cmdData.Score.DeviceControllerRight } if _, err = tx.Exec("INSERT INTO ssRecordData(score_id, ss_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, full_combo, device_hmd, device_controller_left, device_controller_right, generated_time) VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23,?24)", dataLite.ScoreID, dataLite.SsID, dataLite.Name, dataLite.Country, dataLite.SongName, dataLite.SongSubName, dataLite.SongAuthorName, dataLite.SongHash, dataLite.CoverImage, dataLite.DifficultyRaw, dataLite.PP, dataLite.Stars, dataLite.Weight, dataLite.Modifiers, dataLite.Multiplier, dataLite.BadCuts, dataLite.MissedNotes, dataLite.MaxCombo, dataLite.Score, dataLite.FullCombo, dataLite.DeviceHmd, dataLite.DeviceControllerLeft, dataLite.DeviceControllerRight, dataLite.GeneratedTime); err != nil { log.Print(err) } err = tx.Commit() if err != nil { log.Print(err) } } func (ss *ssQuery) GetRecentScores(count int, qqId string) ([]RecordDataLite, error) { tx, err := ss.db.Begin() if err != nil { log.Print(err) } defer tx.Rollback() var ssId string tx.QueryRow("SELECT ssid FROM ssBind WHERE qqid = ?", qqId).Scan(&ssId) if ssId == "" { return nil, errors.New("未绑定ss账号,输入\"绑定ss [ssId]\"绑定") } rows, err := tx.Query("SELECT * FROM ssRecordData WHERE ss_id = ? ORDER BY generated_time DESC LIMIT ?", ssId, count) if err != nil { if err == sql.ErrNoRows { return nil, errors.New("未查询到数据") } log.Println("Query error:", err) return nil, errors.New("") } defer rows.Close() var records []RecordDataLite for rows.Next() { var record RecordDataLite if err := rows.Scan(&record.ID, &record.ScoreID, &record.SsID, &record.Name, &record.Country, &record.SongName, &record.SongSubName, &record.SongAuthorName, &record.SongHash, &record.CoverImage, &record.DifficultyRaw, &record.PP, &record.Stars, &record.Weight, &record.Modifiers, &record.Multiplier, &record.BadCuts, &record.MissedNotes, &record.MaxCombo, &record.Score, &record.FullCombo, &record.DeviceHmd, &record.DeviceControllerLeft, &record.DeviceControllerRight, &record.GeneratedTime); err != nil { log.Println("Scan error:", err) return nil, err } records = append(records, record) } err = tx.Commit() if err != nil { log.Print(err) } return records, nil }