diff --git a/lemmy_api/src/user.rs b/lemmy_api/src/user.rs index 0d96c2a2f..2dd0b2712 100644 --- a/lemmy_api/src/user.rs +++ b/lemmy_api/src/user.rs @@ -13,7 +13,7 @@ use anyhow::Context; use bcrypt::verify; use captcha::{gen, Difficulty}; use chrono::Duration; -use lemmy_apub::ApubObjectType; +use lemmy_apub::{ActorType, ApubObjectType}; use lemmy_db::{ comment::*, comment_report::CommentReportView, @@ -877,6 +877,9 @@ impl Perform for DeleteAccount { return Err(APIError::err("password_incorrect").into()); } + // Send activity to delete user on federated instances + user.send_delete(context).await?; + // Comments let user_id = user.id; let permadelete = move |conn: &'_ _| Comment::permadelete_for_creator(conn, user_id); diff --git a/lemmy_apub/src/activities/send/user.rs b/lemmy_apub/src/activities/send/user.rs index 2839d83ba..81b33fcdb 100644 --- a/lemmy_apub/src/activities/send/user.rs +++ b/lemmy_apub/src/activities/send/user.rs @@ -5,13 +5,16 @@ use crate::{ }; use activitystreams::{ activity::{ - kind::{FollowType, UndoType}, + kind::{DeleteType, FollowType, UndoType}, + Delete, Follow, Undo, }, base::{AnyBase, BaseExt, ExtendsExt}, object::ObjectExt, + public, }; +use itertools::Itertools; use lemmy_db::{ community::{Community, CommunityFollower, CommunityFollowerForm}, user::User_, @@ -105,8 +108,38 @@ impl ActorType for User_ { unimplemented!() } - async fn send_delete(&self, _context: &LemmyContext) -> Result<(), LemmyError> { - unimplemented!() + /// The user has deleted their account + async fn send_delete(&self, context: &LemmyContext) -> Result<(), LemmyError> { + let mut delete = Delete::new(self.actor_id()?, self.actor_id()?); + delete + .set_context(activitystreams::context()) + .set_id(generate_activity_id(DeleteType::Delete)?) + .set_to(public()) + // TODO: who is `cc`? all the communities the user has posted to? our inbox code can only + // handle activities for one activity. sending separate activities would work, but + // would also create a lot of traffic. + .set_many_ccs(vec![self.get_followers_url()?]); + + // TODO: should directly get distinct shared_inboxes from federated communites from db + // (once we store that) + let distinct_communities = blocking(context.pool(), move |conn| { + Community::distinct_federated_communities(conn) + }) + .await??; + let shared_inboxes = distinct_communities + .iter() + .map(|actor_id| Url::parse(actor_id).ok()) + .flatten() + .map(|url| url.host_str().map(|u| u.to_string())) + .flatten() + .unique() + .map(|c| format!("{}/inbox", c)) + .collect::>(); + // TODO: maybe we should add a separate function `send_activity_multi_dest()` + for s in &shared_inboxes { + send_activity_single_dest(delete.to_owned(), self, Url::parse(s)?, context).await?; + } + Ok(()) } async fn send_undo_delete(&self, _context: &LemmyContext) -> Result<(), LemmyError> {