]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/send/community.rs
Rewrite delete activities (#1699)
[lemmy.git] / crates / apub / src / activities / send / community.rs
1 use crate::{
2   activities::generate_activity_id,
3   activity_queue::{send_to_community, send_to_community_followers},
4   check_is_apub_id_valid,
5   extensions::context::lemmy_context,
6   fetcher::get_or_fetch_and_upsert_actor,
7   generate_moderators_url,
8   insert_activity,
9   objects::ToApub,
10   ActorType,
11   CommunityType,
12 };
13 use activitystreams::{
14   activity::{
15     kind::{AddType, AnnounceType, BlockType, RemoveType, UndoType, UpdateType},
16     Add,
17     Announce,
18     Block,
19     OptTargetRefExt,
20     Remove,
21     Undo,
22     Update,
23   },
24   base::{AnyBase, BaseExt, ExtendsExt},
25   object::ObjectExt,
26   public,
27 };
28 use anyhow::Context;
29 use itertools::Itertools;
30 use lemmy_api_common::blocking;
31 use lemmy_db_queries::DbPool;
32 use lemmy_db_schema::source::{community::Community, person::Person};
33 use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
34 use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
35 use lemmy_websocket::LemmyContext;
36 use url::Url;
37
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()
44   }
45   fn name(&self) -> String {
46     self.name.clone()
47   }
48   fn public_key(&self) -> Option<String> {
49     self.public_key.to_owned()
50   }
51   fn private_key(&self) -> Option<String> {
52     self.private_key.to_owned()
53   }
54
55   fn get_shared_inbox_or_inbox_url(&self) -> Url {
56     self
57       .shared_inbox_url
58       .clone()
59       .unwrap_or_else(|| self.inbox_url.to_owned())
60       .into()
61   }
62 }
63
64 #[async_trait::async_trait(?Send)]
65 impl CommunityType for Community {
66   fn followers_url(&self) -> Url {
67     self.followers_url.clone().into()
68   }
69
70   /// If a remote community is updated by a local mod, send the updated info to the community's
71   /// instance.
72   async fn send_update(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError> {
73     if self.local {
74       // Do nothing, other instances will automatically refetch the community
75     } else {
76       let mut update = Update::new(
77         mod_.actor_id(),
78         AnyBase::from_arbitrary_json(self.to_apub(context.pool()).await?)?,
79       );
80       update
81         .set_many_contexts(lemmy_context())
82         .set_id(generate_activity_id(UpdateType::Update)?)
83         .set_to(public())
84         .set_many_ccs(vec![self.actor_id()]);
85       send_to_community(update, &mod_, self, None, context).await?;
86     }
87     Ok(())
88   }
89
90   /// Wraps an activity sent to the community in an announce, and then sends the announce to all
91   /// community followers.
92   ///
93   /// If we are announcing a local activity, it hasn't been stored in the database yet, and we need
94   /// to do it here, so that it can be fetched by ID. Remote activities are inserted into DB in the
95   /// inbox.
96   ///
97   /// If the `object` of the announced activity is an actor, the actor ID needs to be passed as
98   /// `object_actor`, so that the announce can be delivered to that user.
99   async fn send_announce(
100     &self,
101     activity: AnyBase,
102     object_actor: Option<Url>,
103     context: &LemmyContext,
104   ) -> Result<(), LemmyError> {
105     let inner_id = activity.id().context(location_info!())?;
106     if inner_id.domain() == Some(&Settings::get().get_hostname_without_port()?) {
107       insert_activity(inner_id, activity.clone(), true, false, context.pool()).await?;
108     }
109
110     let mut ccs = vec![self.followers_url()];
111     let mut object_actor_inbox: Option<Url> = None;
112     if let Some(actor_id) = object_actor {
113       // Ignore errors, maybe its not actually an actor
114       // TODO: should pass the actual request counter in, but that seems complicated
115       let actor = get_or_fetch_and_upsert_actor(&actor_id, context, &mut 0)
116         .await
117         .ok();
118       if let Some(actor) = actor {
119         ccs.push(actor_id);
120         object_actor_inbox = Some(actor.get_shared_inbox_or_inbox_url());
121       }
122     }
123     let mut announce = Announce::new(self.actor_id(), activity);
124     announce
125       .set_many_contexts(lemmy_context())
126       .set_id(generate_activity_id(AnnounceType::Announce)?)
127       .set_to(public())
128       .set_many_ccs(ccs);
129
130     send_to_community_followers(announce, self, object_actor_inbox, context).await?;
131
132     Ok(())
133   }
134
135   /// For a given community, returns the inboxes of all followers.
136   async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError> {
137     let id = self.id;
138
139     let follows = blocking(pool, move |conn| {
140       CommunityFollowerView::for_community(conn, id)
141     })
142     .await??;
143     let inboxes = follows
144       .into_iter()
145       .filter(|f| !f.follower.local)
146       .map(|f| f.follower.shared_inbox_url.unwrap_or(f.follower.inbox_url))
147       .map(|i| i.into_inner())
148       .unique()
149       // Don't send to blocked instances
150       .filter(|inbox| check_is_apub_id_valid(inbox, false).is_ok())
151       .collect();
152
153     Ok(inboxes)
154   }
155
156   async fn send_add_mod(
157     &self,
158     actor: &Person,
159     added_mod: Person,
160     context: &LemmyContext,
161   ) -> Result<(), LemmyError> {
162     let mut add = Add::new(actor.actor_id(), added_mod.actor_id());
163     add
164       .set_many_contexts(lemmy_context())
165       .set_id(generate_activity_id(AddType::Add)?)
166       .set_to(public())
167       .set_many_ccs(vec![self.actor_id()])
168       .set_target(generate_moderators_url(&self.actor_id)?.into_inner());
169
170     send_to_community(add, actor, self, Some(added_mod.actor_id()), context).await?;
171     Ok(())
172   }
173
174   async fn send_remove_mod(
175     &self,
176     actor: &Person,
177     removed_mod: Person,
178     context: &LemmyContext,
179   ) -> Result<(), LemmyError> {
180     let mut remove = Remove::new(actor.actor_id(), removed_mod.actor_id());
181     remove
182       .set_many_contexts(lemmy_context())
183       .set_id(generate_activity_id(RemoveType::Remove)?)
184       .set_to(public())
185       .set_many_ccs(vec![self.actor_id()])
186       .set_target(generate_moderators_url(&self.actor_id)?.into_inner());
187
188     send_to_community(remove, actor, self, Some(removed_mod.actor_id()), context).await?;
189     Ok(())
190   }
191
192   async fn send_block_user(
193     &self,
194     actor: &Person,
195     blocked_user: Person,
196     context: &LemmyContext,
197   ) -> Result<(), LemmyError> {
198     let mut block = Block::new(actor.actor_id(), blocked_user.actor_id());
199     block
200       .set_many_contexts(lemmy_context())
201       .set_id(generate_activity_id(BlockType::Block)?)
202       .set_to(public())
203       .set_many_ccs(vec![self.actor_id()]);
204
205     send_to_community(block, actor, self, Some(blocked_user.actor_id()), context).await?;
206     Ok(())
207   }
208
209   async fn send_undo_block_user(
210     &self,
211     actor: &Person,
212     unblocked_user: Person,
213     context: &LemmyContext,
214   ) -> Result<(), LemmyError> {
215     let mut block = Block::new(actor.actor_id(), unblocked_user.actor_id());
216     block
217       .set_many_contexts(lemmy_context())
218       .set_id(generate_activity_id(BlockType::Block)?)
219       .set_to(public())
220       .set_many_ccs(vec![self.actor_id()]);
221
222     // Undo that fake activity
223     let mut undo = Undo::new(actor.actor_id(), block.into_any_base()?);
224     undo
225       .set_many_contexts(lemmy_context())
226       .set_id(generate_activity_id(UndoType::Undo)?)
227       .set_to(public())
228       .set_many_ccs(vec![self.actor_id()]);
229
230     send_to_community(undo, actor, self, Some(unblocked_user.actor_id()), context).await?;
231     Ok(())
232   }
233 }