osuapi

package module
v0.0.0-...-b294556 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 19, 2018 License: MIT Imports: 10 Imported by: 45

README

go-osuapi docs Build Status Go Report Card

go-osuapi is a Go package to retrieve data from the osu! API.

Getting started

Everything is (more or less) well-documented at godoc - the methods that interest you most are probably those under Client. Also, client_test.go contains loads of examples on how you can use the package. If you still want to have an example to simply copypaste and then get straight to coding, well, there you go!

package main

import (
	"fmt"
	osuapi "github.com/thehowl/go-osuapi"
)

func main() {
	c := osuapi.NewClient("Your API key https://osu.ppy.sh/p/api")
	beatmaps, err := c.GetBeatmaps(osuapi.GetBeatmapsOpts{
		BeatmapSetID: 332532,
	})
	if err != nil {
		fmt.Printf("An error occurred: %v\n", err)
		return
	}
	for _, beatmap := range beatmaps {
		fmt.Printf("%s - %s [%s] https://osu.ppy.sh/b/%d\n", beatmap.Artist, beatmap.Title, beatmap.DiffName, beatmap.BeatmapID)
	}
}

Please note that if you actually want to use this, you should consider vendoring this using the dep tool, so that your code keeps working regardless of any change we might do in this repository.

I want more than that to explore how it works!

I've made whosu for that purpose. Check it out.

Contributing

Contributions are welcome! Here's what you need to know:

  • Always go fmt your code.
  • If you're writing a big and useful feature, make sure to appropriately write tests!

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrNoSuchUser = errors.New("osuapi: no such user could be found")

ErrNoSuchUser is returned when the requested user could not be found.

Functions

func RateLimit

func RateLimit(maxRequests int)

RateLimit allows you to set the maximum number of requests to do in a minute to the osu! API.

Please note that this function is NOT thread safe. It should be executed only at the start of your program, and never after it.

The reason for this is that creating a Mutex for a channel is just absolutely ridiculous.

Types

type ApprovedStatus

type ApprovedStatus int

ApprovedStatus - also known as ranked status - is the status of a beatmap. Yeah, no shit, I know. It tells whether the beatmap is ranked, qualified, graveyarded or other memes.

const (
	StatusGraveyard ApprovedStatus = iota - 2
	StatusWIP
	StatusPending
	StatusRanked
	StatusApproved
	StatusQualified
	StatusLoved
)

Approved statuses.

func (ApprovedStatus) String

func (a ApprovedStatus) String() string

type Beatmap

type Beatmap struct {
	BeatmapSetID      int            `json:"beatmapset_id,string"`
	BeatmapID         int            `json:"beatmap_id,string"`
	Approved          ApprovedStatus `json:"approved,string"`
	TotalLength       int            `json:"total_length,string"`
	HitLength         int            `json:"hit_length,string"`
	DiffName          string         `json:"version"`
	FileMD5           string         `json:"file_md5"`
	CircleSize        float64        `json:"diff_size,string"`
	OverallDifficulty float64        `json:"diff_overall,string"`
	ApproachRate      float64        `json:"diff_approach,string"`
	HPDrain           float64        `json:"diff_drain,string"`
	Mode              Mode           `json:"mode,string"`
	ApprovedDate      MySQLDate      `json:"approved_date"`
	LastUpdate        MySQLDate      `json:"last_update"`
	Artist            string         `json:"artist"`
	Title             string         `json:"title"`
	Creator           string         `json:"creator"`
	BPM               float64        `json:"bpm,string"`
	Source            string         `json:"source"`
	Tags              string         `json:"tags"`
	Genre             Genre          `json:"genre_id,string"`
	Language          Language       `json:"language_id,string"`
	FavouriteCount    int            `json:"favourite_count,string"`
	Playcount         int            `json:"playcount,string"`
	Passcount         int            `json:"passcount,string"`
	MaxCombo          int            `json:"max_combo,string"`
	DifficultyRating  float64        `json:"difficultyrating,string"`
}

Beatmap is an osu! beatmap.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is an osu! API client that is able to make API requests.

func NewClient

func NewClient(key string) *Client

NewClient generates a new Client based on an API key.

func (Client) GetBeatmaps

func (c Client) GetBeatmaps(opts GetBeatmapsOpts) ([]Beatmap, error)

GetBeatmaps makes a get_beatmaps request to the osu! API.

func (Client) GetMatch

func (c Client) GetMatch(matchID int) (*Match, error)

GetMatch makes a get_match request to the osu! API.

func (Client) GetReplay

func (c Client) GetReplay(opts GetReplayOpts) (io.Reader, error)

GetReplay makes a get_replay request to the osu! API. Returns a reader from which the replay can be retrieved.

func (Client) GetScores

func (c Client) GetScores(opts GetScoresOpts) ([]GSScore, error)

GetScores makes a get_scores request to the osu! API.

func (Client) GetUser

func (c Client) GetUser(opts GetUserOpts) (*User, error)

GetUser makes a get_user request to the osu! API.

func (Client) GetUserBest

func (c Client) GetUserBest(opts GetUserScoresOpts) ([]GUSScore, error)

GetUserBest makes a get_user_best request to the osu! API.

func (Client) GetUserRecent

func (c Client) GetUserRecent(opts GetUserScoresOpts) ([]GUSScore, error)

GetUserRecent makes a get_user_recent request to the osu! API.

func (Client) Test

func (c Client) Test() error

Test makes sure the API is working (and the API key is valid).

type Event

type Event struct {
	DisplayHTML  string    `json:"display_html"`
	BeatmapID    int       `json:"beatmap_id,string"`
	BeatmapsetID int       `json:"beatmapset_id,string"`
	Date         MySQLDate `json:"date"`
	Epicfactor   int       `json:"epicfactor,string"`
}

Event is a notorious action an user has done recently.

type GSScore

type GSScore struct {
	ScoreID  int64  `json:"score_id,string"`
	Username string `json:"username"`
	Score
}

GSScore is basically Score, with the exception it also has ScoreID. (stands for Get Scores Score)

type GUSScore

type GUSScore struct {
	BeatmapID int `json:"beatmap_id,string"`
	Score
}

GUSScore is a score from get_user_best or get_user_recent. It differs from normal Score by having a BeatmapID field. Stands for Get User Scores Score. Yeah, I suck at choosing names. but that's what programming is all about, after all.

type Genre

type Genre int

Genre is the genre of a beatmap's song.

const (
	GenreAny Genre = iota
	GenreUnspecified
	GenreVideoGame
	GenreAnime
	GenreRock
	GenrePop
	GenreOther
	GenreNovelty
	GenreHipHop Genre = iota + 1 // there's no 8, so we must manually increment it by one
	GenreElectronic
)

Genres

func (Genre) String

func (g Genre) String() string

type GetBeatmapsOpts

type GetBeatmapsOpts struct {
	Since        *time.Time
	BeatmapSetID int
	BeatmapID    int
	// If both UserID and Username are set, UserID will be used.
	UserID   int
	Username string
	// Using a pointer because we MUST provide a way to make this parameter
	// optional, and it should be optional by default. This is because simply
	// doing != 0 won't work, because it wouldn't allow filtering with only
	// osu!std maps, and setting m=0 and not setting it it all makes the
	// difference between only allowing STD beatmaps and allowing beatmaps
	// from all modes.
	// Simply doing &osuapi.ModeOsuMania (or similiar) should do the trick,
	// should you need to use this field.
	// God was this comment long.
	Mode             *Mode
	IncludeConverted bool
	BeatmapHash      string
	Limit            int
}

GetBeatmapsOpts is a struct containing the GET query string parameters to an /api/get_beatmaps request.

type GetReplayOpts

type GetReplayOpts struct {
	UserID    int
	Username  string
	Mode      Mode
	BeatmapID int
}

GetReplayOpts are the options that MUST be used to fetch a replay. ALL of the fields are **REQUIRED**, with an exception for UserID/Username, of which only one is required.

type GetScoresOpts

type GetScoresOpts struct {
	BeatmapID int
	// As usual, if both UserID and Username are set, UserID will override Username.
	UserID   int
	Username string
	Mode     Mode
	Mods     *Mods // Pointer because must have the possibility to be 0 (nomod) but also nil (whatever is fine)
	Limit    int
}

GetScoresOpts is a struct containing the GET query string parameters to an /api/get_scores request.

type GetUserOpts

type GetUserOpts struct {
	// If both UserID and Username are set, UserID will be used.
	UserID    int
	Username  string
	Mode      Mode
	EventDays int
}

GetUserOpts is a struct containing the GET query string parameters to an /api/get_user request.

type GetUserScoresOpts

type GetUserScoresOpts struct {
	// You know it by now. UserID overrides Username.
	UserID   int
	Username string
	Mode     Mode
	Limit    int
}

GetUserScoresOpts are the options that can be passed in GetUserBest or in GetUserRecent; they use the same parameters.

type Language

type Language int

Language is the language of a beatmap's song.

const (
	LanguageAny Language = iota
	LanguageOther
	LanguageEnglish
	LanguageJapanese
	LanguageChinese
	LanguageInstrumental
	LanguageKorean
	LanguageFrench
	LanguageGerman
	LanguageSwedish
	LanguageSpanish
	LanguageItalian
)

Languages

func (Language) String

func (l Language) String() string

type Match

type Match struct {
	Info  MatchInfo   `json:"match"`
	Games []MatchGame `json:"games"`
}

Match is a multiplayer match.

type MatchGame

type MatchGame struct {
	GameID    int        `json:"game_id,string"`
	StartTime MySQLDate  `json:"start_time"`
	EndTime   *MySQLDate `json:"end_time"`
	BeatmapID int        `json:"beatmap_id,string"`
	PlayMode  Mode       `json:"play_mode,string"`
	// Refer to the wiki for information about what the three things below
	// are. I personally think that this information wouldn't be that
	// necessary to most people, and what is written on the wiki could be
	// outdated, thus I deemed useless making an appropriate "enum" (like
	// I did for Genre, Language and that stuff.)
	// You really can say I love writing long comments.
	MatchType   int              `json:"match_type,string"`
	ScoringType int              `json:"scoring_type,string"`
	TeamType    int              `json:"team_type,string"`
	Mods        Mods             `json:"mods,string"`
	Scores      []MatchGameScore `json:"scores"`
}

MatchGame is a single beatmap played in the Match.

type MatchGameScore

type MatchGameScore struct {
	Slot     int   `json:"slot,string"`
	Team     int   `json:"team,string"`
	UserID   int   `json:"user_id,string"`
	Score    int64 `json:"score,string"`
	MaxCombo int   `json:"maxcombo,string"`
	// There should be Rank here, but Rank is not actually used. (always 0)
	Count50   int `json:"count50,string"`
	Count100  int `json:"count100,string"`
	Count300  int `json:"count300,string"`
	CountMiss int `json:"countmiss,string"`
	CountGeki int `json:"countgeki,string"`
	CountKatu int `json:"countkatu,string"`
	// There should also be Perfect here, but that seems to also not be used. (always 0)
	Pass OsuBool `json:"pass"`
}

MatchGameScore is a single score done by an user in a specific Game of a Match. I agree, these descriptions are quite confusing.

type MatchInfo

type MatchInfo struct {
	MatchID   int        `json:"match_id,string"`
	Name      string     `json:"name"`
	StartTime MySQLDate  `json:"start_time"`
	EndTime   *MySQLDate `json:"end_time"`
}

MatchInfo contains useful information about a Match.

type Mode

type Mode int

Mode is an osu! game mode.

const (
	ModeOsu Mode = iota
	ModeTaiko
	ModeCatchTheBeat
	ModeOsuMania
)

osu! game modes IDs.

func (Mode) String

func (m Mode) String() string

type Mods

type Mods int

Mods is a bitwise enum of mods used in a score.

Mods may appear complicated to use for a beginner programmer. Fear not! This is how hard they can get for creation of a mod combination:

myModCombination := osuapi.ModHardRock | osuapi.ModDoubleTime | osuapi.ModHidden | osuapi.ModSpunOut

As for checking that an existing mod comination is enabled:

if modCombination&osuapi.ModHardRock != 0 {
    // HardRock is enabled
}

To learn more about bitwise operators, have a look at it on wikipedia: https://en.wikipedia.org/wiki/Bitwise_operation#Bitwise_operators

const (
	ModNoFail Mods = 1 << iota
	ModEasy
	ModNoVideo
	ModHidden
	ModHardRock
	ModSuddenDeath
	ModDoubleTime
	ModRelax
	ModHalfTime
	ModNightcore
	ModFlashlight
	ModAutoplay
	ModSpunOut
	ModRelax2
	ModPerfect
	ModKey4
	ModKey5
	ModKey6
	ModKey7
	ModKey8
	ModFadeIn
	ModRandom
	ModLastMod
	ModKey9
	ModKey10
	ModKey1
	ModKey3
	ModKey2
	ModFreeModAllowed = ModNoFail | ModEasy | ModHidden | ModHardRock | ModSuddenDeath | ModFlashlight | ModFadeIn | ModRelax | ModRelax2 | ModSpunOut | ModKeyMod
	ModKeyMod         = ModKey4 | ModKey5 | ModKey6 | ModKey7 | ModKey8
)

Mods in the game.

func ParseMods

func ParseMods(mods string) (m Mods)

ParseMods parse a string with mods in the format "HDHRDT"

func (Mods) String

func (m Mods) String() (s string)

type MySQLDate

type MySQLDate time.Time

MySQLDate is a wrapper for time.Time that can get a date from an osu! API JSON response.

func (MySQLDate) GetTime

func (m MySQLDate) GetTime() time.Time

GetTime transforms a MySQLDate into a native time.Time.

func (MySQLDate) MarshalJSON

func (m MySQLDate) MarshalJSON() ([]byte, error)

MarshalJSON converts a MySQLDate into JSON.

func (MySQLDate) String

func (m MySQLDate) String() string

func (*MySQLDate) UnmarshalJSON

func (m *MySQLDate) UnmarshalJSON(data []byte) error

UnmarshalJSON takes some JSON data and does some magic to transform it into a native time.Time.

type OsuBool

type OsuBool bool

OsuBool is just a bool. It's used for unmarshaling of bools in the API that are either `"1"` or `"0"`. thank mr peppy for the memes

You can just use it in `if`s and other memes. Should you need to convert it to a native bool, just do `bool(yourOsuBool)`

func (OsuBool) MarshalJSON

func (o OsuBool) MarshalJSON() ([]byte, error)

MarshalJSON does UnmarshalJSON the other way around.

func (*OsuBool) UnmarshalJSON

func (o *OsuBool) UnmarshalJSON(data []byte) error

UnmarshalJSON converts `"1"` and `1` to true and all other values to false.

type Score

type Score struct {
	Score     int64     `json:"score,string"`
	MaxCombo  int       `json:"maxcombo,string"`
	Count50   int       `json:"count50,string"`
	Count100  int       `json:"count100,string"`
	Count300  int       `json:"count300,string"`
	CountMiss int       `json:"countmiss,string"`
	CountKatu int       `json:"countkatu,string"`
	CountGeki int       `json:"countgeki,string"`
	FullCombo OsuBool   `json:"perfect,string"`
	Mods      Mods      `json:"enabled_mods,string"`
	UserID    int       `json:"user_id,string"`
	Date      MySQLDate `json:"date"`
	Rank      string    `json:"rank"` // Rank = SSH, SS, SH, S, A, B, C, D
	PP        float64   `json:"pp,string"`
}

Score is an osu! score. Used in both get_scores, get_user_best and get_user_recent.

type User

type User struct {
	UserID      int       `json:"user_id,string"`
	Username    string    `json:"username"`
	Date        MySQLDate `json:"join_date"`
	Count300    int       `json:"count300,string"`
	Count100    int       `json:"count100,string"`
	Count50     int       `json:"count50,string"`
	Playcount   int       `json:"playcount,string"`
	RankedScore int64     `json:"ranked_score,string"`
	TotalScore  int64     `json:"total_score,string"`
	Rank        int       `json:"pp_rank,string"`
	Level       float64   `json:"level,string"`
	PP          float64   `json:"pp_raw,string"`
	Accuracy    float64   `json:"accuracy,string"`
	CountSS     int       `json:"count_rank_ss,string"`
	CountSSH    int       `json:"count_rank_ssh,string"`
	CountS      int       `json:"count_rank_s,string"`
	CountSH     int       `json:"count_rank_sh,string"`
	CountA      int       `json:"count_rank_a,string"`
	Country     string    `json:"country"`
	CountryRank int       `json:"pp_country_rank,string"`
	Events      []Event   `json:"events"`
}

User is an osu! user.

func (User) ToGetUserOpts

func (u User) ToGetUserOpts() GetUserOpts

ToGetUserOpts converts an user to a GetUserOpts, so that it can be used with GetUser. Note that this does not work very well. It won't auto-detect the game mode, because the bloody osu! API does not return that in a get_user response. So it will just assume you want the osu! standard data and return that.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL