3 check_community_deleted_or_removed,
4 community::send_activity_in_community,
5 create_or_update::get_comment_notif_recipients,
8 verify_person_in_community,
10 activity_lists::AnnouncableActivities,
12 mentions::MentionOrValue,
13 objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
15 activities::{create_or_update::note::CreateOrUpdateNote, CreateOrUpdateType},
21 use activitypub_federation::{
22 core::object_id::ObjectId,
24 traits::{ActivityHandler, Actor, ApubObject},
25 utils::verify_domains_match,
27 use activitystreams_kinds::public;
28 use lemmy_api_common::{
29 comment::{CommentResponse, CreateComment, EditComment},
30 context::LemmyContext,
31 utils::{check_post_deleted_or_removed, is_mod_or_admin},
32 websocket::{send::send_comment_ws_message, UserOperationCrud},
34 use lemmy_db_schema::{
37 comment::{Comment, CommentLike, CommentLikeForm},
42 traits::{Crud, Likeable},
44 use lemmy_utils::error::LemmyError;
47 #[async_trait::async_trait(?Send)]
48 impl SendActivity for CreateComment {
49 type Response = CommentResponse;
51 async fn send_activity(
53 response: &Self::Response,
54 context: &LemmyContext,
55 ) -> Result<(), LemmyError> {
56 CreateOrUpdateNote::send(
57 &response.comment_view.comment,
58 response.comment_view.creator.id,
59 CreateOrUpdateType::Create,
66 #[async_trait::async_trait(?Send)]
67 impl SendActivity for EditComment {
68 type Response = CommentResponse;
70 async fn send_activity(
72 response: &Self::Response,
73 context: &LemmyContext,
74 ) -> Result<(), LemmyError> {
75 CreateOrUpdateNote::send(
76 &response.comment_view.comment,
77 response.comment_view.creator.id,
78 CreateOrUpdateType::Update,
85 impl CreateOrUpdateNote {
86 #[tracing::instrument(skip(comment, person_id, kind, context))]
90 kind: CreateOrUpdateType,
91 context: &LemmyContext,
92 ) -> Result<(), LemmyError> {
93 // TODO: might be helpful to add a comment method to retrieve community directly
94 let post_id = comment.post_id;
95 let post = Post::read(context.pool(), post_id).await?;
96 let community_id = post.community_id;
97 let person: ApubPerson = Person::read(context.pool(), person_id).await?.into();
98 let community: ApubCommunity = Community::read(context.pool(), community_id).await?.into();
100 let id = generate_activity_id(
102 &context.settings().get_protocol_and_hostname(),
104 let note = ApubComment(comment.clone()).into_apub(context).await?;
106 let create_or_update = CreateOrUpdateNote {
107 actor: ObjectId::new(person.actor_id()),
110 tag: note.tag.clone(),
114 audience: Some(ObjectId::new(community.actor_id())),
117 let tagged_users: Vec<ObjectId<ApubPerson>> = create_or_update
121 if let MentionOrValue::Mention(t) = t {
127 .map(|t| t.href.clone())
130 let mut inboxes = vec![];
131 for t in tagged_users {
133 .dereference(context, local_instance(context).await, &mut 0)
135 inboxes.push(person.shared_inbox_or_inbox());
138 let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update);
139 send_activity_in_community(activity, &person, &community, inboxes, false, context).await
143 #[async_trait::async_trait(?Send)]
144 impl ActivityHandler for CreateOrUpdateNote {
145 type DataType = LemmyContext;
146 type Error = LemmyError;
148 fn id(&self) -> &Url {
152 fn actor(&self) -> &Url {
156 #[tracing::instrument(skip_all)]
159 context: &Data<LemmyContext>,
160 request_counter: &mut i32,
161 ) -> Result<(), LemmyError> {
162 verify_is_public(&self.to, &self.cc)?;
163 let post = self.object.get_parents(context, request_counter).await?.0;
164 let community = self.community(context, request_counter).await?;
166 verify_person_in_community(&self.actor, &community, context, request_counter).await?;
167 verify_domains_match(self.actor.inner(), self.object.id.inner())?;
168 check_community_deleted_or_removed(&community)?;
169 check_post_deleted_or_removed(&post)?;
171 ApubComment::verify(&self.object, self.actor.inner(), context, request_counter).await?;
175 #[tracing::instrument(skip_all)]
178 context: &Data<LemmyContext>,
179 request_counter: &mut i32,
180 ) -> Result<(), LemmyError> {
181 // Need to do this check here instead of Note::from_apub because we need the person who
182 // send the activity, not the comment author.
183 let existing_comment = self.object.id.dereference_local(context).await.ok();
184 if let (Some(distinguished), Some(existing_comment)) =
185 (self.object.distinguished, existing_comment)
187 if distinguished != existing_comment.distinguished {
190 .dereference(context, local_instance(context).await, request_counter)
192 let (post, _) = self.object.get_parents(context, request_counter).await?;
193 is_mod_or_admin(context.pool(), creator.id, post.community_id).await?;
197 let comment = ApubComment::from_apub(self.object, context, request_counter).await?;
199 // author likes their own comment by default
200 let like_form = CommentLikeForm {
201 comment_id: comment.id,
202 post_id: comment.post_id,
203 person_id: comment.creator_id,
206 CommentLike::like(context.pool(), &like_form).await?;
208 let do_send_email = self.kind == CreateOrUpdateType::Create;
209 let recipients = get_comment_notif_recipients(
217 let notif_type = match self.kind {
218 CreateOrUpdateType::Create => UserOperationCrud::CreateComment,
219 CreateOrUpdateType::Update => UserOperationCrud::EditComment,
221 send_comment_ws_message(
222 comment.id, notif_type, None, None, None, recipients, context,