]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/following/follow.rs
7e0640a1b021365b1e9b3d5a98108965335554a3
[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   send_lemmy_activity,
11 };
12 use activitystreams::{
13   activity::kind::FollowType,
14   base::AnyBase,
15   primitives::OneOrMany,
16   unparsed::Unparsed,
17 };
18 use lemmy_api_common::blocking;
19 use lemmy_apub_lib::{
20   data::Data,
21   traits::{ActivityFields, ActivityHandler, ActorType},
22   verify::verify_urls_match,
23 };
24 use lemmy_db_queries::Followable;
25 use lemmy_db_schema::source::{
26   community::{Community, CommunityFollower, CommunityFollowerForm},
27   person::Person,
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<Person>,
38   // TODO: is there any reason to put the same community id twice, in to and object?
39   pub(in crate::activities::following) to: ObjectId<Community>,
40   pub(in crate::activities::following) object: ObjectId<Community>,
41   #[serde(rename = "type")]
42   kind: FollowType,
43   id: Url,
44   #[serde(rename = "@context")]
45   context: OneOrMany<AnyBase>,
46   #[serde(flatten)]
47   unparsed: Unparsed,
48 }
49
50 impl FollowCommunity {
51   pub(in crate::activities::following) fn new(
52     actor: &Person,
53     community: &Community,
54     context: &LemmyContext,
55   ) -> Result<FollowCommunity, LemmyError> {
56     Ok(FollowCommunity {
57       actor: ObjectId::new(actor.actor_id()),
58       to: ObjectId::new(community.actor_id()),
59       object: ObjectId::new(community.actor_id()),
60       kind: FollowType::Follow,
61       id: generate_activity_id(
62         FollowType::Follow,
63         &context.settings().get_protocol_and_hostname(),
64       )?,
65       context: lemmy_context(),
66       unparsed: Default::default(),
67     })
68   }
69   pub async fn send(
70     actor: &Person,
71     community: &Community,
72     context: &LemmyContext,
73   ) -> Result<(), LemmyError> {
74     let community_follower_form = CommunityFollowerForm {
75       community_id: community.id,
76       person_id: actor.id,
77       pending: true,
78     };
79     blocking(context.pool(), move |conn| {
80       CommunityFollower::follow(conn, &community_follower_form).ok()
81     })
82     .await?;
83
84     let follow = FollowCommunity::new(actor, community, context)?;
85     let inbox = vec![community.inbox_url.clone().into()];
86     send_lemmy_activity(context, &follow, &follow.id, actor, inbox, true).await
87   }
88 }
89
90 #[async_trait::async_trait(?Send)]
91 impl ActivityHandler for FollowCommunity {
92   type DataType = LemmyContext;
93   async fn verify(
94     &self,
95     context: &Data<LemmyContext>,
96     request_counter: &mut i32,
97   ) -> Result<(), LemmyError> {
98     verify_activity(self, &context.settings())?;
99     verify_urls_match(self.to.inner(), self.object.inner())?;
100     verify_person(&self.actor, context, request_counter).await?;
101     Ok(())
102   }
103
104   async fn receive(
105     self,
106     context: &Data<LemmyContext>,
107     request_counter: &mut i32,
108   ) -> Result<(), LemmyError> {
109     let actor = self.actor.dereference(context, request_counter).await?;
110     let community = self.object.dereference(context, request_counter).await?;
111     let community_follower_form = CommunityFollowerForm {
112       community_id: community.id,
113       person_id: actor.id,
114       pending: false,
115     };
116
117     // This will fail if they're already a follower, but ignore the error.
118     blocking(context.pool(), move |conn| {
119       CommunityFollower::follow(conn, &community_follower_form).ok()
120     })
121     .await?;
122
123     AcceptFollowCommunity::send(self, context, request_counter).await
124   }
125 }