* Added logic to detect and set unicode emoji in the custom status slash command

* Replaced strings.split with strings.Fields

* Added logic to handle empty string as message in custom status slash command

* Changed custom status slash command empty message behavior to set def… (#14)

* Changed custom status slash command empty message behavior to set default emoji

* Code refactoring

* Added unit tests and refactored some code

* WIP: Unit tests and refactoring for detecting unicode emoji in custom status slash commands

* Complete unit testing for Get custom status

* Fixed lint

* Added logic for removing skin tone from unicode emoji (#16)

* Added logic for removing skin tone from unicode emoji
Made a reverse system emojis map of string vs []string and stored the emojiNames in sorted order
Added the logic for detecting and replacing/removing skin tone in unicode emoji with variation selector
Added new unit tests with different skin tone emojis

* Refactored removeSkinTone logic to a separate function

* Added check for emoji before removing skin tone in custom status slash command

* Fixed custom status slash command unit test and refactored some code
Chanded the return type of GetEmojiNameFromUnicode from bool to int
Changed the logic for checking presence of emoji without removing skin tone
Fixed the unit tests

* Review fixes: Indentation changes
Этот коммит содержится в:
Manoj
2021-05-26 23:33:08 +05:30
коммит произвёл GitHub
родитель 46649292f8
Коммит ff2fe1e62c
4 изменённых файлов: 133 добавлений и 14 удалений

Просмотреть файл

@@ -4,7 +4,9 @@
package slashcommands
import (
"regexp"
"strings"
"unicode/utf8"
"github.com/mattermost/mattermost-server/v5/app"
"github.com/mattermost/mattermost-server/v5/app/request"
@@ -49,7 +51,7 @@ func (*CustomStatusProvider) DoCommand(a *app.App, c *request.Context, args *mod
message = strings.TrimSpace(message)
if message == CmdCustomStatusClear {
if err := a.RemoveCustomStatus(args.UserId); err != nil {
mlog.Error(err.Error())
mlog.Debug(err.Error())
return &model.CommandResponse{Text: args.T("api.command_custom_status.clear.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
}
@@ -59,20 +61,9 @@ func (*CustomStatusProvider) DoCommand(a *app.App, c *request.Context, args *mod
}
}
customStatus := &model.CustomStatus{
Emoji: DefaultCustomStatusEmoji,
Text: message,
}
firstEmojiLocations := model.ALL_EMOJI_PATTERN.FindIndex([]byte(message))
if len(firstEmojiLocations) > 0 && firstEmojiLocations[0] == 0 {
// emoji found at starting index
customStatus.Emoji = message[firstEmojiLocations[0]+1 : firstEmojiLocations[1]-1]
customStatus.Text = strings.TrimSpace(message[firstEmojiLocations[1]:])
}
customStatus.TrimMessage()
customStatus := GetCustomStatus(message)
if err := a.SetCustomStatus(args.UserId, customStatus); err != nil {
mlog.Error(err.Error())
mlog.Debug(err.Error())
return &model.CommandResponse{Text: args.T("api.command_custom_status.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
}
@@ -84,3 +75,69 @@ func (*CustomStatusProvider) DoCommand(a *app.App, c *request.Context, args *mod
}),
}
}
func GetCustomStatus(message string) *model.CustomStatus {
customStatus := &model.CustomStatus{
Emoji: DefaultCustomStatusEmoji,
Text: message,
}
firstEmojiLocations := model.ALL_EMOJI_PATTERN.FindIndex([]byte(message))
if len(firstEmojiLocations) > 0 && firstEmojiLocations[0] == 0 {
// emoji found at starting index
customStatus.Emoji = message[firstEmojiLocations[0]+1 : firstEmojiLocations[1]-1]
customStatus.Text = strings.TrimSpace(message[firstEmojiLocations[1]:])
customStatus.TrimMessage()
return customStatus
}
if message == "" {
return customStatus
}
spaceSeparatedMessage := strings.Fields(message)
if len(spaceSeparatedMessage) == 0 {
return customStatus
}
emojiString := spaceSeparatedMessage[0]
var unicode []string
for utf8.RuneCountInString(emojiString) >= 1 {
codepoint, size := utf8.DecodeRuneInString(emojiString)
code := model.RuneToHexadecimalString(codepoint)
unicode = append(unicode, code)
emojiString = emojiString[size:]
}
unicodeString := removeUnicodeSkinTone(strings.Join(unicode, "-"))
emoji, count := model.GetEmojiNameFromUnicode(unicodeString)
if count > 0 {
customStatus.Emoji = emoji
textString := strings.Join(spaceSeparatedMessage[1:], " ")
customStatus.Text = strings.TrimSpace(textString)
}
customStatus.TrimMessage()
return customStatus
}
func removeUnicodeSkinTone(unicodeString string) string {
skinToneDetectorRegex := regexp.MustCompile("-(1f3fb|1f3fc|1f3fd|1f3fe|1f3ff)")
skinToneLocations := skinToneDetectorRegex.FindIndex([]byte(unicodeString))
if len(skinToneLocations) == 0 {
return unicodeString
}
if _, count := model.GetEmojiNameFromUnicode(unicodeString); count == 1 {
return unicodeString
}
unicodeWithRemovedSkinTone := unicodeString[:skinToneLocations[0]] + unicodeString[skinToneLocations[1]:]
unicodeWithVariationSelector := unicodeString[:skinToneLocations[0]] + "-fe0f" + unicodeString[skinToneLocations[1]:]
if _, count := model.GetEmojiNameFromUnicode(unicodeWithRemovedSkinTone); count > 0 {
unicodeString = unicodeWithRemovedSkinTone
} else if _, count := model.GetEmojiNameFromUnicode(unicodeWithVariationSelector); count > 0 {
unicodeString = unicodeWithVariationSelector
}
return unicodeString
}

Просмотреть файл

@@ -0,0 +1,34 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package slashcommands
import (
"testing"
"github.com/mattermost/mattermost-server/v5/model"
)
func TestGetCustomStatus(t *testing.T) {
for msg, expected := range map[string]model.CustomStatus{
"": {Emoji: DefaultCustomStatusEmoji, Text: ""},
"Hey": {Emoji: DefaultCustomStatusEmoji, Text: "Hey"},
":cactus: Hurt": {Emoji: "cactus", Text: "Hurt"},
"👅": {Emoji: "tongue", Text: ""},
"👅 Eating": {Emoji: "tongue", Text: "Eating"},
"💪🏻 Working out": {Emoji: "muscle_light_skin_tone", Text: "Working out"},
"👙 Swimming": {Emoji: "bikini", Text: "Swimming"},
"👙Swimming": {Emoji: DefaultCustomStatusEmoji, Text: "👙Swimming"},
"👍🏿 Okay": {Emoji: "+1_dark_skin_tone", Text: "Okay"},
"🤴🏾 Dark king": {Emoji: "prince_medium_dark_skin_tone", Text: "Dark king"},
"⛹🏾‍♀️ Playing basketball": {Emoji: "basketball_woman", Text: "Playing basketball"},
"🏋🏿‍♀️ Weightlifting": {Emoji: "weight_lifting_woman", Text: "Weightlifting"},
"🏄 Surfing": {Emoji: "surfer", Text: "Surfing"},
"👨‍👨‍👦‍👦 Family": {Emoji: "family_man_man_boy_boy", Text: "Family"},
} {
actual := GetCustomStatus(msg)
if actual.Emoji != expected.Emoji || actual.Text != expected.Text {
t.Errorf("expected `%v`, got `%v`", expected, *actual)
}
}
}

Просмотреть файл

@@ -5,6 +5,7 @@ package model
import (
"encoding/json"
"fmt"
"io"
)
@@ -39,6 +40,10 @@ func CustomStatusFromJson(data io.Reader) *CustomStatus {
return cs
}
func RuneToHexadecimalString(r rune) string {
return fmt.Sprintf("%04x", r)
}
type RecentCustomStatuses []CustomStatus
func (rcs *RecentCustomStatuses) Contains(cs *CustomStatus) bool {

Просмотреть файл

@@ -8,6 +8,7 @@ import (
"io"
"net/http"
"regexp"
"sort"
)
const (
@@ -23,6 +24,8 @@ var EMOJI_PATTERN = regexp.MustCompile(`:[a-zA-Z0-9_-]+:`)
// TODO: Merge ALL_EMOJI_PATTERN with EMOJI_PATTERN after updating custom emoji help texts
var ALL_EMOJI_PATTERN = regexp.MustCompile(`:[a-zA-Z0-9_+-]+:`)
var ReverseSystemEmojisMap = makeReverseEmojiMap()
type Emoji struct {
Id string `json:"id"`
CreateAt int64 `json:"create_at"`
@@ -42,6 +45,26 @@ func GetSystemEmojiId(emojiName string) (string, bool) {
return id, found
}
func makeReverseEmojiMap() map[string][]string {
reverseEmojiMap := make(map[string][]string)
for key, value := range SystemEmojis {
emojiNames := reverseEmojiMap[value]
emojiNames = append(emojiNames, key)
sort.Strings(emojiNames)
reverseEmojiMap[value] = emojiNames
}
return reverseEmojiMap
}
func GetEmojiNameFromUnicode(unicode string) (emojiName string, count int) {
if emojiNames, found := ReverseSystemEmojisMap[unicode]; found {
return emojiNames[0], len(emojiNames)
}
return "", 0
}
func (emoji *Emoji) IsValid() *AppError {
if !IsValidId(emoji.Id) {
return NewAppError("Emoji.IsValid", "model.emoji.id.app_error", nil, "", http.StatusBadRequest)