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