]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/community/collection_remove.rs
a1c443ea8c57ca7a848d03ae15da34933499ef6c
[lemmy.git] / crates / apub / src / activities / community / collection_remove.rs
1 use crate::{
2   activities::{
3     community::send_activity_in_community,
4     generate_activity_id,
5     verify_is_public,
6     verify_mod_action,
7     verify_person_in_community,
8   },
9   activity_lists::AnnouncableActivities,
10   insert_activity,
11   objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
12   protocol::{activities::community::collection_remove::CollectionRemove, InCommunity},
13 };
14 use activitypub_federation::{
15   config::Data,
16   fetch::object_id::ObjectId,
17   kinds::{activity::RemoveType, public},
18   traits::{ActivityHandler, Actor},
19 };
20 use lemmy_api_common::{
21   context::LemmyContext,
22   utils::{generate_featured_url, generate_moderators_url},
23 };
24 use lemmy_db_schema::{
25   impls::community::CollectionType,
26   source::{
27     community::{Community, CommunityModerator, CommunityModeratorForm},
28     moderator::{ModAddCommunity, ModAddCommunityForm},
29     post::{Post, PostUpdateForm},
30   },
31   traits::{Crud, Joinable},
32 };
33 use lemmy_utils::error::LemmyError;
34 use url::Url;
35
36 impl CollectionRemove {
37   #[tracing::instrument(skip_all)]
38   pub async fn send_remove_mod(
39     community: &ApubCommunity,
40     removed_mod: &ApubPerson,
41     actor: &ApubPerson,
42     context: &Data<LemmyContext>,
43   ) -> Result<(), LemmyError> {
44     let id = generate_activity_id(
45       RemoveType::Remove,
46       &context.settings().get_protocol_and_hostname(),
47     )?;
48     let remove = CollectionRemove {
49       actor: actor.id().into(),
50       to: vec![public()],
51       object: removed_mod.id(),
52       target: generate_moderators_url(&community.actor_id)?.into(),
53       id: id.clone(),
54       cc: vec![community.id()],
55       kind: RemoveType::Remove,
56       audience: Some(community.id().into()),
57     };
58
59     let activity = AnnouncableActivities::CollectionRemove(remove);
60     let inboxes = vec![removed_mod.shared_inbox_or_inbox()];
61     send_activity_in_community(activity, actor, community, inboxes, true, context).await
62   }
63
64   pub async fn send_remove_featured_post(
65     community: &ApubCommunity,
66     featured_post: &ApubPost,
67     actor: &ApubPerson,
68     context: &Data<LemmyContext>,
69   ) -> Result<(), LemmyError> {
70     let id = generate_activity_id(
71       RemoveType::Remove,
72       &context.settings().get_protocol_and_hostname(),
73     )?;
74     let remove = CollectionRemove {
75       actor: actor.id().into(),
76       to: vec![public()],
77       object: featured_post.ap_id.clone().into(),
78       target: generate_featured_url(&community.actor_id)?.into(),
79       cc: vec![community.id()],
80       kind: RemoveType::Remove,
81       id: id.clone(),
82       audience: Some(community.id().into()),
83     };
84     let activity = AnnouncableActivities::CollectionRemove(remove);
85     send_activity_in_community(activity, actor, community, vec![], true, context).await
86   }
87 }
88
89 #[async_trait::async_trait]
90 impl ActivityHandler for CollectionRemove {
91   type DataType = LemmyContext;
92   type Error = LemmyError;
93
94   fn id(&self) -> &Url {
95     &self.id
96   }
97
98   fn actor(&self) -> &Url {
99     self.actor.inner()
100   }
101
102   #[tracing::instrument(skip_all)]
103   async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
104     verify_is_public(&self.to, &self.cc)?;
105     let community = self.community(context).await?;
106     verify_person_in_community(&self.actor, &community, context).await?;
107     verify_mod_action(&self.actor, &self.object, community.id, context).await?;
108     Ok(())
109   }
110
111   #[tracing::instrument(skip_all)]
112   async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
113     insert_activity(&self.id, &self, false, false, context).await?;
114     let (community, collection_type) =
115       Community::get_by_collection_url(&mut context.pool(), &self.target.into()).await?;
116     match collection_type {
117       CollectionType::Moderators => {
118         let remove_mod = ObjectId::<ApubPerson>::from(self.object)
119           .dereference(context)
120           .await?;
121
122         let form = CommunityModeratorForm {
123           community_id: community.id,
124           person_id: remove_mod.id,
125         };
126         CommunityModerator::leave(&mut context.pool(), &form).await?;
127
128         // write mod log
129         let actor = self.actor.dereference(context).await?;
130         let form = ModAddCommunityForm {
131           mod_person_id: actor.id,
132           other_person_id: remove_mod.id,
133           community_id: community.id,
134           removed: Some(true),
135         };
136         ModAddCommunity::create(&mut context.pool(), &form).await?;
137
138         // TODO: send websocket notification about removed mod
139       }
140       CollectionType::Featured => {
141         let post = ObjectId::<ApubPost>::from(self.object)
142           .dereference(context)
143           .await?;
144         let form = PostUpdateForm::builder()
145           .featured_community(Some(false))
146           .build();
147         Post::update(&mut context.pool(), post.id, &form).await?;
148       }
149     }
150     Ok(())
151   }
152 }