foxhole/app/actor.py
2023-04-01 17:17:01 +08:00

91 lines
2.2 KiB
Python

#!/usr/bin/env python3
import typing
import json
from loguru import logger
from app.database import AsyncSession
from app import models
from sqlalchemy import select
from urllib.parse import urlparse
if typing.TYPE_CHECKING:
from app.models import Actor as ActorModel
import app.activitypub as ap
class BaseActor:
def __init__(self, ap_actor: ap.RawObject) -> None:
if (ap_type := ap_actor.get("type")) not in ap.ACTOR_TYPES:
raise ValueError(f"Unexpected actor type: {ap_type}")
self._ap_actor = ap_actor
self._ap_type : str = ap_type # type: ignore
@property
def ap_actor(self) -> ap.RawObject:
return self._ap_actor
@property
def inbox_url(self) -> str:
return self.ap_actor["inbox"]
@property
def ap_type(self) -> str:
return self._ap_type
async def fetch_actor(
db_session : AsyncSession,
actor_id : str,
) -> "ActorModel":
exist_actor = (
await db_session.scalars(
select(models.Actor).where(
models.Actor.ap_id == actor_id
)
)
).one_or_none()
if not exist_actor:
ap_object = await ap.fetch(actor_id)
exist_actor = await save_actor(ap_object, db_session)
return exist_actor
else:
try:
_actor = await ap.fetch(actor_id)
exist_actor = await save_actor(_actor, db_session)
except json.JSONDecodeError:
raise ValueError
except KeyError:
logger.warning("actor gone? ")
raise KeyError
return exist_actor
async def save_actor(
ap_object : dict,
db_session : AsyncSession
) -> "ActorModel":
logger.info("save actor " + ap_object["id"])
actor = models.Actor(
ap_id=ap_object["id"],
ap_actor=ap_object,
ap_type=ap_object["type"],
handle=_handle(ap_object),
)
db_session.add(actor)
await db_session.flush()
await db_session.refresh(actor)
return actor
def _handle (
ap_object :dict
) -> str:
ap_id = urlparse(ap_object["id"])
if not ap_id.hostname:
raise ValueError(f"Invalid actor ID {ap_id}")
handle = '@' + ap_object["preferredUsername"] + '@' + ap_id.hostname
return handle