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