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