]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/following/follow.rs
Merge pull request #1897 from LemmyNet/mastodon-compat
[lemmy.git] / crates / apub / src / activities / following / follow.rs
1 use crate::{
2   activities::{
3     generate_activity_id,
4     send_lemmy_activity,
5     verify_activity,
6     verify_person,
7     verify_person_in_community,
8   },
9   objects::{community::ApubCommunity, person::ApubPerson},
10   protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
11 };
12 use activitystreams::activity::kind::FollowType;
13 use lemmy_api_common::blocking;
14 use lemmy_apub_lib::{
15   data::Data,
16   object_id::ObjectId,
17   traits::{ActivityHandler, ActorType},
18 };
19 use lemmy_db_schema::{
20   source::community::{CommunityFollower, CommunityFollowerForm},
21   traits::Followable,
22 };
23 use lemmy_utils::LemmyError;
24 use lemmy_websocket::LemmyContext;
25
26 impl FollowCommunity {
27   pub(in crate::activities::following) fn new(
28     actor: &ApubPerson,
29     community: &ApubCommunity,
30     context: &LemmyContext,
31   ) -> Result<FollowCommunity, LemmyError> {
32     Ok(FollowCommunity {
33       actor: ObjectId::new(actor.actor_id()),
34       object: ObjectId::new(community.actor_id()),
35       kind: FollowType::Follow,
36       id: generate_activity_id(
37         FollowType::Follow,
38         &context.settings().get_protocol_and_hostname(),
39       )?,
40       unparsed: Default::default(),
41     })
42   }
43   pub async fn send(
44     actor: &ApubPerson,
45     community: &ApubCommunity,
46     context: &LemmyContext,
47   ) -> Result<(), LemmyError> {
48     let community_follower_form = CommunityFollowerForm {
49       community_id: community.id,
50       person_id: actor.id,
51       pending: true,
52     };
53     blocking(context.pool(), move |conn| {
54       CommunityFollower::follow(conn, &community_follower_form).ok()
55     })
56     .await?;
57
58     let follow = FollowCommunity::new(actor, community, context)?;
59     let inbox = vec![community.inbox_url.clone().into()];
60     send_lemmy_activity(context, &follow, &follow.id, actor, inbox, true).await
61   }
62 }
63
64 #[async_trait::async_trait(?Send)]
65 impl ActivityHandler for FollowCommunity {
66   type DataType = LemmyContext;
67   async fn verify(
68     &self,
69     context: &Data<LemmyContext>,
70     request_counter: &mut i32,
71   ) -> Result<(), LemmyError> {
72     verify_activity(&self.id, self.actor.inner(), &context.settings())?;
73     verify_person(&self.actor, context, request_counter).await?;
74     let community = self.object.dereference(context, request_counter).await?;
75     verify_person_in_community(&self.actor, &community, context, request_counter).await?;
76     Ok(())
77   }
78
79   async fn receive(
80     self,
81     context: &Data<LemmyContext>,
82     request_counter: &mut i32,
83   ) -> Result<(), LemmyError> {
84     let person = self.actor.dereference(context, request_counter).await?;
85     let community = self.object.dereference(context, request_counter).await?;
86     let community_follower_form = CommunityFollowerForm {
87       community_id: community.id,
88       person_id: person.id,
89       pending: false,
90     };
91
92     // This will fail if they're already a follower, but ignore the error.
93     blocking(context.pool(), move |conn| {
94       CommunityFollower::follow(conn, &community_follower_form).ok()
95     })
96     .await?;
97
98     AcceptFollowCommunity::send(self, context, request_counter).await
99   }
100 }