]> Untitled Git - lemmy.git/commitdiff
GNU social compatibility (#2100)
authorNutomic <me@nutomic.com>
Thu, 24 Mar 2022 16:33:42 +0000 (16:33 +0000)
committerGitHub <noreply@github.com>
Thu, 24 Mar 2022 16:33:42 +0000 (16:33 +0000)
* Use SourceCompat everywhere (better compat with other software)

* Name field should not be mandatory in Group

* also check page.cc field for community id

* add gnu social tests

* better to use option<sourcecompat>

* update gnu social tests, marked vote as "unlisted"

29 files changed:
crates/apub/assets/gnusocial/activities/create_note.json [new file with mode: 0644]
crates/apub/assets/gnusocial/activities/create_page.json [new file with mode: 0644]
crates/apub/assets/gnusocial/activities/like_note.json [new file with mode: 0644]
crates/apub/assets/gnusocial/objects/group.json [new file with mode: 0644]
crates/apub/assets/gnusocial/objects/note.json [new file with mode: 0644]
crates/apub/assets/gnusocial/objects/page.json [new file with mode: 0644]
crates/apub/assets/gnusocial/objects/person.json [new file with mode: 0644]
crates/apub/assets/lemmy/activities/voting/dislike_page.json
crates/apub/assets/lemmy/activities/voting/like_note.json
crates/apub/assets/lemmy/activities/voting/undo_dislike_page.json
crates/apub/assets/lemmy/activities/voting/undo_like_note.json
crates/apub/src/activities/voting/undo_vote.rs
crates/apub/src/activities/voting/vote.rs
crates/apub/src/objects/comment.rs
crates/apub/src/objects/community.rs
crates/apub/src/objects/instance.rs
crates/apub/src/objects/mod.rs
crates/apub/src/objects/person.rs
crates/apub/src/objects/post.rs
crates/apub/src/objects/private_message.rs
crates/apub/src/protocol/activities/mod.rs
crates/apub/src/protocol/mod.rs
crates/apub/src/protocol/objects/chat_message.rs
crates/apub/src/protocol/objects/group.rs
crates/apub/src/protocol/objects/instance.rs
crates/apub/src/protocol/objects/mod.rs
crates/apub/src/protocol/objects/note.rs
crates/apub/src/protocol/objects/page.rs
crates/apub/src/protocol/objects/person.rs

diff --git a/crates/apub/assets/gnusocial/activities/create_note.json b/crates/apub/assets/gnusocial/activities/create_note.json
new file mode 100644 (file)
index 0000000..9e4221b
--- /dev/null
@@ -0,0 +1,53 @@
+{
+  "type": "Create",
+  "@context": [
+    "https://www.w3.org/ns/activitystreams",
+    "https://w3id.org/security/v1",
+    {
+      "gs": "https://www.gnu.org/software/social/ns#"
+    },
+    {
+      "litepub": "http://litepub.social/ns#"
+    },
+    {
+      "chatMessage": "litepub:chatMessage"
+    },
+    {
+      "inConversation": {
+        "@id": "gs:inConversation",
+        "@type": "@id"
+      }
+    }
+  ],
+  "id": "https://instance.gnusocial.test/activity/1339",
+  "published": "2022-03-01T20:58:48+00:00",
+  "actor": "https://instance.gnusocial.test/actor/42",
+  "object": {
+    "type": "Note",
+    "id": "https://instance.gnusocial.test/object/note/1339",
+    "published": "2022-03-01T21:00:16+00:00",
+    "attributedTo": "https://instance.gnusocial.test/actor/42",
+    "content": "<p>yay ^^</p>",
+    "mediaType": "text/html",
+    "source": {
+      "content": "yay ^^",
+      "mediaType": "text/plain"
+    },
+    "attachment": [],
+    "tag": [],
+    "inReplyTo": "https://instance.gnusocial.test/object/note/1338",
+    "inConversation": "https://instance.gnusocial.test/conversation/1338",
+    "to": [
+      "https://www.w3.org/ns/activitystreams#Public"
+    ],
+    "cc": [
+      "https://instance.gnusocial.test/actor/42/subscribers"
+    ]
+  },
+  "to": [
+    "https://www.w3.org/ns/activitystreams#Public"
+  ],
+  "cc": [
+    "https://instance.gnusocial.test/actor/42/subscribers"
+  ]
+}
diff --git a/crates/apub/assets/gnusocial/activities/create_page.json b/crates/apub/assets/gnusocial/activities/create_page.json
new file mode 100644 (file)
index 0000000..2ce600c
--- /dev/null
@@ -0,0 +1,53 @@
+{
+  "type": "Create",
+  "@context": [
+    "https://www.w3.org/ns/activitystreams",
+    "https://w3id.org/security/v1",
+    {
+      "gs": "https://www.gnu.org/software/social/ns#"
+    },
+    {
+      "litepub": "http://litepub.social/ns#"
+    },
+    {
+      "chatMessage": "litepub:chatMessage"
+    },
+    {
+      "inConversation": {
+        "@id": "gs:inConversation",
+        "@type": "@id"
+      }
+    }
+  ],
+  "id": "https://instance.gnusocial.test/activity/1338",
+  "published": "2022-03-17T23:30:26+00:00",
+  "actor": "https://instance.gnusocial.test/actor/42",
+  "to": [
+    "https://www.w3.org/ns/activitystreams#Public"
+  ],
+  "cc": [
+    "https://instance.gnusocial.test/actor/21"
+  ],
+  "object": {
+    "type": "Page",
+    "id": "https://instance.gnusocial.test/object/note/1338",
+    "published": "2022-03-17T23:30:26+00:00",
+    "attributedTo": "https://instance.gnusocial.test/actor/42",
+    "name": "hello, world.",
+    "content": "<p>This is an interesting page.</p>",
+    "mediaType": "text/html",
+    "source": {
+      "content": "This is an interesting page.",
+      "mediaType": "text/markdown"
+    },
+    "attachment": [],
+    "tag": [],
+    "inConversation": "https://instance.gnusocial.test/conversation/1338",
+    "to": [
+      "https://www.w3.org/ns/activitystreams#Public"
+    ],
+    "cc": [
+      "https://instance.gnusocial.test/actor/21"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/crates/apub/assets/gnusocial/activities/like_note.json b/crates/apub/assets/gnusocial/activities/like_note.json
new file mode 100644 (file)
index 0000000..09ce40e
--- /dev/null
@@ -0,0 +1,16 @@
+{
+  "type": "Like",
+  "@context": [
+    "https://www.w3.org/ns/activitystreams"
+  ],
+  "id": "https://another_instance.gnusocial.test/activity/41362",
+  "published": "2022-03-20T17:54:15+00:00",
+  "actor": "https://another_instance.gnusocial.test/actor/43",
+  "to": [
+    "https://www.w3.org/ns/activitystreams#Public"
+  ],
+  "cc": [
+    "https://instance.gnusocial.test/actor/42"
+  ],
+  "object": "https://instance.gnusocial.test/object/note/1337"
+}
diff --git a/crates/apub/assets/gnusocial/objects/group.json b/crates/apub/assets/gnusocial/objects/group.json
new file mode 100644 (file)
index 0000000..9be83c2
--- /dev/null
@@ -0,0 +1,42 @@
+{
+  "type": "Group",
+  "streams": [],
+  "@context": [
+    "https://www.w3.org/ns/activitystreams",
+    "https://w3id.org/security/v1",
+    {
+      "gs": "https://www.gnu.org/software/social/ns#"
+    },
+    {
+      "litepub": "http://litepub.social/ns#"
+    },
+    {
+      "chatMessage": "litepub:chatMessage"
+    },
+    {
+      "inConversation": {
+        "@id": "gs:inConversation",
+        "@type": "@id"
+      }
+    }
+  ],
+  "id": "https://instance.gnusocial.test/actor/21",
+  "inbox": "https://instance.gnusocial.test/actor/21/inbox.json",
+  "outbox": "https://instance.gnusocial.test/actor/21/outbox.json",
+  "following": "https://instance.gnusocial.test/actor/21/subscriptions",
+  "followers": "https://instance.gnusocial.test/actor/21/subscribers",
+  "liked": "https://instance.gnusocial.test/actor/21/favourites",
+  "preferredUsername": "hackers",
+  "publicKey": {
+    "id": "https://instance.gnusocial.test/actor/2#public-key",
+    "owner": "https://instance.gnusocial.test/actor/2",
+    "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoZyKL+GyJbTV/ilVBlzz\n8OL/UwNi3KpfV5kQwXU0pPcBbw6y2JOfWnKUT1CfiHG3ntiOFnc+wQfHZk4hRSE8\n9Xe/G5Y215xW+gqx/kjt2GOENqzSzYXdEZ5Qsx6yumZD/yb6VZK9Og0HjX2mpRs9\nbactY76w4BQVntjZ17gSkMhYcyPFZTAIe7QDkeSPk5lkXfTwtaB3YcJSbQ3+s7La\npeEgukQDkrLUIP6cxayKrgUl4fhHdpx1Yk4Bzd/1XkZCjeBca94lP1p2M12amI+Z\nOLSTuLyEiCcku8aN+Ms9plwATmIDaGvKFVk0YVtBHdIJlYXV0yIscab3bqyhsLBK\njwIDAQAB\n-----END PUBLIC KEY-----\n"
+  },
+  "name": "Hackers!",
+  "published": "2022-02-23T21:54:52+00:00",
+  "updated": "2022-02-23T21:55:16+00:00",
+  "url": "https://instance.gnusocial.test/!hackers",
+  "endpoints": {
+    "sharedInbox": "https://instance.gnusocial.test/inbox.json"
+  }
+}
diff --git a/crates/apub/assets/gnusocial/objects/note.json b/crates/apub/assets/gnusocial/objects/note.json
new file mode 100644 (file)
index 0000000..08f58a9
--- /dev/null
@@ -0,0 +1,44 @@
+{
+  "type": "Note",
+  "@context": [
+    "https://www.w3.org/ns/activitystreams",
+    "https://w3id.org/security/v1",
+    {
+      "gs": "https://www.gnu.org/software/social/ns#"
+    },
+    {
+      "litepub": "http://litepub.social/ns#"
+    },
+    {
+      "chatMessage": "litepub:chatMessage"
+    },
+    {
+      "inConversation": {
+        "@id": "gs:inConversation",
+        "@type": "@id"
+      }
+    },
+    {
+      "@language": "en"
+    }
+  ],
+  "id": "https://instance.gnusocial.test/object/note/1339",
+  "published": "2022-03-01T21:00:16+00:00",
+  "attributedTo": "https://instance.gnusocial.test/actor/42",
+  "content": "<p>yay ^^</p>",
+  "mediaType": "text/html",
+  "source": {
+    "content": "yay ^^",
+    "mediaType": "text/plain"
+  },
+  "attachment": [],
+  "tag": [],
+  "inReplyTo": "https://instance.gnusocial.test/object/note/1338",
+  "inConversation": "https://instance.gnusocial.test/conversation/1338",
+  "to": [
+    "https://www.w3.org/ns/activitystreams#Public"
+  ],
+  "cc": [
+    "https://instance.gnusocial.test/actor/42/subscribers"
+  ]
+}
diff --git a/crates/apub/assets/gnusocial/objects/page.json b/crates/apub/assets/gnusocial/objects/page.json
new file mode 100644 (file)
index 0000000..f66d288
--- /dev/null
@@ -0,0 +1,41 @@
+{
+  "type": "Page",
+  "@context": [
+    "https://www.w3.org/ns/activitystreams",
+    "https://w3id.org/security/v1",
+    {
+      "gs": "https://www.gnu.org/software/social/ns#"
+    },
+    {
+      "litepub": "http://litepub.social/ns#"
+    },
+    {
+      "chatMessage": "litepub:chatMessage"
+    },
+    {
+      "inConversation": {
+        "@id": "gs:inConversation",
+        "@type": "@id"
+      }
+    }
+  ],
+  "id": "https://instance.gnusocial.test/object/note/1338",
+  "published": "2022-03-17T23:30:26+00:00",
+  "attributedTo": "https://instance.gnusocial.test/actor/42",
+  "name": "hello, world.",
+  "content": "<p>This is an interesting page.</p>",
+  "mediaType": "text/html",
+  "source": {
+    "content": "This is an interesting page.",
+    "mediaType": "text/markdown"
+  },
+  "attachment": [],
+  "tag": [],
+  "inConversation": "https://instance.gnusocial.test/conversation/1338",
+  "to": [
+    "https://www.w3.org/ns/activitystreams#Public"
+  ],
+  "cc": [
+    "https://instance.gnusocial.test/actor/21"
+  ]
+}
diff --git a/crates/apub/assets/gnusocial/objects/person.json b/crates/apub/assets/gnusocial/objects/person.json
new file mode 100644 (file)
index 0000000..c633843
--- /dev/null
@@ -0,0 +1,42 @@
+{
+  "type": "Person",
+  "streams": [],
+  "@context": [
+    "https://www.w3.org/ns/activitystreams",
+    "https://w3id.org/security/v1",
+    {
+      "gs": "https://www.gnu.org/software/social/ns#"
+    },
+    {
+      "litepub": "http://litepub.social/ns#"
+    },
+    {
+      "chatMessage": "litepub:chatMessage"
+    },
+    {
+      "inConversation": {
+        "@id": "gs:inConversation",
+        "@type": "@id"
+      }
+    }
+  ],
+  "id": "https://instance.gnusocial.test/actor/42",
+  "inbox": "https://instance.gnusocial.test/actor/42/inbox.json",
+  "outbox": "https://instance.gnusocial.test/actor/42/outbox.json",
+  "following": "https://instance.gnusocial.test/actor/42/subscriptions",
+  "followers": "https://instance.gnusocial.test/actor/42/subscribers",
+  "liked": "https://instance.gnusocial.test/actor/42/favourites",
+  "preferredUsername": "diogo",
+  "publicKey": {
+    "id": "https://instance.gnusocial.test/actor/42#public-key",
+    "owner": "https://instance.gnusocial.test/actor/42",
+    "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArBB+3ldwA2qC1hQTtIho\n9KYhvvMlPdydn8dA6OlyIQ3Jy57ADt2e144jDSY5RQ3esmzWm2QqsI8rAsZsAraO\nl2+855y7Fw35WH4GBc7PJ6MLAEvMk1YWeS/rttXaDzh2i4n/AXkMuxDjS1IBqw2w\nn0qTz2sdGcBJ+mop6AB9Qt2lseBc5IW040jSnfLEDDIaYgoc5m2yRsjGKItOh3BG\njGHDb6JB9FySToSMGIt0/tE5k06wfvAxtkxX5dfGeKtciBpC2MGT169iyMIOM8DN\nFhSl8mowtV1NJQ7nN692USrmNvSJjqe9ugPCDPPvwQ5A6A61Qrgpz5pav/o5Sz69\nzQIDAQAB\n-----END PUBLIC KEY-----\n"
+  },
+  "name": "Diogo Peralta Cordeiro",
+  "published": "2022-02-23T17:20:30+00:00",
+  "updated": "2022-02-25T02:12:48+00:00",
+  "url": "https://instance.gnusocial.test/@diogo",
+  "endpoints": {
+    "sharedInbox": "https://instance.gnusocial.test/inbox.json"
+  }
+}
index 822a9d35702b6f8b27340647041376730a2ae8eb..6683a6663b47e800a954b7e2494510141ff2eb80 100644 (file)
@@ -1,11 +1,11 @@
 {
   "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
   "to": [
-    "https://www.w3.org/ns/activitystreams#Public"
+    "http://enterprise.lemmy.ml/c/main"
   ],
   "object": "http://ds9.lemmy.ml/post/1",
   "cc": [
-    "http://enterprise.lemmy.ml/c/main"
+    "https://www.w3.org/ns/activitystreams#Public"
   ],
   "type": "Dislike",
   "id": "http://enterprise.lemmy.ml/activities/dislike/64d40d40-a829-43a5-8247-1fb595b3ca1c"
index 35e969060121bb5faa69c1c6261df51c051c1ffe..e9fc366aec643b3df7fa0d4cd6f632eb30df7b40 100644 (file)
@@ -1,11 +1,11 @@
 {
   "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
   "to": [
-    "https://www.w3.org/ns/activitystreams#Public"
+    "http://enterprise.lemmy.ml/c/main"
   ],
   "object": "http://ds9.lemmy.ml/comment/1",
   "cc": [
-    "http://enterprise.lemmy.ml/c/main"
+    "https://www.w3.org/ns/activitystreams#Public"
   ],
   "type": "Like",
   "id": "http://ds9.lemmy.ml/activities/like/fd61d070-7382-46a9-b2b7-6bb253732877"
index 4123ebab457ac30f38832e067ace02b3c7cc2bb1..2234e41046d3b8e570b5718c6f1e68fe81f2e501 100644 (file)
@@ -1,22 +1,22 @@
 {
   "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
   "to": [
-    "https://www.w3.org/ns/activitystreams#Public"
+    "http://enterprise.lemmy.ml/c/main"
   ],
   "object": {
     "actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
     "to": [
-      "https://www.w3.org/ns/activitystreams#Public"
+      "http://enterprise.lemmy.ml/c/main"
     ],
     "object": "http://ds9.lemmy.ml/post/1",
     "cc": [
-      "http://enterprise.lemmy.ml/c/main"
+      "https://www.w3.org/ns/activitystreams#Public"
     ],
     "type": "Like",
     "id": "http://enterprise.lemmy.ml/activities/like/2227ab2c-79e2-4fca-a1d2-1d67dacf2457"
   },
   "cc": [
-    "http://enterprise.lemmy.ml/c/main"
+    "https://www.w3.org/ns/activitystreams#Public"
   ],
   "type": "Undo",
   "id": "http://enterprise.lemmy.ml/activities/undo/6cc6fb71-39fe-49ea-9506-f0423b101e98"
index 84a6efe50f9eed6182740a53fa262a4be8a9f63c..e22c8fd1fe1a5c24e2c6e9c2e650d3c9a02c3eab 100644 (file)
@@ -1,22 +1,22 @@
 {
   "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
   "to": [
-    "https://www.w3.org/ns/activitystreams#Public"
+    "http://enterprise.lemmy.ml/c/main"
   ],
   "object": {
     "actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
     "to": [
-      "https://www.w3.org/ns/activitystreams#Public"
+      "http://enterprise.lemmy.ml/c/main"
     ],
     "object": "http://ds9.lemmy.ml/comment/1",
     "cc": [
-      "http://enterprise.lemmy.ml/c/main"
+      "https://www.w3.org/ns/activitystreams#Public"
     ],
     "type": "Like",
     "id": "http://ds9.lemmy.ml/activities/like/efcf7ae2-dfcc-4ff4-9ce4-6adf251ff004"
   },
   "cc": [
-    "http://enterprise.lemmy.ml/c/main"
+    "https://www.w3.org/ns/activitystreams#Public"
   ],
   "type": "Undo",
   "id": "http://ds9.lemmy.ml/activities/undo/3518565c-24a7-4d9e-8e0a-f7a2f45ac618"
index 3606756a3eb1dfba0ea692df659c172cb15ef061..16ea64e00e4fab4ab18acbd0da9be2a910915a20 100644 (file)
@@ -28,6 +28,9 @@ use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
 
 impl UndoVote {
+  /// UndoVote has as:Public value in cc field, unlike other activities. This indicates to other
+  /// software (like GNU social, or presumably Mastodon), that the like actor should not be
+  /// disclosed.
   #[tracing::instrument(skip_all)]
   pub async fn send(
     object: &PostOrComment,
@@ -49,9 +52,9 @@ impl UndoVote {
     )?;
     let undo_vote = UndoVote {
       actor: ObjectId::new(actor.actor_id()),
-      to: vec![public()],
+      to: vec![community.actor_id()],
       object,
-      cc: vec![community.actor_id()],
+      cc: vec![public()],
       kind: UndoType::Undo,
       id: id.clone(),
       unparsed: Default::default(),
index 11ed9b3606ae0a0562475dc1c6b80423bf54a1a7..ce6dee28f36562ee6082eb643d26d0709d54f510 100644 (file)
@@ -28,6 +28,8 @@ use lemmy_db_schema::{
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
 
+/// Vote has as:Public value in cc field, unlike other activities. This indicates to other software
+/// (like GNU social, or presumably Mastodon), that the like actor should not be disclosed.
 impl Vote {
   pub(in crate::activities::voting) fn new(
     object: &PostOrComment,
@@ -38,9 +40,9 @@ impl Vote {
   ) -> Result<Vote, LemmyError> {
     Ok(Vote {
       actor: ObjectId::new(actor.actor_id()),
-      to: vec![public()],
+      to: vec![community.actor_id()],
       object: ObjectId::new(object.ap_id()),
-      cc: vec![community.actor_id()],
+      cc: vec![public()],
       kind: kind.clone(),
       id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?,
       unparsed: Default::default(),
index 2b706aac595eff78d897bd9b11c27bdb2801711e..bc8caceaa9ae7662ba65a4ba526a9ec6ffa5f7e0 100644 (file)
@@ -2,18 +2,15 @@ use crate::{
   activities::{verify_is_public, verify_person_in_community},
   check_is_apub_id_valid,
   mentions::collect_non_local_mentions,
+  objects::read_from_string_or_source,
   protocol::{
-    objects::{
-      note::{Note, SourceCompat},
-      tombstone::Tombstone,
-    },
-    Source,
+    objects::{note::Note, tombstone::Tombstone},
+    SourceCompat,
   },
   PostOrComment,
 };
 use activitystreams_kinds::{object::NoteType, public};
 use chrono::NaiveDateTime;
-use html2md::parse_html;
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   object_id::ObjectId,
@@ -121,7 +118,7 @@ impl ApubObject for ApubComment {
       cc: maa.ccs,
       content: markdown_to_html(&self.content),
       media_type: Some(MediaTypeHtml::Html),
-      source: SourceCompat::Lemmy(Source::new(self.content.clone())),
+      source: Some(SourceCompat::new(self.content.clone())),
       in_reply_to,
       published: Some(convert_datetime(self.published)),
       updated: self.updated.map(convert_datetime),
@@ -180,11 +177,7 @@ impl ApubObject for ApubComment {
       .await?;
     let (post, parent_comment_id) = note.get_parents(context, request_counter).await?;
 
-    let content = if let SourceCompat::Lemmy(source) = &note.source {
-      source.content.clone()
-    } else {
-      parse_html(&note.content)
-    };
+    let content = read_from_string_or_source(&note.content, &note.source);
     let content_slurs_removed = remove_slurs(&content, &context.settings().slur_regex());
 
     let form = CommentForm {
@@ -219,6 +212,7 @@ pub(crate) mod tests {
     protocol::tests::file_to_json_object,
   };
   use assert_json_diff::assert_json_include;
+  use html2md::parse_html;
   use lemmy_db_schema::source::site::Site;
   use serial_test::serial;
 
index 87646e91fb7660c82ec3c3979a9bb5c25ec85910..eb9cb218b0d296cde2de3758fa98331491a4d95a 100644 (file)
@@ -7,7 +7,7 @@ use crate::{
   protocol::{
     objects::{group::Group, tombstone::Tombstone, Endpoints},
     ImageObject,
-    Source,
+    SourceCompat,
   },
 };
 use activitystreams_kinds::actor::GroupType;
@@ -85,9 +85,9 @@ impl ApubObject for ApubCommunity {
       kind: GroupType::Group,
       id: ObjectId::new(self.actor_id()),
       preferred_username: self.name.clone(),
-      name: self.title.clone(),
+      name: Some(self.title.clone()),
       summary: self.description.as_ref().map(|b| markdown_to_html(b)),
-      source: self.description.clone().map(Source::new),
+      source: self.description.clone().map(SourceCompat::new),
       icon: self.icon.clone().map(ImageObject::new),
       image: self.banner.clone().map(ImageObject::new),
       sensitive: Some(self.nsfw),
index 9b5ef117dfa65be8c1dbd6ef7941158aee14c277..9968b81e1072e8b7bfeda278a11c022a108e6fdd 100644 (file)
@@ -1,7 +1,7 @@
 use crate::{
   check_is_apub_id_valid,
-  objects::{get_summary_from_string_or_source, verify_image_domain_matches},
-  protocol::{objects::instance::Instance, ImageObject, Source},
+  objects::{read_from_string_or_source_opt, verify_image_domain_matches},
+  protocol::{objects::instance::Instance, ImageObject, SourceCompat},
 };
 use activitystreams_kinds::actor::ServiceType;
 use chrono::NaiveDateTime;
@@ -77,7 +77,7 @@ impl ApubObject for ApubSite {
       id: ObjectId::new(self.actor_id()),
       name: self.name.clone(),
       content: self.sidebar.as_ref().map(|d| markdown_to_html(d)),
-      source: self.sidebar.clone().map(Source::new),
+      source: self.sidebar.clone().map(SourceCompat::new),
       summary: self.description.clone(),
       media_type: self.sidebar.as_ref().map(|_| MediaTypeHtml::Html),
       icon: self.icon.clone().map(ImageObject::new),
@@ -121,10 +121,7 @@ impl ApubObject for ApubSite {
   ) -> Result<Self, LemmyError> {
     let site_form = SiteForm {
       name: apub.name.clone(),
-      sidebar: Some(get_summary_from_string_or_source(
-        &apub.content,
-        &apub.source,
-      )),
+      sidebar: Some(read_from_string_or_source_opt(&apub.content, &apub.source)),
       updated: apub.updated.map(|u| u.clone().naive_local()),
       icon: Some(apub.icon.clone().map(|i| i.url.into())),
       banner: Some(apub.image.clone().map(|i| i.url.into())),
index 6acbf9ad4e7dd596fc0e5304e0aa1496591ad214..9f939aaa86636aa60dbb9687ec6fd735b2bc6eea 100644 (file)
@@ -1,4 +1,4 @@
-use crate::protocol::{ImageObject, Source};
+use crate::protocol::{ImageObject, SourceCompat};
 use html2md::parse_html;
 use lemmy_apub_lib::verify::verify_domains_match;
 use lemmy_utils::LemmyError;
@@ -11,12 +11,20 @@ pub mod person;
 pub mod post;
 pub mod private_message;
 
-pub(crate) fn get_summary_from_string_or_source(
+pub(crate) fn read_from_string_or_source(raw: &str, source: &Option<SourceCompat>) -> String {
+  if let Some(SourceCompat::Lemmy(s)) = source {
+    s.content.clone()
+  } else {
+    parse_html(raw)
+  }
+}
+
+pub(crate) fn read_from_string_or_source_opt(
   raw: &Option<String>,
-  source: &Option<Source>,
+  source: &Option<SourceCompat>,
 ) -> Option<String> {
-  if let Some(source) = &source {
-    Some(source.content.clone())
+  if let Some(SourceCompat::Lemmy(s2)) = source {
+    Some(s2.content.clone())
   } else {
     raw.as_ref().map(|s| parse_html(s))
   }
index 26edc39b9e0c4dea0c1a1fa6135a540a7e1cddcd..a903d5a65f6528cd82d7b8f3cdd7f10b6e7c40ea 100644 (file)
@@ -2,8 +2,8 @@ use crate::{
   check_is_apub_id_valid,
   generate_outbox_url,
   objects::{
-    get_summary_from_string_or_source,
     instance::fetch_instance_actor_for_object,
+    read_from_string_or_source_opt,
     verify_image_domain_matches,
   },
   protocol::{
@@ -12,7 +12,7 @@ use crate::{
       Endpoints,
     },
     ImageObject,
-    Source,
+    SourceCompat,
   },
 };
 use chrono::NaiveDateTime;
@@ -99,7 +99,7 @@ impl ApubObject for ApubPerson {
       preferred_username: self.name.clone(),
       name: self.display_name.clone(),
       summary: self.bio.as_ref().map(|b| markdown_to_html(b)),
-      source: self.bio.clone().map(Source::new),
+      source: self.bio.clone().map(SourceCompat::new),
       icon: self.avatar.clone().map(ImageObject::new),
       image: self.banner.clone().map(ImageObject::new),
       matrix_user_id: self.matrix_user_id.clone(),
@@ -134,7 +134,7 @@ impl ApubObject for ApubPerson {
     let slur_regex = &context.settings().slur_regex();
     check_slurs(&person.preferred_username, slur_regex)?;
     check_slurs_opt(&person.name, slur_regex)?;
-    let bio = get_summary_from_string_or_source(&person.summary, &person.source);
+    let bio = read_from_string_or_source_opt(&person.summary, &person.source);
     check_slurs_opt(&bio, slur_regex)?;
     Ok(())
   }
@@ -156,7 +156,7 @@ impl ApubObject for ApubPerson {
       published: person.published.map(|u| u.naive_local()),
       updated: person.updated.map(|u| u.naive_local()),
       actor_id: Some(person.id.into()),
-      bio: Some(get_summary_from_string_or_source(
+      bio: Some(read_from_string_or_source_opt(
         &person.summary,
         &person.source,
       )),
index c7d48a6862af0ac7e797554f5bdf821124d99e09..d5309dc177f8c2e42a54f3cc0117786e26aad244 100644 (file)
@@ -1,13 +1,14 @@
 use crate::{
   activities::{verify_is_public, verify_person_in_community},
   check_is_apub_id_valid,
+  objects::read_from_string_or_source_opt,
   protocol::{
     objects::{
       page::{Page, PageType},
       tombstone::Tombstone,
     },
     ImageObject,
-    Source,
+    SourceCompat,
   },
 };
 use activitystreams_kinds::public;
@@ -16,7 +17,7 @@ use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   object_id::ObjectId,
   traits::ApubObject,
-  values::{MediaTypeHtml, MediaTypeMarkdown},
+  values::MediaTypeHtml,
   verify::verify_domains_match,
 };
 use lemmy_db_schema::{
@@ -100,12 +101,6 @@ impl ApubObject for ApubPost {
     })
     .await??;
 
-    let source = self.body.clone().map(|body| Source {
-      content: body,
-      media_type: MediaTypeMarkdown::Markdown,
-    });
-    let image = self.thumbnail_url.clone().map(ImageObject::new);
-
     let page = Page {
       r#type: PageType::Page,
       id: ObjectId::new(self.ap_id.clone()),
@@ -115,9 +110,9 @@ impl ApubObject for ApubPost {
       name: self.name.clone(),
       content: self.body.as_ref().map(|b| markdown_to_html(b)),
       media_type: Some(MediaTypeHtml::Html),
-      source,
+      source: self.body.clone().map(SourceCompat::new),
       url: self.url.clone().map(|u| u.into()),
-      image,
+      image: self.thumbnail_url.clone().map(ImageObject::new),
       comments_enabled: Some(!self.locked),
       sensitive: Some(self.nsfw),
       stickied: Some(self.stickied),
@@ -175,10 +170,8 @@ impl ApubObject for ApubPost {
       .map(|u| (u.title, u.description, u.html))
       .unwrap_or((None, None, None));
 
-    let body_slurs_removed = page
-      .source
-      .as_ref()
-      .map(|s| remove_slurs(&s.content, &context.settings().slur_regex()));
+    let body_slurs_removed = read_from_string_or_source_opt(&page.content, &page.source)
+      .map(|s| remove_slurs(&s, &context.settings().slur_regex()));
     let form = PostForm {
       name: page.name,
       url: page.url.map(|u| u.into()),
index 3768577cc7c1d8bffa0dc3e0fc9d078f8e5f9b3c..6f445184a4d3b375c536d20b7b605225054d43c4 100644 (file)
@@ -1,9 +1,11 @@
-use crate::protocol::{
-  objects::chat_message::{ChatMessage, ChatMessageType},
-  Source,
+use crate::{
+  objects::read_from_string_or_source,
+  protocol::{
+    objects::chat_message::{ChatMessage, ChatMessageType},
+    SourceCompat,
+  },
 };
 use chrono::NaiveDateTime;
-use html2md::parse_html;
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   object_id::ObjectId,
@@ -88,7 +90,7 @@ impl ApubObject for ApubPrivateMessage {
       to: [ObjectId::new(recipient.actor_id)],
       content: markdown_to_html(&self.content),
       media_type: Some(MediaTypeHtml::Html),
-      source: Some(Source::new(self.content.clone())),
+      source: Some(SourceCompat::new(self.content.clone())),
       published: Some(convert_datetime(self.published)),
       updated: self.updated.map(convert_datetime),
     };
@@ -131,16 +133,11 @@ impl ApubObject for ApubPrivateMessage {
     let recipient = note.to[0]
       .dereference(context, context.client(), request_counter)
       .await?;
-    let content = if let Some(source) = &note.source {
-      source.content.clone()
-    } else {
-      parse_html(&note.content)
-    };
 
     let form = PrivateMessageForm {
       creator_id: creator.id,
       recipient_id: recipient.id,
-      content,
+      content: read_from_string_or_source(&note.content, &note.source),
       published: note.published.map(|u| u.naive_local()),
       updated: note.updated.map(|u| u.naive_local()),
       deleted: None,
index 0f9509319febdec043c2e0cba5e6fea5892b0017..38c9e345df3e27455fd9427cc851116536ab89e7 100644 (file)
@@ -21,6 +21,7 @@ mod tests {
       create_or_update::{comment::CreateOrUpdateComment, post::CreateOrUpdatePost},
       deletion::delete::Delete,
       following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity},
+      voting::vote::Vote,
     },
     tests::test_json,
   };
@@ -55,4 +56,11 @@ mod tests {
   fn test_parse_friendica_activities() {
     test_json::<CreateOrUpdateComment>("assets/friendica/activities/create_note.json").unwrap();
   }
+
+  #[test]
+  fn test_parse_gnusocial_activities() {
+    test_json::<CreateOrUpdatePost>("assets/gnusocial/activities/create_page.json").unwrap();
+    test_json::<CreateOrUpdateComment>("assets/gnusocial/activities/create_note.json").unwrap();
+    test_json::<Vote>("assets/gnusocial/activities/like_note.json").unwrap();
+  }
 }
index bb384de643a467f05f887d130699ab38de72c8b8..5f60e2b9139d042e44660a4e85c3ea039aafba02 100644 (file)
@@ -4,6 +4,7 @@ use url::Url;
 
 use lemmy_apub_lib::values::MediaTypeMarkdown;
 use lemmy_db_schema::newtypes::DbUrl;
+use serde_json::Value;
 use std::collections::HashMap;
 
 pub mod activities;
@@ -17,12 +18,20 @@ pub struct Source {
   pub(crate) media_type: MediaTypeMarkdown,
 }
 
-impl Source {
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+#[serde(untagged)]
+pub(crate) enum SourceCompat {
+  Lemmy(Source),
+  Other(Value),
+}
+
+impl SourceCompat {
   pub(crate) fn new(content: String) -> Self {
-    Source {
+    SourceCompat::Lemmy(Source {
       content,
       media_type: MediaTypeMarkdown::Markdown,
-    }
+    })
   }
 }
 
index a39f7b250022c5dc40ae29a3da0d45d173c06bdd..8cd37b59b4f98cf686ea33af5054cd25c30a90b7 100644 (file)
@@ -1,6 +1,6 @@
 use crate::{
   objects::{person::ApubPerson, private_message::ApubPrivateMessage},
-  protocol::Source,
+  protocol::SourceCompat,
 };
 use chrono::{DateTime, FixedOffset};
 use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml};
@@ -19,7 +19,7 @@ pub struct ChatMessage {
   pub(crate) content: String,
 
   pub(crate) media_type: Option<MediaTypeHtml>,
-  pub(crate) source: Option<Source>,
+  pub(crate) source: Option<SourceCompat>,
   pub(crate) published: Option<DateTime<FixedOffset>>,
   pub(crate) updated: Option<DateTime<FixedOffset>>,
 }
index 7820624d91303d97588fb8d35f3c6813182d1ead..4114d4cfede77a7d139b078c9f35288a734ddb41 100644 (file)
@@ -6,10 +6,10 @@ use crate::{
   },
   objects::{
     community::ApubCommunity,
-    get_summary_from_string_or_source,
+    read_from_string_or_source_opt,
     verify_image_domain_matches,
   },
-  protocol::{objects::Endpoints, ImageObject, Source},
+  protocol::{objects::Endpoints, ImageObject, SourceCompat},
 };
 use activitystreams_kinds::actor::GroupType;
 use chrono::{DateTime, FixedOffset};
@@ -33,14 +33,14 @@ pub struct Group {
   pub(crate) id: ObjectId<ApubCommunity>,
   /// username, set at account creation and usually fixed after that
   pub(crate) preferred_username: String,
-  /// displayname
-  pub(crate) name: String,
   pub(crate) inbox: Url,
   pub(crate) followers: Url,
   pub(crate) public_key: PublicKey,
 
+  /// title
+  pub(crate) name: Option<String>,
   pub(crate) summary: Option<String>,
-  pub(crate) source: Option<Source>,
+  pub(crate) source: Option<SourceCompat>,
   pub(crate) icon: Option<ImageObject>,
   /// banner
   pub(crate) image: Option<ImageObject>,
@@ -67,17 +67,17 @@ impl Group {
 
     let slur_regex = &context.settings().slur_regex();
     check_slurs(&self.preferred_username, slur_regex)?;
-    check_slurs(&self.name, slur_regex)?;
-    let description = get_summary_from_string_or_source(&self.summary, &self.source);
+    check_slurs_opt(&self.name, slur_regex)?;
+    let description = read_from_string_or_source_opt(&self.summary, &self.source);
     check_slurs_opt(&description, slur_regex)?;
     Ok(())
   }
 
   pub(crate) fn into_form(self) -> CommunityForm {
     CommunityForm {
-      name: self.preferred_username,
-      title: self.name,
-      description: get_summary_from_string_or_source(&self.summary, &self.source),
+      name: self.preferred_username.clone(),
+      title: self.name.unwrap_or(self.preferred_username),
+      description: read_from_string_or_source_opt(&self.summary, &self.source),
       removed: None,
       published: self.published.map(|u| u.naive_local()),
       updated: self.updated.map(|u| u.naive_local()),
index c70a780f41d8c9a878d0b08c5dadfcaa1e08649b..b507d2991ccfa4a5885940103874ed6eab131b6e 100644 (file)
@@ -1,6 +1,6 @@
 use crate::{
   objects::instance::ApubSite,
-  protocol::{ImageObject, Source},
+  protocol::{ImageObject, SourceCompat},
 };
 use activitystreams_kinds::actor::ServiceType;
 use chrono::{DateTime, FixedOffset};
@@ -25,7 +25,7 @@ pub struct Instance {
 
   // sidebar
   pub(crate) content: Option<String>,
-  pub(crate) source: Option<Source>,
+  pub(crate) source: Option<SourceCompat>,
   // short instance description
   pub(crate) summary: Option<String>,
   pub(crate) media_type: Option<MediaTypeHtml>,
index dfe502f3a27153daa06a5395af890a86f5ac4f0d..2d5f9be3ae970ab08f4244be8151140e9d854d9e 100644 (file)
@@ -74,4 +74,12 @@ mod tests {
     test_json::<Person>("assets/friendica/objects/person.json").unwrap();
     test_json::<Note>("assets/friendica/objects/note.json").unwrap();
   }
+
+  #[test]
+  fn test_parse_object_gnusocial() {
+    test_json::<Person>("assets/gnusocial/objects/person.json").unwrap();
+    test_json::<Group>("assets/gnusocial/objects/group.json").unwrap();
+    test_json::<Page>("assets/gnusocial/objects/page.json").unwrap();
+    test_json::<Note>("assets/gnusocial/objects/note.json").unwrap();
+  }
 }
index d5c61c1f8c5e1f17254c6a39c3637d4f6f25cde2..0aac5b854a469168a9e897880e3ebbb43b8888ca 100644 (file)
@@ -2,7 +2,7 @@ use crate::{
   fetcher::post_or_comment::PostOrComment,
   mentions::Mention,
   objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
-  protocol::Source,
+  protocol::SourceCompat,
 };
 use activitystreams_kinds::object::NoteType;
 use chrono::{DateTime, FixedOffset};
@@ -12,7 +12,6 @@ use lemmy_db_schema::{newtypes::CommentId, source::post::Post, traits::Crud};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
 use serde::{Deserialize, Serialize};
-use serde_json::Value;
 use serde_with::skip_serializing_none;
 use std::ops::Deref;
 use url::Url;
@@ -33,30 +32,13 @@ pub struct Note {
   pub(crate) in_reply_to: ObjectId<PostOrComment>,
 
   pub(crate) media_type: Option<MediaTypeHtml>,
-  #[serde(default)]
-  pub(crate) source: SourceCompat,
+  pub(crate) source: Option<SourceCompat>,
   pub(crate) published: Option<DateTime<FixedOffset>>,
   pub(crate) updated: Option<DateTime<FixedOffset>>,
   #[serde(default)]
   pub(crate) tag: Vec<Mention>,
 }
 
-/// Pleroma puts a raw string in the source, so we have to handle it here for deserialization to work
-#[derive(Clone, Debug, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
-#[serde(untagged)]
-pub(crate) enum SourceCompat {
-  Lemmy(Source),
-  Other(Value),
-  None,
-}
-
-impl Default for SourceCompat {
-  fn default() -> Self {
-    SourceCompat::None
-  }
-}
-
 impl Note {
   pub(crate) async fn get_parents(
     &self,
index f6a2823126debf55f8bff85a475464ec60c0db43..a8a8b77279823a25db82bddffab6daaa3652c73f 100644 (file)
@@ -1,8 +1,9 @@
 use crate::{
   objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
-  protocol::{ImageObject, Source},
+  protocol::{ImageObject, SourceCompat},
 };
 use chrono::{DateTime, FixedOffset};
+use itertools::Itertools;
 use lemmy_apub_lib::{
   data::Data,
   object_id::ObjectId,
@@ -37,7 +38,7 @@ pub struct Page {
   pub(crate) cc: Vec<Url>,
   pub(crate) content: Option<String>,
   pub(crate) media_type: Option<MediaTypeHtml>,
-  pub(crate) source: Option<Source>,
+  pub(crate) source: Option<SourceCompat>,
   pub(crate) url: Option<Url>,
   pub(crate) image: Option<ImageObject>,
   pub(crate) comments_enabled: Option<bool>,
@@ -70,9 +71,9 @@ impl Page {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<ApubCommunity, LemmyError> {
-    let mut to_iter = self.to.iter();
+    let mut iter = self.to.iter().merge(self.cc.iter());
     loop {
-      if let Some(cid) = to_iter.next() {
+      if let Some(cid) = iter.next() {
         let cid = ObjectId::new(cid.clone());
         if let Ok(c) = cid
           .dereference(context, context.client(), request_counter)
index 0ab359dfd13c824586a5c039f968e4e8b322e447..bc45d9c308dcdb7f69cf3741707d3b0114a9531e 100644 (file)
@@ -1,6 +1,6 @@
 use crate::{
   objects::person::ApubPerson,
-  protocol::{objects::Endpoints, ImageObject, Source},
+  protocol::{objects::Endpoints, ImageObject, SourceCompat},
 };
 use chrono::{DateTime, FixedOffset};
 use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey};
@@ -31,7 +31,7 @@ pub struct Person {
   /// displayname
   pub(crate) name: Option<String>,
   pub(crate) summary: Option<String>,
-  pub(crate) source: Option<Source>,
+  pub(crate) source: Option<SourceCompat>,
   /// user avatar
   pub(crate) icon: Option<ImageObject>,
   /// user banner