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
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}
# unless it's a dm, all the followers should receive the activity
if privacy != "direct":
@ -173,18 +174,18 @@ class ActivitypubMixin:
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"""

View file

@ -107,14 +107,14 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
@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(

View file

@ -227,14 +227,18 @@ class ActivitypubMixins(TestCase):
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"""