]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/community/announce.rs
Use audience field to federate items in groups (fixes #2464) (#2584)
[lemmy.git] / crates / apub / src / activities / community / announce.rs
1 use crate::{
2   activities::{
3     generate_activity_id,
4     send_lemmy_activity,
5     verify_is_public,
6     verify_person_in_community,
7   },
8   activity_lists::AnnouncableActivities,
9   insert_activity,
10   objects::community::ApubCommunity,
11   protocol::{
12     activities::community::announce::{AnnounceActivity, RawAnnouncableActivities},
13     Id,
14     IdOrNestedObject,
15     InCommunity,
16   },
17   ActorType,
18 };
19 use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
20 use activitystreams_kinds::{activity::AnnounceType, public};
21 use lemmy_utils::error::LemmyError;
22 use lemmy_websocket::LemmyContext;
23 use serde_json::Value;
24 use tracing::debug;
25 use url::Url;
26
27 #[async_trait::async_trait(?Send)]
28 impl ActivityHandler for RawAnnouncableActivities {
29   type DataType = LemmyContext;
30   type Error = LemmyError;
31
32   fn id(&self) -> &Url {
33     &self.id
34   }
35
36   fn actor(&self) -> &Url {
37     &self.actor
38   }
39
40   #[tracing::instrument(skip_all)]
41   async fn verify(
42     &self,
43     _data: &Data<Self::DataType>,
44     _request_counter: &mut i32,
45   ) -> Result<(), Self::Error> {
46     Ok(())
47   }
48
49   #[tracing::instrument(skip_all)]
50   async fn receive(
51     self,
52     data: &Data<Self::DataType>,
53     request_counter: &mut i32,
54   ) -> Result<(), Self::Error> {
55     let activity: AnnouncableActivities = self.clone().try_into()?;
56     // This is only for sending, not receiving so we reject it.
57     if let AnnouncableActivities::Page(_) = activity {
58       return Err(LemmyError::from_message("Cant receive page"));
59     }
60     let community = activity.community(data, &mut 0).await?;
61     let actor_id = ObjectId::new(activity.actor().clone());
62
63     // verify and receive activity
64     activity.verify(data, request_counter).await?;
65     activity.receive(data, request_counter).await?;
66
67     // send to community followers
68     if community.local {
69       verify_person_in_community(&actor_id, &community, data, &mut 0).await?;
70       AnnounceActivity::send(self, &community, data).await?;
71     }
72     Ok(())
73   }
74 }
75
76 impl AnnounceActivity {
77   pub(crate) fn new(
78     object: RawAnnouncableActivities,
79     community: &ApubCommunity,
80     context: &LemmyContext,
81   ) -> Result<AnnounceActivity, LemmyError> {
82     Ok(AnnounceActivity {
83       actor: ObjectId::new(community.actor_id()),
84       to: vec![public()],
85       object: IdOrNestedObject::NestedObject(object),
86       cc: vec![community.followers_url.clone().into()],
87       kind: AnnounceType::Announce,
88       id: generate_activity_id(
89         &AnnounceType::Announce,
90         &context.settings().get_protocol_and_hostname(),
91       )?,
92     })
93   }
94
95   #[tracing::instrument(skip_all)]
96   pub async fn send(
97     object: RawAnnouncableActivities,
98     community: &ApubCommunity,
99     context: &LemmyContext,
100   ) -> Result<(), LemmyError> {
101     let announce = AnnounceActivity::new(object.clone(), community, context)?;
102     let inboxes = community.get_follower_inboxes(context).await?;
103     send_lemmy_activity(context, announce, community, inboxes.clone(), false).await?;
104
105     // Pleroma and Mastodon can't handle activities like Announce/Create/Page. So for
106     // compatibility, we also send Announce/Page so that they can follow Lemmy communities.
107     let object_parsed = object.try_into()?;
108     if let AnnouncableActivities::CreateOrUpdatePost(c) = object_parsed {
109       // Hack: need to convert Page into a format which can be sent as activity, which requires
110       //       adding actor field.
111       let announcable_page = RawAnnouncableActivities {
112         id: c.object.id.clone().into_inner(),
113         actor: c.actor.clone().into_inner(),
114         other: serde_json::to_value(c.object)?
115           .as_object()
116           .expect("is object")
117           .clone(),
118       };
119       let announce_compat = AnnounceActivity::new(announcable_page, community, context)?;
120       send_lemmy_activity(context, announce_compat, community, inboxes, false).await?;
121     }
122     Ok(())
123   }
124 }
125
126 #[async_trait::async_trait(?Send)]
127 impl ActivityHandler for AnnounceActivity {
128   type DataType = LemmyContext;
129   type Error = LemmyError;
130
131   fn id(&self) -> &Url {
132     &self.id
133   }
134
135   fn actor(&self) -> &Url {
136     self.actor.inner()
137   }
138
139   #[tracing::instrument(skip_all)]
140   async fn verify(
141     &self,
142     _context: &Data<LemmyContext>,
143     _request_counter: &mut i32,
144   ) -> Result<(), LemmyError> {
145     verify_is_public(&self.to, &self.cc)?;
146     Ok(())
147   }
148
149   #[tracing::instrument(skip_all)]
150   async fn receive(
151     self,
152     context: &Data<LemmyContext>,
153     request_counter: &mut i32,
154   ) -> Result<(), LemmyError> {
155     let object: AnnouncableActivities = self
156       .object
157       .object(context, request_counter)
158       .await?
159       .try_into()?;
160     // This is only for sending, not receiving so we reject it.
161     if let AnnouncableActivities::Page(_) = object {
162       return Err(LemmyError::from_message("Cant receive page"));
163     }
164
165     // we have to verify this here in order to avoid fetching the object twice over http
166     object.verify(context, request_counter).await?;
167
168     let object_value = serde_json::to_value(&object)?;
169     let insert = insert_activity(object.id(), object_value, false, true, context.pool()).await?;
170     if !insert {
171       debug!(
172         "Received duplicate activity in announce {}",
173         object.id().to_string()
174       );
175       return Ok(());
176     }
177     object.receive(context, request_counter).await
178   }
179 }
180
181 impl Id for RawAnnouncableActivities {
182   fn object_id(&self) -> &Url {
183     ActivityHandler::id(self)
184   }
185 }
186
187 impl TryFrom<RawAnnouncableActivities> for AnnouncableActivities {
188   type Error = serde_json::error::Error;
189
190   fn try_from(value: RawAnnouncableActivities) -> Result<Self, Self::Error> {
191     let mut map = value.other.clone();
192     map.insert("id".to_string(), Value::String(value.id.to_string()));
193     map.insert("actor".to_string(), Value::String(value.actor.to_string()));
194     serde_json::from_value(Value::Object(map))
195   }
196 }
197
198 impl TryFrom<AnnouncableActivities> for RawAnnouncableActivities {
199   type Error = serde_json::error::Error;
200
201   fn try_from(value: AnnouncableActivities) -> Result<Self, Self::Error> {
202     serde_json::from_value(serde_json::to_value(value)?)
203   }
204 }