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