]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/send/community.rs
Use Url type for ap_id fields in database (fixes #1364) (#1371)
[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_followers},
4   check_is_apub_id_valid,
5   extensions::context::lemmy_context,
6   fetcher::user::get_or_fetch_and_upsert_user,
7   ActorType,
8 };
9 use activitystreams::{
10   activity::{
11     kind::{AcceptType, AnnounceType, DeleteType, LikeType, RemoveType, UndoType},
12     Accept,
13     ActorAndObjectRefExt,
14     Announce,
15     Delete,
16     Follow,
17     Remove,
18     Undo,
19   },
20   base::{AnyBase, BaseExt, ExtendsExt},
21   object::ObjectExt,
22   public,
23 };
24 use anyhow::Context;
25 use itertools::Itertools;
26 use lemmy_db_queries::DbPool;
27 use lemmy_db_schema::source::community::Community;
28 use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
29 use lemmy_structs::blocking;
30 use lemmy_utils::{location_info, settings::Settings, LemmyError};
31 use lemmy_websocket::LemmyContext;
32 use url::Url;
33
34 #[async_trait::async_trait(?Send)]
35 impl ActorType for Community {
36   fn actor_id(&self) -> Url {
37     self.actor_id.to_owned().into_inner()
38   }
39
40   fn public_key(&self) -> Option<String> {
41     self.public_key.to_owned()
42   }
43   fn private_key(&self) -> Option<String> {
44     self.private_key.to_owned()
45   }
46
47   async fn send_follow(
48     &self,
49     _follow_actor_id: &Url,
50     _context: &LemmyContext,
51   ) -> Result<(), LemmyError> {
52     unimplemented!()
53   }
54
55   async fn send_unfollow(
56     &self,
57     _follow_actor_id: &Url,
58     _context: &LemmyContext,
59   ) -> Result<(), LemmyError> {
60     unimplemented!()
61   }
62
63   /// As a local community, accept the follow request from a remote user.
64   async fn send_accept_follow(
65     &self,
66     follow: Follow,
67     context: &LemmyContext,
68   ) -> Result<(), LemmyError> {
69     let actor_uri = follow
70       .actor()?
71       .as_single_xsd_any_uri()
72       .context(location_info!())?;
73     let user = get_or_fetch_and_upsert_user(actor_uri, context, &mut 0).await?;
74
75     let mut accept = Accept::new(
76       self.actor_id.to_owned().into_inner(),
77       follow.into_any_base()?,
78     );
79     accept
80       .set_many_contexts(lemmy_context()?)
81       .set_id(generate_activity_id(AcceptType::Accept)?)
82       .set_to(user.actor_id());
83
84     send_activity_single_dest(accept, self, user.get_inbox_url()?, context).await?;
85     Ok(())
86   }
87
88   /// If the creator of a community deletes the community, send this to all followers.
89   async fn send_delete(&self, context: &LemmyContext) -> Result<(), LemmyError> {
90     let mut delete = Delete::new(self.actor_id(), self.actor_id());
91     delete
92       .set_many_contexts(lemmy_context()?)
93       .set_id(generate_activity_id(DeleteType::Delete)?)
94       .set_to(public())
95       .set_many_ccs(vec![self.get_followers_url()?]);
96
97     send_to_community_followers(delete, self, context).await?;
98     Ok(())
99   }
100
101   /// If the creator of a community reverts the deletion of a community, send this to all followers.
102   async fn send_undo_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.get_followers_url()?]);
109
110     let mut undo = Undo::new(self.actor_id(), delete.into_any_base()?);
111     undo
112       .set_many_contexts(lemmy_context()?)
113       .set_id(generate_activity_id(UndoType::Undo)?)
114       .set_to(public())
115       .set_many_ccs(vec![self.get_followers_url()?]);
116
117     send_to_community_followers(undo, self, context).await?;
118     Ok(())
119   }
120
121   /// If an admin removes a community, send this to all followers.
122   async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError> {
123     let mut remove = Remove::new(self.actor_id(), self.actor_id());
124     remove
125       .set_many_contexts(lemmy_context()?)
126       .set_id(generate_activity_id(RemoveType::Remove)?)
127       .set_to(public())
128       .set_many_ccs(vec![self.get_followers_url()?]);
129
130     send_to_community_followers(remove, self, context).await?;
131     Ok(())
132   }
133
134   /// If an admin reverts the removal of a community, send this to all followers.
135   async fn send_undo_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.get_followers_url()?]);
142
143     // Undo that fake activity
144     let mut undo = Undo::new(self.actor_id(), remove.into_any_base()?);
145     undo
146       .set_many_contexts(lemmy_context()?)
147       .set_id(generate_activity_id(LikeType::Like)?)
148       .set_to(public())
149       .set_many_ccs(vec![self.get_followers_url()?]);
150
151     send_to_community_followers(undo, self, context).await?;
152     Ok(())
153   }
154
155   /// Wraps an activity sent to the community in an announce, and then sends the announce to all
156   /// community followers.
157   async fn send_announce(
158     &self,
159     activity: AnyBase,
160     context: &LemmyContext,
161   ) -> Result<(), LemmyError> {
162     let mut announce = Announce::new(self.actor_id.to_owned().into_inner(), activity);
163     announce
164       .set_many_contexts(lemmy_context()?)
165       .set_id(generate_activity_id(AnnounceType::Announce)?)
166       .set_to(public())
167       .set_many_ccs(vec![self.get_followers_url()?]);
168
169     send_to_community_followers(announce, self, context).await?;
170
171     Ok(())
172   }
173
174   /// For a given community, returns the inboxes of all followers.
175   ///
176   /// TODO: this function is very badly implemented, we should just store shared_inbox_url in
177   ///       CommunityFollowerView
178   async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError> {
179     let id = self.id;
180
181     let inboxes = blocking(pool, move |conn| {
182       CommunityFollowerView::for_community(conn, id)
183     })
184     .await??;
185     let inboxes = inboxes
186       .into_iter()
187       .filter(|i| !i.follower.local)
188       .map(|u| -> Result<Url, LemmyError> {
189         let url = u.follower.actor_id.into_inner();
190         let domain = url.domain().context(location_info!())?;
191         let port = if let Some(port) = url.port() {
192           format!(":{}", port)
193         } else {
194           "".to_string()
195         };
196         Ok(Url::parse(&format!(
197           "{}://{}{}/inbox",
198           Settings::get().get_protocol_string(),
199           domain,
200           port,
201         ))?)
202       })
203       .filter_map(Result::ok)
204       // Don't send to blocked instances
205       .filter(|inbox| check_is_apub_id_valid(inbox).is_ok())
206       .unique()
207       .collect();
208
209     Ok(inboxes)
210   }
211 }