Perform shared inbox delivery
This commit is contained in:
parent
f4a8a96b81
commit
a875dd7a54
|
@ -72,7 +72,10 @@ class FanOutStates(StateGraph):
|
||||||
try:
|
try:
|
||||||
await post.author.signed_request(
|
await post.author.signed_request(
|
||||||
method="post",
|
method="post",
|
||||||
uri=fan_out.identity.inbox_uri,
|
uri=(
|
||||||
|
fan_out.identity.shared_inbox_uri
|
||||||
|
or fan_out.identity.inbox_uri
|
||||||
|
),
|
||||||
body=canonicalise(post.to_create_ap()),
|
body=canonicalise(post.to_create_ap()),
|
||||||
)
|
)
|
||||||
except httpx.RequestError:
|
except httpx.RequestError:
|
||||||
|
@ -85,7 +88,10 @@ class FanOutStates(StateGraph):
|
||||||
try:
|
try:
|
||||||
await post.author.signed_request(
|
await post.author.signed_request(
|
||||||
method="post",
|
method="post",
|
||||||
uri=fan_out.identity.inbox_uri,
|
uri=(
|
||||||
|
fan_out.identity.shared_inbox_uri
|
||||||
|
or fan_out.identity.inbox_uri
|
||||||
|
),
|
||||||
body=canonicalise(post.to_update_ap()),
|
body=canonicalise(post.to_update_ap()),
|
||||||
)
|
)
|
||||||
except httpx.RequestError:
|
except httpx.RequestError:
|
||||||
|
@ -108,7 +114,10 @@ class FanOutStates(StateGraph):
|
||||||
try:
|
try:
|
||||||
await post.author.signed_request(
|
await post.author.signed_request(
|
||||||
method="post",
|
method="post",
|
||||||
uri=fan_out.identity.inbox_uri,
|
uri=(
|
||||||
|
fan_out.identity.shared_inbox_uri
|
||||||
|
or fan_out.identity.inbox_uri
|
||||||
|
),
|
||||||
body=canonicalise(post.to_delete_ap()),
|
body=canonicalise(post.to_delete_ap()),
|
||||||
)
|
)
|
||||||
except httpx.RequestError:
|
except httpx.RequestError:
|
||||||
|
@ -130,7 +139,10 @@ class FanOutStates(StateGraph):
|
||||||
try:
|
try:
|
||||||
await interaction.identity.signed_request(
|
await interaction.identity.signed_request(
|
||||||
method="post",
|
method="post",
|
||||||
uri=fan_out.identity.inbox_uri,
|
uri=(
|
||||||
|
fan_out.identity.shared_inbox_uri
|
||||||
|
or fan_out.identity.inbox_uri
|
||||||
|
),
|
||||||
body=canonicalise(interaction.to_ap()),
|
body=canonicalise(interaction.to_ap()),
|
||||||
)
|
)
|
||||||
except httpx.RequestError:
|
except httpx.RequestError:
|
||||||
|
@ -153,7 +165,10 @@ class FanOutStates(StateGraph):
|
||||||
try:
|
try:
|
||||||
await interaction.identity.signed_request(
|
await interaction.identity.signed_request(
|
||||||
method="post",
|
method="post",
|
||||||
uri=fan_out.identity.inbox_uri,
|
uri=(
|
||||||
|
fan_out.identity.shared_inbox_uri
|
||||||
|
or fan_out.identity.inbox_uri
|
||||||
|
),
|
||||||
body=canonicalise(interaction.to_undo_ap()),
|
body=canonicalise(interaction.to_undo_ap()),
|
||||||
)
|
)
|
||||||
except httpx.RequestError:
|
except httpx.RequestError:
|
||||||
|
@ -165,7 +180,10 @@ class FanOutStates(StateGraph):
|
||||||
try:
|
try:
|
||||||
await identity.signed_request(
|
await identity.signed_request(
|
||||||
method="post",
|
method="post",
|
||||||
uri=fan_out.identity.inbox_uri,
|
uri=(
|
||||||
|
fan_out.identity.shared_inbox_uri
|
||||||
|
or fan_out.identity.inbox_uri
|
||||||
|
),
|
||||||
body=canonicalise(fan_out.subject_identity.to_update_ap()),
|
body=canonicalise(fan_out.subject_identity.to_update_ap()),
|
||||||
)
|
)
|
||||||
except httpx.RequestError:
|
except httpx.RequestError:
|
||||||
|
@ -177,7 +195,10 @@ class FanOutStates(StateGraph):
|
||||||
try:
|
try:
|
||||||
await identity.signed_request(
|
await identity.signed_request(
|
||||||
method="post",
|
method="post",
|
||||||
uri=fan_out.identity.inbox_uri,
|
uri=(
|
||||||
|
fan_out.identity.shared_inbox_uri
|
||||||
|
or fan_out.identity.inbox_uri
|
||||||
|
),
|
||||||
body=canonicalise(fan_out.subject_identity.to_delete_ap()),
|
body=canonicalise(fan_out.subject_identity.to_delete_ap()),
|
||||||
)
|
)
|
||||||
except httpx.RequestError:
|
except httpx.RequestError:
|
||||||
|
@ -214,6 +235,9 @@ class FanOut(StatorModel):
|
||||||
state = StateField(FanOutStates)
|
state = StateField(FanOutStates)
|
||||||
|
|
||||||
# The user this event is targeted at
|
# The user this event is targeted at
|
||||||
|
# We always need this, but if there is a shared inbox URL on the user
|
||||||
|
# we'll deliver to that and won't have fanouts for anyone else with the
|
||||||
|
# same one.
|
||||||
identity = models.ForeignKey(
|
identity = models.ForeignKey(
|
||||||
"users.Identity",
|
"users.Identity",
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
|
|
|
@ -707,7 +707,20 @@ class Post(StatorModel):
|
||||||
# If it's a local post, include the author
|
# If it's a local post, include the author
|
||||||
if self.local:
|
if self.local:
|
||||||
targets.add(self.author)
|
targets.add(self.author)
|
||||||
return targets
|
# Now dedupe the targets based on shared inboxes (we only keep one per
|
||||||
|
# shared inbox)
|
||||||
|
deduped_targets = set()
|
||||||
|
shared_inboxes = set()
|
||||||
|
for target in targets:
|
||||||
|
if target.local or not target.shared_inbox_uri:
|
||||||
|
deduped_targets.add(target)
|
||||||
|
elif target.shared_inbox_uri not in shared_inboxes:
|
||||||
|
shared_inboxes.add(target.shared_inbox_uri)
|
||||||
|
deduped_targets.add(target)
|
||||||
|
else:
|
||||||
|
# Their shared inbox is already being sent to
|
||||||
|
pass
|
||||||
|
return deduped_targets
|
||||||
|
|
||||||
### ActivityPub (inbound) ###
|
### ActivityPub (inbound) ###
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import pytest
|
||||||
from asgiref.sync import async_to_sync
|
from asgiref.sync import async_to_sync
|
||||||
|
|
||||||
from activities.models import Post
|
from activities.models import Post
|
||||||
from users.models import Follow
|
from users.models import Domain, Follow, Identity
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
|
@ -50,6 +50,59 @@ def test_post_targets_simple(identity, other_identity, remote_identity):
|
||||||
assert targets == {other_identity}
|
assert targets == {other_identity}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_post_targets_shared(identity, other_identity):
|
||||||
|
"""
|
||||||
|
Tests that remote identities with the same shared inbox only get one target.
|
||||||
|
"""
|
||||||
|
# Create a pair of remote identities that share an inbox URI
|
||||||
|
domain = Domain.objects.create(domain="remote.test", local=False, state="updated")
|
||||||
|
remote1 = Identity.objects.create(
|
||||||
|
actor_uri="https://remote.test/test1/",
|
||||||
|
inbox_uri="https://remote.test/@test1/inbox/",
|
||||||
|
shared_inbox_uri="https://remote.test/inbox/",
|
||||||
|
profile_uri="https://remote.test/@test1/",
|
||||||
|
username="test1",
|
||||||
|
domain=domain,
|
||||||
|
name="Test1",
|
||||||
|
local=False,
|
||||||
|
state="updated",
|
||||||
|
)
|
||||||
|
remote2 = Identity.objects.create(
|
||||||
|
actor_uri="https://remote.test/test2/",
|
||||||
|
inbox_uri="https://remote.test/@test2/inbox/",
|
||||||
|
shared_inbox_uri="https://remote.test/inbox/",
|
||||||
|
profile_uri="https://remote.test/@test2/",
|
||||||
|
username="test2",
|
||||||
|
domain=domain,
|
||||||
|
name="Test2",
|
||||||
|
local=False,
|
||||||
|
state="updated",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make a post mentioning one local and two remote identities
|
||||||
|
post = Post.objects.create(
|
||||||
|
content="<p>Test</p>",
|
||||||
|
author=identity,
|
||||||
|
local=True,
|
||||||
|
)
|
||||||
|
post.mentions.add(other_identity)
|
||||||
|
post.mentions.add(remote1)
|
||||||
|
post.mentions.add(remote2)
|
||||||
|
targets = async_to_sync(post.aget_targets)()
|
||||||
|
|
||||||
|
# We should only have one of remote1 or remote2 in there as they share a
|
||||||
|
# shared inbox URI
|
||||||
|
assert (targets == {identity, other_identity, remote1}) or (
|
||||||
|
targets
|
||||||
|
== {
|
||||||
|
identity,
|
||||||
|
other_identity,
|
||||||
|
remote2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_post_local_only(identity, other_identity, remote_identity):
|
def test_post_local_only(identity, other_identity, remote_identity):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue