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