arikawa/bot/extras/arguments/emoji.go

111 lines
1.8 KiB
Go
Raw Normal View History

2020-01-19 06:06:00 +00:00
package arguments
import (
"errors"
"regexp"
2020-01-25 02:49:33 +00:00
"unicode/utf16"
2020-01-19 06:06:00 +00:00
)
var (
EmojiRegex = regexp.MustCompile(`<(a?):(.+?):(\d+)>`)
ErrInvalidEmoji = errors.New("Invalid emoji")
)
type Emoji struct {
ID string
Custom bool
Name string
Animated bool
}
func (e Emoji) APIString() string {
2020-01-24 03:54:47 +00:00
if !e.Custom {
return e.ID
}
return e.Name + ":" + e.ID
}
func (e Emoji) String() string {
if !e.Custom {
return e.ID
}
if e.Animated {
return "<a:" + e.Name + ":" + e.ID + ">"
} else {
return "<:" + e.Name + ":" + e.ID + ">"
}
}
2020-01-24 03:54:47 +00:00
func (e Emoji) URL() string {
if e.Custom {
return ""
}
base := "https://cdn.discordapp.com/emojis/" + e.ID
if e.Animated {
return base + ".gif"
} else {
return base + ".png"
}
}
2020-01-19 06:06:00 +00:00
func (e *Emoji) Parse(arg string) error {
2020-01-25 02:49:33 +00:00
// Check if Unicode emoji
if stringIsEmojiOnly(arg) {
e.ID = arg
2020-01-19 06:06:00 +00:00
e.Custom = false
return nil
}
var matches = EmojiRegex.FindStringSubmatch(arg)
if len(matches) != 4 {
return ErrInvalidEmoji
}
e.Custom = true
e.Animated = matches[1] == "a"
e.Name = matches[2]
e.ID = matches[3]
return nil
}
2020-01-25 02:49:33 +00:00
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
}