add bgeatmapget
This commit is contained in:
parent
dd00c39075
commit
b81dffcecc
186
beatmapget/beatmapget.go
Normal file
186
beatmapget/beatmapget.go
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
// Package beatmapget is an helper package to retrieve beatmap information from
|
||||||
|
// the osu! API, if the beatmap in the database is too old.
|
||||||
|
package beatmapget
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.zxq.co/ripple/rippleapi/common"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"gopkg.in/thehowl/go-osuapi.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Expire is the duration after which a beatmap expires.
|
||||||
|
const Expire = time.Hour * 12
|
||||||
|
|
||||||
|
// DB is the database.
|
||||||
|
var DB *sqlx.DB
|
||||||
|
|
||||||
|
// Client is the osu! api client to use
|
||||||
|
var Client *osuapi.Client
|
||||||
|
|
||||||
|
// BeatmapDefiningQuality is the defining quality of the beatmap to be updated,
|
||||||
|
// which is to say either the ID, the set ID or the md5 hash.
|
||||||
|
type BeatmapDefiningQuality struct {
|
||||||
|
ID int
|
||||||
|
MD5 string
|
||||||
|
frozen bool
|
||||||
|
ranked int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BeatmapDefiningQuality) String() string {
|
||||||
|
if b.MD5 != "" {
|
||||||
|
return "#/" + b.MD5
|
||||||
|
}
|
||||||
|
if b.ID != 0 {
|
||||||
|
return "b/" + strconv.Itoa(b.ID)
|
||||||
|
}
|
||||||
|
return "n/a"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BeatmapDefiningQuality) isSomethingSet() error {
|
||||||
|
if b.ID == 0 && b.MD5 == "" {
|
||||||
|
return errors.New("beatmapget: at least one field in BeatmapDefiningQuality must be set")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BeatmapDefiningQuality) whereAndParams() (where string, params []interface{}) {
|
||||||
|
var wheres []string
|
||||||
|
if b.ID != 0 {
|
||||||
|
wheres = append(wheres, "beatmap_id = ?")
|
||||||
|
params = append(params, b.ID)
|
||||||
|
}
|
||||||
|
if b.MD5 != "" {
|
||||||
|
wheres = append(wheres, "beatmap_md5 = ?")
|
||||||
|
params = append(params, b.MD5)
|
||||||
|
}
|
||||||
|
where = strings.Join(wheres, " AND ")
|
||||||
|
if where == "" {
|
||||||
|
where = "1"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateIfRequired updates the beatmap in the database if an update is required.
|
||||||
|
func UpdateIfRequired(b BeatmapDefiningQuality) error {
|
||||||
|
required, err := UpdateRequired(&b)
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !required {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return Update(b, err != sql.ErrNoRows)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRequired checks an update is required. If error is sql.ErrNoRows,
|
||||||
|
// it means that the beatmap should be updated, and that there was not the
|
||||||
|
// beatmap in the database previously.
|
||||||
|
func UpdateRequired(b *BeatmapDefiningQuality) (bool, error) {
|
||||||
|
if err := b.isSomethingSet(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
where, params := b.whereAndParams()
|
||||||
|
var data struct {
|
||||||
|
Difficulties [3]float64
|
||||||
|
Ranked int
|
||||||
|
Frozen bool
|
||||||
|
LatestUpdate common.UnixTimestamp
|
||||||
|
}
|
||||||
|
err := DB.QueryRow("SELECT difficulty_taiko, difficulty_ctb, difficulty_mania, ranked,"+
|
||||||
|
"ranked_status_freezed, latest_update FROM beatmaps WHERE "+
|
||||||
|
where+" LIMIT 1", params...).
|
||||||
|
Scan(
|
||||||
|
&data.Difficulties[0], &data.Difficulties[1], &data.Difficulties[2],
|
||||||
|
&data.Ranked, &data.Frozen, &data.LatestUpdate,
|
||||||
|
)
|
||||||
|
b.frozen = data.Frozen
|
||||||
|
if b.frozen {
|
||||||
|
b.ranked = data.Ranked
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if data.Difficulties[0] == 0 && data.Difficulties[1] == 0 && data.Difficulties[2] == 0 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
expire := Expire
|
||||||
|
if data.Ranked == 2 {
|
||||||
|
expire *= 6
|
||||||
|
}
|
||||||
|
|
||||||
|
if expire != 0 && time.Now().After(time.Time(data.LatestUpdate).Add(expire)) &&
|
||||||
|
!data.Frozen {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates a beatmap.
|
||||||
|
func Update(b BeatmapDefiningQuality, beatmapInDB bool) error {
|
||||||
|
var data [4]osuapi.Beatmap
|
||||||
|
for i := 0; i <= 3; i++ {
|
||||||
|
mode := osuapi.Mode(i)
|
||||||
|
beatmaps, err := Client.GetBeatmaps(osuapi.GetBeatmapsOpts{
|
||||||
|
BeatmapID: b.ID,
|
||||||
|
BeatmapHash: b.MD5,
|
||||||
|
Mode: &mode,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(beatmaps) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data[i] = beatmaps[0]
|
||||||
|
}
|
||||||
|
var main *osuapi.Beatmap
|
||||||
|
for _, el := range data {
|
||||||
|
if el.FileMD5 != "" {
|
||||||
|
main = &el
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if main == nil {
|
||||||
|
return fmt.Errorf("beatmapget: beatmap %s not found", b.String())
|
||||||
|
}
|
||||||
|
if beatmapInDB {
|
||||||
|
w, p := b.whereAndParams()
|
||||||
|
DB.MustExec("DELETE FROM beatmaps WHERE "+w, p...)
|
||||||
|
}
|
||||||
|
if b.frozen {
|
||||||
|
main.Approved = osuapi.ApprovedStatus(b.ranked)
|
||||||
|
}
|
||||||
|
songName := fmt.Sprintf("%s - %s [%s]", main.Artist, main.Title, main.DiffName)
|
||||||
|
_, err := DB.Exec(`INSERT INTO
|
||||||
|
beatmaps (
|
||||||
|
beatmap_id, beatmapset_id, beatmap_md5,
|
||||||
|
song_name, ar, od, difficulty_std, difficulty_taiko,
|
||||||
|
difficulty_ctb, difficulty_mania, max_combo, hit_length,
|
||||||
|
bpm, ranked, latest_update, ranked_status_freezed
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`,
|
||||||
|
main.BeatmapID, main.BeatmapSetID, main.FileMD5,
|
||||||
|
songName, main.ApproachRate, main.OverallDifficulty, data[0].DifficultyRating, data[1].DifficultyRating,
|
||||||
|
data[2].DifficultyRating, data[3].DifficultyRating, main.MaxCombo, main.HitLength,
|
||||||
|
int(main.BPM), main.Approved, time.Now().Unix(), b.frozen,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
osuapi.RateLimit(200)
|
||||||
|
}
|
63
beatmapget/fullset.go
Normal file
63
beatmapget/fullset.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package beatmapget
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.zxq.co/ripple/rippleapi/common"
|
||||||
|
"gopkg.in/thehowl/go-osuapi.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set checks if an update is required for all beatmaps in a set.
|
||||||
|
func Set(s int) error {
|
||||||
|
var (
|
||||||
|
lastUpdated common.UnixTimestamp
|
||||||
|
ranked int
|
||||||
|
)
|
||||||
|
err := DB.QueryRow("SELECT last_updated, ranked FROM beatmaps WHERE beatmapset_id = ? LIMIT 1", s).
|
||||||
|
Scan(&lastUpdated, &ranked)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return set(s, lastUpdated, ranked)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Beatmap check if an update is required for all beatmaps in the set
|
||||||
|
// containing this beatmap.
|
||||||
|
func Beatmap(b int) error {
|
||||||
|
var (
|
||||||
|
setID int
|
||||||
|
lastUpdated common.UnixTimestamp
|
||||||
|
ranked int
|
||||||
|
)
|
||||||
|
err := DB.QueryRow("SELECT beatmapset_id, last_updated, ranked FROM beatmaps WHERE beatmap_id = ? LIMIT 1", b).
|
||||||
|
Scan(&setID, &lastUpdated, &ranked)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return set(setID, lastUpdated, ranked)
|
||||||
|
}
|
||||||
|
|
||||||
|
func set(s int, updated common.UnixTimestamp, ranked int) error {
|
||||||
|
expire := Expire
|
||||||
|
if ranked == 2 {
|
||||||
|
expire *= 6
|
||||||
|
}
|
||||||
|
if time.Now().Before(time.Time(updated).Add(expire)) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
beatmaps, err := Client.GetBeatmaps(osuapi.GetBeatmapsOpts{
|
||||||
|
BeatmapSetID: s,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, beatmap := range beatmaps {
|
||||||
|
err := UpdateIfRequired(BeatmapDefiningQuality{
|
||||||
|
ID: beatmap.BeatmapID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ type Conf struct {
|
||||||
HanayoKey string
|
HanayoKey string
|
||||||
BeatmapRequestsPerUser int
|
BeatmapRequestsPerUser int
|
||||||
RankQueueSize int
|
RankQueueSize int
|
||||||
|
OsuAPIKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
var cachedConf *Conf
|
var cachedConf *Conf
|
||||||
|
|
6
main.go
6
main.go
|
@ -7,11 +7,13 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"git.zxq.co/ripple/rippleapi/app"
|
"git.zxq.co/ripple/rippleapi/app"
|
||||||
|
"git.zxq.co/ripple/rippleapi/beatmapget"
|
||||||
"git.zxq.co/ripple/rippleapi/common"
|
"git.zxq.co/ripple/rippleapi/common"
|
||||||
"git.zxq.co/ripple/schiavolib"
|
"git.zxq.co/ripple/schiavolib"
|
||||||
// Golint pls dont break balls
|
// Golint pls dont break balls
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
"gopkg.in/thehowl/go-osuapi.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version is the git hash of the application. Do not edit. This is
|
// Version is the git hash of the application. Do not edit. This is
|
||||||
|
@ -54,6 +56,10 @@ func main() {
|
||||||
schiavo.Bunker.Send(err.Error())
|
schiavo.Bunker.Send(err.Error())
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beatmapget.Client = osuapi.NewClient(conf.OsuAPIKey)
|
||||||
|
beatmapget.DB = db
|
||||||
|
|
||||||
engine := app.Start(conf, db)
|
engine := app.Start(conf, db)
|
||||||
|
|
||||||
startuato(engine)
|
startuato(engine)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user