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,
15 messages::{SendComment, SendCommunityRoomMessage, SendPost},
20 use activitystreams::{
22 base::{AnyBase, AsBase},
26 use actix_web::HttpResponse;
27 use anyhow::{anyhow, Context};
28 use lemmy_api_structs::{
30 comment::CommentResponse,
31 community::CommunityResponse,
35 comment::{Comment, CommentForm, CommentLike},
36 comment_view::CommentView,
37 community::{Community, CommunityForm},
38 community_view::CommunityView,
40 post::{Post, PostForm, PostLike},
45 use lemmy_utils::{location_info, LemmyError};
47 pub async fn receive_undo(
49 context: &LemmyContext,
50 ) -> Result<HttpResponse, LemmyError> {
51 let undo = Undo::from_any_base(activity)?.context(location_info!())?;
52 match undo.object().as_single_kind_str() {
53 Some("Delete") => receive_undo_delete(undo, context).await,
54 Some("Remove") => receive_undo_remove(undo, context).await,
55 Some("Like") => receive_undo_like(undo, context).await,
56 Some("Dislike") => receive_undo_dislike(undo, context).await,
57 _ => receive_unhandled_activity(undo),
61 fn check_is_undo_valid<T, A>(outer_activity: &Undo, inner_activity: &T) -> Result<(), LemmyError>
63 T: AsBase<A> + ActorAndObjectRef,
65 let outer_actor = outer_activity.actor()?;
66 let outer_actor_uri = outer_actor
67 .as_single_xsd_any_uri()
68 .context(location_info!())?;
70 let inner_actor = inner_activity.actor()?;
71 let inner_actor_uri = inner_actor
72 .as_single_xsd_any_uri()
73 .context(location_info!())?;
75 if outer_actor_uri.domain() != inner_actor_uri.domain() {
76 Err(anyhow!("Cant undo activities from a different instance").into())
82 async fn receive_undo_delete(
84 context: &LemmyContext,
85 ) -> Result<HttpResponse, LemmyError> {
86 let delete = Delete::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
87 .context(location_info!())?;
88 check_is_undo_valid(&undo, &delete)?;
92 .context(location_info!())?;
94 "Note" => receive_undo_delete_comment(undo, &delete, context).await,
95 "Page" => receive_undo_delete_post(undo, &delete, context).await,
96 "Group" => receive_undo_delete_community(undo, &delete, context).await,
97 d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
101 async fn receive_undo_remove(
103 context: &LemmyContext,
104 ) -> Result<HttpResponse, LemmyError> {
105 let remove = Remove::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
106 .context(location_info!())?;
107 check_is_undo_valid(&undo, &remove)?;
111 .as_single_kind_str()
112 .context(location_info!())?;
114 "Note" => receive_undo_remove_comment(undo, &remove, context).await,
115 "Page" => receive_undo_remove_post(undo, &remove, context).await,
116 "Group" => receive_undo_remove_community(undo, &remove, context).await,
117 d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
121 async fn receive_undo_like(undo: Undo, context: &LemmyContext) -> Result<HttpResponse, LemmyError> {
122 let like = Like::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
123 .context(location_info!())?;
124 check_is_undo_valid(&undo, &like)?;
128 .as_single_kind_str()
129 .context(location_info!())?;
131 "Note" => receive_undo_like_comment(undo, &like, context).await,
132 "Page" => receive_undo_like_post(undo, &like, context).await,
133 d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
137 async fn receive_undo_dislike(
139 context: &LemmyContext,
140 ) -> Result<HttpResponse, LemmyError> {
141 let dislike = Dislike::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
142 .context(location_info!())?;
143 check_is_undo_valid(&undo, &dislike)?;
147 .as_single_kind_str()
148 .context(location_info!())?;
150 "Note" => receive_undo_dislike_comment(undo, &dislike, context).await,
151 "Page" => receive_undo_dislike_post(undo, &dislike, context).await,
152 d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
156 async fn receive_undo_delete_comment(
159 context: &LemmyContext,
160 ) -> Result<HttpResponse, LemmyError> {
161 let user = get_user_from_activity(delete, context).await?;
162 let note = Note::from_any_base(delete.object().to_owned().one().context(location_info!())?)?
163 .context(location_info!())?;
165 let comment_ap_id = CommentForm::from_apub(¬e, context, Some(user.actor_id()?))
169 let comment = get_or_fetch_and_insert_comment(&comment_ap_id, context).await?;
171 let comment_form = CommentForm {
172 content: comment.content.to_owned(),
173 parent_id: comment.parent_id,
174 post_id: comment.post_id,
175 creator_id: comment.creator_id,
177 deleted: Some(false),
180 updated: Some(naive_now()),
181 ap_id: Some(comment.ap_id),
182 local: comment.local,
184 let comment_id = comment.id;
185 blocking(context.pool(), move |conn| {
186 Comment::update(conn, comment_id, &comment_form)
191 let comment_id = comment.id;
192 let comment_view = blocking(context.pool(), move |conn| {
193 CommentView::read(conn, comment_id, None)
197 // TODO get those recipient actor ids from somewhere
198 let recipient_ids = vec![];
199 let res = CommentResponse {
200 comment: comment_view,
205 context.chat_server().do_send(SendComment {
206 op: UserOperation::EditComment,
211 announce_if_community_is_local(undo, &user, context).await?;
212 Ok(HttpResponse::Ok().finish())
215 async fn receive_undo_remove_comment(
218 context: &LemmyContext,
219 ) -> Result<HttpResponse, LemmyError> {
220 let mod_ = get_user_from_activity(remove, context).await?;
221 let note = Note::from_any_base(remove.object().to_owned().one().context(location_info!())?)?
222 .context(location_info!())?;
224 let comment_ap_id = CommentForm::from_apub(¬e, context, None)
228 let comment = get_or_fetch_and_insert_comment(&comment_ap_id, context).await?;
230 let comment_form = CommentForm {
231 content: comment.content.to_owned(),
232 parent_id: comment.parent_id,
233 post_id: comment.post_id,
234 creator_id: comment.creator_id,
235 removed: Some(false),
239 updated: Some(naive_now()),
240 ap_id: Some(comment.ap_id),
241 local: comment.local,
243 let comment_id = comment.id;
244 blocking(context.pool(), move |conn| {
245 Comment::update(conn, comment_id, &comment_form)
250 let comment_id = comment.id;
251 let comment_view = blocking(context.pool(), move |conn| {
252 CommentView::read(conn, comment_id, None)
256 // TODO get those recipient actor ids from somewhere
257 let recipient_ids = vec![];
258 let res = CommentResponse {
259 comment: comment_view,
264 context.chat_server().do_send(SendComment {
265 op: UserOperation::EditComment,
270 announce_if_community_is_local(undo, &mod_, context).await?;
271 Ok(HttpResponse::Ok().finish())
274 async fn receive_undo_delete_post(
277 context: &LemmyContext,
278 ) -> Result<HttpResponse, LemmyError> {
279 let user = get_user_from_activity(delete, context).await?;
280 let page = PageExt::from_any_base(delete.object().to_owned().one().context(location_info!())?)?
281 .context(location_info!())?;
283 let post_ap_id = PostForm::from_apub(&page, context, Some(user.actor_id()?))
287 let post = get_or_fetch_and_insert_post(&post_ap_id, context).await?;
289 let post_form = PostForm {
290 name: post.name.to_owned(),
291 url: post.url.to_owned(),
292 body: post.body.to_owned(),
293 creator_id: post.creator_id.to_owned(),
294 community_id: post.community_id,
296 deleted: Some(false),
300 updated: Some(naive_now()),
301 embed_title: post.embed_title,
302 embed_description: post.embed_description,
303 embed_html: post.embed_html,
304 thumbnail_url: post.thumbnail_url,
305 ap_id: Some(post.ap_id),
309 let post_id = post.id;
310 blocking(context.pool(), move |conn| {
311 Post::update(conn, post_id, &post_form)
316 let post_id = post.id;
317 let post_view = blocking(context.pool(), move |conn| {
318 PostView::read(conn, post_id, None)
322 let res = PostResponse { post: post_view };
324 context.chat_server().do_send(SendPost {
325 op: UserOperation::EditPost,
330 announce_if_community_is_local(undo, &user, context).await?;
331 Ok(HttpResponse::Ok().finish())
334 async fn receive_undo_remove_post(
337 context: &LemmyContext,
338 ) -> Result<HttpResponse, LemmyError> {
339 let mod_ = get_user_from_activity(remove, context).await?;
340 let page = PageExt::from_any_base(remove.object().to_owned().one().context(location_info!())?)?
341 .context(location_info!())?;
343 let post_ap_id = PostForm::from_apub(&page, context, None)
347 let post = get_or_fetch_and_insert_post(&post_ap_id, context).await?;
349 let post_form = PostForm {
350 name: post.name.to_owned(),
351 url: post.url.to_owned(),
352 body: post.body.to_owned(),
353 creator_id: post.creator_id.to_owned(),
354 community_id: post.community_id,
355 removed: Some(false),
360 updated: Some(naive_now()),
361 embed_title: post.embed_title,
362 embed_description: post.embed_description,
363 embed_html: post.embed_html,
364 thumbnail_url: post.thumbnail_url,
365 ap_id: Some(post.ap_id),
369 let post_id = post.id;
370 blocking(context.pool(), move |conn| {
371 Post::update(conn, post_id, &post_form)
376 let post_id = post.id;
377 let post_view = blocking(context.pool(), move |conn| {
378 PostView::read(conn, post_id, None)
382 let res = PostResponse { post: post_view };
384 context.chat_server().do_send(SendPost {
385 op: UserOperation::EditPost,
390 announce_if_community_is_local(undo, &mod_, context).await?;
391 Ok(HttpResponse::Ok().finish())
394 async fn receive_undo_delete_community(
397 context: &LemmyContext,
398 ) -> Result<HttpResponse, LemmyError> {
399 let user = get_user_from_activity(delete, context).await?;
400 let group = GroupExt::from_any_base(delete.object().to_owned().one().context(location_info!())?)?
401 .context(location_info!())?;
403 let community_actor_id = CommunityForm::from_apub(&group, context, Some(user.actor_id()?))
406 .context(location_info!())?;
408 let community = blocking(context.pool(), move |conn| {
409 Community::read_from_actor_id(conn, &community_actor_id)
413 let community_form = CommunityForm {
414 name: community.name.to_owned(),
415 title: community.title.to_owned(),
416 description: community.description.to_owned(),
417 category_id: community.category_id, // Note: need to keep this due to foreign key constraint
418 creator_id: community.creator_id, // Note: need to keep this due to foreign key constraint
421 updated: Some(naive_now()),
422 deleted: Some(false),
423 nsfw: community.nsfw,
424 actor_id: Some(community.actor_id),
425 local: community.local,
426 private_key: community.private_key,
427 public_key: community.public_key,
428 last_refreshed_at: None,
429 icon: Some(community.icon.to_owned()),
430 banner: Some(community.banner.to_owned()),
433 let community_id = community.id;
434 blocking(context.pool(), move |conn| {
435 Community::update(conn, community_id, &community_form)
439 let community_id = community.id;
440 let res = CommunityResponse {
441 community: blocking(context.pool(), move |conn| {
442 CommunityView::read(conn, community_id, None)
447 let community_id = res.community.id;
449 context.chat_server().do_send(SendCommunityRoomMessage {
450 op: UserOperation::EditCommunity,
456 announce_if_community_is_local(undo, &user, context).await?;
457 Ok(HttpResponse::Ok().finish())
460 async fn receive_undo_remove_community(
463 context: &LemmyContext,
464 ) -> Result<HttpResponse, LemmyError> {
465 let mod_ = get_user_from_activity(remove, context).await?;
466 let group = GroupExt::from_any_base(remove.object().to_owned().one().context(location_info!())?)?
467 .context(location_info!())?;
469 let community_actor_id = CommunityForm::from_apub(&group, context, Some(mod_.actor_id()?))
472 .context(location_info!())?;
474 let community = blocking(context.pool(), move |conn| {
475 Community::read_from_actor_id(conn, &community_actor_id)
479 let community_form = CommunityForm {
480 name: community.name.to_owned(),
481 title: community.title.to_owned(),
482 description: community.description.to_owned(),
483 category_id: community.category_id, // Note: need to keep this due to foreign key constraint
484 creator_id: community.creator_id, // Note: need to keep this due to foreign key constraint
485 removed: Some(false),
487 updated: Some(naive_now()),
489 nsfw: community.nsfw,
490 actor_id: Some(community.actor_id),
491 local: community.local,
492 private_key: community.private_key,
493 public_key: community.public_key,
494 last_refreshed_at: None,
495 icon: Some(community.icon.to_owned()),
496 banner: Some(community.banner.to_owned()),
499 let community_id = community.id;
500 blocking(context.pool(), move |conn| {
501 Community::update(conn, community_id, &community_form)
505 let community_id = community.id;
506 let res = CommunityResponse {
507 community: blocking(context.pool(), move |conn| {
508 CommunityView::read(conn, community_id, None)
513 let community_id = res.community.id;
515 context.chat_server().do_send(SendCommunityRoomMessage {
516 op: UserOperation::EditCommunity,
522 announce_if_community_is_local(undo, &mod_, context).await?;
523 Ok(HttpResponse::Ok().finish())
526 async fn receive_undo_like_comment(
529 context: &LemmyContext,
530 ) -> Result<HttpResponse, LemmyError> {
531 let user = get_user_from_activity(like, context).await?;
532 let note = Note::from_any_base(like.object().to_owned().one().context(location_info!())?)?
533 .context(location_info!())?;
535 let comment = CommentForm::from_apub(¬e, context, None).await?;
537 let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context)
541 let user_id = user.id;
542 blocking(context.pool(), move |conn| {
543 CommentLike::remove(conn, user_id, comment_id)
548 let comment_view = blocking(context.pool(), move |conn| {
549 CommentView::read(conn, comment_id, None)
553 // TODO get those recipient actor ids from somewhere
554 let recipient_ids = vec![];
555 let res = CommentResponse {
556 comment: comment_view,
561 context.chat_server().do_send(SendComment {
562 op: UserOperation::CreateCommentLike,
567 announce_if_community_is_local(undo, &user, context).await?;
568 Ok(HttpResponse::Ok().finish())
571 async fn receive_undo_like_post(
574 context: &LemmyContext,
575 ) -> Result<HttpResponse, LemmyError> {
576 let user = get_user_from_activity(like, context).await?;
577 let page = PageExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
578 .context(location_info!())?;
580 let post = PostForm::from_apub(&page, context, None).await?;
582 let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context)
586 let user_id = user.id;
587 blocking(context.pool(), move |conn| {
588 PostLike::remove(conn, user_id, post_id)
593 let post_view = blocking(context.pool(), move |conn| {
594 PostView::read(conn, post_id, None)
598 let res = PostResponse { post: post_view };
600 context.chat_server().do_send(SendPost {
601 op: UserOperation::CreatePostLike,
606 announce_if_community_is_local(undo, &user, context).await?;
607 Ok(HttpResponse::Ok().finish())
610 async fn receive_undo_dislike_comment(
613 context: &LemmyContext,
614 ) -> Result<HttpResponse, LemmyError> {
615 let user = get_user_from_activity(dislike, context).await?;
616 let note = Note::from_any_base(
621 .context(location_info!())?,
623 .context(location_info!())?;
625 let comment = CommentForm::from_apub(¬e, context, None).await?;
627 let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context)
631 let user_id = user.id;
632 blocking(context.pool(), move |conn| {
633 CommentLike::remove(conn, user_id, comment_id)
638 let comment_view = blocking(context.pool(), move |conn| {
639 CommentView::read(conn, comment_id, None)
643 // TODO get those recipient actor ids from somewhere
644 let recipient_ids = vec![];
645 let res = CommentResponse {
646 comment: comment_view,
651 context.chat_server().do_send(SendComment {
652 op: UserOperation::CreateCommentLike,
657 announce_if_community_is_local(undo, &user, context).await?;
658 Ok(HttpResponse::Ok().finish())
661 async fn receive_undo_dislike_post(
664 context: &LemmyContext,
665 ) -> Result<HttpResponse, LemmyError> {
666 let user = get_user_from_activity(dislike, context).await?;
667 let page = PageExt::from_any_base(
672 .context(location_info!())?,
674 .context(location_info!())?;
676 let post = PostForm::from_apub(&page, context, None).await?;
678 let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context)
682 let user_id = user.id;
683 blocking(context.pool(), move |conn| {
684 PostLike::remove(conn, user_id, post_id)
689 let post_view = blocking(context.pool(), move |conn| {
690 PostView::read(conn, post_id, None)
694 let res = PostResponse { post: post_view };
696 context.chat_server().do_send(SendPost {
697 op: UserOperation::CreatePostLike,
702 announce_if_community_is_local(undo, &user, context).await?;
703 Ok(HttpResponse::Ok().finish())