[bugfix] Fix failure to look up remote profiles with duplicate emojis in some cases (#1534)

* Tidy up emoji parsing on profile submission

Don't bother reparsing for emoji unless one of the fields that can have
emoji in it has changed.
Deduplicate emoji between the display name and profile note - I'm not
sure whether this was hurting anything, but better safe.

* Deduplicate emoji when parsing remote accounts

Some servers - Misskey at least - don't deduplicate emoji, so it's
possible to get an account which has the same emoji used in both the
display name and note and therefore includes that emoji twice in its
metadata. When we start trying to put those into our database, we run
into a uniqueness constraint and fall over.

This change just deduplicates at the point of construction of an
account.
This commit is contained in:
Sam Lade 2023-02-20 15:27:41 +00:00 committed by GitHub
parent 2af33d3fb5
commit f559d46261
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 37 additions and 25 deletions

View file

@ -453,6 +453,7 @@ func ExtractHashtag(i Hashtaggable) (*gtsmodel.Tag, error) {
// ExtractEmojis returns a slice of emojis on the interface. // ExtractEmojis returns a slice of emojis on the interface.
func ExtractEmojis(i WithTag) ([]*gtsmodel.Emoji, error) { func ExtractEmojis(i WithTag) ([]*gtsmodel.Emoji, error) {
emojis := []*gtsmodel.Emoji{} emojis := []*gtsmodel.Emoji{}
emojiMap := make(map[string]*gtsmodel.Emoji)
tagsProp := i.GetActivityStreamsTag() tagsProp := i.GetActivityStreamsTag()
if tagsProp == nil { if tagsProp == nil {
return emojis, nil return emojis, nil
@ -477,6 +478,9 @@ func ExtractEmojis(i WithTag) ([]*gtsmodel.Emoji, error) {
continue continue
} }
emojiMap[emoji.URI] = emoji
}
for _, emoji := range emojiMap {
emojis = append(emojis, emoji) emojis = append(emojis, emoji)
} }
return emojis, nil return emojis, nil

View file

@ -45,23 +45,14 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form
account.Bot = form.Bot account.Bot = form.Bot
} }
account.Emojis = []*gtsmodel.Emoji{} reparseEmojis := false
account.EmojiIDs = []string{}
if form.DisplayName != nil { if form.DisplayName != nil {
if err := validate.DisplayName(*form.DisplayName); err != nil { if err := validate.DisplayName(*form.DisplayName); err != nil {
return nil, gtserror.NewErrorBadRequest(err) return nil, gtserror.NewErrorBadRequest(err)
} }
account.DisplayName = text.SanitizePlaintext(*form.DisplayName) account.DisplayName = text.SanitizePlaintext(*form.DisplayName)
} reparseEmojis = true
// Re-parse for emojis regardless of whether the DisplayName changed
// because we can't otherwise tell which emojis belong to DisplayName
// and which belong to Note
formatResult := p.formatter.FromPlainEmojiOnly(ctx, p.parseMention, account.ID, "", account.DisplayName)
for _, emoji := range formatResult.Emojis {
account.Emojis = append(account.Emojis, emoji)
account.EmojiIDs = append(account.EmojiIDs, emoji.ID)
} }
if form.Note != nil { if form.Note != nil {
@ -71,23 +62,40 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form
// Set the raw note before processing // Set the raw note before processing
account.NoteRaw = *form.Note account.NoteRaw = *form.Note
reparseEmojis = true
} }
// As per DisplayName, we need to reparse regardless to keep emojis straight if reparseEmojis {
// Process note to generate a valid HTML representation // If either DisplayName or Note changed, reparse both, because we
var f text.FormatFunc // can't otherwise tell which one each emoji belongs to.
if account.StatusFormat == "markdown" { // Deduplicate emojis between the two fields.
f = p.formatter.FromMarkdown emojis := make(map[string]*gtsmodel.Emoji)
} else { formatResult := p.formatter.FromPlainEmojiOnly(ctx, p.parseMention, account.ID, "", account.DisplayName)
f = p.formatter.FromPlain for _, emoji := range formatResult.Emojis {
} emojis[emoji.ID] = emoji
formatted := f(ctx, p.parseMention, account.ID, "", account.NoteRaw) }
// Set updated HTML-ified note // Process note to generate a valid HTML representation
account.Note = formatted.HTML var f text.FormatFunc
for _, emoji := range formatted.Emojis { if account.StatusFormat == "markdown" {
account.Emojis = append(account.Emojis, emoji) f = p.formatter.FromMarkdown
account.EmojiIDs = append(account.EmojiIDs, emoji.ID) } else {
f = p.formatter.FromPlain
}
formatted := f(ctx, p.parseMention, account.ID, "", account.NoteRaw)
// Set updated HTML-ified note
account.Note = formatted.HTML
for _, emoji := range formatted.Emojis {
emojis[emoji.ID] = emoji
}
account.Emojis = []*gtsmodel.Emoji{}
account.EmojiIDs = []string{}
for eid, emoji := range emojis {
account.Emojis = append(account.Emojis, emoji)
account.EmojiIDs = append(account.EmojiIDs, eid)
}
} }
if form.Avatar != nil && form.Avatar.Size != 0 { if form.Avatar != nil && form.Avatar.Size != 0 {