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_api_structs::blocking;
47 use lemmy_db_queries::Crud;
48 use lemmy_db_schema::source::site::Site;
49 use lemmy_utils::{location_info, LemmyError};
50 use lemmy_websocket::LemmyContext;
51 use strum_macros::EnumString;
60 /// This file is for post/comment activities received by the community, and for post/comment
61 /// activities announced by the community and received by the user.
63 /// A post or comment being created
64 pub(in crate::inbox) async fn receive_create_for_community(
65 context: &LemmyContext,
67 expected_domain: &Url,
68 request_counter: &mut i32,
69 ) -> Result<(), LemmyError> {
70 let create = Create::from_any_base(activity)?.context(location_info!())?;
71 verify_activity_domains_valid(&create, &expected_domain, true)?;
72 is_addressed_to_public(&create)?;
77 .and_then(|s| s.parse().ok());
79 Some(PageOrNote::Page) => receive_create_post(create, context, request_counter).await,
80 Some(PageOrNote::Note) => receive_create_comment(create, context, request_counter).await,
81 _ => receive_unhandled_activity(create),
85 /// A post or comment being edited
86 pub(in crate::inbox) async fn receive_update_for_community(
87 context: &LemmyContext,
89 expected_domain: &Url,
90 request_counter: &mut i32,
91 ) -> Result<(), LemmyError> {
92 let update = Update::from_any_base(activity)?.context(location_info!())?;
93 verify_activity_domains_valid(&update, &expected_domain, true)?;
94 is_addressed_to_public(&update)?;
99 .and_then(|s| s.parse().ok());
101 Some(PageOrNote::Page) => receive_update_post(update, context, request_counter).await,
102 Some(PageOrNote::Note) => receive_update_comment(update, context, request_counter).await,
103 _ => receive_unhandled_activity(update),
107 /// A post or comment being upvoted
108 pub(in crate::inbox) async fn receive_like_for_community(
109 context: &LemmyContext,
111 expected_domain: &Url,
112 request_counter: &mut i32,
113 ) -> Result<(), LemmyError> {
114 let like = Like::from_any_base(activity)?.context(location_info!())?;
115 verify_activity_domains_valid(&like, &expected_domain, false)?;
116 is_addressed_to_public(&like)?;
120 .as_single_xsd_any_uri()
121 .context(location_info!())?;
122 match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
123 PostOrComment::Post(post) => receive_like_post(like, *post, context, request_counter).await,
124 PostOrComment::Comment(comment) => {
125 receive_like_comment(like, *comment, context, request_counter).await
130 /// A post or comment being downvoted
131 pub(in crate::inbox) async fn receive_dislike_for_community(
132 context: &LemmyContext,
134 expected_domain: &Url,
135 request_counter: &mut i32,
136 ) -> Result<(), LemmyError> {
137 let enable_downvotes = blocking(context.pool(), move |conn| {
138 Site::read(conn, 1).map(|s| s.enable_downvotes)
141 if !enable_downvotes {
145 let dislike = Dislike::from_any_base(activity)?.context(location_info!())?;
146 verify_activity_domains_valid(&dislike, &expected_domain, false)?;
147 is_addressed_to_public(&dislike)?;
149 let object_id = dislike
151 .as_single_xsd_any_uri()
152 .context(location_info!())?;
153 match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
154 PostOrComment::Post(post) => {
155 receive_dislike_post(dislike, *post, context, request_counter).await
157 PostOrComment::Comment(comment) => {
158 receive_dislike_comment(dislike, *comment, context, request_counter).await
163 /// A post or comment being deleted by its creator
164 pub(in crate::inbox) async fn receive_delete_for_community(
165 context: &LemmyContext,
167 expected_domain: &Url,
168 ) -> Result<(), LemmyError> {
169 let delete = Delete::from_any_base(activity)?.context(location_info!())?;
170 verify_activity_domains_valid(&delete, &expected_domain, true)?;
171 is_addressed_to_public(&delete)?;
176 .single_xsd_any_uri()
177 .context(location_info!())?;
179 match find_post_or_comment_by_id(context, object).await {
180 Ok(PostOrComment::Post(p)) => receive_delete_post(context, *p).await,
181 Ok(PostOrComment::Comment(c)) => receive_delete_comment(context, *c).await,
182 // if we dont have the object, no need to do anything
187 /// A post or comment being removed by a mod/admin
188 pub(in crate::inbox) async fn receive_remove_for_community(
189 context: &LemmyContext,
191 expected_domain: &Url,
192 ) -> Result<(), LemmyError> {
193 let remove = Remove::from_any_base(activity)?.context(location_info!())?;
194 verify_activity_domains_valid(&remove, &expected_domain, false)?;
195 is_addressed_to_public(&remove)?;
199 .map(|c| c.as_many())
201 .context(location_info!())?;
202 let community_id = cc
204 .map(|c| c.as_xsd_any_uri())
206 .context(location_info!())?;
211 .single_xsd_any_uri()
212 .context(location_info!())?;
214 // Ensure that remove activity comes from the same domain as the community
215 remove.id(community_id.domain().context(location_info!())?)?;
217 match find_post_or_comment_by_id(context, object).await {
218 Ok(PostOrComment::Post(p)) => receive_remove_post(context, remove, *p).await,
219 Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, remove, *c).await,
220 // if we dont have the object, no need to do anything
225 #[derive(EnumString)]
226 enum UndoableActivities {
233 /// A post/comment action being reverted (either a delete, remove, upvote or downvote)
234 pub(in crate::inbox) async fn receive_undo_for_community(
235 context: &LemmyContext,
237 expected_domain: &Url,
238 request_counter: &mut i32,
239 ) -> Result<(), LemmyError> {
240 let undo = Undo::from_any_base(activity)?.context(location_info!())?;
241 verify_activity_domains_valid(&undo, &expected_domain.to_owned(), true)?;
242 is_addressed_to_public(&undo)?;
244 use UndoableActivities::*;
247 .as_single_kind_str()
248 .and_then(|s| s.parse().ok())
250 Some(Delete) => receive_undo_delete_for_community(context, undo, expected_domain).await,
251 Some(Remove) => receive_undo_remove_for_community(context, undo, expected_domain).await,
253 receive_undo_like_for_community(context, undo, expected_domain, request_counter).await
256 receive_undo_dislike_for_community(context, undo, expected_domain, request_counter).await
258 _ => receive_unhandled_activity(undo),
262 /// A post or comment deletion being reverted
263 pub(in crate::inbox) async fn receive_undo_delete_for_community(
264 context: &LemmyContext,
266 expected_domain: &Url,
267 ) -> Result<(), LemmyError> {
268 let delete = Delete::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
269 .context(location_info!())?;
270 verify_activity_domains_valid(&delete, &expected_domain, true)?;
271 is_addressed_to_public(&delete)?;
276 .single_xsd_any_uri()
277 .context(location_info!())?;
278 match find_post_or_comment_by_id(context, object).await {
279 Ok(PostOrComment::Post(p)) => receive_undo_delete_post(context, *p).await,
280 Ok(PostOrComment::Comment(c)) => receive_undo_delete_comment(context, *c).await,
281 // if we dont have the object, no need to do anything
286 /// A post or comment removal being reverted
287 pub(in crate::inbox) async fn receive_undo_remove_for_community(
288 context: &LemmyContext,
290 expected_domain: &Url,
291 ) -> Result<(), LemmyError> {
292 let remove = Remove::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
293 .context(location_info!())?;
294 verify_activity_domains_valid(&remove, &expected_domain, false)?;
295 is_addressed_to_public(&remove)?;
300 .single_xsd_any_uri()
301 .context(location_info!())?;
302 match find_post_or_comment_by_id(context, object).await {
303 Ok(PostOrComment::Post(p)) => receive_undo_remove_post(context, *p).await,
304 Ok(PostOrComment::Comment(c)) => receive_undo_remove_comment(context, *c).await,
305 // if we dont have the object, no need to do anything
310 /// A post or comment upvote being reverted
311 pub(in crate::inbox) async fn receive_undo_like_for_community(
312 context: &LemmyContext,
314 expected_domain: &Url,
315 request_counter: &mut i32,
316 ) -> Result<(), LemmyError> {
317 let like = Like::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
318 .context(location_info!())?;
319 verify_activity_domains_valid(&like, &expected_domain, false)?;
320 is_addressed_to_public(&like)?;
324 .as_single_xsd_any_uri()
325 .context(location_info!())?;
326 match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
327 PostOrComment::Post(post) => {
328 receive_undo_like_post(&like, *post, context, request_counter).await
330 PostOrComment::Comment(comment) => {
331 receive_undo_like_comment(&like, *comment, context, request_counter).await
336 /// A post or comment downvote being reverted
337 pub(in crate::inbox) async fn receive_undo_dislike_for_community(
338 context: &LemmyContext,
340 expected_domain: &Url,
341 request_counter: &mut i32,
342 ) -> Result<(), LemmyError> {
343 let dislike = Dislike::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
344 .context(location_info!())?;
345 verify_activity_domains_valid(&dislike, &expected_domain, false)?;
346 is_addressed_to_public(&dislike)?;
348 let object_id = dislike
350 .as_single_xsd_any_uri()
351 .context(location_info!())?;
352 match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
353 PostOrComment::Post(post) => {
354 receive_undo_dislike_post(&dislike, *post, context, request_counter).await
356 PostOrComment::Comment(comment) => {
357 receive_undo_dislike_comment(&dislike, *comment, context, request_counter).await
362 async fn fetch_post_or_comment_by_id(
364 context: &LemmyContext,
365 request_counter: &mut i32,
366 ) -> Result<PostOrComment, LemmyError> {
367 if let Ok(post) = get_or_fetch_and_insert_post(apub_id, context, request_counter).await {
368 return Ok(PostOrComment::Post(Box::new(post)));
371 if let Ok(comment) = get_or_fetch_and_insert_comment(apub_id, context, request_counter).await {
372 return Ok(PostOrComment::Comment(Box::new(comment)));