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