]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/community/announce.rs
6165eb2f00d212b0a09d116ef5f0cec7d029b818
[lemmy.git] / crates / apub / src / activities / community / announce.rs
1 use crate::{
2   activities::{
3     comment::create_or_update::CreateOrUpdateComment,
4     community::{
5       add_mod::AddMod,
6       block_user::BlockUserFromCommunity,
7       list_community_follower_inboxes,
8       remove_mod::RemoveMod,
9       undo_block_user::UndoBlockUserFromCommunity,
10       update::UpdateCommunity,
11     },
12     deletion::{delete::Delete, undo_delete::UndoDelete},
13     generate_activity_id,
14     post::create_or_update::CreateOrUpdatePost,
15     verify_activity,
16     verify_is_public,
17     voting::{undo_vote::UndoVote, vote::Vote},
18   },
19   context::lemmy_context,
20   fetcher::object_id::ObjectId,
21   http::is_activity_already_known,
22   insert_activity,
23   objects::community::ApubCommunity,
24   send_lemmy_activity,
25 };
26 use activitystreams::{
27   activity::kind::AnnounceType,
28   base::AnyBase,
29   primitives::OneOrMany,
30   public,
31   unparsed::Unparsed,
32 };
33 use lemmy_apub_lib::{
34   data::Data,
35   traits::{ActivityFields, ActivityHandler, ActorType},
36   verify::verify_urls_match,
37 };
38 use lemmy_utils::LemmyError;
39 use lemmy_websocket::LemmyContext;
40 use serde::{Deserialize, Serialize};
41 use url::Url;
42
43 #[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
44 #[serde(untagged)]
45 #[activity_handler(LemmyContext)]
46 pub enum AnnouncableActivities {
47   CreateOrUpdateComment(CreateOrUpdateComment),
48   CreateOrUpdatePost(Box<CreateOrUpdatePost>),
49   Vote(Vote),
50   UndoVote(UndoVote),
51   Delete(Delete),
52   UndoDelete(UndoDelete),
53   UpdateCommunity(Box<UpdateCommunity>),
54   BlockUserFromCommunity(BlockUserFromCommunity),
55   UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
56   AddMod(AddMod),
57   RemoveMod(RemoveMod),
58 }
59
60 #[async_trait::async_trait(?Send)]
61 pub(crate) trait GetCommunity {
62   async fn get_community(
63     &self,
64     context: &LemmyContext,
65     request_counter: &mut i32,
66   ) -> Result<ApubCommunity, LemmyError>;
67 }
68
69 #[async_trait::async_trait(?Send)]
70 impl GetCommunity for AnnouncableActivities {
71   async fn get_community(
72     &self,
73     context: &LemmyContext,
74     request_counter: &mut i32,
75   ) -> Result<ApubCommunity, LemmyError> {
76     use AnnouncableActivities::*;
77     let community = match self {
78       CreateOrUpdateComment(a) => a.get_community(context, request_counter).await?,
79       CreateOrUpdatePost(a) => a.get_community(context, request_counter).await?,
80       Vote(a) => a.get_community(context, request_counter).await?,
81       UndoVote(a) => a.get_community(context, request_counter).await?,
82       Delete(a) => a.get_community(context, request_counter).await?,
83       UndoDelete(a) => a.get_community(context, request_counter).await?,
84       UpdateCommunity(a) => a.get_community(context, request_counter).await?,
85       BlockUserFromCommunity(a) => a.get_community(context, request_counter).await?,
86       UndoBlockUserFromCommunity(a) => a.get_community(context, request_counter).await?,
87       AddMod(a) => a.get_community(context, request_counter).await?,
88       RemoveMod(a) => a.get_community(context, request_counter).await?,
89     };
90     verify_urls_match(self.actor(), &community.actor_id())?;
91     Ok(community)
92   }
93 }
94
95 #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
96 #[serde(rename_all = "camelCase")]
97 pub struct AnnounceActivity {
98   actor: ObjectId<ApubCommunity>,
99   to: Vec<Url>,
100   object: AnnouncableActivities,
101   cc: Vec<Url>,
102   #[serde(rename = "type")]
103   kind: AnnounceType,
104   id: Url,
105   #[serde(rename = "@context")]
106   context: OneOrMany<AnyBase>,
107   #[serde(flatten)]
108   unparsed: Unparsed,
109 }
110
111 impl AnnounceActivity {
112   pub async fn send(
113     object: AnnouncableActivities,
114     community: &ApubCommunity,
115     additional_inboxes: Vec<Url>,
116     context: &LemmyContext,
117   ) -> Result<(), LemmyError> {
118     let announce = AnnounceActivity {
119       actor: ObjectId::new(community.actor_id()),
120       to: vec![public()],
121       object,
122       cc: vec![community.followers_url.clone().into_inner()],
123       kind: AnnounceType::Announce,
124       id: generate_activity_id(
125         &AnnounceType::Announce,
126         &context.settings().get_protocol_and_hostname(),
127       )?,
128       context: lemmy_context(),
129       unparsed: Default::default(),
130     };
131     let inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?;
132     send_lemmy_activity(context, &announce, &announce.id, community, inboxes, false).await
133   }
134 }
135
136 #[async_trait::async_trait(?Send)]
137 impl ActivityHandler for AnnounceActivity {
138   type DataType = LemmyContext;
139   async fn verify(
140     &self,
141     context: &Data<LemmyContext>,
142     request_counter: &mut i32,
143   ) -> Result<(), LemmyError> {
144     verify_is_public(&self.to)?;
145     verify_activity(self, &context.settings())?;
146     self.object.verify(context, request_counter).await?;
147     Ok(())
148   }
149
150   async fn receive(
151     self,
152     context: &Data<LemmyContext>,
153     request_counter: &mut i32,
154   ) -> Result<(), LemmyError> {
155     if is_activity_already_known(context.pool(), self.object.id_unchecked()).await? {
156       return Ok(());
157     }
158     insert_activity(
159       self.object.id_unchecked(),
160       self.object.clone(),
161       false,
162       true,
163       context.pool(),
164     )
165     .await?;
166     self.object.receive(context, request_counter).await
167   }
168 }