AP ingest: accept Emoji icon variants
This commit is contained in:
parent
e9e3d255e5
commit
9912a9f603
6 changed files with 246 additions and 8 deletions
|
|
@ -59,12 +59,29 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.TagValidator do
|
||||||
end
|
end
|
||||||
|
|
||||||
def changeset(struct, %{"type" => "Emoji"} = data) do
|
def changeset(struct, %{"type" => "Emoji"} = data) do
|
||||||
data = Map.put(data, "name", String.trim(data["name"], ":"))
|
data =
|
||||||
|
data
|
||||||
|
|> Map.put("name", String.trim(data["name"], ":"))
|
||||||
|
|> normalize_emoji_icon()
|
||||||
|
|
||||||
|
case data["icon"] do
|
||||||
|
%{"url" => url} when is_binary(url) ->
|
||||||
|
if valid_http_url?(url) do
|
||||||
struct
|
struct
|
||||||
|> cast(data, [:type, :name, :updated, :id])
|
|> cast(data, [:type, :name, :updated, :id])
|
||||||
|> cast_embed(:icon, with: &icon_changeset/2)
|
|> cast_embed(:icon, with: &icon_changeset/2)
|
||||||
|> validate_required([:type, :name, :icon])
|
|> validate_required([:type, :name, :icon])
|
||||||
|
else
|
||||||
|
struct
|
||||||
|
|> cast(data, [])
|
||||||
|
|> Map.put(:action, :ignore)
|
||||||
|
end
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
struct
|
||||||
|
|> cast(data, [])
|
||||||
|
|> Map.put(:action, :ignore)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def changeset(struct, %{"type" => "Link"} = data) do
|
def changeset(struct, %{"type" => "Link"} = data) do
|
||||||
|
|
@ -101,6 +118,41 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.TagValidator do
|
||||||
|
|
||||||
defp infer_type(data), do: data
|
defp infer_type(data), do: data
|
||||||
|
|
||||||
|
defp normalize_emoji_icon(%{"icon" => icon} = data) when is_binary(icon) do
|
||||||
|
Map.put(data, "icon", %{"type" => "Image", "url" => icon})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_emoji_icon(%{"icon" => icon} = data) when is_map(icon) do
|
||||||
|
case extract_icon_url(icon) do
|
||||||
|
url when is_binary(url) -> Map.put(data, "icon", %{"type" => "Image", "url" => url})
|
||||||
|
_ -> Map.delete(data, "icon")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_emoji_icon(data), do: data
|
||||||
|
|
||||||
|
defp extract_icon_url(%{"url" => url}) when is_binary(url), do: url
|
||||||
|
|
||||||
|
defp extract_icon_url(%{"url" => %{"href" => href}}) when is_binary(href), do: href
|
||||||
|
|
||||||
|
defp extract_icon_url(%{"url" => [first | _]}) do
|
||||||
|
cond do
|
||||||
|
is_binary(first) -> first
|
||||||
|
is_map(first) -> first["href"]
|
||||||
|
true -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp extract_icon_url(%{"href" => href}) when is_binary(href), do: href
|
||||||
|
|
||||||
|
defp extract_icon_url(_), do: nil
|
||||||
|
|
||||||
|
defp valid_http_url?(url) when is_binary(url) do
|
||||||
|
match?({:ok, _}, ObjectValidators.Uri.cast(url))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp valid_http_url?(_), do: false
|
||||||
|
|
||||||
def icon_changeset(struct, data) do
|
def icon_changeset(struct, data) do
|
||||||
struct
|
struct
|
||||||
|> cast(data, [:type, :url])
|
|> cast(data, [:type, :url])
|
||||||
|
|
|
||||||
|
|
@ -386,6 +386,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
|
|
||||||
def fix_url(object), do: object
|
def fix_url(object), do: object
|
||||||
|
|
||||||
|
defp emoji_icon_url(icon) when is_binary(icon), do: icon
|
||||||
|
|
||||||
|
defp emoji_icon_url(%{"url" => url}) when is_binary(url), do: url
|
||||||
|
|
||||||
|
defp emoji_icon_url(%{"url" => %{"href" => href}}) when is_binary(href), do: href
|
||||||
|
|
||||||
|
defp emoji_icon_url(%{"url" => [first | _]}) do
|
||||||
|
cond do
|
||||||
|
is_binary(first) -> first
|
||||||
|
is_map(first) -> first["href"] || first["url"] || first["id"]
|
||||||
|
true -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp emoji_icon_url(%{"href" => href}) when is_binary(href), do: href
|
||||||
|
|
||||||
|
defp emoji_icon_url(_), do: nil
|
||||||
|
|
||||||
def fix_emoji(%{"tag" => tags} = object) when is_list(tags) do
|
def fix_emoji(%{"tag" => tags} = object) when is_list(tags) do
|
||||||
emoji =
|
emoji =
|
||||||
tags
|
tags
|
||||||
|
|
@ -393,7 +411,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
|> Enum.reduce(%{}, fn data, mapping ->
|
|> Enum.reduce(%{}, fn data, mapping ->
|
||||||
name = String.trim(data["name"], ":")
|
name = String.trim(data["name"], ":")
|
||||||
|
|
||||||
Map.put(mapping, name, data["icon"]["url"])
|
case emoji_icon_url(data["icon"]) do
|
||||||
|
url when is_binary(url) -> Map.put(mapping, name, url)
|
||||||
|
_ -> mapping
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Map.put(object, "emoji", emoji)
|
Map.put(object, "emoji", emoji)
|
||||||
|
|
@ -401,9 +422,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
|
|
||||||
def fix_emoji(%{"tag" => %{"type" => "Emoji"} = tag} = object) do
|
def fix_emoji(%{"tag" => %{"type" => "Emoji"} = tag} = object) do
|
||||||
name = String.trim(tag["name"], ":")
|
name = String.trim(tag["name"], ":")
|
||||||
emoji = %{name => tag["icon"]["url"]}
|
|
||||||
|
|
||||||
Map.put(object, "emoji", emoji)
|
with url when is_binary(url) <- emoji_icon_url(tag["icon"]) do
|
||||||
|
Map.put(object, "emoji", %{name => url})
|
||||||
|
else
|
||||||
|
_ -> object
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_emoji(object), do: object
|
def fix_emoji(object), do: object
|
||||||
|
|
|
||||||
32
test/fixtures/fedidev.fun/emoji_icon_string.json
vendored
Normal file
32
test/fixtures/fedidev.fun/emoji_icon_string.json
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams"
|
||||||
|
],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "https://fedidev.fun/activity/emoji-icon-string",
|
||||||
|
"actor": "https://fedidev.fun/users/alice",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc": [],
|
||||||
|
"published": "2025-11-21T14:40:00Z",
|
||||||
|
"object": {
|
||||||
|
"type": "Note",
|
||||||
|
"id": "https://fedidev.fun/objects/note-emoji-icon-string",
|
||||||
|
"attributedTo": "https://fedidev.fun/users/alice",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc": [],
|
||||||
|
"published": "2025-11-21T14:40:00Z",
|
||||||
|
"content": "emoji icon as string :cow:",
|
||||||
|
"tag": [
|
||||||
|
{
|
||||||
|
"type": "Emoji",
|
||||||
|
"name": ":cow:",
|
||||||
|
"icon": "https://fedidev.fun/static/cow.png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
34
test/fixtures/fedidev.fun/emoji_icon_url_missing_type.json
vendored
Normal file
34
test/fixtures/fedidev.fun/emoji_icon_url_missing_type.json
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams"
|
||||||
|
],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "https://fedidev.fun/activity/emoji-icon-url-missing-type",
|
||||||
|
"actor": "https://fedidev.fun/users/alice",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc": [],
|
||||||
|
"published": "2025-11-21T14:40:01Z",
|
||||||
|
"object": {
|
||||||
|
"type": "Note",
|
||||||
|
"id": "https://fedidev.fun/objects/note-emoji-icon-url-missing-type",
|
||||||
|
"attributedTo": "https://fedidev.fun/users/alice",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc": [],
|
||||||
|
"published": "2025-11-21T14:40:01Z",
|
||||||
|
"content": "emoji icon url missing type :cow2:",
|
||||||
|
"tag": [
|
||||||
|
{
|
||||||
|
"type": "Emoji",
|
||||||
|
"name": ":cow2:",
|
||||||
|
"icon": {
|
||||||
|
"url": "https://fedidev.fun/static/cow.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
31
test/fixtures/fedidev.fun/emoji_missing_icon.json
vendored
Normal file
31
test/fixtures/fedidev.fun/emoji_missing_icon.json
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams"
|
||||||
|
],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "https://fedidev.fun/activity/emoji-missing-icon",
|
||||||
|
"actor": "https://fedidev.fun/users/alice",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc": [],
|
||||||
|
"published": "2025-11-21T14:40:02Z",
|
||||||
|
"object": {
|
||||||
|
"type": "Note",
|
||||||
|
"id": "https://fedidev.fun/objects/note-emoji-missing-icon",
|
||||||
|
"attributedTo": "https://fedidev.fun/users/alice",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc": [],
|
||||||
|
"published": "2025-11-21T14:40:02Z",
|
||||||
|
"content": "emoji missing icon :cow3:",
|
||||||
|
"tag": [
|
||||||
|
{
|
||||||
|
"type": "Emoji",
|
||||||
|
"name": ":cow3:"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.Transmogrifier.FedidevFunEmojiTest do
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
setup_all do
|
||||||
|
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
defp ensure_remote_actor!(ap_id) when is_binary(ap_id) do
|
||||||
|
case User.get_cached_by_ap_id(ap_id) do
|
||||||
|
%User{} ->
|
||||||
|
:ok
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
ap_id: ap_id,
|
||||||
|
follower_address: ap_id <> "/followers",
|
||||||
|
following_address: ap_id <> "/following",
|
||||||
|
featured_address: ap_id <> "/collections/featured",
|
||||||
|
inbox: ap_id <> "/inbox"
|
||||||
|
)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp ingest_fixture!(rel_path) do
|
||||||
|
data = rel_path |> File.read!() |> Jason.decode!()
|
||||||
|
ensure_remote_actor!(data["actor"])
|
||||||
|
|
||||||
|
assert {:ok, activity} = Transmogrifier.handle_incoming(data)
|
||||||
|
object = Object.normalize(activity.data["object"], fetch: false)
|
||||||
|
|
||||||
|
{activity, object}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ingests Emoji tag with string icon" do
|
||||||
|
{_activity, object} = ingest_fixture!("test/fixtures/fedidev.fun/emoji_icon_string.json")
|
||||||
|
assert object.data["emoji"]["cow"] == "https://fedidev.fun/static/cow.png"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ingests Emoji tag with icon missing type" do
|
||||||
|
{_activity, object} =
|
||||||
|
ingest_fixture!("test/fixtures/fedidev.fun/emoji_icon_url_missing_type.json")
|
||||||
|
|
||||||
|
assert object.data["emoji"]["cow2"] == "https://fedidev.fun/static/cow.png"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ignores Emoji tag missing icon" do
|
||||||
|
{_activity, object} = ingest_fixture!("test/fixtures/fedidev.fun/emoji_missing_icon.json")
|
||||||
|
assert object.data["emoji"] == %{}
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue