Merge pull request #3257 from dato/prefer_shared_inbox

Use shared inboxes for mentions too
This commit is contained in:
Mouse Reeve 2024-02-03 07:25:51 -08:00 committed by GitHub
commit 0f0420ce04
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 24 additions and 19 deletions

View file

@ -152,8 +152,9 @@ class ActivitypubMixin:
# find anyone who's tagged in a status, for example # find anyone who's tagged in a status, for example
mentions = self.recipients if hasattr(self, "recipients") else [] mentions = self.recipients if hasattr(self, "recipients") else []
# we always send activities to explicitly mentioned users' inboxes # we always send activities to explicitly mentioned users (using shared inboxes
recipients = [u.inbox for u in mentions or [] if not u.local] # 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}
# unless it's a dm, all the followers should receive the activity # unless it's a dm, all the followers should receive the activity
if privacy != "direct": if privacy != "direct":
@ -173,18 +174,18 @@ class ActivitypubMixin:
if user: if user:
queryset = queryset.filter(following=user) queryset = queryset.filter(following=user)
# ideally, we will send to shared inboxes for efficiency # as above, we prefer shared inboxes if available
shared_inboxes = ( recipients.update(
queryset.filter(shared_inbox__isnull=False) queryset.filter(shared_inbox__isnull=False).values_list(
.values_list("shared_inbox", flat=True) "shared_inbox", flat=True
.distinct() )
) )
# but not everyone has a shared inbox recipients.update(
inboxes = queryset.filter(shared_inbox__isnull=True).values_list( queryset.filter(shared_inbox__isnull=True).values_list(
"inbox", flat=True "inbox", flat=True
)
) )
recipients += list(shared_inboxes) + list(inboxes) return list(recipients)
return list(set(recipients))
def to_activity_dataclass(self): def to_activity_dataclass(self):
"""convert from a model to an activity""" """convert from a model to an activity"""

View file

@ -107,14 +107,14 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
@property @property
def recipients(self): def recipients(self):
"""tagged users who definitely need to get this status in broadcast""" """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 ( if (
hasattr(self, "reply_parent") hasattr(self, "reply_parent")
and self.reply_parent and self.reply_parent
and not self.reply_parent.user.local and not self.reply_parent.user.local
): ):
mentions.append(self.reply_parent.user) mentions.add(self.reply_parent.user)
return list(set(mentions)) return list(mentions)
@classmethod @classmethod
def ignore_activity( def ignore_activity(

View file

@ -227,14 +227,18 @@ class ActivitypubMixins(TestCase):
shared_inbox="http://example.com/inbox", shared_inbox="http://example.com/inbox",
outbox="https://example.com/users/nutria/outbox", outbox="https://example.com/users/nutria/outbox",
) )
MockSelf = namedtuple("Self", ("privacy", "user")) MockSelf = namedtuple("Self", ("privacy", "user", "recipients"))
mock_self = MockSelf("public", self.local_user)
self.local_user.followers.add(self.remote_user) self.local_user.followers.add(self.remote_user)
self.local_user.followers.add(another_remote_user) self.local_user.followers.add(another_remote_user)
mock_self = MockSelf("public", self.local_user, [])
recipients = ActivitypubMixin.get_recipients(mock_self) recipients = ActivitypubMixin.get_recipients(mock_self)
self.assertEqual(len(recipients), 1) self.assertCountEqual(recipients, ["http://example.com/inbox"])
self.assertEqual(recipients[0], "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, *_): def test_get_recipients_software(self, *_):
"""should differentiate between bookwyrm and other remote users""" """should differentiate between bookwyrm and other remote users"""