From 03d8ac75efa9badfb476a54d026c5511e028b2b6 Mon Sep 17 00:00:00 2001
From: Felix Ableitner <me@nutomic.com>
Date: Fri, 29 Oct 2021 12:32:42 +0200
Subject: [PATCH] Move activity structs to protocol folder

---
 crates/api/src/comment.rs                     |  11 +-
 crates/api/src/comment_report.rs              |   6 +-
 crates/api/src/community.rs                   |   6 +-
 crates/api/src/post.rs                        |   8 +-
 crates/api/src/post_report.rs                 |   6 +-
 crates/api_crud/src/comment/create.rs         |  12 +-
 crates/api_crud/src/comment/update.rs         |   8 +-
 crates/api_crud/src/community/update.rs       |   2 +-
 crates/api_crud/src/post/create.rs            |  16 +--
 crates/api_crud/src/post/update.rs            |   9 +-
 crates/api_crud/src/private_message/create.rs |   4 +-
 crates/api_crud/src/private_message/delete.rs |   2 +-
 crates/api_crud/src/private_message/update.rs |   2 +-
 .../activities/comment/create_or_update.rs    |  31 +----
 .../apub/src/activities/community/add_mod.rs  |  29 +----
 .../apub/src/activities/community/announce.rs |  76 +-----------
 .../src/activities/community/block_user.rs    |  28 +----
 crates/apub/src/activities/community/mod.rs   |  20 ++--
 .../src/activities/community/remove_mod.rs    |  29 +----
 .../src/activities/{ => community}/report.rs  |  43 +++----
 .../activities/community/undo_block_user.rs   |  31 ++---
 .../apub/src/activities/community/update.rs   |  53 +++-----
 crates/apub/src/activities/deletion/delete.rs |  71 ++++-------
 crates/apub/src/activities/deletion/mod.rs    |  19 ++-
 .../src/activities/deletion/undo_delete.rs    |  55 ++++-----
 .../apub/src/activities/following/accept.rs   |  26 +---
 .../apub/src/activities/following/follow.rs   |  21 +---
 crates/apub/src/activities/following/mod.rs   |   2 +-
 .../following/{undo.rs => undo_follow.rs}     |  26 +---
 crates/apub/src/activities/mod.rs             |  10 +-
 .../src/activities/post/create_or_update.rs   |  27 +----
 .../private_message/create_or_update.rs       |  31 +----
 .../src/activities/private_message/delete.rs  |  20 +---
 .../activities/private_message/undo_delete.rs |  29 +----
 crates/apub/src/activities/voting/mod.rs      |   9 +-
 .../apub/src/activities/voting/undo_vote.rs   |  57 ++++-----
 crates/apub/src/activities/voting/vote.rs     |  83 ++++---------
 crates/apub/src/activity_lists.rs             | 113 ++++++++++++++++++
 .../apub/src/collections/community_outbox.rs  |  20 ++--
 crates/apub/src/http/community.rs             |  47 +++-----
 crates/apub/src/http/mod.rs                   |  16 +--
 crates/apub/src/http/person.rs                |  41 ++-----
 crates/apub/src/lib.rs                        |   3 +-
 .../protocol/activities/community/add_mod.rs  |  20 ++++
 .../protocol/activities/community/announce.rs |  23 ++++
 .../activities/community/block_user.rs        |  23 ++++
 .../src/protocol/activities/community/mod.rs  |   7 ++
 .../activities/community/remove_mod.rs        |  20 ++++
 .../protocol/activities/community/report.rs   |  22 ++++
 .../activities/community/undo_block_user.rs   |  23 ++++
 .../protocol/activities/community/update.rs   |  26 ++++
 .../activities/create_or_update/comment.rs    |  25 ++++
 .../activities/create_or_update/mod.rs        |   2 +
 .../activities/create_or_update/post.rs       |  23 ++++
 .../protocol/activities/deletion/delete.rs    |  24 ++++
 .../src/protocol/activities/deletion/mod.rs   |   2 +
 .../activities/deletion/undo_delete.rs        |  25 ++++
 .../protocol/activities/following/accept.rs   |  22 ++++
 .../protocol/activities/following/follow.rs   |  21 ++++
 .../src/protocol/activities/following/mod.rs  |   3 +
 .../activities/following/undo_follow.rs       |  22 ++++
 crates/apub/src/protocol/activities/mod.rs    |  15 +++
 .../private_message/create_or_update.rs       |  22 ++++
 .../activities/private_message/delete.rs      |  21 ++++
 .../activities/private_message/mod.rs         |   3 +
 .../activities/private_message/undo_delete.rs |  22 ++++
 .../src/protocol/activities/voting/mod.rs     |   2 +
 .../protocol/activities/voting/undo_vote.rs   |  25 ++++
 .../src/protocol/activities/voting/vote.rs    |  53 ++++++++
 .../src/protocol/collections/group_outbox.rs  |   2 +-
 crates/apub/src/protocol/mod.rs               |   1 +
 71 files changed, 908 insertions(+), 749 deletions(-)
 rename crates/apub/src/activities/{ => community}/report.rs (88%)
 rename crates/apub/src/activities/following/{undo.rs => undo_follow.rs} (79%)
 create mode 100644 crates/apub/src/activity_lists.rs
 create mode 100644 crates/apub/src/protocol/activities/community/add_mod.rs
 create mode 100644 crates/apub/src/protocol/activities/community/announce.rs
 create mode 100644 crates/apub/src/protocol/activities/community/block_user.rs
 create mode 100644 crates/apub/src/protocol/activities/community/mod.rs
 create mode 100644 crates/apub/src/protocol/activities/community/remove_mod.rs
 create mode 100644 crates/apub/src/protocol/activities/community/report.rs
 create mode 100644 crates/apub/src/protocol/activities/community/undo_block_user.rs
 create mode 100644 crates/apub/src/protocol/activities/community/update.rs
 create mode 100644 crates/apub/src/protocol/activities/create_or_update/comment.rs
 create mode 100644 crates/apub/src/protocol/activities/create_or_update/mod.rs
 create mode 100644 crates/apub/src/protocol/activities/create_or_update/post.rs
 create mode 100644 crates/apub/src/protocol/activities/deletion/delete.rs
 create mode 100644 crates/apub/src/protocol/activities/deletion/mod.rs
 create mode 100644 crates/apub/src/protocol/activities/deletion/undo_delete.rs
 create mode 100644 crates/apub/src/protocol/activities/following/accept.rs
 create mode 100644 crates/apub/src/protocol/activities/following/follow.rs
 create mode 100644 crates/apub/src/protocol/activities/following/mod.rs
 create mode 100644 crates/apub/src/protocol/activities/following/undo_follow.rs
 create mode 100644 crates/apub/src/protocol/activities/mod.rs
 create mode 100644 crates/apub/src/protocol/activities/private_message/create_or_update.rs
 create mode 100644 crates/apub/src/protocol/activities/private_message/delete.rs
 create mode 100644 crates/apub/src/protocol/activities/private_message/mod.rs
 create mode 100644 crates/apub/src/protocol/activities/private_message/undo_delete.rs
 create mode 100644 crates/apub/src/protocol/activities/voting/mod.rs
 create mode 100644 crates/apub/src/protocol/activities/voting/undo_vote.rs
 create mode 100644 crates/apub/src/protocol/activities/voting/vote.rs

diff --git a/crates/api/src/comment.rs b/crates/api/src/comment.rs
index 62b01b33..e6eef2cb 100644
--- a/crates/api/src/comment.rs
+++ b/crates/api/src/comment.rs
@@ -1,5 +1,7 @@
-use crate::Perform;
+use std::convert::TryInto;
+
 use actix_web::web::Data;
+
 use lemmy_api_common::{
   blocking,
   check_community_ban,
@@ -9,11 +11,11 @@ use lemmy_api_common::{
   get_local_user_view_from_jwt,
 };
 use lemmy_apub::{
-  activities::voting::{
+  fetcher::post_or_comment::PostOrComment,
+  protocol::activities::voting::{
     undo_vote::UndoVote,
     vote::{Vote, VoteType},
   },
-  fetcher::post_or_comment::PostOrComment,
 };
 use lemmy_db_schema::{
   newtypes::LocalUserId,
@@ -23,7 +25,8 @@ use lemmy_db_schema::{
 use lemmy_db_views::{comment_view::CommentView, local_user_view::LocalUserView};
 use lemmy_utils::{ApiError, ConnectionId, LemmyError};
 use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation};
-use std::convert::TryInto;
+
+use crate::Perform;
 
 #[async_trait::async_trait(?Send)]
 impl Perform for MarkCommentAsRead {
diff --git a/crates/api/src/comment_report.rs b/crates/api/src/comment_report.rs
index a7299b7a..82ecc1f4 100644
--- a/crates/api/src/comment_report.rs
+++ b/crates/api/src/comment_report.rs
@@ -1,5 +1,5 @@
-use crate::Perform;
 use actix_web::web::Data;
+
 use lemmy_api_common::{
   blocking,
   check_community_ban,
@@ -7,7 +7,7 @@ use lemmy_api_common::{
   get_local_user_view_from_jwt,
   is_mod_or_admin,
 };
-use lemmy_apub::{activities::report::Report, fetcher::object_id::ObjectId};
+use lemmy_apub::{fetcher::object_id::ObjectId, protocol::activities::community::report::Report};
 use lemmy_db_schema::{source::comment_report::*, traits::Reportable};
 use lemmy_db_views::{
   comment_report_view::{CommentReportQueryBuilder, CommentReportView},
@@ -16,6 +16,8 @@ use lemmy_db_views::{
 use lemmy_utils::{ApiError, ConnectionId, LemmyError};
 use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
 
+use crate::Perform;
+
 /// Creates a comment report and notifies the moderators of the community
 #[async_trait::async_trait(?Send)]
 impl Perform for CreateCommentReport {
diff --git a/crates/api/src/community.rs b/crates/api/src/community.rs
index ac39751c..b47a4ec6 100644
--- a/crates/api/src/community.rs
+++ b/crates/api/src/community.rs
@@ -10,16 +10,16 @@ use lemmy_api_common::{
   is_mod_or_admin,
 };
 use lemmy_apub::{
-  activities::{
+  objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::{
     community::{
       add_mod::AddMod,
       block_user::BlockUserFromCommunity,
       remove_mod::RemoveMod,
       undo_block_user::UndoBlockUserFromCommunity,
     },
-    following::{follow::FollowCommunity as FollowCommunityApub, undo::UndoFollowCommunity},
+    following::{follow::FollowCommunity as FollowCommunityApub, undo_follow::UndoFollowCommunity},
   },
-  objects::{community::ApubCommunity, person::ApubPerson},
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/api/src/post.rs b/crates/api/src/post.rs
index 488c8f59..3564f135 100644
--- a/crates/api/src/post.rs
+++ b/crates/api/src/post.rs
@@ -12,16 +12,16 @@ use lemmy_api_common::{
   post::*,
 };
 use lemmy_apub::{
-  activities::{
-    post::create_or_update::CreateOrUpdatePost,
+  fetcher::post_or_comment::PostOrComment,
+  objects::post::ApubPost,
+  protocol::activities::{
+    create_or_update::post::CreateOrUpdatePost,
     voting::{
       undo_vote::UndoVote,
       vote::{Vote, VoteType},
     },
     CreateOrUpdateType,
   },
-  fetcher::post_or_comment::PostOrComment,
-  objects::post::ApubPost,
 };
 use lemmy_db_schema::{
   source::{moderator::*, post::*},
diff --git a/crates/api/src/post_report.rs b/crates/api/src/post_report.rs
index 3e610bff..98b2f1c1 100644
--- a/crates/api/src/post_report.rs
+++ b/crates/api/src/post_report.rs
@@ -1,5 +1,5 @@
-use crate::Perform;
 use actix_web::web::Data;
+
 use lemmy_api_common::{
   blocking,
   check_community_ban,
@@ -13,7 +13,7 @@ use lemmy_api_common::{
     ResolvePostReport,
   },
 };
-use lemmy_apub::{activities::report::Report, fetcher::object_id::ObjectId};
+use lemmy_apub::{fetcher::object_id::ObjectId, protocol::activities::community::report::Report};
 use lemmy_db_schema::{
   source::post_report::{PostReport, PostReportForm},
   traits::Reportable,
@@ -25,6 +25,8 @@ use lemmy_db_views::{
 use lemmy_utils::{ApiError, ConnectionId, LemmyError};
 use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
 
+use crate::Perform;
+
 /// Creates a post report and notifies the moderators of the community
 #[async_trait::async_trait(?Send)]
 impl Perform for CreatePostReport {
diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs
index 6c90e94e..ab093ea1 100644
--- a/crates/api_crud/src/comment/create.rs
+++ b/crates/api_crud/src/comment/create.rs
@@ -1,5 +1,5 @@
-use crate::PerformCrud;
 use actix_web::web::Data;
+
 use lemmy_api_common::{
   blocking,
   check_community_ban,
@@ -11,13 +11,13 @@ use lemmy_api_common::{
   get_post,
 };
 use lemmy_apub::{
-  activities::{
-    comment::create_or_update::CreateOrUpdateComment,
+  fetcher::post_or_comment::PostOrComment,
+  generate_local_apub_endpoint,
+  protocol::activities::{
+    create_or_update::comment::CreateOrUpdateComment,
     voting::vote::{Vote, VoteType},
     CreateOrUpdateType,
   },
-  fetcher::post_or_comment::PostOrComment,
-  generate_local_apub_endpoint,
   EndpointType,
 };
 use lemmy_db_schema::{
@@ -40,6 +40,8 @@ use lemmy_websocket::{
   UserOperationCrud,
 };
 
+use crate::PerformCrud;
+
 #[async_trait::async_trait(?Send)]
 impl PerformCrud for CreateComment {
   type Response = CommentResponse;
diff --git a/crates/api_crud/src/comment/update.rs b/crates/api_crud/src/comment/update.rs
index 9a164fc3..70f15dfb 100644
--- a/crates/api_crud/src/comment/update.rs
+++ b/crates/api_crud/src/comment/update.rs
@@ -1,5 +1,5 @@
-use crate::PerformCrud;
 use actix_web::web::Data;
+
 use lemmy_api_common::{
   blocking,
   check_community_ban,
@@ -8,8 +8,8 @@ use lemmy_api_common::{
   comment::*,
   get_local_user_view_from_jwt,
 };
-use lemmy_apub::activities::{
-  comment::create_or_update::CreateOrUpdateComment,
+use lemmy_apub::protocol::activities::{
+  create_or_update::comment::CreateOrUpdateComment,
   CreateOrUpdateType,
 };
 use lemmy_db_schema::source::comment::Comment;
@@ -26,6 +26,8 @@ use lemmy_websocket::{
   UserOperationCrud,
 };
 
+use crate::PerformCrud;
+
 #[async_trait::async_trait(?Send)]
 impl PerformCrud for EditComment {
   type Response = CommentResponse;
diff --git a/crates/api_crud/src/community/update.rs b/crates/api_crud/src/community/update.rs
index 97722a98..4764b025 100644
--- a/crates/api_crud/src/community/update.rs
+++ b/crates/api_crud/src/community/update.rs
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   community::{CommunityResponse, EditCommunity},
   get_local_user_view_from_jwt,
 };
-use lemmy_apub::activities::community::update::UpdateCommunity;
+use lemmy_apub::protocol::activities::community::update::UpdateCommunity;
 use lemmy_db_schema::{
   diesel_option_overwrite_to_url,
   naive_now,
diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs
index a6641034..99b67d2e 100644
--- a/crates/api_crud/src/post/create.rs
+++ b/crates/api_crud/src/post/create.rs
@@ -1,5 +1,7 @@
-use crate::PerformCrud;
 use actix_web::web::Data;
+use log::warn;
+use webmention::{Webmention, WebmentionError};
+
 use lemmy_api_common::{
   blocking,
   check_community_ban,
@@ -10,13 +12,13 @@ use lemmy_api_common::{
   post::*,
 };
 use lemmy_apub::{
-  activities::{
-    post::create_or_update::CreateOrUpdatePost,
+  fetcher::post_or_comment::PostOrComment,
+  generate_local_apub_endpoint,
+  protocol::activities::{
+    create_or_update::post::CreateOrUpdatePost,
     voting::vote::{Vote, VoteType},
     CreateOrUpdateType,
   },
-  fetcher::post_or_comment::PostOrComment,
-  generate_local_apub_endpoint,
   EndpointType,
 };
 use lemmy_db_schema::{
@@ -31,8 +33,8 @@ use lemmy_utils::{
   LemmyError,
 };
 use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
-use log::warn;
-use webmention::{Webmention, WebmentionError};
+
+use crate::PerformCrud;
 
 #[async_trait::async_trait(?Send)]
 impl PerformCrud for CreatePost {
diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs
index 96e4400a..0a982d68 100644
--- a/crates/api_crud/src/post/update.rs
+++ b/crates/api_crud/src/post/update.rs
@@ -1,5 +1,5 @@
-use crate::PerformCrud;
 use actix_web::web::Data;
+
 use lemmy_api_common::{
   blocking,
   check_community_ban,
@@ -7,7 +7,10 @@ use lemmy_api_common::{
   get_local_user_view_from_jwt,
   post::*,
 };
-use lemmy_apub::activities::{post::create_or_update::CreateOrUpdatePost, CreateOrUpdateType};
+use lemmy_apub::protocol::activities::{
+  create_or_update::post::CreateOrUpdatePost,
+  CreateOrUpdateType,
+};
 use lemmy_db_schema::{
   naive_now,
   source::post::{Post, PostForm},
@@ -22,6 +25,8 @@ use lemmy_utils::{
 };
 use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
 
+use crate::PerformCrud;
+
 #[async_trait::async_trait(?Send)]
 impl PerformCrud for EditPost {
   type Response = PostResponse;
diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs
index c7bca4e0..705d781e 100644
--- a/crates/api_crud/src/private_message/create.rs
+++ b/crates/api_crud/src/private_message/create.rs
@@ -7,11 +7,11 @@ use lemmy_api_common::{
   person::{CreatePrivateMessage, PrivateMessageResponse},
 };
 use lemmy_apub::{
-  activities::{
+  generate_local_apub_endpoint,
+  protocol::activities::{
     private_message::create_or_update::CreateOrUpdatePrivateMessage,
     CreateOrUpdateType,
   },
-  generate_local_apub_endpoint,
   EndpointType,
 };
 use lemmy_db_schema::{
diff --git a/crates/api_crud/src/private_message/delete.rs b/crates/api_crud/src/private_message/delete.rs
index f369f82b..06bc22ed 100644
--- a/crates/api_crud/src/private_message/delete.rs
+++ b/crates/api_crud/src/private_message/delete.rs
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   get_local_user_view_from_jwt,
   person::{DeletePrivateMessage, PrivateMessageResponse},
 };
-use lemmy_apub::activities::private_message::{
+use lemmy_apub::protocol::activities::private_message::{
   delete::DeletePrivateMessage as DeletePrivateMessageApub,
   undo_delete::UndoDeletePrivateMessage,
 };
diff --git a/crates/api_crud/src/private_message/update.rs b/crates/api_crud/src/private_message/update.rs
index d72e3b13..8114556c 100644
--- a/crates/api_crud/src/private_message/update.rs
+++ b/crates/api_crud/src/private_message/update.rs
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   get_local_user_view_from_jwt,
   person::{EditPrivateMessage, PrivateMessageResponse},
 };
-use lemmy_apub::activities::{
+use lemmy_apub::protocol::activities::{
   private_message::create_or_update::CreateOrUpdatePrivateMessage,
   CreateOrUpdateType,
 };
diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs
index d53801aa..335e1713 100644
--- a/crates/apub/src/activities/comment/create_or_update.rs
+++ b/crates/apub/src/activities/comment/create_or_update.rs
@@ -1,11 +1,9 @@
-use activitystreams::{link::Mention, public, unparsed::Unparsed};
-use serde::{Deserialize, Serialize};
-use url::Url;
+use activitystreams::public;
 
 use lemmy_api_common::{blocking, check_post_deleted_or_removed};
 use lemmy_apub_lib::{
   data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType, ApubObject},
+  traits::{ActivityHandler, ActorType, ApubObject},
   verify::verify_domains_match,
 };
 use lemmy_db_schema::{
@@ -19,37 +17,18 @@ use crate::{
   activities::{
     check_community_deleted_or_removed,
     comment::{collect_non_local_mentions, get_notif_recipients},
-    community::{
-      announce::{AnnouncableActivities, GetCommunity},
-      send_to_community,
-    },
+    community::{announce::GetCommunity, send_to_community},
     generate_activity_id,
     verify_activity,
     verify_is_public,
     verify_person_in_community,
-    CreateOrUpdateType,
   },
+  activity_lists::AnnouncableActivities,
   fetcher::object_id::ObjectId,
   objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
-  protocol::objects::note::Note,
+  protocol::activities::{create_or_update::comment::CreateOrUpdateComment, CreateOrUpdateType},
 };
 
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct CreateOrUpdateComment {
-  actor: ObjectId<ApubPerson>,
-  to: Vec<Url>,
-  object: Note,
-  cc: Vec<Url>,
-  #[serde(default)]
-  tag: Vec<Mention>,
-  #[serde(rename = "type")]
-  kind: CreateOrUpdateType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
-
 impl CreateOrUpdateComment {
   pub async fn send(
     comment: &ApubComment,
diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs
index b65fa48e..b18e50ad 100644
--- a/crates/apub/src/activities/community/add_mod.rs
+++ b/crates/apub/src/activities/community/add_mod.rs
@@ -1,10 +1,6 @@
 use crate::{
   activities::{
-    community::{
-      announce::{AnnouncableActivities, GetCommunity},
-      get_community_from_moderators_url,
-      send_to_community,
-    },
+    community::{announce::GetCommunity, get_community_from_moderators_url, send_to_community},
     generate_activity_id,
     verify_activity,
     verify_add_remove_moderator_target,
@@ -12,15 +8,17 @@ use crate::{
     verify_mod_action,
     verify_person_in_community,
   },
+  activity_lists::AnnouncableActivities,
   fetcher::object_id::ObjectId,
   generate_moderators_url,
   objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::community::add_mod::AddMod,
 };
-use activitystreams::{activity::kind::AddType, public, unparsed::Unparsed};
+use activitystreams::{activity::kind::AddType, public};
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType},
+  traits::{ActivityHandler, ActorType},
 };
 use lemmy_db_schema::{
   source::community::{CommunityModerator, CommunityModeratorForm},
@@ -28,23 +26,6 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct AddMod {
-  actor: ObjectId<ApubPerson>,
-  to: Vec<Url>,
-  object: ObjectId<ApubPerson>,
-  target: Url,
-  cc: Vec<Url>,
-  #[serde(rename = "type")]
-  kind: AddType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
 
 impl AddMod {
   pub async fn send(
diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs
index 007fcbb3..62e7c56f 100644
--- a/crates/apub/src/activities/community/announce.rs
+++ b/crates/apub/src/activities/community/announce.rs
@@ -1,55 +1,27 @@
 use crate::{
   activities::{
-    comment::create_or_update::CreateOrUpdateComment,
-    community::{
-      add_mod::AddMod,
-      block_user::BlockUserFromCommunity,
-      list_community_follower_inboxes,
-      remove_mod::RemoveMod,
-      undo_block_user::UndoBlockUserFromCommunity,
-      update::UpdateCommunity,
-    },
-    deletion::{delete::Delete, undo_delete::UndoDelete},
+    community::list_community_follower_inboxes,
     generate_activity_id,
-    post::create_or_update::CreateOrUpdatePost,
     send_lemmy_activity,
     verify_activity,
     verify_is_public,
-    voting::{undo_vote::UndoVote, vote::Vote},
   },
+  activity_lists::AnnouncableActivities,
   fetcher::object_id::ObjectId,
   http::is_activity_already_known,
   insert_activity,
   objects::community::ApubCommunity,
+  protocol::activities::community::announce::AnnounceActivity,
 };
-use activitystreams::{activity::kind::AnnounceType, public, unparsed::Unparsed};
+use activitystreams::{activity::kind::AnnounceType, public};
 use lemmy_apub_lib::{
   data::Data,
   traits::{ActivityFields, ActivityHandler, ActorType},
-  verify::verify_urls_match,
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
-#[serde(untagged)]
-#[activity_handler(LemmyContext)]
-pub enum AnnouncableActivities {
-  CreateOrUpdateComment(CreateOrUpdateComment),
-  CreateOrUpdatePost(Box<CreateOrUpdatePost>),
-  Vote(Vote),
-  UndoVote(UndoVote),
-  Delete(Delete),
-  UndoDelete(UndoDelete),
-  UpdateCommunity(Box<UpdateCommunity>),
-  BlockUserFromCommunity(BlockUserFromCommunity),
-  UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
-  AddMod(AddMod),
-  RemoveMod(RemoveMod),
-}
-
 #[async_trait::async_trait(?Send)]
 pub(crate) trait GetCommunity {
   async fn get_community(
@@ -59,46 +31,6 @@ pub(crate) trait GetCommunity {
   ) -> Result<ApubCommunity, LemmyError>;
 }
 
-#[async_trait::async_trait(?Send)]
-impl GetCommunity for AnnouncableActivities {
-  async fn get_community(
-    &self,
-    context: &LemmyContext,
-    request_counter: &mut i32,
-  ) -> Result<ApubCommunity, LemmyError> {
-    use AnnouncableActivities::*;
-    let community = match self {
-      CreateOrUpdateComment(a) => a.get_community(context, request_counter).await?,
-      CreateOrUpdatePost(a) => a.get_community(context, request_counter).await?,
-      Vote(a) => a.get_community(context, request_counter).await?,
-      UndoVote(a) => a.get_community(context, request_counter).await?,
-      Delete(a) => a.get_community(context, request_counter).await?,
-      UndoDelete(a) => a.get_community(context, request_counter).await?,
-      UpdateCommunity(a) => a.get_community(context, request_counter).await?,
-      BlockUserFromCommunity(a) => a.get_community(context, request_counter).await?,
-      UndoBlockUserFromCommunity(a) => a.get_community(context, request_counter).await?,
-      AddMod(a) => a.get_community(context, request_counter).await?,
-      RemoveMod(a) => a.get_community(context, request_counter).await?,
-    };
-    verify_urls_match(self.actor(), &community.actor_id())?;
-    Ok(community)
-  }
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct AnnounceActivity {
-  actor: ObjectId<ApubCommunity>,
-  to: Vec<Url>,
-  object: AnnouncableActivities,
-  cc: Vec<Url>,
-  #[serde(rename = "type")]
-  kind: AnnounceType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
-
 impl AnnounceActivity {
   pub async fn send(
     object: AnnouncableActivities,
diff --git a/crates/apub/src/activities/community/block_user.rs b/crates/apub/src/activities/community/block_user.rs
index 0a716228..dfe6c4c9 100644
--- a/crates/apub/src/activities/community/block_user.rs
+++ b/crates/apub/src/activities/community/block_user.rs
@@ -1,23 +1,22 @@
 use crate::{
   activities::{
-    community::{
-      announce::{AnnouncableActivities, GetCommunity},
-      send_to_community,
-    },
+    community::{announce::GetCommunity, send_to_community},
     generate_activity_id,
     verify_activity,
     verify_is_public,
     verify_mod_action,
     verify_person_in_community,
   },
+  activity_lists::AnnouncableActivities,
   fetcher::object_id::ObjectId,
   objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::community::block_user::BlockUserFromCommunity,
 };
-use activitystreams::{activity::kind::BlockType, public, unparsed::Unparsed};
+use activitystreams::{activity::kind::BlockType, public};
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType},
+  traits::{ActivityHandler, ActorType},
 };
 use lemmy_db_schema::{
   source::community::{
@@ -30,23 +29,6 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct BlockUserFromCommunity {
-  actor: ObjectId<ApubPerson>,
-  to: Vec<Url>,
-  pub(in crate::activities::community) object: ObjectId<ApubPerson>,
-  cc: Vec<Url>,
-  target: ObjectId<ApubCommunity>,
-  #[serde(rename = "type")]
-  kind: BlockType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
 
 impl BlockUserFromCommunity {
   pub(in crate::activities::community) fn new(
diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs
index 1f51c033..ebc6e7f9 100644
--- a/crates/apub/src/activities/community/mod.rs
+++ b/crates/apub/src/activities/community/mod.rs
@@ -1,23 +1,25 @@
+use itertools::Itertools;
+use url::Url;
+
+use lemmy_apub_lib::traits::ActorType;
+use lemmy_utils::LemmyError;
+use lemmy_websocket::LemmyContext;
+
 use crate::{
-  activities::{
-    community::announce::{AnnouncableActivities, AnnounceActivity},
-    send_lemmy_activity,
-  },
+  activities::send_lemmy_activity,
+  activity_lists::AnnouncableActivities,
   check_is_apub_id_valid,
   fetcher::object_id::ObjectId,
   insert_activity,
   objects::community::ApubCommunity,
+  protocol::activities::community::announce::AnnounceActivity,
 };
-use itertools::Itertools;
-use lemmy_apub_lib::traits::ActorType;
-use lemmy_utils::LemmyError;
-use lemmy_websocket::LemmyContext;
-use url::Url;
 
 pub mod add_mod;
 pub mod announce;
 pub mod block_user;
 pub mod remove_mod;
+pub mod report;
 pub mod undo_block_user;
 pub mod update;
 
diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs
index f0c0b90e..02ff3c06 100644
--- a/crates/apub/src/activities/community/remove_mod.rs
+++ b/crates/apub/src/activities/community/remove_mod.rs
@@ -1,10 +1,6 @@
 use crate::{
   activities::{
-    community::{
-      announce::{AnnouncableActivities, GetCommunity},
-      get_community_from_moderators_url,
-      send_to_community,
-    },
+    community::{announce::GetCommunity, get_community_from_moderators_url, send_to_community},
     generate_activity_id,
     verify_activity,
     verify_add_remove_moderator_target,
@@ -12,15 +8,17 @@ use crate::{
     verify_mod_action,
     verify_person_in_community,
   },
+  activity_lists::AnnouncableActivities,
   fetcher::object_id::ObjectId,
   generate_moderators_url,
   objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::community::remove_mod::RemoveMod,
 };
-use activitystreams::{activity::kind::RemoveType, public, unparsed::Unparsed};
+use activitystreams::{activity::kind::RemoveType, public};
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType},
+  traits::{ActivityHandler, ActorType},
 };
 use lemmy_db_schema::{
   source::community::{CommunityModerator, CommunityModeratorForm},
@@ -28,23 +26,6 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct RemoveMod {
-  actor: ObjectId<ApubPerson>,
-  to: Vec<Url>,
-  pub(in crate::activities) object: ObjectId<ApubPerson>,
-  cc: Vec<Url>,
-  #[serde(rename = "type")]
-  kind: RemoveType,
-  pub(in crate::activities) target: Url,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
 
 impl RemoveMod {
   pub async fn send(
diff --git a/crates/apub/src/activities/report.rs b/crates/apub/src/activities/community/report.rs
similarity index 88%
rename from crates/apub/src/activities/report.rs
rename to crates/apub/src/activities/community/report.rs
index 9256920a..1e7cc5fa 100644
--- a/crates/apub/src/activities/report.rs
+++ b/crates/apub/src/activities/community/report.rs
@@ -1,19 +1,9 @@
-use crate::{
-  activities::{
-    generate_activity_id,
-    send_lemmy_activity,
-    verify_activity,
-    verify_person_in_community,
-  },
-  fetcher::object_id::ObjectId,
-  objects::{community::ApubCommunity, person::ApubPerson},
-  PostOrComment,
-};
-use activitystreams::{activity::kind::FlagType, unparsed::Unparsed};
+use activitystreams::activity::kind::FlagType;
+
 use lemmy_api_common::{blocking, comment::CommentReportResponse, post::PostReportResponse};
 use lemmy_apub_lib::{
   data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType},
+  traits::{ActivityHandler, ActorType},
 };
 use lemmy_db_schema::{
   source::{
@@ -25,22 +15,19 @@ use lemmy_db_schema::{
 use lemmy_db_views::{comment_report_view::CommentReportView, post_report_view::PostReportView};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
-use serde::{Deserialize, Serialize};
-use url::Url;
 
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct Report {
-  actor: ObjectId<ApubPerson>,
-  to: [ObjectId<ApubCommunity>; 1],
-  object: ObjectId<PostOrComment>,
-  summary: String,
-  #[serde(rename = "type")]
-  kind: FlagType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
+use crate::{
+  activities::{
+    generate_activity_id,
+    send_lemmy_activity,
+    verify_activity,
+    verify_person_in_community,
+  },
+  fetcher::object_id::ObjectId,
+  objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::community::report::Report,
+  PostOrComment,
+};
 
 impl Report {
   pub async fn send(
diff --git a/crates/apub/src/activities/community/undo_block_user.rs b/crates/apub/src/activities/community/undo_block_user.rs
index ad220c96..2bda9444 100644
--- a/crates/apub/src/activities/community/undo_block_user.rs
+++ b/crates/apub/src/activities/community/undo_block_user.rs
@@ -1,24 +1,25 @@
 use crate::{
   activities::{
-    community::{
-      announce::{AnnouncableActivities, GetCommunity},
-      block_user::BlockUserFromCommunity,
-      send_to_community,
-    },
+    community::{announce::GetCommunity, send_to_community},
     generate_activity_id,
     verify_activity,
     verify_is_public,
     verify_mod_action,
     verify_person_in_community,
   },
+  activity_lists::AnnouncableActivities,
   fetcher::object_id::ObjectId,
   objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::community::{
+    block_user::BlockUserFromCommunity,
+    undo_block_user::UndoBlockUserFromCommunity,
+  },
 };
-use activitystreams::{activity::kind::UndoType, public, unparsed::Unparsed};
+use activitystreams::{activity::kind::UndoType, public};
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType},
+  traits::{ActivityHandler, ActorType},
 };
 use lemmy_db_schema::{
   source::community::{CommunityPersonBan, CommunityPersonBanForm},
@@ -26,22 +27,6 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct UndoBlockUserFromCommunity {
-  actor: ObjectId<ApubPerson>,
-  to: Vec<Url>,
-  object: BlockUserFromCommunity,
-  cc: Vec<Url>,
-  #[serde(rename = "type")]
-  kind: UndoType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
 
 impl UndoBlockUserFromCommunity {
   pub async fn send(
diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs
index df34ca28..28de0db0 100644
--- a/crates/apub/src/activities/community/update.rs
+++ b/crates/apub/src/activities/community/update.rs
@@ -1,52 +1,29 @@
-use activitystreams::{activity::kind::UpdateType, public, unparsed::Unparsed};
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::{
-  data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType, ApubObject},
-};
-use lemmy_db_schema::{
-  source::community::{Community, CommunityForm},
-  traits::Crud,
-};
-use lemmy_utils::LemmyError;
-use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
-
 use crate::{
   activities::{
-    community::{
-      announce::{AnnouncableActivities, GetCommunity},
-      send_to_community,
-    },
+    community::{announce::GetCommunity, send_to_community},
     generate_activity_id,
     verify_activity,
     verify_is_public,
     verify_mod_action,
     verify_person_in_community,
   },
+  activity_lists::AnnouncableActivities,
   fetcher::object_id::ObjectId,
   objects::{community::ApubCommunity, person::ApubPerson},
-  protocol::objects::group::Group,
+  protocol::{activities::community::update::UpdateCommunity, objects::group::Group},
 };
-
-/// This activity is received from a remote community mod, and updates the description or other
-/// fields of a local community.
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct UpdateCommunity {
-  actor: ObjectId<ApubPerson>,
-  to: Vec<Url>,
-  // TODO: would be nice to use a separate struct here, which only contains the fields updated here
-  object: Group,
-  cc: Vec<Url>,
-  #[serde(rename = "type")]
-  kind: UpdateType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
+use activitystreams::{activity::kind::UpdateType, public};
+use lemmy_api_common::blocking;
+use lemmy_apub_lib::{
+  data::Data,
+  traits::{ActivityHandler, ActorType, ApubObject},
+};
+use lemmy_db_schema::{
+  source::community::{Community, CommunityForm},
+  traits::Crud,
+};
+use lemmy_utils::LemmyError;
+use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
 
 impl UpdateCommunity {
   pub async fn send(
diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs
index 0db590b9..672fca52 100644
--- a/crates/apub/src/activities/deletion/delete.rs
+++ b/crates/apub/src/activities/deletion/delete.rs
@@ -1,28 +1,11 @@
-use crate::{
-  activities::{
-    community::{
-      announce::{AnnouncableActivities, GetCommunity},
-      send_to_community,
-    },
-    deletion::{
-      receive_delete_action,
-      verify_delete_activity,
-      DeletableObjects,
-      WebsocketMessages,
-    },
-    generate_activity_id,
-    verify_activity,
-    verify_is_public,
-  },
-  fetcher::object_id::ObjectId,
-  objects::{community::ApubCommunity, person::ApubPerson},
-};
-use activitystreams::{activity::kind::DeleteType, public, unparsed::Unparsed};
+use activitystreams::{activity::kind::DeleteType, public};
 use anyhow::anyhow;
+use url::Url;
+
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType},
+  traits::{ActivityHandler, ActorType},
 };
 use lemmy_db_schema::{
   source::{
@@ -46,35 +29,25 @@ use lemmy_websocket::{
   LemmyContext,
   UserOperationCrud,
 };
-use serde::{Deserialize, Serialize};
-use serde_with::skip_serializing_none;
-use url::Url;
 
-/// This is very confusing, because there are four distinct cases to handle:
-/// - user deletes their post
-/// - user deletes their comment
-/// - remote community mod deletes local community
-/// - remote community deletes itself (triggered by a mod)
-///
-/// TODO: we should probably change how community deletions work to simplify this. Probably by
-/// wrapping it in an announce just like other activities, instead of having the community send it.
-#[skip_serializing_none]
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct Delete {
-  actor: ObjectId<ApubPerson>,
-  to: Vec<Url>,
-  pub(in crate::activities::deletion) object: Url,
-  pub(in crate::activities::deletion) cc: Vec<Url>,
-  #[serde(rename = "type")]
-  kind: DeleteType,
-  /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
-  /// deleting their own content.
-  pub(in crate::activities::deletion) summary: Option<String>,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
+use crate::{
+  activities::{
+    community::{announce::GetCommunity, send_to_community},
+    deletion::{
+      receive_delete_action,
+      verify_delete_activity,
+      DeletableObjects,
+      WebsocketMessages,
+    },
+    generate_activity_id,
+    verify_activity,
+    verify_is_public,
+  },
+  activity_lists::AnnouncableActivities,
+  fetcher::object_id::ObjectId,
+  objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::deletion::delete::Delete,
+};
 
 #[async_trait::async_trait(?Send)]
 impl ActivityHandler for Delete {
diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs
index 1af02245..b9c11291 100644
--- a/crates/apub/src/activities/deletion/mod.rs
+++ b/crates/apub/src/activities/deletion/mod.rs
@@ -1,12 +1,5 @@
-use crate::{
-  activities::{
-    deletion::{delete::Delete, undo_delete::UndoDelete},
-    verify_mod_action,
-    verify_person_in_community,
-  },
-  fetcher::object_id::ObjectId,
-  objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
-};
+use url::Url;
+
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   traits::{ActivityFields, ActorType, ApubObject},
@@ -19,7 +12,13 @@ use lemmy_websocket::{
   LemmyContext,
   UserOperationCrud,
 };
-use url::Url;
+
+use crate::{
+  activities::{verify_mod_action, verify_person_in_community},
+  fetcher::object_id::ObjectId,
+  objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
+  protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
+};
 
 pub mod delete;
 pub mod undo_delete;
diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs
index 5cc4dffc..c1689119 100644
--- a/crates/apub/src/activities/deletion/undo_delete.rs
+++ b/crates/apub/src/activities/deletion/undo_delete.rs
@@ -1,11 +1,24 @@
+use activitystreams::{activity::kind::UndoType, public};
+use anyhow::anyhow;
+use url::Url;
+
+use lemmy_api_common::blocking;
+use lemmy_apub_lib::{
+  data::Data,
+  traits::{ActivityHandler, ActorType},
+};
+use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
+use lemmy_utils::LemmyError;
+use lemmy_websocket::{
+  send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
+  LemmyContext,
+  UserOperationCrud,
+};
+
 use crate::{
   activities::{
-    community::{
-      announce::{AnnouncableActivities, GetCommunity},
-      send_to_community,
-    },
+    community::{announce::GetCommunity, send_to_community},
     deletion::{
-      delete::Delete,
       receive_delete_action,
       verify_delete_activity,
       DeletableObjects,
@@ -15,39 +28,11 @@ use crate::{
     verify_activity,
     verify_is_public,
   },
+  activity_lists::AnnouncableActivities,
   fetcher::object_id::ObjectId,
   objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
 };
-use activitystreams::{activity::kind::UndoType, public, unparsed::Unparsed};
-use anyhow::anyhow;
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::{
-  data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType},
-};
-use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
-use lemmy_utils::LemmyError;
-use lemmy_websocket::{
-  send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
-  LemmyContext,
-  UserOperationCrud,
-};
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct UndoDelete {
-  actor: ObjectId<ApubPerson>,
-  to: Vec<Url>,
-  object: Delete,
-  cc: Vec<Url>,
-  #[serde(rename = "type")]
-  kind: UndoType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
 
 #[async_trait::async_trait(?Send)]
 impl ActivityHandler for UndoDelete {
diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs
index 28f6c108..984d622a 100644
--- a/crates/apub/src/activities/following/accept.rs
+++ b/crates/apub/src/activities/following/accept.rs
@@ -1,14 +1,9 @@
 use crate::{
-  activities::{
-    following::follow::FollowCommunity,
-    generate_activity_id,
-    send_lemmy_activity,
-    verify_activity,
-  },
+  activities::{generate_activity_id, send_lemmy_activity, verify_activity},
   fetcher::object_id::ObjectId,
-  objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
 };
-use activitystreams::{activity::kind::AcceptType, unparsed::Unparsed};
+use activitystreams::activity::kind::AcceptType;
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   data::Data,
@@ -18,21 +13,6 @@ use lemmy_apub_lib::{
 use lemmy_db_schema::{source::community::CommunityFollower, traits::Followable};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct AcceptFollowCommunity {
-  actor: ObjectId<ApubCommunity>,
-  to: [ObjectId<ApubPerson>; 1],
-  object: FollowCommunity,
-  #[serde(rename = "type")]
-  kind: AcceptType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
 
 impl AcceptFollowCommunity {
   pub async fn send(
diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs
index 57a112e0..e048907f 100644
--- a/crates/apub/src/activities/following/follow.rs
+++ b/crates/apub/src/activities/following/follow.rs
@@ -1,6 +1,5 @@
 use crate::{
   activities::{
-    following::accept::AcceptFollowCommunity,
     generate_activity_id,
     send_lemmy_activity,
     verify_activity,
@@ -9,12 +8,13 @@ use crate::{
   },
   fetcher::object_id::ObjectId,
   objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
 };
-use activitystreams::{activity::kind::FollowType, unparsed::Unparsed};
+use activitystreams::activity::kind::FollowType;
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType},
+  traits::{ActivityHandler, ActorType},
   verify::verify_urls_match,
 };
 use lemmy_db_schema::{
@@ -23,21 +23,6 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct FollowCommunity {
-  pub(in crate::activities::following) actor: ObjectId<ApubPerson>,
-  pub(in crate::activities::following) to: [ObjectId<ApubCommunity>; 1],
-  pub(in crate::activities::following) object: ObjectId<ApubCommunity>,
-  #[serde(rename = "type")]
-  kind: FollowType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
 
 impl FollowCommunity {
   pub(in crate::activities::following) fn new(
diff --git a/crates/apub/src/activities/following/mod.rs b/crates/apub/src/activities/following/mod.rs
index 050c3691..60bdd5f7 100644
--- a/crates/apub/src/activities/following/mod.rs
+++ b/crates/apub/src/activities/following/mod.rs
@@ -1,3 +1,3 @@
 pub mod accept;
 pub mod follow;
-pub mod undo;
+pub mod undo_follow;
diff --git a/crates/apub/src/activities/following/undo.rs b/crates/apub/src/activities/following/undo_follow.rs
similarity index 79%
rename from crates/apub/src/activities/following/undo.rs
rename to crates/apub/src/activities/following/undo_follow.rs
index a9326a26..c3fd78b5 100644
--- a/crates/apub/src/activities/following/undo.rs
+++ b/crates/apub/src/activities/following/undo_follow.rs
@@ -1,15 +1,10 @@
 use crate::{
-  activities::{
-    following::follow::FollowCommunity,
-    generate_activity_id,
-    send_lemmy_activity,
-    verify_activity,
-    verify_person,
-  },
+  activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
   fetcher::object_id::ObjectId,
   objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity},
 };
-use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
+use activitystreams::activity::kind::UndoType;
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   data::Data,
@@ -22,21 +17,6 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct UndoFollowCommunity {
-  actor: ObjectId<ApubPerson>,
-  to: [ObjectId<ApubCommunity>; 1],
-  object: FollowCommunity,
-  #[serde(rename = "type")]
-  kind: UndoType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
 
 impl UndoFollowCommunity {
   pub async fn send(
diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs
index 9509babb..bc6cfb51 100644
--- a/crates/apub/src/activities/mod.rs
+++ b/crates/apub/src/activities/mod.rs
@@ -22,8 +22,7 @@ use lemmy_db_views_actor::{
 use lemmy_utils::{settings::structs::Settings, LemmyError};
 use lemmy_websocket::LemmyContext;
 use log::info;
-use serde::{Deserialize, Serialize};
-use strum_macros::ToString;
+use serde::Serialize;
 use url::{ParseError, Url};
 use uuid::Uuid;
 
@@ -33,15 +32,8 @@ pub mod deletion;
 pub mod following;
 pub mod post;
 pub mod private_message;
-pub mod report;
 pub mod voting;
 
-#[derive(Clone, Debug, ToString, Deserialize, Serialize)]
-pub enum CreateOrUpdateType {
-  Create,
-  Update,
-}
-
 /// Checks that the specified Url actually identifies a Person (by fetching it), and that the person
 /// doesn't have a site ban.
 async fn verify_person(
diff --git a/crates/apub/src/activities/post/create_or_update.rs b/crates/apub/src/activities/post/create_or_update.rs
index ee1bf19c..41590493 100644
--- a/crates/apub/src/activities/post/create_or_update.rs
+++ b/crates/apub/src/activities/post/create_or_update.rs
@@ -1,7 +1,5 @@
-use activitystreams::{public, unparsed::Unparsed};
+use activitystreams::public;
 use anyhow::anyhow;
-use serde::{Deserialize, Serialize};
-use url::Url;
 
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
@@ -16,36 +14,19 @@ use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCru
 use crate::{
   activities::{
     check_community_deleted_or_removed,
-    community::{
-      announce::{AnnouncableActivities, GetCommunity},
-      send_to_community,
-    },
+    community::{announce::GetCommunity, send_to_community},
     generate_activity_id,
     verify_activity,
     verify_is_public,
     verify_mod_action,
     verify_person_in_community,
-    CreateOrUpdateType,
   },
+  activity_lists::AnnouncableActivities,
   fetcher::object_id::ObjectId,
   objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
-  protocol::objects::page::Page,
+  protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
 };
 
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct CreateOrUpdatePost {
-  actor: ObjectId<ApubPerson>,
-  to: Vec<Url>,
-  object: Page,
-  cc: Vec<Url>,
-  #[serde(rename = "type")]
-  kind: CreateOrUpdateType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
-
 impl CreateOrUpdatePost {
   pub(crate) async fn new(
     post: &ApubPost,
diff --git a/crates/apub/src/activities/private_message/create_or_update.rs b/crates/apub/src/activities/private_message/create_or_update.rs
index 0067607e..cfd7c8bc 100644
--- a/crates/apub/src/activities/private_message/create_or_update.rs
+++ b/crates/apub/src/activities/private_message/create_or_update.rs
@@ -1,40 +1,21 @@
 use crate::{
-  activities::{
-    generate_activity_id,
-    send_lemmy_activity,
-    verify_activity,
-    verify_person,
-    CreateOrUpdateType,
-  },
+  activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
   fetcher::object_id::ObjectId,
   objects::{person::ApubPerson, private_message::ApubPrivateMessage},
-  protocol::objects::chat_message::ChatMessage,
+  protocol::activities::{
+    private_message::create_or_update::CreateOrUpdatePrivateMessage,
+    CreateOrUpdateType,
+  },
 };
-use activitystreams::unparsed::Unparsed;
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType, ApubObject},
+  traits::{ActivityHandler, ActorType, ApubObject},
   verify::verify_domains_match,
 };
 use lemmy_db_schema::{source::person::Person, traits::Crud};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct CreateOrUpdatePrivateMessage {
-  id: Url,
-  actor: ObjectId<ApubPerson>,
-  to: [ObjectId<ApubPerson>; 1],
-  object: ChatMessage,
-  #[serde(rename = "type")]
-  kind: CreateOrUpdateType,
-  #[serde(flatten)]
-  pub unparsed: Unparsed,
-}
 
 impl CreateOrUpdatePrivateMessage {
   pub async fn send(
diff --git a/crates/apub/src/activities/private_message/delete.rs b/crates/apub/src/activities/private_message/delete.rs
index 0928cba2..da3b6472 100644
--- a/crates/apub/src/activities/private_message/delete.rs
+++ b/crates/apub/src/activities/private_message/delete.rs
@@ -2,12 +2,13 @@ use crate::{
   activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
   fetcher::object_id::ObjectId,
   objects::{person::ApubPerson, private_message::ApubPrivateMessage},
+  protocol::activities::private_message::delete::DeletePrivateMessage,
 };
-use activitystreams::{activity::kind::DeleteType, unparsed::Unparsed};
+use activitystreams::activity::kind::DeleteType;
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType},
+  traits::{ActivityHandler, ActorType},
   verify::verify_domains_match,
 };
 use lemmy_db_schema::{
@@ -16,21 +17,6 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct DeletePrivateMessage {
-  actor: ObjectId<ApubPerson>,
-  to: [ObjectId<ApubPerson>; 1],
-  pub(in crate::activities::private_message) object: ObjectId<ApubPrivateMessage>,
-  #[serde(rename = "type")]
-  kind: DeleteType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
 
 impl DeletePrivateMessage {
   pub(in crate::activities::private_message) fn new(
diff --git a/crates/apub/src/activities/private_message/undo_delete.rs b/crates/apub/src/activities/private_message/undo_delete.rs
index 4275ea9c..bba9e0f2 100644
--- a/crates/apub/src/activities/private_message/undo_delete.rs
+++ b/crates/apub/src/activities/private_message/undo_delete.rs
@@ -1,15 +1,13 @@
 use crate::{
-  activities::{
-    generate_activity_id,
-    private_message::delete::DeletePrivateMessage,
-    send_lemmy_activity,
-    verify_activity,
-    verify_person,
-  },
+  activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
   fetcher::object_id::ObjectId,
   objects::{person::ApubPerson, private_message::ApubPrivateMessage},
+  protocol::activities::private_message::{
+    delete::DeletePrivateMessage,
+    undo_delete::UndoDeletePrivateMessage,
+  },
 };
-use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
+use activitystreams::activity::kind::UndoType;
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   data::Data,
@@ -22,21 +20,6 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct UndoDeletePrivateMessage {
-  actor: ObjectId<ApubPerson>,
-  to: [ObjectId<ApubPerson>; 1],
-  object: DeletePrivateMessage,
-  #[serde(rename = "type")]
-  kind: UndoType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
 
 impl UndoDeletePrivateMessage {
   pub async fn send(
diff --git a/crates/apub/src/activities/voting/mod.rs b/crates/apub/src/activities/voting/mod.rs
index 829553d3..0a2a8fd1 100644
--- a/crates/apub/src/activities/voting/mod.rs
+++ b/crates/apub/src/activities/voting/mod.rs
@@ -1,7 +1,3 @@
-use crate::{
-  activities::voting::vote::VoteType,
-  objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
-};
 use lemmy_api_common::blocking;
 use lemmy_db_schema::{
   source::{
@@ -17,6 +13,11 @@ use lemmy_websocket::{
   UserOperation,
 };
 
+use crate::{
+  objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
+  protocol::activities::voting::vote::VoteType,
+};
+
 pub mod undo_vote;
 pub mod vote;
 
diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs
index cc620661..e95d2517 100644
--- a/crates/apub/src/activities/voting/undo_vote.rs
+++ b/crates/apub/src/activities/voting/undo_vote.rs
@@ -1,50 +1,35 @@
+use std::ops::Deref;
+
+use activitystreams::{activity::kind::UndoType, public};
+
+use lemmy_api_common::blocking;
+use lemmy_apub_lib::{
+  data::Data,
+  traits::{ActivityFields, ActivityHandler, ActorType},
+  verify::verify_urls_match,
+};
+use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
+use lemmy_utils::LemmyError;
+use lemmy_websocket::LemmyContext;
+
 use crate::{
   activities::{
-    community::{
-      announce::{AnnouncableActivities, GetCommunity},
-      send_to_community,
-    },
+    community::{announce::GetCommunity, send_to_community},
     generate_activity_id,
     verify_activity,
     verify_is_public,
     verify_person_in_community,
-    voting::{
-      undo_vote_comment,
-      undo_vote_post,
-      vote::{Vote, VoteType},
-    },
+    voting::{undo_vote_comment, undo_vote_post},
   },
+  activity_lists::AnnouncableActivities,
   fetcher::object_id::ObjectId,
   objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::voting::{
+    undo_vote::UndoVote,
+    vote::{Vote, VoteType},
+  },
   PostOrComment,
 };
-use activitystreams::{activity::kind::UndoType, public, unparsed::Unparsed};
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::{
-  data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType},
-  verify::verify_urls_match,
-};
-use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
-use lemmy_utils::LemmyError;
-use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-use std::ops::Deref;
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct UndoVote {
-  actor: ObjectId<ApubPerson>,
-  to: Vec<Url>,
-  object: Vote,
-  cc: Vec<Url>,
-  #[serde(rename = "type")]
-  kind: UndoType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
 
 impl UndoVote {
   pub async fn send(
diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs
index 3efef7cf..01df4b93 100644
--- a/crates/apub/src/activities/voting/vote.rs
+++ b/crates/apub/src/activities/voting/vote.rs
@@ -1,25 +1,11 @@
-use crate::{
-  activities::{
-    community::{
-      announce::{AnnouncableActivities, GetCommunity},
-      send_to_community,
-    },
-    generate_activity_id,
-    verify_activity,
-    verify_is_public,
-    verify_person_in_community,
-    voting::{vote_comment, vote_post},
-  },
-  fetcher::object_id::ObjectId,
-  objects::{community::ApubCommunity, person::ApubPerson},
-  PostOrComment,
-};
-use activitystreams::{public, unparsed::Unparsed};
-use anyhow::anyhow;
+use std::ops::Deref;
+
+use activitystreams::public;
+
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType},
+  traits::{ActivityHandler, ActorType},
 };
 use lemmy_db_schema::{
   newtypes::CommunityId,
@@ -28,51 +14,22 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-use std::{convert::TryFrom, ops::Deref};
-use strum_macros::ToString;
-use url::Url;
-
-#[derive(Clone, Debug, ToString, Deserialize, Serialize)]
-pub enum VoteType {
-  Like,
-  Dislike,
-}
-
-impl TryFrom<i16> for VoteType {
-  type Error = LemmyError;
-
-  fn try_from(value: i16) -> Result<Self, Self::Error> {
-    match value {
-      1 => Ok(VoteType::Like),
-      -1 => Ok(VoteType::Dislike),
-      _ => Err(anyhow!("invalid vote value").into()),
-    }
-  }
-}
-
-impl From<&VoteType> for i16 {
-  fn from(value: &VoteType) -> i16 {
-    match value {
-      VoteType::Like => 1,
-      VoteType::Dislike => -1,
-    }
-  }
-}
 
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct Vote {
-  actor: ObjectId<ApubPerson>,
-  to: Vec<Url>,
-  pub(in crate::activities::voting) object: ObjectId<PostOrComment>,
-  cc: Vec<Url>,
-  #[serde(rename = "type")]
-  pub(in crate::activities::voting) kind: VoteType,
-  id: Url,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
+use crate::{
+  activities::{
+    community::{announce::GetCommunity, send_to_community},
+    generate_activity_id,
+    verify_activity,
+    verify_is_public,
+    verify_person_in_community,
+    voting::{vote_comment, vote_post},
+  },
+  activity_lists::AnnouncableActivities,
+  fetcher::object_id::ObjectId,
+  objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::voting::vote::{Vote, VoteType},
+  PostOrComment,
+};
 
 impl Vote {
   pub(in crate::activities::voting) fn new(
diff --git a/crates/apub/src/activity_lists.rs b/crates/apub/src/activity_lists.rs
new file mode 100644
index 00000000..9fd1a9dc
--- /dev/null
+++ b/crates/apub/src/activity_lists.rs
@@ -0,0 +1,113 @@
+use serde::{Deserialize, Serialize};
+
+use lemmy_apub_lib::{
+  traits::{ActivityFields, ActivityHandler, ActorType},
+  verify::verify_urls_match,
+};
+use lemmy_utils::LemmyError;
+use lemmy_websocket::LemmyContext;
+
+use crate::{
+  activities::community::announce::GetCommunity,
+  objects::community::ApubCommunity,
+  protocol::activities::{
+    community::{
+      add_mod::AddMod,
+      announce::AnnounceActivity,
+      block_user::BlockUserFromCommunity,
+      remove_mod::RemoveMod,
+      report::Report,
+      undo_block_user::UndoBlockUserFromCommunity,
+      update::UpdateCommunity,
+    },
+    create_or_update::{comment::CreateOrUpdateComment, post::CreateOrUpdatePost},
+    deletion::{delete::Delete, undo_delete::UndoDelete},
+    following::{
+      accept::AcceptFollowCommunity,
+      follow::FollowCommunity,
+      undo_follow::UndoFollowCommunity,
+    },
+    private_message::{
+      create_or_update::CreateOrUpdatePrivateMessage,
+      delete::DeletePrivateMessage,
+      undo_delete::UndoDeletePrivateMessage,
+    },
+    voting::{undo_vote::UndoVote, vote::Vote},
+  },
+};
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
+#[serde(untagged)]
+#[activity_handler(LemmyContext)]
+pub enum SharedInboxActivities {
+  GroupInboxActivities(GroupInboxActivities),
+  // Note, pm activities need to be at the end, otherwise comments will end up here. We can probably
+  // avoid this problem by replacing createpm.object with our own struct, instead of NoteExt.
+  PersonInboxActivities(PersonInboxActivities),
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
+#[serde(untagged)]
+#[activity_handler(LemmyContext)]
+pub enum GroupInboxActivities {
+  FollowCommunity(FollowCommunity),
+  UndoFollowCommunity(UndoFollowCommunity),
+  AnnouncableActivities(AnnouncableActivities),
+  Report(Report),
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
+#[serde(untagged)]
+#[activity_handler(LemmyContext)]
+pub enum PersonInboxActivities {
+  AcceptFollowCommunity(AcceptFollowCommunity),
+  /// Some activities can also be sent from user to user, eg a comment with mentions
+  AnnouncableActivities(AnnouncableActivities),
+  CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
+  DeletePrivateMessage(DeletePrivateMessage),
+  UndoDeletePrivateMessage(UndoDeletePrivateMessage),
+  AnnounceActivity(Box<AnnounceActivity>),
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
+#[serde(untagged)]
+#[activity_handler(LemmyContext)]
+pub enum AnnouncableActivities {
+  CreateOrUpdateComment(CreateOrUpdateComment),
+  CreateOrUpdatePost(Box<CreateOrUpdatePost>),
+  Vote(Vote),
+  UndoVote(UndoVote),
+  Delete(Delete),
+  UndoDelete(UndoDelete),
+  UpdateCommunity(Box<UpdateCommunity>),
+  BlockUserFromCommunity(BlockUserFromCommunity),
+  UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
+  AddMod(AddMod),
+  RemoveMod(RemoveMod),
+}
+
+#[async_trait::async_trait(?Send)]
+impl GetCommunity for AnnouncableActivities {
+  async fn get_community(
+    &self,
+    context: &LemmyContext,
+    request_counter: &mut i32,
+  ) -> Result<ApubCommunity, LemmyError> {
+    use AnnouncableActivities::*;
+    let community = match self {
+      CreateOrUpdateComment(a) => a.get_community(context, request_counter).await?,
+      CreateOrUpdatePost(a) => a.get_community(context, request_counter).await?,
+      Vote(a) => a.get_community(context, request_counter).await?,
+      UndoVote(a) => a.get_community(context, request_counter).await?,
+      Delete(a) => a.get_community(context, request_counter).await?,
+      UndoDelete(a) => a.get_community(context, request_counter).await?,
+      UpdateCommunity(a) => a.get_community(context, request_counter).await?,
+      BlockUserFromCommunity(a) => a.get_community(context, request_counter).await?,
+      UndoBlockUserFromCommunity(a) => a.get_community(context, request_counter).await?,
+      AddMod(a) => a.get_community(context, request_counter).await?,
+      RemoveMod(a) => a.get_community(context, request_counter).await?,
+    };
+    verify_urls_match(self.actor(), &community.actor_id())?;
+    Ok(community)
+  }
+}
diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs
index cf5ad8b2..451c3fa9 100644
--- a/crates/apub/src/collections/community_outbox.rs
+++ b/crates/apub/src/collections/community_outbox.rs
@@ -1,12 +1,7 @@
-use crate::{
-  activities::{post::create_or_update::CreateOrUpdatePost, CreateOrUpdateType},
-  collections::CommunityContext,
-  generate_outbox_url,
-  objects::{person::ApubPerson, post::ApubPost},
-  protocol::collections::group_outbox::GroupOutbox,
-};
 use activitystreams::collection::kind::OrderedCollectionType;
 use chrono::NaiveDateTime;
+use url::Url;
+
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   data::Data,
@@ -18,7 +13,16 @@ use lemmy_db_schema::{
   traits::Crud,
 };
 use lemmy_utils::LemmyError;
-use url::Url;
+
+use crate::{
+  collections::CommunityContext,
+  generate_outbox_url,
+  objects::{person::ApubPerson, post::ApubPost},
+  protocol::{
+    activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
+    collections::group_outbox::GroupOutbox,
+  },
+};
 
 #[derive(Clone, Debug)]
 pub(crate) struct ApubCommunityOutbox(Vec<ApubPost>);
diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs
index 0a7cb664..ffc76d3f 100644
--- a/crates/apub/src/http/community.rs
+++ b/crates/apub/src/http/community.rs
@@ -1,23 +1,6 @@
-use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
-use log::info;
-use serde::{Deserialize, Serialize};
-
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::{
-  traits::{ActivityFields, ActivityHandler, ActorType, ApubObject},
-  verify::verify_domains_match,
-};
-use lemmy_db_schema::source::community::Community;
-use lemmy_utils::LemmyError;
-use lemmy_websocket::LemmyContext;
-
 use crate::{
-  activities::{
-    community::announce::{AnnouncableActivities, AnnounceActivity, GetCommunity},
-    following::{follow::FollowCommunity, undo::UndoFollowCommunity},
-    report::Report,
-    verify_person_in_community,
-  },
+  activities::{community::announce::GetCommunity, verify_person_in_community},
+  activity_lists::GroupInboxActivities,
   collections::{
     community_moderators::ApubCommunityModerators,
     community_outbox::ApubCommunityOutbox,
@@ -33,8 +16,22 @@ use crate::{
     receive_activity,
   },
   objects::community::ApubCommunity,
-  protocol::collections::group_followers::CommunityFollowers,
+  protocol::{
+    activities::community::announce::AnnounceActivity,
+    collections::group_followers::CommunityFollowers,
+  },
+};
+use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
+use lemmy_api_common::blocking;
+use lemmy_apub_lib::{
+  traits::{ActivityFields, ActorType, ApubObject},
+  verify::verify_domains_match,
 };
+use lemmy_db_schema::source::community::Community;
+use lemmy_utils::LemmyError;
+use lemmy_websocket::LemmyContext;
+use log::info;
+use serde::Deserialize;
 
 #[derive(Deserialize)]
 pub(crate) struct CommunityQuery {
@@ -61,16 +58,6 @@ pub(crate) async fn get_apub_community_http(
   }
 }
 
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
-#[serde(untagged)]
-#[activity_handler(LemmyContext)]
-pub enum GroupInboxActivities {
-  FollowCommunity(FollowCommunity),
-  UndoFollowCommunity(UndoFollowCommunity),
-  AnnouncableActivities(AnnouncableActivities),
-  Report(Report),
-}
-
 /// Handler for all incoming receive to community inboxes.
 pub async fn community_inbox(
   request: HttpRequest,
diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs
index 48956cdf..9c61c274 100644
--- a/crates/apub/src/http/mod.rs
+++ b/crates/apub/src/http/mod.rs
@@ -1,11 +1,9 @@
 use crate::{
+  activity_lists::SharedInboxActivities,
   check_is_apub_id_valid,
   context::WithContext,
   fetcher::get_or_fetch_and_upsert_actor,
-  http::{
-    community::{receive_group_inbox, GroupInboxActivities},
-    person::{receive_person_inbox, PersonInboxActivities},
-  },
+  http::{community::receive_group_inbox, person::receive_person_inbox},
   insert_activity,
 };
 use actix_web::{
@@ -39,16 +37,6 @@ mod person;
 mod post;
 pub mod routes;
 
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
-#[serde(untagged)]
-#[activity_handler(LemmyContext)]
-pub enum SharedInboxActivities {
-  GroupInboxActivities(GroupInboxActivities),
-  // Note, pm activities need to be at the end, otherwise comments will end up here. We can probably
-  // avoid this problem by replacing createpm.object with our own struct, instead of NoteExt.
-  PersonInboxActivities(PersonInboxActivities),
-}
-
 pub async fn shared_inbox(
   request: HttpRequest,
   payload: Payload,
diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs
index 77bfd694..3f18bcc5 100644
--- a/crates/apub/src/http/person.rs
+++ b/crates/apub/src/http/person.rs
@@ -1,23 +1,5 @@
-use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
-use log::info;
-use serde::{Deserialize, Serialize};
-
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ApubObject};
-use lemmy_db_schema::source::person::Person;
-use lemmy_utils::LemmyError;
-use lemmy_websocket::LemmyContext;
-
 use crate::{
-  activities::{
-    community::announce::{AnnouncableActivities, AnnounceActivity},
-    following::accept::AcceptFollowCommunity,
-    private_message::{
-      create_or_update::CreateOrUpdatePrivateMessage,
-      delete::DeletePrivateMessage,
-      undo_delete::UndoDeletePrivateMessage,
-    },
-  },
+  activity_lists::PersonInboxActivities,
   context::WithContext,
   http::{
     create_apub_response,
@@ -28,6 +10,14 @@ use crate::{
   objects::person::ApubPerson,
   protocol::collections::person_outbox::UserOutbox,
 };
+use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
+use lemmy_api_common::blocking;
+use lemmy_apub_lib::traits::ApubObject;
+use lemmy_db_schema::source::person::Person;
+use lemmy_utils::LemmyError;
+use lemmy_websocket::LemmyContext;
+use log::info;
+use serde::Deserialize;
 
 #[derive(Deserialize)]
 pub struct PersonQuery {
@@ -56,19 +46,6 @@ pub(crate) async fn get_apub_person_http(
   }
 }
 
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
-#[serde(untagged)]
-#[activity_handler(LemmyContext)]
-pub enum PersonInboxActivities {
-  AcceptFollowCommunity(AcceptFollowCommunity),
-  /// Some activities can also be sent from user to user, eg a comment with mentions
-  AnnouncableActivities(AnnouncableActivities),
-  CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
-  DeletePrivateMessage(DeletePrivateMessage),
-  UndoDeletePrivateMessage(UndoDeletePrivateMessage),
-  AnnounceActivity(Box<AnnounceActivity>),
-}
-
 pub async fn person_inbox(
   request: HttpRequest,
   payload: Payload,
diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs
index d188274b..f38a9f86 100644
--- a/crates/apub/src/lib.rs
+++ b/crates/apub/src/lib.rs
@@ -1,11 +1,12 @@
 pub mod activities;
+pub(crate) mod activity_lists;
 pub(crate) mod collections;
 mod context;
 pub mod fetcher;
 pub mod http;
 pub mod migrations;
 pub mod objects;
-pub(crate) mod protocol;
+pub mod protocol;
 
 #[macro_use]
 extern crate lazy_static;
diff --git a/crates/apub/src/protocol/activities/community/add_mod.rs b/crates/apub/src/protocol/activities/community/add_mod.rs
new file mode 100644
index 00000000..74ec4645
--- /dev/null
+++ b/crates/apub/src/protocol/activities/community/add_mod.rs
@@ -0,0 +1,20 @@
+use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson};
+use activitystreams::{activity::kind::AddType, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct AddMod {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: Vec<Url>,
+  pub(crate) object: ObjectId<ApubPerson>,
+  pub(crate) target: Url,
+  pub(crate) cc: Vec<Url>,
+  #[serde(rename = "type")]
+  pub(crate) kind: AddType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/community/announce.rs b/crates/apub/src/protocol/activities/community/announce.rs
new file mode 100644
index 00000000..2f4e9bd2
--- /dev/null
+++ b/crates/apub/src/protocol/activities/community/announce.rs
@@ -0,0 +1,23 @@
+use crate::{
+  activity_lists::AnnouncableActivities,
+  fetcher::object_id::ObjectId,
+  objects::community::ApubCommunity,
+};
+use activitystreams::{activity::kind::AnnounceType, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct AnnounceActivity {
+  pub(crate) actor: ObjectId<ApubCommunity>,
+  pub(crate) to: Vec<Url>,
+  pub(crate) object: AnnouncableActivities,
+  pub(crate) cc: Vec<Url>,
+  #[serde(rename = "type")]
+  pub(crate) kind: AnnounceType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/community/block_user.rs b/crates/apub/src/protocol/activities/community/block_user.rs
new file mode 100644
index 00000000..4ede06ae
--- /dev/null
+++ b/crates/apub/src/protocol/activities/community/block_user.rs
@@ -0,0 +1,23 @@
+use crate::{
+  fetcher::object_id::ObjectId,
+  objects::{community::ApubCommunity, person::ApubPerson},
+};
+use activitystreams::{activity::kind::BlockType, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct BlockUserFromCommunity {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: Vec<Url>,
+  pub(crate) object: ObjectId<ApubPerson>,
+  pub(crate) cc: Vec<Url>,
+  pub(crate) target: ObjectId<ApubCommunity>,
+  #[serde(rename = "type")]
+  pub(crate) kind: BlockType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/community/mod.rs b/crates/apub/src/protocol/activities/community/mod.rs
new file mode 100644
index 00000000..62f8329b
--- /dev/null
+++ b/crates/apub/src/protocol/activities/community/mod.rs
@@ -0,0 +1,7 @@
+pub mod add_mod;
+pub mod announce;
+pub mod block_user;
+pub mod remove_mod;
+pub mod report;
+pub mod undo_block_user;
+pub mod update;
diff --git a/crates/apub/src/protocol/activities/community/remove_mod.rs b/crates/apub/src/protocol/activities/community/remove_mod.rs
new file mode 100644
index 00000000..db30ddbe
--- /dev/null
+++ b/crates/apub/src/protocol/activities/community/remove_mod.rs
@@ -0,0 +1,20 @@
+use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson};
+use activitystreams::{activity::kind::RemoveType, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct RemoveMod {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: Vec<Url>,
+  pub(crate) object: ObjectId<ApubPerson>,
+  pub(crate) cc: Vec<Url>,
+  #[serde(rename = "type")]
+  pub(crate) kind: RemoveType,
+  pub(crate) target: Url,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/community/report.rs b/crates/apub/src/protocol/activities/community/report.rs
new file mode 100644
index 00000000..5efdd792
--- /dev/null
+++ b/crates/apub/src/protocol/activities/community/report.rs
@@ -0,0 +1,22 @@
+use crate::{
+  fetcher::{object_id::ObjectId, post_or_comment::PostOrComment},
+  objects::{community::ApubCommunity, person::ApubPerson},
+};
+use activitystreams::{activity::kind::FlagType, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct Report {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: [ObjectId<ApubCommunity>; 1],
+  pub(crate) object: ObjectId<PostOrComment>,
+  pub(crate) summary: String,
+  #[serde(rename = "type")]
+  pub(crate) kind: FlagType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/community/undo_block_user.rs b/crates/apub/src/protocol/activities/community/undo_block_user.rs
new file mode 100644
index 00000000..0e89f87e
--- /dev/null
+++ b/crates/apub/src/protocol/activities/community/undo_block_user.rs
@@ -0,0 +1,23 @@
+use crate::{
+  fetcher::object_id::ObjectId,
+  objects::person::ApubPerson,
+  protocol::activities::community::block_user::BlockUserFromCommunity,
+};
+use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct UndoBlockUserFromCommunity {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: Vec<Url>,
+  pub(crate) object: BlockUserFromCommunity,
+  pub(crate) cc: Vec<Url>,
+  #[serde(rename = "type")]
+  pub(crate) kind: UndoType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/community/update.rs b/crates/apub/src/protocol/activities/community/update.rs
new file mode 100644
index 00000000..4ba1ed84
--- /dev/null
+++ b/crates/apub/src/protocol/activities/community/update.rs
@@ -0,0 +1,26 @@
+use crate::{
+  fetcher::object_id::ObjectId,
+  objects::person::ApubPerson,
+  protocol::objects::group::Group,
+};
+use activitystreams::{activity::kind::UpdateType, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+/// This activity is received from a remote community mod, and updates the description or other
+/// fields of a local community.
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct UpdateCommunity {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: Vec<Url>,
+  // TODO: would be nice to use a separate struct here, which only contains the fields updated here
+  pub(crate) object: Group,
+  pub(crate) cc: Vec<Url>,
+  #[serde(rename = "type")]
+  pub(crate) kind: UpdateType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/create_or_update/comment.rs b/crates/apub/src/protocol/activities/create_or_update/comment.rs
new file mode 100644
index 00000000..ede7417b
--- /dev/null
+++ b/crates/apub/src/protocol/activities/create_or_update/comment.rs
@@ -0,0 +1,25 @@
+use crate::{
+  fetcher::object_id::ObjectId,
+  objects::person::ApubPerson,
+  protocol::{activities::CreateOrUpdateType, objects::note::Note},
+};
+use activitystreams::{link::Mention, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateOrUpdateComment {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: Vec<Url>,
+  pub(crate) object: Note,
+  pub(crate) cc: Vec<Url>,
+  #[serde(default)]
+  pub(crate) tag: Vec<Mention>,
+  #[serde(rename = "type")]
+  pub(crate) kind: CreateOrUpdateType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/create_or_update/mod.rs b/crates/apub/src/protocol/activities/create_or_update/mod.rs
new file mode 100644
index 00000000..38203b5e
--- /dev/null
+++ b/crates/apub/src/protocol/activities/create_or_update/mod.rs
@@ -0,0 +1,2 @@
+pub mod comment;
+pub mod post;
diff --git a/crates/apub/src/protocol/activities/create_or_update/post.rs b/crates/apub/src/protocol/activities/create_or_update/post.rs
new file mode 100644
index 00000000..03b283e3
--- /dev/null
+++ b/crates/apub/src/protocol/activities/create_or_update/post.rs
@@ -0,0 +1,23 @@
+use crate::{
+  fetcher::object_id::ObjectId,
+  objects::person::ApubPerson,
+  protocol::{activities::CreateOrUpdateType, objects::page::Page},
+};
+use activitystreams::unparsed::Unparsed;
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateOrUpdatePost {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: Vec<Url>,
+  pub(crate) object: Page,
+  pub(crate) cc: Vec<Url>,
+  #[serde(rename = "type")]
+  pub(crate) kind: CreateOrUpdateType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/deletion/delete.rs b/crates/apub/src/protocol/activities/deletion/delete.rs
new file mode 100644
index 00000000..f8e81b47
--- /dev/null
+++ b/crates/apub/src/protocol/activities/deletion/delete.rs
@@ -0,0 +1,24 @@
+use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson};
+use activitystreams::{activity::kind::DeleteType, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use serde_with::skip_serializing_none;
+use url::Url;
+
+#[skip_serializing_none]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct Delete {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: Vec<Url>,
+  pub(crate) object: Url,
+  pub(crate) cc: Vec<Url>,
+  #[serde(rename = "type")]
+  pub(crate) kind: DeleteType,
+  /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
+  /// deleting their own content.
+  pub(crate) summary: Option<String>,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/deletion/mod.rs b/crates/apub/src/protocol/activities/deletion/mod.rs
new file mode 100644
index 00000000..b440edd6
--- /dev/null
+++ b/crates/apub/src/protocol/activities/deletion/mod.rs
@@ -0,0 +1,2 @@
+pub mod delete;
+pub mod undo_delete;
diff --git a/crates/apub/src/protocol/activities/deletion/undo_delete.rs b/crates/apub/src/protocol/activities/deletion/undo_delete.rs
new file mode 100644
index 00000000..d962820b
--- /dev/null
+++ b/crates/apub/src/protocol/activities/deletion/undo_delete.rs
@@ -0,0 +1,25 @@
+use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+use lemmy_apub_lib::traits::ActivityFields;
+
+use crate::{
+  fetcher::object_id::ObjectId,
+  objects::person::ApubPerson,
+  protocol::activities::deletion::delete::Delete,
+};
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct UndoDelete {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: Vec<Url>,
+  pub(crate) object: Delete,
+  pub(crate) cc: Vec<Url>,
+  #[serde(rename = "type")]
+  pub(crate) kind: UndoType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/following/accept.rs b/crates/apub/src/protocol/activities/following/accept.rs
new file mode 100644
index 00000000..502a908c
--- /dev/null
+++ b/crates/apub/src/protocol/activities/following/accept.rs
@@ -0,0 +1,22 @@
+use crate::{
+  fetcher::object_id::ObjectId,
+  objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::following::follow::FollowCommunity,
+};
+use activitystreams::{activity::kind::AcceptType, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct AcceptFollowCommunity {
+  pub(crate) actor: ObjectId<ApubCommunity>,
+  pub(crate) to: [ObjectId<ApubPerson>; 1],
+  pub(crate) object: FollowCommunity,
+  #[serde(rename = "type")]
+  pub(crate) kind: AcceptType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/following/follow.rs b/crates/apub/src/protocol/activities/following/follow.rs
new file mode 100644
index 00000000..9dfec216
--- /dev/null
+++ b/crates/apub/src/protocol/activities/following/follow.rs
@@ -0,0 +1,21 @@
+use crate::{
+  fetcher::object_id::ObjectId,
+  objects::{community::ApubCommunity, person::ApubPerson},
+};
+use activitystreams::{activity::kind::FollowType, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct FollowCommunity {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: [ObjectId<ApubCommunity>; 1],
+  pub(crate) object: ObjectId<ApubCommunity>,
+  #[serde(rename = "type")]
+  pub(crate) kind: FollowType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/following/mod.rs b/crates/apub/src/protocol/activities/following/mod.rs
new file mode 100644
index 00000000..5dc6a3f8
--- /dev/null
+++ b/crates/apub/src/protocol/activities/following/mod.rs
@@ -0,0 +1,3 @@
+pub(crate) mod accept;
+pub mod follow;
+pub mod undo_follow;
diff --git a/crates/apub/src/protocol/activities/following/undo_follow.rs b/crates/apub/src/protocol/activities/following/undo_follow.rs
new file mode 100644
index 00000000..be6a7ab8
--- /dev/null
+++ b/crates/apub/src/protocol/activities/following/undo_follow.rs
@@ -0,0 +1,22 @@
+use crate::{
+  fetcher::object_id::ObjectId,
+  objects::{community::ApubCommunity, person::ApubPerson},
+  protocol::activities::following::follow::FollowCommunity,
+};
+use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct UndoFollowCommunity {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: [ObjectId<ApubCommunity>; 1],
+  pub(crate) object: FollowCommunity,
+  #[serde(rename = "type")]
+  pub(crate) kind: UndoType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/mod.rs b/crates/apub/src/protocol/activities/mod.rs
new file mode 100644
index 00000000..23575c27
--- /dev/null
+++ b/crates/apub/src/protocol/activities/mod.rs
@@ -0,0 +1,15 @@
+use serde::{Deserialize, Serialize};
+use strum_macros::ToString;
+
+pub mod community;
+pub mod create_or_update;
+pub mod deletion;
+pub mod following;
+pub mod private_message;
+pub mod voting;
+
+#[derive(Clone, Debug, ToString, Deserialize, Serialize)]
+pub enum CreateOrUpdateType {
+  Create,
+  Update,
+}
diff --git a/crates/apub/src/protocol/activities/private_message/create_or_update.rs b/crates/apub/src/protocol/activities/private_message/create_or_update.rs
new file mode 100644
index 00000000..7632ef9f
--- /dev/null
+++ b/crates/apub/src/protocol/activities/private_message/create_or_update.rs
@@ -0,0 +1,22 @@
+use crate::{
+  fetcher::object_id::ObjectId,
+  objects::person::ApubPerson,
+  protocol::{activities::CreateOrUpdateType, objects::chat_message::ChatMessage},
+};
+use activitystreams::unparsed::Unparsed;
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateOrUpdatePrivateMessage {
+  pub(crate) id: Url,
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: [ObjectId<ApubPerson>; 1],
+  pub(crate) object: ChatMessage,
+  #[serde(rename = "type")]
+  pub(crate) kind: CreateOrUpdateType,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/private_message/delete.rs b/crates/apub/src/protocol/activities/private_message/delete.rs
new file mode 100644
index 00000000..499d7d1d
--- /dev/null
+++ b/crates/apub/src/protocol/activities/private_message/delete.rs
@@ -0,0 +1,21 @@
+use crate::{
+  fetcher::object_id::ObjectId,
+  objects::{person::ApubPerson, private_message::ApubPrivateMessage},
+};
+use activitystreams::{activity::kind::DeleteType, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct DeletePrivateMessage {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: [ObjectId<ApubPerson>; 1],
+  pub(crate) object: ObjectId<ApubPrivateMessage>,
+  #[serde(rename = "type")]
+  pub(crate) kind: DeleteType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/private_message/mod.rs b/crates/apub/src/protocol/activities/private_message/mod.rs
new file mode 100644
index 00000000..4bcda7c7
--- /dev/null
+++ b/crates/apub/src/protocol/activities/private_message/mod.rs
@@ -0,0 +1,3 @@
+pub mod create_or_update;
+pub mod delete;
+pub mod undo_delete;
diff --git a/crates/apub/src/protocol/activities/private_message/undo_delete.rs b/crates/apub/src/protocol/activities/private_message/undo_delete.rs
new file mode 100644
index 00000000..699f6eec
--- /dev/null
+++ b/crates/apub/src/protocol/activities/private_message/undo_delete.rs
@@ -0,0 +1,22 @@
+use crate::{
+  fetcher::object_id::ObjectId,
+  objects::person::ApubPerson,
+  protocol::activities::private_message::delete::DeletePrivateMessage,
+};
+use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
+use lemmy_apub_lib::traits::ActivityFields;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct UndoDeletePrivateMessage {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: [ObjectId<ApubPerson>; 1],
+  pub(crate) object: DeletePrivateMessage,
+  #[serde(rename = "type")]
+  pub(crate) kind: UndoType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/voting/mod.rs b/crates/apub/src/protocol/activities/voting/mod.rs
new file mode 100644
index 00000000..4583c231
--- /dev/null
+++ b/crates/apub/src/protocol/activities/voting/mod.rs
@@ -0,0 +1,2 @@
+pub mod undo_vote;
+pub mod vote;
diff --git a/crates/apub/src/protocol/activities/voting/undo_vote.rs b/crates/apub/src/protocol/activities/voting/undo_vote.rs
new file mode 100644
index 00000000..0d3e6636
--- /dev/null
+++ b/crates/apub/src/protocol/activities/voting/undo_vote.rs
@@ -0,0 +1,25 @@
+use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+use lemmy_apub_lib::traits::ActivityFields;
+
+use crate::{
+  fetcher::object_id::ObjectId,
+  objects::person::ApubPerson,
+  protocol::activities::voting::vote::Vote,
+};
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct UndoVote {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: Vec<Url>,
+  pub(crate) object: Vote,
+  pub(crate) cc: Vec<Url>,
+  #[serde(rename = "type")]
+  pub(crate) kind: UndoType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
diff --git a/crates/apub/src/protocol/activities/voting/vote.rs b/crates/apub/src/protocol/activities/voting/vote.rs
new file mode 100644
index 00000000..fdc87a3b
--- /dev/null
+++ b/crates/apub/src/protocol/activities/voting/vote.rs
@@ -0,0 +1,53 @@
+use crate::{
+  fetcher::{object_id::ObjectId, post_or_comment::PostOrComment},
+  objects::person::ApubPerson,
+};
+use activitystreams::unparsed::Unparsed;
+use anyhow::anyhow;
+use lemmy_apub_lib::traits::ActivityFields;
+use lemmy_utils::LemmyError;
+use serde::{Deserialize, Serialize};
+use std::convert::TryFrom;
+use strum_macros::ToString;
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct Vote {
+  pub(crate) actor: ObjectId<ApubPerson>,
+  pub(crate) to: Vec<Url>,
+  pub(crate) object: ObjectId<PostOrComment>,
+  pub(crate) cc: Vec<Url>,
+  #[serde(rename = "type")]
+  pub(crate) kind: VoteType,
+  pub(crate) id: Url,
+  #[serde(flatten)]
+  pub(crate) unparsed: Unparsed,
+}
+
+#[derive(Clone, Debug, ToString, Deserialize, Serialize)]
+pub enum VoteType {
+  Like,
+  Dislike,
+}
+
+impl TryFrom<i16> for VoteType {
+  type Error = LemmyError;
+
+  fn try_from(value: i16) -> Result<Self, Self::Error> {
+    match value {
+      1 => Ok(VoteType::Like),
+      -1 => Ok(VoteType::Dislike),
+      _ => Err(anyhow!("invalid vote value").into()),
+    }
+  }
+}
+
+impl From<&VoteType> for i16 {
+  fn from(value: &VoteType) -> i16 {
+    match value {
+      VoteType::Like => 1,
+      VoteType::Dislike => -1,
+    }
+  }
+}
diff --git a/crates/apub/src/protocol/collections/group_outbox.rs b/crates/apub/src/protocol/collections/group_outbox.rs
index 26da4b6f..3b295003 100644
--- a/crates/apub/src/protocol/collections/group_outbox.rs
+++ b/crates/apub/src/protocol/collections/group_outbox.rs
@@ -1,4 +1,4 @@
-use crate::activities::post::create_or_update::CreateOrUpdatePost;
+use crate::protocol::activities::create_or_update::post::CreateOrUpdatePost;
 use activitystreams::collection::kind::OrderedCollectionType;
 use serde::{Deserialize, Serialize};
 use url::Url;
diff --git a/crates/apub/src/protocol/mod.rs b/crates/apub/src/protocol/mod.rs
index f4ad9e23..f587dba7 100644
--- a/crates/apub/src/protocol/mod.rs
+++ b/crates/apub/src/protocol/mod.rs
@@ -4,6 +4,7 @@ use url::Url;
 
 use lemmy_apub_lib::values::MediaTypeMarkdown;
 
+pub mod activities;
 pub(crate) mod collections;
 pub(crate) mod objects;
 
-- 
2.44.1