diff --git a/activitypub.py b/activitypub.py index 9a1d0b8..ddd260a 100644 --- a/activitypub.py +++ b/activitypub.py @@ -32,13 +32,24 @@ logger = logging.getLogger(__name__) ACTORS_CACHE = LRUCache(maxsize=256) -def _actor_to_meta(actor: ap.BaseActivity) -> Dict[str, Any]: - return { +def _actor_to_meta(actor: ap.BaseActivity, with_inbox=False) -> Dict[str, Any]: + meta = { + "id": actor.id, "url": actor.url, "icon": actor.icon, "name": actor.name, "preferredUsername": actor.preferredUsername, } + if with_inbox: + meta.update( + { + "inbox": actor.inbox, + "sharedInbox": actor._data.get("endpoints", {}).get("sharedInbox"), + } + ) + logger.debug(f"meta={meta}") + + return meta def _remove_id(doc: ap.ObjectType) -> ap.ObjectType: @@ -120,6 +131,20 @@ class MicroblogPubBackend(Backend): } return [doc["activity"]["actor"] for doc in DB.activities.find(q)] + def followers_as_recipients(self) -> List[str]: + q = { + "box": Box.INBOX.value, + "type": ap.ActivityType.FOLLOW.value, + "meta.undo": False, + } + recipients = [] + for doc in DB.activities.find(q): + recipients.append( + doc["meta"]["actor"]["sharedInbox"] or doc["meta"]["actor"]["inbox"] + ) + + return list(set(recipients)) + def following(self) -> List[str]: q = { "box": Box.OUTBOX.value, diff --git a/app.py b/app.py index 91b6ab0..7b99cc1 100644 --- a/app.py +++ b/app.py @@ -689,6 +689,7 @@ def tmp_migrate5(): def tmp_migrate6(): for activity in DB.activities.find(): # tasks.cache_actor.delay(activity["remote_id"], also_cache_attachments=False) + try: a = ap.parse_activity(activity["activity"]) if a.has_type([ActivityType.LIKE, ActivityType.FOLLOW]): diff --git a/tasks.py b/tasks.py index f777820..2687545 100644 --- a/tasks.py +++ b/tasks.py @@ -54,7 +54,7 @@ def process_new_activity(self, iri: str) -> None: ) and activity.is_public(): # The reply is public "local reply", forward the reply (i.e. the original activity) to the original # recipients - activity.forward(back.followers()) + activity.forward(back.followers_as_recipients()) # (partial) Ghost replies handling # [X] This is the first time the server has seen this Activity. @@ -70,7 +70,7 @@ def process_new_activity(self, iri: str) -> None: should_forward = False if should_forward: - activity.forward(back.followers()) + activity.forward(back.followers_as_recipients()) else: tag_stream = True @@ -96,10 +96,34 @@ def cache_actor(self, iri: str, also_cache_attachments: bool = True) -> None: actor = activity.get_actor() + cache_actor_with_inbox = False + if activity.has_type(ap.ActivityType.FOLLOW): + if actor.id != ID: + # It's a Follow from the Inbox + cache_actor_with_inbox = True + else: + # It's a new following, cache the "object" (which is the actor we follow) + DB.activities.update_one( + {"remote_id": iri}, + { + "$set": { + "meta.object": activitypub._actor_to_meta( + activity.get_object() + ) + } + }, + ) + # Cache the actor info DB.activities.update_one( {"remote_id": iri}, - {"$set": {"meta.actor": activitypub._actor_to_meta(actor)}}, + { + "$set": { + "meta.actor": activitypub._actor_to_meta( + actor, cache_actor_with_inbox + ) + } + }, ) log.info(f"actor cached for {iri}") diff --git a/utils/lookup.py b/utils/lookup.py index abd0d6f..50267fb 100644 --- a/utils/lookup.py +++ b/utils/lookup.py @@ -3,10 +3,16 @@ import json import little_boxes.activitypub as ap import mf2py import requests +from little_boxes.webfinger import get_actor_url def lookup(url: str) -> ap.BaseActivity: """Try to find an AP object related to the given URL.""" + try: + return ap.fetch_remote_activity(get_actor_url(url)) + except Exception: + pass + backend = ap.get_backend() resp = requests.get( url,