]> Untitled Git - lemmy.git/blob - crates/apub/src/objects/private_message.rs
Rewrite activitypub following, person, community, pm (#1692)
[lemmy.git] / crates / apub / src / objects / private_message.rs
1 use crate::{
2   extensions::context::lemmy_context,
3   fetcher::person::get_or_fetch_and_upsert_person,
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::{ApubObject, 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: Url,
39   to: Url,
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, &self.id)?;
64     let person =
65       get_or_fetch_and_upsert_person(&self.attributed_to, context, request_counter).await?;
66     if person.banned {
67       return Err(anyhow!("Person is banned from site").into());
68     }
69     Ok(())
70   }
71 }
72
73 #[async_trait::async_trait(?Send)]
74 impl ToApub for PrivateMessage {
75   type ApubType = Note;
76
77   async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> {
78     let creator_id = self.creator_id;
79     let creator = blocking(pool, move |conn| Person::read(conn, creator_id)).await??;
80
81     let recipient_id = self.recipient_id;
82     let recipient = blocking(pool, move |conn| Person::read(conn, recipient_id)).await??;
83
84     let note = Note {
85       context: lemmy_context(),
86       r#type: NoteType::Note,
87       id: self.ap_id.clone().into(),
88       attributed_to: creator.actor_id.into_inner(),
89       to: recipient.actor_id.into(),
90       content: self.content.clone(),
91       media_type: MediaTypeHtml::Html,
92       source: Source {
93         content: self.content.clone(),
94         media_type: MediaTypeMarkdown::Markdown,
95       },
96       published: convert_datetime(self.published),
97       updated: self.updated.map(convert_datetime),
98       unparsed: Default::default(),
99     };
100     Ok(note)
101   }
102
103   fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
104     create_tombstone(
105       self.deleted,
106       self.ap_id.to_owned().into(),
107       self.updated,
108       NoteType::Note,
109     )
110   }
111 }
112
113 #[async_trait::async_trait(?Send)]
114 impl FromApub for PrivateMessage {
115   type ApubType = Note;
116
117   async fn from_apub(
118     note: &Note,
119     context: &LemmyContext,
120     expected_domain: &Url,
121     request_counter: &mut i32,
122   ) -> Result<PrivateMessage, LemmyError> {
123     let ap_id = Some(note.id(expected_domain)?.clone().into());
124     let creator =
125       get_or_fetch_and_upsert_person(&note.attributed_to, context, request_counter).await?;
126     let recipient = get_or_fetch_and_upsert_person(&note.to, context, request_counter).await?;
127
128     let form = PrivateMessageForm {
129       creator_id: creator.id,
130       recipient_id: recipient.id,
131       content: note.source.content.clone(),
132       published: Some(note.published.naive_local()),
133       updated: note.updated.map(|u| u.to_owned().naive_local()),
134       deleted: None,
135       read: None,
136       ap_id,
137       local: Some(false),
138     };
139     Ok(
140       blocking(context.pool(), move |conn| {
141         PrivateMessage::upsert(conn, &form)
142       })
143       .await??,
144     )
145   }
146 }