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