-
Notifications
You must be signed in to change notification settings - Fork 23
/
bucketpath.go
146 lines (131 loc) · 3.33 KB
/
bucketpath.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
package lib
import (
"strings"
"time"
)
const (
MajorUnknown = "unk"
MajorChannels = "channels"
MajorGuilds = "guilds"
MajorWebhooks = "webhooks"
MajorInvites = "invites"
MajorInteractions = "interactions"
)
func IsSnowflake(str string) bool {
l := len(str)
if l < 17 || l > 20 {
return false
}
for _, d := range str {
if d < '0' || d > '9' {
return false
}
}
return true
}
func IsNumericInput(str string) bool {
for _, d := range str {
if d < '0' || d > '9' {
return false
}
}
return true
}
func GetMetricsPath(route string) string {
route = GetOptimisticBucketPath(route, "")
var path = ""
parts := strings.Split(route, "/")
if strings.HasPrefix(route, "/invite/!") {
return "/invite/!"
}
for _, part := range parts {
if part == "" { continue }
if IsNumericInput(part) {
path += "/!"
} else {
path += "/" + part
}
}
return path
}
func GetOptimisticBucketPath(url string, method string) string {
var bucket = "/"
cleanUrl := strings.SplitN(url, "?", 1)[0]
if strings.HasPrefix(cleanUrl, "/api/v") {
cleanUrl = strings.ReplaceAll(cleanUrl, "/api/v", "")
l := len(cleanUrl)
i := strings.Index(cleanUrl, "/")
cleanUrl = cleanUrl[i+1:l]
}
parts := strings.Split(cleanUrl, "/")
numParts := len(parts)
if numParts <= 1 {
return cleanUrl
}
currMajor := MajorUnknown
// ! stands for any replaceable id
switch parts[0] {
case MajorChannels:
if numParts == 2 {
// Return the same bucket for all reqs to /channels/id
// In this case, the discord bucket is the same regardless of the id
bucket += MajorChannels + "/!"
return bucket
}
bucket += MajorChannels + "/" + parts[1]
currMajor = MajorChannels
case MajorInvites:
bucket += MajorInvites + "/!"
currMajor = MajorInvites
case MajorGuilds:
/* TODO: Figure out why this makes the bot unresponsive
if numParts == 3 && parts[2] == "channels" {
return "/guilds/!/channels"
}
*/
fallthrough
case MajorWebhooks:
fallthrough
default:
bucket += parts[0] + "/" + parts[1]
currMajor = parts[0]
}
if numParts == 2 {
return bucket
}
// At this point, the major + id part is already accounted for
// In this loop, we only need to strip all remaining snowflakes, emoji names and webhook tokens(optional)
for idx, part := range parts[2:] {
if IsSnowflake(part) {
// Custom rule for messages older than 14d
if currMajor == MajorChannels && parts[idx - 1] == "messages" && method == "DELETE" {
createdAt, _ := GetSnowflakeCreatedAt(part)
if createdAt.Before(time.Now().Add(-1 * 14 * 24 * time.Hour)) {
bucket += "/!14dmsg"
}
continue
}
bucket += "/!"
} else {
if currMajor == MajorChannels && part == "reactions" {
// reaction put/delete fall under a different bucket from other reaction endpoints
if method == "PUT" || method == "DELETE" {
bucket += "/reactions/!modify"
break
}
//All other reaction endpoints falls under the same bucket, so it's irrelevant if the user
//is passing userid, emoji, etc.
bucket += "/reactions/!/!"
//Reactions can only be followed by emoji/userid combo, since we don't care, break
break
}
// Strip webhook tokens and interaction tokens
if (currMajor == MajorWebhooks || currMajor == MajorInteractions) && len(part) >= 64 {
bucket += "/!"
continue
}
bucket += "/" + part
}
}
return bucket
}