From d6f67915c5cfe756be602c5f1d1e9a79b563734e Mon Sep 17 00:00:00 2001 From: Howl Date: Sun, 15 May 2016 13:57:04 +0200 Subject: [PATCH] Start implementing peppyapi; implement /api/get_user --- app/method.go | 7 +++--- app/peppy/common.go | 52 +++++++++++++++++++++++++++++++++++++++++++++ app/peppy/user.go | 51 ++++++++++++++++++++++++++++++++++++++++++++ app/peppy_method.go | 16 ++++++++++++++ app/start.go | 51 +++++++++++++++++++++++++------------------- 5 files changed, 151 insertions(+), 26 deletions(-) create mode 100644 app/peppy/common.go create mode 100644 app/peppy_method.go diff --git a/app/method.go b/app/method.go index c8b48ca..aab73d8 100644 --- a/app/method.go +++ b/app/method.go @@ -1,7 +1,6 @@ package app import ( - "database/sql" "encoding/json" "io/ioutil" @@ -10,13 +9,13 @@ import ( ) // Method wraps an API method to a HandlerFunc. -func Method(f func(md common.MethodData) common.CodeMessager, db *sql.DB, privilegesNeeded ...int) gin.HandlerFunc { +func Method(f func(md common.MethodData) common.CodeMessager, privilegesNeeded ...int) gin.HandlerFunc { return func(c *gin.Context) { - initialCaretaker(c, f, db, privilegesNeeded...) + initialCaretaker(c, f, privilegesNeeded...) } } -func initialCaretaker(c *gin.Context, f func(md common.MethodData) common.CodeMessager, db *sql.DB, privilegesNeeded ...int) { +func initialCaretaker(c *gin.Context, f func(md common.MethodData) common.CodeMessager, privilegesNeeded ...int) { data, err := ioutil.ReadAll(c.Request.Body) if err != nil { c.Error(err) diff --git a/app/peppy/common.go b/app/peppy/common.go new file mode 100644 index 0000000..080bcaf --- /dev/null +++ b/app/peppy/common.go @@ -0,0 +1,52 @@ +package peppy + +import ( + "database/sql" + "strconv" + + "github.com/gin-gonic/gin" +) + +func genmode(m string) string { + switch m { + case "1": + m = "taiko" + case "2": + m = "ctb" + case "3": + m = "mania" + default: + m = "std" + } + return m +} + +func genUser(c *gin.Context, db *sql.DB) (string, string) { + var whereClause string + var p string + + // used in second case of switch + _, err := strconv.Atoi(c.Query("u")) + + switch { + // We know for sure that it's an username. + case c.Query("type") == "string": + whereClause = "WHERE users.username = ?" + p = c.Query("u") + // It could be an user ID, so we look for an user with that username first. + case err == nil: + err = db.QueryRow("SELECT id FROM users WHERE username = ? LIMIT 1", c.Query("u")).Scan(&p) + // If there is an error, that means u is an userID. + // If there is none, p will automatically have become the user id retrieved from the database + // in the last query. + if err == sql.ErrNoRows { + p = c.Query("u") + } + whereClause = "WHERE users.id = ?" + // u contains letters, so it's an username. + default: + p = c.Query("u") + whereClause = "WHERE users.username = ?" + } + return whereClause, p +} diff --git a/app/peppy/user.go b/app/peppy/user.go index 15032f9..b3f159e 100644 --- a/app/peppy/user.go +++ b/app/peppy/user.go @@ -1,2 +1,53 @@ // Package peppy implements the osu! API as defined on the osu-api repository wiki (https://github.com/ppy/osu-api/wiki). package peppy + +import ( + "database/sql" + "fmt" + + "github.com/gin-gonic/gin" + "github.com/thehowl/go-osuapi" +) + +// GetUser retrieves general user information. +func GetUser(c *gin.Context, db *sql.DB) { + if c.Query("u") == "" { + c.JSON(200, []struct{}{}) + return + } + var user osuapi.User + whereClause, p := genUser(c, db) + + mode := genmode(c.Query("m")) + + fmt.Println(whereClause, p) + + var display bool + err := db.QueryRow(fmt.Sprintf( + `SELECT + users.id, users.username, + users_stats.playcount_%s, users_stats.ranked_score_%s, users_stats.total_score_%s, + leaderboard_%s.position, users_stats.pp_%s, users_stats.avg_accuracy_%s, + users_stats.country, users_stats.show_country + FROM users + LEFT JOIN users_stats ON users_stats.id = users.id + LEFT JOIN leaderboard_%s ON leaderboard_%s.user = users.id + %s + LIMIT 1`, + mode, mode, mode, mode, mode, mode, mode, mode, whereClause, + ), p).Scan( + &user.UserID, &user.Username, + &user.Playcount, &user.RankedScore, &user.TotalScore, + &user.Rank, &user.PP, &user.Accuracy, + &user.Country, &display, + ) + if err != nil { + c.JSON(200, []struct{}{}) + return + } + if !display { + user.Country = "XX" + } + + c.JSON(200, []osuapi.User{user}) +} diff --git a/app/peppy_method.go b/app/peppy_method.go new file mode 100644 index 0000000..608ef84 --- /dev/null +++ b/app/peppy_method.go @@ -0,0 +1,16 @@ +package app + +import ( + "database/sql" + + "github.com/gin-gonic/gin" +) + +// PeppyMethod generates a method for the peppyapi +func PeppyMethod(a func(c *gin.Context, db *sql.DB)) gin.HandlerFunc { + return func(c *gin.Context) { + // I have no idea how, but I manged to accidentally string the first 4 + // letters of the alphabet into a single function call. + a(c, db) + } +} diff --git a/app/start.go b/app/start.go index ec50afa..ddc5474 100644 --- a/app/start.go +++ b/app/start.go @@ -4,14 +4,18 @@ import ( "database/sql" "git.zxq.co/ripple/rippleapi/app/internals" + "git.zxq.co/ripple/rippleapi/app/peppy" "git.zxq.co/ripple/rippleapi/app/v1" "git.zxq.co/ripple/rippleapi/common" "github.com/gin-gonic/contrib/gzip" "github.com/gin-gonic/gin" ) +var db *sql.DB + // Start begins taking HTTP connections. -func Start(conf common.Conf, db *sql.DB) *gin.Engine { +func Start(conf common.Conf, dbO *sql.DB) *gin.Engine { + db = dbO r := gin.Default() r.Use(gzip.Gzip(gzip.DefaultCompression), ErrorHandler()) @@ -19,47 +23,50 @@ func Start(conf common.Conf, db *sql.DB) *gin.Engine { { gv1 := api.Group("/v1") { - gv1.POST("/tokens/new", Method(v1.TokenNewPOST, db)) + gv1.POST("/tokens/new", Method(v1.TokenNewPOST)) // Auth-free API endpoints - gv1.GET("/ping", Method(v1.PingGET, db)) - gv1.GET("/surprise_me", Method(v1.SurpriseMeGET, db)) - gv1.GET("/privileges", Method(v1.PrivilegesGET, db)) + gv1.GET("/ping", Method(v1.PingGET)) + gv1.GET("/surprise_me", Method(v1.SurpriseMeGET)) + gv1.GET("/privileges", Method(v1.PrivilegesGET)) // Read privilege required - gv1.GET("/users", Method(v1.UsersGET, db, common.PrivilegeRead)) - gv1.GET("/users/self", Method(v1.UserSelfGET, db, common.PrivilegeRead)) - gv1.GET("/users/whatid", Method(v1.UserWhatsTheIDGET, db, common.PrivilegeRead)) - gv1.GET("/users/full", Method(v1.UserFullGET, db, common.PrivilegeRead)) - gv1.GET("/users/userpage", Method(v1.UserUserpageGET, db, common.PrivilegeRead)) - gv1.GET("/users/lookup", Method(v1.UserLookupGET, db, common.PrivilegeRead)) - gv1.GET("/badges", Method(v1.BadgesGET, db, common.PrivilegeRead)) + gv1.GET("/users", Method(v1.UsersGET, common.PrivilegeRead)) + gv1.GET("/users/self", Method(v1.UserSelfGET, common.PrivilegeRead)) + gv1.GET("/users/whatid", Method(v1.UserWhatsTheIDGET, common.PrivilegeRead)) + gv1.GET("/users/full", Method(v1.UserFullGET, common.PrivilegeRead)) + gv1.GET("/users/userpage", Method(v1.UserUserpageGET, common.PrivilegeRead)) + gv1.GET("/users/lookup", Method(v1.UserLookupGET, common.PrivilegeRead)) + gv1.GET("/badges", Method(v1.BadgesGET, common.PrivilegeRead)) // ReadConfidential privilege required - gv1.GET("/friends", Method(v1.FriendsGET, db, common.PrivilegeReadConfidential)) - gv1.GET("/friends/with", Method(v1.FriendsWithGET, db, common.PrivilegeReadConfidential)) + gv1.GET("/friends", Method(v1.FriendsGET, common.PrivilegeReadConfidential)) + gv1.GET("/friends/with", Method(v1.FriendsWithGET, common.PrivilegeReadConfidential)) // Write privilege required - gv1.GET("/friends/add", Method(v1.FriendsAddGET, db, common.PrivilegeWrite)) - gv1.GET("/friends/del", Method(v1.FriendsDelGET, db, common.PrivilegeWrite)) + gv1.GET("/friends/add", Method(v1.FriendsAddGET, common.PrivilegeWrite)) + gv1.GET("/friends/del", Method(v1.FriendsDelGET, common.PrivilegeWrite)) // Admin: beatmap - gv1.POST("/beatmaps/set_status", Method(v1.BeatmapSetStatusPOST, db, common.PrivilegeBeatmap)) + gv1.POST("/beatmaps/set_status", Method(v1.BeatmapSetStatusPOST, common.PrivilegeBeatmap)) // Admin: user managing - gv1.POST("/users/manage/set_allowed", Method(v1.UserManageSetAllowedPOST, db, common.PrivilegeManageUser)) + gv1.POST("/users/manage/set_allowed", Method(v1.UserManageSetAllowedPOST, common.PrivilegeManageUser)) // M E T A // E T "wow thats so meta" // T E -- the one who said "wow thats so meta" // A T E M - gv1.GET("/meta/restart", Method(v1.MetaRestartGET, db, common.PrivilegeAPIMeta)) - gv1.GET("/meta/kill", Method(v1.MetaKillGET, db, common.PrivilegeAPIMeta)) - gv1.GET("/meta/up_since", Method(v1.MetaUpSinceGET, db, common.PrivilegeAPIMeta)) - gv1.GET("/meta/update", Method(v1.MetaUpdateGET, db, common.PrivilegeAPIMeta)) + gv1.GET("/meta/restart", Method(v1.MetaRestartGET, common.PrivilegeAPIMeta)) + gv1.GET("/meta/kill", Method(v1.MetaKillGET, common.PrivilegeAPIMeta)) + gv1.GET("/meta/up_since", Method(v1.MetaUpSinceGET, common.PrivilegeAPIMeta)) + gv1.GET("/meta/update", Method(v1.MetaUpdateGET, common.PrivilegeAPIMeta)) } api.GET("/status", internals.Status) + + // peppyapi + api.GET("/get_user", PeppyMethod(peppy.GetUser)) } r.NoRoute(v1.Handle404)