diff --git a/activities/models/fan_out.py b/activities/models/fan_out.py index 6ecfbd1..896d4e7 100644 --- a/activities/models/fan_out.py +++ b/activities/models/fan_out.py @@ -5,6 +5,7 @@ from django.db import models from activities.models.timeline_event import TimelineEvent from core.ld import canonicalise from stator.models import State, StateField, StateGraph, StatorModel +from users.models import FollowStates class FanOutStates(StateGraph): @@ -33,15 +34,21 @@ class FanOutStates(StateGraph): post = await fan_out.subject_post.afetch_full() # Make a timeline event directly # If it's a reply, we only add it if we follow at least one - # of the people mentioned AND the author + # of the people mentioned AND the author, or we're mentioned, + # or it's a reply to us or the author add = True mentioned = {identity.id for identity in post.mentions.all()} followed = await sync_to_async(set)( - fan_out.identity.outbound_follows.values_list("id", flat=True) + fan_out.identity.outbound_follows.filter( + state__in=FollowStates.group_active() + ).values_list("target_id", flat=True) ) if post.in_reply_to: - add = (post.author_id in followed) and bool( - mentioned.intersection(followed) + interested_in = followed.union( + {post.author_id, fan_out.identity_id} + ) + add = (post.author_id in followed) and ( + bool(mentioned.intersection(interested_in)) ) if add: await sync_to_async(TimelineEvent.add_post)( diff --git a/activities/models/post.py b/activities/models/post.py index bc988c9..a0bee66 100644 --- a/activities/models/post.py +++ b/activities/models/post.py @@ -543,7 +543,7 @@ class Post(StatorModel): Returns the AP JSON for this object """ value = { - "to": "Public", + "to": [], "cc": [], "type": self.type, "id": self.object_uri, @@ -573,6 +573,12 @@ class Post(StatorModel): value["inReplyTo"] = self.in_reply_to if self.edited: value["updated"] = format_ld_date(self.edited) + # Targeting + # TODO: Add followers object + if self.visibility == self.Visibilities.public: + value["to"].append("Public") + elif self.visibility == self.Visibilities.unlisted: + value["cc"].append("Public") # Mentions for mention in self.mentions.all(): value["tag"].append(mention.to_ap_tag()) @@ -593,7 +599,7 @@ class Post(StatorModel): for attachment in self.attachments.all(): value["attachment"].append(attachment.to_ap()) # Remove fields if they're empty - for field in ["cc", "tag", "attachment"]: + for field in ["to", "cc", "tag", "attachment"]: if not value[field]: del value[field] return value @@ -604,7 +610,7 @@ class Post(StatorModel): """ object = self.to_ap() return { - "to": object["to"], + "to": object.get("to", []), "cc": object.get("cc", []), "type": "Create", "id": self.object_uri + "#create", @@ -618,7 +624,7 @@ class Post(StatorModel): """ object = self.to_ap() return { - "to": object["to"], + "to": object.get("to", []), "cc": object.get("cc", []), "type": "Update", "id": self.object_uri + "#update", @@ -632,7 +638,7 @@ class Post(StatorModel): """ object = self.to_ap() return { - "to": object["to"], + "to": object.get("to", []), "cc": object.get("cc", []), "type": "Delete", "id": self.object_uri + "#delete", @@ -739,13 +745,15 @@ class Post(StatorModel): else: raise ValueError(f"Unknown tag type {tag['type']}") # Visibility and to - # (a post is public if it's ever to/cc as:Public, otherwise we - # regard it as unlisted for now) - targets = get_list(data, "to") + get_list(data, "cc") - post.visibility = Post.Visibilities.unlisted - for target in targets: - if target.lower() == "as:public": - post.visibility = Post.Visibilities.public + # (a post is public if it's to:public, otherwise it's unlisted if + # it's cc:public, otherwise it's more limited) + to = [x.lower() for x in get_list(data, "to")] + cc = [x.lower() for x in get_list(data, "cc")] + post.visibility = Post.Visibilities.mentioned + if "public" in to or "as:public" in to: + post.visibility = Post.Visibilities.public + elif "public" in cc or "as:public" in cc: + post.visibility = Post.Visibilities.unlisted # Attachments # These have no IDs, so we have to wipe them each time post.attachments.all().delete()