]> Untitled Git - lemmy.git/blob - crates/apub/src/objects/private_message.rs
8cf40c9e46b8495e16ec90ae0b97595c7bef11c7
[lemmy.git] / crates / apub / src / objects / private_message.rs
1 use crate::{
2   context::lemmy_context,
3   fetcher::object_id::ObjectId,
4   objects::{create_tombstone, FromApub, Source, ToApub},
5 };
6 use activitystreams::{
7   base::AnyBase,
8   object::{kind::NoteType, Tombstone},
9   primitives::OneOrMany,
10   unparsed::Unparsed,
11 };
12 use anyhow::anyhow;
13 use chrono::{DateTime, FixedOffset};
14 use lemmy_api_common::blocking;
15 use lemmy_apub_lib::{
16   values::{MediaTypeHtml, MediaTypeMarkdown},
17   verify::verify_domains_match,
18 };
19 use lemmy_db_schema::{
20   source::{
21     person::Person,
22     private_message::{PrivateMessage, PrivateMessageForm},
23   },
24   traits::Crud,
25   DbPool,
26 };
27 use lemmy_utils::{utils::convert_datetime, LemmyError};
28 use lemmy_websocket::LemmyContext;
29 use serde::{Deserialize, Serialize};
30 use serde_with::skip_serializing_none;
31 use url::Url;
32
33 #[skip_serializing_none]
34 #[derive(Clone, Debug, Deserialize, Serialize)]
35 #[serde(rename_all = "camelCase")]
36 pub struct Note {
37   #[serde(rename = "@context")]
38   context: OneOrMany<AnyBase>,
39   r#type: NoteType,
40   id: Url,
41   pub(crate) attributed_to: ObjectId<Person>,
42   to: ObjectId<Person>,
43   content: String,
44   media_type: MediaTypeHtml,
45   source: Source,
46   published: DateTime<FixedOffset>,
47   updated: Option<DateTime<FixedOffset>>,
48   #[serde(flatten)]
49   unparsed: Unparsed,
50 }
51
52 impl Note {
53   pub(crate) fn id_unchecked(&self) -> &Url {
54     &self.id
55   }
56   pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> {
57     verify_domains_match(&self.id, expected_domain)?;
58     Ok(&self.id)
59   }
60
61   pub(crate) async fn verify(
62     &self,
63     context: &LemmyContext,
64     request_counter: &mut i32,
65   ) -> Result<(), LemmyError> {
66     verify_domains_match(self.attributed_to.inner(), &self.id)?;
67     let person = self
68       .attributed_to
69       .dereference(context, request_counter)
70       .await?;
71     if person.banned {
72       return Err(anyhow!("Person is banned from site").into());
73     }
74     Ok(())
75   }
76 }
77
78 #[async_trait::async_trait(?Send)]
79 impl ToApub for PrivateMessage {
80   type ApubType = Note;
81
82   async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> {
83     let creator_id = self.creator_id;
84     let creator = blocking(pool, move |conn| Person::read(conn, creator_id)).await??;
85
86     let recipient_id = self.recipient_id;
87     let recipient = blocking(pool, move |conn| Person::read(conn, recipient_id)).await??;
88
89     let note = Note {
90       context: lemmy_context(),
91       r#type: NoteType::Note,
92       id: self.ap_id.clone().into(),
93       attributed_to: ObjectId::new(creator.actor_id),
94       to: ObjectId::new(recipient.actor_id),
95       content: self.content.clone(),
96       media_type: MediaTypeHtml::Html,
97       source: Source {
98         content: self.content.clone(),
99         media_type: MediaTypeMarkdown::Markdown,
100       },
101       published: convert_datetime(self.published),
102       updated: self.updated.map(convert_datetime),
103       unparsed: Default::default(),
104     };
105     Ok(note)
106   }
107
108   fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
109     create_tombstone(
110       self.deleted,
111       self.ap_id.to_owned().into(),
112       self.updated,
113       NoteType::Note,
114     )
115   }
116 }
117
118 #[async_trait::async_trait(?Send)]
119 impl FromApub for PrivateMessage {
120   type ApubType = Note;
121
122   async fn from_apub(
123     note: &Note,
124     context: &LemmyContext,
125     expected_domain: &Url,
126     request_counter: &mut i32,
127   ) -> Result<PrivateMessage, LemmyError> {
128     let ap_id = Some(note.id(expected_domain)?.clone().into());
129     let creator = note
130       .attributed_to
131       .dereference(context, request_counter)
132       .await?;
133     let recipient = note.to.dereference(context, request_counter).await?;
134
135     let form = PrivateMessageForm {
136       creator_id: creator.id,
137       recipient_id: recipient.id,
138       content: note.source.content.clone(),
139       published: Some(note.published.naive_local()),
140       updated: note.updated.map(|u| u.to_owned().naive_local()),
141       deleted: None,
142       read: None,
143       ap_id,
144       local: Some(false),
145     };
146     Ok(
147       blocking(context.pool(), move |conn| {
148         PrivateMessage::upsert(conn, &form)
149       })
150       .await??,
151     )
152   }
153 }