2 fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
4 announce_if_community_is_local,
5 get_user_from_activity,
6 receive_unhandled_activity,
13 use activitystreams::{
15 base::{AnyBase, AsBase},
19 use actix_web::HttpResponse;
20 use anyhow::{anyhow, Context};
22 comment::{Comment, CommentForm, CommentLike},
23 comment_view::CommentView,
24 community::{Community, CommunityForm},
25 community_view::CommunityView,
27 post::{Post, PostForm, PostLike},
34 comment::CommentResponse,
35 community::CommunityResponse,
38 use lemmy_utils::{location_info, LemmyError};
39 use lemmy_websocket::{
40 messages::{SendComment, SendCommunityRoomMessage, SendPost},
45 pub async fn receive_undo(
47 context: &LemmyContext,
48 ) -> Result<HttpResponse, LemmyError> {
49 let undo = Undo::from_any_base(activity)?.context(location_info!())?;
50 match undo.object().as_single_kind_str() {
51 Some("Delete") => receive_undo_delete(undo, context).await,
52 Some("Remove") => receive_undo_remove(undo, context).await,
53 Some("Like") => receive_undo_like(undo, context).await,
54 Some("Dislike") => receive_undo_dislike(undo, context).await,
55 _ => receive_unhandled_activity(undo),
59 fn check_is_undo_valid<T, A>(outer_activity: &Undo, inner_activity: &T) -> Result<(), LemmyError>
61 T: AsBase<A> + ActorAndObjectRef,
63 let outer_actor = outer_activity.actor()?;
64 let outer_actor_uri = outer_actor
65 .as_single_xsd_any_uri()
66 .context(location_info!())?;
68 let inner_actor = inner_activity.actor()?;
69 let inner_actor_uri = inner_actor
70 .as_single_xsd_any_uri()
71 .context(location_info!())?;
73 if outer_actor_uri.domain() != inner_actor_uri.domain() {
74 Err(anyhow!("Cant undo activities from a different instance").into())
80 async fn receive_undo_delete(
82 context: &LemmyContext,
83 ) -> Result<HttpResponse, LemmyError> {
84 let delete = Delete::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
85 .context(location_info!())?;
86 check_is_undo_valid(&undo, &delete)?;
90 .context(location_info!())?;
92 "Note" => receive_undo_delete_comment(undo, &delete, context).await,
93 "Page" => receive_undo_delete_post(undo, &delete, context).await,
94 "Group" => receive_undo_delete_community(undo, &delete, context).await,
95 d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
99 async fn receive_undo_remove(
101 context: &LemmyContext,
102 ) -> Result<HttpResponse, LemmyError> {
103 let remove = Remove::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
104 .context(location_info!())?;
105 check_is_undo_valid(&undo, &remove)?;
109 .as_single_kind_str()
110 .context(location_info!())?;
112 "Note" => receive_undo_remove_comment(undo, &remove, context).await,
113 "Page" => receive_undo_remove_post(undo, &remove, context).await,
114 "Group" => receive_undo_remove_community(undo, &remove, context).await,
115 d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
119 async fn receive_undo_like(undo: Undo, context: &LemmyContext) -> Result<HttpResponse, LemmyError> {
120 let like = Like::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
121 .context(location_info!())?;
122 check_is_undo_valid(&undo, &like)?;
126 .as_single_kind_str()
127 .context(location_info!())?;
129 "Note" => receive_undo_like_comment(undo, &like, context).await,
130 "Page" => receive_undo_like_post(undo, &like, context).await,
131 d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
135 async fn receive_undo_dislike(
137 context: &LemmyContext,
138 ) -> Result<HttpResponse, LemmyError> {
139 let dislike = Dislike::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
140 .context(location_info!())?;
141 check_is_undo_valid(&undo, &dislike)?;
145 .as_single_kind_str()
146 .context(location_info!())?;
148 "Note" => receive_undo_dislike_comment(undo, &dislike, context).await,
149 "Page" => receive_undo_dislike_post(undo, &dislike, context).await,
150 d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
154 async fn receive_undo_delete_comment(
157 context: &LemmyContext,
158 ) -> Result<HttpResponse, LemmyError> {
159 let user = get_user_from_activity(delete, context).await?;
160 let note = Note::from_any_base(delete.object().to_owned().one().context(location_info!())?)?
161 .context(location_info!())?;
163 let comment_ap_id = CommentForm::from_apub(¬e, context, Some(user.actor_id()?))
167 let comment = get_or_fetch_and_insert_comment(&comment_ap_id, context).await?;
169 let comment_form = CommentForm {
170 content: comment.content.to_owned(),
171 parent_id: comment.parent_id,
172 post_id: comment.post_id,
173 creator_id: comment.creator_id,
175 deleted: Some(false),
178 updated: Some(naive_now()),
179 ap_id: Some(comment.ap_id),
180 local: comment.local,
182 let comment_id = comment.id;
183 blocking(context.pool(), move |conn| {
184 Comment::update(conn, comment_id, &comment_form)
189 let comment_id = comment.id;
190 let comment_view = blocking(context.pool(), move |conn| {
191 CommentView::read(conn, comment_id, None)
195 // TODO get those recipient actor ids from somewhere
196 let recipient_ids = vec![];
197 let res = CommentResponse {
198 comment: comment_view,
203 context.chat_server().do_send(SendComment {
204 op: UserOperation::EditComment,
209 announce_if_community_is_local(undo, &user, context).await?;
210 Ok(HttpResponse::Ok().finish())
213 async fn receive_undo_remove_comment(
216 context: &LemmyContext,
217 ) -> Result<HttpResponse, LemmyError> {
218 let mod_ = get_user_from_activity(remove, context).await?;
219 let note = Note::from_any_base(remove.object().to_owned().one().context(location_info!())?)?
220 .context(location_info!())?;
222 let comment_ap_id = CommentForm::from_apub(¬e, context, None)
226 let comment = get_or_fetch_and_insert_comment(&comment_ap_id, context).await?;
228 let comment_form = CommentForm {
229 content: comment.content.to_owned(),
230 parent_id: comment.parent_id,
231 post_id: comment.post_id,
232 creator_id: comment.creator_id,
233 removed: Some(false),
237 updated: Some(naive_now()),
238 ap_id: Some(comment.ap_id),
239 local: comment.local,
241 let comment_id = comment.id;
242 blocking(context.pool(), move |conn| {
243 Comment::update(conn, comment_id, &comment_form)
248 let comment_id = comment.id;
249 let comment_view = blocking(context.pool(), move |conn| {
250 CommentView::read(conn, comment_id, None)
254 // TODO get those recipient actor ids from somewhere
255 let recipient_ids = vec![];
256 let res = CommentResponse {
257 comment: comment_view,
262 context.chat_server().do_send(SendComment {
263 op: UserOperation::EditComment,
268 announce_if_community_is_local(undo, &mod_, context).await?;
269 Ok(HttpResponse::Ok().finish())
272 async fn receive_undo_delete_post(
275 context: &LemmyContext,
276 ) -> Result<HttpResponse, LemmyError> {
277 let user = get_user_from_activity(delete, context).await?;
278 let page = PageExt::from_any_base(delete.object().to_owned().one().context(location_info!())?)?
279 .context(location_info!())?;
281 let post_ap_id = PostForm::from_apub(&page, context, Some(user.actor_id()?))
285 let post = get_or_fetch_and_insert_post(&post_ap_id, context).await?;
287 let post_form = PostForm {
288 name: post.name.to_owned(),
289 url: post.url.to_owned(),
290 body: post.body.to_owned(),
291 creator_id: post.creator_id.to_owned(),
292 community_id: post.community_id,
294 deleted: Some(false),
298 updated: Some(naive_now()),
299 embed_title: post.embed_title,
300 embed_description: post.embed_description,
301 embed_html: post.embed_html,
302 thumbnail_url: post.thumbnail_url,
303 ap_id: Some(post.ap_id),
307 let post_id = post.id;
308 blocking(context.pool(), move |conn| {
309 Post::update(conn, post_id, &post_form)
314 let post_id = post.id;
315 let post_view = blocking(context.pool(), move |conn| {
316 PostView::read(conn, post_id, None)
320 let res = PostResponse { post: post_view };
322 context.chat_server().do_send(SendPost {
323 op: UserOperation::EditPost,
328 announce_if_community_is_local(undo, &user, context).await?;
329 Ok(HttpResponse::Ok().finish())
332 async fn receive_undo_remove_post(
335 context: &LemmyContext,
336 ) -> Result<HttpResponse, LemmyError> {
337 let mod_ = get_user_from_activity(remove, context).await?;
338 let page = PageExt::from_any_base(remove.object().to_owned().one().context(location_info!())?)?
339 .context(location_info!())?;
341 let post_ap_id = PostForm::from_apub(&page, context, None)
345 let post = get_or_fetch_and_insert_post(&post_ap_id, context).await?;
347 let post_form = PostForm {
348 name: post.name.to_owned(),
349 url: post.url.to_owned(),
350 body: post.body.to_owned(),
351 creator_id: post.creator_id.to_owned(),
352 community_id: post.community_id,
353 removed: Some(false),
358 updated: Some(naive_now()),
359 embed_title: post.embed_title,
360 embed_description: post.embed_description,
361 embed_html: post.embed_html,
362 thumbnail_url: post.thumbnail_url,
363 ap_id: Some(post.ap_id),
367 let post_id = post.id;
368 blocking(context.pool(), move |conn| {
369 Post::update(conn, post_id, &post_form)
374 let post_id = post.id;
375 let post_view = blocking(context.pool(), move |conn| {
376 PostView::read(conn, post_id, None)
380 let res = PostResponse { post: post_view };
382 context.chat_server().do_send(SendPost {
383 op: UserOperation::EditPost,
388 announce_if_community_is_local(undo, &mod_, context).await?;
389 Ok(HttpResponse::Ok().finish())
392 async fn receive_undo_delete_community(
395 context: &LemmyContext,
396 ) -> Result<HttpResponse, LemmyError> {
397 let user = get_user_from_activity(delete, context).await?;
398 let group = GroupExt::from_any_base(delete.object().to_owned().one().context(location_info!())?)?
399 .context(location_info!())?;
401 let community_actor_id = CommunityForm::from_apub(&group, context, Some(user.actor_id()?))
404 .context(location_info!())?;
406 let community = blocking(context.pool(), move |conn| {
407 Community::read_from_actor_id(conn, &community_actor_id)
411 let community_form = CommunityForm {
412 name: community.name.to_owned(),
413 title: community.title.to_owned(),
414 description: community.description.to_owned(),
415 category_id: community.category_id, // Note: need to keep this due to foreign key constraint
416 creator_id: community.creator_id, // Note: need to keep this due to foreign key constraint
419 updated: Some(naive_now()),
420 deleted: Some(false),
421 nsfw: community.nsfw,
422 actor_id: Some(community.actor_id),
423 local: community.local,
424 private_key: community.private_key,
425 public_key: community.public_key,
426 last_refreshed_at: None,
427 icon: Some(community.icon.to_owned()),
428 banner: Some(community.banner.to_owned()),
431 let community_id = community.id;
432 blocking(context.pool(), move |conn| {
433 Community::update(conn, community_id, &community_form)
437 let community_id = community.id;
438 let res = CommunityResponse {
439 community: blocking(context.pool(), move |conn| {
440 CommunityView::read(conn, community_id, None)
445 let community_id = res.community.id;
447 context.chat_server().do_send(SendCommunityRoomMessage {
448 op: UserOperation::EditCommunity,
454 announce_if_community_is_local(undo, &user, context).await?;
455 Ok(HttpResponse::Ok().finish())
458 async fn receive_undo_remove_community(
461 context: &LemmyContext,
462 ) -> Result<HttpResponse, LemmyError> {
463 let mod_ = get_user_from_activity(remove, context).await?;
464 let group = GroupExt::from_any_base(remove.object().to_owned().one().context(location_info!())?)?
465 .context(location_info!())?;
467 let community_actor_id = CommunityForm::from_apub(&group, context, Some(mod_.actor_id()?))
470 .context(location_info!())?;
472 let community = blocking(context.pool(), move |conn| {
473 Community::read_from_actor_id(conn, &community_actor_id)
477 let community_form = CommunityForm {
478 name: community.name.to_owned(),
479 title: community.title.to_owned(),
480 description: community.description.to_owned(),
481 category_id: community.category_id, // Note: need to keep this due to foreign key constraint
482 creator_id: community.creator_id, // Note: need to keep this due to foreign key constraint
483 removed: Some(false),
485 updated: Some(naive_now()),
487 nsfw: community.nsfw,
488 actor_id: Some(community.actor_id),
489 local: community.local,
490 private_key: community.private_key,
491 public_key: community.public_key,
492 last_refreshed_at: None,
493 icon: Some(community.icon.to_owned()),
494 banner: Some(community.banner.to_owned()),
497 let community_id = community.id;
498 blocking(context.pool(), move |conn| {
499 Community::update(conn, community_id, &community_form)
503 let community_id = community.id;
504 let res = CommunityResponse {
505 community: blocking(context.pool(), move |conn| {
506 CommunityView::read(conn, community_id, None)
511 let community_id = res.community.id;
513 context.chat_server().do_send(SendCommunityRoomMessage {
514 op: UserOperation::EditCommunity,
520 announce_if_community_is_local(undo, &mod_, context).await?;
521 Ok(HttpResponse::Ok().finish())
524 async fn receive_undo_like_comment(
527 context: &LemmyContext,
528 ) -> Result<HttpResponse, LemmyError> {
529 let user = get_user_from_activity(like, context).await?;
530 let note = Note::from_any_base(like.object().to_owned().one().context(location_info!())?)?
531 .context(location_info!())?;
533 let comment = CommentForm::from_apub(¬e, context, None).await?;
535 let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context)
539 let user_id = user.id;
540 blocking(context.pool(), move |conn| {
541 CommentLike::remove(conn, user_id, comment_id)
546 let comment_view = blocking(context.pool(), move |conn| {
547 CommentView::read(conn, comment_id, None)
551 // TODO get those recipient actor ids from somewhere
552 let recipient_ids = vec![];
553 let res = CommentResponse {
554 comment: comment_view,
559 context.chat_server().do_send(SendComment {
560 op: UserOperation::CreateCommentLike,
565 announce_if_community_is_local(undo, &user, context).await?;
566 Ok(HttpResponse::Ok().finish())
569 async fn receive_undo_like_post(
572 context: &LemmyContext,
573 ) -> Result<HttpResponse, LemmyError> {
574 let user = get_user_from_activity(like, context).await?;
575 let page = PageExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
576 .context(location_info!())?;
578 let post = PostForm::from_apub(&page, context, None).await?;
580 let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context)
584 let user_id = user.id;
585 blocking(context.pool(), move |conn| {
586 PostLike::remove(conn, user_id, post_id)
591 let post_view = blocking(context.pool(), move |conn| {
592 PostView::read(conn, post_id, None)
596 let res = PostResponse { post: post_view };
598 context.chat_server().do_send(SendPost {
599 op: UserOperation::CreatePostLike,
604 announce_if_community_is_local(undo, &user, context).await?;
605 Ok(HttpResponse::Ok().finish())
608 async fn receive_undo_dislike_comment(
611 context: &LemmyContext,
612 ) -> Result<HttpResponse, LemmyError> {
613 let user = get_user_from_activity(dislike, context).await?;
614 let note = Note::from_any_base(
619 .context(location_info!())?,
621 .context(location_info!())?;
623 let comment = CommentForm::from_apub(¬e, context, None).await?;
625 let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context)
629 let user_id = user.id;
630 blocking(context.pool(), move |conn| {
631 CommentLike::remove(conn, user_id, comment_id)
636 let comment_view = blocking(context.pool(), move |conn| {
637 CommentView::read(conn, comment_id, None)
641 // TODO get those recipient actor ids from somewhere
642 let recipient_ids = vec![];
643 let res = CommentResponse {
644 comment: comment_view,
649 context.chat_server().do_send(SendComment {
650 op: UserOperation::CreateCommentLike,
655 announce_if_community_is_local(undo, &user, context).await?;
656 Ok(HttpResponse::Ok().finish())
659 async fn receive_undo_dislike_post(
662 context: &LemmyContext,
663 ) -> Result<HttpResponse, LemmyError> {
664 let user = get_user_from_activity(dislike, context).await?;
665 let page = PageExt::from_any_base(
670 .context(location_info!())?,
672 .context(location_info!())?;
674 let post = PostForm::from_apub(&page, context, None).await?;
676 let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context)
680 let user_id = user.id;
681 blocking(context.pool(), move |conn| {
682 PostLike::remove(conn, user_id, post_id)
687 let post_view = blocking(context.pool(), move |conn| {
688 PostView::read(conn, post_id, None)
692 let res = PostResponse { post: post_view };
694 context.chat_server().do_send(SendPost {
695 op: UserOperation::CreatePostLike,
700 announce_if_community_is_local(undo, &user, context).await?;
701 Ok(HttpResponse::Ok().finish())