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(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(context.pool(), new_mod_id).await?;
134 if !moderated_communities.contains(&community.id) {
135 let form = CommunityModeratorForm {
136 community_id: community.id,
137 person_id: new_mod.id,
139 CommunityModerator::join(context.pool(), &form).await?;
142 let actor = self.actor.dereference(context).await?;
143 let form = ModAddCommunityForm {
144 mod_person_id: actor.id,
145 other_person_id: new_mod.id,
146 community_id: community.id,
147 removed: Some(false),
149 ModAddCommunity::create(context.pool(), &form).await?;
151 // TODO: send websocket notification about added mod
153 CollectionType::Featured => {
154 let post = ObjectId::<ApubPost>::from(self.object)
155 .dereference(context)
157 let form = PostUpdateForm::builder()
158 .featured_community(Some(true))
160 Post::update(context.pool(), post.id, &form).await?;
167 #[async_trait::async_trait]
168 impl SendActivity for AddModToCommunity {
169 type Response = AddModToCommunityResponse;
171 async fn send_activity(
173 _response: &Self::Response,
174 context: &Data<LemmyContext>,
175 ) -> Result<(), LemmyError> {
176 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
177 let community: ApubCommunity = Community::read(context.pool(), request.community_id)
180 let updated_mod: ApubPerson = Person::read(context.pool(), request.person_id)
184 CollectionAdd::send_add_mod(
187 &local_user_view.person.into(),
192 CollectionRemove::send_remove_mod(
195 &local_user_view.person.into(),
203 #[async_trait::async_trait]
204 impl SendActivity for FeaturePost {
205 type Response = PostResponse;
207 async fn send_activity(
209 response: &Self::Response,
210 context: &Data<LemmyContext>,
211 ) -> Result<(), LemmyError> {
212 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
213 let community = Community::read(context.pool(), response.post_view.community.id)
216 let post = response.post_view.post.clone().into();
217 let person = local_user_view.person.into();
218 if request.featured {
219 CollectionAdd::send_add_featured_post(&community, &post, &person, context).await
221 CollectionRemove::send_remove_featured_post(&community, &post, &person, context).await