3 fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
5 announce_if_community_is_local,
6 get_user_from_activity,
7 receive_unhandled_activity,
16 use activitystreams::{
18 base::{AnyBase, AsBase},
22 use actix_web::HttpResponse;
23 use anyhow::{anyhow, Context};
25 comment::{Comment, CommentForm, CommentLike},
26 comment_view::CommentView,
27 community::{Community, CommunityForm},
28 community_view::CommunityView,
30 post::{Post, PostForm, PostLike},
37 comment::CommentResponse,
38 community::CommunityResponse,
40 websocket::{SendComment, SendCommunityRoomMessage, SendPost, UserOperation},
42 use lemmy_utils::{location_info, LemmyError};
44 pub async fn receive_undo(
46 context: &LemmyContext,
47 ) -> Result<HttpResponse, LemmyError> {
48 let undo = Undo::from_any_base(activity)?.context(location_info!())?;
49 match undo.object().as_single_kind_str() {
50 Some("Delete") => receive_undo_delete(undo, context).await,
51 Some("Remove") => receive_undo_remove(undo, context).await,
52 Some("Like") => receive_undo_like(undo, context).await,
53 Some("Dislike") => receive_undo_dislike(undo, context).await,
54 _ => receive_unhandled_activity(undo),
58 fn check_is_undo_valid<T, A>(outer_activity: &Undo, inner_activity: &T) -> Result<(), LemmyError>
60 T: AsBase<A> + ActorAndObjectRef,
62 let outer_actor = outer_activity.actor()?;
63 let outer_actor_uri = outer_actor
64 .as_single_xsd_any_uri()
65 .context(location_info!())?;
67 let inner_actor = inner_activity.actor()?;
68 let inner_actor_uri = inner_actor
69 .as_single_xsd_any_uri()
70 .context(location_info!())?;
72 if outer_actor_uri.domain() != inner_actor_uri.domain() {
73 Err(anyhow!("Cant undo activities from a different instance").into())
79 async fn receive_undo_delete(
81 context: &LemmyContext,
82 ) -> Result<HttpResponse, LemmyError> {
83 let delete = Delete::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
84 .context(location_info!())?;
85 check_is_undo_valid(&undo, &delete)?;
89 .context(location_info!())?;
91 "Note" => receive_undo_delete_comment(undo, &delete, context).await,
92 "Page" => receive_undo_delete_post(undo, &delete, context).await,
93 "Group" => receive_undo_delete_community(undo, &delete, context).await,
94 d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
98 async fn receive_undo_remove(
100 context: &LemmyContext,
101 ) -> Result<HttpResponse, LemmyError> {
102 let remove = Remove::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
103 .context(location_info!())?;
104 check_is_undo_valid(&undo, &remove)?;
108 .as_single_kind_str()
109 .context(location_info!())?;
111 "Note" => receive_undo_remove_comment(undo, &remove, context).await,
112 "Page" => receive_undo_remove_post(undo, &remove, context).await,
113 "Group" => receive_undo_remove_community(undo, &remove, context).await,
114 d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
118 async fn receive_undo_like(undo: Undo, context: &LemmyContext) -> Result<HttpResponse, LemmyError> {
119 let like = Like::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
120 .context(location_info!())?;
121 check_is_undo_valid(&undo, &like)?;
125 .as_single_kind_str()
126 .context(location_info!())?;
128 "Note" => receive_undo_like_comment(undo, &like, context).await,
129 "Page" => receive_undo_like_post(undo, &like, context).await,
130 d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
134 async fn receive_undo_dislike(
136 context: &LemmyContext,
137 ) -> Result<HttpResponse, LemmyError> {
138 let dislike = Dislike::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
139 .context(location_info!())?;
140 check_is_undo_valid(&undo, &dislike)?;
144 .as_single_kind_str()
145 .context(location_info!())?;
147 "Note" => receive_undo_dislike_comment(undo, &dislike, context).await,
148 "Page" => receive_undo_dislike_post(undo, &dislike, context).await,
149 d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
153 async fn receive_undo_delete_comment(
156 context: &LemmyContext,
157 ) -> Result<HttpResponse, LemmyError> {
158 let user = get_user_from_activity(delete, context).await?;
159 let note = Note::from_any_base(delete.object().to_owned().one().context(location_info!())?)?
160 .context(location_info!())?;
162 let comment_ap_id = CommentForm::from_apub(¬e, context, Some(user.actor_id()?))
166 let comment = get_or_fetch_and_insert_comment(&comment_ap_id, context).await?;
168 let comment_form = CommentForm {
169 content: comment.content.to_owned(),
170 parent_id: comment.parent_id,
171 post_id: comment.post_id,
172 creator_id: comment.creator_id,
174 deleted: Some(false),
177 updated: Some(naive_now()),
178 ap_id: Some(comment.ap_id),
179 local: comment.local,
181 let comment_id = comment.id;
182 blocking(context.pool(), move |conn| {
183 Comment::update(conn, comment_id, &comment_form)
188 let comment_id = comment.id;
189 let comment_view = blocking(context.pool(), move |conn| {
190 CommentView::read(conn, comment_id, None)
194 // TODO get those recipient actor ids from somewhere
195 let recipient_ids = vec![];
196 let res = CommentResponse {
197 comment: comment_view,
202 context.chat_server().do_send(SendComment {
203 op: UserOperation::EditComment,
208 announce_if_community_is_local(undo, &user, context).await?;
209 Ok(HttpResponse::Ok().finish())
212 async fn receive_undo_remove_comment(
215 context: &LemmyContext,
216 ) -> Result<HttpResponse, LemmyError> {
217 let mod_ = get_user_from_activity(remove, context).await?;
218 let note = Note::from_any_base(remove.object().to_owned().one().context(location_info!())?)?
219 .context(location_info!())?;
221 let comment_ap_id = CommentForm::from_apub(¬e, context, None)
225 let comment = get_or_fetch_and_insert_comment(&comment_ap_id, context).await?;
227 let comment_form = CommentForm {
228 content: comment.content.to_owned(),
229 parent_id: comment.parent_id,
230 post_id: comment.post_id,
231 creator_id: comment.creator_id,
232 removed: Some(false),
236 updated: Some(naive_now()),
237 ap_id: Some(comment.ap_id),
238 local: comment.local,
240 let comment_id = comment.id;
241 blocking(context.pool(), move |conn| {
242 Comment::update(conn, comment_id, &comment_form)
247 let comment_id = comment.id;
248 let comment_view = blocking(context.pool(), move |conn| {
249 CommentView::read(conn, comment_id, None)
253 // TODO get those recipient actor ids from somewhere
254 let recipient_ids = vec![];
255 let res = CommentResponse {
256 comment: comment_view,
261 context.chat_server().do_send(SendComment {
262 op: UserOperation::EditComment,
267 announce_if_community_is_local(undo, &mod_, context).await?;
268 Ok(HttpResponse::Ok().finish())
271 async fn receive_undo_delete_post(
274 context: &LemmyContext,
275 ) -> Result<HttpResponse, LemmyError> {
276 let user = get_user_from_activity(delete, context).await?;
277 let page = PageExt::from_any_base(delete.object().to_owned().one().context(location_info!())?)?
278 .context(location_info!())?;
280 let post_ap_id = PostForm::from_apub(&page, context, Some(user.actor_id()?))
284 let post = get_or_fetch_and_insert_post(&post_ap_id, context).await?;
286 let post_form = PostForm {
287 name: post.name.to_owned(),
288 url: post.url.to_owned(),
289 body: post.body.to_owned(),
290 creator_id: post.creator_id.to_owned(),
291 community_id: post.community_id,
293 deleted: Some(false),
297 updated: Some(naive_now()),
298 embed_title: post.embed_title,
299 embed_description: post.embed_description,
300 embed_html: post.embed_html,
301 thumbnail_url: post.thumbnail_url,
302 ap_id: Some(post.ap_id),
306 let post_id = post.id;
307 blocking(context.pool(), move |conn| {
308 Post::update(conn, post_id, &post_form)
313 let post_id = post.id;
314 let post_view = blocking(context.pool(), move |conn| {
315 PostView::read(conn, post_id, None)
319 let res = PostResponse { post: post_view };
321 context.chat_server().do_send(SendPost {
322 op: UserOperation::EditPost,
327 announce_if_community_is_local(undo, &user, context).await?;
328 Ok(HttpResponse::Ok().finish())
331 async fn receive_undo_remove_post(
334 context: &LemmyContext,
335 ) -> Result<HttpResponse, LemmyError> {
336 let mod_ = get_user_from_activity(remove, context).await?;
337 let page = PageExt::from_any_base(remove.object().to_owned().one().context(location_info!())?)?
338 .context(location_info!())?;
340 let post_ap_id = PostForm::from_apub(&page, context, None)
344 let post = get_or_fetch_and_insert_post(&post_ap_id, context).await?;
346 let post_form = PostForm {
347 name: post.name.to_owned(),
348 url: post.url.to_owned(),
349 body: post.body.to_owned(),
350 creator_id: post.creator_id.to_owned(),
351 community_id: post.community_id,
352 removed: Some(false),
357 updated: Some(naive_now()),
358 embed_title: post.embed_title,
359 embed_description: post.embed_description,
360 embed_html: post.embed_html,
361 thumbnail_url: post.thumbnail_url,
362 ap_id: Some(post.ap_id),
366 let post_id = post.id;
367 blocking(context.pool(), move |conn| {
368 Post::update(conn, post_id, &post_form)
373 let post_id = post.id;
374 let post_view = blocking(context.pool(), move |conn| {
375 PostView::read(conn, post_id, None)
379 let res = PostResponse { post: post_view };
381 context.chat_server().do_send(SendPost {
382 op: UserOperation::EditPost,
387 announce_if_community_is_local(undo, &mod_, context).await?;
388 Ok(HttpResponse::Ok().finish())
391 async fn receive_undo_delete_community(
394 context: &LemmyContext,
395 ) -> Result<HttpResponse, LemmyError> {
396 let user = get_user_from_activity(delete, context).await?;
397 let group = GroupExt::from_any_base(delete.object().to_owned().one().context(location_info!())?)?
398 .context(location_info!())?;
400 let community_actor_id = CommunityForm::from_apub(&group, context, Some(user.actor_id()?))
403 .context(location_info!())?;
405 let community = blocking(context.pool(), move |conn| {
406 Community::read_from_actor_id(conn, &community_actor_id)
410 let community_form = CommunityForm {
411 name: community.name.to_owned(),
412 title: community.title.to_owned(),
413 description: community.description.to_owned(),
414 category_id: community.category_id, // Note: need to keep this due to foreign key constraint
415 creator_id: community.creator_id, // Note: need to keep this due to foreign key constraint
418 updated: Some(naive_now()),
419 deleted: Some(false),
420 nsfw: community.nsfw,
421 actor_id: Some(community.actor_id),
422 local: community.local,
423 private_key: community.private_key,
424 public_key: community.public_key,
425 last_refreshed_at: None,
426 icon: Some(community.icon.to_owned()),
427 banner: Some(community.banner.to_owned()),
430 let community_id = community.id;
431 blocking(context.pool(), move |conn| {
432 Community::update(conn, community_id, &community_form)
436 let community_id = community.id;
437 let res = CommunityResponse {
438 community: blocking(context.pool(), move |conn| {
439 CommunityView::read(conn, community_id, None)
444 let community_id = res.community.id;
446 context.chat_server().do_send(SendCommunityRoomMessage {
447 op: UserOperation::EditCommunity,
453 announce_if_community_is_local(undo, &user, context).await?;
454 Ok(HttpResponse::Ok().finish())
457 async fn receive_undo_remove_community(
460 context: &LemmyContext,
461 ) -> Result<HttpResponse, LemmyError> {
462 let mod_ = get_user_from_activity(remove, context).await?;
463 let group = GroupExt::from_any_base(remove.object().to_owned().one().context(location_info!())?)?
464 .context(location_info!())?;
466 let community_actor_id = CommunityForm::from_apub(&group, context, Some(mod_.actor_id()?))
469 .context(location_info!())?;
471 let community = blocking(context.pool(), move |conn| {
472 Community::read_from_actor_id(conn, &community_actor_id)
476 let community_form = CommunityForm {
477 name: community.name.to_owned(),
478 title: community.title.to_owned(),
479 description: community.description.to_owned(),
480 category_id: community.category_id, // Note: need to keep this due to foreign key constraint
481 creator_id: community.creator_id, // Note: need to keep this due to foreign key constraint
482 removed: Some(false),
484 updated: Some(naive_now()),
486 nsfw: community.nsfw,
487 actor_id: Some(community.actor_id),
488 local: community.local,
489 private_key: community.private_key,
490 public_key: community.public_key,
491 last_refreshed_at: None,
492 icon: Some(community.icon.to_owned()),
493 banner: Some(community.banner.to_owned()),
496 let community_id = community.id;
497 blocking(context.pool(), move |conn| {
498 Community::update(conn, community_id, &community_form)
502 let community_id = community.id;
503 let res = CommunityResponse {
504 community: blocking(context.pool(), move |conn| {
505 CommunityView::read(conn, community_id, None)
510 let community_id = res.community.id;
512 context.chat_server().do_send(SendCommunityRoomMessage {
513 op: UserOperation::EditCommunity,
519 announce_if_community_is_local(undo, &mod_, context).await?;
520 Ok(HttpResponse::Ok().finish())
523 async fn receive_undo_like_comment(
526 context: &LemmyContext,
527 ) -> Result<HttpResponse, LemmyError> {
528 let user = get_user_from_activity(like, context).await?;
529 let note = Note::from_any_base(like.object().to_owned().one().context(location_info!())?)?
530 .context(location_info!())?;
532 let comment = CommentForm::from_apub(¬e, context, None).await?;
534 let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context)
538 let user_id = user.id;
539 blocking(context.pool(), move |conn| {
540 CommentLike::remove(conn, user_id, comment_id)
545 let comment_view = blocking(context.pool(), move |conn| {
546 CommentView::read(conn, comment_id, None)
550 // TODO get those recipient actor ids from somewhere
551 let recipient_ids = vec![];
552 let res = CommentResponse {
553 comment: comment_view,
558 context.chat_server().do_send(SendComment {
559 op: UserOperation::CreateCommentLike,
564 announce_if_community_is_local(undo, &user, context).await?;
565 Ok(HttpResponse::Ok().finish())
568 async fn receive_undo_like_post(
571 context: &LemmyContext,
572 ) -> Result<HttpResponse, LemmyError> {
573 let user = get_user_from_activity(like, context).await?;
574 let page = PageExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
575 .context(location_info!())?;
577 let post = PostForm::from_apub(&page, context, None).await?;
579 let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context)
583 let user_id = user.id;
584 blocking(context.pool(), move |conn| {
585 PostLike::remove(conn, user_id, post_id)
590 let post_view = blocking(context.pool(), move |conn| {
591 PostView::read(conn, post_id, None)
595 let res = PostResponse { post: post_view };
597 context.chat_server().do_send(SendPost {
598 op: UserOperation::CreatePostLike,
603 announce_if_community_is_local(undo, &user, context).await?;
604 Ok(HttpResponse::Ok().finish())
607 async fn receive_undo_dislike_comment(
610 context: &LemmyContext,
611 ) -> Result<HttpResponse, LemmyError> {
612 let user = get_user_from_activity(dislike, context).await?;
613 let note = Note::from_any_base(
618 .context(location_info!())?,
620 .context(location_info!())?;
622 let comment = CommentForm::from_apub(¬e, context, None).await?;
624 let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context)
628 let user_id = user.id;
629 blocking(context.pool(), move |conn| {
630 CommentLike::remove(conn, user_id, comment_id)
635 let comment_view = blocking(context.pool(), move |conn| {
636 CommentView::read(conn, comment_id, None)
640 // TODO get those recipient actor ids from somewhere
641 let recipient_ids = vec![];
642 let res = CommentResponse {
643 comment: comment_view,
648 context.chat_server().do_send(SendComment {
649 op: UserOperation::CreateCommentLike,
654 announce_if_community_is_local(undo, &user, context).await?;
655 Ok(HttpResponse::Ok().finish())
658 async fn receive_undo_dislike_post(
661 context: &LemmyContext,
662 ) -> Result<HttpResponse, LemmyError> {
663 let user = get_user_from_activity(dislike, context).await?;
664 let page = PageExt::from_any_base(
669 .context(location_info!())?,
671 .context(location_info!())?;
673 let post = PostForm::from_apub(&page, context, None).await?;
675 let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context)
679 let user_id = user.id;
680 blocking(context.pool(), move |conn| {
681 PostLike::remove(conn, user_id, post_id)
686 let post_view = blocking(context.pool(), move |conn| {
687 PostView::read(conn, post_id, None)
691 let res = PostResponse { post: post_view };
693 context.chat_server().do_send(SendPost {
694 op: UserOperation::CreatePostLike,
699 announce_if_community_is_local(undo, &user, context).await?;
700 Ok(HttpResponse::Ok().finish())