]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/community/add_mod.rs
Move code to generate apub urls into lemmy_api_common
[lemmy.git] / crates / apub / src / activities / community / add_mod.rs
1 use crate::{
2   activities::{
3     community::send_activity_in_community,
4     generate_activity_id,
5     verify_add_remove_moderator_target,
6     verify_is_public,
7     verify_mod_action,
8     verify_person_in_community,
9   },
10   activity_lists::AnnouncableActivities,
11   local_instance,
12   objects::{community::ApubCommunity, person::ApubPerson},
13   protocol::{activities::community::add_mod::AddMod, InCommunity},
14   ActorType,
15 };
16 use activitypub_federation::{
17   core::object_id::ObjectId,
18   data::Data,
19   traits::{ActivityHandler, Actor},
20 };
21 use activitystreams_kinds::{activity::AddType, public};
22 use lemmy_api_common::{generate_moderators_url, LemmyContext};
23 use lemmy_db_schema::{
24   source::{
25     community::{CommunityModerator, CommunityModeratorForm},
26     moderator::{ModAddCommunity, ModAddCommunityForm},
27   },
28   traits::{Crud, Joinable},
29 };
30 use lemmy_utils::error::LemmyError;
31 use url::Url;
32
33 impl AddMod {
34   #[tracing::instrument(skip_all)]
35   pub async fn send(
36     community: &ApubCommunity,
37     added_mod: &ApubPerson,
38     actor: &ApubPerson,
39     context: &LemmyContext,
40   ) -> Result<(), LemmyError> {
41     let id = generate_activity_id(
42       AddType::Add,
43       &context.settings().get_protocol_and_hostname(),
44     )?;
45     let add = AddMod {
46       actor: ObjectId::new(actor.actor_id()),
47       to: vec![public()],
48       object: ObjectId::new(added_mod.actor_id()),
49       target: generate_moderators_url(&community.actor_id)?.into(),
50       cc: vec![community.actor_id()],
51       kind: AddType::Add,
52       id: id.clone(),
53       audience: Some(ObjectId::new(community.actor_id())),
54     };
55
56     let activity = AnnouncableActivities::AddMod(add);
57     let inboxes = vec![added_mod.shared_inbox_or_inbox()];
58     send_activity_in_community(activity, actor, community, inboxes, true, context).await
59   }
60 }
61
62 #[async_trait::async_trait(?Send)]
63 impl ActivityHandler for AddMod {
64   type DataType = LemmyContext;
65   type Error = LemmyError;
66
67   fn id(&self) -> &Url {
68     &self.id
69   }
70
71   fn actor(&self) -> &Url {
72     self.actor.inner()
73   }
74
75   #[tracing::instrument(skip_all)]
76   async fn verify(
77     &self,
78     context: &Data<LemmyContext>,
79     request_counter: &mut i32,
80   ) -> Result<(), LemmyError> {
81     verify_is_public(&self.to, &self.cc)?;
82     let community = self.community(context, request_counter).await?;
83     verify_person_in_community(&self.actor, &community, context, request_counter).await?;
84     verify_mod_action(
85       &self.actor,
86       self.object.inner(),
87       community.id,
88       context,
89       request_counter,
90     )
91     .await?;
92     verify_add_remove_moderator_target(&self.target, &community)?;
93     Ok(())
94   }
95
96   #[tracing::instrument(skip_all)]
97   async fn receive(
98     self,
99     context: &Data<LemmyContext>,
100     request_counter: &mut i32,
101   ) -> Result<(), LemmyError> {
102     let community = self.community(context, request_counter).await?;
103     let new_mod = self
104       .object
105       .dereference(context, local_instance(context).await, request_counter)
106       .await?;
107
108     // If we had to refetch the community while parsing the activity, then the new mod has already
109     // been added. Skip it here as it would result in a duplicate key error.
110     let new_mod_id = new_mod.id;
111     let moderated_communities =
112       CommunityModerator::get_person_moderated_communities(context.pool(), new_mod_id).await?;
113     if !moderated_communities.contains(&community.id) {
114       let form = CommunityModeratorForm {
115         community_id: community.id,
116         person_id: new_mod.id,
117       };
118       CommunityModerator::join(context.pool(), &form).await?;
119
120       // write mod log
121       let actor = self
122         .actor
123         .dereference(context, local_instance(context).await, request_counter)
124         .await?;
125       let form = ModAddCommunityForm {
126         mod_person_id: actor.id,
127         other_person_id: new_mod.id,
128         community_id: community.id,
129         removed: Some(false),
130       };
131       ModAddCommunity::create(context.pool(), &form).await?;
132     }
133     // TODO: send websocket notification about added mod
134     Ok(())
135   }
136 }