From 1ddb6a3954e3573b8810a7ab0b0c216c5115b031 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Thu, 14 May 2026 22:13:39 +0200 Subject: [PATCH] feat(activitypub-base): impl fetch_outbox_page; populate all RemoteActor fields in lookup_actor --- .../adapters/activitypub-base/src/service.rs | 73 ++++++++++++++++--- 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/crates/adapters/activitypub-base/src/service.rs b/crates/adapters/activitypub-base/src/service.rs index d714397..73a4c5d 100644 --- a/crates/adapters/activitypub-base/src/service.rs +++ b/crates/adapters/activitypub-base/src/service.rs @@ -1394,11 +1394,15 @@ impl domain::ports::FederationActionPort for ActivityPubService { public_key: actor.public_key_pem.clone(), avatar_url: actor.avatar_url.as_ref().map(|u| u.to_string()), last_fetched_at: actor.last_refreshed_at, - bio: None, - banner_url: None, - also_known_as: None, - outbox_url: None, - attachment: vec![], + bio: actor.bio.clone(), + banner_url: actor.banner_url.as_ref().map(|u| u.to_string()), + also_known_as: actor.also_known_as.clone(), + outbox_url: Some(actor.outbox_url.to_string()), + attachment: actor + .attachment + .iter() + .map(|f| (f.name.clone(), f.value.clone())) + .collect(), }) } @@ -1519,12 +1523,61 @@ impl domain::ports::FederationActionPort for ActivityPubService { async fn fetch_outbox_page( &self, - _outbox_url: &str, - _page: u32, + outbox_url: &str, + page: u32, ) -> Result, domain::errors::DomainError> { - Err(domain::errors::DomainError::Internal( - "not implemented".into(), - )) + use chrono::DateTime; + + let url = format!("{}?page={}", outbox_url, page); + let resp: serde_json::Value = reqwest::Client::new() + .get(&url) + .header("Accept", "application/activity+json, application/ld+json") + .send() + .await + .map_err(|e| domain::errors::DomainError::ExternalService(e.to_string()))? + .json() + .await + .map_err(|e| domain::errors::DomainError::ExternalService(e.to_string()))?; + + let empty = vec![]; + let items = resp["orderedItems"].as_array().unwrap_or(&empty); + + let notes = items + .iter() + .filter_map(|item| { + // Items are Create activities wrapping a Note, or Notes directly + let note = if item["type"].as_str() == Some("Create") { + &item["object"] + } else if item["type"].as_str() == Some("Note") { + item + } else { + return None; + }; + + // Only public notes + let to = note["to"].as_array()?; + let is_public = to + .iter() + .any(|t| t.as_str() == Some("https://www.w3.org/ns/activitystreams#Public")); + if !is_public { + return None; + } + + let published = DateTime::parse_from_rfc3339(note["published"].as_str()?) + .ok()? + .with_timezone(&chrono::Utc); + + Some(domain::models::remote_note::RemoteNote { + ap_id: note["id"].as_str()?.to_string(), + content: note["content"].as_str().unwrap_or("").to_string(), + published, + sensitive: note["sensitive"].as_bool().unwrap_or(false), + content_warning: note["summary"].as_str().map(|s| s.to_string()), + }) + }) + .collect(); + + Ok(notes) } }