summaryrefslogtreecommitdiffstats
path: root/activities/models
diff options
context:
space:
mode:
Diffstat (limited to 'activities/models')
-rw-r--r--activities/models/__init__.py1
-rw-r--r--activities/models/post.py42
-rw-r--r--activities/models/post_attachment.py55
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",
+ ]