3 community::send_activity_in_community,
7 verify_person_in_community,
9 activity_lists::AnnouncableActivities,
11 objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
13 activities::community::{collection_add::CollectionAdd, collection_remove::CollectionRemove},
18 use activitypub_federation::{
20 fetch::object_id::ObjectId,
21 kinds::{activity::AddType, public},
22 traits::{ActivityHandler, Actor},
24 use lemmy_api_common::{
25 community::{AddModToCommunity, AddModToCommunityResponse},
26 context::LemmyContext,
27 post::{FeaturePost, PostResponse},
28 utils::{generate_featured_url, generate_moderators_url, local_user_view_from_jwt},
30 use lemmy_db_schema::{
31 impls::community::CollectionType,
33 community::{Community, CommunityModerator, CommunityModeratorForm},
34 moderator::{ModAddCommunity, ModAddCommunityForm},
36 post::{Post, PostUpdateForm},
38 traits::{Crud, Joinable},
40 use lemmy_utils::error::LemmyError;
44 #[tracing::instrument(skip_all)]
45 pub async fn send_add_mod(
46 community: &ApubCommunity,
47 added_mod: &ApubPerson,
49 context: &Data<LemmyContext>,
50 ) -> Result<(), LemmyError> {
51 let id = generate_activity_id(
53 &context.settings().get_protocol_and_hostname(),
55 let add = CollectionAdd {
56 actor: actor.id().into(),
58 object: added_mod.id(),
59 target: generate_moderators_url(&community.actor_id)?.into(),
60 cc: vec![community.id()],
63 audience: Some(community.id().into()),
66 let activity = AnnouncableActivities::CollectionAdd(add);
67 let inboxes = vec![added_mod.shared_inbox_or_inbox()];
68 send_activity_in_community(activity, actor, community, inboxes, true, context).await
71 pub async fn send_add_featured_post(
72 community: &ApubCommunity,
73 featured_post: &ApubPost,
75 context: &Data<LemmyContext>,
76 ) -> Result<(), LemmyError> {
77 let id = generate_activity_id(
79 &context.settings().get_protocol_and_hostname(),
81 let add = CollectionAdd {
82 actor: actor.id().into(),
84 object: featured_post.ap_id.clone().into(),
85 target: generate_featured_url(&community.actor_id)?.into(),
86 cc: vec![community.id()],
89 audience: Some(community.id().into()),
91 let activity = AnnouncableActivities::CollectionAdd(add);
92 send_activity_in_community(activity, actor, community, vec![], true, context).await
96 #[async_trait::async_trait]
97 impl ActivityHandler for CollectionAdd {
98 type DataType = LemmyContext;
99 type Error = LemmyError;
101 fn id(&self) -> &Url {
105 fn actor(&self) -> &Url {
109 #[tracing::instrument(skip_all)]
110 async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
111 verify_is_public(&self.to, &self.cc)?;
112 let community = self.community(context).await?;
113 verify_person_in_community(&self.actor, &community, context).await?;
114 verify_mod_action(&self.actor, &self.object, community.id, context).await?;
118 #[tracing::instrument(skip_all)]
119 async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
120 insert_activity(&self.id, &self, false, false, context).await?;
121 let (community, collection_type) =
122 Community::get_by_collection_url(&mut context.pool(), &self.target.into()).await?;
123 match collection_type {
124 CollectionType::Moderators => {
125 let new_mod = ObjectId::<ApubPerson>::from(self.object)
126 .dereference(context)
129 // If we had to refetch the community while parsing the activity, then the new mod has already
130 // been added. Skip it here as it would result in a duplicate key error.
131 let new_mod_id = new_mod.id;
132 let moderated_communities =
133 CommunityModerator::get_person_moderated_communities(&mut context.pool(), new_mod_id)
135 if !moderated_communities.contains(&community.id) {
136 let form = CommunityModeratorForm {
137 community_id: community.id,
138 person_id: new_mod.id,
140 CommunityModerator::join(&mut context.pool(), &form).await?;
143 let actor = self.actor.dereference(context).await?;
144 let form = ModAddCommunityForm {
145 mod_person_id: actor.id,
146 other_person_id: new_mod.id,
147 community_id: community.id,
148 removed: Some(false),
150 ModAddCommunity::create(&mut context.pool(), &form).await?;
152 // TODO: send websocket notification about added mod
154 CollectionType::Featured => {
155 let post = ObjectId::<ApubPost>::from(self.object)
156 .dereference(context)
158 let form = PostUpdateForm::builder()
159 .featured_community(Some(true))
161 Post::update(&mut context.pool(), post.id, &form).await?;
168 #[async_trait::async_trait]
169 impl SendActivity for AddModToCommunity {
170 type Response = AddModToCommunityResponse;
172 async fn send_activity(
174 _response: &Self::Response,
175 context: &Data<LemmyContext>,
176 ) -> Result<(), LemmyError> {
177 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
178 let community: ApubCommunity = Community::read(&mut context.pool(), request.community_id)
181 let updated_mod: ApubPerson = Person::read(&mut context.pool(), request.person_id)
185 CollectionAdd::send_add_mod(
188 &local_user_view.person.into(),
193 CollectionRemove::send_remove_mod(
196 &local_user_view.person.into(),
204 #[async_trait::async_trait]
205 impl SendActivity for FeaturePost {
206 type Response = PostResponse;
208 async fn send_activity(
210 response: &Self::Response,
211 context: &Data<LemmyContext>,
212 ) -> Result<(), LemmyError> {
213 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
214 let community = Community::read(&mut context.pool(), response.post_view.community.id)
217 let post = response.post_view.post.clone().into();
218 let person = local_user_view.person.into();
219 if request.featured {
220 CollectionAdd::send_add_featured_post(&community, &post, &person, context).await
222 CollectionRemove::send_remove_featured_post(&community, &post, &person, context).await