]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/following/follow.rs
Split activity table into sent and received parts (fixes #3103) (#3583)
[lemmy.git] / crates / apub / src / activities / following / follow.rs
1 use crate::{
2   activities::{
3     generate_activity_id,
4     send_lemmy_activity,
5     verify_person,
6     verify_person_in_community,
7   },
8   fetcher::user_or_community::UserOrCommunity,
9   insert_received_activity,
10   objects::{community::ApubCommunity, person::ApubPerson},
11   protocol::activities::following::{
12     accept::AcceptFollow,
13     follow::Follow,
14     undo_follow::UndoFollow,
15   },
16   SendActivity,
17 };
18 use activitypub_federation::{
19   config::Data,
20   kinds::activity::FollowType,
21   protocol::verification::verify_urls_match,
22   traits::{ActivityHandler, Actor},
23 };
24 use lemmy_api_common::{
25   community::{BlockCommunity, BlockCommunityResponse},
26   context::LemmyContext,
27   utils::local_user_view_from_jwt,
28 };
29 use lemmy_db_schema::{
30   source::{
31     community::{Community, CommunityFollower, CommunityFollowerForm},
32     person::{PersonFollower, PersonFollowerForm},
33   },
34   traits::{Crud, Followable},
35 };
36 use lemmy_utils::error::LemmyError;
37 use url::Url;
38
39 impl Follow {
40   pub(in crate::activities::following) fn new(
41     actor: &ApubPerson,
42     community: &ApubCommunity,
43     context: &Data<LemmyContext>,
44   ) -> Result<Follow, LemmyError> {
45     Ok(Follow {
46       actor: actor.id().into(),
47       object: community.id().into(),
48       to: Some([community.id().into()]),
49       kind: FollowType::Follow,
50       id: generate_activity_id(
51         FollowType::Follow,
52         &context.settings().get_protocol_and_hostname(),
53       )?,
54     })
55   }
56
57   #[tracing::instrument(skip_all)]
58   pub async fn send(
59     actor: &ApubPerson,
60     community: &ApubCommunity,
61     context: &Data<LemmyContext>,
62   ) -> Result<(), LemmyError> {
63     let community_follower_form = CommunityFollowerForm {
64       community_id: community.id,
65       person_id: actor.id,
66       pending: true,
67     };
68     CommunityFollower::follow(&mut context.pool(), &community_follower_form)
69       .await
70       .ok();
71
72     let follow = Follow::new(actor, community, context)?;
73     let inbox = vec![community.shared_inbox_or_inbox()];
74     send_lemmy_activity(context, follow, actor, inbox, true).await
75   }
76 }
77
78 #[async_trait::async_trait]
79 impl ActivityHandler for Follow {
80   type DataType = LemmyContext;
81   type Error = LemmyError;
82
83   fn id(&self) -> &Url {
84     &self.id
85   }
86
87   fn actor(&self) -> &Url {
88     self.actor.inner()
89   }
90
91   #[tracing::instrument(skip_all)]
92   async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
93     insert_received_activity(&self.id, context).await?;
94     verify_person(&self.actor, context).await?;
95     let object = self.object.dereference(context).await?;
96     if let UserOrCommunity::Community(c) = object {
97       verify_person_in_community(&self.actor, &c, context).await?;
98     }
99     if let Some(to) = &self.to {
100       verify_urls_match(to[0].inner(), self.object.inner())?;
101     }
102     Ok(())
103   }
104
105   #[tracing::instrument(skip_all)]
106   async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
107     let actor = self.actor.dereference(context).await?;
108     let object = self.object.dereference(context).await?;
109     match object {
110       UserOrCommunity::User(u) => {
111         let form = PersonFollowerForm {
112           person_id: u.id,
113           follower_id: actor.id,
114           pending: false,
115         };
116         PersonFollower::follow(&mut context.pool(), &form).await?;
117       }
118       UserOrCommunity::Community(c) => {
119         let form = CommunityFollowerForm {
120           community_id: c.id,
121           person_id: actor.id,
122           pending: false,
123         };
124         CommunityFollower::follow(&mut context.pool(), &form).await?;
125       }
126     }
127
128     AcceptFollow::send(self, context).await
129   }
130 }
131
132 #[async_trait::async_trait]
133 impl SendActivity for BlockCommunity {
134   type Response = BlockCommunityResponse;
135
136   async fn send_activity(
137     request: &Self,
138     _response: &Self::Response,
139     context: &Data<LemmyContext>,
140   ) -> Result<(), LemmyError> {
141     let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
142     let community = Community::read(&mut context.pool(), request.community_id).await?;
143     UndoFollow::send(&local_user_view.person.into(), &community.into(), context).await
144   }
145 }