2 activities::{post::create_or_update::CreateOrUpdatePost, CreateOrUpdateType},
3 collections::CommunityContext,
4 context::lemmy_context,
6 objects::{person::ApubPerson, post::ApubPost},
10 chrono::NaiveDateTime,
11 collection::kind::OrderedCollectionType,
12 primitives::OneOrMany,
15 use lemmy_api_common::blocking;
18 traits::{ActivityHandler, ApubObject},
19 verify::verify_domains_match,
21 use lemmy_db_schema::{
22 source::{person::Person, post::Post},
25 use lemmy_utils::LemmyError;
26 use serde::{Deserialize, Serialize};
27 use serde_with::skip_serializing_none;
29 #[skip_serializing_none]
30 #[derive(Clone, Debug, Deserialize, Serialize)]
31 #[serde(rename_all = "camelCase")]
32 pub struct GroupOutbox {
33 #[serde(rename = "@context")]
34 context: OneOrMany<AnyBase>,
35 r#type: OrderedCollectionType,
37 ordered_items: Vec<CreateOrUpdatePost>,
40 #[derive(Clone, Debug)]
41 pub(crate) struct ApubCommunityOutbox(Vec<ApubPost>);
43 #[async_trait::async_trait(?Send)]
44 impl ApubObject for ApubCommunityOutbox {
45 type DataType = CommunityContext;
46 type TombstoneType = ();
47 type ApubType = GroupOutbox;
49 fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
53 async fn read_from_apub_id(
55 data: &Self::DataType,
56 ) -> Result<Option<Self>, LemmyError> {
57 // Only read from database if its a local community, otherwise fetch over http
59 let community_id = data.0.id;
60 let post_list: Vec<ApubPost> = blocking(data.1.pool(), move |conn| {
61 Post::list_for_community(conn, community_id)
67 Ok(Some(ApubCommunityOutbox(post_list)))
73 async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> {
74 // do nothing (it gets deleted automatically with the community)
78 async fn to_apub(&self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
79 let mut ordered_items = vec![];
81 let actor = post.creator_id;
82 let actor: ApubPerson = blocking(data.1.pool(), move |conn| Person::read(conn, actor))
86 CreateOrUpdatePost::new(post, &actor, &data.0, CreateOrUpdateType::Create, &data.1).await?;
87 ordered_items.push(a);
91 context: lemmy_context(),
92 r#type: OrderedCollectionType::OrderedCollection,
93 id: generate_outbox_url(&data.0.actor_id)?.into(),
98 fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError> {
99 // no tombstone for this, there is only a tombstone for the community
104 apub: &Self::ApubType,
105 data: &Self::DataType,
106 expected_domain: &Url,
107 request_counter: &mut i32,
108 ) -> Result<Self, LemmyError> {
109 verify_domains_match(expected_domain, &apub.id)?;
110 let mut outbox_activities = apub.ordered_items.clone();
111 if outbox_activities.len() > 20 {
112 outbox_activities = outbox_activities[0..20].to_vec();
115 // We intentionally ignore errors here. This is because the outbox might contain posts from old
116 // Lemmy versions, or from other software which we cant parse. In that case, we simply skip the
117 // item and only parse the ones that work.
118 for activity in outbox_activities {
120 .receive(&Data::new(data.1.clone()), request_counter)
125 // This return value is unused, so just set an empty vec
126 Ok(ApubCommunityOutbox { 0: vec![] })