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