API: Added reactions into rate limit parsers

This commit is contained in:
diamondburned 2020-02-07 08:31:10 -08:00
parent 5acf01d984
commit f3b4362ada
6 changed files with 141 additions and 65 deletions

64
api/rate/emoji.go Normal file
View File

@ -0,0 +1,64 @@
package rate
import (
"strconv"
"strings"
"unicode/utf16"
)
func StringIsEmojiOnly(emoji string) bool {
runes := []rune(emoji)
// Slice of runes is 2, since some emojis have 2 runes.
switch len(runes) {
case 0:
return false
case 1, 2:
return EmojiRune(runes[0])
// case 2:
// return EmojiRune(runes[0]) && EmojiRune(runes[1])
default:
return false
}
}
func StringIsCustomEmoji(emoji string) bool {
parts := strings.Split(emoji, ":")
if len(parts) != 2 {
return false
}
// Validate ID
_, err := strconv.Atoi(parts[1])
if err != nil {
return false
}
// Validate name, shouldn't have whitespaces
if strings.ContainsRune(parts[0], ' ') {
return false
}
return true
}
var surrogates = [...][2]rune{ // [0] from, [1] to
{utf16.DecodeRune(0xD83C, 0xD000), utf16.DecodeRune(0xD83C, 0xDFFF)},
{utf16.DecodeRune(0xD83E, 0xD000), utf16.DecodeRune(0xD83E, 0xDFFF)},
{utf16.DecodeRune(0xD83F, 0xD000), utf16.DecodeRune(0xD83F, 0xDFFF)},
}
func EmojiRune(r rune) bool {
b := r == '\u00a9' || r == '\u00ae' ||
(r >= '\u2000' && r <= '\u3300')
if b {
return true
}
for _, surrogate := range surrogates {
if surrogate[0] <= r && r <= surrogate[1] {
return true
}
}
return false
}

43
api/rate/emoji_test.go Normal file
View File

@ -0,0 +1,43 @@
// +build unit
package rate
import "testing"
func TestEmojiRuneParsing(t *testing.T) {
var emojis = []string{
"🏑",
"❄️",
"🤲🏿",
}
var notEmojis = []string{
"🏃🏿🏃🏿", // dual emojis
"te", // not emoji
}
for i, emoji := range emojis {
if !StringIsEmojiOnly(emoji) {
t.Fatal(i, "is an emoji, function returned false")
}
}
for i, not := range notEmojis {
if StringIsEmojiOnly(not) {
t.Fatal(i, "is not an emoji, function returned true")
}
}
}
func TestEmojiCustomParsing(t *testing.T) {
var emojis = []string{
"emoji_thing:213131141",
"StareNeutral:612368399732965376",
}
for i, emoji := range emojis {
if !StringIsCustomEmoji(emoji) {
t.Fatal(i, "is a custom emoji, fn returned false")
}
}
}

View File

@ -29,9 +29,22 @@ func ParseBucketKey(path string) string {
// we need to remove IDs from these
for ; skip < len(parts); skip++ {
// Check if it's a number:
if _, err := strconv.Atoi(parts[skip]); err == nil {
// is a number, DELET
parts[skip] = ""
continue
}
// Check if it's an emoji:
if StringIsEmojiOnly(parts[skip]) {
parts[skip] = ""
continue
}
// Check if it's a custom emoji:
if StringIsCustomEmoji(parts[skip]) {
parts[skip] = ""
continue
}
}

View File

@ -6,12 +6,24 @@ import "testing"
func TestBucketKey(t *testing.T) {
var tests = [][2]string{
{"/guilds/123123/messages", "/guilds/123123/messages"},
{"/guilds/123123/", "/guilds/123123/"},
{"/channels/123131231", "/channels/123131231"},
{"/channels/123123/message/123456", "/channels/123123/message/"},
{"/guilds/123123/messages",
"/guilds/123123/messages"},
{"/guilds/123123/",
"/guilds/123123/"},
{"/channels/123131231",
"/channels/123131231"},
{"/channels/123123/message/123456",
"/channels/123123/message/"},
{"/user/123123", "/user/"},
{"/user/123123/", "/user//"}, // not sure about this
// Not sure about this:
{"/user/123123/", "/user//"},
{"/channels/1/messages/1/reactions/🤔/@me",
"/channels/1/messages//reactions//@me"},
{"/channels/1/messages/2/reactions/thonk:123123/@me",
"/channels/1/messages//reactions//@me"},
// Actual URL:
{"/channels/486833611564253186/messages/540519319814275089/reactions/🥺/@me",
"/channels/486833611564253186/messages//reactions//@me"},
}
for _, conds := range tests {

View File

@ -3,7 +3,8 @@ package arguments
import (
"errors"
"regexp"
"unicode/utf16"
"github.com/diamondburned/arikawa/api/rate"
)
var (
@ -56,7 +57,7 @@ func (e Emoji) URL() string {
func (e *Emoji) Parse(arg string) error {
// Check if Unicode emoji
if stringIsEmojiOnly(arg) {
if rate.StringIsEmojiOnly(arg) {
e.ID = arg
e.Custom = false
@ -76,35 +77,3 @@ func (e *Emoji) Parse(arg string) error {
return nil
}
func stringIsEmojiOnly(emoji string) bool {
runes := []rune(emoji)
// Slice of runes is 2, since some emojis have 2 runes.
if len(runes) > 2 {
return false
}
return emojiRune(runes[0])
}
var surrogates = [...][2]rune{ // [0] from, [1] to
{utf16.DecodeRune(0xD83C, 0xD000), utf16.DecodeRune(0xD83C, 0xDFFF)},
{utf16.DecodeRune(0xD83E, 0xD000), utf16.DecodeRune(0xD83E, 0xDFFF)},
{utf16.DecodeRune(0xD83F, 0xD000), utf16.DecodeRune(0xD83F, 0xDFFF)},
}
func emojiRune(r rune) bool {
b := r == '\u00a9' || r == '\u00ae' ||
(r >= '\u2000' && r <= '\u3300')
if b {
return true
}
for _, surrogate := range surrogates {
if surrogate[0] <= r && r <= surrogate[1] {
return true
}
}
return false
}

View File

@ -2,31 +2,6 @@ package arguments
import "testing"
func TestEmojiRuneParsing(t *testing.T) {
var emojis = []string{
"🏑",
"❄️",
"🤲🏿",
}
var notEmojis = []string{
"🏃🏿🏃🏿", // dual emojis
"te", // not emoji
}
for i, emoji := range emojis {
if !stringIsEmojiOnly(emoji) {
t.Fatal(i, "is an emoji, function returned false")
}
}
for i, not := range notEmojis {
if stringIsEmojiOnly(not) {
t.Fatal(i, "is not an emoji, function returned true")
}
}
}
func TestEmojiRune(t *testing.T) {
const emoji = "💩"