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