3 community::send_activity_in_community,
7 verify_person_in_community,
9 activity_lists::AnnouncableActivities,
11 objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
14 community::{collection_add::CollectionAdd, collection_remove::CollectionRemove},
15 create_or_update::page::CreateOrUpdatePage,
22 use activitypub_federation::{
24 fetch::object_id::ObjectId,
25 kinds::{activity::AddType, public},
26 traits::{ActivityHandler, Actor},
28 use lemmy_api_common::{
29 community::{AddModToCommunity, AddModToCommunityResponse},
30 context::LemmyContext,
31 post::{FeaturePost, PostResponse},
32 utils::{generate_featured_url, generate_moderators_url, local_user_view_from_jwt},
34 use lemmy_db_schema::{
35 impls::community::CollectionType,
37 community::{Community, CommunityModerator, CommunityModeratorForm},
38 moderator::{ModAddCommunity, ModAddCommunityForm},
40 post::{Post, PostUpdateForm},
42 traits::{Crud, Joinable},
44 use lemmy_utils::error::LemmyError;
48 #[tracing::instrument(skip_all)]
49 pub async fn send_add_mod(
50 community: &ApubCommunity,
51 added_mod: &ApubPerson,
53 context: &Data<LemmyContext>,
54 ) -> Result<(), LemmyError> {
55 let id = generate_activity_id(
57 &context.settings().get_protocol_and_hostname(),
59 let add = CollectionAdd {
60 actor: actor.id().into(),
62 object: added_mod.id(),
63 target: generate_moderators_url(&community.actor_id)?.into(),
64 cc: vec![community.id()],
67 audience: Some(community.id().into()),
70 let activity = AnnouncableActivities::CollectionAdd(add);
71 let inboxes = vec![added_mod.shared_inbox_or_inbox()];
72 send_activity_in_community(activity, actor, community, inboxes, true, context).await
75 pub async fn send_add_featured_post(
76 community: &ApubCommunity,
77 featured_post: &ApubPost,
79 context: &Data<LemmyContext>,
80 ) -> Result<(), LemmyError> {
81 let id = generate_activity_id(
83 &context.settings().get_protocol_and_hostname(),
85 let add = CollectionAdd {
86 actor: actor.id().into(),
88 object: featured_post.ap_id.clone().into(),
89 target: generate_featured_url(&community.actor_id)?.into(),
90 cc: vec![community.id()],
93 audience: Some(community.id().into()),
95 let activity = AnnouncableActivities::CollectionAdd(add);
96 send_activity_in_community(activity, actor, community, vec![], true, context).await
100 #[async_trait::async_trait]
101 impl ActivityHandler for CollectionAdd {
102 type DataType = LemmyContext;
103 type Error = LemmyError;
105 fn id(&self) -> &Url {
109 fn actor(&self) -> &Url {
113 #[tracing::instrument(skip_all)]
114 async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
115 verify_is_public(&self.to, &self.cc)?;
116 let community = self.community(context).await?;
117 verify_person_in_community(&self.actor, &community, context).await?;
118 verify_mod_action(&self.actor, &self.object, community.id, context).await?;
122 #[tracing::instrument(skip_all)]
123 async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
124 insert_activity(&self.id, &self, false, false, context).await?;
125 let (community, collection_type) =
126 Community::get_by_collection_url(context.pool(), &self.target.into()).await?;
127 match collection_type {
128 CollectionType::Moderators => {
129 let new_mod = ObjectId::<ApubPerson>::from(self.object)
130 .dereference(context)
133 // If we had to refetch the community while parsing the activity, then the new mod has already
134 // been added. Skip it here as it would result in a duplicate key error.
135 let new_mod_id = new_mod.id;
136 let moderated_communities =
137 CommunityModerator::get_person_moderated_communities(context.pool(), new_mod_id).await?;
138 if !moderated_communities.contains(&community.id) {
139 let form = CommunityModeratorForm {
140 community_id: community.id,
141 person_id: new_mod.id,
143 CommunityModerator::join(context.pool(), &form).await?;
146 let actor = self.actor.dereference(context).await?;
147 let form = ModAddCommunityForm {
148 mod_person_id: actor.id,
149 other_person_id: new_mod.id,
150 community_id: community.id,
151 removed: Some(false),
153 ModAddCommunity::create(context.pool(), &form).await?;
155 // TODO: send websocket notification about added mod
157 CollectionType::Featured => {
158 let post = ObjectId::<ApubPost>::from(self.object)
159 .dereference(context)
161 let form = PostUpdateForm::builder()
162 .featured_community(Some(true))
164 Post::update(context.pool(), post.id, &form).await?;
171 #[async_trait::async_trait]
172 impl SendActivity for AddModToCommunity {
173 type Response = AddModToCommunityResponse;
175 async fn send_activity(
177 _response: &Self::Response,
178 context: &Data<LemmyContext>,
179 ) -> Result<(), LemmyError> {
180 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
181 let community: ApubCommunity = Community::read(context.pool(), request.community_id)
184 let updated_mod: ApubPerson = Person::read(context.pool(), request.person_id)
188 CollectionAdd::send_add_mod(
191 &local_user_view.person.into(),
196 CollectionRemove::send_remove_mod(
199 &local_user_view.person.into(),
207 #[async_trait::async_trait]
208 impl SendActivity for FeaturePost {
209 type Response = PostResponse;
211 async fn send_activity(
213 response: &Self::Response,
214 context: &Data<LemmyContext>,
215 ) -> Result<(), LemmyError> {
216 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
217 // Deprecated, for backwards compatibility with 0.17
218 CreateOrUpdatePage::send(
219 &response.post_view.post,
220 local_user_view.person.id,
221 CreateOrUpdateType::Update,
225 let community = Community::read(context.pool(), response.post_view.community.id)
228 let post = response.post_view.post.clone().into();
229 let person = local_user_view.person.into();
230 if request.featured {
231 CollectionAdd::send_add_featured_post(&community, &post, &person, context).await
233 CollectionRemove::send_remove_featured_post(&community, &post, &person, context).await