]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/following/follow.rs
Rewrite remaining activities (#1712)
[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   activity_queue::send_activity_new,
9   extensions::context::lemmy_context,
10   fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
11   ActorType,
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::{verify_urls_match, ActivityFields, ActivityHandler};
21 use lemmy_db_queries::Followable;
22 use lemmy_db_schema::source::{
23   community::{Community, CommunityFollower, CommunityFollowerForm},
24   person::Person,
25 };
26 use lemmy_utils::LemmyError;
27 use lemmy_websocket::LemmyContext;
28 use serde::{Deserialize, Serialize};
29 use url::Url;
30
31 #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
32 #[serde(rename_all = "camelCase")]
33 pub struct FollowCommunity {
34   actor: Url,
35   pub(in crate::activities::following) to: Url,
36   pub(in crate::activities::following) object: Url,
37   #[serde(rename = "type")]
38   kind: FollowType,
39   id: Url,
40   #[serde(rename = "@context")]
41   context: OneOrMany<AnyBase>,
42   #[serde(flatten)]
43   unparsed: Unparsed,
44 }
45
46 impl FollowCommunity {
47   pub(in crate::activities::following) fn new(
48     actor: &Person,
49     community: &Community,
50   ) -> Result<FollowCommunity, LemmyError> {
51     Ok(FollowCommunity {
52       actor: actor.actor_id(),
53       to: community.actor_id(),
54       object: community.actor_id(),
55       kind: FollowType::Follow,
56       id: generate_activity_id(FollowType::Follow)?,
57       context: lemmy_context(),
58       unparsed: Default::default(),
59     })
60   }
61   pub async fn send(
62     actor: &Person,
63     community: &Community,
64     context: &LemmyContext,
65   ) -> Result<(), LemmyError> {
66     let community_follower_form = CommunityFollowerForm {
67       community_id: community.id,
68       person_id: actor.id,
69       pending: true,
70     };
71     blocking(context.pool(), move |conn| {
72       CommunityFollower::follow(conn, &community_follower_form).ok()
73     })
74     .await?;
75
76     let follow = FollowCommunity::new(actor, community)?;
77     let inbox = vec![community.inbox_url.clone().into()];
78     send_activity_new(context, &follow, &follow.id, actor, inbox, true).await
79   }
80 }
81
82 #[async_trait::async_trait(?Send)]
83 impl ActivityHandler for FollowCommunity {
84   async fn verify(
85     &self,
86     context: &LemmyContext,
87     request_counter: &mut i32,
88   ) -> Result<(), LemmyError> {
89     verify_activity(self)?;
90     verify_urls_match(&self.to, &self.object)?;
91     verify_person(&self.actor, context, request_counter).await?;
92     Ok(())
93   }
94
95   async fn receive(
96     self,
97     context: &LemmyContext,
98     request_counter: &mut i32,
99   ) -> Result<(), LemmyError> {
100     let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
101     let community =
102       get_or_fetch_and_upsert_community(&self.object, context, request_counter).await?;
103     let community_follower_form = CommunityFollowerForm {
104       community_id: community.id,
105       person_id: actor.id,
106       pending: false,
107     };
108
109     // This will fail if they're already a follower, but ignore the error.
110     blocking(context.pool(), move |conn| {
111       CommunityFollower::follow(conn, &community_follower_form).ok()
112     })
113     .await?;
114
115     AcceptFollowCommunity::send(self, context).await
116   }
117 }