Skip to content

Commit

Permalink
Implement federated user following (fixes #752)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nutomic committed Nov 22, 2022
1 parent bc19d94 commit 8d582d9
Show file tree
Hide file tree
Showing 32 changed files with 316 additions and 152 deletions.
4 changes: 2 additions & 2 deletions crates/api/src/community/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use lemmy_api_common::{
community::{BlockCommunity, BlockCommunityResponse},
utils::get_local_user_view_from_jwt,
};
use lemmy_apub::protocol::activities::following::undo_follow::UndoFollowCommunity;
use lemmy_apub::protocol::activities::following::undo_follow::UndoFollow;
use lemmy_db_schema::{
source::{
community::{Community, CommunityFollower, CommunityFollowerForm},
Expand Down Expand Up @@ -53,7 +53,7 @@ impl Perform for BlockCommunity {
.await
.ok();
let community = Community::read(context.pool(), community_id).await?;
UndoFollowCommunity::send(&local_user_view.person.into(), &community.into(), context).await?;
UndoFollow::send(&local_user_view.person.into(), &community.into(), context).await?;
} else {
CommunityBlock::unblock(context.pool(), &community_block_form)
.await
Expand Down
7 changes: 3 additions & 4 deletions crates/api/src/community/follow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use lemmy_api_common::{
use lemmy_apub::{
objects::community::ApubCommunity,
protocol::activities::following::{
follow::FollowCommunity as FollowCommunityApub,
undo_follow::UndoFollowCommunity,
follow::Follow as FollowCommunityApub,
undo_follow::UndoFollow,
},
};
use lemmy_db_schema::{
Expand Down Expand Up @@ -60,8 +60,7 @@ impl Perform for FollowCommunity {
FollowCommunityApub::send(&local_user_view.person.clone().into(), &community, context)
.await?;
} else {
UndoFollowCommunity::send(&local_user_view.person.clone().into(), &community, context)
.await?;
UndoFollow::send(&local_user_view.person.clone().into(), &community, context).await?;
CommunityFollower::unfollow(context.pool(), &community_follower_form)
.await
.map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?;
Expand Down
2 changes: 1 addition & 1 deletion crates/apub/src/activities/block/block_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ impl BlockUser {
SiteOrCommunity::Community(c) => {
let activity = AnnouncableActivities::BlockUser(block);
let inboxes = vec![user.shared_inbox_or_inbox()];
send_activity_in_community(activity, mod_, c, inboxes, context).await
send_activity_in_community(activity, mod_, c, inboxes, true, context).await
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/apub/src/activities/block/undo_block_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl UndoBlockUser {
}
SiteOrCommunity::Community(c) => {
let activity = AnnouncableActivities::UndoBlockUser(undo);
send_activity_in_community(activity, mod_, c, inboxes, context).await
send_activity_in_community(activity, mod_, c, inboxes, true, context).await
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/apub/src/activities/community/add_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl AddMod {

let activity = AnnouncableActivities::AddMod(add);
let inboxes = vec![added_mod.shared_inbox_or_inbox()];
send_activity_in_community(activity, actor, community, inboxes, context).await
send_activity_in_community(activity, actor, community, inboxes, true, context).await
}
}

Expand Down
34 changes: 25 additions & 9 deletions crates/apub/src/activities/community/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use crate::{
activities::send_lemmy_activity,
activity_lists::AnnouncableActivities,
local_instance,
objects::community::ApubCommunity,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::community::announce::AnnounceActivity,
ActorType,
};
use activitypub_federation::{core::object_id::ObjectId, traits::Actor};
use lemmy_db_schema::source::person::PersonFollower;
use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use url::Url;
Expand All @@ -17,20 +17,36 @@ pub mod remove_mod;
pub mod report;
pub mod update;

#[tracing::instrument(skip_all)]
pub(crate) async fn send_activity_in_community<ActorT>(
pub(crate) async fn send_activity_in_community(
activity: AnnouncableActivities,
actor: &ActorT,
actor: &ApubPerson,
community: &ApubCommunity,
mut inboxes: Vec<Url>,
is_mod_action: bool,
context: &LemmyContext,
) -> Result<(), LemmyError>
where
ActorT: Actor + ActorType,
{
) -> Result<(), LemmyError> {
// send to user followers
if !is_mod_action {
let person_follower_inboxes = PersonFollower::list_followers(context.pool(), actor.id)
.await?
.into_iter()
.map(|p| ApubPerson(p).shared_inbox_or_inbox())
.collect();
send_lemmy_activity(
context,
activity.clone(),
actor,
person_follower_inboxes,
false,
)
.await?;
}

// send to community
inboxes.push(community.shared_inbox_or_inbox());
send_lemmy_activity(context, activity.clone(), actor, inboxes, false).await?;

// send to community followers
if community.local {
AnnounceActivity::send(activity.try_into()?, community, context).await?;
}
Expand Down
2 changes: 1 addition & 1 deletion crates/apub/src/activities/community/remove_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl RemoveMod {

let activity = AnnouncableActivities::RemoveMod(remove);
let inboxes = vec![removed_mod.shared_inbox_or_inbox()];
send_activity_in_community(activity, actor, community, inboxes, context).await
send_activity_in_community(activity, actor, community, inboxes, true, context).await
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/apub/src/activities/community/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl UpdateCommunity {
};

let activity = AnnouncableActivities::UpdateCommunity(update);
send_activity_in_community(activity, actor, &community, vec![], context).await
send_activity_in_community(activity, actor, &community, vec![], true, context).await
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/apub/src/activities/create_or_update/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl CreateOrUpdateComment {
}

let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update);
send_activity_in_community(activity, actor, &community, inboxes, context).await
send_activity_in_community(activity, actor, &community, inboxes, false, context).await
}
}

Expand Down
3 changes: 2 additions & 1 deletion crates/apub/src/activities/create_or_update/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ impl CreateOrUpdatePost {
let community: ApubCommunity = Community::read(context.pool(), community_id).await?.into();

let create_or_update = CreateOrUpdatePost::new(post, actor, &community, kind, context).await?;
let is_mod_action = create_or_update.object.is_mod_action(context).await?;
let activity = AnnouncableActivities::CreateOrUpdatePost(create_or_update);
send_activity_in_community(activity, actor, &community, vec![], context).await?;
send_activity_in_community(activity, actor, &community, vec![], is_mod_action, context).await?;
Ok(())
}
}
Expand Down
11 changes: 10 additions & 1 deletion crates/apub/src/activities/deletion/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,23 @@ pub async fn send_apub_delete_in_community(
context: &LemmyContext,
) -> Result<(), LemmyError> {
let actor = ApubPerson::from(actor);
let is_mod_action = reason.is_some();
let activity = if deleted {
let delete = Delete::new(&actor, object, public(), Some(&community), reason, context)?;
AnnouncableActivities::Delete(delete)
} else {
let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, context)?;
AnnouncableActivities::UndoDelete(undo)
};
send_activity_in_community(activity, &actor, &community.into(), vec![], context).await
send_activity_in_community(
activity,
&actor,
&community.into(),
vec![],
is_mod_action,
context,
)
.await
}

#[tracing::instrument(skip_all)]
Expand Down
16 changes: 8 additions & 8 deletions crates/apub/src/activities/following/accept.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
activities::{generate_activity_id, send_lemmy_activity},
local_instance,
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
protocol::activities::following::{accept::AcceptFollow, follow::Follow},
ActorType,
};
use activitypub_federation::{
Expand All @@ -19,21 +19,21 @@ use lemmy_utils::error::LemmyError;
use lemmy_websocket::{messages::SendUserRoomMessage, LemmyContext, UserOperation};
use url::Url;

impl AcceptFollowCommunity {
impl AcceptFollow {
#[tracing::instrument(skip_all)]
pub async fn send(
follow: FollowCommunity,
follow: Follow,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let community = follow.object.dereference_local(context).await?;
let user_or_community = follow.object.dereference_local(context).await?;
let person = follow
.actor
.clone()
.dereference(context, local_instance(context).await, request_counter)
.await?;
let accept = AcceptFollowCommunity {
actor: ObjectId::new(community.actor_id()),
let accept = AcceptFollow {
actor: ObjectId::new(user_or_community.actor_id()),
object: follow,
kind: AcceptType::Accept,
id: generate_activity_id(
Expand All @@ -42,13 +42,13 @@ impl AcceptFollowCommunity {
)?,
};
let inbox = vec![person.shared_inbox_or_inbox()];
send_lemmy_activity(context, accept, &community, inbox, true).await
send_lemmy_activity(context, accept, &user_or_community, inbox, true).await
}
}

/// Handle accepted follows
#[async_trait::async_trait(?Send)]
impl ActivityHandler for AcceptFollowCommunity {
impl ActivityHandler for AcceptFollow {
type DataType = LemmyContext;
type Error = LemmyError;

Expand Down
58 changes: 36 additions & 22 deletions crates/apub/src/activities/following/follow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ use crate::{
verify_person,
verify_person_in_community,
},
fetcher::user_or_community::UserOrCommunity,
local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
protocol::activities::following::{accept::AcceptFollow, follow::Follow},
ActorType,
};
use activitypub_federation::{
Expand All @@ -17,20 +18,23 @@ use activitypub_federation::{
};
use activitystreams_kinds::activity::FollowType;
use lemmy_db_schema::{
source::community::{CommunityFollower, CommunityFollowerForm},
source::{
community::{CommunityFollower, CommunityFollowerForm},
person::{PersonFollower, PersonFollowerForm},
},
traits::Followable,
};
use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use url::Url;

impl FollowCommunity {
impl Follow {
pub(in crate::activities::following) fn new(
actor: &ApubPerson,
community: &ApubCommunity,
context: &LemmyContext,
) -> Result<FollowCommunity, LemmyError> {
Ok(FollowCommunity {
) -> Result<Follow, LemmyError> {
Ok(Follow {
actor: ObjectId::new(actor.actor_id()),
object: ObjectId::new(community.actor_id()),
kind: FollowType::Follow,
Expand All @@ -56,14 +60,14 @@ impl FollowCommunity {
.await
.ok();

let follow = FollowCommunity::new(actor, community, context)?;
let follow = Follow::new(actor, community, context)?;
let inbox = vec![community.shared_inbox_or_inbox()];
send_lemmy_activity(context, follow, actor, inbox, true).await
}
}

#[async_trait::async_trait(?Send)]
impl ActivityHandler for FollowCommunity {
impl ActivityHandler for Follow {
type DataType = LemmyContext;
type Error = LemmyError;

Expand All @@ -82,11 +86,13 @@ impl ActivityHandler for FollowCommunity {
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_person(&self.actor, context, request_counter).await?;
let community = self
let object = self
.object
.dereference(context, local_instance(context).await, request_counter)
.await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
if let UserOrCommunity::Community(c) = object {
verify_person_in_community(&self.actor, &c, context, request_counter).await?;
}
Ok(())
}

Expand All @@ -96,25 +102,33 @@ impl ActivityHandler for FollowCommunity {
context: &Data<LemmyContext>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let person = self
let actor = self
.actor
.dereference(context, local_instance(context).await, request_counter)
.await?;
let community = self
let object = self
.object
.dereference(context, local_instance(context).await, request_counter)
.await?;
let community_follower_form = CommunityFollowerForm {
community_id: community.id,
person_id: person.id,
pending: false,
};

// This will fail if they're already a follower, but ignore the error.
CommunityFollower::follow(context.pool(), &community_follower_form)
.await
.ok();
match object {
UserOrCommunity::User(u) => {
let form = PersonFollowerForm {
person_id: u.id,
follower_id: actor.id,
pending: false,
};
PersonFollower::follow(context.pool(), &form).await?;
}
UserOrCommunity::Community(c) => {
let form = CommunityFollowerForm {
community_id: c.id,
person_id: actor.id,
pending: false,
};
CommunityFollower::follow(context.pool(), &form).await?;
}
}

AcceptFollowCommunity::send(self, context, request_counter).await
AcceptFollow::send(self, context, request_counter).await
}
}
Loading

0 comments on commit 8d582d9

Please sign in to comment.