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