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