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