]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/community/announce.rs
Federate with Peertube (#2244)
[lemmy.git] / crates / apub / src / activities / community / announce.rs
1 use crate::{
2   activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_is_public},
3   activity_lists::AnnouncableActivities,
4   http::ActivityCommonFields,
5   insert_activity,
6   objects::community::ApubCommunity,
7   protocol::{
8     activities::{community::announce::AnnounceActivity, CreateOrUpdateType},
9     IdOrNestedObject,
10   },
11 };
12 use activitystreams_kinds::{activity::AnnounceType, public};
13 use lemmy_apub_lib::{
14   data::Data,
15   object_id::ObjectId,
16   traits::{ActivityHandler, ActorType},
17 };
18 use lemmy_utils::LemmyError;
19 use lemmy_websocket::LemmyContext;
20 use tracing::debug;
21
22 #[async_trait::async_trait(?Send)]
23 pub(crate) trait GetCommunity {
24   async fn get_community(
25     &self,
26     context: &LemmyContext,
27     request_counter: &mut i32,
28   ) -> Result<ApubCommunity, LemmyError>;
29 }
30
31 impl AnnounceActivity {
32   pub(crate) fn new(
33     object: AnnouncableActivities,
34     community: &ApubCommunity,
35     context: &LemmyContext,
36   ) -> Result<AnnounceActivity, LemmyError> {
37     Ok(AnnounceActivity {
38       actor: ObjectId::new(community.actor_id()),
39       to: vec![public()],
40       object: IdOrNestedObject::NestedObject(object),
41       cc: vec![community.followers_url.clone().into()],
42       kind: AnnounceType::Announce,
43       id: generate_activity_id(
44         &AnnounceType::Announce,
45         &context.settings().get_protocol_and_hostname(),
46       )?,
47       unparsed: Default::default(),
48     })
49   }
50
51   #[tracing::instrument(skip_all)]
52   pub async fn send(
53     object: AnnouncableActivities,
54     community: &ApubCommunity,
55     context: &LemmyContext,
56   ) -> Result<(), LemmyError> {
57     let announce = AnnounceActivity::new(object.clone(), community, context)?;
58     let inboxes = community.get_follower_inboxes(context).await?;
59     send_lemmy_activity(
60       context,
61       &announce,
62       &announce.id,
63       community,
64       inboxes.clone(),
65       false,
66     )
67     .await?;
68
69     // Pleroma and Mastodon can't handle activities like Announce/Create/Page. So for
70     // compatibility, we also send Announce/Page so that they can follow Lemmy communities.
71     use AnnouncableActivities::*;
72     let object = match object {
73       CreateOrUpdatePost(c) if c.kind == CreateOrUpdateType::Create => Page(c.object),
74       _ => return Ok(()),
75     };
76     let announce_compat = AnnounceActivity::new(object, community, context)?;
77     send_lemmy_activity(
78       context,
79       &announce_compat,
80       &announce_compat.id,
81       community,
82       inboxes,
83       false,
84     )
85     .await?;
86     Ok(())
87   }
88 }
89
90 #[async_trait::async_trait(?Send)]
91 impl ActivityHandler for AnnounceActivity {
92   type DataType = LemmyContext;
93
94   #[tracing::instrument(skip_all)]
95   async fn verify(
96     &self,
97     context: &Data<LemmyContext>,
98     _request_counter: &mut i32,
99   ) -> Result<(), LemmyError> {
100     verify_is_public(&self.to, &self.cc)?;
101     verify_activity(&self.id, self.actor.inner(), &context.settings())?;
102     Ok(())
103   }
104
105   #[tracing::instrument(skip_all)]
106   async fn receive(
107     self,
108     context: &Data<LemmyContext>,
109     request_counter: &mut i32,
110   ) -> Result<(), LemmyError> {
111     let object = self.object.object(context, request_counter).await?;
112     // we have to verify this here in order to avoid fetching the object twice over http
113     object.verify(context, request_counter).await?;
114
115     // TODO: this can probably be implemented in a cleaner way
116     match object {
117       // Dont insert these into activities table, as they are not activities.
118       AnnouncableActivities::Page(_) => {}
119       _ => {
120         let object_value = serde_json::to_value(&object)?;
121         let object_data: ActivityCommonFields = serde_json::from_value(object_value.to_owned())?;
122
123         let insert =
124           insert_activity(&object_data.id, object_value, false, true, context.pool()).await?;
125         if !insert {
126           debug!(
127             "Received duplicate activity in announce {}",
128             object_data.id.to_string()
129           );
130           return Ok(());
131         }
132       }
133     }
134     object.receive(context, request_counter).await
135   }
136 }