Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use shared inboxes for mentions too #3257

Merged
merged 2 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions bookwyrm/models/activitypub_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,9 @@ def get_recipients(self, software=None) -> list[str]:
# find anyone who's tagged in a status, for example
mentions = self.recipients if hasattr(self, "recipients") else []

# we always send activities to explicitly mentioned users' inboxes
recipients = [u.inbox for u in mentions or [] if not u.local]
# we always send activities to explicitly mentioned users (using shared inboxes
# where available to avoid duplicate submissions to a given instance)
recipients = {u.shared_inbox or u.inbox for u in mentions if not u.local}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't realize you could create sets this way, this is great! Thank you


# unless it's a dm, all the followers should receive the activity
if privacy != "direct":
Expand All @@ -173,18 +174,18 @@ def get_recipients(self, software=None) -> list[str]:
if user:
queryset = queryset.filter(following=user)

# ideally, we will send to shared inboxes for efficiency
shared_inboxes = (
queryset.filter(shared_inbox__isnull=False)
.values_list("shared_inbox", flat=True)
.distinct()
# as above, we prefer shared inboxes if available
recipients.update(
queryset.filter(shared_inbox__isnull=False).values_list(
"shared_inbox", flat=True
)
)
# but not everyone has a shared inbox
inboxes = queryset.filter(shared_inbox__isnull=True).values_list(
"inbox", flat=True
recipients.update(
queryset.filter(shared_inbox__isnull=True).values_list(
"inbox", flat=True
)
)
recipients += list(shared_inboxes) + list(inboxes)
return list(set(recipients))
return list(recipients)

def to_activity_dataclass(self):
"""convert from a model to an activity"""
Expand Down
6 changes: 3 additions & 3 deletions bookwyrm/models/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,14 @@ def delete(self, *args, **kwargs): # pylint: disable=unused-argument
@property
def recipients(self):
"""tagged users who definitely need to get this status in broadcast"""
mentions = [u for u in self.mention_users.all() if not u.local]
mentions = {u for u in self.mention_users.all() if not u.local}
if (
hasattr(self, "reply_parent")
and self.reply_parent
and not self.reply_parent.user.local
):
mentions.append(self.reply_parent.user)
return list(set(mentions))
mentions.add(self.reply_parent.user)
return list(mentions)

@classmethod
def ignore_activity(
Expand Down
12 changes: 8 additions & 4 deletions bookwyrm/tests/models/test_activitypub_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,14 +227,18 @@ def test_get_recipients_combine_inboxes(self, *_):
shared_inbox="http://example.com/inbox",
outbox="https://example.com/users/nutria/outbox",
)
MockSelf = namedtuple("Self", ("privacy", "user"))
mock_self = MockSelf("public", self.local_user)
MockSelf = namedtuple("Self", ("privacy", "user", "recipients"))
self.local_user.followers.add(self.remote_user)
self.local_user.followers.add(another_remote_user)

mock_self = MockSelf("public", self.local_user, [])
recipients = ActivitypubMixin.get_recipients(mock_self)
self.assertEqual(len(recipients), 1)
self.assertEqual(recipients[0], "http://example.com/inbox")
self.assertCountEqual(recipients, ["http://example.com/inbox"])

# should also work with recipient that is a follower
mock_self.recipients.append(another_remote_user)
recipients = ActivitypubMixin.get_recipients(mock_self)
self.assertCountEqual(recipients, ["http://example.com/inbox"])

def test_get_recipients_software(self, *_):
"""should differentiate between bookwyrm and other remote users"""
Expand Down
Loading