2 activities::{community::announce::GetCommunity, verify_person_in_community},
3 objects::community::ApubCommunity,
6 block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
9 announce::AnnounceActivity,
10 remove_mod::RemoveMod,
12 update::UpdateCommunity,
15 comment::CreateOrUpdateComment,
16 post::CreateOrUpdatePost,
17 private_message::CreateOrUpdatePrivateMessage,
19 deletion::{delete::Delete, delete_user::DeleteUser, undo_delete::UndoDelete},
21 accept::AcceptFollowCommunity,
22 follow::FollowCommunity,
23 undo_follow::UndoFollowCommunity,
25 voting::{undo_vote::UndoVote, vote::Vote},
31 use activitypub_federation::{
32 core::object_id::ObjectId,
34 deser::context::WithContext,
35 traits::{activity_handler, ActivityHandler},
37 use lemmy_utils::error::LemmyError;
38 use lemmy_websocket::LemmyContext;
39 use serde::{Deserialize, Serialize};
42 #[derive(Debug, Deserialize, Serialize)]
44 #[activity_handler(LemmyContext, LemmyError)]
45 pub enum SharedInboxActivities {
46 GroupInboxActivities(Box<WithContext<GroupInboxActivities>>),
47 // Note, pm activities need to be at the end, otherwise comments will end up here. We can probably
48 // avoid this problem by replacing createpm.object with our own struct, instead of NoteExt.
49 PersonInboxActivities(Box<WithContext<PersonInboxActivities>>),
52 #[derive(Debug, Deserialize, Serialize)]
54 pub enum GroupInboxActivities {
55 FollowCommunity(FollowCommunity),
56 UndoFollowCommunity(UndoFollowCommunity),
57 AnnouncableActivities(Box<AnnouncableActivities>),
61 #[derive(Clone, Debug, Deserialize, Serialize)]
63 #[activity_handler(LemmyContext, LemmyError)]
64 pub enum PersonInboxActivities {
65 AcceptFollowCommunity(AcceptFollowCommunity),
66 /// Some activities can also be sent from user to user, eg a comment with mentions
67 AnnouncableActivities(AnnouncableActivities),
68 CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
70 UndoDelete(UndoDelete),
71 AnnounceActivity(AnnounceActivity),
74 #[derive(Clone, Debug, Deserialize, Serialize)]
76 #[activity_handler(LemmyContext, LemmyError)]
77 pub enum AnnouncableActivities {
78 CreateOrUpdateComment(CreateOrUpdateComment),
79 CreateOrUpdatePost(Box<CreateOrUpdatePost>),
83 UndoDelete(UndoDelete),
84 UpdateCommunity(UpdateCommunity),
86 UndoBlockUser(UndoBlockUser),
89 // For compatibility with Pleroma/Mastodon (send only)
93 #[derive(Clone, Debug, Deserialize, Serialize)]
95 #[activity_handler(LemmyContext, LemmyError)]
96 #[allow(clippy::enum_variant_names)]
97 pub enum SiteInboxActivities {
99 UndoBlockUser(UndoBlockUser),
100 DeleteUser(DeleteUser),
103 #[async_trait::async_trait(?Send)]
104 impl GetCommunity for AnnouncableActivities {
105 #[tracing::instrument(skip(self, context))]
106 async fn get_community(
108 context: &LemmyContext,
109 request_counter: &mut i32,
110 ) -> Result<ApubCommunity, LemmyError> {
111 use AnnouncableActivities::*;
112 let community = match self {
113 CreateOrUpdateComment(a) => a.get_community(context, request_counter).await?,
114 CreateOrUpdatePost(a) => a.get_community(context, request_counter).await?,
115 Vote(a) => a.get_community(context, request_counter).await?,
116 UndoVote(a) => a.get_community(context, request_counter).await?,
117 Delete(a) => a.get_community(context, request_counter).await?,
118 UndoDelete(a) => a.get_community(context, request_counter).await?,
119 UpdateCommunity(a) => a.get_community(context, request_counter).await?,
120 BlockUser(a) => a.get_community(context, request_counter).await?,
121 UndoBlockUser(a) => a.get_community(context, request_counter).await?,
122 AddMod(a) => a.get_community(context, request_counter).await?,
123 RemoveMod(a) => a.get_community(context, request_counter).await?,
124 Page(_) => unimplemented!(),
130 impl Id for AnnouncableActivities {
131 fn object_id(&self) -> &Url {
132 ActivityHandler::id(self)
136 // Need to implement this manually to announce matching activities
137 #[async_trait::async_trait(?Send)]
138 impl ActivityHandler for GroupInboxActivities {
139 type DataType = LemmyContext;
140 type Error = LemmyError;
142 fn id(&self) -> &Url {
144 GroupInboxActivities::FollowCommunity(a) => a.id(),
145 GroupInboxActivities::UndoFollowCommunity(a) => a.id(),
146 GroupInboxActivities::AnnouncableActivities(a) => a.object_id(),
147 GroupInboxActivities::Report(a) => a.id(),
151 fn actor(&self) -> &Url {
153 GroupInboxActivities::FollowCommunity(a) => a.actor(),
154 GroupInboxActivities::UndoFollowCommunity(a) => a.actor(),
155 GroupInboxActivities::AnnouncableActivities(a) => a.actor(),
156 GroupInboxActivities::Report(a) => a.actor(),
162 data: &Data<Self::DataType>,
163 request_counter: &mut i32,
164 ) -> Result<(), LemmyError> {
166 GroupInboxActivities::FollowCommunity(a) => a.verify(data, request_counter).await,
167 GroupInboxActivities::UndoFollowCommunity(a) => a.verify(data, request_counter).await,
168 GroupInboxActivities::AnnouncableActivities(a) => a.verify(data, request_counter).await,
169 GroupInboxActivities::Report(a) => a.verify(data, request_counter).await,
175 data: &Data<Self::DataType>,
176 request_counter: &mut i32,
177 ) -> Result<(), LemmyError> {
179 GroupInboxActivities::FollowCommunity(a) => a.receive(data, request_counter).await,
180 GroupInboxActivities::UndoFollowCommunity(a) => a.receive(data, request_counter).await,
181 GroupInboxActivities::AnnouncableActivities(activity) => {
182 activity.clone().receive(data, request_counter).await?;
184 // Ignore failures in get_community(). those happen because Delete/PrivateMessage is not in a
185 // community, but looks identical to Delete/Post or Delete/Comment which are in a community.
186 let community = activity.get_community(data, &mut 0).await;
187 if let Ok(community) = community {
189 let actor_id = ObjectId::new(activity.actor().clone());
190 verify_person_in_community(&actor_id, &community, data, &mut 0).await?;
191 AnnounceActivity::send(*activity, &community, data).await?;
196 GroupInboxActivities::Report(a) => a.receive(data, request_counter).await,
204 activity_lists::{GroupInboxActivities, PersonInboxActivities, SiteInboxActivities},
205 protocol::tests::test_parse_lemmy_item,
209 fn test_group_inbox() {
210 test_parse_lemmy_item::<GroupInboxActivities>("assets/lemmy/activities/following/follow.json")
212 test_parse_lemmy_item::<GroupInboxActivities>(
213 "assets/lemmy/activities/create_or_update/create_note.json",
219 fn test_person_inbox() {
220 test_parse_lemmy_item::<PersonInboxActivities>("assets/lemmy/activities/following/accept.json")
222 test_parse_lemmy_item::<PersonInboxActivities>(
223 "assets/lemmy/activities/create_or_update/create_note.json",
226 test_parse_lemmy_item::<PersonInboxActivities>(
227 "assets/lemmy/activities/create_or_update/create_private_message.json",
233 fn test_site_inbox() {
234 test_parse_lemmy_item::<SiteInboxActivities>(
235 "assets/lemmy/activities/deletion/delete_user.json",