]> 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 6165eb2f00d212b0a09d116ef5f0cec7d029b818..cc30a89f393185ba29b62346bb3c70c2b955eafd 100644 (file)
@@ -1,61 +1,19 @@
 use crate::{
-  activities::{
-    comment::create_or_update::CreateOrUpdateComment,
-    community::{
-      add_mod::AddMod,
-      block_user::BlockUserFromCommunity,
-      list_community_follower_inboxes,
-      remove_mod::RemoveMod,
-      undo_block_user::UndoBlockUserFromCommunity,
-      update::UpdateCommunity,
-    },
-    deletion::{delete::Delete, undo_delete::UndoDelete},
-    generate_activity_id,
-    post::create_or_update::CreateOrUpdatePost,
-    verify_activity,
-    verify_is_public,
-    voting::{undo_vote::UndoVote, vote::Vote},
-  },
-  context::lemmy_context,
-  fetcher::object_id::ObjectId,
-  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,
-  send_lemmy_activity,
-};
-use activitystreams::{
-  activity::kind::AnnounceType,
-  base::AnyBase,
-  primitives::OneOrMany,
-  public,
-  unparsed::Unparsed,
+  protocol::activities::{community::announce::AnnounceActivity, CreateOrUpdateType},
 };
+use activitystreams_kinds::{activity::AnnounceType, public};
 use lemmy_apub_lib::{
   data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType},
-  verify::verify_urls_match,
+  object_id::ObjectId,
+  traits::{ActivityHandler, ActorType},
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
-#[serde(untagged)]
-#[activity_handler(LemmyContext)]
-pub enum AnnouncableActivities {
-  CreateOrUpdateComment(CreateOrUpdateComment),
-  CreateOrUpdatePost(Box<CreateOrUpdatePost>),
-  Vote(Vote),
-  UndoVote(UndoVote),
-  Delete(Delete),
-  UndoDelete(UndoDelete),
-  UpdateCommunity(Box<UpdateCommunity>),
-  BlockUserFromCommunity(BlockUserFromCommunity),
-  UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
-  AddMod(AddMod),
-  RemoveMod(RemoveMod),
-}
 
 #[async_trait::async_trait(?Send)]
 pub(crate) trait GetCommunity {
@@ -66,70 +24,61 @@ pub(crate) trait GetCommunity {
   ) -> Result<ApubCommunity, LemmyError>;
 }
 
-#[async_trait::async_trait(?Send)]
-impl GetCommunity for AnnouncableActivities {
-  async fn get_community(
-    &self,
-    context: &LemmyContext,
-    request_counter: &mut i32,
-  ) -> Result<ApubCommunity, LemmyError> {
-    use AnnouncableActivities::*;
-    let community = match self {
-      CreateOrUpdateComment(a) => a.get_community(context, request_counter).await?,
-      CreateOrUpdatePost(a) => a.get_community(context, request_counter).await?,
-      Vote(a) => a.get_community(context, request_counter).await?,
-      UndoVote(a) => a.get_community(context, request_counter).await?,
-      Delete(a) => a.get_community(context, request_counter).await?,
-      UndoDelete(a) => a.get_community(context, request_counter).await?,
-      UpdateCommunity(a) => a.get_community(context, request_counter).await?,
-      BlockUserFromCommunity(a) => a.get_community(context, request_counter).await?,
-      UndoBlockUserFromCommunity(a) => a.get_community(context, request_counter).await?,
-      AddMod(a) => a.get_community(context, request_counter).await?,
-      RemoveMod(a) => a.get_community(context, request_counter).await?,
-    };
-    verify_urls_match(self.actor(), &community.actor_id())?;
-    Ok(community)
-  }
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
-#[serde(rename_all = "camelCase")]
-pub struct AnnounceActivity {
-  actor: ObjectId<ApubCommunity>,
-  to: Vec<Url>,
-  object: AnnouncableActivities,
-  cc: Vec<Url>,
-  #[serde(rename = "type")]
-  kind: AnnounceType,
-  id: Url,
-  #[serde(rename = "@context")]
-  context: OneOrMany<AnyBase>,
-  #[serde(flatten)]
-  unparsed: Unparsed,
-}
-
 impl AnnounceActivity {
-  pub async fn send(
+  pub(crate) fn new(
     object: AnnouncableActivities,
     community: &ApubCommunity,
-    additional_inboxes: Vec<Url>,
     context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    let announce = AnnounceActivity {
+  ) -> Result<AnnounceActivity, LemmyError> {
+    Ok(AnnounceActivity {
       actor: ObjectId::new(community.actor_id()),
       to: vec![public()],
       object,
-      cc: vec![community.followers_url.clone().into_inner()],
+      cc: vec![community.followers_url.clone().into()],
       kind: AnnounceType::Announce,
       id: generate_activity_id(
         &AnnounceType::Announce,
         &context.settings().get_protocol_and_hostname(),
       )?,
-      context: lemmy_context(),
       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 inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?;
-    send_lemmy_activity(context, &announce, &announce.id, community, inboxes, false).await
+    let announce_compat = AnnounceActivity::new(object, community, context)?;
+    send_lemmy_activity(
+      context,
+      &announce_compat,
+      &announce_compat.id,
+      community,
+      inboxes,
+      false,
+    )
+    .await?;
+    Ok(())
   }
 }
 
@@ -141,8 +90,8 @@ impl ActivityHandler for AnnounceActivity {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_is_public(&self.to)?;
-    verify_activity(self, &context.settings())?;
+    verify_is_public(&self.to, &self.cc)?;
+    verify_activity(&self.id, self.actor.inner(), &context.settings())?;
     self.object.verify(context, request_counter).await?;
     Ok(())
   }
@@ -152,17 +101,17 @@ impl ActivityHandler for AnnounceActivity {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    if is_activity_already_known(context.pool(), self.object.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.id_unchecked(),
-      self.object.clone(),
-      false,
-      true,
-      context.pool(),
-    )
-    .await?;
     self.object.receive(context, request_counter).await
   }
 }