]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/following/undo.rs
Merge branch 'remove_settings_and_secret_singletons_squashed'
[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::object_id::ObjectId,
11   ActorType,
12 };
13 use activitystreams::{
14   activity::kind::UndoType,
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 UndoFollowCommunity {
34   actor: ObjectId<Person>,
35   to: ObjectId<Community>,
36   object: FollowCommunity,
37   #[serde(rename = "type")]
38   kind: UndoType,
39   id: Url,
40   #[serde(rename = "@context")]
41   context: OneOrMany<AnyBase>,
42   #[serde(flatten)]
43   unparsed: Unparsed,
44 }
45
46 impl UndoFollowCommunity {
47   pub async fn send(
48     actor: &Person,
49     community: &Community,
50     context: &LemmyContext,
51   ) -> Result<(), LemmyError> {
52     let object = FollowCommunity::new(actor, community, context)?;
53     let undo = UndoFollowCommunity {
54       actor: ObjectId::new(actor.actor_id()),
55       to: ObjectId::new(community.actor_id()),
56       object,
57       kind: UndoType::Undo,
58       id: generate_activity_id(
59         UndoType::Undo,
60         &context.settings().get_protocol_and_hostname(),
61       )?,
62       context: lemmy_context(),
63       unparsed: Default::default(),
64     };
65     let inbox = vec![community.get_shared_inbox_or_inbox_url()];
66     send_activity_new(context, &undo, &undo.id, actor, inbox, true).await
67   }
68 }
69
70 #[async_trait::async_trait(?Send)]
71 impl ActivityHandler for UndoFollowCommunity {
72   async fn verify(
73     &self,
74     context: &LemmyContext,
75     request_counter: &mut i32,
76   ) -> Result<(), LemmyError> {
77     verify_activity(self, &context.settings())?;
78     verify_urls_match(self.to.inner(), self.object.object.inner())?;
79     verify_urls_match(self.actor(), self.object.actor())?;
80     verify_person(&self.actor, context, request_counter).await?;
81     self.object.verify(context, request_counter).await?;
82     Ok(())
83   }
84
85   async fn receive(
86     self,
87     context: &LemmyContext,
88     request_counter: &mut i32,
89   ) -> Result<(), LemmyError> {
90     let actor = self.actor.dereference(context, request_counter).await?;
91     let community = self.to.dereference(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 }