]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/following/undo_follow.rs
Implement federated user following (fixes #752) (#2577)
[lemmy.git] / crates / apub / src / activities / following / undo_follow.rs
1 use crate::{
2   activities::{generate_activity_id, send_lemmy_activity, verify_person},
3   fetcher::user_or_community::UserOrCommunity,
4   local_instance,
5   objects::{community::ApubCommunity, person::ApubPerson},
6   protocol::activities::following::{follow::Follow, undo_follow::UndoFollow},
7   ActorType,
8 };
9 use activitypub_federation::{
10   core::object_id::ObjectId,
11   data::Data,
12   traits::{ActivityHandler, Actor},
13   utils::verify_urls_match,
14 };
15 use activitystreams_kinds::activity::UndoType;
16 use lemmy_db_schema::{
17   source::{
18     community::{CommunityFollower, CommunityFollowerForm},
19     person::{PersonFollower, PersonFollowerForm},
20   },
21   traits::Followable,
22 };
23 use lemmy_utils::error::LemmyError;
24 use lemmy_websocket::LemmyContext;
25 use url::Url;
26
27 impl UndoFollow {
28   #[tracing::instrument(skip_all)]
29   pub async fn send(
30     actor: &ApubPerson,
31     community: &ApubCommunity,
32     context: &LemmyContext,
33   ) -> Result<(), LemmyError> {
34     let object = Follow::new(actor, community, context)?;
35     let undo = UndoFollow {
36       actor: ObjectId::new(actor.actor_id()),
37       object,
38       kind: UndoType::Undo,
39       id: generate_activity_id(
40         UndoType::Undo,
41         &context.settings().get_protocol_and_hostname(),
42       )?,
43     };
44     let inbox = vec![community.shared_inbox_or_inbox()];
45     send_lemmy_activity(context, undo, actor, inbox, true).await
46   }
47 }
48
49 #[async_trait::async_trait(?Send)]
50 impl ActivityHandler for UndoFollow {
51   type DataType = LemmyContext;
52   type Error = LemmyError;
53
54   fn id(&self) -> &Url {
55     &self.id
56   }
57
58   fn actor(&self) -> &Url {
59     self.actor.inner()
60   }
61
62   #[tracing::instrument(skip_all)]
63   async fn verify(
64     &self,
65     context: &Data<LemmyContext>,
66     request_counter: &mut i32,
67   ) -> Result<(), LemmyError> {
68     verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
69     verify_person(&self.actor, context, request_counter).await?;
70     self.object.verify(context, request_counter).await?;
71     Ok(())
72   }
73
74   #[tracing::instrument(skip_all)]
75   async fn receive(
76     self,
77     context: &Data<LemmyContext>,
78     request_counter: &mut i32,
79   ) -> Result<(), LemmyError> {
80     let person = self
81       .actor
82       .dereference(context, local_instance(context).await, request_counter)
83       .await?;
84     let object = self
85       .object
86       .object
87       .dereference(context, local_instance(context).await, request_counter)
88       .await?;
89
90     match object {
91       UserOrCommunity::User(u) => {
92         let form = PersonFollowerForm {
93           person_id: u.id,
94           follower_id: person.id,
95           pending: false,
96         };
97         PersonFollower::unfollow(context.pool(), &form).await?;
98       }
99       UserOrCommunity::Community(c) => {
100         let form = CommunityFollowerForm {
101           community_id: c.id,
102           person_id: person.id,
103           pending: false,
104         };
105         CommunityFollower::unfollow(context.pool(), &form).await?;
106       }
107     }
108
109     Ok(())
110   }
111 }