let banAlpha = await banPersonFromCommunity(beta, alphaUser.person.id, 2, true);
expect(banAlpha.banned).toBe(true);
- // Alpha makes post on beta
+ // Alpha tries to make post on beta, but it fails because of ban
let postRes = await createPost(alpha, betaCommunity.community.id);
- expect(postRes.post_view.post).toBeDefined();
- expect(postRes.post_view.community.local).toBe(false);
- expect(postRes.post_view.creator.local).toBe(true);
- expect(postRes.post_view.counts.score).toBe(1);
-
- // Make sure that post doesn't make it to beta community
- let searchBeta = await searchPostLocal(beta, postRes.post_view.post);
- let betaPost = searchBeta.posts[0];
- expect(betaPost).toBeUndefined();
+ expect(postRes.post_view).toBeUndefined();
// Unban alpha
let unBanAlpha = await banPersonFromCommunity(
false
);
expect(unBanAlpha.banned).toBe(false);
+ let postRes2 = await createPost(alpha, betaCommunity.community.id);
+ expect(postRes2.post_view.post).toBeDefined();
+ expect(postRes2.post_view.community.local).toBe(false);
+ expect(postRes2.post_view.creator.local).toBe(true);
+ expect(postRes2.post_view.counts.score).toBe(1);
+
+ // Make sure that post makes it to beta community
+ let searchBeta = await searchPostLocal(beta, postRes2.post_view.post);
+ let betaPost = searchBeta.posts[0];
+ expect(betaPost).toBeDefined();
});
person_id: data.person_id,
};
+ let community = blocking(context.pool(), move |conn: &'_ _| {
+ Community::read(conn, community_id)
+ })
+ .await??;
+ let banned_person = blocking(context.pool(), move |conn: &'_ _| {
+ Person::read(conn, banned_person_id)
+ })
+ .await??;
+
if data.ban {
let ban = move |conn: &'_ _| CommunityPersonBan::ban(conn, &community_user_ban_form);
if blocking(context.pool(), ban).await?.is_err() {
})
.await?
.ok();
+
+ community
+ .send_block_user(&local_user_view.person, banned_person, context)
+ .await?;
} else {
let unban = move |conn: &'_ _| CommunityPersonBan::unban(conn, &community_user_ban_form);
if blocking(context.pool(), unban).await?.is_err() {
return Err(ApiError::err("community_user_already_banned").into());
}
+ community
+ .send_undo_block_user(&local_user_view.person, banned_person, context)
+ .await?;
}
// Remove/Restore their data if that's desired
// Set the mention tags
.set_many_tags(maa.get_tags()?);
- send_to_community(create.clone(), &creator, &community, context).await?;
+ send_to_community(create.clone(), &creator, &community, None, context).await?;
send_comment_mentions(&creator, maa.inboxes, create, context).await?;
Ok(())
}
// Set the mention tags
.set_many_tags(maa.get_tags()?);
- send_to_community(update.clone(), &creator, &community, context).await?;
+ send_to_community(update.clone(), &creator, &community, None, context).await?;
send_comment_mentions(&creator, maa.inboxes, update, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(delete, &creator, &community, context).await?;
+ send_to_community(delete, &creator, &community, None, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(undo, &creator, &community, context).await?;
+ send_to_community(undo, &creator, &community, None, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(remove, &mod_, &community, context).await?;
+ send_to_community(remove, &mod_, &community, None, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(undo, &mod_, &community, context).await?;
+ send_to_community(undo, &mod_, &community, None, context).await?;
Ok(())
}
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(like, &creator, &community, context).await?;
+ send_to_community(like, &creator, &community, None, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(dislike, &creator, &community, context).await?;
+ send_to_community(dislike, &creator, &community, None, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(undo, &creator, &community, context).await?;
+ send_to_community(undo, &creator, &community, None, context).await?;
Ok(())
}
}
activity_queue::{send_activity_single_dest, send_to_community, send_to_community_followers},
check_is_apub_id_valid,
extensions::context::lemmy_context,
- fetcher::person::get_or_fetch_and_upsert_person,
+ fetcher::{get_or_fetch_and_upsert_actor, person::get_or_fetch_and_upsert_person},
generate_moderators_url,
insert_activity,
ActorType,
};
use activitystreams::{
activity::{
- kind::{AcceptType, AddType, AnnounceType, DeleteType, LikeType, RemoveType, UndoType},
+ kind::{
+ AcceptType,
+ AddType,
+ AnnounceType,
+ BlockType,
+ DeleteType,
+ LikeType,
+ RemoveType,
+ UndoType,
+ },
Accept,
ActorAndObjectRefExt,
Add,
Announce,
+ Block,
Delete,
Follow,
OptTargetRefExt,
#[async_trait::async_trait(?Send)]
impl CommunityType for Community {
+ fn followers_url(&self) -> Url {
+ self.followers_url.clone().into_inner()
+ }
+
/// As a local community, accept the follow request from a remote person.
async fn send_accept_follow(
&self,
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(DeleteType::Delete)?)
.set_to(public())
- .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
+ .set_many_ccs(vec![self.followers_url()]);
- send_to_community_followers(delete, self, context).await?;
+ send_to_community_followers(delete, self, None, context).await?;
Ok(())
}
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(DeleteType::Delete)?)
.set_to(public())
- .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
+ .set_many_ccs(vec![self.followers_url()]);
let mut undo = Undo::new(self.actor_id(), delete.into_any_base()?);
undo
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(UndoType::Undo)?)
.set_to(public())
- .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
+ .set_many_ccs(vec![self.followers_url()]);
- send_to_community_followers(undo, self, context).await?;
+ send_to_community_followers(undo, self, None, context).await?;
Ok(())
}
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(RemoveType::Remove)?)
.set_to(public())
- .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
+ .set_many_ccs(vec![self.followers_url()]);
- send_to_community_followers(remove, self, context).await?;
+ send_to_community_followers(remove, self, None, context).await?;
Ok(())
}
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(RemoveType::Remove)?)
.set_to(public())
- .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
+ .set_many_ccs(vec![self.followers_url()]);
// Undo that fake activity
let mut undo = Undo::new(self.actor_id(), remove.into_any_base()?);
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(LikeType::Like)?)
.set_to(public())
- .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
+ .set_many_ccs(vec![self.followers_url()]);
- send_to_community_followers(undo, self, context).await?;
+ send_to_community_followers(undo, self, None, context).await?;
Ok(())
}
/// If we are announcing a local activity, it hasn't been stored in the database yet, and we need
/// to do it here, so that it can be fetched by ID. Remote activities are inserted into DB in the
/// inbox.
+ ///
+ /// If the `object` of the announced activity is an actor, the actor ID needs to be passed as
+ /// `object_actor`, so that the announce can be delivered to that user.
async fn send_announce(
&self,
activity: AnyBase,
+ object_actor: Option<Url>,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let inner_id = activity.id().context(location_info!())?;
insert_activity(inner_id, activity.clone(), true, false, context.pool()).await?;
}
- let mut announce = Announce::new(self.actor_id.to_owned().into_inner(), activity);
+ let mut ccs = vec![self.followers_url()];
+ let mut object_actor_inbox: Option<Url> = None;
+ if let Some(actor_id) = object_actor {
+ // Ignore errors, maybe its not actually an actor
+ // TODO: should pass the actual request counter in, but that seems complicated
+ let actor = get_or_fetch_and_upsert_actor(&actor_id, context, &mut 0)
+ .await
+ .ok();
+ if let Some(actor) = actor {
+ ccs.push(actor_id);
+ object_actor_inbox = Some(actor.get_shared_inbox_or_inbox_url());
+ }
+ }
+ let mut announce = Announce::new(self.actor_id(), activity);
announce
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(AnnounceType::Announce)?)
.set_to(public())
- .set_many_ccs(vec![self.followers_url.clone().into_inner()]);
+ .set_many_ccs(ccs);
- send_to_community_followers(announce, self, context).await?;
+ send_to_community_followers(announce, self, object_actor_inbox, context).await?;
Ok(())
}
added_mod: Person,
context: &LemmyContext,
) -> Result<(), LemmyError> {
- let mut add = Add::new(
- actor.actor_id.clone().into_inner(),
- added_mod.actor_id.into_inner(),
- );
+ let mut add = Add::new(actor.actor_id(), added_mod.actor_id());
add
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(AddType::Add)?)
.set_many_ccs(vec![self.actor_id()])
.set_target(generate_moderators_url(&self.actor_id)?.into_inner());
- send_to_community(add, actor, self, context).await?;
+ send_to_community(add, actor, self, Some(added_mod.actor_id()), context).await?;
Ok(())
}
removed_mod: Person,
context: &LemmyContext,
) -> Result<(), LemmyError> {
- let mut remove = Remove::new(
- actor.actor_id.clone().into_inner(),
- removed_mod.actor_id.into_inner(),
- );
+ let mut remove = Remove::new(actor.actor_id(), removed_mod.actor_id());
remove
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(RemoveType::Remove)?)
.set_many_ccs(vec![self.actor_id()])
.set_target(generate_moderators_url(&self.actor_id)?.into_inner());
- send_to_community(remove, &actor, self, context).await?;
+ send_to_community(remove, &actor, self, Some(removed_mod.actor_id()), context).await?;
+ Ok(())
+ }
+
+ async fn send_block_user(
+ &self,
+ actor: &Person,
+ blocked_user: Person,
+ context: &LemmyContext,
+ ) -> Result<(), LemmyError> {
+ let mut block = Block::new(actor.actor_id(), blocked_user.actor_id());
+ block
+ .set_many_contexts(lemmy_context()?)
+ .set_id(generate_activity_id(BlockType::Block)?)
+ .set_to(public())
+ .set_many_ccs(vec![self.actor_id()]);
+
+ send_to_community(block, &actor, self, Some(blocked_user.actor_id()), context).await?;
+ Ok(())
+ }
+
+ async fn send_undo_block_user(
+ &self,
+ actor: &Person,
+ unblocked_user: Person,
+ context: &LemmyContext,
+ ) -> Result<(), LemmyError> {
+ let mut block = Block::new(actor.actor_id(), unblocked_user.actor_id());
+ block
+ .set_many_contexts(lemmy_context()?)
+ .set_id(generate_activity_id(BlockType::Block)?)
+ .set_to(public())
+ .set_many_ccs(vec![self.actor_id()]);
+
+ // Undo that fake activity
+ let mut undo = Undo::new(actor.actor_id(), block.into_any_base()?);
+ undo
+ .set_many_contexts(lemmy_context()?)
+ .set_id(generate_activity_id(UndoType::Undo)?)
+ .set_to(public())
+ .set_many_ccs(vec![self.actor_id()]);
+
+ send_to_community(undo, &actor, self, Some(unblocked_user.actor_id()), context).await?;
Ok(())
}
}
})
.await?;
- let mut follow = Follow::new(self.actor_id.to_owned().into_inner(), community.actor_id());
+ let mut follow = Follow::new(self.actor_id(), community.actor_id());
follow
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(FollowType::Follow)?)
})
.await??;
- let mut follow = Follow::new(self.actor_id.to_owned().into_inner(), community.actor_id());
+ let mut follow = Follow::new(self.actor_id(), community.actor_id());
follow
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(FollowType::Follow)?)
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(create, creator, &community, context).await?;
+ send_to_community(create, creator, &community, None, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(update, creator, &community, context).await?;
+ send_to_community(update, creator, &community, None, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(delete, creator, &community, context).await?;
+ send_to_community(delete, creator, &community, None, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(undo, creator, &community, context).await?;
+ send_to_community(undo, creator, &community, None, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(remove, mod_, &community, context).await?;
+ send_to_community(remove, mod_, &community, None, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(undo, mod_, &community, context).await?;
+ send_to_community(undo, mod_, &community, None, context).await?;
Ok(())
}
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(like, &creator, &community, context).await?;
+ send_to_community(like, &creator, &community, None, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(dislike, &creator, &community, context).await?;
+ send_to_community(dislike, &creator, &community, None, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()]);
- send_to_community(undo, &creator, &community, context).await?;
+ send_to_community(undo, &creator, &community, None, context).await?;
Ok(())
}
}
WorkerConfig,
};
use itertools::Itertools;
-use lemmy_db_queries::DbPool;
use lemmy_db_schema::source::{community::Community, person::Person};
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext;
&activity.id_unchecked(),
&inbox
);
- send_activity_internal(
- context.activity_queue(),
- activity,
- creator,
- vec![inbox],
- context.pool(),
- true,
- true,
- )
- .await?;
+ send_activity_internal(context, activity, creator, vec![inbox], true, true).await?;
}
Ok(())
///
/// * `activity` the apub activity to send
/// * `community` the sending community
-/// * `sender_shared_inbox` in case of an announce, this should be the shared inbox of the inner
-/// activities creator, as receiving a known activity will cause an error
+/// * `extra_inbox` actor inbox which should receive the activity, in addition to followers
pub(crate) async fn send_to_community_followers<T, Kind>(
activity: T,
community: &Community,
+ extra_inbox: Option<Url>,
context: &LemmyContext,
) -> Result<(), LemmyError>
where
Kind: Serialize,
<T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
{
- let follower_inboxes: Vec<Url> = community
- .get_follower_inboxes(context.pool())
- .await?
- .iter()
- .unique()
- .filter(|inbox| inbox.host_str() != Some(&Settings::get().hostname()))
- .filter(|inbox| check_is_apub_id_valid(inbox).is_ok())
- .map(|inbox| inbox.to_owned())
- .collect();
+ let extra_inbox: Vec<Url> = extra_inbox.into_iter().collect();
+ let follower_inboxes: Vec<Url> = vec![
+ community.get_follower_inboxes(context.pool()).await?,
+ extra_inbox,
+ ]
+ .iter()
+ .flatten()
+ .unique()
+ .filter(|inbox| inbox.host_str() != Some(&Settings::get().hostname()))
+ .filter(|inbox| check_is_apub_id_valid(inbox).is_ok())
+ .map(|inbox| inbox.to_owned())
+ .collect();
debug!(
"Sending activity {:?} to followers of {}",
&activity.id_unchecked().map(|i| i.to_string()),
&community.actor_id
);
- send_activity_internal(
- context.activity_queue(),
- activity,
- community,
- follower_inboxes,
- context.pool(),
- true,
- false,
- )
- .await?;
+ send_activity_internal(context, activity, community, follower_inboxes, true, false).await?;
Ok(())
}
/// * `activity` the activity to send
/// * `creator` the creator of the activity
/// * `community` the destination community
+/// * `object_actor` if the object of the activity is an actor, it should be passed here so it can
+/// be sent directly to the actor
///
pub(crate) async fn send_to_community<T, Kind>(
activity: T,
creator: &Person,
community: &Community,
+ object_actor: Option<Url>,
context: &LemmyContext,
) -> Result<(), LemmyError>
where
// if this is a local community, we need to do an announce from the community instead
if community.local {
community
- .send_announce(activity.into_any_base()?, context)
+ .send_announce(activity.into_any_base()?, object_actor, context)
.await?;
} else {
let inbox = community.get_shared_inbox_or_inbox_url();
&activity.id_unchecked(),
&community.actor_id
);
- send_activity_internal(
- context.activity_queue(),
- activity,
- creator,
- vec![inbox],
- context.pool(),
- true,
- false,
- )
- .await?;
+ // dont send to object_actor here, as that is responsibility of the community itself
+ send_activity_internal(context, activity, creator, vec![inbox], true, false).await?;
}
Ok(())
.map(|i| i.to_owned())
.collect();
send_activity_internal(
- context.activity_queue(),
- activity,
- creator,
- mentions,
- context.pool(),
- false, // Don't create a new DB row
+ context, activity, creator, mentions, false, // Don't create a new DB row
false,
)
.await?;
/// The caller of this function needs to remove any blocked domains from `to`,
/// using `check_is_apub_id_valid()`.
async fn send_activity_internal<T, Kind>(
- activity_sender: &QueueHandle,
+ context: &LemmyContext,
activity: T,
actor: &dyn ActorType,
inboxes: Vec<Url>,
- pool: &DbPool,
insert_into_db: bool,
sensitive: bool,
) -> Result<(), LemmyError>
// might send the same ap_id
if insert_into_db {
let id = activity.id().context(location_info!())?;
- insert_activity(id, activity.clone(), true, sensitive, pool).await?;
+ insert_activity(id, activity.clone(), true, sensitive, context.pool()).await?;
}
for i in inboxes {
if env::var("LEMMY_TEST_SEND_SYNC").is_ok() {
do_send(message, &Client::default()).await?;
} else {
- activity_sender.queue::<SendActivityTask>(message)?;
+ context.activity_queue.queue::<SendActivityTask>(message)?;
}
}
#[async_trait::async_trait(?Send)]
pub trait CommunityType {
+ fn followers_url(&self) -> Url;
async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError>;
async fn send_accept_follow(
&self,
async fn send_announce(
&self,
activity: AnyBase,
+ object: Option<Url>,
context: &LemmyContext,
) -> Result<(), LemmyError>;
removed_mod: Person,
context: &LemmyContext,
) -> Result<(), LemmyError>;
+
+ async fn send_block_user(
+ &self,
+ actor: &Person,
+ blocked_user: Person,
+ context: &LemmyContext,
+ ) -> Result<(), LemmyError>;
+ async fn send_undo_block_user(
+ &self,
+ actor: &Person,
+ blocked_user: Person,
+ context: &LemmyContext,
+ ) -> Result<(), LemmyError>;
}
#[async_trait::async_trait(?Send)]
is_activity_already_known,
receive_for_community::{
receive_add_for_community,
+ receive_block_user_for_community,
receive_create_for_community,
receive_delete_for_community,
receive_dislike_for_community,
Delete, // post or comment deleted by creator
Remove, // post or comment removed by mod or admin, or mod removed from community
Add, // mod added to community
+ Block, // user blocked by community
}
pub type CommunityAcceptedActivities = ActorAndObject<CommunityValidTypes>;
.await?;
true
}
+ CommunityValidTypes::Block => {
+ Box::pin(receive_block_user_for_community(
+ context,
+ any_base.clone(),
+ None,
+ request_counter,
+ ))
+ .await?;
+ true
+ }
};
if do_announce {
// Check again that the activity is public, just to be sure
verify_is_addressed_to_public(&activity)?;
+ let mut object_actor = activity.object().clone().single_xsd_any_uri();
+ // If activity is something like Undo/Block, we need to access activity.object.object
+ if object_actor.is_none() {
+ object_actor = activity
+ .object()
+ .as_one()
+ .map(|a| ActorAndObject::from_any_base(a.to_owned()).ok())
+ .flatten()
+ .flatten()
+ .map(|a: ActorAndObject<CommunityValidTypes>| a.object().to_owned().single_xsd_any_uri())
+ .flatten();
+ }
to_community
- .send_announce(activity.into_any_base()?, context)
+ .send_announce(activity.into_any_base()?, object_actor, context)
.await?;
}
is_addressed_to_local_person,
receive_for_community::{
receive_add_for_community,
+ receive_block_user_for_community,
receive_create_for_community,
receive_delete_for_community,
receive_dislike_for_community,
Remove,
Undo,
Add,
+ Block,
}
/// Takes an announce and passes the inner activity to the appropriate handler.
Some(Add) => {
receive_add_for_community(context, inner_activity, Some(announce), request_counter).await
}
+ Some(Block) => {
+ receive_block_user_for_community(context, inner_activity, Some(announce), request_counter)
+ .await
+ }
_ => receive_unhandled_activity(inner_activity),
}
}
ActorAndObjectRef,
Add,
Announce,
+ Block,
Create,
Delete,
Dislike,
CommunityType,
PostOrComment,
};
-use lemmy_db_queries::{source::community::CommunityModerator_, ApubObject, Crud, Joinable};
+use lemmy_db_queries::{
+ source::community::CommunityModerator_,
+ ApubObject,
+ Bannable,
+ Crud,
+ Followable,
+ Joinable,
+};
use lemmy_db_schema::{
source::{
- community::{Community, CommunityModerator, CommunityModeratorForm},
+ community::{
+ Community,
+ CommunityFollower,
+ CommunityFollowerForm,
+ CommunityModerator,
+ CommunityModeratorForm,
+ CommunityPersonBan,
+ CommunityPersonBanForm,
+ },
person::Person,
site::Site,
},
CommunityModerator::leave(conn, &form)
})
.await??;
- community.send_announce(remove_any_base, context).await?;
+ community
+ .send_announce(
+ remove_any_base,
+ remove.object().clone().single_xsd_any_uri(),
+ context,
+ )
+ .await?;
// TODO: send websocket notification about removed mod
Ok(())
}
Remove,
Like,
Dislike,
+ Block,
}
/// A post/comment action being reverted (either a delete, remove, upvote or downvote)
Some(Dislike) => {
receive_undo_dislike_for_community(context, undo, expected_domain, request_counter).await
}
+ Some(Block) => {
+ receive_undo_block_user_for_community(
+ context,
+ undo,
+ announce,
+ expected_domain,
+ request_counter,
+ )
+ .await
+ }
_ => receive_unhandled_activity(undo),
}
}
.await??;
}
if community.local {
- community.send_announce(add_any_base, context).await?;
+ community
+ .send_announce(
+ add_any_base,
+ add.object().clone().single_xsd_any_uri(),
+ context,
+ )
+ .await?;
}
// TODO: send websocket notification about added mod
Ok(())
}
}
+pub(crate) async fn receive_block_user_for_community(
+ context: &LemmyContext,
+ block_any_base: AnyBase,
+ announce: Option<Announce>,
+ request_counter: &mut i32,
+) -> Result<(), LemmyError> {
+ let block = Block::from_any_base(block_any_base.to_owned())?.context(location_info!())?;
+ let community = extract_community_from_cc(&block, context).await?;
+
+ verify_mod_activity(&block, announce, &community, context).await?;
+ verify_is_addressed_to_public(&block)?;
+
+ let blocked_user = block
+ .object()
+ .as_single_xsd_any_uri()
+ .context(location_info!())?;
+ let blocked_user =
+ get_or_fetch_and_upsert_person(&blocked_user, context, request_counter).await?;
+
+ let community_user_ban_form = CommunityPersonBanForm {
+ community_id: community.id,
+ person_id: blocked_user.id,
+ };
+
+ blocking(context.pool(), move |conn: &'_ _| {
+ CommunityPersonBan::ban(conn, &community_user_ban_form)
+ })
+ .await??;
+
+ // Also unsubscribe them from the community, if they are subscribed
+ let community_follower_form = CommunityFollowerForm {
+ community_id: community.id,
+ person_id: blocked_user.id,
+ pending: false,
+ };
+ blocking(context.pool(), move |conn: &'_ _| {
+ CommunityFollower::unfollow(conn, &community_follower_form)
+ })
+ .await?
+ .ok();
+
+ Ok(())
+}
+
+pub(crate) async fn receive_undo_block_user_for_community(
+ context: &LemmyContext,
+ undo: Undo,
+ announce: Option<Announce>,
+ expected_domain: &Url,
+ request_counter: &mut i32,
+) -> Result<(), LemmyError> {
+ let object = undo.object().clone().one().context(location_info!())?;
+ let block = Block::from_any_base(object)?.context(location_info!())?;
+ let community = extract_community_from_cc(&block, context).await?;
+
+ verify_activity_domains_valid(&block, &expected_domain, false)?;
+ verify_is_addressed_to_public(&block)?;
+ verify_undo_remove_actor_instance(&undo, &block, &announce, context).await?;
+
+ let blocked_user = block
+ .object()
+ .as_single_xsd_any_uri()
+ .context(location_info!())?;
+ let blocked_user =
+ get_or_fetch_and_upsert_person(&blocked_user, context, request_counter).await?;
+
+ let community_user_ban_form = CommunityPersonBanForm {
+ community_id: community.id,
+ person_id: blocked_user.id,
+ };
+
+ blocking(context.pool(), move |conn: &'_ _| {
+ CommunityPersonBan::unban(conn, &community_user_ban_form)
+ })
+ .await??;
+
+ Ok(())
+}
+
async fn fetch_post_or_comment_by_id(
apub_id: &Url,
context: &LemmyContext,
Remove,
Announce,
Add,
+ Block,
}
// TODO: this isnt entirely correct, cause some of these receive are not ActorAndObject,