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