]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/create_or_update/comment.rs
5def04604bfd9f23def0f2c0afc1fe86cf36887e
[lemmy.git] / crates / apub / src / activities / create_or_update / comment.rs
1 use crate::{
2   activities::{
3     check_community_deleted_or_removed,
4     community::send_activity_in_community,
5     create_or_update::get_comment_notif_recipients,
6     generate_activity_id,
7     verify_is_public,
8     verify_person_in_community,
9   },
10   activity_lists::AnnouncableActivities,
11   local_instance,
12   mentions::MentionOrValue,
13   objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
14   protocol::{
15     activities::{create_or_update::note::CreateOrUpdateNote, CreateOrUpdateType},
16     InCommunity,
17   },
18   ActorType,
19 };
20 use activitypub_federation::{
21   core::object_id::ObjectId,
22   data::Data,
23   traits::{ActivityHandler, Actor, ApubObject},
24   utils::verify_domains_match,
25 };
26 use activitystreams_kinds::public;
27 use lemmy_api_common::{
28   utils::check_post_deleted_or_removed,
29   websocket::{send::send_comment_ws_message, UserOperationCrud},
30   LemmyContext,
31 };
32 use lemmy_db_schema::{
33   source::{
34     comment::{CommentLike, CommentLikeForm},
35     community::Community,
36     post::Post,
37   },
38   traits::{Crud, Likeable},
39 };
40 use lemmy_utils::error::LemmyError;
41 use url::Url;
42
43 impl CreateOrUpdateNote {
44   #[tracing::instrument(skip(comment, actor, kind, context))]
45   pub async fn send(
46     comment: ApubComment,
47     actor: &ApubPerson,
48     kind: CreateOrUpdateType,
49     context: &LemmyContext,
50     request_counter: &mut i32,
51   ) -> Result<(), LemmyError> {
52     // TODO: might be helpful to add a comment method to retrieve community directly
53     let post_id = comment.post_id;
54     let post = Post::read(context.pool(), post_id).await?;
55     let community_id = post.community_id;
56     let community: ApubCommunity = Community::read(context.pool(), community_id).await?.into();
57
58     let id = generate_activity_id(
59       kind.clone(),
60       &context.settings().get_protocol_and_hostname(),
61     )?;
62     let note = comment.into_apub(context).await?;
63
64     let create_or_update = CreateOrUpdateNote {
65       actor: ObjectId::new(actor.actor_id()),
66       to: vec![public()],
67       cc: note.cc.clone(),
68       tag: note.tag.clone(),
69       object: note,
70       kind,
71       id: id.clone(),
72       audience: Some(ObjectId::new(community.actor_id())),
73     };
74
75     let tagged_users: Vec<ObjectId<ApubPerson>> = create_or_update
76       .tag
77       .iter()
78       .filter_map(|t| {
79         if let MentionOrValue::Mention(t) = t {
80           Some(t)
81         } else {
82           None
83         }
84       })
85       .map(|t| t.href.clone())
86       .map(ObjectId::new)
87       .collect();
88     let mut inboxes = vec![];
89     for t in tagged_users {
90       let person = t
91         .dereference(context, local_instance(context).await, request_counter)
92         .await?;
93       inboxes.push(person.shared_inbox_or_inbox());
94     }
95
96     let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update);
97     send_activity_in_community(activity, actor, &community, inboxes, false, context).await
98   }
99 }
100
101 #[async_trait::async_trait(?Send)]
102 impl ActivityHandler for CreateOrUpdateNote {
103   type DataType = LemmyContext;
104   type Error = LemmyError;
105
106   fn id(&self) -> &Url {
107     &self.id
108   }
109
110   fn actor(&self) -> &Url {
111     self.actor.inner()
112   }
113
114   #[tracing::instrument(skip_all)]
115   async fn verify(
116     &self,
117     context: &Data<LemmyContext>,
118     request_counter: &mut i32,
119   ) -> Result<(), LemmyError> {
120     verify_is_public(&self.to, &self.cc)?;
121     let post = self.object.get_parents(context, request_counter).await?.0;
122     let community = self.community(context, request_counter).await?;
123
124     verify_person_in_community(&self.actor, &community, context, request_counter).await?;
125     verify_domains_match(self.actor.inner(), self.object.id.inner())?;
126     check_community_deleted_or_removed(&community)?;
127     check_post_deleted_or_removed(&post)?;
128
129     ApubComment::verify(&self.object, self.actor.inner(), context, request_counter).await?;
130     Ok(())
131   }
132
133   #[tracing::instrument(skip_all)]
134   async fn receive(
135     self,
136     context: &Data<LemmyContext>,
137     request_counter: &mut i32,
138   ) -> Result<(), LemmyError> {
139     let comment = ApubComment::from_apub(self.object, context, request_counter).await?;
140
141     // author likes their own comment by default
142     let like_form = CommentLikeForm {
143       comment_id: comment.id,
144       post_id: comment.post_id,
145       person_id: comment.creator_id,
146       score: 1,
147     };
148     CommentLike::like(context.pool(), &like_form).await?;
149
150     let do_send_email = self.kind == CreateOrUpdateType::Create;
151     let recipients = get_comment_notif_recipients(
152       &self.actor,
153       &comment,
154       do_send_email,
155       context,
156       request_counter,
157     )
158     .await?;
159     let notif_type = match self.kind {
160       CreateOrUpdateType::Create => UserOperationCrud::CreateComment,
161       CreateOrUpdateType::Update => UserOperationCrud::EditComment,
162     };
163     send_comment_ws_message(
164       comment.id, notif_type, None, None, None, recipients, context,
165     )
166     .await?;
167     Ok(())
168   }
169 }