get_local_user_view_from_jwt,
is_mod_or_admin,
};
-use lemmy_apub::{
- activities::following::{
- follow::FollowCommunity as FollowCommunityApub,
- undo::UndoFollowCommunity,
+use lemmy_apub::activities::{
+ community::{
+ add_mod::AddMod,
+ block_user::BlockUserFromCommunity,
+ remove_mod::RemoveMod,
+ undo_block_user::UndoBlockUserFromCommunity,
},
- CommunityType,
+ following::{follow::FollowCommunity as FollowCommunityApub, undo::UndoFollowCommunity},
};
use lemmy_db_queries::{
source::{comment::Comment_, community::CommunityModerator_, post::Post_},
.await?
.ok();
- community
- .send_block_user(&local_user_view.person, banned_person, context)
+ BlockUserFromCommunity::send(&community, &banned_person, &local_user_view.person, context)
.await?;
} else {
let unban = move |conn: &'_ _| CommunityPersonBan::unban(conn, &community_user_ban_form);
if blocking(context.pool(), unban).await?.is_err() {
return Err(ApiError::err("community_user_already_banned").into());
}
- community
- .send_undo_block_user(&local_user_view.person, banned_person, context)
- .await?;
+ UndoBlockUserFromCommunity::send(
+ &community,
+ &banned_person,
+ &local_user_view.person,
+ context,
+ )
+ .await?;
}
// Remove/Restore their data if that's desired
})
.await??;
if data.added {
- community
- .send_add_mod(&local_user_view.person, updated_mod, context)
- .await?;
+ AddMod::send(&community, &updated_mod, &local_user_view.person, context).await?;
} else {
- community
- .send_remove_mod(&local_user_view.person, updated_mod, context)
- .await?;
+ RemoveMod::send(&community, &updated_mod, &local_user_view.person, context).await?;
}
// Note: in case a remote mod is added, this returns the old moderators list, it will only get
community::{CommunityResponse, EditCommunity},
get_local_user_view_from_jwt,
};
-use lemmy_apub::CommunityType;
+use lemmy_apub::activities::community::update::UpdateCommunity;
use lemmy_db_queries::{diesel_option_overwrite_to_url, Crud};
use lemmy_db_schema::{
naive_now,
.await?
.map_err(|_| ApiError::err("couldnt_update_community"))?;
- updated_community
- .send_update(local_user_view.person.to_owned(), context)
- .await?;
+ UpdateCommunity::send(&updated_community, &local_user_view.person, context).await?;
let op = UserOperationCrud::EditCommunity;
send_community_ws_message(data.community_id, op, websocket_id, None, context).await
objects::{comment::Note, FromApub, ToApub},
ActorType,
};
-use activitystreams::link::Mention;
+use activitystreams::{base::AnyBase, link::Mention, primitives::OneOrMany, unparsed::Unparsed};
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{
- values::PublicUrl,
- verify_domains_match,
- ActivityCommonFields,
- ActivityHandler,
-};
+use lemmy_apub_lib::{values::PublicUrl, verify_domains_match, ActivityFields, ActivityHandler};
use lemmy_db_queries::Crud;
use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
use lemmy_utils::LemmyError;
use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, Deserialize, Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct CreateOrUpdateComment {
+ actor: Url,
to: PublicUrl,
object: Note,
cc: Vec<Url>,
tag: Vec<Mention>,
#[serde(rename = "type")]
kind: CreateOrUpdateType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- common: ActivityCommonFields,
+ unparsed: Unparsed,
}
impl CreateOrUpdateComment {
let maa = collect_non_local_mentions(comment, &community, context).await?;
let create_or_update = CreateOrUpdateComment {
+ actor: actor.actor_id(),
to: PublicUrl::Public,
object: comment.to_apub(context.pool()).await?,
cc: maa.ccs,
tag: maa.tags,
kind,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: id.clone(),
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
+ id: id.clone(),
+ context: lemmy_context(),
+ unparsed: Default::default(),
};
let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update);
) -> Result<(), LemmyError> {
let community = extract_community(&self.cc, context, request_counter).await?;
- verify_activity(self.common())?;
- verify_person_in_community(
- &self.common.actor,
- &community.actor_id(),
- context,
- request_counter,
- )
- .await?;
- verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
+ verify_activity(self)?;
+ verify_person_in_community(&self.actor, &community.actor_id(), context, request_counter)
+ .await?;
+ verify_domains_match(&self.actor, self.object.id_unchecked())?;
// TODO: should add a check that the correct community is in cc (probably needs changes to
// comment deserialization)
self.object.verify(context, request_counter).await?;
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let comment =
- Comment::from_apub(&self.object, context, &self.common.actor, request_counter).await?;
- let recipients =
- get_notif_recipients(&self.common.actor, &comment, context, request_counter).await?;
+ let comment = Comment::from_apub(&self.object, context, &self.actor, request_counter).await?;
+ let recipients = get_notif_recipients(&self.actor, &comment, context, request_counter).await?;
let notif_type = match self.kind {
CreateOrUpdateType::Create => UserOperationCrud::CreateComment,
CreateOrUpdateType::Update => UserOperationCrud::EditComment,
.await?;
Ok(())
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
use crate::{
activities::{
+ community::announce::AnnouncableActivities,
+ generate_activity_id,
verify_activity,
verify_add_remove_moderator_target,
verify_mod_action,
verify_person_in_community,
},
+ activity_queue::send_to_community_new,
+ extensions::context::lemmy_context,
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
- CommunityType,
+ generate_moderators_url,
+ ActorType,
+};
+use activitystreams::{
+ activity::kind::AddType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
};
-use activitystreams::{activity::kind::AddType, base::AnyBase};
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
use lemmy_db_queries::{source::community::CommunityModerator_, Joinable};
-use lemmy_db_schema::source::community::{CommunityModerator, CommunityModeratorForm};
+use lemmy_db_schema::source::{
+ community::{Community, CommunityModerator, CommunityModeratorForm},
+ person::Person,
+};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct AddMod {
+ actor: Url,
to: PublicUrl,
object: Url,
target: Url,
cc: [Url; 1],
#[serde(rename = "type")]
kind: AddType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- common: ActivityCommonFields,
+ unparsed: Unparsed,
+}
+
+impl AddMod {
+ pub async fn send(
+ community: &Community,
+ added_mod: &Person,
+ actor: &Person,
+ context: &LemmyContext,
+ ) -> Result<(), LemmyError> {
+ let id = generate_activity_id(AddType::Add)?;
+ let add = AddMod {
+ actor: actor.actor_id(),
+ to: PublicUrl::Public,
+ object: added_mod.actor_id(),
+ target: generate_moderators_url(&community.actor_id)?.into(),
+ cc: [community.actor_id()],
+ kind: AddType::Add,
+ id: id.clone(),
+ context: lemmy_context(),
+ unparsed: Default::default(),
+ };
+
+ let activity = AnnouncableActivities::AddMod(add);
+ let inboxes = vec![added_mod.get_shared_inbox_or_inbox_url()];
+ send_to_community_new(activity, &id, actor, community, inboxes, context).await
+ }
}
#[async_trait::async_trait(?Send)]
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
- verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
- verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
+ verify_activity(self)?;
+ verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
+ verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
verify_add_remove_moderator_target(&self.target, self.cc[0].clone())?;
Ok(())
}
})
.await??;
}
- if community.local {
- let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(&self)?)?;
- community
- .send_announce(anybase, Some(self.object.clone()), context)
- .await?;
- }
// TODO: send websocket notification about added mod
Ok(())
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
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},
generate_activity_id,
post::create_or_update::CreateOrUpdatePost,
- removal::{remove::RemoveMod, undo_remove::UndoRemovePostCommentOrCommunity},
+ undo_remove::UndoRemovePostCommentOrCommunity,
verify_activity,
verify_community,
voting::{undo_vote::UndoVote, vote::Vote},
ActorType,
CommunityType,
};
-use activitystreams::activity::kind::AnnounceType;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use activitystreams::{
+ activity::kind::AnnounceType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
+};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
use lemmy_db_schema::source::community::Community;
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
#[serde(untagged)]
pub enum AnnouncableActivities {
CreateOrUpdateComment(CreateOrUpdateComment),
Delete(Delete),
UndoDelete(UndoDelete),
UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity),
+ UpdateCommunity(Box<UpdateCommunity>),
BlockUserFromCommunity(BlockUserFromCommunity),
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
AddMod(AddMod),
RemoveMod(RemoveMod),
}
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct AnnounceActivity {
+ actor: Url,
to: PublicUrl,
object: AnnouncableActivities,
cc: Vec<Url>,
#[serde(rename = "type")]
kind: AnnounceType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- common: ActivityCommonFields,
+ unparsed: Unparsed,
}
impl AnnounceActivity {
context: &LemmyContext,
) -> Result<(), LemmyError> {
let announce = AnnounceActivity {
+ actor: community.actor_id(),
to: PublicUrl::Public,
object,
cc: vec![community.followers_url()],
kind: AnnounceType::Announce,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: generate_activity_id(&AnnounceType::Announce)?,
- actor: community.actor_id(),
- unparsed: Default::default(),
- },
+ id: generate_activity_id(&AnnounceType::Announce)?,
+ context: lemmy_context(),
+ unparsed: Default::default(),
};
let inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?;
- send_activity_new(
- context,
- &announce,
- &announce.common.id,
- community,
- inboxes,
- false,
- )
- .await
+ send_activity_new(context, &announce, &announce.id, community, inboxes, false).await
}
}
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
- verify_community(&self.common.actor, context, request_counter).await?;
+ verify_activity(self)?;
+ verify_community(&self.actor, context, request_counter).await?;
self.object.verify(context, request_counter).await?;
Ok(())
}
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- if is_activity_already_known(context.pool(), self.object.common().id_unchecked()).await? {
+ if is_activity_already_known(context.pool(), self.object.id_unchecked()).await? {
return Ok(());
}
insert_activity(
- self.object.common().id_unchecked(),
+ self.object.id_unchecked(),
self.object.clone(),
false,
true,
.await?;
self.object.receive(context, request_counter).await
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
use crate::{
- activities::{verify_activity, verify_mod_action, verify_person_in_community},
+ activities::{
+ community::announce::AnnouncableActivities,
+ generate_activity_id,
+ verify_activity,
+ verify_mod_action,
+ verify_person_in_community,
+ },
+ activity_queue::send_to_community_new,
+ extensions::context::lemmy_context,
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
+ ActorType,
+};
+use activitystreams::{
+ activity::kind::BlockType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
};
-use activitystreams::activity::kind::BlockType;
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
use lemmy_db_queries::{Bannable, Followable};
-use lemmy_db_schema::source::community::{
- CommunityFollower,
- CommunityFollowerForm,
- CommunityPersonBan,
- CommunityPersonBanForm,
+use lemmy_db_schema::source::{
+ community::{
+ Community,
+ CommunityFollower,
+ CommunityFollowerForm,
+ CommunityPersonBan,
+ CommunityPersonBanForm,
+ },
+ person::Person,
};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct BlockUserFromCommunity {
+ actor: Url,
to: PublicUrl,
pub(in crate::activities::community) object: Url,
cc: [Url; 1],
#[serde(rename = "type")]
kind: BlockType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- common: ActivityCommonFields,
+ unparsed: Unparsed,
+}
+
+impl BlockUserFromCommunity {
+ pub(in crate::activities::community) fn new(
+ community: &Community,
+ target: &Person,
+ actor: &Person,
+ ) -> Result<BlockUserFromCommunity, LemmyError> {
+ Ok(BlockUserFromCommunity {
+ actor: actor.actor_id(),
+ to: PublicUrl::Public,
+ object: target.actor_id(),
+ cc: [community.actor_id()],
+ kind: BlockType::Block,
+ id: generate_activity_id(BlockType::Block)?,
+ context: lemmy_context(),
+ unparsed: Default::default(),
+ })
+ }
+
+ pub async fn send(
+ community: &Community,
+ target: &Person,
+ actor: &Person,
+ context: &LemmyContext,
+ ) -> Result<(), LemmyError> {
+ let block = BlockUserFromCommunity::new(community, target, actor)?;
+ let block_id = block.id.clone();
+
+ let activity = AnnouncableActivities::BlockUserFromCommunity(block);
+ let inboxes = vec![target.get_shared_inbox_or_inbox_url()];
+ send_to_community_new(activity, &block_id, actor, community, inboxes, context).await
+ }
}
#[async_trait::async_trait(?Send)]
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
- verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
- verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
+ verify_activity(self)?;
+ verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
+ verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
Ok(())
}
Ok(())
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
pub mod add_mod;
pub mod announce;
pub mod block_user;
+pub mod remove_mod;
pub mod undo_block_user;
pub mod update;
--- /dev/null
+use crate::{
+ activities::{
+ community::announce::AnnouncableActivities,
+ deletion::{delete::receive_remove_action, verify_delete_activity},
+ generate_activity_id,
+ verify_activity,
+ verify_add_remove_moderator_target,
+ verify_mod_action,
+ verify_person_in_community,
+ },
+ activity_queue::send_to_community_new,
+ extensions::context::lemmy_context,
+ fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
+ generate_moderators_url,
+ ActorType,
+};
+use activitystreams::{
+ activity::kind::RemoveType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
+};
+use lemmy_api_common::blocking;
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
+use lemmy_db_queries::Joinable;
+use lemmy_db_schema::source::{
+ community::{Community, CommunityModerator, CommunityModeratorForm},
+ person::Person,
+};
+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: Url,
+ to: PublicUrl,
+ pub(in crate::activities) object: Url,
+ cc: [Url; 1],
+ #[serde(rename = "type")]
+ kind: RemoveType,
+ // if target is set, this is means remove mod from community
+ pub(in crate::activities) target: Option<Url>,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
+ #[serde(flatten)]
+ unparsed: Unparsed,
+}
+
+impl RemoveMod {
+ pub async fn send(
+ community: &Community,
+ removed_mod: &Person,
+ actor: &Person,
+ context: &LemmyContext,
+ ) -> Result<(), LemmyError> {
+ let id = generate_activity_id(RemoveType::Remove)?;
+ let remove = RemoveMod {
+ actor: actor.actor_id(),
+ to: PublicUrl::Public,
+ object: removed_mod.actor_id(),
+ target: Some(generate_moderators_url(&community.actor_id)?.into()),
+ id: id.clone(),
+ context: lemmy_context(),
+ cc: [community.actor_id()],
+ kind: RemoveType::Remove,
+ unparsed: Default::default(),
+ };
+
+ let activity = AnnouncableActivities::RemoveMod(remove);
+ let inboxes = vec![removed_mod.get_shared_inbox_or_inbox_url()];
+ send_to_community_new(activity, &id, actor, community, inboxes, context).await
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl ActivityHandler for RemoveMod {
+ async fn verify(
+ &self,
+ context: &LemmyContext,
+ request_counter: &mut i32,
+ ) -> Result<(), LemmyError> {
+ verify_activity(self)?;
+ if let Some(target) = &self.target {
+ verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
+ verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
+ verify_add_remove_moderator_target(target, self.cc[0].clone())?;
+ } else {
+ verify_delete_activity(
+ &self.object,
+ self,
+ &self.cc[0],
+ true,
+ context,
+ request_counter,
+ )
+ .await?;
+ }
+ Ok(())
+ }
+
+ async fn receive(
+ self,
+ context: &LemmyContext,
+ request_counter: &mut i32,
+ ) -> Result<(), LemmyError> {
+ if self.target.is_some() {
+ let community =
+ get_or_fetch_and_upsert_community(&self.cc[0], context, request_counter).await?;
+ let remove_mod =
+ get_or_fetch_and_upsert_person(&self.object, context, request_counter).await?;
+
+ let form = CommunityModeratorForm {
+ community_id: community.id,
+ person_id: remove_mod.id,
+ };
+ blocking(context.pool(), move |conn| {
+ CommunityModerator::leave(conn, &form)
+ })
+ .await??;
+ // TODO: send websocket notification about removed mod
+ Ok(())
+ } else {
+ receive_remove_action(&self.actor, &self.object, None, context, request_counter).await
+ }
+ }
+}
use crate::{
activities::{
- community::block_user::BlockUserFromCommunity,
+ community::{announce::AnnouncableActivities, block_user::BlockUserFromCommunity},
+ generate_activity_id,
verify_activity,
verify_mod_action,
verify_person_in_community,
},
+ activity_queue::send_to_community_new,
+ extensions::context::lemmy_context,
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
+ ActorType,
+};
+use activitystreams::{
+ activity::kind::UndoType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
};
-use activitystreams::activity::kind::UndoType;
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
use lemmy_db_queries::Bannable;
-use lemmy_db_schema::source::community::{CommunityPersonBan, CommunityPersonBanForm};
+use lemmy_db_schema::source::{
+ community::{Community, CommunityPersonBan, CommunityPersonBanForm},
+ person::Person,
+};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UndoBlockUserFromCommunity {
+ actor: Url,
to: PublicUrl,
object: BlockUserFromCommunity,
cc: [Url; 1],
#[serde(rename = "type")]
kind: UndoType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- common: ActivityCommonFields,
+ unparsed: Unparsed,
+}
+
+impl UndoBlockUserFromCommunity {
+ pub async fn send(
+ community: &Community,
+ target: &Person,
+ actor: &Person,
+ context: &LemmyContext,
+ ) -> Result<(), LemmyError> {
+ let block = BlockUserFromCommunity::new(community, target, actor)?;
+
+ let id = generate_activity_id(UndoType::Undo)?;
+ let undo = UndoBlockUserFromCommunity {
+ actor: actor.actor_id(),
+ to: PublicUrl::Public,
+ object: block,
+ cc: [community.actor_id()],
+ kind: UndoType::Undo,
+ id: id.clone(),
+ context: lemmy_context(),
+ unparsed: Default::default(),
+ };
+
+ let activity = AnnouncableActivities::UndoBlockUserFromCommunity(undo);
+ let inboxes = vec![target.get_shared_inbox_or_inbox_url()];
+ send_to_community_new(activity, &id, actor, community, inboxes, context).await
+ }
}
#[async_trait::async_trait(?Send)]
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
- verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
- verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
+ verify_activity(self)?;
+ verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
+ verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
self.object.verify(context, request_counter).await?;
Ok(())
}
Ok(())
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
use crate::{
- activities::{verify_activity, verify_mod_action, verify_person_in_community},
- objects::community::Group,
+ activities::{
+ community::announce::AnnouncableActivities,
+ generate_activity_id,
+ verify_activity,
+ verify_mod_action,
+ verify_person_in_community,
+ },
+ activity_queue::send_to_community_new,
+ extensions::context::lemmy_context,
+ objects::{community::Group, ToApub},
+ ActorType,
+};
+use activitystreams::{
+ activity::kind::UpdateType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
};
-use activitystreams::activity::kind::UpdateType;
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
use lemmy_db_queries::{ApubObject, Crud};
-use lemmy_db_schema::source::community::{Community, CommunityForm};
+use lemmy_db_schema::source::{
+ community::{Community, CommunityForm},
+ person::Person,
+};
use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
+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, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UpdateCommunity {
+ actor: Url,
to: PublicUrl,
+ // TODO: would be nice to use a separate struct here, which only contains the fields updated here
object: Group,
cc: [Url; 1],
#[serde(rename = "type")]
kind: UpdateType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- common: ActivityCommonFields,
+ unparsed: Unparsed,
+}
+
+impl UpdateCommunity {
+ pub async fn send(
+ community: &Community,
+ actor: &Person,
+ context: &LemmyContext,
+ ) -> Result<(), LemmyError> {
+ let id = generate_activity_id(UpdateType::Update)?;
+ let update = UpdateCommunity {
+ actor: actor.actor_id(),
+ to: PublicUrl::Public,
+ object: community.to_apub(context.pool()).await?,
+ cc: [community.actor_id()],
+ kind: UpdateType::Update,
+ id: id.clone(),
+ context: lemmy_context(),
+ unparsed: Default::default(),
+ };
+
+ let activity = AnnouncableActivities::UpdateCommunity(Box::new(update));
+ send_to_community_new(activity, &id, actor, community, vec![], context).await
+ }
}
#[async_trait::async_trait(?Send)]
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
- verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
- verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
+ verify_activity(self)?;
+ verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
+ verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
Ok(())
}
.await?;
Ok(())
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
fetcher::person::get_or_fetch_and_upsert_person,
ActorType,
};
-use activitystreams::activity::kind::DeleteType;
+use activitystreams::{
+ activity::kind::DeleteType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
+};
use anyhow::anyhow;
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
use lemmy_db_queries::{
source::{comment::Comment_, community::Community_, post::Post_},
Crud,
LemmyContext,
UserOperationCrud,
};
+use serde::{Deserialize, Serialize};
use url::Url;
/// This is very confusing, because there are four distinct cases to handle:
///
/// 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.
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct Delete {
- pub(in crate::activities::deletion) to: PublicUrl,
+ actor: Url,
+ to: PublicUrl,
pub(in crate::activities::deletion) object: Url,
pub(in crate::activities::deletion) cc: [Url; 1],
#[serde(rename = "type")]
- pub(in crate::activities::deletion) kind: DeleteType,
+ 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(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- pub(in crate::activities::deletion) common: ActivityCommonFields,
+ unparsed: Unparsed,
}
#[async_trait::async_trait(?Send)]
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
+ verify_activity(self)?;
verify_delete_activity(
&self.object,
+ self,
&self.cc[0],
- &self.common,
self.summary.is_some(),
context,
request_counter,
} else {
Some(reason)
};
- receive_remove_action(
- &self.common.actor,
- &self.object,
- reason,
- context,
- request_counter,
- )
- .await
+ receive_remove_action(&self.actor, &self.object, reason, context, request_counter).await
} else {
receive_delete_action(
&self.object,
- &self.common.actor,
+ &self.actor,
WebsocketMessages {
community: UserOperationCrud::DeleteCommunity,
post: UserOperationCrud::DeletePost,
.await
}
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
impl Delete {
- pub(in crate::activities::deletion) async fn send(
+ pub(in crate::activities::deletion) fn new(
actor: &Person,
community: &Community,
object_id: Url,
summary: Option<String>,
- context: &LemmyContext,
- ) -> Result<(), LemmyError> {
- let id = generate_activity_id(DeleteType::Delete)?;
- let delete = Delete {
+ ) -> Result<Delete, LemmyError> {
+ Ok(Delete {
+ actor: actor.actor_id(),
to: PublicUrl::Public,
object: object_id,
cc: [community.actor_id()],
kind: DeleteType::Delete,
summary,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: id.clone(),
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
- };
+ id: generate_activity_id(DeleteType::Delete)?,
+ context: lemmy_context(),
+ unparsed: Default::default(),
+ })
+ }
+ pub(in crate::activities::deletion) async fn send(
+ actor: &Person,
+ community: &Community,
+ object_id: Url,
+ summary: Option<String>,
+ context: &LemmyContext,
+ ) -> Result<(), LemmyError> {
+ let delete = Delete::new(actor, community, object_id, summary)?;
+ let delete_id = delete.id.clone();
let activity = AnnouncableActivities::Delete(delete);
- send_to_community_new(activity, &id, actor, community, vec![], context).await
+ send_to_community_new(activity, &delete_id, actor, community, vec![], context).await
}
}
ActorType,
};
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields};
+use lemmy_apub_lib::{verify_domains_match, ActivityFields};
use lemmy_db_queries::{
source::{comment::Comment_, community::Community_, post::Post_},
ApubObject,
pub(in crate::activities) async fn verify_delete_activity(
object: &Url,
- cc: &Url,
- common: &ActivityCommonFields,
+ activity: &dyn ActivityFields,
+ community_id: &Url,
is_mod_action: bool,
context: &LemmyContext,
request_counter: &mut i32,
if c.local {
// can only do this check for local community, in remote case it would try to fetch the
// deleted community (which fails)
- verify_person_in_community(&common.actor, cc, context, request_counter).await?;
+ verify_person_in_community(activity.actor(), community_id, context, request_counter)
+ .await?;
}
// community deletion is always a mod (or admin) action
- verify_mod_action(&common.actor, c.actor_id(), context).await?;
+ verify_mod_action(activity.actor(), c.actor_id(), context).await?;
}
DeletableObjects::Post(p) => {
verify_delete_activity_post_or_comment(
- cc,
- common,
+ activity,
&p.ap_id.into(),
+ community_id,
is_mod_action,
context,
request_counter,
}
DeletableObjects::Comment(c) => {
verify_delete_activity_post_or_comment(
- cc,
- common,
+ activity,
&c.ap_id.into(),
+ community_id,
is_mod_action,
context,
request_counter,
}
async fn verify_delete_activity_post_or_comment(
- cc: &Url,
- common: &ActivityCommonFields,
+ activity: &dyn ActivityFields,
object_id: &Url,
+ community_id: &Url,
is_mod_action: bool,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_person_in_community(&common.actor, cc, context, request_counter).await?;
+ verify_person_in_community(activity.actor(), community_id, context, request_counter).await?;
if is_mod_action {
- verify_mod_action(&common.actor, cc.clone(), context).await?;
+ verify_mod_action(activity.actor(), community_id.clone(), context).await?;
} else {
// domain of post ap_id and post.creator ap_id are identical, so we just check the former
- verify_domains_match(&common.actor, object_id)?;
+ verify_domains_match(activity.actor(), object_id)?;
}
Ok(())
}
extensions::context::lemmy_context,
ActorType,
};
-use activitystreams::activity::kind::{DeleteType, UndoType};
+use activitystreams::{
+ activity::kind::UndoType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
+};
use anyhow::anyhow;
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_};
use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
use lemmy_utils::LemmyError;
LemmyContext,
UserOperationCrud,
};
+use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UndoDelete {
+ actor: Url,
to: PublicUrl,
object: Delete,
cc: [Url; 1],
#[serde(rename = "type")]
kind: UndoType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- common: ActivityCommonFields,
+ unparsed: Unparsed,
}
#[async_trait::async_trait(?Send)]
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
+ verify_activity(self)?;
self.object.verify(context, request_counter).await?;
verify_delete_activity(
&self.object.object,
+ self,
&self.cc[0],
- &self.common,
self.object.summary.is_some(),
context,
request_counter,
} else {
receive_delete_action(
&self.object.object,
- &self.common.actor,
+ &self.actor,
WebsocketMessages {
community: UserOperationCrud::EditCommunity,
post: UserOperationCrud::EditPost,
.await
}
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
impl UndoDelete {
summary: Option<String>,
context: &LemmyContext,
) -> Result<(), LemmyError> {
- let delete = Delete {
- to: PublicUrl::Public,
- object: object_id,
- cc: [community.actor_id()],
- kind: DeleteType::Delete,
- summary,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: generate_activity_id(DeleteType::Delete)?,
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
- };
+ let object = Delete::new(actor, community, object_id, summary)?;
let id = generate_activity_id(UndoType::Undo)?;
let undo = UndoDelete {
+ actor: actor.actor_id(),
to: PublicUrl::Public,
- object: delete,
+ object,
cc: [community.actor_id()],
kind: UndoType::Undo,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: id.clone(),
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
+ id: id.clone(),
+ context: lemmy_context(),
+ unparsed: Default::default(),
};
let activity = AnnouncableActivities::UndoDelete(undo);
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
ActorType,
};
-use activitystreams::activity::kind::AcceptType;
+use activitystreams::{
+ activity::kind::AcceptType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
+};
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler};
use lemmy_db_queries::{ApubObject, Followable};
use lemmy_db_schema::source::{
community::{Community, CommunityFollower},
};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct AcceptFollowCommunity {
+ actor: Url,
to: Url,
object: FollowCommunity,
#[serde(rename = "type")]
kind: AcceptType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- common: ActivityCommonFields,
+ unparsed: Unparsed,
}
impl AcceptFollowCommunity {
Community::read_from_apub_id(conn, &community_id.into())
})
.await??;
- let person_id = follow.common.actor.clone();
+ let person_id = follow.actor().clone();
let person = blocking(context.pool(), move |conn| {
Person::read_from_apub_id(conn, &person_id.into())
})
.await??;
- let id = generate_activity_id(AcceptType::Accept)?;
let accept = AcceptFollowCommunity {
+ actor: community.actor_id(),
to: person.actor_id(),
object: follow,
kind: AcceptType::Accept,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: id.clone(),
- actor: community.actor_id(),
- unparsed: Default::default(),
- },
+ id: generate_activity_id(AcceptType::Accept)?,
+ context: lemmy_context(),
+ unparsed: Default::default(),
};
let inbox = vec![person.inbox_url.into()];
- send_activity_new(context, &accept, &id, &community, inbox, true).await
+ send_activity_new(context, &accept, &accept.id, &community, inbox, true).await
}
}
/// Handle accepted follows
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
- verify_urls_match(&self.to, &self.object.common.actor)?;
- verify_urls_match(&self.common.actor, &self.object.to)?;
- verify_community(&self.common.actor, context, request_counter).await?;
+ verify_activity(self)?;
+ verify_urls_match(&self.to, self.object.actor())?;
+ verify_urls_match(&self.actor, &self.object.to)?;
+ verify_community(&self.actor, context, request_counter).await?;
self.object.verify(context, request_counter).await?;
Ok(())
}
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let actor =
- get_or_fetch_and_upsert_community(&self.common.actor, context, request_counter).await?;
+ let actor = get_or_fetch_and_upsert_community(&self.actor, context, request_counter).await?;
let to = get_or_fetch_and_upsert_person(&self.to, context, request_counter).await?;
// This will throw an error if no follow was requested
blocking(context.pool(), move |conn| {
Ok(())
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
ActorType,
};
-use activitystreams::activity::kind::FollowType;
+use activitystreams::{
+ activity::kind::FollowType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
+};
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler};
use lemmy_db_queries::Followable;
use lemmy_db_schema::source::{
community::{Community, CommunityFollower, CommunityFollowerForm},
};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct FollowCommunity {
+ actor: Url,
pub(in crate::activities::following) to: Url,
pub(in crate::activities::following) object: Url,
#[serde(rename = "type")]
- pub(in crate::activities::following) kind: FollowType,
+ kind: FollowType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- pub(in crate::activities::following) common: ActivityCommonFields,
+ unparsed: Unparsed,
}
impl FollowCommunity {
+ pub(in crate::activities::following) fn new(
+ actor: &Person,
+ community: &Community,
+ ) -> Result<FollowCommunity, LemmyError> {
+ Ok(FollowCommunity {
+ actor: actor.actor_id(),
+ to: community.actor_id(),
+ object: community.actor_id(),
+ kind: FollowType::Follow,
+ id: generate_activity_id(FollowType::Follow)?,
+ context: lemmy_context(),
+ unparsed: Default::default(),
+ })
+ }
pub async fn send(
actor: &Person,
community: &Community,
})
.await?;
- let id = generate_activity_id(FollowType::Follow)?;
- let follow = FollowCommunity {
- to: community.actor_id(),
- object: community.actor_id(),
- kind: FollowType::Follow,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: id.clone(),
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
- };
+ let follow = FollowCommunity::new(actor, community)?;
let inbox = vec![community.inbox_url.clone().into()];
- send_activity_new(context, &follow, &id, actor, inbox, true).await
+ send_activity_new(context, &follow, &follow.id, actor, inbox, true).await
}
}
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
+ verify_activity(self)?;
verify_urls_match(&self.to, &self.object)?;
- verify_person(&self.common.actor, context, request_counter).await?;
+ verify_person(&self.actor, context, request_counter).await?;
Ok(())
}
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let actor =
- get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
+ let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
let community =
get_or_fetch_and_upsert_community(&self.object, context, request_counter).await?;
let community_follower_form = CommunityFollowerForm {
AcceptFollowCommunity::send(self, context).await
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
ActorType,
};
-use activitystreams::activity::kind::{FollowType, UndoType};
+use activitystreams::{
+ activity::kind::UndoType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
+};
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler};
use lemmy_db_queries::Followable;
use lemmy_db_schema::source::{
community::{Community, CommunityFollower, CommunityFollowerForm},
};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UndoFollowCommunity {
+ actor: Url,
to: Url,
object: FollowCommunity,
#[serde(rename = "type")]
kind: UndoType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- common: ActivityCommonFields,
+ unparsed: Unparsed,
}
impl UndoFollowCommunity {
community: &Community,
context: &LemmyContext,
) -> Result<(), LemmyError> {
- let id = generate_activity_id(UndoType::Undo)?;
+ let object = FollowCommunity::new(actor, community)?;
let undo = UndoFollowCommunity {
+ actor: actor.actor_id(),
to: community.actor_id(),
- object: FollowCommunity {
- to: community.actor_id(),
- object: community.actor_id(),
- kind: FollowType::Follow,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: generate_activity_id(FollowType::Follow)?,
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
- },
+ object,
kind: UndoType::Undo,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: id.clone(),
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
+ id: generate_activity_id(UndoType::Undo)?,
+ context: lemmy_context(),
+ unparsed: Default::default(),
};
let inbox = vec![community.get_shared_inbox_or_inbox_url()];
- send_activity_new(context, &undo, &id, actor, inbox, true).await
+ send_activity_new(context, &undo, &undo.id, actor, inbox, true).await
}
}
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
+ verify_activity(self)?;
verify_urls_match(&self.to, &self.object.object)?;
- verify_urls_match(&self.common.actor, &self.object.common.actor)?;
- verify_person(&self.common.actor, context, request_counter).await?;
+ verify_urls_match(&self.actor, self.object.actor())?;
+ verify_person(&self.actor, context, request_counter).await?;
self.object.verify(context, request_counter).await?;
Ok(())
}
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let actor =
- get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
+ let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
let community = get_or_fetch_and_upsert_community(&self.to, context, request_counter).await?;
let community_follower_form = CommunityFollowerForm {
.await?;
Ok(())
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
};
use anyhow::anyhow;
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields};
+use lemmy_apub_lib::{verify_domains_match, ActivityFields};
use lemmy_db_queries::ApubObject;
use lemmy_db_schema::{
source::{community::Community, person::Person},
pub mod following;
pub mod post;
pub mod private_message;
-pub mod removal;
pub mod send;
+pub mod undo_remove;
pub mod voting;
#[derive(Clone, Debug, ToString, Deserialize, Serialize)]
Ok(())
}
-fn verify_activity(common: &ActivityCommonFields) -> Result<(), LemmyError> {
- check_is_apub_id_valid(&common.actor, false)?;
- verify_domains_match(common.id_unchecked(), &common.actor)?;
+fn verify_activity(activity: &dyn ActivityFields) -> Result<(), LemmyError> {
+ check_is_apub_id_valid(activity.actor(), false)?;
+ verify_domains_match(activity.id_unchecked(), activity.actor())?;
Ok(())
}
objects::{post::Page, FromApub, ToApub},
ActorType,
};
+use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
use anyhow::anyhow;
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
values::PublicUrl,
verify_domains_match,
verify_urls_match,
- ActivityCommonFields,
+ ActivityFields,
ActivityHandler,
};
use lemmy_db_queries::Crud;
use lemmy_db_schema::source::{community::Community, person::Person, post::Post};
use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
+use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct CreateOrUpdatePost {
+ actor: Url,
to: PublicUrl,
object: Page,
cc: [Url; 1],
#[serde(rename = "type")]
kind: CreateOrUpdateType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- common: ActivityCommonFields,
+ unparsed: Unparsed,
}
impl CreateOrUpdatePost {
let id = generate_activity_id(kind.clone())?;
let create_or_update = CreateOrUpdatePost {
+ actor: actor.actor_id(),
to: PublicUrl::Public,
object: post.to_apub(context.pool()).await?,
cc: [community.actor_id()],
kind,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: id.clone(),
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
+ id: id.clone(),
+ context: lemmy_context(),
+ unparsed: Default::default(),
};
let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update));
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
+ verify_activity(self)?;
let community = extract_community(&self.cc, context, request_counter).await?;
let community_id = community.actor_id();
- verify_person_in_community(&self.common.actor, &community_id, context, request_counter).await?;
+ verify_person_in_community(&self.actor, &community_id, context, request_counter).await?;
match self.kind {
CreateOrUpdateType::Create => {
- verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
- verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
+ verify_domains_match(&self.actor, self.object.id_unchecked())?;
+ verify_urls_match(&self.actor, &self.object.attributed_to)?;
// Check that the post isnt locked or stickied, as that isnt possible for newly created posts.
// However, when fetching a remote post we generate a new create activity with the current
// locked/stickied value, so this check may fail. So only check if its a local community,
CreateOrUpdateType::Update => {
let is_mod_action = self.object.is_mod_action(context.pool()).await?;
if is_mod_action {
- verify_mod_action(&self.common.actor, community_id, context).await?;
+ verify_mod_action(&self.actor, community_id, context).await?;
} else {
- verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
- verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
+ verify_domains_match(&self.actor, self.object.id_unchecked())?;
+ verify_urls_match(&self.actor, &self.object.attributed_to)?;
}
}
}
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let actor =
- get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
+ let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
let post = Post::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?;
let notif_type = match self.kind {
send_post_ws_message(post.id, notif_type, None, None, context).await?;
Ok(())
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
objects::{private_message::Note, FromApub, ToApub},
ActorType,
};
+use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{verify_domains_match, ActivityFields, ActivityHandler};
use lemmy_db_queries::Crud;
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
+use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct CreateOrUpdatePrivateMessage {
+ #[serde(rename = "@context")]
+ pub context: OneOrMany<AnyBase>,
+ id: Url,
+ actor: Url,
to: Url,
+ cc: [Url; 0],
object: Note,
#[serde(rename = "type")]
kind: CreateOrUpdateType,
#[serde(flatten)]
- common: ActivityCommonFields,
+ pub unparsed: Unparsed,
}
impl CreateOrUpdatePrivateMessage {
let id = generate_activity_id(kind.clone())?;
let create_or_update = CreateOrUpdatePrivateMessage {
+ context: lemmy_context(),
+ id: id.clone(),
+ actor: actor.actor_id(),
to: recipient.actor_id(),
+ cc: [],
object: private_message.to_apub(context.pool()).await?,
kind,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: id.clone(),
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
+ unparsed: Default::default(),
};
let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
send_activity_new(context, &create_or_update, &id, actor, inbox, true).await
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
- verify_person(&self.common.actor, context, request_counter).await?;
- verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
+ verify_activity(self)?;
+ verify_person(&self.actor, context, request_counter).await?;
+ verify_domains_match(&self.actor, self.object.id_unchecked())?;
self.object.verify(context, request_counter).await?;
Ok(())
}
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let private_message =
- PrivateMessage::from_apub(&self.object, context, &self.common.actor, request_counter).await?;
+ PrivateMessage::from_apub(&self.object, context, &self.actor, request_counter).await?;
let notif_type = match self.kind {
CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage,
Ok(())
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
extensions::context::lemmy_context,
ActorType,
};
-use activitystreams::activity::kind::DeleteType;
+use activitystreams::{
+ activity::kind::DeleteType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
+};
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{verify_domains_match, ActivityFields, ActivityHandler};
use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud};
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
+use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct DeletePrivateMessage {
- pub(in crate::activities::private_message) to: Url,
+ actor: Url,
+ to: Url,
pub(in crate::activities::private_message) object: Url,
#[serde(rename = "type")]
- pub(in crate::activities::private_message) kind: DeleteType,
+ kind: DeleteType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- pub(in crate::activities::private_message) common: ActivityCommonFields,
+ unparsed: Unparsed,
}
impl DeletePrivateMessage {
+ pub(in crate::activities::private_message) fn new(
+ actor: &Person,
+ pm: &PrivateMessage,
+ ) -> Result<DeletePrivateMessage, LemmyError> {
+ Ok(DeletePrivateMessage {
+ actor: actor.actor_id(),
+ to: actor.actor_id(),
+ object: pm.ap_id.clone().into(),
+ kind: DeleteType::Delete,
+ id: generate_activity_id(DeleteType::Delete)?,
+ context: lemmy_context(),
+ unparsed: Default::default(),
+ })
+ }
pub async fn send(
actor: &Person,
pm: &PrivateMessage,
context: &LemmyContext,
) -> Result<(), LemmyError> {
+ let delete = DeletePrivateMessage::new(actor, pm)?;
+ let delete_id = delete.id.clone();
+
let recipient_id = pm.recipient_id;
let recipient =
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
-
- let id = generate_activity_id(DeleteType::Delete)?;
- let delete = DeletePrivateMessage {
- to: actor.actor_id(),
- object: pm.ap_id.clone().into(),
- kind: DeleteType::Delete,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: id.clone(),
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
- };
let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
- send_activity_new(context, &delete, &id, actor, inbox, true).await
+ send_activity_new(context, &delete, &delete_id, actor, inbox, true).await
}
}
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
- verify_person(&self.common.actor, context, request_counter).await?;
- verify_domains_match(&self.common.actor, &self.object)?;
+ verify_activity(self)?;
+ verify_person(&self.actor, context, request_counter).await?;
+ verify_domains_match(&self.actor, &self.object)?;
Ok(())
}
Ok(())
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
extensions::context::lemmy_context,
ActorType,
};
-use activitystreams::activity::kind::{DeleteType, UndoType};
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::{
- verify_domains_match,
- verify_urls_match,
- ActivityCommonFields,
- ActivityHandler,
+use activitystreams::{
+ activity::kind::UndoType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
};
+use lemmy_api_common::blocking;
+use lemmy_apub_lib::{verify_domains_match, verify_urls_match, ActivityFields, ActivityHandler};
use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud};
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
+use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UndoDeletePrivateMessage {
+ actor: Url,
to: Url,
object: DeletePrivateMessage,
#[serde(rename = "type")]
kind: UndoType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- common: ActivityCommonFields,
+ unparsed: Unparsed,
}
impl UndoDeletePrivateMessage {
let recipient =
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
- let object = DeletePrivateMessage {
- to: recipient.actor_id(),
- object: pm.ap_id.clone().into(),
- kind: DeleteType::Delete,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: generate_activity_id(DeleteType::Delete)?,
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
- };
-
+ let object = DeletePrivateMessage::new(actor, pm)?;
let id = generate_activity_id(UndoType::Undo)?;
let undo = UndoDeletePrivateMessage {
+ actor: actor.actor_id(),
to: recipient.actor_id(),
object,
kind: UndoType::Undo,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: id.clone(),
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
+ id: id.clone(),
+ context: lemmy_context(),
+ unparsed: Default::default(),
};
let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
send_activity_new(context, &undo, &id, actor, inbox, true).await
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
- verify_person(&self.common.actor, context, request_counter).await?;
- verify_urls_match(&self.common.actor, &self.object.common.actor)?;
- verify_domains_match(&self.common.actor, &self.object.object)?;
+ verify_activity(self)?;
+ verify_person(&self.actor, context, request_counter).await?;
+ verify_urls_match(&self.actor, self.object.actor())?;
+ verify_domains_match(&self.actor, &self.object.object)?;
self.object.verify(context, request_counter).await?;
Ok(())
}
Ok(())
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
+++ /dev/null
-pub mod remove;
-pub mod undo_remove;
+++ /dev/null
-use crate::{
- activities::{
- deletion::{delete::receive_remove_action, verify_delete_activity},
- verify_activity,
- verify_add_remove_moderator_target,
- verify_mod_action,
- verify_person_in_community,
- },
- fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
- CommunityType,
-};
-use activitystreams::{activity::kind::RemoveType, base::AnyBase};
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
-use lemmy_db_queries::Joinable;
-use lemmy_db_schema::source::community::{CommunityModerator, CommunityModeratorForm};
-use lemmy_utils::LemmyError;
-use lemmy_websocket::LemmyContext;
-use url::Url;
-
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct RemoveMod {
- to: PublicUrl,
- pub(in crate::activities::removal) object: Url,
- cc: [Url; 1],
- #[serde(rename = "type")]
- kind: RemoveType,
- // if target is set, this is means remove mod from community
- pub(in crate::activities::removal) target: Option<Url>,
- #[serde(flatten)]
- common: ActivityCommonFields,
-}
-
-#[async_trait::async_trait(?Send)]
-impl ActivityHandler for RemoveMod {
- async fn verify(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
- if let Some(target) = &self.target {
- verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
- verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
- verify_add_remove_moderator_target(target, self.cc[0].clone())?;
- } else {
- verify_delete_activity(
- &self.object,
- &self.cc[0],
- self.common(),
- true,
- context,
- request_counter,
- )
- .await?;
- }
- Ok(())
- }
-
- async fn receive(
- self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- if self.target.is_some() {
- let community =
- get_or_fetch_and_upsert_community(&self.cc[0], context, request_counter).await?;
- let remove_mod =
- get_or_fetch_and_upsert_person(&self.object, context, request_counter).await?;
-
- let form = CommunityModeratorForm {
- community_id: community.id,
- person_id: remove_mod.id,
- };
- blocking(context.pool(), move |conn| {
- CommunityModerator::leave(conn, &form)
- })
- .await??;
- let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(&self)?)?;
- community
- .send_announce(anybase, Some(self.object.clone()), context)
- .await?;
- // TODO: send websocket notification about removed mod
- Ok(())
- } else {
- receive_remove_action(
- &self.common.actor,
- &self.object,
- None,
- context,
- request_counter,
- )
- .await
- }
- }
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
-}
-use crate::{
- activities::generate_activity_id,
- activity_queue::{send_to_community, send_to_community_followers},
- check_is_apub_id_valid,
- extensions::context::lemmy_context,
- fetcher::get_or_fetch_and_upsert_actor,
- generate_moderators_url,
- insert_activity,
- objects::ToApub,
- ActorType,
- CommunityType,
-};
-use activitystreams::{
- activity::{
- kind::{AddType, AnnounceType, BlockType, RemoveType, UndoType, UpdateType},
- Add,
- Announce,
- Block,
- OptTargetRefExt,
- Remove,
- Undo,
- Update,
- },
- base::{AnyBase, BaseExt, ExtendsExt},
- object::ObjectExt,
- public,
-};
-use anyhow::Context;
+use crate::{check_is_apub_id_valid, ActorType, CommunityType};
use itertools::Itertools;
use lemmy_api_common::blocking;
use lemmy_db_queries::DbPool;
-use lemmy_db_schema::source::{community::Community, person::Person};
+use lemmy_db_schema::source::community::Community;
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
-use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
-use lemmy_websocket::LemmyContext;
+use lemmy_utils::LemmyError;
use url::Url;
impl ActorType for Community {
self.followers_url.clone().into()
}
- /// If a remote community is updated by a local mod, send the updated info to the community's
- /// instance.
- async fn send_update(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError> {
- if self.local {
- // Do nothing, other instances will automatically refetch the community
- } else {
- let mut update = Update::new(
- mod_.actor_id(),
- AnyBase::from_arbitrary_json(self.to_apub(context.pool()).await?)?,
- );
- update
- .set_many_contexts(lemmy_context())
- .set_id(generate_activity_id(UpdateType::Update)?)
- .set_to(public())
- .set_many_ccs(vec![self.actor_id()]);
- send_to_community(update, &mod_, self, None, context).await?;
- }
- Ok(())
- }
-
- /// Wraps an activity sent to the community in an announce, and then sends the announce to all
- /// community followers.
- ///
- /// If we are announcing a local activity, it hasn't been stored in the database yet, and we need
- /// to do it here, so that it can be fetched by ID. Remote activities are inserted into DB in the
- /// inbox.
- ///
- /// If the `object` of the announced activity is an actor, the actor ID needs to be passed as
- /// `object_actor`, so that the announce can be delivered to that user.
- async fn send_announce(
- &self,
- activity: AnyBase,
- object_actor: Option<Url>,
- context: &LemmyContext,
- ) -> Result<(), LemmyError> {
- let inner_id = activity.id().context(location_info!())?;
- if inner_id.domain() == Some(&Settings::get().get_hostname_without_port()?) {
- insert_activity(inner_id, activity.clone(), true, false, context.pool()).await?;
- }
-
- let mut ccs = vec![self.followers_url()];
- let mut object_actor_inbox: Option<Url> = None;
- if let Some(actor_id) = object_actor {
- // Ignore errors, maybe its not actually an actor
- // TODO: should pass the actual request counter in, but that seems complicated
- let actor = get_or_fetch_and_upsert_actor(&actor_id, context, &mut 0)
- .await
- .ok();
- if let Some(actor) = actor {
- ccs.push(actor_id);
- object_actor_inbox = Some(actor.get_shared_inbox_or_inbox_url());
- }
- }
- let mut announce = Announce::new(self.actor_id(), activity);
- announce
- .set_many_contexts(lemmy_context())
- .set_id(generate_activity_id(AnnounceType::Announce)?)
- .set_to(public())
- .set_many_ccs(ccs);
-
- send_to_community_followers(announce, self, object_actor_inbox, context).await?;
-
- Ok(())
- }
-
/// For a given community, returns the inboxes of all followers.
async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError> {
let id = self.id;
Ok(inboxes)
}
-
- async fn send_add_mod(
- &self,
- actor: &Person,
- added_mod: Person,
- context: &LemmyContext,
- ) -> Result<(), LemmyError> {
- let mut add = Add::new(actor.actor_id(), added_mod.actor_id());
- add
- .set_many_contexts(lemmy_context())
- .set_id(generate_activity_id(AddType::Add)?)
- .set_to(public())
- .set_many_ccs(vec![self.actor_id()])
- .set_target(generate_moderators_url(&self.actor_id)?.into_inner());
-
- send_to_community(add, actor, self, Some(added_mod.actor_id()), context).await?;
- Ok(())
- }
-
- async fn send_remove_mod(
- &self,
- actor: &Person,
- removed_mod: Person,
- context: &LemmyContext,
- ) -> Result<(), LemmyError> {
- let mut remove = Remove::new(actor.actor_id(), removed_mod.actor_id());
- remove
- .set_many_contexts(lemmy_context())
- .set_id(generate_activity_id(RemoveType::Remove)?)
- .set_to(public())
- .set_many_ccs(vec![self.actor_id()])
- .set_target(generate_moderators_url(&self.actor_id)?.into_inner());
-
- send_to_community(remove, actor, self, Some(removed_mod.actor_id()), context).await?;
- Ok(())
- }
-
- async fn send_block_user(
- &self,
- actor: &Person,
- blocked_user: Person,
- context: &LemmyContext,
- ) -> Result<(), LemmyError> {
- let mut block = Block::new(actor.actor_id(), blocked_user.actor_id());
- block
- .set_many_contexts(lemmy_context())
- .set_id(generate_activity_id(BlockType::Block)?)
- .set_to(public())
- .set_many_ccs(vec![self.actor_id()]);
-
- send_to_community(block, actor, self, Some(blocked_user.actor_id()), context).await?;
- Ok(())
- }
-
- async fn send_undo_block_user(
- &self,
- actor: &Person,
- unblocked_user: Person,
- context: &LemmyContext,
- ) -> Result<(), LemmyError> {
- let mut block = Block::new(actor.actor_id(), unblocked_user.actor_id());
- block
- .set_many_contexts(lemmy_context())
- .set_id(generate_activity_id(BlockType::Block)?)
- .set_to(public())
- .set_many_ccs(vec![self.actor_id()]);
-
- // Undo that fake activity
- let mut undo = Undo::new(actor.actor_id(), block.into_any_base()?);
- undo
- .set_many_contexts(lemmy_context())
- .set_id(generate_activity_id(UndoType::Undo)?)
- .set_to(public())
- .set_many_ccs(vec![self.actor_id()]);
-
- send_to_community(undo, actor, self, Some(unblocked_user.actor_id()), context).await?;
- Ok(())
- }
}
use crate::activities::{
+ community::remove_mod::RemoveMod,
deletion::{undo_delete::UndoDelete, verify_delete_activity},
- removal::remove::RemoveMod,
verify_activity,
};
-use activitystreams::activity::kind::UndoType;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use activitystreams::{
+ activity::kind::UndoType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
+};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UndoRemovePostCommentOrCommunity {
+ actor: Url,
to: PublicUrl,
// Note, there is no such thing as Undo/Remove/Mod, so we ignore that
object: RemoveMod,
cc: [Url; 1],
#[serde(rename = "type")]
kind: UndoType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- common: ActivityCommonFields,
+ unparsed: Unparsed,
}
#[async_trait::async_trait(?Send)]
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
+ verify_activity(self)?;
self.object.verify(context, request_counter).await?;
verify_delete_activity(
&self.object.object,
+ self,
&self.cc[0],
- self.common(),
true,
context,
request_counter,
) -> Result<(), LemmyError> {
UndoDelete::receive_undo_remove_action(&self.object.object, context).await
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
ActorType,
PostOrComment,
};
-use activitystreams::activity::kind::UndoType;
+use activitystreams::{
+ activity::kind::UndoType,
+ base::AnyBase,
+ primitives::OneOrMany,
+ unparsed::Unparsed,
+};
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityFields, ActivityHandler};
use lemmy_db_queries::Crud;
use lemmy_db_schema::{
source::{community::Community, person::Person},
};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
use std::ops::Deref;
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UndoVote {
+ actor: Url,
to: PublicUrl,
object: Vote,
cc: [Url; 1],
#[serde(rename = "type")]
kind: UndoType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- common: ActivityCommonFields,
+ unparsed: Unparsed,
}
impl UndoVote {
Community::read(conn, community_id)
})
.await??;
- let id = generate_activity_id(UndoType::Undo)?;
+ let object = Vote::new(object, actor, &community, kind.clone())?;
+ let id = generate_activity_id(UndoType::Undo)?;
let undo_vote = UndoVote {
+ actor: actor.actor_id(),
to: PublicUrl::Public,
- object: Vote {
- to: PublicUrl::Public,
- object: object.ap_id(),
- cc: [community.actor_id()],
- kind: kind.clone(),
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: generate_activity_id(kind)?,
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
- },
+ object,
cc: [community.actor_id()],
kind: UndoType::Undo,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: id.clone(),
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
+ id: id.clone(),
+ context: lemmy_context(),
+ unparsed: Default::default(),
};
let activity = AnnouncableActivities::UndoVote(undo_vote);
send_to_community_new(activity, &id, actor, &community, vec![], context).await
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
- verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
- verify_urls_match(&self.common.actor, &self.object.common().actor)?;
+ verify_activity(self)?;
+ verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
+ verify_urls_match(&self.actor, self.object.actor())?;
self.object.verify(context, request_counter).await?;
Ok(())
}
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let actor =
- get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
+ let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
let object =
get_or_fetch_and_insert_post_or_comment(&self.object.object, context, request_counter)
.await?;
PostOrComment::Comment(c) => undo_vote_comment(actor, c.deref(), context).await,
}
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
ActorType,
PostOrComment,
};
+use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
use anyhow::anyhow;
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
use lemmy_db_queries::Crud;
use lemmy_db_schema::{
source::{community::Community, person::Person},
}
}
-#[derive(Clone, Debug, Deserialize, Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct Vote {
- pub(in crate::activities::voting) to: PublicUrl,
+ actor: Url,
+ to: PublicUrl,
pub(in crate::activities::voting) object: Url,
- pub(in crate::activities::voting) cc: [Url; 1],
+ cc: [Url; 1],
#[serde(rename = "type")]
pub(in crate::activities::voting) kind: VoteType,
+ id: Url,
+ #[serde(rename = "@context")]
+ context: OneOrMany<AnyBase>,
#[serde(flatten)]
- pub(in crate::activities::voting) common: ActivityCommonFields,
+ unparsed: Unparsed,
}
impl Vote {
+ pub(in crate::activities::voting) fn new(
+ object: &PostOrComment,
+ actor: &Person,
+ community: &Community,
+ kind: VoteType,
+ ) -> Result<Vote, LemmyError> {
+ Ok(Vote {
+ actor: actor.actor_id(),
+ to: PublicUrl::Public,
+ object: object.ap_id(),
+ cc: [community.actor_id()],
+ kind: kind.clone(),
+ id: generate_activity_id(kind)?,
+ context: lemmy_context(),
+ unparsed: Default::default(),
+ })
+ }
+
pub async fn send(
object: &PostOrComment,
actor: &Person,
Community::read(conn, community_id)
})
.await??;
- let id = generate_activity_id(kind.clone())?;
+ let vote = Vote::new(object, actor, &community, kind)?;
+ let vote_id = vote.id.clone();
- let vote = Vote {
- to: PublicUrl::Public,
- object: object.ap_id(),
- cc: [community.actor_id()],
- kind,
- common: ActivityCommonFields {
- context: lemmy_context(),
- id: id.clone(),
- actor: actor.actor_id(),
- unparsed: Default::default(),
- },
- };
let activity = AnnouncableActivities::Vote(vote);
- send_to_community_new(activity, &id, actor, &community, vec![], context).await
+ send_to_community_new(activity, &vote_id, actor, &community, vec![], context).await
}
}
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(self.common())?;
- verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
+ verify_activity(self)?;
+ verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
Ok(())
}
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let actor =
- get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
+ let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
let object =
get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await?;
match object {
PostOrComment::Comment(c) => vote_comment(&self.kind, actor, c.deref(), context).await,
}
}
-
- fn common(&self) -> &ActivityCommonFields {
- &self.common
- }
}
use crate::{
activities::community::announce::{AnnouncableActivities, AnnounceActivity},
- check_is_apub_id_valid,
extensions::signatures::sign_and_send,
insert_activity,
ActorType,
- CommunityType,
APUB_JSON_CONTENT_TYPE,
};
-use activitystreams::{
- base::{BaseExt, Extends, ExtendsExt},
- object::AsObject,
-};
use anyhow::{anyhow, Context, Error};
use background_jobs::{
create_server,
QueueHandle,
WorkerConfig,
};
-use itertools::Itertools;
-use lemmy_db_schema::source::{community::Community, person::Person};
+use lemmy_db_schema::source::community::Community;
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext;
-use log::{debug, info, warn};
+use log::{info, warn};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::{collections::BTreeMap, env, fmt::Debug, future::Future, pin::Pin};
use url::Url;
-/// From a local community, send activity to all remote followers.
-///
-/// * `activity` the apub activity to send
-/// * `community` the sending community
-/// * `extra_inbox` actor inbox which should receive the activity, in addition to followers
-pub(crate) async fn send_to_community_followers<T, Kind>(
- activity: T,
- community: &Community,
- extra_inbox: Option<Url>,
- context: &LemmyContext,
-) -> Result<(), LemmyError>
-where
- T: AsObject<Kind> + Extends<Kind> + Debug + BaseExt<Kind>,
- Kind: Serialize,
- <T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
-{
- let extra_inbox: Vec<Url> = extra_inbox.into_iter().collect();
- let follower_inboxes: Vec<Url> = vec![
- community.get_follower_inboxes(context.pool()).await?,
- extra_inbox,
- ]
- .iter()
- .flatten()
- .unique()
- .filter(|inbox| inbox.host_str() != Some(&Settings::get().hostname))
- .filter(|inbox| check_is_apub_id_valid(inbox, false).is_ok())
- .map(|inbox| inbox.to_owned())
- .collect();
- debug!(
- "Sending activity {:?} to followers of {}",
- &activity.id_unchecked().map(ToString::to_string),
- &community.actor_id
- );
-
- send_activity_internal(context, activity, community, follower_inboxes, true, false).await?;
-
- Ok(())
-}
-
-/// Sends an activity from a local person to a remote community.
-///
-/// * `activity` the activity to send
-/// * `creator` the creator of the activity
-/// * `community` the destination community
-/// * `object_actor` if the object of the activity is an actor, it should be passed here so it can
-/// be sent directly to the actor
-///
-pub(crate) async fn send_to_community<T, Kind>(
- activity: T,
- creator: &Person,
- community: &Community,
- object_actor: Option<Url>,
- context: &LemmyContext,
-) -> Result<(), LemmyError>
-where
- T: AsObject<Kind> + Extends<Kind> + Debug + BaseExt<Kind>,
- Kind: Serialize,
- <T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
-{
- // if this is a local community, we need to do an announce from the community instead
- if community.local {
- community
- .send_announce(activity.into_any_base()?, object_actor, context)
- .await?;
- } else {
- let inbox = community.get_shared_inbox_or_inbox_url();
- check_is_apub_id_valid(&inbox, false)?;
- debug!(
- "Sending activity {:?} to community {}",
- &activity.id_unchecked().map(ToString::to_string),
- &community.actor_id
- );
- // dont send to object_actor here, as that is responsibility of the community itself
- send_activity_internal(context, activity, creator, vec![inbox], true, false).await?;
- }
-
- Ok(())
-}
-
pub(crate) async fn send_to_community_new(
activity: AnnouncableActivities,
activity_id: &Url,
Ok(())
}
-/// Create new `SendActivityTasks`, which will deliver the given activity to inboxes, as well as
-/// handling signing and retrying failed deliveres.
-///
-/// The caller of this function needs to remove any blocked domains from `to`,
-/// using `check_is_apub_id_valid()`.
-async fn send_activity_internal<T, Kind>(
- context: &LemmyContext,
- activity: T,
- actor: &dyn ActorType,
- inboxes: Vec<Url>,
- insert_into_db: bool,
- sensitive: bool,
-) -> Result<(), LemmyError>
-where
- T: AsObject<Kind> + Extends<Kind> + Debug,
- Kind: Serialize,
- <T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
-{
- if !Settings::get().federation.enabled || inboxes.is_empty() {
- return Ok(());
- }
-
- // Don't send anything to ourselves
- let hostname = Settings::get().get_hostname_without_port()?;
- let inboxes: Vec<&Url> = inboxes
- .iter()
- .filter(|i| i.domain().expect("valid inbox url") != hostname)
- .collect();
-
- let activity = activity.into_any_base()?;
- let serialised_activity = serde_json::to_string(&activity)?;
-
- // This is necessary because send_comment and send_comment_mentions
- // might send the same ap_id
- if insert_into_db {
- let id = activity.id().context(location_info!())?;
- insert_activity(id, activity.clone(), true, sensitive, context.pool()).await?;
- }
-
- for i in inboxes {
- let message = SendActivityTask {
- activity: serialised_activity.to_owned(),
- inbox: i.to_owned(),
- actor_id: actor.actor_id(),
- private_key: actor.private_key().context(location_info!())?,
- };
- if env::var("LEMMY_TEST_SEND_SYNC").is_ok() {
- do_send(message, &Client::default()).await?;
- } else {
- context.activity_queue.queue::<SendActivityTask>(message)?;
- }
- }
-
- Ok(())
-}
-
#[derive(Clone, Debug, Deserialize, Serialize)]
struct SendActivityTask {
activity: String,
use crate::{
+ activities::{
+ community::announce::{AnnouncableActivities, AnnounceActivity},
+ extract_community,
+ following::{follow::FollowCommunity, undo::UndoFollowCommunity},
+ },
extensions::context::lemmy_context,
generate_moderators_url,
http::{
create_apub_response,
create_apub_tombstone_response,
- inbox_enums::GroupInboxActivities,
payload_to_string,
receive_activity,
},
};
use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
use lemmy_api_common::blocking;
+use lemmy_apub_lib::{ActivityFields, ActivityHandler};
use lemmy_db_queries::source::{activity::Activity_, community::Community_};
use lemmy_db_schema::source::{activity::Activity, community::Community};
use lemmy_db_views_actor::{
};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
-use serde::Deserialize;
+use log::trace;
+use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
pub(crate) struct CommunityQuery {
}
}
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
+#[serde(untagged)]
+pub enum GroupInboxActivities {
+ FollowCommunity(FollowCommunity),
+ UndoFollowCommunity(UndoFollowCommunity),
+ AnnouncableActivities(AnnouncableActivities),
+}
+
/// Handler for all incoming receive to community inboxes.
pub async fn community_inbox(
request: HttpRequest,
context: web::Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
let unparsed = payload_to_string(payload).await?;
- receive_activity::<GroupInboxActivities>(request, &unparsed, context).await
+ trace!("Received community inbox activity {}", unparsed);
+ let activity = serde_json::from_str::<GroupInboxActivities>(&unparsed)?;
+
+ receive_group_inbox(activity.clone(), request, &context).await?;
+
+ if let GroupInboxActivities::AnnouncableActivities(announcable) = activity {
+ let community = extract_community(&announcable.cc(), &context, &mut 0).await?;
+ if community.local {
+ AnnounceActivity::send(announcable, &community, vec![], &context).await?;
+ }
+ }
+ Ok(HttpResponse::Ok().finish())
+}
+
+pub(in crate::http) async fn receive_group_inbox(
+ activity: GroupInboxActivities,
+ request: HttpRequest,
+ context: &LemmyContext,
+) -> Result<HttpResponse, LemmyError> {
+ receive_activity(request, activity.clone(), context).await
}
/// Returns an empty followers collection, only populating the size (for privacy).
+++ /dev/null
-use crate::activities::{
- comment::create_or_update::CreateOrUpdateComment,
- community::{
- add_mod::AddMod,
- announce::AnnounceActivity,
- block_user::BlockUserFromCommunity,
- undo_block_user::UndoBlockUserFromCommunity,
- update::UpdateCommunity,
- },
- deletion::{delete::Delete, undo_delete::UndoDelete},
- following::{accept::AcceptFollowCommunity, follow::FollowCommunity, undo::UndoFollowCommunity},
- post::create_or_update::CreateOrUpdatePost,
- private_message::{
- create_or_update::CreateOrUpdatePrivateMessage,
- delete::DeletePrivateMessage,
- undo_delete::UndoDeletePrivateMessage,
- },
- removal::{remove::RemoveMod, undo_remove::UndoRemovePostCommentOrCommunity},
- voting::{undo_vote::UndoVote, vote::Vote},
-};
-use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler};
-use lemmy_utils::LemmyError;
-use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
-#[serde(untagged)]
-pub enum PersonInboxActivities {
- AcceptFollowCommunity(AcceptFollowCommunity),
- CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
- DeletePrivateMessage(DeletePrivateMessage),
- UndoDeletePrivateMessage(UndoDeletePrivateMessage),
- AnnounceActivity(Box<AnnounceActivity>),
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
-#[serde(untagged)]
-pub enum GroupInboxActivities {
- FollowCommunity(FollowCommunity),
- UndoFollowCommunity(UndoFollowCommunity),
- CreateOrUpdateComment(CreateOrUpdateComment),
- CreateOrUpdatePost(Box<CreateOrUpdatePost>),
- Vote(Vote),
- UndoVote(UndoVote),
- DeletePostCommentOrCommunity(Delete),
- UndoDeletePostCommentOrCommunity(UndoDelete),
- UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity),
- UpdateCommunity(Box<UpdateCommunity>),
- BlockUserFromCommunity(BlockUserFromCommunity),
- UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
- AddMod(AddMod),
- RemoveMod(RemoveMod),
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
-#[serde(untagged)]
-pub enum SharedInboxActivities {
- // received by group
- FollowCommunity(FollowCommunity),
- UndoFollowCommunity(UndoFollowCommunity),
- CreateOrUpdateComment(CreateOrUpdateComment),
- CreateOrUpdatePost(Box<CreateOrUpdatePost>),
- Vote(Vote),
- UndoVote(UndoVote),
- Delete(Delete),
- UndoDelete(UndoDelete),
- UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity),
- UpdateCommunity(Box<UpdateCommunity>),
- BlockUserFromCommunity(BlockUserFromCommunity),
- UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
- AddMod(AddMod),
- RemoveMod(RemoveMod),
- // received by person
- AcceptFollowCommunity(AcceptFollowCommunity),
- // 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.
- CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
- DeletePrivateMessage(DeletePrivateMessage),
- UndoDeletePrivateMessage(UndoDeletePrivateMessage),
- AnnounceActivity(Box<AnnounceActivity>),
-}
check_is_apub_id_valid,
extensions::signatures::verify_signature,
fetcher::get_or_fetch_and_upsert_actor,
- http::inbox_enums::SharedInboxActivities,
+ http::{
+ community::{receive_group_inbox, GroupInboxActivities},
+ person::{receive_person_inbox, PersonInboxActivities},
+ },
insert_activity,
APUB_JSON_CONTENT_TYPE,
};
use futures::StreamExt;
use http::StatusCode;
use lemmy_api_common::blocking;
-use lemmy_apub_lib::ActivityHandler;
+use lemmy_apub_lib::{ActivityFields, ActivityHandler};
use lemmy_db_queries::{source::activity::Activity_, DbPool};
use lemmy_db_schema::source::activity::Activity;
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext;
+use log::{info, trace};
use serde::{Deserialize, Serialize};
use std::{fmt::Debug, io::Read};
use url::Url;
mod comment;
mod community;
-mod inbox_enums;
mod person;
mod post;
pub mod routes;
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
+#[serde(untagged)]
+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,
context: web::Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
let unparsed = payload_to_string(payload).await?;
- receive_activity::<SharedInboxActivities>(request, &unparsed, context).await
+ trace!("Received shared inbox activity {}", unparsed);
+ let activity = serde_json::from_str::<SharedInboxActivities>(&unparsed)?;
+ match activity {
+ SharedInboxActivities::GroupInboxActivities(g) => {
+ receive_group_inbox(g, request, &context).await
+ }
+ SharedInboxActivities::PersonInboxActivities(p) => {
+ receive_person_inbox(p, request, &context).await
+ }
+ }
}
async fn payload_to_string(mut payload: Payload) -> Result<String, LemmyError> {
// TODO: move most of this code to library
async fn receive_activity<'a, T>(
request: HttpRequest,
- activity: &'a str,
- context: web::Data<LemmyContext>,
+ activity: T,
+ context: &LemmyContext,
) -> Result<HttpResponse, LemmyError>
where
- T: ActivityHandler + Clone + Deserialize<'a> + Serialize + std::fmt::Debug + Send + 'static,
+ T: ActivityHandler
+ + ActivityFields
+ + Clone
+ + Deserialize<'a>
+ + Serialize
+ + std::fmt::Debug
+ + Send
+ + 'static,
{
- let activity = serde_json::from_str::<T>(activity)?;
- let activity_data = activity.common();
-
let request_counter = &mut 0;
- let actor =
- get_or_fetch_and_upsert_actor(&activity_data.actor, &context, request_counter).await?;
+ let actor = get_or_fetch_and_upsert_actor(activity.actor(), context, request_counter).await?;
verify_signature(&request, &actor.public_key().context(location_info!())?)?;
// Do nothing if we received the same activity before
- if is_activity_already_known(context.pool(), activity_data.id_unchecked()).await? {
+ if is_activity_already_known(context.pool(), activity.id_unchecked()).await? {
return Ok(HttpResponse::Ok().finish());
}
- check_is_apub_id_valid(&activity_data.actor, false)?;
- println!(
- "Verifying activity {}",
- activity_data.id_unchecked().to_string()
- );
- activity.verify(&context, request_counter).await?;
+ check_is_apub_id_valid(activity.actor(), false)?;
+ info!("Verifying activity {}", activity.id_unchecked().to_string());
+ activity.verify(context, request_counter).await?;
assert_activity_not_local(&activity)?;
// Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen
// if we receive the same activity twice in very quick succession.
insert_activity(
- activity_data.id_unchecked(),
+ activity.id_unchecked(),
activity.clone(),
false,
true,
)
.await?;
- println!(
- "Receiving activity {}",
- activity_data.id_unchecked().to_string()
- );
- activity.receive(&context, request_counter).await?;
+ info!("Receiving activity {}", activity.id_unchecked().to_string());
+ activity.receive(context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
}
}
-fn assert_activity_not_local<T: Debug + ActivityHandler>(activity: &T) -> Result<(), LemmyError> {
- let activity_domain = activity
- .common()
- .id_unchecked()
- .domain()
- .context(location_info!())?;
+fn assert_activity_not_local<T: Debug + ActivityFields>(activity: &T) -> Result<(), LemmyError> {
+ let activity_domain = activity.id_unchecked().domain().context(location_info!())?;
if activity_domain == Settings::get().hostname {
return Err(
use crate::{
+ activities::{
+ community::announce::{AnnouncableActivities, AnnounceActivity},
+ following::accept::AcceptFollowCommunity,
+ private_message::{
+ create_or_update::CreateOrUpdatePrivateMessage,
+ delete::DeletePrivateMessage,
+ undo_delete::UndoDeletePrivateMessage,
+ },
+ },
extensions::context::lemmy_context,
http::{
create_apub_response,
create_apub_tombstone_response,
- inbox_enums::PersonInboxActivities,
payload_to_string,
receive_activity,
},
};
use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
use lemmy_api_common::blocking;
+use lemmy_apub_lib::{ActivityFields, ActivityHandler};
use lemmy_db_queries::source::person::Person_;
use lemmy_db_schema::source::person::Person;
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
-use serde::Deserialize;
+use log::trace;
+use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Deserialize)]
}
}
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
+#[serde(untagged)]
+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,
context: web::Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
let unparsed = payload_to_string(payload).await?;
- receive_activity::<PersonInboxActivities>(request, &unparsed, context).await
+ trace!("Received person inbox activity {}", unparsed);
+ let activity = serde_json::from_str::<PersonInboxActivities>(&unparsed)?;
+ receive_person_inbox(activity, request, &context).await
+}
+
+pub(in crate::http) async fn receive_person_inbox(
+ activity: PersonInboxActivities,
+ request: HttpRequest,
+ context: &LemmyContext,
+) -> Result<HttpResponse, LemmyError> {
+ receive_activity(request, activity, context).await
}
pub(crate) async fn get_apub_person_outbox(
pub mod objects;
use crate::extensions::signatures::PublicKey;
-use activitystreams::base::AnyBase;
use anyhow::{anyhow, Context};
use diesel::NotFound;
use lemmy_api_common::blocking;
pub trait CommunityType {
fn followers_url(&self) -> Url;
async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError>;
-
- async fn send_update(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError>;
-
- async fn send_announce(
- &self,
- activity: AnyBase,
- object: Option<Url>,
- context: &LemmyContext,
- ) -> Result<(), LemmyError>;
-
- async fn send_add_mod(
- &self,
- actor: &Person,
- added_mod: Person,
- context: &LemmyContext,
- ) -> Result<(), LemmyError>;
- async fn send_remove_mod(
- &self,
- actor: &Person,
- removed_mod: Person,
- context: &LemmyContext,
- ) -> Result<(), LemmyError>;
-
- async fn send_block_user(
- &self,
- actor: &Person,
- blocked_user: Person,
- context: &LemmyContext,
- ) -> Result<(), LemmyError>;
- async fn send_undo_block_user(
- &self,
- actor: &Person,
- blocked_user: Person,
- context: &LemmyContext,
- ) -> Result<(), LemmyError>;
}
pub enum EndpointType {
pub mod values;
-use activitystreams::{
- base::AnyBase,
- error::DomainError,
- primitives::OneOrMany,
- unparsed::Unparsed,
-};
+use activitystreams::error::DomainError;
pub use lemmy_apub_lib_derive::*;
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use url::Url;
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ActivityCommonFields {
- #[serde(rename = "@context")]
- pub context: OneOrMany<AnyBase>,
- pub id: Url,
- pub actor: Url,
-
- // unparsed fields
- #[serde(flatten)]
- pub unparsed: Unparsed,
-}
-
-impl ActivityCommonFields {
- pub fn id_unchecked(&self) -> &Url {
- &self.id
- }
+pub trait ActivityFields {
+ fn id_unchecked(&self) -> &Url;
+ fn actor(&self) -> &Url;
+ fn cc(&self) -> Vec<Url>;
}
#[async_trait::async_trait(?Send)]
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError>;
- fn common(&self) -> &ActivityCommonFields;
}
pub fn verify_domains_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
Ok(())
}
-pub fn verify_domains_match_opt(a: &Url, b: Option<&Url>) -> Result<(), LemmyError> {
- if let Some(b2) = b {
- return verify_domains_match(a, b2);
- }
- Ok(())
-}
-
pub fn verify_urls_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
if a != b {
return Err(DomainError.into());
use proc_macro2::TokenStream;
use quote::quote;
-use syn::{parse_macro_input, Data, DeriveInput};
+use syn::{parse_macro_input, Data, DeriveInput, Fields::Unnamed, Ident, Variant};
/// Generates implementation ActivityHandler for an enum, which looks like the following (handling
/// all enum variants).
/// }
///
/// ```
-///
-/// TODO: consider replacing this macro with https://crates.io/crates/typetag crate, though it
-/// doesnt support untagged enums which we need for apub.
#[proc_macro_derive(ActivityHandler)]
pub fn derive_activity_handler(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
- // Parse the input tokens into a syntax tree.
let input = parse_macro_input!(input as DeriveInput);
- // Used in the quasi-quotation below as `#name`.
- let name = input.ident;
+ let enum_name = input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
- let input_enum = if let Data::Enum(d) = input.data {
- d
+ let enum_variants = if let Data::Enum(d) = input.data {
+ d.variants
} else {
unimplemented!()
};
- let impl_verify = input_enum
- .variants
+ let body_verify = quote! {a.verify(context, request_counter).await};
+ let impl_verify = enum_variants
.iter()
- .map(|variant| variant_impl_verify(&name, variant));
- let impl_receive = input_enum
- .variants
+ .map(|v| generate_match_arm(&enum_name, v, &body_verify));
+ let body_receive = quote! {a.receive(context, request_counter).await};
+ let impl_receive = enum_variants
.iter()
- .map(|variant| variant_impl_receive(&name, variant));
- let impl_common = input_enum
- .variants
- .iter()
- .map(|variant| variant_impl_common(&name, variant));
+ .map(|v| generate_match_arm(&enum_name, v, &body_receive));
- // The generated impl.
let expanded = quote! {
#[async_trait::async_trait(?Send)]
- impl #impl_generics lemmy_apub_lib::ActivityHandler for #name #ty_generics #where_clause {
+ impl #impl_generics lemmy_apub_lib::ActivityHandler for #enum_name #ty_generics #where_clause {
async fn verify(
&self,
- context: &LemmyContext,
+ context: &lemmy_websocket::LemmyContext,
request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ ) -> Result<(), lemmy_utils::LemmyError> {
match self {
#(#impl_verify)*
}
}
async fn receive(
self,
- context: &LemmyContext,
+ context: &lemmy_websocket::LemmyContext,
request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ ) -> Result<(), lemmy_utils::LemmyError> {
match self {
#(#impl_receive)*
}
}
- fn common(&self) -> &ActivityCommonFields {
- match self {
- #(#impl_common)*
- }
- }
}
};
-
- // Hand the output tokens back to the compiler.
- proc_macro::TokenStream::from(expanded)
+ expanded.into()
}
-fn variant_impl_common(name: &syn::Ident, variant: &syn::Variant) -> TokenStream {
+fn generate_match_arm(enum_name: &Ident, variant: &Variant, body: &TokenStream) -> TokenStream {
let id = &variant.ident;
match &variant.fields {
- syn::Fields::Unnamed(_) => {
+ Unnamed(_) => {
quote! {
- #name::#id(a) => a.common(),
+ #enum_name::#id(a) => #body,
}
}
_ => unimplemented!(),
}
}
-fn variant_impl_verify(name: &syn::Ident, variant: &syn::Variant) -> TokenStream {
- let id = &variant.ident;
- match &variant.fields {
- syn::Fields::Unnamed(_) => {
+#[proc_macro_derive(ActivityFields)]
+pub fn derive_activity_fields(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+
+ let name = input.ident;
+
+ let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+
+ let expanded = match input.data {
+ Data::Enum(e) => {
+ let variants = e.variants;
+ let impl_id = variants
+ .iter()
+ .map(|v| generate_match_arm(&name, v, "e! {a.id_unchecked()}));
+ let impl_actor = variants
+ .iter()
+ .map(|v| generate_match_arm(&name, v, "e! {a.actor()}));
+ let impl_cc = variants
+ .iter()
+ .map(|v| generate_match_arm(&name, v, "e! {a.cc()}));
quote! {
- #name::#id(a) => a.verify(context, request_counter).await,
+ impl #impl_generics lemmy_apub_lib::ActivityFields for #name #ty_generics #where_clause {
+ fn id_unchecked(&self) -> &url::Url { match self { #(#impl_id)* } }
+ fn actor(&self) -> &url::Url { match self { #(#impl_actor)* } }
+ fn cc(&self) -> Vec<url::Url> { match self { #(#impl_cc)* } }
+ }
}
}
- _ => unimplemented!(),
- }
-}
-
-fn variant_impl_receive(name: &syn::Ident, variant: &syn::Variant) -> TokenStream {
- let id = &variant.ident;
- match &variant.fields {
- syn::Fields::Unnamed(_) => {
+ Data::Struct(s) => {
+ // check if the struct has a field "cc", and generate impl for cc() function depending on that
+ let has_cc = if let syn::Fields::Named(n) = s.fields {
+ n.named
+ .iter()
+ .any(|i| format!("{}", i.ident.as_ref().unwrap()) == "cc")
+ } else {
+ unimplemented!()
+ };
+ let cc_impl = if has_cc {
+ quote! {self.cc.clone().into()}
+ } else {
+ quote! {vec![]}
+ };
quote! {
- #name::#id(a) => a.receive(context, request_counter).await,
+ impl #impl_generics lemmy_apub_lib::ActivityFields for #name #ty_generics #where_clause {
+ fn id_unchecked(&self) -> &url::Url { &self.id }
+ fn actor(&self) -> &url::Url { &self.actor }
+ fn cc(&self) -> Vec<url::Url> { #cc_impl }
+ }
}
}
_ => unimplemented!(),
- }
+ };
+ expanded.into()
}