]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/following/accept.rs
Add `to` field in follow activities for better compatibility (#2829)
[lemmy.git] / crates / apub / src / activities / following / accept.rs
1 use crate::{
2   activities::{generate_activity_id, send_lemmy_activity},
3   insert_activity,
4   protocol::activities::following::{accept::AcceptFollow, follow::Follow},
5 };
6 use activitypub_federation::{
7   config::Data,
8   kinds::activity::AcceptType,
9   protocol::verification::verify_urls_match,
10   traits::{ActivityHandler, Actor},
11 };
12 use lemmy_api_common::{
13   community::CommunityResponse,
14   context::LemmyContext,
15   websocket::{
16     handlers::messages::SendUserRoomMessage,
17     serialize_websocket_message,
18     UserOperation,
19   },
20 };
21 use lemmy_db_schema::{
22   source::{actor_language::CommunityLanguage, community::CommunityFollower},
23   traits::Followable,
24 };
25 use lemmy_db_views::structs::LocalUserView;
26 use lemmy_db_views_actor::structs::CommunityView;
27 use lemmy_utils::error::LemmyError;
28 use url::Url;
29
30 impl AcceptFollow {
31   #[tracing::instrument(skip_all)]
32   pub async fn send(follow: Follow, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
33     let user_or_community = follow.object.dereference_local(context).await?;
34     let person = follow.actor.clone().dereference(context).await?;
35     let accept = AcceptFollow {
36       actor: user_or_community.id().into(),
37       to: Some([person.id().into()]),
38       object: follow,
39       kind: AcceptType::Accept,
40       id: generate_activity_id(
41         AcceptType::Accept,
42         &context.settings().get_protocol_and_hostname(),
43       )?,
44     };
45     let inbox = vec![person.shared_inbox_or_inbox()];
46     send_lemmy_activity(context, accept, &user_or_community, inbox, true).await
47   }
48 }
49
50 /// Handle accepted follows
51 #[async_trait::async_trait]
52 impl ActivityHandler for AcceptFollow {
53   type DataType = LemmyContext;
54   type Error = LemmyError;
55
56   fn id(&self) -> &Url {
57     &self.id
58   }
59
60   fn actor(&self) -> &Url {
61     self.actor.inner()
62   }
63
64   #[tracing::instrument(skip_all)]
65   async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
66     verify_urls_match(self.actor.inner(), self.object.object.inner())?;
67     self.object.verify(context).await?;
68     if let Some(to) = &self.to {
69       verify_urls_match(to[0].inner(), self.object.actor.inner())?;
70     }
71     Ok(())
72   }
73
74   #[tracing::instrument(skip_all)]
75   async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
76     insert_activity(&self.id, &self, false, true, context).await?;
77     let community = self.actor.dereference(context).await?;
78     let person = self.object.actor.dereference(context).await?;
79     // This will throw an error if no follow was requested
80     let community_id = community.id;
81     let person_id = person.id;
82     CommunityFollower::follow_accepted(context.pool(), community_id, person_id).await?;
83
84     // Send the Subscribed message over websocket
85     // Re-read the community_view to get the new SubscribedType
86     let community_view =
87       CommunityView::read(context.pool(), community_id, Some(person_id), None).await?;
88
89     // Get the local_user_id
90     let local_recipient_id = LocalUserView::read_person(context.pool(), person_id)
91       .await?
92       .local_user
93       .id;
94     let discussion_languages = CommunityLanguage::read(context.pool(), community_id).await?;
95
96     let res = CommunityResponse {
97       community_view,
98       discussion_languages,
99     };
100
101     let message = serialize_websocket_message(&UserOperation::FollowCommunity, &res)?;
102
103     context.chat_server().do_send(SendUserRoomMessage {
104       recipient_id: local_recipient_id,
105       message,
106       websocket_id: None,
107     });
108
109     Ok(())
110   }
111 }