forked from forks/microblog.pub
Improved Block support
This commit is contained in:
parent
d909bf93a0
commit
f50a233ce9
7 changed files with 147 additions and 3 deletions
28
app/actor.py
28
app/actor.py
|
@ -259,9 +259,11 @@ class ActorMetadata:
|
|||
is_following: bool
|
||||
is_follower: bool
|
||||
is_follow_request_sent: bool
|
||||
is_follow_request_rejected: bool
|
||||
outbox_follow_ap_id: str | None
|
||||
inbox_follow_ap_id: str | None
|
||||
moved_to: typing.Optional["ActorModel"]
|
||||
has_blocked_local_actor: bool
|
||||
|
||||
|
||||
ActorsMetadata = dict[str, ActorMetadata]
|
||||
|
@ -304,6 +306,26 @@ async def get_actors_metadata(
|
|||
)
|
||||
)
|
||||
}
|
||||
rejected_follow_requests = {
|
||||
reject.activity_object_ap_id
|
||||
for reject in await db_session.execute(
|
||||
select(models.InboxObject.activity_object_ap_id).where(
|
||||
models.InboxObject.ap_type == "Reject",
|
||||
models.InboxObject.ap_actor_id.in_(ap_actor_ids),
|
||||
)
|
||||
)
|
||||
}
|
||||
blocks = {
|
||||
block.ap_actor_id
|
||||
for block in await db_session.execute(
|
||||
select(models.InboxObject.ap_actor_id).where(
|
||||
models.InboxObject.ap_type == "Block",
|
||||
models.InboxObject.undone_by_inbox_object_id.is_(None),
|
||||
models.InboxObject.ap_actor_id.in_(ap_actor_ids),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
idx: ActorsMetadata = {}
|
||||
for actor in actors:
|
||||
if not actor.ap_id:
|
||||
|
@ -326,9 +348,15 @@ async def get_actors_metadata(
|
|||
is_following=actor.ap_id in following,
|
||||
is_follower=actor.ap_id in followers,
|
||||
is_follow_request_sent=actor.ap_id in sent_follow_requests,
|
||||
is_follow_request_rejected=bool(
|
||||
sent_follow_requests[actor.ap_id] in rejected_follow_requests
|
||||
)
|
||||
if actor.ap_id in sent_follow_requests
|
||||
else False,
|
||||
outbox_follow_ap_id=sent_follow_requests.get(actor.ap_id),
|
||||
inbox_follow_ap_id=followers.get(actor.ap_id),
|
||||
moved_to=moved_to,
|
||||
has_blocked_local_actor=actor.ap_id in blocks,
|
||||
)
|
||||
return idx
|
||||
|
||||
|
|
35
app/boxes.py
35
app/boxes.py
|
@ -1406,6 +1406,13 @@ async def _handle_undo_activity(
|
|||
inbox_object_id=ap_activity_to_undo.id,
|
||||
)
|
||||
db_session.add(notif)
|
||||
elif ap_activity_to_undo.ap_type == "Block":
|
||||
notif = models.Notification(
|
||||
notification_type=models.NotificationType.UNBLOCKED,
|
||||
actor_id=from_actor.id,
|
||||
inbox_object_id=ap_activity_to_undo.id,
|
||||
)
|
||||
db_session.add(notif)
|
||||
else:
|
||||
logger.warning(f"Don't know how to undo {ap_activity_to_undo.ap_type} activity")
|
||||
|
||||
|
@ -1945,6 +1952,28 @@ async def _handle_like_activity(
|
|||
db_session.add(notif)
|
||||
|
||||
|
||||
async def _handle_block_activity(
|
||||
db_session: AsyncSession,
|
||||
actor: models.Actor,
|
||||
block_activity: models.InboxObject,
|
||||
):
|
||||
if block_activity.activity_object_ap_id != LOCAL_ACTOR.ap_id:
|
||||
logger.warning(
|
||||
"Received invalid Block activity "
|
||||
f"{block_activity.activity_object_ap_id=}"
|
||||
)
|
||||
await db_session.delete(block_activity)
|
||||
return
|
||||
|
||||
# Create a notification
|
||||
notif = models.Notification(
|
||||
notification_type=models.NotificationType.BLOCKED,
|
||||
actor_id=actor.id,
|
||||
inbox_object_id=block_activity.id,
|
||||
)
|
||||
db_session.add(notif)
|
||||
|
||||
|
||||
async def _process_transient_object(
|
||||
db_session: AsyncSession,
|
||||
raw_object: ap.RawObject,
|
||||
|
@ -2210,7 +2239,11 @@ async def save_to_inbox(
|
|||
# View is used by Peertube, there's nothing useful we can do with it
|
||||
await db_session.delete(inbox_object)
|
||||
elif activity_ro.ap_type == "Block":
|
||||
pass
|
||||
await _handle_block_activity(
|
||||
db_session,
|
||||
actor,
|
||||
inbox_object,
|
||||
)
|
||||
else:
|
||||
logger.warning(f"Received an unknown {inbox_object.ap_type} object")
|
||||
|
||||
|
|
|
@ -551,6 +551,9 @@ class NotificationType(str, enum.Enum):
|
|||
UPDATED_WEBMENTION = "updated_webmention"
|
||||
DELETED_WEBMENTION = "deleted_webmention"
|
||||
|
||||
BLOCKED = "blocked"
|
||||
UNBLOCKED = "unblocked"
|
||||
|
||||
|
||||
class Notification(Base):
|
||||
__tablename__ = "notifications"
|
||||
|
|
|
@ -36,6 +36,12 @@
|
|||
{%- elif notif.notification_type.value == "follow_request_rejected" %}
|
||||
{{ notif_actor_action(notif, "rejected your follow request") }}
|
||||
{{ utils.display_actor(notif.actor, actors_metadata) }}
|
||||
{% elif notif.notification_type.value == "blocked" %}
|
||||
{{ notif_actor_action(notif, "blocked you") }}
|
||||
{{ utils.display_actor(notif.actor, actors_metadata) }}
|
||||
{% elif notif.notification_type.value == "unblocked" %}
|
||||
{{ notif_actor_action(notif, "unblocked you") }}
|
||||
{{ utils.display_actor(notif.actor, actors_metadata) }}
|
||||
{%- elif notif.notification_type.value == "move" %}
|
||||
{# for move notif, the actor is the target and the inbox object the Move activity #}
|
||||
<div class="actor-action">
|
||||
|
|
|
@ -216,6 +216,9 @@
|
|||
<div>
|
||||
<nav class="flexbox actor-metadata">
|
||||
<ul>
|
||||
{% if metadata.has_blocked_local_actor %}
|
||||
<li>blocked you</li>
|
||||
{% endif %}
|
||||
{% if metadata.is_following %}
|
||||
<li>already following</li>
|
||||
<li>{{ admin_undo_button(metadata.outbox_follow_ap_id, "unfollow")}}</li>
|
||||
|
@ -223,8 +226,15 @@
|
|||
<li>{{ admin_profile_button(actor.ap_id) }}</li>
|
||||
{% endif %}
|
||||
{% elif metadata.is_follow_request_sent %}
|
||||
<li>follow request sent</li>
|
||||
<li>{{ admin_undo_button(metadata.outbox_follow_ap_id, "undo follow") }}</li>
|
||||
{% if metadata.is_follow_request_rejected %}
|
||||
<li>follow request rejected</li>
|
||||
{% if not metadata.has_blocked_local_actor %}
|
||||
<li>{{ admin_follow_button(actor) }}</li>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<li>follow request sent</li>
|
||||
<li>{{ admin_undo_button(metadata.outbox_follow_ap_id, "undo follow") }}</li>
|
||||
{% endif %}
|
||||
{% elif not actor.moved_to %}
|
||||
<li>{{ admin_follow_button(actor) }}</li>
|
||||
{% endif %}
|
||||
|
|
|
@ -68,6 +68,20 @@ def build_accept_activity(
|
|||
}
|
||||
|
||||
|
||||
def build_block_activity(
|
||||
from_remote_actor: actor.RemoteActor,
|
||||
for_remote_actor: actor.RemoteActor,
|
||||
outbox_public_id: str | None = None,
|
||||
) -> ap.RawObject:
|
||||
return {
|
||||
"@context": ap.AS_CTX,
|
||||
"type": "Block",
|
||||
"id": from_remote_actor.ap_id + "/block/" + (outbox_public_id or uuid4().hex),
|
||||
"actor": from_remote_actor.ap_id,
|
||||
"object": for_remote_actor.ap_id,
|
||||
}
|
||||
|
||||
|
||||
def build_move_activity(
|
||||
from_remote_actor: actor.RemoteActor,
|
||||
for_remote_object: actor.RemoteActor,
|
||||
|
|
|
@ -423,3 +423,53 @@ def test_inbox__move_activity(
|
|||
).scalar_one()
|
||||
assert notif.actor.ap_id == new_ra.ap_id
|
||||
assert notif.inbox_object_id == inbox_activity.id
|
||||
|
||||
|
||||
def test_inbox__block_activity(
|
||||
db: Session,
|
||||
client: TestClient,
|
||||
respx_mock: respx.MockRouter,
|
||||
) -> None:
|
||||
# Given a remote actor
|
||||
ra = setup_remote_actor(respx_mock)
|
||||
|
||||
# Which is followed by the local actor
|
||||
setup_remote_actor_as_following(ra)
|
||||
|
||||
# When receiving a Block activity
|
||||
follow_activity = RemoteObject(
|
||||
factories.build_block_activity(
|
||||
from_remote_actor=ra,
|
||||
for_remote_actor=LOCAL_ACTOR,
|
||||
),
|
||||
ra,
|
||||
)
|
||||
with mock_httpsig_checker(ra):
|
||||
response = client.post(
|
||||
"/inbox",
|
||||
headers={"Content-Type": ap.AS_CTX},
|
||||
json=follow_activity.ap_object,
|
||||
)
|
||||
|
||||
# Then the server returns a 202
|
||||
assert response.status_code == 202
|
||||
|
||||
run_process_next_incoming_activity()
|
||||
|
||||
# And the actor was saved in DB
|
||||
saved_actor = db.execute(select(models.Actor)).scalar_one()
|
||||
assert saved_actor.ap_id == ra.ap_id
|
||||
|
||||
# And the Block activity was saved in the inbox
|
||||
inbox_activity = db.execute(
|
||||
select(models.InboxObject).where(models.InboxObject.ap_type == "Block")
|
||||
).scalar_one()
|
||||
|
||||
# And a notification was created
|
||||
notif = db.execute(
|
||||
select(models.Notification).where(
|
||||
models.Notification.notification_type == models.NotificationType.BLOCKED
|
||||
)
|
||||
).scalar_one()
|
||||
assert notif.actor.ap_id == ra.ap_id
|
||||
assert notif.inbox_object_id == inbox_activity.id
|
||||
|
|
Loading…
Reference in a new issue