From 769308389afc957432340c12fe135046a3e221a5 Mon Sep 17 00:00:00 2001 From: lixiangwuxian Date: Fri, 11 Oct 2024 00:14:11 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + auth/auth.go | 5 + go.mod | 1 + go.sum | 9 ++ handler/beatleader/beatleader.go | 26 +++++ handler/health/health.go | 19 ++++ handler/say/say.go | 6 +- handler/scoresaber/score.go | 13 ++- service/jrrp/jrrp.go | 62 ++++------- service/say/say.go | 4 + service/scoresaber/bind_ss.go | 169 ++++++++++++++++++------------ service/scoresaber/gen_picture.go | 5 + service/scoresaber/hot.go | 40 ++++--- service/scoresaber/model.go | 45 ++++---- service/xibao/image_gen.go | 14 +-- sqlite3/db.go | 32 ++++++ 16 files changed, 289 insertions(+), 164 deletions(-) create mode 100644 auth/auth.go create mode 100644 handler/beatleader/beatleader.go create mode 100644 handler/health/health.go create mode 100644 service/say/say.go create mode 100644 service/scoresaber/gen_picture.go create mode 100644 sqlite3/db.go diff --git a/.gitignore b/.gitignore index 71c04c0..4f103e1 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ bindss.db tmp/xibao.png resource/font.ttf resource/xibao_background.png +data.db +config.yml +tmp/ \ No newline at end of file diff --git a/auth/auth.go b/auth/auth.go new file mode 100644 index 0000000..f8ebc53 --- /dev/null +++ b/auth/auth.go @@ -0,0 +1,5 @@ +package auth + +func isRootUser(qqID string) bool { + return false +} diff --git a/go.mod b/go.mod index 3f6224b..7b360de 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.22.6 require ( github.com/fogleman/gg v1.3.0 github.com/gorilla/websocket v1.5.3 + github.com/jmoiron/sqlx v1.4.0 github.com/mattn/go-sqlite3 v1.14.23 github.com/sashabaranov/go-openai v1.30.3 ) diff --git a/go.sum b/go.sum index 467f856..555cd5c 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,18 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0= github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/sashabaranov/go-openai v1.30.3 h1:TEdRP3otRXX2A7vLoU+kI5XpoSo7VUUlM/rEttUqgek= diff --git a/handler/beatleader/beatleader.go b/handler/beatleader/beatleader.go new file mode 100644 index 0000000..562837e --- /dev/null +++ b/handler/beatleader/beatleader.go @@ -0,0 +1,26 @@ +package beatleader + +import ( + "git.lxtend.com/qqbot/handler" + "git.lxtend.com/qqbot/model" +) + +func init() { + handler.RegisterHandler("查bl", getMyBL) + // handler.RegisterHandler("绑定bl", bindBL) + // handler.RegisterHandler("解绑bl", unbindSS) + // handler.RegisterHandler("最新bl", getMyRecentScore) + // handler.RegisterHandler("最热bl", getRecentScore) +} + +func getMyBL(msg model.Message) (reply model.Reply) { + if len(msg.Msg) <= len("查bl ") { + return model.Reply{} + } + bindResult := "" + return model.Reply{ + ReplyMsg: bindResult, + ReferOriginMsg: true, + FromMsg: msg, + } +} diff --git a/handler/health/health.go b/handler/health/health.go new file mode 100644 index 0000000..e0d6f02 --- /dev/null +++ b/handler/health/health.go @@ -0,0 +1,19 @@ +package health + +import ( + "git.lxtend.com/qqbot/handler" + "git.lxtend.com/qqbot/model" +) + +func init() { + handler.RegisterHandler("health", health) +} + +func health(msg model.Message) (reply model.Reply) { + heathResult := "" + return model.Reply{ + ReplyMsg: heathResult, + ReferOriginMsg: true, + FromMsg: msg, + } +} diff --git a/handler/say/say.go b/handler/say/say.go index 315cbbc..090bbd2 100644 --- a/handler/say/say.go +++ b/handler/say/say.go @@ -6,15 +6,15 @@ import ( ) func init() { - handler.RegisterHandler("记下", say) + handler.RegisterHandler("kw", say) } func say(msg model.Message) (reply model.Reply) { - if len(msg.Msg) <= 5 { + if len(msg.Msg) <= len("kw ") { return model.Reply{} } return model.Reply{ - ReplyMsg: msg.Msg[5:], + ReplyMsg: "记下了", ReferOriginMsg: true, FromMsg: msg, } diff --git a/handler/scoresaber/score.go b/handler/scoresaber/score.go index bc550d2..91be312 100644 --- a/handler/scoresaber/score.go +++ b/handler/scoresaber/score.go @@ -9,16 +9,21 @@ import ( ) func init() { - handler.RegisterHandler("查ss", getScore) + handler.RegisterHandler("查ss", getMySS) handler.RegisterHandler("绑定ss", bindSS) handler.RegisterHandler("解绑ss", unbindSS) handler.RegisterHandler("最新ss", getMyRecentScore) handler.RegisterHandler("最热ss", getRecentScore) } -func getScore(msg model.Message) (reply model.Reply) { +func getMySS(msg model.Message) (reply model.Reply) { + resultStr := "" + var err error + for ; err != nil; resultStr, err = scoresaber.SSQuery.GetScore(strconv.Itoa(int(msg.UserId))) { + + } return model.Reply{ - ReplyMsg: scoresaber.SSQuery.GetScore(strconv.Itoa(int(msg.UserId))), + ReplyMsg: resultStr, ReferOriginMsg: true, FromMsg: msg, } @@ -54,7 +59,7 @@ func getRecentScore(msg model.Message) (reply model.Reply) { } } scoreMsg := "" - for _, v := range scoresaber.ScoresManager.GetRecentScores(count) { + for _, v := range scoresaber.ScoresManager.GetRecentScores(count, " WHERE country = 'CN' ") { scoreMsg += v.ToString() + "\n\n" } if len(scoreMsg) > 0 { diff --git a/service/jrrp/jrrp.go b/service/jrrp/jrrp.go index cff79c8..edfc0e1 100644 --- a/service/jrrp/jrrp.go +++ b/service/jrrp/jrrp.go @@ -2,34 +2,24 @@ package jrrp import ( "database/sql" - "fmt" - "log" "math" "math/rand" "time" + "git.lxtend.com/qqbot/sqlite3" + "github.com/jmoiron/sqlx" _ "github.com/mattn/go-sqlite3" ) // 初始化 SQLite 数据库 func initDB() { - db, err := sql.Open("sqlite3", "./jrrp.db") - if err != nil { - log.Print(err) - } - defer db.Close() - createTableSQL := `CREATE TABLE IF NOT EXISTS jrrp ( id INTEGER PRIMARY KEY AUTOINCREMENT, qqid TEXT UNIQUE, date TEXT, rp_value INTEGER );` - - _, err = db.Exec(createTableSQL) - if err != nil { - log.Print(err) - } + sqlite3.TryCreateTable(createTableSQL) } // 获取今天的日期 @@ -50,26 +40,21 @@ func rpValueConstructor() (string, int) { // Jrrp 结构体 type Jrrp struct { - db *sql.DB } // NewJrrp 创建 Jrrp 实例 func NewJrrp() *Jrrp { initDB() - db, err := sql.Open("sqlite3", "./jrrp.db") - if err != nil { - log.Print(err) - } - return &Jrrp{db: db} + return &Jrrp{} } // findJrrp 查找RP数据 -func (j *Jrrp) findJrrp(qqid string) (string, int, error) { +func (j *Jrrp) findJrrp(qqid string, tx *sqlx.Tx) (string, int, error) { var date string var rpValue int query := "SELECT date, rp_value FROM jrrp WHERE qqid = ?" - err := j.db.QueryRow(query, qqid).Scan(&date, &rpValue) + err := tx.QueryRow(query, qqid).Scan(&date, &rpValue) if err != nil { if err == sql.ErrNoRows { return "", 0, nil @@ -80,47 +65,44 @@ func (j *Jrrp) findJrrp(qqid string) (string, int, error) { } // insertJrrp 插入RP数据 -func (j *Jrrp) insertJrrp(qqid string, date string, rpValue int) error { +func (j *Jrrp) insertJrrp(qqid string, date string, rpValue int, tx *sqlx.Tx) error { query := "INSERT OR REPLACE INTO jrrp (qqid, date, rp_value) VALUES (?, ?, ?)" - _, err := j.db.Exec(query, qqid, date, rpValue) + _, err := tx.Exec(query, qqid, date, rpValue) return err } -// updateJrrp 更新RP数据 -func (j *Jrrp) updateJrrp(qqid string) error { - date, _, err := j.findJrrp(qqid) - if err != nil { - return err - } - if date == "" { - return fmt.Errorf("updateJrrp()-> Error: the qqid does not exist!") - } - newDate, newRpValue := rpValueConstructor() - return j.insertJrrp(qqid, newDate, newRpValue) -} - // GetJrrp 获取RP值 func (j *Jrrp) GetJrrp(qqid string) (int, error) { + tx, err := sqlite3.GetTran() + if err != nil { + return 0, err + } + defer tx.Rollback() today := getTodayFullDate() - date, rpValue, err := j.findJrrp(qqid) + date, rpValue, err := j.findJrrp(qqid, tx) if err != nil { return 0, err } if date == "" || date != today { newDate, newRpValue := rpValueConstructor() - err = j.insertJrrp(qqid, newDate, newRpValue) + err = j.insertJrrp(qqid, newDate, newRpValue, tx) if err != nil { return 0, err } + if err = tx.Commit(); err != nil { + return 0, err + } return newRpValue, nil } - + if err = tx.Commit(); err != nil { + return 0, err + } return rpValue, nil } // Close 关闭数据库连接 func (j *Jrrp) Close() { - j.db.Close() + sqlite3.GetDB().Close() } diff --git a/service/say/say.go b/service/say/say.go new file mode 100644 index 0000000..ec74ee4 --- /dev/null +++ b/service/say/say.go @@ -0,0 +1,4 @@ +package say + +func saveSay(key string, value string, group string) { +} diff --git a/service/scoresaber/bind_ss.go b/service/scoresaber/bind_ss.go index 98216ba..7cce51b 100644 --- a/service/scoresaber/bind_ss.go +++ b/service/scoresaber/bind_ss.go @@ -7,16 +7,11 @@ import ( "strconv" "time" + "git.lxtend.com/qqbot/sqlite3" _ "github.com/mattn/go-sqlite3" ) -func initDB() { - db, err := sql.Open("sqlite3", "./bindss.db") - if err != nil { - log.Print(err) - } - defer db.Close() - +func init() { createBindTableSQL := `CREATE TABLE IF NOT EXISTS ssBind ( id INTEGER PRIMARY KEY AUTOINCREMENT, qqid TEXT UNIQUE, @@ -60,6 +55,7 @@ func initDB() { missed_notes INT, max_combo INT, score INT, + max_score INT, full_combo BOOLEAN, device_hmd VARCHAR(100), device_controller_left VARCHAR(100), @@ -67,37 +63,18 @@ func initDB() { 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) - } + sqlite3.TryCreateTable(createBindTableSQL) + sqlite3.TryCreateTable(createScoreTableSQL) + sqlite3.TryCreateTable(createRecordTableSQL) } 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() + tx, err := sqlite3.GetTran() if err != nil { log.Print(err) } @@ -129,7 +106,7 @@ func (ss *ssQuery) BindSS(qqId string, ssId string) (reply string) { } func (ss *ssQuery) UnbindSS(qqId string) (reply string) { - tx, err := ss.db.Begin() + tx, err := sqlite3.GetTran() if err != nil { log.Print(err) } @@ -152,24 +129,29 @@ func (ss *ssQuery) UnbindSS(qqId string) (reply string) { return "解绑成功,重新绑定请输入\"绑定ss [ssId]\"" } -func (ss *ssQuery) GetScore(qqId string) (reply string) { - tx, err := ss.db.Begin() +func (ss *ssQuery) 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() - //是否已绑定 - 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) + err = tx.Get(&ssId, "SELECT ssid FROM ssBind WHERE qqid = ?", qqId) + if err != nil { + return "您未绑定ss账号,输入\"绑定ss [ssId]\"绑定", nil + } + + // 查询玩家数据 data, _ := FetchPlayerData(ssId) if data == nil { - return "查询出错,服务器返回了空数据" + return "查询出错,服务器返回了空数据", errors.New("查询出错,服务器返回了空数据") } + + // 构建 PlayerDataLite 结构体 dataLite := PlayerDataLite{ ID: data.ID, Name: data.Name, @@ -183,31 +165,60 @@ func (ss *ssQuery) GetScore(qqId string) (reply string) { TotalPlayCount: data.ScoreStats.TotalPlayCount, RankedPlayCount: data.ScoreStats.RankedPlayCount, ReplaysWatched: data.ScoreStats.ReplaysWatched, - GeneratedTime: time.Now(), + GeneratedTime: time.Now().Format("2006-01-02 15:04:05.999999999-07:00"), } + + // 查询最近的玩家数据 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) + err = tx.Get(&lastDataLite, "SELECT * FROM ssData WHERE id = ? ORDER BY generated_time DESC LIMIT 1", dataLite.ID) + if err != nil && err != sql.ErrNoRows { + log.Print(err) + return "查询历史数据时出错", err + } + + // 如果有新的数据,则插入 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.NamedExec(`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 (: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 { - return "SQL事务提交失败,请重试" + log.Print(err) + return "SQL事务提交失败,请重试", err } - return data.LastDiffToString(lastDataLite) + + // 返回差异信息 + return data.LastDiffToString(lastDataLite), nil } + + // 如果没有新数据,直接提交事务 err = tx.Commit() if err != nil { - return "SQL事务提交失败,请重试" + log.Print(err) + return "SQL事务提交失败,请重试", err } - return data.ToString() + + // 返回当前数据的字符串表示 + return data.ToString(), nil } func (ss *ssQuery) SaveRecord(cmdData CommandData) { - tx, err := ss.db.Begin() + 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: cmdData.Score.ID, SsID: cmdData.Score.LeaderboardPlayerInfo.ID, @@ -226,6 +237,7 @@ func (ss *ssQuery) SaveRecord(cmdData CommandData) { Multiplier: cmdData.Score.Multiplier, BadCuts: cmdData.Score.BadCuts, Score: cmdData.Score.ModifiedScore, + MaxScore: cmdData.Leaderboard.MaxScore, MissedNotes: cmdData.Score.MissedNotes, MaxCombo: cmdData.Score.MaxCombo, FullCombo: cmdData.Score.FullCombo, @@ -234,6 +246,8 @@ func (ss *ssQuery) SaveRecord(cmdData CommandData) { DeviceControllerRight: "", GeneratedTime: time.Now().Format("2006-01-02 15:04:05.999999999-07:00"), } + + // 检查设备信息并设置 if cmdData.Score.DeviceHmd != nil { dataLite.DeviceHmd = *cmdData.Score.DeviceHmd } @@ -243,9 +257,25 @@ func (ss *ssQuery) SaveRecord(cmdData CommandData) { 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 { + + // 使用 NamedExec 插入数据 + _, err = tx.NamedExec(`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, max_score, + full_combo, device_hmd, device_controller_left, device_controller_right, + generated_time) + VALUES (: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, :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) @@ -253,37 +283,42 @@ func (ss *ssQuery) SaveRecord(cmdData CommandData) { } func (ss *ssQuery) GetRecentScores(count int, qqId string) ([]RecordDataLite, error) { - tx, err := ss.db.Begin() + db := sqlite3.GetDB() // 假设 sqlite3.GetDB() 返回 *sqlx.DB + tx, err := db.Beginx() if err != nil { log.Print(err) + return nil, errors.New("数据库连接失败,请稍后重试") } defer tx.Rollback() + + // 查询绑定的 ssId var ssId string - tx.QueryRow("SELECT ssid FROM ssBind WHERE qqid = ?", qqId).Scan(&ssId) - if ssId == "" { - return nil, errors.New("未绑定ss账号,输入\"绑定ss [ssId]\"绑定") + err = tx.Get(&ssId, "SELECT ssid FROM ssBind WHERE qqid = ?", qqId) + if err != nil { + if err == sql.ErrNoRows { + return nil, errors.New("未绑定ss账号,输入\"绑定ss [ssId]\"绑定") + } + log.Println("查询 ssId 出错:", err) + return nil, errors.New("查询 ssId 失败") } - rows, err := tx.Query("SELECT * FROM ssRecordData WHERE ss_id = ? ORDER BY generated_time DESC LIMIT ?", ssId, count) + + // 查询记录 + var records []RecordDataLite + err = tx.Select(&records, "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) + log.Println("查询数据出错:", err) + return nil, errors.New("查询记录失败") } + + // 提交事务 err = tx.Commit() if err != nil { log.Print(err) + return nil, errors.New("提交事务失败") } + return records, nil } diff --git a/service/scoresaber/gen_picture.go b/service/scoresaber/gen_picture.go new file mode 100644 index 0000000..ce60089 --- /dev/null +++ b/service/scoresaber/gen_picture.go @@ -0,0 +1,5 @@ +package scoresaber + +func (data *PlayerData) ToPicture() (outputImgPath string) { + return "" +} diff --git a/service/scoresaber/hot.go b/service/scoresaber/hot.go index 9f60928..3c07abf 100644 --- a/service/scoresaber/hot.go +++ b/service/scoresaber/hot.go @@ -2,9 +2,9 @@ package scoresaber import ( "log" - "sync" "time" + "git.lxtend.com/qqbot/sqlite3" "github.com/gorilla/websocket" ) @@ -13,10 +13,8 @@ const wsURL = "wss://scoresaber.com/ws" var ScoresManager = scoresManager{} type scoresManager struct { - recentScores []Command - mu sync.Mutex - conn *websocket.Conn - retryTimes int + conn *websocket.Conn + retryTimes int } func init() { @@ -33,7 +31,6 @@ func (sm *scoresManager) connect() error { return err } sm.retryTimes = 0 - sm.recentScores = make([]Command, 0) go sm.receiveData() return nil } @@ -58,25 +55,24 @@ func (sm *scoresManager) receiveData() { continue } SSQuery.SaveRecord(cmd.CommandData) - if cmd.CommandData.Score.LeaderboardPlayerInfo.Country != "CN" { - continue - } - sm.mu.Lock() - if len(sm.recentScores) >= 50 { - sm.recentScores = sm.recentScores[1:] - } - sm.recentScores = append(sm.recentScores, cmd) - sm.mu.Unlock() } } -func (sm *scoresManager) GetRecentScores(count int) []Command { - sm.mu.Lock() - defer sm.mu.Unlock() - if count > len(sm.recentScores) { - count = len(sm.recentScores) +func (sm *scoresManager) GetRecentScores(count int, predict string) []RecordDataLite { + db := sqlite3.GetDB() // 假设 sqlite3.GetDB() 返回 *sqlx.DB + scoresCopy := make([]RecordDataLite, 0, count) + + query := "SELECT * FROM ssRecordData" + if predict != "" { + query += " " + predict } - scoresCopy := make([]Command, count) - copy(scoresCopy, sm.recentScores[len(sm.recentScores)-count:]) + query += " ORDER BY generated_time DESC LIMIT ?" + + err := db.Select(&scoresCopy, query, count) + if err != nil { + log.Print(err) + return nil + } + return scoresCopy } diff --git a/service/scoresaber/model.go b/service/scoresaber/model.go index 6a5b429..241d219 100644 --- a/service/scoresaber/model.go +++ b/service/scoresaber/model.go @@ -129,6 +129,7 @@ type RecordDataLite struct { MissedNotes int `json:"missedNotes" db:"missed_notes"` MaxCombo int `json:"maxCombo" db:"max_combo"` Score int `json:"score" db:"score"` + MaxScore int `json:"maxScore" db:"max_score"` FullCombo bool `json:"fullCombo" db:"full_combo"` DeviceHmd string `json:"deviceHmd" db:"device_hmd"` DeviceControllerLeft string `json:"deviceControllerLeft" db:"device_controller_left"` @@ -137,19 +138,19 @@ type RecordDataLite struct { } func (r RecordDataLite) ToString() string { - formatedStrRanked := "使用 %s 在 %s 的 %s 难度(%.1f星级)中获得了 %d 分,pp 为 %.2f。" - formatedStrUnranked := "使用 %s 在 %s 的 %s 难度中获得了 %d 分。" - formatedStrWithoutDevice := "在 %s 的 %s 难度(%.1f星级)中获得了 %d 分,pp 为 %.2f。" - formatedStrWithoutDeviceAndRank := "在 %s 的 %s 难度(%.1f星级)中获得了 %d 分。" + formatedStrRanked := "%s 使用 %s 在 %s 的 %s 难度(%.1f星级)中获得了 %d 分,pp 为 %.2f,准度为 %s。" + formatedStrUnranked := "%s 使用 %s 在 %s 的 %s 难度中获得了 %d 分,准度为 %s。" + formatedStrWithoutDevice := "%s 在 %s 的 %s 难度(%.1f星级)中获得了 %d 分,pp 为 %.2f,准度为 %s。" + formatedStrWithoutDeviceAndRank := "%s 在 %s 的 %s 难度(%.1f星级)中获得了 %d 分,准度为 %s。" hardStr := strings.Split(r.DifficultyRaw, "_")[1] if r.Stars == 0 && r.DeviceHmd != "" { - return fmt.Sprintf(formatedStrUnranked, r.DeviceHmd, r.SongName, hardStr, r.Score) + return fmt.Sprintf(formatedStrUnranked, r.Name, r.DeviceHmd, r.SongName, hardStr, r.Score, fmt.Sprintf("%.2f%%", float64(r.Score)/float64(r.MaxScore)*100)) } else if r.Stars != 0 && r.DeviceHmd != "" { - return fmt.Sprintf(formatedStrRanked, r.DeviceHmd, r.SongName, hardStr, r.Stars, r.Score, r.PP) + return fmt.Sprintf(formatedStrRanked, r.Name, r.DeviceHmd, r.SongName, hardStr, r.Stars, r.Score, r.PP, fmt.Sprintf("%.2f%%", float64(r.Score)/float64(r.MaxScore)*100)) } else if r.Stars != 0 && r.DeviceHmd == "" { - return fmt.Sprintf(formatedStrWithoutDevice, r.SongName, hardStr, r.Stars, r.Score, r.PP) + return fmt.Sprintf(formatedStrWithoutDevice, r.Name, r.SongName, hardStr, r.Stars, r.Score, r.PP, fmt.Sprintf("%.2f%%", float64(r.Score)/float64(r.MaxScore)*100)) } else { - return fmt.Sprintf(formatedStrWithoutDeviceAndRank, r.SongName, hardStr, r.Stars, r.Score) + return fmt.Sprintf(formatedStrWithoutDeviceAndRank, r.Name, r.SongName, hardStr, r.Stars, r.Score, fmt.Sprintf("%.2f%%", float64(r.Score)/float64(r.MaxScore)*100)) } } @@ -186,20 +187,20 @@ type PlayerData struct { } type PlayerDataLite struct { - ID string `json:"id" db:"id"` - Name string `json:"name" db:"name"` - Country string `json:"country" db:"country"` - Device string `json:"device" db:"device"` - PP float64 `json:"pp" db:"pp"` - Rank int `json:"rank" db:"rank"` - CountryRank int `json:"countryRank" db:"country_rank"` - TotalScore int `json:"totalScore" db:"total_score"` - TotalRankedScore int `json:"totalRankedScore" db:"total_ranked_score"` - AverageRankedAccuracy float64 `json:"averageRankedAccuracy" db:"average_ranked_accuracy"` - TotalPlayCount int `json:"totalPlayCount" db:"total_play_count"` - RankedPlayCount int `json:"rankedPlayCount" db:"ranked_play_count"` - ReplaysWatched int `json:"replaysWatched" db:"replays_watched"` - GeneratedTime time.Time `json:"generatedTime" db:"generated_time"` + ID string `json:"id" db:"id"` + Name string `json:"name" db:"name"` + Country string `json:"country" db:"country"` + Device string `json:"device" db:"device"` + PP float64 `json:"pp" db:"pp"` + Rank int `json:"rank" db:"rank"` + CountryRank int `json:"countryRank" db:"country_rank"` + TotalScore int `json:"totalScore" db:"total_score"` + TotalRankedScore int `json:"totalRankedScore" db:"total_ranked_score"` + AverageRankedAccuracy float64 `json:"averageRankedAccuracy" db:"average_ranked_accuracy"` + TotalPlayCount int `json:"totalPlayCount" db:"total_play_count"` + RankedPlayCount int `json:"rankedPlayCount" db:"ranked_play_count"` + ReplaysWatched int `json:"replaysWatched" db:"replays_watched"` + GeneratedTime string `json:"generatedTime" db:"generated_time"` } func (p PlayerData) ToString() string { diff --git a/service/xibao/image_gen.go b/service/xibao/image_gen.go index 719aafb..c7b8f78 100644 --- a/service/xibao/image_gen.go +++ b/service/xibao/image_gen.go @@ -7,9 +7,9 @@ import ( "github.com/fogleman/gg" ) -func GenerateCongratulationImage(text string, outputFile string) { +func GenerateCongratulationImage(text string, inputFile, outputFile string, isGood bool) { // 加载喜报背景图片 - im, err := gg.LoadImage("./resource/xibao_background.png") // 需要提前准备的背景图片 + im, err := gg.LoadImage(inputFile) // 需要提前准备的背景图片 if err != nil { log.Print("无法加载喜报图片:", err) return @@ -28,10 +28,12 @@ func GenerateCongratulationImage(text string, outputFile string) { log.Print("无法加载字体:", err) return } - - // 设置文本颜色为红色 - dc.SetRGB(1, 0, 0) - + if isGood { + // 设置文本颜色为红色 + dc.SetRGB(1, 0.1, 0.1) + } else { + dc.SetRGB(0.1, 0.1, 1) + } // 将文本按 \n 分割为多行 lines := strings.Split(text, "\n") diff --git a/sqlite3/db.go b/sqlite3/db.go new file mode 100644 index 0000000..1a88264 --- /dev/null +++ b/sqlite3/db.go @@ -0,0 +1,32 @@ +package sqlite3 + +import ( + "github.com/jmoiron/sqlx" + + _ "github.com/mattn/go-sqlite3" +) + +var db *sqlx.DB + +func InitDB() { + if db == nil { + db, _ = sqlx.Open("sqlite3", "./data.db") + } +} + +func TryCreateTable(query string) error { + InitDB() + _, err := db.Exec(query) + if err != nil { + return err + } + return nil +} + +func GetDB() *sqlx.DB { + return db +} + +func GetTran() (*sqlx.Tx, error) { + return db.Beginx() +}