This repository has been archived by the owner on Nov 20, 2020. It is now read-only.
forked from osuthailand/api
/
beatmap_requests.go
159 lines (144 loc) · 4.27 KB
/
beatmap_requests.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package v1
import (
"database/sql"
"encoding/json"
"strconv"
"time"
"github.com/osuthailand/api/common"
"github.com/osuthailand/api/limit"
)
type rankRequestsStatusResponse struct {
common.ResponseBase
QueueSize int `json:"queue_size"`
MaxPerUser int `json:"max_per_user"`
Submitted int `json:"submitted"`
SubmittedByUser *int `json:"submitted_by_user,omitempty"`
CanSubmit *bool `json:"can_submit,omitempty"`
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
// 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 {
r.SubmittedByUser = new(int)
}
isFirst := true
for rows.Next() {
var (
user int
timestamp common.UnixTimestamp
)
err := rows.Scan(&user, ×tamp)
if err != nil {
md.Err(err)
continue
}
// if the user submitted this rank request, increase the number of
// rank requests submitted by this user
if user == md.ID() && r.SubmittedByUser != nil {
(*r.SubmittedByUser)++
}
// also, if this is the first result, it means it will be the next to
// expire.
if isFirst {
x := time.Time(timestamp)
r.NextExpiration = &x
isFirst = false
}
r.Submitted++
}
r.QueueSize = c.RankQueueSize
r.MaxPerUser = c.BeatmapRequestsPerUser
if hasConfid {
x := r.Submitted < r.QueueSize && *r.SubmittedByUser < r.MaxPerUser
r.CanSubmit = &x
}
r.Code = 200
return r
}
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.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.")
}
// 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.")
}
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.")
}
switch err {
case nil:
// move on
case sql.ErrNoRows:
data, _ := json.Marshal(d)
md.R.Publish("lets:beatmap_updates", string(data))
default:
md.Err(err)
return Err500
}
// type and value of beatmap rank request
t := "b"
v := d.ID
if d.SetID != 0 {
t = "s"
v = d.SetID
}
err = md.DB.QueryRow("SELECT 1 FROM rank_requests WHERE bid = ? AND type = ? AND time > ?",
v, t, time.Now().Add(-time.Hour*24).Unix()).Scan(new(int))
// error handling
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.
return BeatmapRankRequestsStatusGET(md)
default:
md.Err(err)
return Err500
}
_, err = md.DB.Exec(
"INSERT INTO rank_requests (userid, bid, type, time, blacklisted) VALUES (?, ?, ?, ?, 0)",
md.ID(), v, t, time.Now().Unix())
if err != nil {
md.Err(err)
return Err500
}
return BeatmapRankRequestsStatusGET(md)
}