]> Untitled Git - lemmy.git/blob - crates/apub/src/collections/community_outbox.rs
When receiving activity, dont read community from cc (for pleroma compat and better...
[lemmy.git] / crates / apub / src / collections / community_outbox.rs
1 use crate::{
2   activities::{post::create_or_update::CreateOrUpdatePost, CreateOrUpdateType},
3   collections::CommunityContext,
4   context::lemmy_context,
5   generate_outbox_url,
6   objects::{person::ApubPerson, post::ApubPost},
7 };
8 use activitystreams::{
9   base::AnyBase,
10   chrono::NaiveDateTime,
11   collection::kind::OrderedCollectionType,
12   primitives::OneOrMany,
13   url::Url,
14 };
15 use lemmy_api_common::blocking;
16 use lemmy_apub_lib::{
17   data::Data,
18   traits::{ActivityHandler, ApubObject},
19   verify::verify_domains_match,
20 };
21 use lemmy_db_schema::{
22   source::{person::Person, post::Post},
23   traits::Crud,
24 };
25 use lemmy_utils::LemmyError;
26 use serde::{Deserialize, Serialize};
27 use serde_with::skip_serializing_none;
28
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,
36   id: Url,
37   ordered_items: Vec<CreateOrUpdatePost>,
38 }
39
40 #[derive(Clone, Debug)]
41 pub(crate) struct ApubCommunityOutbox(Vec<ApubPost>);
42
43 #[async_trait::async_trait(?Send)]
44 impl ApubObject for ApubCommunityOutbox {
45   type DataType = CommunityContext;
46   type TombstoneType = ();
47   type ApubType = GroupOutbox;
48
49   fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
50     None
51   }
52
53   async fn read_from_apub_id(
54     _object_id: Url,
55     data: &Self::DataType,
56   ) -> Result<Option<Self>, LemmyError> {
57     // Only read from database if its a local community, otherwise fetch over http
58     if data.0.local {
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)
62       })
63       .await??
64       .into_iter()
65       .map(Into::into)
66       .collect();
67       Ok(Some(ApubCommunityOutbox(post_list)))
68     } else {
69       Ok(None)
70     }
71   }
72
73   async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> {
74     // do nothing (it gets deleted automatically with the community)
75     Ok(())
76   }
77
78   async fn to_apub(&self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
79     let mut ordered_items = vec![];
80     for post in &self.0 {
81       let actor = post.creator_id;
82       let actor: ApubPerson = blocking(data.1.pool(), move |conn| Person::read(conn, actor))
83         .await??
84         .into();
85       let a =
86         CreateOrUpdatePost::new(post, &actor, &data.0, CreateOrUpdateType::Create, &data.1).await?;
87       ordered_items.push(a);
88     }
89
90     Ok(GroupOutbox {
91       context: lemmy_context(),
92       r#type: OrderedCollectionType::OrderedCollection,
93       id: generate_outbox_url(&data.0.actor_id)?.into(),
94       ordered_items,
95     })
96   }
97
98   fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError> {
99     // no tombstone for this, there is only a tombstone for the community
100     unimplemented!()
101   }
102
103   async fn from_apub(
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();
113     }
114
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 {
119       activity
120         .receive(&Data::new(data.1.clone()), request_counter)
121         .await
122         .ok();
123     }
124
125     // This return value is unused, so just set an empty vec
126     Ok(ApubCommunityOutbox { 0: vec![] })
127   }
128 }