Compare commits
1 Commits
widget-pha
...
04f7c5e85c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04f7c5e85c |
@@ -221,6 +221,11 @@ class FederationService(Protocol):
|
|||||||
self, session: AsyncSession, username: str,
|
self, session: AsyncSession, username: str,
|
||||||
) -> list[APFollowerDTO]: ...
|
) -> list[APFollowerDTO]: ...
|
||||||
|
|
||||||
|
async def get_followers_paginated(
|
||||||
|
self, session: AsyncSession, username: str,
|
||||||
|
page: int = 1, per_page: int = 20,
|
||||||
|
) -> tuple[list[RemoteActorDTO], int]: ...
|
||||||
|
|
||||||
async def add_follower(
|
async def add_follower(
|
||||||
self, session: AsyncSession, username: str,
|
self, session: AsyncSession, username: str,
|
||||||
follower_acct: str, follower_inbox: str, follower_actor_url: str,
|
follower_acct: str, follower_inbox: str, follower_actor_url: str,
|
||||||
@@ -283,6 +288,11 @@ class FederationService(Protocol):
|
|||||||
before: datetime | None = None, limit: int = 20,
|
before: datetime | None = None, limit: int = 20,
|
||||||
) -> list[TimelineItemDTO]: ...
|
) -> list[TimelineItemDTO]: ...
|
||||||
|
|
||||||
|
async def get_actor_timeline(
|
||||||
|
self, session: AsyncSession, remote_actor_id: int,
|
||||||
|
before: datetime | None = None, limit: int = 20,
|
||||||
|
) -> list[TimelineItemDTO]: ...
|
||||||
|
|
||||||
# -- Local posts ----------------------------------------------------------
|
# -- Local posts ----------------------------------------------------------
|
||||||
async def create_local_post(
|
async def create_local_post(
|
||||||
self, session: AsyncSession, actor_profile_id: int,
|
self, session: AsyncSession, actor_profile_id: int,
|
||||||
|
|||||||
@@ -376,6 +376,65 @@ class SqlFederationService:
|
|||||||
)
|
)
|
||||||
return result.rowcount > 0
|
return result.rowcount > 0
|
||||||
|
|
||||||
|
async def get_followers_paginated(
|
||||||
|
self, session: AsyncSession, username: str,
|
||||||
|
page: int = 1, per_page: int = 20,
|
||||||
|
) -> tuple[list[RemoteActorDTO], int]:
|
||||||
|
actor = (
|
||||||
|
await session.execute(
|
||||||
|
select(ActorProfile).where(ActorProfile.preferred_username == username)
|
||||||
|
)
|
||||||
|
).scalar_one_or_none()
|
||||||
|
if actor is None:
|
||||||
|
return [], 0
|
||||||
|
|
||||||
|
total = (
|
||||||
|
await session.execute(
|
||||||
|
select(func.count(APFollower.id)).where(
|
||||||
|
APFollower.actor_profile_id == actor.id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).scalar() or 0
|
||||||
|
|
||||||
|
offset = (page - 1) * per_page
|
||||||
|
followers = (
|
||||||
|
await session.execute(
|
||||||
|
select(APFollower)
|
||||||
|
.where(APFollower.actor_profile_id == actor.id)
|
||||||
|
.order_by(APFollower.created_at.desc())
|
||||||
|
.limit(per_page)
|
||||||
|
.offset(offset)
|
||||||
|
)
|
||||||
|
).scalars().all()
|
||||||
|
|
||||||
|
results: list[RemoteActorDTO] = []
|
||||||
|
for f in followers:
|
||||||
|
# Try to resolve from cached remote actors first
|
||||||
|
remote = (
|
||||||
|
await session.execute(
|
||||||
|
select(RemoteActor).where(
|
||||||
|
RemoteActor.actor_url == f.follower_actor_url,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).scalar_one_or_none()
|
||||||
|
if remote:
|
||||||
|
results.append(_remote_actor_to_dto(remote))
|
||||||
|
else:
|
||||||
|
# Synthesise a minimal DTO from follower data
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
domain = urlparse(f.follower_actor_url).netloc
|
||||||
|
results.append(RemoteActorDTO(
|
||||||
|
id=0,
|
||||||
|
actor_url=f.follower_actor_url,
|
||||||
|
inbox_url=f.follower_inbox,
|
||||||
|
preferred_username=f.follower_acct.split("@")[0] if "@" in f.follower_acct else f.follower_acct,
|
||||||
|
domain=domain,
|
||||||
|
display_name=None,
|
||||||
|
summary=None,
|
||||||
|
icon_url=None,
|
||||||
|
))
|
||||||
|
return results, total
|
||||||
|
|
||||||
# -- Remote actors --------------------------------------------------------
|
# -- Remote actors --------------------------------------------------------
|
||||||
|
|
||||||
async def get_or_fetch_remote_actor(
|
async def get_or_fetch_remote_actor(
|
||||||
@@ -966,6 +1025,46 @@ class SqlFederationService:
|
|||||||
))
|
))
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
async def get_actor_timeline(
|
||||||
|
self, session: AsyncSession, remote_actor_id: int,
|
||||||
|
before: datetime | None = None, limit: int = 20,
|
||||||
|
) -> list[TimelineItemDTO]:
|
||||||
|
remote_actor = (
|
||||||
|
await session.execute(
|
||||||
|
select(RemoteActor).where(RemoteActor.id == remote_actor_id)
|
||||||
|
)
|
||||||
|
).scalar_one_or_none()
|
||||||
|
if not remote_actor:
|
||||||
|
return []
|
||||||
|
|
||||||
|
q = (
|
||||||
|
select(APRemotePost)
|
||||||
|
.where(APRemotePost.remote_actor_id == remote_actor_id)
|
||||||
|
)
|
||||||
|
if before:
|
||||||
|
q = q.where(APRemotePost.published < before)
|
||||||
|
q = q.order_by(APRemotePost.published.desc()).limit(limit)
|
||||||
|
|
||||||
|
posts = (await session.execute(q)).scalars().all()
|
||||||
|
return [
|
||||||
|
TimelineItemDTO(
|
||||||
|
id=f"remote:{p.id}",
|
||||||
|
post_type="remote",
|
||||||
|
content=p.content or "",
|
||||||
|
published=p.published,
|
||||||
|
actor_name=remote_actor.display_name or remote_actor.preferred_username,
|
||||||
|
actor_username=remote_actor.preferred_username,
|
||||||
|
object_id=p.object_id,
|
||||||
|
summary=p.summary,
|
||||||
|
url=p.url,
|
||||||
|
actor_domain=remote_actor.domain,
|
||||||
|
actor_icon=remote_actor.icon_url,
|
||||||
|
actor_url=remote_actor.actor_url,
|
||||||
|
author_inbox=remote_actor.inbox_url,
|
||||||
|
)
|
||||||
|
for p in posts
|
||||||
|
]
|
||||||
|
|
||||||
# -- Local posts ----------------------------------------------------------
|
# -- Local posts ----------------------------------------------------------
|
||||||
|
|
||||||
async def create_local_post(
|
async def create_local_post(
|
||||||
|
|||||||
@@ -239,6 +239,9 @@ class StubFederationService:
|
|||||||
async def get_following(self, session, username, page=1, per_page=20):
|
async def get_following(self, session, username, page=1, per_page=20):
|
||||||
return [], 0
|
return [], 0
|
||||||
|
|
||||||
|
async def get_followers_paginated(self, session, username, page=1, per_page=20):
|
||||||
|
return [], 0
|
||||||
|
|
||||||
async def accept_follow_response(self, session, local_username, remote_actor_url):
|
async def accept_follow_response(self, session, local_username, remote_actor_url):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -260,6 +263,9 @@ class StubFederationService:
|
|||||||
async def get_public_timeline(self, session, before=None, limit=20):
|
async def get_public_timeline(self, session, before=None, limit=20):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
async def get_actor_timeline(self, session, remote_actor_id, before=None, limit=20):
|
||||||
|
return []
|
||||||
|
|
||||||
async def create_local_post(self, session, actor_profile_id, content, visibility="public", in_reply_to=None):
|
async def create_local_post(self, session, actor_profile_id, content, visibility="public", in_reply_to=None):
|
||||||
raise RuntimeError("FederationService not available")
|
raise RuntimeError("FederationService not available")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user