]> Untitled Git - lemmy.git/blob - crates/apub/src/collections/community_outbox.rs
Rewrite community moderators collection
[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, chrono::NaiveDateTime, collection::kind::OrderedCollectionType,
10   primitives::OneOrMany, url::Url,
11 };
12 use lemmy_api_common::blocking;
13 use lemmy_apub_lib::{
14   data::Data,
15   traits::{ActivityHandler, ApubObject},
16   verify::verify_domains_match,
17 };
18 use lemmy_db_schema::{
19   source::{person::Person, post::Post},
20   traits::Crud,
21 };
22 use lemmy_utils::LemmyError;
23 use serde::{Deserialize, Serialize};
24 use serde_with::skip_serializing_none;
25
26 #[skip_serializing_none]
27 #[derive(Clone, Debug, Deserialize, Serialize)]
28 #[serde(rename_all = "camelCase")]
29 pub struct GroupOutbox {
30   #[serde(rename = "@context")]
31   context: OneOrMany<AnyBase>,
32   r#type: OrderedCollectionType,
33   id: Url,
34   ordered_items: Vec<CreateOrUpdatePost>,
35 }
36
37 #[derive(Clone, Debug)]
38 pub(crate) struct ApubCommunityOutbox(Vec<ApubPost>);
39
40 #[async_trait::async_trait(?Send)]
41 impl ApubObject for ApubCommunityOutbox {
42   type DataType = CommunityContext;
43   type TombstoneType = ();
44   type ApubType = GroupOutbox;
45
46   fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
47     None
48   }
49
50   async fn read_from_apub_id(
51     _object_id: Url,
52     data: &Self::DataType,
53   ) -> Result<Option<Self>, LemmyError> {
54     // Only read from database if its a local community, otherwise fetch over http
55     if data.0.local {
56       let community_id = data.0.id;
57       let post_list: Vec<ApubPost> = blocking(data.1.pool(), move |conn| {
58         Post::list_for_community(conn, community_id)
59       })
60       .await??
61       .into_iter()
62       .map(Into::into)
63       .collect();
64       Ok(Some(ApubCommunityOutbox(post_list)))
65     } else {
66       Ok(None)
67     }
68   }
69
70   async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> {
71     // do nothing (it gets deleted automatically with the community)
72     Ok(())
73   }
74
75   async fn to_apub(&self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
76     let mut ordered_items = vec![];
77     for post in &self.0 {
78       let actor = post.creator_id;
79       let actor: ApubPerson = blocking(data.1.pool(), move |conn| Person::read(conn, actor))
80         .await??
81         .into();
82       let a =
83         CreateOrUpdatePost::new(post, &actor, &data.0, CreateOrUpdateType::Create, &data.1).await?;
84       ordered_items.push(a);
85     }
86
87     Ok(GroupOutbox {
88       context: lemmy_context(),
89       r#type: OrderedCollectionType::OrderedCollection,
90       id: generate_outbox_url(&data.0.actor_id)?.into(),
91       ordered_items,
92     })
93   }
94
95   fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError> {
96     // no tombstone for this, there is only a tombstone for the community
97     unimplemented!()
98   }
99
100   async fn from_apub(
101     apub: &Self::ApubType,
102     data: &Self::DataType,
103     expected_domain: &Url,
104     request_counter: &mut i32,
105   ) -> Result<Self, LemmyError> {
106     verify_domains_match(expected_domain, &apub.id)?;
107     let mut outbox_activities = apub.ordered_items.clone();
108     if outbox_activities.len() > 20 {
109       outbox_activities = outbox_activities[0..20].to_vec();
110     }
111
112     // We intentionally ignore errors here. This is because the outbox might contain posts from old
113     // Lemmy versions, or from other software which we cant parse. In that case, we simply skip the
114     // item and only parse the ones that work.
115     for activity in outbox_activities {
116       activity
117         .receive(&Data::new(data.1.clone()), request_counter)
118         .await
119         .ok();
120     }
121
122     // This return value is unused, so just set an empty vec
123     Ok(ApubCommunityOutbox { 0: vec![] })
124   }
125 }