4 receive_create_comment,
5 receive_delete_comment,
6 receive_dislike_comment,
8 receive_remove_comment,
9 receive_update_comment,
12 receive_undo_delete_comment,
13 receive_undo_dislike_comment,
14 receive_undo_like_comment,
15 receive_undo_remove_comment,
26 receive_undo_delete_post,
27 receive_undo_dislike_post,
28 receive_undo_like_post,
29 receive_undo_remove_post,
31 receive_unhandled_activity,
32 verify_activity_domains_valid,
34 fetcher::objects::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
35 find_post_or_comment_by_id,
36 inbox::is_addressed_to_public,
39 use activitystreams::{
40 activity::{Create, Delete, Dislike, Like, Remove, Undo, Update},
45 use diesel::result::Error::NotFound;
46 use lemmy_db_queries::Crud;
47 use lemmy_db_schema::source::site::Site;
48 use lemmy_structs::blocking;
49 use lemmy_utils::{location_info, LemmyError};
50 use lemmy_websocket::LemmyContext;
53 /// This file is for post/comment activities received by the community, and for post/comment
54 /// activities announced by the community and received by the user.
56 /// A post or comment being created
57 pub(in crate::inbox) async fn receive_create_for_community(
58 context: &LemmyContext,
60 expected_domain: &Url,
61 request_counter: &mut i32,
62 ) -> Result<(), LemmyError> {
63 let create = Create::from_any_base(activity)?.context(location_info!())?;
64 verify_activity_domains_valid(&create, &expected_domain, true)?;
65 is_addressed_to_public(&create)?;
67 match create.object().as_single_kind_str() {
68 Some("Page") => receive_create_post(create, context, request_counter).await,
69 Some("Note") => receive_create_comment(create, context, request_counter).await,
70 _ => receive_unhandled_activity(create),
74 /// A post or comment being edited
75 pub(in crate::inbox) async fn receive_update_for_community(
76 context: &LemmyContext,
78 expected_domain: &Url,
79 request_counter: &mut i32,
80 ) -> Result<(), LemmyError> {
81 let update = Update::from_any_base(activity)?.context(location_info!())?;
82 verify_activity_domains_valid(&update, &expected_domain, true)?;
83 is_addressed_to_public(&update)?;
85 match update.object().as_single_kind_str() {
86 Some("Page") => receive_update_post(update, context, request_counter).await,
87 Some("Note") => receive_update_comment(update, context, request_counter).await,
88 _ => receive_unhandled_activity(update),
92 /// A post or comment being upvoted
93 pub(in crate::inbox) async fn receive_like_for_community(
94 context: &LemmyContext,
96 expected_domain: &Url,
97 request_counter: &mut i32,
98 ) -> Result<(), LemmyError> {
99 let like = Like::from_any_base(activity)?.context(location_info!())?;
100 verify_activity_domains_valid(&like, &expected_domain, false)?;
101 is_addressed_to_public(&like)?;
103 let object_id = get_like_object_id(&like)?;
104 match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
105 PostOrComment::Post(post) => receive_like_post(like, post, context, request_counter).await,
106 PostOrComment::Comment(comment) => {
107 receive_like_comment(like, comment, context, request_counter).await
112 /// A post or comment being downvoted
113 pub(in crate::inbox) async fn receive_dislike_for_community(
114 context: &LemmyContext,
116 expected_domain: &Url,
117 request_counter: &mut i32,
118 ) -> Result<(), LemmyError> {
119 let enable_downvotes = blocking(context.pool(), move |conn| {
120 Site::read(conn, 1).map(|s| s.enable_downvotes)
123 if !enable_downvotes {
127 let dislike = Dislike::from_any_base(activity)?.context(location_info!())?;
128 verify_activity_domains_valid(&dislike, &expected_domain, false)?;
129 is_addressed_to_public(&dislike)?;
131 let object_id = get_like_object_id(&dislike)?;
132 match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
133 PostOrComment::Post(post) => {
134 receive_dislike_post(dislike, post, context, request_counter).await
136 PostOrComment::Comment(comment) => {
137 receive_dislike_comment(dislike, comment, context, request_counter).await
142 /// A post or comment being deleted by its creator
143 pub(in crate::inbox) async fn receive_delete_for_community(
144 context: &LemmyContext,
146 expected_domain: &Url,
147 ) -> Result<(), LemmyError> {
148 let delete = Delete::from_any_base(activity)?.context(location_info!())?;
149 verify_activity_domains_valid(&delete, &expected_domain, true)?;
150 is_addressed_to_public(&delete)?;
155 .single_xsd_any_uri()
156 .context(location_info!())?;
158 match find_post_or_comment_by_id(context, object).await {
159 Ok(PostOrComment::Post(p)) => receive_delete_post(context, p).await,
160 Ok(PostOrComment::Comment(c)) => receive_delete_comment(context, c).await,
161 // if we dont have the object, no need to do anything
166 /// A post or comment being removed by a mod/admin
167 pub(in crate::inbox) async fn receive_remove_for_community(
168 context: &LemmyContext,
170 expected_domain: &Url,
171 ) -> Result<(), LemmyError> {
172 let remove = Remove::from_any_base(activity)?.context(location_info!())?;
173 verify_activity_domains_valid(&remove, &expected_domain, false)?;
174 is_addressed_to_public(&remove)?;
178 .map(|c| c.as_many())
180 .context(location_info!())?;
181 let community_id = cc
183 .map(|c| c.as_xsd_any_uri())
185 .context(location_info!())?;
190 .single_xsd_any_uri()
191 .context(location_info!())?;
193 // Ensure that remove activity comes from the same domain as the community
194 remove.id(community_id.domain().context(location_info!())?)?;
196 match find_post_or_comment_by_id(context, object).await {
197 Ok(PostOrComment::Post(p)) => receive_remove_post(context, remove, p).await,
198 Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, remove, c).await,
199 // if we dont have the object, no need to do anything
204 /// A post/comment action being reverted (either a delete, remove, upvote or downvote)
205 pub(in crate::inbox) async fn receive_undo_for_community(
206 context: &LemmyContext,
208 expected_domain: &Url,
209 request_counter: &mut i32,
210 ) -> Result<(), LemmyError> {
211 let undo = Undo::from_any_base(activity)?.context(location_info!())?;
212 verify_activity_domains_valid(&undo, &expected_domain.to_owned(), true)?;
213 is_addressed_to_public(&undo)?;
215 match undo.object().as_single_kind_str() {
216 Some("Delete") => receive_undo_delete_for_community(context, undo, expected_domain).await,
217 Some("Remove") => receive_undo_remove_for_community(context, undo, expected_domain).await,
219 receive_undo_like_for_community(context, undo, expected_domain, request_counter).await
222 receive_undo_dislike_for_community(context, undo, expected_domain, request_counter).await
224 _ => receive_unhandled_activity(undo),
228 /// A post or comment deletion being reverted
229 pub(in crate::inbox) async fn receive_undo_delete_for_community(
230 context: &LemmyContext,
232 expected_domain: &Url,
233 ) -> Result<(), LemmyError> {
234 let delete = Delete::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
235 .context(location_info!())?;
236 verify_activity_domains_valid(&delete, &expected_domain, true)?;
237 is_addressed_to_public(&delete)?;
242 .single_xsd_any_uri()
243 .context(location_info!())?;
244 match find_post_or_comment_by_id(context, object).await {
245 Ok(PostOrComment::Post(p)) => receive_undo_delete_post(context, p).await,
246 Ok(PostOrComment::Comment(c)) => receive_undo_delete_comment(context, c).await,
247 // if we dont have the object, no need to do anything
252 /// A post or comment removal being reverted
253 pub(in crate::inbox) async fn receive_undo_remove_for_community(
254 context: &LemmyContext,
256 expected_domain: &Url,
257 ) -> Result<(), LemmyError> {
258 let remove = Remove::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
259 .context(location_info!())?;
260 verify_activity_domains_valid(&remove, &expected_domain, false)?;
261 is_addressed_to_public(&remove)?;
266 .single_xsd_any_uri()
267 .context(location_info!())?;
268 match find_post_or_comment_by_id(context, object).await {
269 Ok(PostOrComment::Post(p)) => receive_undo_remove_post(context, p).await,
270 Ok(PostOrComment::Comment(c)) => receive_undo_remove_comment(context, c).await,
271 // if we dont have the object, no need to do anything
276 /// A post or comment upvote being reverted
277 pub(in crate::inbox) async fn receive_undo_like_for_community(
278 context: &LemmyContext,
280 expected_domain: &Url,
281 request_counter: &mut i32,
282 ) -> Result<(), LemmyError> {
283 let like = Like::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
284 .context(location_info!())?;
285 verify_activity_domains_valid(&like, &expected_domain, false)?;
286 is_addressed_to_public(&like)?;
288 let object_id = get_like_object_id(&like)?;
289 match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
290 PostOrComment::Post(post) => {
291 receive_undo_like_post(&like, post, context, request_counter).await
293 PostOrComment::Comment(comment) => {
294 receive_undo_like_comment(&like, comment, context, request_counter).await
299 /// A post or comment downvote being reverted
300 pub(in crate::inbox) async fn receive_undo_dislike_for_community(
301 context: &LemmyContext,
303 expected_domain: &Url,
304 request_counter: &mut i32,
305 ) -> Result<(), LemmyError> {
306 let dislike = Dislike::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
307 .context(location_info!())?;
308 verify_activity_domains_valid(&dislike, &expected_domain, false)?;
309 is_addressed_to_public(&dislike)?;
311 let object_id = get_like_object_id(&dislike)?;
312 match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
313 PostOrComment::Post(post) => {
314 receive_undo_dislike_post(&dislike, post, context, request_counter).await
316 PostOrComment::Comment(comment) => {
317 receive_undo_dislike_comment(&dislike, comment, context, request_counter).await
322 async fn fetch_post_or_comment_by_id(
324 context: &LemmyContext,
325 request_counter: &mut i32,
326 ) -> Result<PostOrComment, LemmyError> {
327 if let Ok(post) = get_or_fetch_and_insert_post(apub_id, context, request_counter).await {
328 return Ok(PostOrComment::Post(post));
331 if let Ok(comment) = get_or_fetch_and_insert_comment(apub_id, context, request_counter).await {
332 return Ok(PostOrComment::Comment(comment));
338 fn get_like_object_id<Activity>(like_or_dislike: &Activity) -> Result<Url, LemmyError>
340 Activity: ActorAndObjectRefExt,
342 // TODO: For backwards compatibility with older Lemmy versions where like.object contains a full
343 // post/comment. This can be removed after some time, using
344 // `activity.oject().as_single_xsd_any_uri()` instead.
345 let object = like_or_dislike.object();
346 if let Some(xsd_uri) = object.as_single_xsd_any_uri() {
347 Ok(xsd_uri.to_owned())
353 .context(location_info!())?
355 .context(location_info!())?