Copy emoji in the subject from parent post
authorrinpatch <rin@patch.cx>
Mon, 22 Mar 2021 17:07:07 +0000 (20:07 +0300)
committerrinpatch <rin@patch.cx>
Mon, 22 Mar 2021 18:20:47 +0000 (21:20 +0300)
Sometimes people put emoji in the subject, which results in the subject
looking broken if someone replies to it from a server that does not
have the said emoji under the same shortcode. This patch solves the problem
by extending the emoji set available in the summary to that of the parent
post.

lib/pleroma/web/common_api/activity_draft.ex
test/fixtures/tesla_mock/emoji-in-summary.json [new file with mode: 0644]
test/pleroma/web/common_api_test.exs
test/support/http_request_mock.ex

index 8668b600ebd5b69f75bbf2556986c89fe1dae21f..80a9fa7bb5eabd4ab78d8462ca4440fa2921be55 100644 (file)
@@ -5,6 +5,7 @@
 defmodule Pleroma.Web.CommonAPI.ActivityDraft do
   alias Pleroma.Activity
   alias Pleroma.Conversation.Participation
+  alias Pleroma.Object
   alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.CommonAPI.Utils
 
@@ -186,6 +187,32 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
   defp object(draft) do
     emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji)
 
+    # Sometimes people create posts with subject containing emoji,
+    # since subjects are usually copied this will result in a broken
+    # subject when someone replies from an instance that does not have
+    # the emoji or has it under different shortcode. This is an attempt
+    # to mitigate this by copying emoji from inReplyTo if they are present
+    # in the subject.
+    summary_emoji =
+      with %Activity{} <- draft.in_reply_to,
+           %Object{data: %{"tag" => [_ | _] = tag}} <- Object.normalize(draft.in_reply_to) do
+        Enum.reduce(tag, %{}, fn
+          %{"type" => "Emoji", "name" => name, "icon" => %{"url" => url}}, acc ->
+            if String.contains?(draft.summary, name) do
+              Map.put(acc, name, url)
+            else
+              acc
+            end
+
+          _, acc ->
+            acc
+        end)
+      else
+        _ -> %{}
+      end
+
+    emoji = Map.merge(emoji, summary_emoji)
+
     object =
       Utils.make_note_data(draft)
       |> Map.put("emoji", emoji)
diff --git a/test/fixtures/tesla_mock/emoji-in-summary.json b/test/fixtures/tesla_mock/emoji-in-summary.json
new file mode 100644 (file)
index 0000000..f77c6e2
--- /dev/null
@@ -0,0 +1,49 @@
+{
+  "@context": [
+    "https://www.w3.org/ns/activitystreams",
+    "https://patch.cx/schemas/litepub-0.1.jsonld",
+    {
+      "@language": "und"
+    }
+  ],
+  "actor": "https://patch.cx/users/rin",
+  "attachment": [],
+  "attributedTo": "https://patch.cx/users/rin",
+  "cc": [
+    "https://patch.cx/users/rin/followers"
+  ],
+  "content": ":joker_disapprove: <br><br>just grabbing a test fixture, nevermind me",
+  "context": "https://patch.cx/contexts/2c3ce4b4-18b1-4b1a-8965-3932027b5326",
+  "conversation": "https://patch.cx/contexts/2c3ce4b4-18b1-4b1a-8965-3932027b5326",
+  "id": "https://patch.cx/objects/a399c28e-c821-4820-bc3e-4afeb044c16f",
+  "published": "2021-03-22T16:54:46.461939Z",
+  "sensitive": null,
+  "source": ":joker_disapprove: \r\n\r\njust grabbing a test fixture, nevermind me",
+  "summary": ":joker_smile: ",
+  "tag": [
+    {
+      "icon": {
+        "type": "Image",
+        "url": "https://patch.cx/emoji/custom/joker_disapprove.png"
+      },
+      "id": "https://patch.cx/emoji/custom/joker_disapprove.png",
+      "name": ":joker_disapprove:",
+      "type": "Emoji",
+      "updated": "1970-01-01T00:00:00Z"
+    },
+    {
+      "icon": {
+        "type": "Image",
+        "url": "https://patch.cx/emoji/custom/joker_smile.png"
+      },
+      "id": "https://patch.cx/emoji/custom/joker_smile.png",
+      "name": ":joker_smile:",
+      "type": "Emoji",
+      "updated": "1970-01-01T00:00:00Z"
+    }
+  ],
+  "to": [
+    "https://www.w3.org/ns/activitystreams#Public"
+  ],
+  "type": "Note"
+}
index 9d005697cd0efc6d4dbe812d99513161dea30f9c..6619f8fc8c7b0fb900d2d86deca01bb5df59d0a3 100644 (file)
@@ -25,6 +25,11 @@ defmodule Pleroma.Web.CommonAPITest do
 
   require Pleroma.Constants
 
+  setup_all do
+    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+    :ok
+  end
+
   setup do: clear_config([:instance, :safe_dm_mentions])
   setup do: clear_config([:instance, :limit])
   setup do: clear_config([:instance, :max_pinned_statuses])
@@ -517,6 +522,27 @@ defmodule Pleroma.Web.CommonAPITest do
       assert url == "#{Pleroma.Web.base_url()}/emoji/blank.png"
     end
 
+    test "it copies emoji from the subject of the parent post" do
+      %Object{} =
+        object =
+        Object.normalize("https://patch.cx/objects/a399c28e-c821-4820-bc3e-4afeb044c16f",
+          fetch: true
+        )
+
+      activity = Activity.get_create_by_object_ap_id(object.data["id"])
+      user = insert(:user)
+
+      {:ok, reply_activity} =
+        CommonAPI.post(user, %{
+          in_reply_to_id: activity.id,
+          status: ":joker_disapprove:",
+          spoiler_text: ":joker_smile:"
+        })
+
+      assert Object.normalize(reply_activity).data["emoji"][":joker_smile:"]
+      refute Object.normalize(reply_activity).data["emoji"][":joker_disapprove:"]
+    end
+
     test "deactivated users can't post" do
       user = insert(:user, is_active: false)
       assert {:error, _} = CommonAPI.post(user, %{status: "ye"})
index 1e98020f0106b048cec226fdfdf47427c774c3c7..eb692fab5d1366e707578496b819bb305e3e1c8e 100644 (file)
@@ -1278,6 +1278,15 @@ defmodule HttpRequestMock do
      }}
   end
 
+  def get("https://patch.cx/objects/a399c28e-c821-4820-bc3e-4afeb044c16f", _, _, _) do
+    {:ok,
+     %Tesla.Env{
+       status: 200,
+       body: File.read!("test/fixtures/tesla_mock/emoji-in-summary.json"),
+       headers: activitypub_object_headers()
+     }}
+  end
+
   def get(url, query, body, headers) do
     {:error,
      "Mock response not implemented for GET #{inspect(url)}, #{query}, #{inspect(body)}, #{