]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/following/undo.rs
Rewrite activitypub following, person, community, pm (#1692)
[lemmy.git] / crates / apub / src / activities / following / undo.rs
1 use crate::{
2   activities::{
3     following::follow::FollowCommunity,
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, UndoType};
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 UndoFollowCommunity {
28   to: Url,
29   object: FollowCommunity,
30   #[serde(rename = "type")]
31   kind: UndoType,
32   #[serde(flatten)]
33   common: ActivityCommonFields,
34 }
35
36 impl UndoFollowCommunity {
37   pub async fn send(
38     actor: &Person,
39     community: &Community,
40     context: &LemmyContext,
41   ) -> Result<(), LemmyError> {
42     let id = generate_activity_id(UndoType::Undo)?;
43     let undo = UndoFollowCommunity {
44       to: community.actor_id(),
45       object: FollowCommunity {
46         to: community.actor_id(),
47         object: community.actor_id(),
48         kind: FollowType::Follow,
49         common: ActivityCommonFields {
50           context: lemmy_context(),
51           id: generate_activity_id(FollowType::Follow)?,
52           actor: actor.actor_id(),
53           unparsed: Default::default(),
54         },
55       },
56       kind: UndoType::Undo,
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.get_shared_inbox_or_inbox_url()];
65     send_activity_new(context, &undo, &id, actor, inbox, true).await
66   }
67 }
68
69 #[async_trait::async_trait(?Send)]
70 impl ActivityHandler for UndoFollowCommunity {
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.object)?;
78     verify_urls_match(&self.common.actor, &self.object.common.actor)?;
79     verify_person(&self.common.actor, context, request_counter).await?;
80     self.object.verify(context, request_counter).await?;
81     Ok(())
82   }
83
84   async fn receive(
85     self,
86     context: &LemmyContext,
87     request_counter: &mut i32,
88   ) -> Result<(), LemmyError> {
89     let actor =
90       get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
91     let community = get_or_fetch_and_upsert_community(&self.to, context, request_counter).await?;
92
93     let community_follower_form = CommunityFollowerForm {
94       community_id: community.id,
95       person_id: actor.id,
96       pending: false,
97     };
98
99     // This will fail if they aren't a follower, but ignore the error.
100     blocking(context.pool(), move |conn| {
101       CommunityFollower::unfollow(conn, &community_follower_form).ok()
102     })
103     .await?;
104     Ok(())
105   }
106
107   fn common(&self) -> &ActivityCommonFields {
108     &self.common
109   }
110 }