diff options
Diffstat (limited to 'activities/models')
-rw-r--r-- | activities/models/__init__.py | 1 | ||||
-rw-r--r-- | activities/models/post.py | 42 | ||||
-rw-r--r-- | activities/models/post_attachment.py | 55 |
3 files changed, 91 insertions, 7 deletions
diff --git a/activities/models/__init__.py b/activities/models/__init__.py index 48ba879..1ae3f4c 100644 --- a/activities/models/__init__.py +++ b/activities/models/__init__.py @@ -1,4 +1,5 @@ from .fan_out import FanOut, FanOutStates # noqa from .post import Post, PostStates # noqa +from .post_attachment import PostAttachment, PostAttachmentStates # noqa from .post_interaction import PostInteraction, PostInteractionStates # noqa from .timeline_event import TimelineEvent # noqa diff --git a/activities/models/post.py b/activities/models/post.py index 473755b..caa2981 100644 --- a/activities/models/post.py +++ b/activities/models/post.py @@ -146,6 +146,9 @@ class Post(StatorModel): def __str__(self): return f"{self.author} #{self.id}" + def get_absolute_url(self): + return self.urls.view + @property def safe_content(self): return sanitize_post(self.content) @@ -244,11 +247,12 @@ class Post(StatorModel): raise KeyError(f"No post with ID {data['id']}", data) if update or created: post.content = sanitize_post(data["content"]) - post.summary = data.get("summary", None) + post.summary = data.get("summary") post.sensitive = data.get("as:sensitive", False) - post.url = data.get("url", None) - post.published = parse_ld_date(data.get("published", None)) - post.edited = parse_ld_date(data.get("updated", None)) + post.url = data.get("url") + post.published = parse_ld_date(data.get("published")) + post.edited = parse_ld_date(data.get("updated")) + post.in_reply_to = data.get("inReplyTo") # Mentions and hashtags post.hashtags = [] for tag in get_list(data, "tag"): @@ -270,6 +274,26 @@ class Post(StatorModel): for target in targets: if target.lower() == "as:public": post.visibility = Post.Visibilities.public + # Attachments + # These have no IDs, so we have to wipe them each time + post.attachments.all().delete() + for attachment in get_list(data, "attachment"): + if "http://joinmastodon.org/ns#focalPoint" in attachment: + focal_x, focal_y = attachment[ + "http://joinmastodon.org/ns#focalPoint" + ]["@list"] + else: + focal_x, focal_y = None, None + post.attachments.create( + remote_url=attachment["url"], + mimetype=attachment["mediaType"], + name=attachment.get("name"), + width=attachment.get("width"), + height=attachment.get("height"), + blurhash=attachment.get("http://joinmastodon.org/ns#blurhash"), + focal_x=focal_x, + focal_y=focal_y, + ) post.save() return post @@ -308,9 +332,13 @@ class Post(StatorModel): raise ValueError("Create actor does not match its Post object", data) # Create it post = cls.by_ap(data["object"], create=True, update=True) - # Make timeline events for followers - for follow in Follow.objects.filter(target=post.author, source__local=True): - TimelineEvent.add_post(follow.source, post) + # Make timeline events for followers if it's not a reply + # TODO: _do_ show replies to people we follow somehow + if not post.in_reply_to: + for follow in Follow.objects.filter( + target=post.author, source__local=True + ): + TimelineEvent.add_post(follow.source, post) # Make timeline events for mentions if they're local for mention in post.mentions.all(): if mention.local: diff --git a/activities/models/post_attachment.py b/activities/models/post_attachment.py new file mode 100644 index 0000000..ee77d29 --- /dev/null +++ b/activities/models/post_attachment.py @@ -0,0 +1,55 @@ +from django.db import models + +from stator.models import State, StateField, StateGraph, StatorModel + + +class PostAttachmentStates(StateGraph): + new = State(try_interval=30000) + fetched = State() + + new.transitions_to(fetched) + + @classmethod + async def handle_new(cls, instance): + # TODO: Fetch images to our own media storage + pass + + +class PostAttachment(StatorModel): + """ + An attachment to a Post. Could be an image, a video, etc. + """ + + post = models.ForeignKey( + "activities.post", + on_delete=models.CASCADE, + related_name="attachments", + ) + + state = StateField(graph=PostAttachmentStates) + + mimetype = models.CharField(max_length=200) + + # File may not be populated if it's remote and not cached on our side yet + file = models.FileField(upload_to="attachments/%Y/%m/%d/", null=True, blank=True) + + remote_url = models.CharField(max_length=500, null=True, blank=True) + + # This is the description for images, at least + name = models.TextField(null=True, blank=True) + + width = models.IntegerField(null=True, blank=True) + height = models.IntegerField(null=True, blank=True) + focal_x = models.IntegerField(null=True, blank=True) + focal_y = models.IntegerField(null=True, blank=True) + blurhash = models.TextField(null=True, blank=True) + + def is_image(self): + return self.mimetype in [ + "image/apng", + "image/avif", + "image/gif", + "image/jpeg", + "image/png", + "image/webp", + ] |