add bgeatmapget

This commit is contained in:
Howl 2016-09-07 01:51:23 +02:00
parent dd00c39075
commit b81dffcecc
4 changed files with 256 additions and 0 deletions

186
beatmapget/beatmapget.go Normal file
View 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
View 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
}

View File

@ -21,6 +21,7 @@ type Conf struct {
HanayoKey string
BeatmapRequestsPerUser int
RankQueueSize int
OsuAPIKey string
}
var cachedConf *Conf

View File

@ -7,11 +7,13 @@ import (
"syscall"
"git.zxq.co/ripple/rippleapi/app"
"git.zxq.co/ripple/rippleapi/beatmapget"
"git.zxq.co/ripple/rippleapi/common"
"git.zxq.co/ripple/schiavolib"
// Golint pls dont break balls
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
"gopkg.in/thehowl/go-osuapi.v1"
)
// Version is the git hash of the application. Do not edit. This is
@ -54,6 +56,10 @@ func main() {
schiavo.Bunker.Send(err.Error())
log.Fatalln(err)
}
beatmapget.Client = osuapi.NewClient(conf.OsuAPIKey)
beatmapget.DB = db
engine := app.Start(conf, db)
startuato(engine)