]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/send/community.rs
Allow adding remote users as community mods (ref #1061)
[lemmy.git] / crates / apub / src / activities / send / community.rs
1 use crate::{
2   activities::send::generate_activity_id,
3   activity_queue::{send_activity_single_dest, send_to_community, send_to_community_followers},
4   check_is_apub_id_valid,
5   extensions::context::lemmy_context,
6   fetcher::user::get_or_fetch_and_upsert_user,
7   generate_moderators_url,
8   ActorType,
9 };
10 use activitystreams::{
11   activity::{
12     kind::{AcceptType, AddType, AnnounceType, DeleteType, LikeType, RemoveType, UndoType},
13     Accept,
14     ActorAndObjectRefExt,
15     Add,
16     Announce,
17     Delete,
18     Follow,
19     OptTargetRefExt,
20     Remove,
21     Undo,
22   },
23   base::{AnyBase, BaseExt, ExtendsExt},
24   object::ObjectExt,
25   public,
26 };
27 use anyhow::Context;
28 use itertools::Itertools;
29 use lemmy_api_structs::blocking;
30 use lemmy_db_queries::DbPool;
31 use lemmy_db_schema::source::{community::Community, user::User_};
32 use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
33 use lemmy_utils::{location_info, LemmyError};
34 use lemmy_websocket::LemmyContext;
35 use url::Url;
36
37 #[async_trait::async_trait(?Send)]
38 impl ActorType for Community {
39   fn is_local(&self) -> bool {
40     self.local
41   }
42   fn actor_id(&self) -> Url {
43     self.actor_id.to_owned().into_inner()
44   }
45   fn public_key(&self) -> Option<String> {
46     self.public_key.to_owned()
47   }
48   fn private_key(&self) -> Option<String> {
49     self.private_key.to_owned()
50   }
51
52   fn get_shared_inbox_or_inbox_url(&self) -> Url {
53     self
54       .shared_inbox_url
55       .clone()
56       .unwrap_or_else(|| self.inbox_url.to_owned())
57       .into()
58   }
59
60   async fn send_follow(
61     &self,
62     _follow_actor_id: &Url,
63     _context: &LemmyContext,
64   ) -> Result<(), LemmyError> {
65     unimplemented!()
66   }
67
68   async fn send_unfollow(
69     &self,
70     _follow_actor_id: &Url,
71     _context: &LemmyContext,
72   ) -> Result<(), LemmyError> {
73     unimplemented!()
74   }
75
76   /// As a local community, accept the follow request from a remote user.
77   async fn send_accept_follow(
78     &self,
79     follow: Follow,
80     context: &LemmyContext,
81   ) -> Result<(), LemmyError> {
82     let actor_uri = follow
83       .actor()?
84       .as_single_xsd_any_uri()
85       .context(location_info!())?;
86     let user = get_or_fetch_and_upsert_user(actor_uri, context, &mut 0).await?;
87
88     let mut accept = Accept::new(
89       self.actor_id.to_owned().into_inner(),
90       follow.into_any_base()?,
91     );
92     accept
93       .set_many_contexts(lemmy_context()?)
94       .set_id(generate_activity_id(AcceptType::Accept)?)
95       .set_to(user.actor_id());
96
97     send_activity_single_dest(accept, self, user.inbox_url.into(), context).await?;
98     Ok(())
99   }
100
101   /// If the creator of a community deletes the community, send this to all followers.
102   async fn send_delete(&self, context: &LemmyContext) -> Result<(), LemmyError> {
103     let mut delete = Delete::new(self.actor_id(), self.actor_id());
104     delete
105       .set_many_contexts(lemmy_context()?)
106       .set_id(generate_activity_id(DeleteType::Delete)?)
107       .set_to(public())
108       .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
109
110     send_to_community_followers(delete, self, context).await?;
111     Ok(())
112   }
113
114   /// If the creator of a community reverts the deletion of a community, send this to all followers.
115   async fn send_undo_delete(&self, context: &LemmyContext) -> Result<(), LemmyError> {
116     let mut delete = Delete::new(self.actor_id(), self.actor_id());
117     delete
118       .set_many_contexts(lemmy_context()?)
119       .set_id(generate_activity_id(DeleteType::Delete)?)
120       .set_to(public())
121       .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
122
123     let mut undo = Undo::new(self.actor_id(), delete.into_any_base()?);
124     undo
125       .set_many_contexts(lemmy_context()?)
126       .set_id(generate_activity_id(UndoType::Undo)?)
127       .set_to(public())
128       .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
129
130     send_to_community_followers(undo, self, context).await?;
131     Ok(())
132   }
133
134   /// If an admin removes a community, send this to all followers.
135   async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError> {
136     let mut remove = Remove::new(self.actor_id(), self.actor_id());
137     remove
138       .set_many_contexts(lemmy_context()?)
139       .set_id(generate_activity_id(RemoveType::Remove)?)
140       .set_to(public())
141       .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
142
143     send_to_community_followers(remove, self, context).await?;
144     Ok(())
145   }
146
147   /// If an admin reverts the removal of a community, send this to all followers.
148   async fn send_undo_remove(&self, context: &LemmyContext) -> Result<(), LemmyError> {
149     let mut remove = Remove::new(self.actor_id(), self.actor_id());
150     remove
151       .set_many_contexts(lemmy_context()?)
152       .set_id(generate_activity_id(RemoveType::Remove)?)
153       .set_to(public())
154       .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
155
156     // Undo that fake activity
157     let mut undo = Undo::new(self.actor_id(), remove.into_any_base()?);
158     undo
159       .set_many_contexts(lemmy_context()?)
160       .set_id(generate_activity_id(LikeType::Like)?)
161       .set_to(public())
162       .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
163
164     send_to_community_followers(undo, self, context).await?;
165     Ok(())
166   }
167
168   /// Wraps an activity sent to the community in an announce, and then sends the announce to all
169   /// community followers.
170   async fn send_announce(
171     &self,
172     activity: AnyBase,
173     context: &LemmyContext,
174   ) -> Result<(), LemmyError> {
175     let mut announce = Announce::new(self.actor_id.to_owned().into_inner(), activity);
176     announce
177       .set_many_contexts(lemmy_context()?)
178       .set_id(generate_activity_id(AnnounceType::Announce)?)
179       .set_to(public())
180       .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
181
182     send_to_community_followers(announce, self, context).await?;
183
184     Ok(())
185   }
186
187   /// For a given community, returns the inboxes of all followers.
188   async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError> {
189     let id = self.id;
190
191     let follows = blocking(pool, move |conn| {
192       CommunityFollowerView::for_community(conn, id)
193     })
194     .await??;
195     let inboxes = follows
196       .into_iter()
197       .filter(|f| !f.follower.local)
198       .map(|f| f.follower.shared_inbox_url.unwrap_or(f.follower.inbox_url))
199       .map(|i| i.into_inner())
200       .unique()
201       // Don't send to blocked instances
202       .filter(|inbox| check_is_apub_id_valid(inbox).is_ok())
203       .collect();
204
205     Ok(inboxes)
206   }
207 }
208
209 pub async fn send_add_mod(
210   actor: User_,
211   added_mod: User_,
212   community: Community,
213   context: &LemmyContext,
214 ) -> Result<(), LemmyError> {
215   let mut add = Add::new(
216     actor.actor_id.clone().into_inner(),
217     added_mod.actor_id.into_inner(),
218   );
219   add
220     .set_many_contexts(lemmy_context()?)
221     .set_id(generate_activity_id(AddType::Add)?)
222     .set_many_tos(vec![community.actor_id.to_owned().into_inner(), public()])
223     .set_target(generate_moderators_url(&community.actor_id)?.into_inner());
224
225   if community.local {
226     community
227       .send_announce(add.into_any_base()?, context)
228       .await?;
229   } else {
230     send_to_community(add, &actor, &community, context).await?;
231   }
232   Ok(())
233 }
234
235 pub async fn send_remove_mod(
236   actor: User_,
237   removed_mod: User_,
238   community: Community,
239   context: &LemmyContext,
240 ) -> Result<(), LemmyError> {
241   let mut remove = Remove::new(
242     actor.actor_id.clone().into_inner(),
243     removed_mod.actor_id.into_inner(),
244   );
245   remove
246     .set_many_contexts(lemmy_context()?)
247     .set_id(generate_activity_id(RemoveType::Remove)?)
248     .set_many_tos(vec![community.actor_id.to_owned().into_inner(), public()])
249     .set_target(generate_moderators_url(&community.actor_id)?.into_inner());
250
251   if community.local {
252     community
253       .send_announce(remove.into_any_base()?, context)
254       .await?;
255   } else {
256     send_to_community(remove, &actor, &community, context).await?;
257   }
258   Ok(())
259 }