]> Untitled Git - lemmy.git/blobdiff - crates/apub/src/activities/community/announce.rs
Adding unique constraint for activity ap_id. Fixes #1878 (#1935)
[lemmy.git] / crates / apub / src / activities / community / announce.rs
index 301ccc64840574e0bcce1a9e839b96677cfa7700..cc30a89f393185ba29b62346bb3c70c2b955eafd 100644 (file)
 use crate::{
-  activities::{
-    comment::{create::CreateComment, update::UpdateComment},
-    community::{
-      add_mod::AddMod,
-      block_user::BlockUserFromCommunity,
-      undo_block_user::UndoBlockUserFromCommunity,
-    },
-    deletion::{
-      delete::DeletePostCommentOrCommunity,
-      undo_delete::UndoDeletePostCommentOrCommunity,
-    },
-    post::{create::CreatePost, update::UpdatePost},
-    removal::{
-      remove::RemovePostCommentCommunityOrMod,
-      undo_remove::UndoRemovePostCommentOrCommunity,
-    },
-    verify_activity,
-    verify_community,
-    voting::{
-      dislike::DislikePostOrComment,
-      like::LikePostOrComment,
-      undo_dislike::UndoDislikePostOrComment,
-      undo_like::UndoLikePostOrComment,
-    },
-  },
-  http::is_activity_already_known,
+  activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_is_public},
+  activity_lists::AnnouncableActivities,
+  http::ActivityCommonFields,
   insert_activity,
+  objects::community::ApubCommunity,
+  protocol::activities::{community::announce::AnnounceActivity, CreateOrUpdateType},
+};
+use activitystreams_kinds::{activity::AnnounceType, public};
+use lemmy_apub_lib::{
+  data::Data,
+  object_id::ObjectId,
+  traits::{ActivityHandler, ActorType},
 };
-use activitystreams::activity::kind::AnnounceType;
-use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler, PublicUrl};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-use url::Url;
 
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
-#[serde(untagged)]
-pub enum AnnouncableActivities {
-  CreateComment(CreateComment),
-  UpdateComment(UpdateComment),
-  CreatePost(CreatePost),
-  UpdatePost(UpdatePost),
-  LikePostOrComment(LikePostOrComment),
-  DislikePostOrComment(DislikePostOrComment),
-  UndoLikePostOrComment(UndoLikePostOrComment),
-  UndoDislikePostOrComment(UndoDislikePostOrComment),
-  DeletePostCommentOrCommunity(DeletePostCommentOrCommunity),
-  UndoDeletePostCommentOrCommunity(UndoDeletePostCommentOrCommunity),
-  RemovePostCommentCommunityOrMod(RemovePostCommentCommunityOrMod),
-  UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity),
-  BlockUserFromCommunity(BlockUserFromCommunity),
-  UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
-  AddMod(AddMod),
+#[async_trait::async_trait(?Send)]
+pub(crate) trait GetCommunity {
+  async fn get_community(
+    &self,
+    context: &LemmyContext,
+    request_counter: &mut i32,
+  ) -> Result<ApubCommunity, LemmyError>;
 }
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct AnnounceActivity {
-  to: PublicUrl,
-  object: AnnouncableActivities,
-  cc: Vec<Url>,
-  #[serde(rename = "type")]
-  kind: AnnounceType,
-  #[serde(flatten)]
-  common: ActivityCommonFields,
+impl AnnounceActivity {
+  pub(crate) fn new(
+    object: AnnouncableActivities,
+    community: &ApubCommunity,
+    context: &LemmyContext,
+  ) -> Result<AnnounceActivity, LemmyError> {
+    Ok(AnnounceActivity {
+      actor: ObjectId::new(community.actor_id()),
+      to: vec![public()],
+      object,
+      cc: vec![community.followers_url.clone().into()],
+      kind: AnnounceType::Announce,
+      id: generate_activity_id(
+        &AnnounceType::Announce,
+        &context.settings().get_protocol_and_hostname(),
+      )?,
+      unparsed: Default::default(),
+    })
+  }
+
+  pub async fn send(
+    object: AnnouncableActivities,
+    community: &ApubCommunity,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let announce = AnnounceActivity::new(object.clone(), community, context)?;
+    let inboxes = community.get_follower_inboxes(context).await?;
+    send_lemmy_activity(
+      context,
+      &announce,
+      &announce.id,
+      community,
+      inboxes.clone(),
+      false,
+    )
+    .await?;
+
+    // Pleroma and Mastodon can't handle activities like Announce/Create/Page. So for
+    // compatibility, we also send Announce/Page so that they can follow Lemmy communities.
+    use AnnouncableActivities::*;
+    let object = match object {
+      CreateOrUpdatePost(c) if c.kind == CreateOrUpdateType::Create => Page(c.object),
+      _ => return Ok(()),
+    };
+    let announce_compat = AnnounceActivity::new(object, community, context)?;
+    send_lemmy_activity(
+      context,
+      &announce_compat,
+      &announce_compat.id,
+      community,
+      inboxes,
+      false,
+    )
+    .await?;
+    Ok(())
+  }
 }
 
 #[async_trait::async_trait(?Send)]
 impl ActivityHandler for AnnounceActivity {
+  type DataType = LemmyContext;
   async fn verify(
     &self,
-    context: &LemmyContext,
+    context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
-    verify_community(&self.common.actor, context, request_counter).await?;
+    verify_is_public(&self.to, &self.cc)?;
+    verify_activity(&self.id, self.actor.inner(), &context.settings())?;
     self.object.verify(context, request_counter).await?;
     Ok(())
   }
 
   async fn receive(
-    &self,
-    context: &LemmyContext,
+    self,
+    context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    if is_activity_already_known(context.pool(), self.object.common().id_unchecked()).await? {
-      return Ok(());
+    // TODO: this can probably be implemented in a cleaner way
+    match self.object {
+      // Dont insert these into activities table, as they are not activities.
+      AnnouncableActivities::Page(_) => {}
+      _ => {
+        let object_value = serde_json::to_value(&self.object)?;
+        let object_data: ActivityCommonFields = serde_json::from_value(object_value.to_owned())?;
+
+        insert_activity(&object_data.id, object_value, false, true, context.pool()).await?;
+      }
     }
-    insert_activity(
-      self.object.common().id_unchecked(),
-      self.object.clone(),
-      false,
-      true,
-      context.pool(),
-    )
-    .await?;
     self.object.receive(context, request_counter).await
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }