]> 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 f560b09f6e78b73b9af5fc00739218f1a78eae71..cc30a89f393185ba29b62346bb3c70c2b955eafd 100644 (file)
@@ -1,20 +1,19 @@
 use crate::{
   activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_is_public},
   activity_lists::AnnouncableActivities,
-  fetcher::object_id::ObjectId,
-  http::is_activity_already_known,
+  http::ActivityCommonFields,
   insert_activity,
   objects::community::ApubCommunity,
-  protocol::activities::community::announce::AnnounceActivity,
+  protocol::activities::{community::announce::AnnounceActivity, CreateOrUpdateType},
 };
-use activitystreams::{activity::kind::AnnounceType, public};
+use activitystreams_kinds::{activity::AnnounceType, public};
 use lemmy_apub_lib::{
   data::Data,
-  traits::{ActivityFields, ActivityHandler, ActorType},
+  object_id::ObjectId,
+  traits::{ActivityHandler, ActorType},
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use url::Url;
 
 #[async_trait::async_trait(?Send)]
 pub(crate) trait GetCommunity {
@@ -26,28 +25,60 @@ pub(crate) trait GetCommunity {
 }
 
 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(),
       )?,
       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 = community
-      .get_follower_inboxes(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(())
   }
 }
 
@@ -59,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(())
   }
@@ -70,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
   }
 }