Improve Delete handling

This commit is contained in:
Thomas Sileo 2022-07-29 09:24:36 +02:00
parent 9d6a3ea3cf
commit 9c325674c6

View file

@ -629,12 +629,20 @@ async def _get_followers(db_session: AsyncSession) -> list[models.Follower]:
) )
async def _get_followers_recipients(db_session: AsyncSession) -> set[str]: async def _get_followers_recipients(
db_session: AsyncSession,
skip_actors: list[models.Actor] | None = None,
) -> set[str]:
"""Returns all the recipients from the local follower collection.""" """Returns all the recipients from the local follower collection."""
actor_ap_ids_to_skip = []
if skip_actors:
actor_ap_ids_to_skip = [actor.ap_id for actor in skip_actors]
followers = await _get_followers(db_session) followers = await _get_followers(db_session)
return { return {
follower.actor.shared_inbox_url or follower.actor.inbox_url # type: ignore follower.actor.shared_inbox_url or follower.actor.inbox_url # type: ignore
for follower in followers for follower in followers
if follower.actor.ap_id not in actor_ap_ids_to_skip
} }
@ -715,6 +723,7 @@ async def _handle_delete_activity(
from_actor: models.Actor, from_actor: models.Actor,
delete_activity: models.InboxObject, delete_activity: models.InboxObject,
ap_object_to_delete: models.InboxObject | None, ap_object_to_delete: models.InboxObject | None,
forwarded_by_actor: models.Actor | None,
) -> None: ) -> None:
if ap_object_to_delete is None: if ap_object_to_delete is None:
logger.info( logger.info(
@ -731,33 +740,26 @@ async def _handle_delete_activity(
) )
return return
# If it's a local replies, it was forwarded, so we also need to forward
# the Delete activity if possible
if (
delete_activity.has_ld_signature
and ap_object_to_delete.in_reply_to
and ap_object_to_delete.in_reply_to.startswith(BASE_URL)
):
logger.info("Forwarding Delete activity as it's a local reply")
recipients = await _get_followers_recipients(db_session)
for rcp in recipients:
await new_outgoing_activity(
db_session,
rcp,
outbox_object_id=None,
inbox_object_id=delete_activity.id,
)
logger.info(f"Deleting {ap_object_to_delete.ap_type}/{ap_object_to_delete.ap_id}") logger.info(f"Deleting {ap_object_to_delete.ap_type}/{ap_object_to_delete.ap_id}")
await _revert_side_effect_for_deleted_object(
db_session,
delete_activity,
ap_object_to_delete,
forwarded_by_actor,
)
ap_object_to_delete.is_deleted = True ap_object_to_delete.is_deleted = True
await _revert_side_effect_for_deleted_object(db_session, ap_object_to_delete) await db_session.flush()
async def _revert_side_effect_for_deleted_object( async def _revert_side_effect_for_deleted_object(
db_session: AsyncSession, db_session: AsyncSession,
delete_activity: models.InboxObject,
deleted_ap_object: models.InboxObject, deleted_ap_object: models.InboxObject,
forwarded_by_actor: models.Actor | None,
) -> None: ) -> None:
is_delete_needs_to_be_forwarded = False
# Decrement the replies counter if needed # Decrement the replies counter if needed
if deleted_ap_object.in_reply_to: if deleted_ap_object.in_reply_to:
replied_object = await get_anybox_object_by_ap_id( replied_object = await get_anybox_object_by_ap_id(
@ -766,6 +768,10 @@ async def _revert_side_effect_for_deleted_object(
) )
if replied_object: if replied_object:
if replied_object.is_from_outbox: if replied_object.is_from_outbox:
# It's a local reply that was likely forwarded, the Delete
# also needs to be forwarded
is_delete_needs_to_be_forwarded = True
await db_session.execute( await db_session.execute(
update(models.OutboxObject) update(models.OutboxObject)
.where( .where(
@ -782,6 +788,36 @@ async def _revert_side_effect_for_deleted_object(
.values(replies_count=models.InboxObject.replies_count - 1) .values(replies_count=models.InboxObject.replies_count - 1)
) )
# Delete any Like/Announce
await db_session.execute(
update(models.OutboxObject)
.where(
models.OutboxObject.activity_object_ap_id == deleted_ap_object.ap_id,
)
.values(is_deleted=True)
)
# If it's a local replies, it was forwarded, so we also need to forward
# the Delete activity if possible
if delete_activity.has_ld_signature and is_delete_needs_to_be_forwarded:
logger.info("Forwarding Delete activity as it's a local reply")
# Don't forward to the forwarding actor and the original Delete actor
skip_actors = [delete_activity.actor]
if forwarded_by_actor:
skip_actors.append(forwarded_by_actor)
recipients = await _get_followers_recipients(
db_session,
skip_actors=skip_actors,
)
for rcp in recipients:
await new_outgoing_activity(
db_session,
rcp,
outbox_object_id=None,
inbox_object_id=delete_activity.id,
)
async def _handle_follow_follow_activity( async def _handle_follow_follow_activity(
db_session: AsyncSession, db_session: AsyncSession,
@ -1222,12 +1258,15 @@ async def save_to_inbox(
return None return None
raw_object_id = ap.get_id(raw_object) raw_object_id = ap.get_id(raw_object)
forwarded_by_actor = None
# Ensure forwarded activities have a valid LD sig # Ensure forwarded activities have a valid LD sig
if sent_by_ap_actor_id != actor.ap_id: if sent_by_ap_actor_id != actor.ap_id:
logger.info( logger.info(
f"Processing a forwarded activity {sent_by_ap_actor_id=}/{actor.ap_id}" f"Processing a forwarded activity {sent_by_ap_actor_id=}/{actor.ap_id}"
) )
forwarded_by_actor = await fetch_actor(db_session, sent_by_ap_actor_id)
if not (await ldsig.verify_signature(db_session, raw_object)): if not (await ldsig.verify_signature(db_session, raw_object)):
logger.warning( logger.warning(
f"Failed to verify LD sig, fetching remote object {raw_object_id}" f"Failed to verify LD sig, fetching remote object {raw_object_id}"
@ -1314,6 +1353,7 @@ async def save_to_inbox(
actor, actor,
inbox_object, inbox_object,
relates_to_inbox_object, relates_to_inbox_object,
forwarded_by_actor=forwarded_by_actor,
) )
elif activity_ro.ap_type == "Follow": elif activity_ro.ap_type == "Follow":
await _handle_follow_follow_activity(db_session, actor, inbox_object) await _handle_follow_follow_activity(db_session, actor, inbox_object)