diff --git a/daemon.py b/daemon.py index e21f25203..48917cd17 100644 --- a/daemon.py +++ b/daemon.py @@ -120,6 +120,7 @@ from inbox import save_post_to_inbox_queue from inbox import populate_replies from inbox import receive_edit_to_post from follow import get_followers_sync_json +from follow import get_followers_sync_hash from follow import follower_approval_active from follow import is_following_actor from follow import get_following_feed @@ -16917,12 +16918,24 @@ class PubServer(BaseHTTPRequestHandler): # check authorized fetch if self._secure_mode(curr_session, proxy_type): nickname = get_nickname_from_actor(self.path) - sync_json = \ - get_followers_sync_json(self.server.base_dir, - nickname, self.server.domain, - self.server.http_prefix, - self.server.domain_full, - calling_domain) + foll_sync_key = nickname + ':' + calling_domain + sync_dict = self.server.followers_sync_cache + if sync_dict.get(foll_sync_key): + sync_hash = sync_dict[foll_sync_key]['hash'] + sync_json = sync_dict[foll_sync_key]['response'] + else: + sync_json = \ + get_followers_sync_json(self.server.base_dir, + nickname, self.server.domain, + self.server.http_prefix, + self.server.domain_full, + calling_domain) + sync_hash = get_followers_sync_hash(sync_json) + if sync_hash: + self.server.followers_sync_cache[foll_sync_key] = { + "hash": sync_hash, + "response": sync_json + } msg_str = json.dumps(sync_json, ensure_ascii=False) msg_str = self._convert_domains(calling_domain, referer_domain, msg_str) @@ -23382,6 +23395,9 @@ def run_daemon(max_hashtags: int, # lock for followers synchronization httpd.followers_synchronization = False + # cache containing followers synchronization hashes and json + httpd.followers_sync_cache = {} + # permitted sites from which the buy button may be displayed httpd.buy_sites = load_buy_sites(base_dir) diff --git a/follow.py b/follow.py index fe93fdb41..5a576c5f7 100644 --- a/follow.py +++ b/follow.py @@ -9,6 +9,7 @@ __module_group__ = "ActivityPub" from pprint import pprint import os +from hashlib import sha256 from utils import get_user_paths from utils import acct_handle_dir from utils import has_object_string_object @@ -1401,6 +1402,7 @@ def _get_followers_for_domain(base_dir: str, break elif '://' + search_domain in line_str: result.append(line_str) + result.sort() return result @@ -1426,6 +1428,24 @@ def get_followers_sync_json(base_dir: str, return sync_json +def get_followers_sync_hash(sync_json: {}) -> str: + """Returns a hash used within the Collection-Synchronization http header + See https://github.com/mastodon/mastodon/pull/14510 + """ + if not sync_json: + return None + sync_hash = None + for actor in sync_json['orderedItems']: + actor_hash = sha256(actor.encode('utf-8')) + if sync_hash: + sync_hash = sync_hash ^ actor_hash + else: + sync_hash = actor_hash + if sync_hash: + sync_hash = sync_hash.hexdigest() + return sync_hash + + def get_followers_of_actor(base_dir: str, actor: str, debug: bool) -> {}: """In a shared inbox if we receive a post we know who it's from and if it's addressed to followers then we need to get a list of those.