forked from forks/microblog.pub
94 lines
3 KiB
Python
94 lines
3 KiB
Python
import logging
|
|
from urllib.parse import urlparse
|
|
|
|
import requests
|
|
from Crypto.PublicKey import RSA
|
|
|
|
from .errors import ActivityNotFoundError
|
|
from .urlutils import check_url
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class NotAnActorError(Exception):
|
|
def __init__(self, activity):
|
|
self.activity = activity
|
|
|
|
|
|
class ActorService(object):
|
|
def __init__(self, user_agent, col, actor_id, actor_data, instances):
|
|
logger.debug(f"Initializing ActorService user_agent={user_agent}")
|
|
self._user_agent = user_agent
|
|
self._col = col
|
|
self._in_mem = {actor_id: actor_data}
|
|
self._instances = instances
|
|
self._known_instances = set()
|
|
|
|
def _fetch(self, actor_url):
|
|
logger.debug(f"fetching remote object {actor_url}")
|
|
|
|
check_url(actor_url)
|
|
|
|
resp = requests.get(
|
|
actor_url,
|
|
headers={
|
|
"Accept": "application/activity+json",
|
|
"User-Agent": self._user_agent,
|
|
},
|
|
)
|
|
if resp.status_code == 404:
|
|
raise ActivityNotFoundError(
|
|
f"{actor_url} cannot be fetched, 404 not found error"
|
|
)
|
|
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
def get(self, actor_url, reload_cache=False):
|
|
logger.info(f"get actor {actor_url} (reload_cache={reload_cache})")
|
|
|
|
if actor_url in self._in_mem:
|
|
return self._in_mem[actor_url]
|
|
|
|
instance = urlparse(actor_url)._replace(path="", query="", fragment="").geturl()
|
|
if instance not in self._known_instances:
|
|
self._known_instances.add(instance)
|
|
if not self._instances.find_one({"instance": instance}):
|
|
self._instances.insert(
|
|
{"instance": instance, "first_object": actor_url}
|
|
)
|
|
|
|
if reload_cache:
|
|
actor = self._fetch(actor_url)
|
|
self._in_mem[actor_url] = actor
|
|
self._col.update(
|
|
{"actor_id": actor_url},
|
|
{"$set": {"cached_response": actor}},
|
|
upsert=True,
|
|
)
|
|
return actor
|
|
|
|
cached_actor = self._col.find_one({"actor_id": actor_url})
|
|
if cached_actor:
|
|
return cached_actor["cached_response"]
|
|
|
|
actor = self._fetch(actor_url)
|
|
if not "type" in actor:
|
|
raise NotAnActorError(None)
|
|
if actor["type"] != "Person":
|
|
raise NotAnActorError(actor)
|
|
|
|
self._col.update(
|
|
{"actor_id": actor_url}, {"$set": {"cached_response": actor}}, upsert=True
|
|
)
|
|
self._in_mem[actor_url] = actor
|
|
return actor
|
|
|
|
def get_public_key(self, actor_url, reload_cache=False):
|
|
profile = self.get(actor_url, reload_cache=reload_cache)
|
|
pub = profile["publicKey"]
|
|
return pub["id"], RSA.importKey(pub["publicKeyPem"])
|
|
|
|
def get_inbox_url(self, actor_url, reload_cache=False):
|
|
profile = self.get(actor_url, reload_cache=reload_cache)
|
|
return profile.get("inbox")
|