]> Untitled Git - lemmy.git/blob - server/src/apub/inbox/activities/undo.rs
Merge remote-tracking branch 'weblate/main' into main
[lemmy.git] / server / src / apub / inbox / activities / undo.rs
1 use crate::{
2   api::{comment::CommentResponse, community::CommunityResponse, post::PostResponse},
3   apub::{
4     fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
5     inbox::shared_inbox::{
6       announce_if_community_is_local,
7       get_user_from_activity,
8       receive_unhandled_activity,
9     },
10     FromApub,
11     GroupExt,
12     PageExt,
13   },
14   blocking,
15   routes::ChatServerParam,
16   websocket::{
17     server::{SendComment, SendCommunityRoomMessage, SendPost},
18     UserOperation,
19   },
20   DbPool,
21   LemmyError,
22 };
23 use activitystreams_new::{activity::*, base::AnyBase, object::Note, prelude::*};
24 use actix_web::{client::Client, HttpResponse};
25 use lemmy_db::{
26   comment::{Comment, CommentForm, CommentLike, CommentLikeForm},
27   comment_view::CommentView,
28   community::{Community, CommunityForm},
29   community_view::CommunityView,
30   naive_now,
31   post::{Post, PostForm, PostLike, PostLikeForm},
32   post_view::PostView,
33   Crud,
34   Likeable,
35 };
36
37 pub async fn receive_undo(
38   activity: AnyBase,
39   client: &Client,
40   pool: &DbPool,
41   chat_server: ChatServerParam,
42 ) -> Result<HttpResponse, LemmyError> {
43   let undo = Undo::from_any_base(activity)?.unwrap();
44   match undo.object().as_single_kind_str() {
45     Some("Delete") => receive_undo_delete(undo, client, pool, chat_server).await,
46     Some("Remove") => receive_undo_remove(undo, client, pool, chat_server).await,
47     Some("Like") => receive_undo_like(undo, client, pool, chat_server).await,
48     Some("Dislike") => receive_undo_dislike(undo, client, pool, chat_server).await,
49     // TODO: handle undo_dislike?
50     _ => receive_unhandled_activity(undo),
51   }
52 }
53
54 async fn receive_undo_delete(
55   undo: Undo,
56   client: &Client,
57   pool: &DbPool,
58   chat_server: ChatServerParam,
59 ) -> Result<HttpResponse, LemmyError> {
60   let delete = Delete::from_any_base(undo.object().to_owned().one().unwrap())?.unwrap();
61   let type_ = delete.object().as_single_kind_str().unwrap();
62   match type_ {
63     "Note" => receive_undo_delete_comment(undo, &delete, client, pool, chat_server).await,
64     "Page" => receive_undo_delete_post(undo, &delete, client, pool, chat_server).await,
65     "Group" => receive_undo_delete_community(undo, &delete, client, pool, chat_server).await,
66     d => Err(format_err!("Undo Delete type {} not supported", d).into()),
67   }
68 }
69
70 async fn receive_undo_remove(
71   undo: Undo,
72   client: &Client,
73   pool: &DbPool,
74   chat_server: ChatServerParam,
75 ) -> Result<HttpResponse, LemmyError> {
76   let remove = Remove::from_any_base(undo.object().to_owned().one().unwrap())?.unwrap();
77
78   let type_ = remove.object().as_single_kind_str().unwrap();
79   match type_ {
80     "Note" => receive_undo_remove_comment(undo, &remove, client, pool, chat_server).await,
81     "Page" => receive_undo_remove_post(undo, &remove, client, pool, chat_server).await,
82     "Group" => receive_undo_remove_community(undo, &remove, client, pool, chat_server).await,
83     d => Err(format_err!("Undo Delete type {} not supported", d).into()),
84   }
85 }
86
87 async fn receive_undo_like(
88   undo: Undo,
89   client: &Client,
90   pool: &DbPool,
91   chat_server: ChatServerParam,
92 ) -> Result<HttpResponse, LemmyError> {
93   let like = Like::from_any_base(undo.object().to_owned().one().unwrap())?.unwrap();
94
95   let type_ = like.object().as_single_kind_str().unwrap();
96   match type_ {
97     "Note" => receive_undo_like_comment(undo, &like, client, pool, chat_server).await,
98     "Page" => receive_undo_like_post(undo, &like, client, pool, chat_server).await,
99     d => Err(format_err!("Undo Delete type {} not supported", d).into()),
100   }
101 }
102
103 async fn receive_undo_dislike(
104   undo: Undo,
105   _client: &Client,
106   _pool: &DbPool,
107   _chat_server: ChatServerParam,
108 ) -> Result<HttpResponse, LemmyError> {
109   let dislike = Dislike::from_any_base(undo.object().to_owned().one().unwrap())?.unwrap();
110
111   let type_ = dislike.object().as_single_kind_str().unwrap();
112   Err(format_err!("Undo Delete type {} not supported", type_).into())
113 }
114
115 async fn receive_undo_delete_comment(
116   undo: Undo,
117   delete: &Delete,
118   client: &Client,
119   pool: &DbPool,
120   chat_server: ChatServerParam,
121 ) -> Result<HttpResponse, LemmyError> {
122   let user = get_user_from_activity(delete, client, pool).await?;
123   let note = Note::from_any_base(delete.object().to_owned().one().unwrap())?.unwrap();
124
125   let comment_ap_id = CommentForm::from_apub(&note, client, pool)
126     .await?
127     .get_ap_id()?;
128
129   let comment = get_or_fetch_and_insert_comment(&comment_ap_id, client, pool).await?;
130
131   let comment_form = CommentForm {
132     content: comment.content.to_owned(),
133     parent_id: comment.parent_id,
134     post_id: comment.post_id,
135     creator_id: comment.creator_id,
136     removed: None,
137     deleted: Some(false),
138     read: None,
139     published: None,
140     updated: Some(naive_now()),
141     ap_id: comment.ap_id,
142     local: comment.local,
143   };
144   let comment_id = comment.id;
145   blocking(pool, move |conn| {
146     Comment::update(conn, comment_id, &comment_form)
147   })
148   .await??;
149
150   // Refetch the view
151   let comment_id = comment.id;
152   let comment_view =
153     blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
154
155   // TODO get those recipient actor ids from somewhere
156   let recipient_ids = vec![];
157   let res = CommentResponse {
158     comment: comment_view,
159     recipient_ids,
160     form_id: None,
161   };
162
163   chat_server.do_send(SendComment {
164     op: UserOperation::EditComment,
165     comment: res,
166     my_id: None,
167   });
168
169   announce_if_community_is_local(undo, &user, client, pool).await?;
170   Ok(HttpResponse::Ok().finish())
171 }
172
173 async fn receive_undo_remove_comment(
174   undo: Undo,
175   remove: &Remove,
176   client: &Client,
177   pool: &DbPool,
178   chat_server: ChatServerParam,
179 ) -> Result<HttpResponse, LemmyError> {
180   let mod_ = get_user_from_activity(remove, client, pool).await?;
181   let note = Note::from_any_base(remove.object().to_owned().one().unwrap())?.unwrap();
182
183   let comment_ap_id = CommentForm::from_apub(&note, client, pool)
184     .await?
185     .get_ap_id()?;
186
187   let comment = get_or_fetch_and_insert_comment(&comment_ap_id, client, pool).await?;
188
189   let comment_form = CommentForm {
190     content: comment.content.to_owned(),
191     parent_id: comment.parent_id,
192     post_id: comment.post_id,
193     creator_id: comment.creator_id,
194     removed: Some(false),
195     deleted: None,
196     read: None,
197     published: None,
198     updated: Some(naive_now()),
199     ap_id: comment.ap_id,
200     local: comment.local,
201   };
202   let comment_id = comment.id;
203   blocking(pool, move |conn| {
204     Comment::update(conn, comment_id, &comment_form)
205   })
206   .await??;
207
208   // Refetch the view
209   let comment_id = comment.id;
210   let comment_view =
211     blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
212
213   // TODO get those recipient actor ids from somewhere
214   let recipient_ids = vec![];
215   let res = CommentResponse {
216     comment: comment_view,
217     recipient_ids,
218     form_id: None,
219   };
220
221   chat_server.do_send(SendComment {
222     op: UserOperation::EditComment,
223     comment: res,
224     my_id: None,
225   });
226
227   announce_if_community_is_local(undo, &mod_, client, pool).await?;
228   Ok(HttpResponse::Ok().finish())
229 }
230
231 async fn receive_undo_delete_post(
232   undo: Undo,
233   delete: &Delete,
234   client: &Client,
235   pool: &DbPool,
236   chat_server: ChatServerParam,
237 ) -> Result<HttpResponse, LemmyError> {
238   let user = get_user_from_activity(delete, client, pool).await?;
239   let page = PageExt::from_any_base(delete.object().to_owned().one().unwrap())?.unwrap();
240
241   let post_ap_id = PostForm::from_apub(&page, client, pool)
242     .await?
243     .get_ap_id()?;
244
245   let post = get_or_fetch_and_insert_post(&post_ap_id, client, pool).await?;
246
247   let post_form = PostForm {
248     name: post.name.to_owned(),
249     url: post.url.to_owned(),
250     body: post.body.to_owned(),
251     creator_id: post.creator_id.to_owned(),
252     community_id: post.community_id,
253     removed: None,
254     deleted: Some(false),
255     nsfw: post.nsfw,
256     locked: None,
257     stickied: None,
258     updated: Some(naive_now()),
259     embed_title: post.embed_title,
260     embed_description: post.embed_description,
261     embed_html: post.embed_html,
262     thumbnail_url: post.thumbnail_url,
263     ap_id: post.ap_id,
264     local: post.local,
265     published: None,
266   };
267   let post_id = post.id;
268   blocking(pool, move |conn| Post::update(conn, post_id, &post_form)).await??;
269
270   // Refetch the view
271   let post_id = post.id;
272   let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
273
274   let res = PostResponse { post: post_view };
275
276   chat_server.do_send(SendPost {
277     op: UserOperation::EditPost,
278     post: res,
279     my_id: None,
280   });
281
282   announce_if_community_is_local(undo, &user, client, pool).await?;
283   Ok(HttpResponse::Ok().finish())
284 }
285
286 async fn receive_undo_remove_post(
287   undo: Undo,
288   remove: &Remove,
289   client: &Client,
290   pool: &DbPool,
291   chat_server: ChatServerParam,
292 ) -> Result<HttpResponse, LemmyError> {
293   let mod_ = get_user_from_activity(remove, client, pool).await?;
294   let page = PageExt::from_any_base(remove.object().to_owned().one().unwrap())?.unwrap();
295
296   let post_ap_id = PostForm::from_apub(&page, client, pool)
297     .await?
298     .get_ap_id()?;
299
300   let post = get_or_fetch_and_insert_post(&post_ap_id, client, pool).await?;
301
302   let post_form = PostForm {
303     name: post.name.to_owned(),
304     url: post.url.to_owned(),
305     body: post.body.to_owned(),
306     creator_id: post.creator_id.to_owned(),
307     community_id: post.community_id,
308     removed: Some(false),
309     deleted: None,
310     nsfw: post.nsfw,
311     locked: None,
312     stickied: None,
313     updated: Some(naive_now()),
314     embed_title: post.embed_title,
315     embed_description: post.embed_description,
316     embed_html: post.embed_html,
317     thumbnail_url: post.thumbnail_url,
318     ap_id: post.ap_id,
319     local: post.local,
320     published: None,
321   };
322   let post_id = post.id;
323   blocking(pool, move |conn| Post::update(conn, post_id, &post_form)).await??;
324
325   // Refetch the view
326   let post_id = post.id;
327   let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
328
329   let res = PostResponse { post: post_view };
330
331   chat_server.do_send(SendPost {
332     op: UserOperation::EditPost,
333     post: res,
334     my_id: None,
335   });
336
337   announce_if_community_is_local(undo, &mod_, client, pool).await?;
338   Ok(HttpResponse::Ok().finish())
339 }
340
341 async fn receive_undo_delete_community(
342   undo: Undo,
343   delete: &Delete,
344   client: &Client,
345   pool: &DbPool,
346   chat_server: ChatServerParam,
347 ) -> Result<HttpResponse, LemmyError> {
348   let user = get_user_from_activity(delete, client, pool).await?;
349   let group = GroupExt::from_any_base(delete.object().to_owned().one().unwrap())?.unwrap();
350
351   let community_actor_id = CommunityForm::from_apub(&group, client, pool)
352     .await?
353     .actor_id;
354
355   let community = blocking(pool, move |conn| {
356     Community::read_from_actor_id(conn, &community_actor_id)
357   })
358   .await??;
359
360   let community_form = CommunityForm {
361     name: community.name.to_owned(),
362     title: community.title.to_owned(),
363     description: community.description.to_owned(),
364     category_id: community.category_id, // Note: need to keep this due to foreign key constraint
365     creator_id: community.creator_id,   // Note: need to keep this due to foreign key constraint
366     removed: None,
367     published: None,
368     updated: Some(naive_now()),
369     deleted: Some(false),
370     nsfw: community.nsfw,
371     actor_id: community.actor_id,
372     local: community.local,
373     private_key: community.private_key,
374     public_key: community.public_key,
375     last_refreshed_at: None,
376   };
377
378   let community_id = community.id;
379   blocking(pool, move |conn| {
380     Community::update(conn, community_id, &community_form)
381   })
382   .await??;
383
384   let community_id = community.id;
385   let res = CommunityResponse {
386     community: blocking(pool, move |conn| {
387       CommunityView::read(conn, community_id, None)
388     })
389     .await??,
390   };
391
392   let community_id = res.community.id;
393
394   chat_server.do_send(SendCommunityRoomMessage {
395     op: UserOperation::EditCommunity,
396     response: res,
397     community_id,
398     my_id: None,
399   });
400
401   announce_if_community_is_local(undo, &user, client, pool).await?;
402   Ok(HttpResponse::Ok().finish())
403 }
404
405 async fn receive_undo_remove_community(
406   undo: Undo,
407   remove: &Remove,
408   client: &Client,
409   pool: &DbPool,
410   chat_server: ChatServerParam,
411 ) -> Result<HttpResponse, LemmyError> {
412   let mod_ = get_user_from_activity(remove, client, pool).await?;
413   let group = GroupExt::from_any_base(remove.object().to_owned().one().unwrap())?.unwrap();
414
415   let community_actor_id = CommunityForm::from_apub(&group, client, pool)
416     .await?
417     .actor_id;
418
419   let community = blocking(pool, move |conn| {
420     Community::read_from_actor_id(conn, &community_actor_id)
421   })
422   .await??;
423
424   let community_form = CommunityForm {
425     name: community.name.to_owned(),
426     title: community.title.to_owned(),
427     description: community.description.to_owned(),
428     category_id: community.category_id, // Note: need to keep this due to foreign key constraint
429     creator_id: community.creator_id,   // Note: need to keep this due to foreign key constraint
430     removed: Some(false),
431     published: None,
432     updated: Some(naive_now()),
433     deleted: None,
434     nsfw: community.nsfw,
435     actor_id: community.actor_id,
436     local: community.local,
437     private_key: community.private_key,
438     public_key: community.public_key,
439     last_refreshed_at: None,
440   };
441
442   let community_id = community.id;
443   blocking(pool, move |conn| {
444     Community::update(conn, community_id, &community_form)
445   })
446   .await??;
447
448   let community_id = community.id;
449   let res = CommunityResponse {
450     community: blocking(pool, move |conn| {
451       CommunityView::read(conn, community_id, None)
452     })
453     .await??,
454   };
455
456   let community_id = res.community.id;
457
458   chat_server.do_send(SendCommunityRoomMessage {
459     op: UserOperation::EditCommunity,
460     response: res,
461     community_id,
462     my_id: None,
463   });
464
465   announce_if_community_is_local(undo, &mod_, client, pool).await?;
466   Ok(HttpResponse::Ok().finish())
467 }
468
469 async fn receive_undo_like_comment(
470   undo: Undo,
471   like: &Like,
472   client: &Client,
473   pool: &DbPool,
474   chat_server: ChatServerParam,
475 ) -> Result<HttpResponse, LemmyError> {
476   let user = get_user_from_activity(like, client, pool).await?;
477   let note = Note::from_any_base(like.object().to_owned().one().unwrap())?.unwrap();
478
479   let comment = CommentForm::from_apub(&note, client, pool).await?;
480
481   let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, client, pool)
482     .await?
483     .id;
484
485   let like_form = CommentLikeForm {
486     comment_id,
487     post_id: comment.post_id,
488     user_id: user.id,
489     score: 0,
490   };
491   blocking(pool, move |conn| CommentLike::remove(conn, &like_form)).await??;
492
493   // Refetch the view
494   let comment_view =
495     blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
496
497   // TODO get those recipient actor ids from somewhere
498   let recipient_ids = vec![];
499   let res = CommentResponse {
500     comment: comment_view,
501     recipient_ids,
502     form_id: None,
503   };
504
505   chat_server.do_send(SendComment {
506     op: UserOperation::CreateCommentLike,
507     comment: res,
508     my_id: None,
509   });
510
511   announce_if_community_is_local(undo, &user, client, pool).await?;
512   Ok(HttpResponse::Ok().finish())
513 }
514
515 async fn receive_undo_like_post(
516   undo: Undo,
517   like: &Like,
518   client: &Client,
519   pool: &DbPool,
520   chat_server: ChatServerParam,
521 ) -> Result<HttpResponse, LemmyError> {
522   let user = get_user_from_activity(like, client, pool).await?;
523   let page = PageExt::from_any_base(like.object().to_owned().one().unwrap())?.unwrap();
524
525   let post = PostForm::from_apub(&page, client, pool).await?;
526
527   let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, client, pool)
528     .await?
529     .id;
530
531   let like_form = PostLikeForm {
532     post_id,
533     user_id: user.id,
534     score: 1,
535   };
536   blocking(pool, move |conn| PostLike::remove(conn, &like_form)).await??;
537
538   // Refetch the view
539   let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
540
541   let res = PostResponse { post: post_view };
542
543   chat_server.do_send(SendPost {
544     op: UserOperation::CreatePostLike,
545     post: res,
546     my_id: None,
547   });
548
549   announce_if_community_is_local(undo, &user, client, pool).await?;
550   Ok(HttpResponse::Ok().finish())
551 }