Fixed #559: Trim hashtags to 100 chars or less

This commit is contained in:
Andrew Godwin 2023-05-03 23:11:34 -06:00
parent ac41f2afde
commit 90e8a6d6c0
3 changed files with 43 additions and 5 deletions

View file

@ -87,6 +87,8 @@ class HashtagManager(models.Manager):
class Hashtag(StatorModel): class Hashtag(StatorModel):
MAXIMUM_LENGTH = 100
# Normalized hashtag without the '#' # Normalized hashtag without the '#'
hashtag = models.SlugField(primary_key=True, max_length=100) hashtag = models.SlugField(primary_key=True, max_length=100)

View file

@ -489,7 +489,10 @@ class Post(StatorModel):
# Strip all unwanted HTML and apply linebreaks filter, grabbing hashtags on the way # Strip all unwanted HTML and apply linebreaks filter, grabbing hashtags on the way
parser = FediverseHtmlParser(linebreaks_filter(content), find_hashtags=True) parser = FediverseHtmlParser(linebreaks_filter(content), find_hashtags=True)
content = parser.html content = parser.html
hashtags = sorted(parser.hashtags) or None hashtags = (
sorted([tag[: Hashtag.MAXIMUM_LENGTH] for tag in parser.hashtags])
or None
)
# Make the Post object # Make the Post object
post = cls.objects.create( post = cls.objects.create(
author=author, author=author,
@ -529,7 +532,10 @@ class Post(StatorModel):
# Strip all HTML and apply linebreaks filter # Strip all HTML and apply linebreaks filter
parser = FediverseHtmlParser(linebreaks_filter(content), find_hashtags=True) parser = FediverseHtmlParser(linebreaks_filter(content), find_hashtags=True)
self.content = parser.html self.content = parser.html
self.hashtags = sorted(parser.hashtags) or None self.hashtags = (
sorted([tag[: Hashtag.MAXIMUM_LENGTH] for tag in parser.hashtags])
or None
)
self.summary = summary or None self.summary = summary or None
self.sensitive = bool(summary) if sensitive is None else sensitive self.sensitive = bool(summary) if sensitive is None else sensitive
self.visibility = visibility self.visibility = visibility
@ -577,7 +583,7 @@ class Post(StatorModel):
if self.hashtags: if self.hashtags:
for hashtag in self.hashtags: for hashtag in self.hashtags:
tag, _ = await Hashtag.objects.aget_or_create( tag, _ = await Hashtag.objects.aget_or_create(
hashtag=hashtag, hashtag=hashtag[: Hashtag.MAXIMUM_LENGTH],
) )
await tag.atransition_perform(HashtagStates.outdated) await tag.atransition_perform(HashtagStates.outdated)
@ -876,7 +882,9 @@ class Post(StatorModel):
post.mentions.add(mention_identity) post.mentions.add(mention_identity)
elif tag_type in ["_:hashtag", "hashtag"]: elif tag_type in ["_:hashtag", "hashtag"]:
post.hashtags.append( post.hashtags.append(
get_value_or_map(tag, "name", "nameMap").lower().lstrip("#") get_value_or_map(tag, "name", "nameMap")
.lower()
.lstrip("#")[: Hashtag.MAXIMUM_LENGTH]
) )
elif tag_type in ["toot:emoji", "emoji"]: elif tag_type in ["toot:emoji", "emoji"]:
emoji = Emoji.by_ap_tag(post.author.domain, tag, create=True) emoji = Emoji.by_ap_tag(post.author.domain, tag, create=True)

View file

@ -1,7 +1,7 @@
import pytest import pytest
from pytest_httpx import HTTPXMock from pytest_httpx import HTTPXMock
from activities.models import Post, PostStates from activities.models import Hashtag, Post, PostStates
from activities.models.post_types import QuestionData from activities.models.post_types import QuestionData
from users.models import Identity, InboxMessage from users.models import Identity, InboxMessage
@ -57,6 +57,34 @@ def test_post_create_edit(identity: Identity, config_system):
assert list(post.mentions.all()) == [] assert list(post.mentions.all()) == []
@pytest.mark.django_db
def test_ensure_hashtag(identity: Identity, config_system, stator):
"""
Tests that normal hashtags get a Hashtag object created, and a hashtag
over our limit of 100 characters is truncated.
"""
# Normal length hashtag
post = Post.create_local(
author=identity,
content="Hello, #testtag",
)
stator.run_single_cycle_sync()
assert post.hashtags == ["testtag"]
assert Hashtag.objects.filter(hashtag="testtag").exists()
# Excessively long hashtag
post = Post.create_local(
author=identity,
content="Hello, #thisisahashtagthatiswaytoolongandissignificantlyaboveourmaximumlimitofonehundredcharacterswhytheywouldbethislongidontknow",
)
stator.run_single_cycle_sync()
assert post.hashtags == [
"thisisahashtagthatiswaytoolongandissignificantlyaboveourmaximumlimitofonehundredcharacterswhytheywou"
]
assert Hashtag.objects.filter(
hashtag="thisisahashtagthatiswaytoolongandissignificantlyaboveourmaximumlimitofonehundredcharacterswhytheywou"
).exists()
@pytest.mark.django_db @pytest.mark.django_db
def test_linkify_mentions_remote( def test_linkify_mentions_remote(
identity, identity2, remote_identity, remote_identity2 identity, identity2, remote_identity, remote_identity2