]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/community/add_mod.rs
Merge branch 'remove_settings_and_secret_singletons_squashed'
[lemmy.git] / crates / apub / src / activities / community / add_mod.rs
1 use crate::{
2   activities::{
3     community::announce::AnnouncableActivities,
4     generate_activity_id,
5     verify_activity,
6     verify_add_remove_moderator_target,
7     verify_mod_action,
8     verify_person_in_community,
9   },
10   activity_queue::send_to_community_new,
11   extensions::context::lemmy_context,
12   fetcher::object_id::ObjectId,
13   generate_moderators_url,
14   ActorType,
15 };
16 use activitystreams::{
17   activity::kind::AddType,
18   base::AnyBase,
19   primitives::OneOrMany,
20   unparsed::Unparsed,
21 };
22 use lemmy_api_common::blocking;
23 use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
24 use lemmy_db_queries::{source::community::CommunityModerator_, Joinable};
25 use lemmy_db_schema::source::{
26   community::{Community, CommunityModerator, CommunityModeratorForm},
27   person::Person,
28 };
29 use lemmy_utils::LemmyError;
30 use lemmy_websocket::LemmyContext;
31 use serde::{Deserialize, Serialize};
32 use url::Url;
33
34 #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
35 #[serde(rename_all = "camelCase")]
36 pub struct AddMod {
37   actor: ObjectId<Person>,
38   to: [PublicUrl; 1],
39   object: ObjectId<Person>,
40   target: Url,
41   cc: [ObjectId<Community>; 1],
42   #[serde(rename = "type")]
43   kind: AddType,
44   id: Url,
45   #[serde(rename = "@context")]
46   context: OneOrMany<AnyBase>,
47   #[serde(flatten)]
48   unparsed: Unparsed,
49 }
50
51 impl AddMod {
52   pub async fn send(
53     community: &Community,
54     added_mod: &Person,
55     actor: &Person,
56     context: &LemmyContext,
57   ) -> Result<(), LemmyError> {
58     let id = generate_activity_id(
59       AddType::Add,
60       &context.settings().get_protocol_and_hostname(),
61     )?;
62     let add = AddMod {
63       actor: ObjectId::new(actor.actor_id()),
64       to: [PublicUrl::Public],
65       object: ObjectId::new(added_mod.actor_id()),
66       target: generate_moderators_url(&community.actor_id)?.into(),
67       cc: [ObjectId::new(community.actor_id())],
68       kind: AddType::Add,
69       id: id.clone(),
70       context: lemmy_context(),
71       unparsed: Default::default(),
72     };
73
74     let activity = AnnouncableActivities::AddMod(add);
75     let inboxes = vec![added_mod.get_shared_inbox_or_inbox_url()];
76     send_to_community_new(activity, &id, actor, community, inboxes, context).await
77   }
78 }
79
80 #[async_trait::async_trait(?Send)]
81 impl ActivityHandler for AddMod {
82   async fn verify(
83     &self,
84     context: &LemmyContext,
85     request_counter: &mut i32,
86   ) -> Result<(), LemmyError> {
87     verify_activity(self, &context.settings())?;
88     verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
89     verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
90     verify_add_remove_moderator_target(&self.target, &self.cc[0])?;
91     Ok(())
92   }
93
94   async fn receive(
95     self,
96     context: &LemmyContext,
97     request_counter: &mut i32,
98   ) -> Result<(), LemmyError> {
99     let community = self.cc[0].dereference(context, request_counter).await?;
100     let new_mod = self.object.dereference(context, request_counter).await?;
101
102     // If we had to refetch the community while parsing the activity, then the new mod has already
103     // been added. Skip it here as it would result in a duplicate key error.
104     let new_mod_id = new_mod.id;
105     let moderated_communities = blocking(context.pool(), move |conn| {
106       CommunityModerator::get_person_moderated_communities(conn, new_mod_id)
107     })
108     .await??;
109     if !moderated_communities.contains(&community.id) {
110       let form = CommunityModeratorForm {
111         community_id: community.id,
112         person_id: new_mod.id,
113       };
114       blocking(context.pool(), move |conn| {
115         CommunityModerator::join(conn, &form)
116       })
117       .await??;
118     }
119     // TODO: send websocket notification about added mod
120     Ok(())
121   }
122 }