]> Untitled Git - lemmy.git/blob - server/src/apub/inbox/activities/create.rs
routes.api: fix get_captcha endpoint (#1135)
[lemmy.git] / server / src / apub / inbox / activities / create.rs
1 use crate::{
2   apub::{
3     inbox::shared_inbox::{
4       announce_if_community_is_local,
5       get_user_from_activity,
6       receive_unhandled_activity,
7     },
8     ActorType,
9     FromApub,
10     PageExt,
11   },
12   websocket::{
13     messages::{SendComment, SendPost},
14     UserOperation,
15   },
16   LemmyContext,
17 };
18 use activitystreams::{activity::Create, base::AnyBase, object::Note, prelude::*};
19 use actix_web::HttpResponse;
20 use anyhow::Context;
21 use lemmy_api_structs::{
22   blocking,
23   comment::CommentResponse,
24   post::PostResponse,
25   send_local_notifs,
26 };
27 use lemmy_db::{
28   comment::{Comment, CommentForm},
29   comment_view::CommentView,
30   post::{Post, PostForm},
31   post_view::PostView,
32 };
33 use lemmy_utils::{location_info, utils::scrape_text_for_mentions, LemmyError};
34
35 pub async fn receive_create(
36   activity: AnyBase,
37   context: &LemmyContext,
38 ) -> Result<HttpResponse, LemmyError> {
39   let create = Create::from_any_base(activity)?.context(location_info!())?;
40
41   // ensure that create and actor come from the same instance
42   let user = get_user_from_activity(&create, context).await?;
43   create.id(user.actor_id()?.domain().context(location_info!())?)?;
44
45   match create.object().as_single_kind_str() {
46     Some("Page") => receive_create_post(create, context).await,
47     Some("Note") => receive_create_comment(create, context).await,
48     _ => receive_unhandled_activity(create),
49   }
50 }
51
52 async fn receive_create_post(
53   create: Create,
54   context: &LemmyContext,
55 ) -> Result<HttpResponse, LemmyError> {
56   let user = get_user_from_activity(&create, context).await?;
57   let page = PageExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
58     .context(location_info!())?;
59
60   let post = PostForm::from_apub(&page, context, Some(user.actor_id()?)).await?;
61
62   // Using an upsert, since likes (which fetch the post), sometimes come in before the create
63   // resulting in double posts.
64   let inserted_post = blocking(context.pool(), move |conn| Post::upsert(conn, &post)).await??;
65
66   // Refetch the view
67   let inserted_post_id = inserted_post.id;
68   let post_view = blocking(context.pool(), move |conn| {
69     PostView::read(conn, inserted_post_id, None)
70   })
71   .await??;
72
73   let res = PostResponse { post: post_view };
74
75   context.chat_server().do_send(SendPost {
76     op: UserOperation::CreatePost,
77     post: res,
78     websocket_id: None,
79   });
80
81   announce_if_community_is_local(create, &user, context).await?;
82   Ok(HttpResponse::Ok().finish())
83 }
84
85 async fn receive_create_comment(
86   create: Create,
87   context: &LemmyContext,
88 ) -> Result<HttpResponse, LemmyError> {
89   let user = get_user_from_activity(&create, context).await?;
90   let note = Note::from_any_base(create.object().to_owned().one().context(location_info!())?)?
91     .context(location_info!())?;
92
93   let comment = CommentForm::from_apub(&note, context, Some(user.actor_id()?)).await?;
94
95   let inserted_comment =
96     blocking(context.pool(), move |conn| Comment::upsert(conn, &comment)).await??;
97
98   let post_id = inserted_comment.post_id;
99   let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
100
101   // Note:
102   // Although mentions could be gotten from the post tags (they are included there), or the ccs,
103   // Its much easier to scrape them from the comment body, since the API has to do that
104   // anyway.
105   let mentions = scrape_text_for_mentions(&inserted_comment.content);
106   let recipient_ids = send_local_notifs(
107     mentions,
108     inserted_comment.clone(),
109     &user,
110     post,
111     context.pool(),
112     true,
113   )
114   .await?;
115
116   // Refetch the view
117   let comment_view = blocking(context.pool(), move |conn| {
118     CommentView::read(conn, inserted_comment.id, None)
119   })
120   .await??;
121
122   let res = CommentResponse {
123     comment: comment_view,
124     recipient_ids,
125     form_id: None,
126   };
127
128   context.chat_server().do_send(SendComment {
129     op: UserOperation::CreateComment,
130     comment: res,
131     websocket_id: None,
132   });
133
134   announce_if_community_is_local(create, &user, context).await?;
135   Ok(HttpResponse::Ok().finish())
136 }