forked from AkkomaGang/akkoma
[#1505] Added Mastodon-compatible replies
collection to Note federated representation.
This commit is contained in:
parent
6e6f1ead31
commit
cf96c40057
6 changed files with 145 additions and 1 deletions
|
@ -620,6 +620,10 @@
|
||||||
|
|
||||||
config :pleroma, configurable_from_database: false
|
config :pleroma, configurable_from_database: false
|
||||||
|
|
||||||
|
config :pleroma, :mastodon_compatibility,
|
||||||
|
# https://git.pleroma.social/pleroma/pleroma/issues/1505
|
||||||
|
federated_note_replies_limit: 5
|
||||||
|
|
||||||
config :swarm, node_blacklist: [~r/myhtml_.*$/]
|
config :swarm, node_blacklist: [~r/myhtml_.*$/]
|
||||||
# Import environment specific config. This must remain at the bottom
|
# Import environment specific config. This must remain at the bottom
|
||||||
# of this file so it overrides the configuration defined above.
|
# of this file so it overrides the configuration defined above.
|
||||||
|
|
|
@ -3089,6 +3089,20 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
group: :pleroma,
|
||||||
|
key: :mastodon_compatibility,
|
||||||
|
type: :group,
|
||||||
|
description: "Mastodon compatibility-related settings.",
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :federated_note_replies_limit,
|
||||||
|
type: :integer,
|
||||||
|
description:
|
||||||
|
"The number of Note self-reply URIs to be included with outgoing federation (`5` to mimic Mastodon hardcoded value, `0` to disable)."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
type: :group,
|
type: :group,
|
||||||
|
|
|
@ -329,4 +329,23 @@ def direct_conversation_id(activity, for_user) do
|
||||||
_ -> nil
|
_ -> nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def replies(activity, opts \\ []) do
|
||||||
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
|
query =
|
||||||
|
Activity
|
||||||
|
|> Queries.by_type("Create")
|
||||||
|
|> Queries.by_object_in_reply_to_id(object.data["id"], skip_preloading: true)
|
||||||
|
|> order_by([activity], asc: activity.id)
|
||||||
|
|
||||||
|
if opts[:self_only] do
|
||||||
|
where(query, [a], a.actor == ^activity.actor)
|
||||||
|
else
|
||||||
|
query
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self_replies(activity, opts \\ []),
|
||||||
|
do: replies(activity, Keyword.put(opts, :self_only, true))
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Activity.Queries do
|
||||||
Contains queries for Activity.
|
Contains queries for Activity.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import Ecto.Query, only: [from: 2]
|
import Ecto.Query, only: [from: 2, where: 3]
|
||||||
|
|
||||||
@type query :: Ecto.Queryable.t() | Activity.t()
|
@type query :: Ecto.Queryable.t() | Activity.t()
|
||||||
|
|
||||||
|
@ -63,6 +63,22 @@ def by_object_id(query, object_id) when is_binary(object_id) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec by_object_id(query, String.t()) :: query
|
||||||
|
def by_object_in_reply_to_id(query, in_reply_to_id, opts \\ []) do
|
||||||
|
query =
|
||||||
|
if opts[:skip_preloading] do
|
||||||
|
Activity.with_joined_object(query)
|
||||||
|
else
|
||||||
|
Activity.with_preloaded_object(query)
|
||||||
|
end
|
||||||
|
|
||||||
|
where(
|
||||||
|
query,
|
||||||
|
[activity, object: o],
|
||||||
|
fragment("(?)->>'inReplyTo' = ?", o.data, ^to_string(in_reply_to_id))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
@spec by_type(query, String.t()) :: query
|
@spec by_type(query, String.t()) :: query
|
||||||
def by_type(query \\ Activity, activity_type) do
|
def by_type(query \\ Activity, activity_type) do
|
||||||
from(
|
from(
|
||||||
|
|
|
@ -903,6 +903,49 @@ def set_reply_to_uri(%{"inReplyTo" => in_reply_to} = object) when is_binary(in_r
|
||||||
|
|
||||||
def set_reply_to_uri(obj), do: obj
|
def set_reply_to_uri(obj), do: obj
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Serialized Mastodon-compatible `replies` collection containing _self-replies_.
|
||||||
|
Based on Mastodon's ActivityPub::NoteSerializer#replies.
|
||||||
|
"""
|
||||||
|
def set_replies(obj) do
|
||||||
|
limit = Pleroma.Config.get([:mastodon_compatibility, :federated_note_replies_limit], 0)
|
||||||
|
|
||||||
|
replies_uris =
|
||||||
|
with true <- limit > 0 || nil,
|
||||||
|
%Activity{} = activity <- Activity.get_create_by_object_ap_id(obj["id"]) do
|
||||||
|
activity
|
||||||
|
|> Activity.self_replies()
|
||||||
|
|> select([a], fragment("?->>'id'", a.data))
|
||||||
|
|> limit(^limit)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
|
set_replies(obj, replies_uris || [])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp set_replies(obj, replies_uris) when replies_uris in [nil, []] do
|
||||||
|
obj
|
||||||
|
end
|
||||||
|
|
||||||
|
defp set_replies(obj, replies_uris) do
|
||||||
|
# Note: stubs (Mastodon doesn't make separate requests via those URIs in FetchRepliesService)
|
||||||
|
masto_replies_uri = nil
|
||||||
|
masto_replies_next_page_uri = nil
|
||||||
|
|
||||||
|
replies_collection = %{
|
||||||
|
"type" => "Collection",
|
||||||
|
"id" => masto_replies_uri,
|
||||||
|
"first" => %{
|
||||||
|
"type" => "Collection",
|
||||||
|
"part_of" => masto_replies_uri,
|
||||||
|
"items" => replies_uris,
|
||||||
|
"next" => masto_replies_next_page_uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map.merge(obj, %{"replies" => replies_collection})
|
||||||
|
end
|
||||||
|
|
||||||
# Prepares the object of an outgoing create activity.
|
# Prepares the object of an outgoing create activity.
|
||||||
def prepare_object(object) do
|
def prepare_object(object) do
|
||||||
object
|
object
|
||||||
|
@ -914,6 +957,7 @@ def prepare_object(object) do
|
||||||
|> prepare_attachments
|
|> prepare_attachments
|
||||||
|> set_conversation
|
|> set_conversation
|
||||||
|> set_reply_to_uri
|
|> set_reply_to_uri
|
||||||
|
|> set_replies
|
||||||
|> strip_internal_fields
|
|> strip_internal_fields
|
||||||
|> strip_internal_tags
|
|> strip_internal_tags
|
||||||
|> set_type
|
|> set_type
|
||||||
|
|
|
@ -2027,4 +2027,51 @@ test "returns object with emoji when object contains map tag" do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "set_replies/1" do
|
||||||
|
clear_config([:mastodon_compatibility, :federated_note_replies_limit]) do
|
||||||
|
Pleroma.Config.put([:mastodon_compatibility, :federated_note_replies_limit], 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns unmodified object if activity doesn't have self-replies" do
|
||||||
|
data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
|
||||||
|
assert Transmogrifier.set_replies(data) == data
|
||||||
|
end
|
||||||
|
|
||||||
|
test "sets `replies` collection with a limited number of self-replies" do
|
||||||
|
[user, another_user] = insert_list(2, :user)
|
||||||
|
|
||||||
|
{:ok, %{id: id1} = activity} = CommonAPI.post(user, %{"status" => "1"})
|
||||||
|
|
||||||
|
{:ok, %{id: id2} = self_reply1} =
|
||||||
|
CommonAPI.post(user, %{"status" => "self-reply 1", "in_reply_to_status_id" => id1})
|
||||||
|
|
||||||
|
{:ok, self_reply2} =
|
||||||
|
CommonAPI.post(user, %{"status" => "self-reply 2", "in_reply_to_status_id" => id1})
|
||||||
|
|
||||||
|
# Assuming to _not_ be present in `replies` due to :federated_note_replies_limit is set to 2
|
||||||
|
{:ok, _} =
|
||||||
|
CommonAPI.post(user, %{"status" => "self-reply 3", "in_reply_to_status_id" => id1})
|
||||||
|
|
||||||
|
{:ok, _} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
"status" => "self-reply to self-reply",
|
||||||
|
"in_reply_to_status_id" => id2
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, _} =
|
||||||
|
CommonAPI.post(another_user, %{
|
||||||
|
"status" => "another user's reply",
|
||||||
|
"in_reply_to_status_id" => id1
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity)
|
||||||
|
replies_uris = Enum.map([self_reply1, self_reply2], fn a -> a.data["id"] end)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"type" => "Collection",
|
||||||
|
"first" => %{"type" => "Collection", "items" => ^replies_uris}
|
||||||
|
} = Transmogrifier.set_replies(object.data)["replies"]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue