diff --git a/app/activitypub.py b/app/activitypub.py index 6f174fd..3080649 100644 --- a/app/activitypub.py +++ b/app/activitypub.py @@ -34,6 +34,7 @@ AS_EXTENDED_CTX = [ "sensitive": "as:sensitive", "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", "alsoKnownAs": {"@id": "as:alsoKnownAs", "@type": "@id"}, + "movedTo": {"@id": "as:movedTo", "@type": "@id"}, # toot "toot": "http://joinmastodon.org/ns#", "featured": {"@id": "toot:featured", "@type": "@id"}, diff --git a/app/actor.py b/app/actor.py index 3766588..a8dc734 100644 --- a/app/actor.py +++ b/app/actor.py @@ -5,6 +5,7 @@ from functools import cached_property from typing import Union from urllib.parse import urlparse +from loguru import logger from sqlalchemy import select from sqlalchemy.orm import joinedload @@ -118,6 +119,10 @@ class Actor: def attachments(self) -> list[ap.RawObject]: return ap.as_list(self.ap_actor.get("attachment", [])) + @cached_property + def moved_to(self) -> str | None: + return self.ap_actor.get("movedTo") + @cached_property def server(self) -> str: return urlparse(self.ap_id).hostname # type: ignore @@ -214,6 +219,7 @@ class ActorMetadata: is_follow_request_sent: bool outbox_follow_ap_id: str | None inbox_follow_ap_id: str | None + moved_to: typing.Optional["ActorModel"] ActorsMetadata = dict[str, ActorMetadata] @@ -260,6 +266,19 @@ async def get_actors_metadata( for actor in actors: if not actor.ap_id: raise ValueError("Should never happen") + moved_to = None + if actor.moved_to: + try: + moved_to = await fetch_actor( + db_session, + actor.moved_to, + save_if_not_found=False, + ) + except ap.ObjectNotFoundError: + pass + except Exception: + logger.exception(f"Failed to fetch {actor.moved_to=}") + idx[actor.ap_id] = ActorMetadata( ap_actor_id=actor.ap_id, is_following=actor.ap_id in following, @@ -267,6 +286,7 @@ async def get_actors_metadata( is_follow_request_sent=actor.ap_id in sent_follow_requests, outbox_follow_ap_id=sent_follow_requests.get(actor.ap_id), inbox_follow_ap_id=followers.get(actor.ap_id), + moved_to=moved_to, ) return idx diff --git a/app/boxes.py b/app/boxes.py index d016778..27e1ae0 100644 --- a/app/boxes.py +++ b/app/boxes.py @@ -908,7 +908,7 @@ async def _handle_delete_activity( except ap.ObjectNotFoundError: pass - if ap_object_to_delete is None: + if ap_object_to_delete is None or not ap_object_to_delete.is_from_db: logger.info( "Received Delete for an unknown object " f"{delete_activity.activity_object_ap_id}" diff --git a/app/templates/utils.html b/app/templates/utils.html index 530140c..5147126 100644 --- a/app/templates/utils.html +++ b/app/templates/utils.html @@ -223,7 +223,7 @@ {% elif metadata.is_follow_request_sent %}
  • follow request sent
  • {{ admin_undo_button(metadata.outbox_follow_ap_id, "undo follow") }}
  • - {% else %} + {% elif not actor.moved_to %}
  • {{ admin_follow_button(actor) }}
  • {% endif %} {% if metadata.is_follower %} @@ -231,7 +231,11 @@ {% if not metadata.is_following and not with_details %}
  • {{ admin_profile_button(actor.ap_id) }}
  • {% endif %} - + {$ elif actor.is_from_db and not with_details %} +
  • {{ admin_profile_button(actor.ap_id) }}
  • + {% endif %} + {% if actor.moved_to %} +
  • has moved to {% if metadata.moved_to %}{{ metadata.moved_to.handle }}{% else %}{{ actor.moved_to }}{% endif %}
  • {% endif %} {% if actor.is_from_db %} {% if actor.is_blocked %}