]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/following/follow.rs
512c074cdc068472a975fc880c994114c80958d1
[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   check_apub_id_valid,
9   fetch_local_site_data,
10   local_instance,
11   objects::{community::ApubCommunity, person::ApubPerson},
12   protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
13   ActorType,
14 };
15 use activitypub_federation::{
16   core::object_id::ObjectId,
17   data::Data,
18   traits::{ActivityHandler, Actor},
19 };
20 use activitystreams_kinds::activity::FollowType;
21 use lemmy_api_common::utils::blocking;
22 use lemmy_db_schema::{
23   source::community::{CommunityFollower, CommunityFollowerForm},
24   traits::Followable,
25 };
26 use lemmy_utils::error::LemmyError;
27 use lemmy_websocket::LemmyContext;
28 use url::Url;
29
30 impl FollowCommunity {
31   pub(in crate::activities::following) fn new(
32     actor: &ApubPerson,
33     community: &ApubCommunity,
34     context: &LemmyContext,
35   ) -> Result<FollowCommunity, LemmyError> {
36     Ok(FollowCommunity {
37       actor: ObjectId::new(actor.actor_id()),
38       object: ObjectId::new(community.actor_id()),
39       kind: FollowType::Follow,
40       id: generate_activity_id(
41         FollowType::Follow,
42         &context.settings().get_protocol_and_hostname(),
43       )?,
44       unparsed: Default::default(),
45     })
46   }
47
48   #[tracing::instrument(skip_all)]
49   pub async fn send(
50     actor: &ApubPerson,
51     community: &ApubCommunity,
52     context: &LemmyContext,
53   ) -> Result<(), LemmyError> {
54     let community_follower_form = CommunityFollowerForm {
55       community_id: community.id,
56       person_id: actor.id,
57       pending: true,
58     };
59     blocking(context.pool(), move |conn| {
60       CommunityFollower::follow(conn, &community_follower_form).ok()
61     })
62     .await?;
63
64     let follow = FollowCommunity::new(actor, community, context)?;
65     let inbox = vec![community.shared_inbox_or_inbox()];
66     send_lemmy_activity(context, follow, actor, inbox, true).await
67   }
68 }
69
70 #[async_trait::async_trait(?Send)]
71 impl ActivityHandler for FollowCommunity {
72   type DataType = LemmyContext;
73   type Error = LemmyError;
74
75   fn id(&self) -> &Url {
76     &self.id
77   }
78
79   fn actor(&self) -> &Url {
80     self.actor.inner()
81   }
82
83   #[tracing::instrument(skip_all)]
84   async fn verify(
85     &self,
86     context: &Data<LemmyContext>,
87     request_counter: &mut i32,
88   ) -> Result<(), LemmyError> {
89     let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
90     check_apub_id_valid(self.id(), &local_site_data, context.settings())
91       .map_err(LemmyError::from_message)?;
92     verify_person(&self.actor, context, request_counter).await?;
93     let community = self
94       .object
95       .dereference(context, local_instance(context), request_counter)
96       .await?;
97     verify_person_in_community(&self.actor, &community, context, request_counter).await?;
98     Ok(())
99   }
100
101   #[tracing::instrument(skip_all)]
102   async fn receive(
103     self,
104     context: &Data<LemmyContext>,
105     request_counter: &mut i32,
106   ) -> Result<(), LemmyError> {
107     let person = self
108       .actor
109       .dereference(context, local_instance(context), request_counter)
110       .await?;
111     let community = self
112       .object
113       .dereference(context, local_instance(context), request_counter)
114       .await?;
115     let community_follower_form = CommunityFollowerForm {
116       community_id: community.id,
117       person_id: person.id,
118       pending: false,
119     };
120
121     // This will fail if they're already a follower, but ignore the error.
122     blocking(context.pool(), move |conn| {
123       CommunityFollower::follow(conn, &community_follower_form).ok()
124     })
125     .await?;
126
127     AcceptFollowCommunity::send(self, context, request_counter).await
128   }
129 }