]> Untitled Git - lemmy.git/blob - crates/websocket/src/send.rs
fbc88962aa16c1d1cbb8695e76e4411c987e817b
[lemmy.git] / crates / websocket / src / send.rs
1 use crate::{
2   messages::{SendComment, SendCommunityRoomMessage, SendPost, SendUserRoomMessage},
3   LemmyContext,
4   OperationType,
5 };
6 use lemmy_api_common::{
7   comment::CommentResponse,
8   community::CommunityResponse,
9   person::PrivateMessageResponse,
10   post::PostResponse,
11   utils::{blocking, check_person_block, get_user_lang, send_email_to_user},
12 };
13 use lemmy_db_schema::{
14   newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId},
15   source::{
16     comment::Comment,
17     person::Person,
18     person_mention::{PersonMention, PersonMentionForm},
19     post::Post,
20   },
21   traits::{Crud, DeleteableOrRemoveable},
22   SubscribedType,
23 };
24 use lemmy_db_views::structs::{CommentView, LocalUserView, PostView, PrivateMessageView};
25 use lemmy_db_views_actor::structs::CommunityView;
26 use lemmy_utils::{error::LemmyError, utils::MentionData, ConnectionId};
27
28 #[tracing::instrument(skip_all)]
29 pub async fn send_post_ws_message<OP: ToString + Send + OperationType + 'static>(
30   post_id: PostId,
31   op: OP,
32   websocket_id: Option<ConnectionId>,
33   person_id: Option<PersonId>,
34   context: &LemmyContext,
35 ) -> Result<PostResponse, LemmyError> {
36   let post_view = blocking(context.pool(), move |conn| {
37     PostView::read(conn, post_id, person_id)
38   })
39   .await??;
40
41   let res = PostResponse { post_view };
42
43   context.chat_server().do_send(SendPost {
44     op,
45     post: res.clone(),
46     websocket_id,
47   });
48
49   Ok(res)
50 }
51
52 // TODO: in many call sites in apub crate, we are setting an empty vec for recipient_ids,
53 //       we should get the actual recipient actors from somewhere
54 #[tracing::instrument(skip_all)]
55 pub async fn send_comment_ws_message_simple<OP: ToString + Send + OperationType + 'static>(
56   comment_id: CommentId,
57   op: OP,
58   context: &LemmyContext,
59 ) -> Result<CommentResponse, LemmyError> {
60   send_comment_ws_message(comment_id, op, None, None, None, vec![], context).await
61 }
62
63 #[tracing::instrument(skip_all)]
64 pub async fn send_comment_ws_message<OP: ToString + Send + OperationType + 'static>(
65   comment_id: CommentId,
66   op: OP,
67   websocket_id: Option<ConnectionId>,
68   form_id: Option<String>,
69   person_id: Option<PersonId>,
70   recipient_ids: Vec<LocalUserId>,
71   context: &LemmyContext,
72 ) -> Result<CommentResponse, LemmyError> {
73   let mut view = blocking(context.pool(), move |conn| {
74     CommentView::read(conn, comment_id, person_id)
75   })
76   .await??;
77
78   if view.comment.deleted || view.comment.removed {
79     view.comment = view.comment.blank_out_deleted_or_removed_info();
80   }
81
82   let mut res = CommentResponse {
83     comment_view: view,
84     recipient_ids,
85     // The sent out form id should be null
86     form_id: None,
87   };
88
89   context.chat_server().do_send(SendComment {
90     op,
91     comment: res.clone(),
92     websocket_id,
93   });
94
95   // The recipient_ids should be empty for returns
96   res.recipient_ids = Vec::new();
97   res.form_id = form_id;
98
99   Ok(res)
100 }
101
102 #[tracing::instrument(skip_all)]
103 pub async fn send_community_ws_message<OP: ToString + Send + OperationType + 'static>(
104   community_id: CommunityId,
105   op: OP,
106   websocket_id: Option<ConnectionId>,
107   person_id: Option<PersonId>,
108   context: &LemmyContext,
109 ) -> Result<CommunityResponse, LemmyError> {
110   let community_view = blocking(context.pool(), move |conn| {
111     CommunityView::read(conn, community_id, person_id)
112   })
113   .await??;
114
115   let res = CommunityResponse { community_view };
116
117   // Strip out the person id and subscribed when sending to others
118   let mut res_mut = res.clone();
119   res_mut.community_view.subscribed = SubscribedType::NotSubscribed;
120
121   context.chat_server().do_send(SendCommunityRoomMessage {
122     op,
123     response: res_mut,
124     community_id: res.community_view.community.id,
125     websocket_id,
126   });
127
128   Ok(res)
129 }
130
131 #[tracing::instrument(skip_all)]
132 pub async fn send_pm_ws_message<OP: ToString + Send + OperationType + 'static>(
133   private_message_id: PrivateMessageId,
134   op: OP,
135   websocket_id: Option<ConnectionId>,
136   context: &LemmyContext,
137 ) -> Result<PrivateMessageResponse, LemmyError> {
138   let mut view = blocking(context.pool(), move |conn| {
139     PrivateMessageView::read(conn, private_message_id)
140   })
141   .await??;
142
143   // Blank out deleted or removed info
144   if view.private_message.deleted {
145     view.private_message = view.private_message.blank_out_deleted_or_removed_info();
146   }
147
148   let res = PrivateMessageResponse {
149     private_message_view: view,
150   };
151
152   // Send notifications to the local recipient, if one exists
153   if res.private_message_view.recipient.local {
154     let recipient_id = res.private_message_view.recipient.id;
155     let local_recipient = blocking(context.pool(), move |conn| {
156       LocalUserView::read_person(conn, recipient_id)
157     })
158     .await??;
159     context.chat_server().do_send(SendUserRoomMessage {
160       op,
161       response: res.clone(),
162       local_recipient_id: local_recipient.local_user.id,
163       websocket_id,
164     });
165   }
166
167   Ok(res)
168 }
169
170 #[tracing::instrument(skip_all)]
171 pub async fn send_local_notifs(
172   mentions: Vec<MentionData>,
173   comment: &Comment,
174   person: &Person,
175   post: &Post,
176   do_send_email: bool,
177   context: &LemmyContext,
178 ) -> Result<Vec<LocalUserId>, LemmyError> {
179   let mut recipient_ids = Vec::new();
180   let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
181
182   // Send the local mentions
183   for mention in mentions
184     .iter()
185     .filter(|m| m.is_local(&context.settings().hostname) && m.name.ne(&person.name))
186     .collect::<Vec<&MentionData>>()
187   {
188     let mention_name = mention.name.clone();
189     let user_view = blocking(context.pool(), move |conn| {
190       LocalUserView::read_from_name(conn, &mention_name)
191     })
192     .await?;
193     if let Ok(mention_user_view) = user_view {
194       // TODO
195       // At some point, make it so you can't tag the parent creator either
196       // This can cause two notifications, one for reply and the other for mention
197       recipient_ids.push(mention_user_view.local_user.id);
198
199       let user_mention_form = PersonMentionForm {
200         recipient_id: mention_user_view.person.id,
201         comment_id: comment.id,
202         read: None,
203       };
204
205       // Allow this to fail softly, since comment edits might re-update or replace it
206       // Let the uniqueness handle this fail
207       blocking(context.pool(), move |conn| {
208         PersonMention::create(conn, &user_mention_form)
209       })
210       .await?
211       .ok();
212
213       // Send an email to those local users that have notifications on
214       if do_send_email {
215         let lang = get_user_lang(&mention_user_view);
216         send_email_to_user(
217           &mention_user_view,
218           &lang.notification_mentioned_by_subject(&person.name),
219           &lang.notification_mentioned_by_body(&comment.content, &inbox_link, &person.name),
220           context.settings(),
221         )
222       }
223     }
224   }
225
226   // Send notifs to the parent commenter / poster
227   match comment.parent_id {
228     Some(parent_id) => {
229       let parent_comment =
230         blocking(context.pool(), move |conn| Comment::read(conn, parent_id)).await?;
231       if let Ok(parent_comment) = parent_comment {
232         // Get the parent commenter local_user
233         let parent_creator_id = parent_comment.creator_id;
234
235         // Only add to recipients if that person isn't blocked
236         let creator_blocked = check_person_block(person.id, parent_creator_id, context.pool())
237           .await
238           .is_err();
239
240         // Don't send a notif to yourself
241         if parent_comment.creator_id != person.id && !creator_blocked {
242           let user_view = blocking(context.pool(), move |conn| {
243             LocalUserView::read_person(conn, parent_creator_id)
244           })
245           .await?;
246           if let Ok(parent_user_view) = user_view {
247             recipient_ids.push(parent_user_view.local_user.id);
248
249             if do_send_email {
250               let lang = get_user_lang(&parent_user_view);
251               send_email_to_user(
252                 &parent_user_view,
253                 &lang.notification_comment_reply_subject(&person.name),
254                 &lang.notification_comment_reply_body(&comment.content, &inbox_link, &person.name),
255                 context.settings(),
256               )
257             }
258           }
259         }
260       }
261     }
262     // Its a post
263     // Don't send a notif to yourself
264     None => {
265       // Only add to recipients if that person isn't blocked
266       let creator_blocked = check_person_block(person.id, post.creator_id, context.pool())
267         .await
268         .is_err();
269
270       if post.creator_id != person.id && !creator_blocked {
271         let creator_id = post.creator_id;
272         let parent_user = blocking(context.pool(), move |conn| {
273           LocalUserView::read_person(conn, creator_id)
274         })
275         .await?;
276         if let Ok(parent_user_view) = parent_user {
277           recipient_ids.push(parent_user_view.local_user.id);
278
279           if do_send_email {
280             let lang = get_user_lang(&parent_user_view);
281             send_email_to_user(
282               &parent_user_view,
283               &lang.notification_post_reply_subject(&person.name),
284               &lang.notification_post_reply_body(&comment.content, &inbox_link, &person.name),
285               context.settings(),
286             )
287           }
288         }
289       }
290     }
291   };
292   Ok(recipient_ids)
293 }