2016-09-06 18:11:46 +00:00
package v1
import (
2016-09-20 16:14:02 +00:00
"database/sql"
2016-09-06 18:11:46 +00:00
"strconv"
"time"
"git.zxq.co/ripple/rippleapi/common"
2016-09-20 16:14:02 +00:00
"git.zxq.co/ripple/rippleapi/limit"
2016-09-06 18:11:46 +00:00
)
type rankRequestsStatusResponse struct {
common . ResponseBase
QueueSize int ` json:"queue_size" `
2016-09-20 16:14:02 +00:00
MaxPerUser int ` json:"max_per_user" `
2016-09-06 18:11:46 +00:00
Submitted int ` json:"submitted" `
2016-09-20 16:14:02 +00:00
SubmittedByUser * int ` json:"submitted_by_user,omitempty" `
CanSubmit * bool ` json:"can_submit,omitempty" `
2016-09-06 18:11:46 +00:00
NextExpiration * time . Time ` json:"next_expiration" `
}
// BeatmapRankRequestsStatusGET gets the current status for beatmap ranking requests.
func BeatmapRankRequestsStatusGET ( md common . MethodData ) common . CodeMessager {
c := common . GetConf ( )
rows , err := md . DB . Query ( "SELECT userid, time FROM rank_requests WHERE time > ? ORDER BY id ASC LIMIT " + strconv . Itoa ( c . RankQueueSize ) , time . Now ( ) . Add ( - time . Hour * 24 ) . Unix ( ) )
if err != nil {
md . Err ( err )
return Err500
}
var r rankRequestsStatusResponse
2016-09-24 17:45:07 +00:00
// if it's not auth-free access and we have got ReadConfidential, we can
// know if this user can submit beatmaps or not.
hasConfid := md . ID ( ) != 0 && md . User . TokenPrivileges & common . PrivilegeReadConfidential > 0
if hasConfid {
2016-09-20 16:14:02 +00:00
r . SubmittedByUser = new ( int )
}
2016-09-06 18:11:46 +00:00
isFirst := true
for rows . Next ( ) {
var (
user int
timestamp common . UnixTimestamp
)
err := rows . Scan ( & user , & timestamp )
if err != nil {
md . Err ( err )
continue
}
2016-09-20 16:14:02 +00:00
// if the user submitted this rank request, increase the number of
// rank requests submitted by this user
2016-09-24 17:45:07 +00:00
if user == md . ID ( ) && r . SubmittedByUser != nil {
2016-09-20 16:14:02 +00:00
( * r . SubmittedByUser ) ++
2016-09-06 18:11:46 +00:00
}
2016-09-20 16:14:02 +00:00
// also, if this is the first result, it means it will be the next to
// expire.
2016-09-06 18:11:46 +00:00
if isFirst {
x := time . Time ( timestamp )
r . NextExpiration = & x
isFirst = false
}
r . Submitted ++
}
r . QueueSize = c . RankQueueSize
2016-09-20 16:14:02 +00:00
r . MaxPerUser = c . BeatmapRequestsPerUser
2016-09-24 17:45:07 +00:00
if hasConfid {
2016-09-20 16:14:02 +00:00
x := r . Submitted < r . QueueSize && * r . SubmittedByUser < r . MaxPerUser
r . CanSubmit = & x
}
2016-09-06 18:11:46 +00:00
r . Code = 200
return r
}
2016-09-20 16:14:02 +00:00
type submitRequestData struct {
ID int ` json:"id" `
SetID int ` json:"set_id" `
}
// BeatmapRankRequestsSubmitPOST submits a new beatmap for ranking approval.
func BeatmapRankRequestsSubmitPOST ( md common . MethodData ) common . CodeMessager {
var d submitRequestData
err := md . RequestData . Unmarshal ( & d )
if err != nil {
return ErrBadJSON
}
// check json data is present
if d . ID == 0 && d . SetID == 0 {
return ErrMissingField ( "id|set_id" )
}
// you've been rate limited
if ! limit . NonBlockingRequest ( "rankrequest:u:" + strconv . Itoa ( md . ID ( ) ) , 5 ) {
return common . SimpleResponse ( 429 , "You may only try to request 5 beatmaps per minute." )
}
if ! limit . NonBlockingRequest ( "rankrequest:ip:" + md . C . ClientIP ( ) , 8 ) {
return common . SimpleResponse ( 429 , "You may only try to request 8 beatmaps per minute from the same IP." )
}
// find out from BeatmapRankRequestsStatusGET if we can submit beatmaps.
statusRaw := BeatmapRankRequestsStatusGET ( md )
status , ok := statusRaw . ( rankRequestsStatusResponse )
if ! ok {
// if it's not a rankRequestsStatusResponse, it means it's an error
return statusRaw
}
if ! * status . CanSubmit {
return common . SimpleResponse ( 403 , "It's not possible to do a rank request at this time." )
}
2016-11-19 18:53:55 +00:00
w := common .
Where ( "beatmap_id = ?" , strconv . Itoa ( d . ID ) ) . Or ( ) .
Where ( "beatmapset_id = ?" , strconv . Itoa ( d . SetID ) )
var ranked int
err = md . DB . QueryRow ( "SELECT ranked FROM beatmaps " + w . Clause + " LIMIT 1" , w . Params ... ) . Scan ( & ranked )
if ranked >= 2 {
return common . SimpleResponse ( 406 , "That beatmap is already ranked." )
2016-09-20 16:14:02 +00:00
}
2016-11-19 18:53:55 +00:00
switch err {
case nil :
// move on
case sql . ErrNoRows :
if d . SetID != 0 {
2016-11-20 10:40:01 +00:00
md . R . Publish ( "lets:beatmap_updates:sets" , strconv . Itoa ( d . SetID ) )
2016-11-19 18:53:55 +00:00
} else {
2016-11-20 10:40:01 +00:00
md . R . Publish ( "lets:beatmap_updates:single" , strconv . Itoa ( d . ID ) )
2016-11-19 18:53:55 +00:00
}
default :
2016-09-20 16:14:02 +00:00
md . Err ( err )
return Err500
}
2016-11-19 18:53:55 +00:00
// type and value of beatmap rank request
t := "b"
v := d . ID
if d . SetID != 0 {
t = "s"
v = d . SetID
}
2016-09-20 16:14:02 +00:00
err = md . DB . QueryRow ( "SELECT 1 FROM rank_requests WHERE bid = ? AND type = ? AND time > ?" ,
2016-11-19 18:53:55 +00:00
v , t , time . Now ( ) . Add ( - time . Hour * 24 ) . Unix ( ) ) . Scan ( new ( int ) )
// error handling
2016-09-20 16:14:02 +00:00
switch err {
case sql . ErrNoRows :
break
case nil :
// we're returning a success because if the request was already sent in the past 24
// hours, it's as if the user submitted it.
2016-11-19 19:46:52 +00:00
return BeatmapRankRequestsStatusGET ( md )
2016-09-20 16:14:02 +00:00
default :
md . Err ( err )
return Err500
}
_ , err = md . DB . Exec (
"INSERT INTO rank_requests (userid, bid, type, time, blacklisted) VALUES (?, ?, ?, ?, 0)" ,
2016-11-19 18:53:55 +00:00
md . ID ( ) , v , t , time . Now ( ) . Unix ( ) )
2016-09-20 16:14:02 +00:00
if err != nil {
md . Err ( err )
return Err500
}
2016-11-19 19:46:52 +00:00
return BeatmapRankRequestsStatusGET ( md )
2016-09-20 16:14:02 +00:00
}