Quote posting #113

Merged
floatingghost merged 9 commits from quote-posts into develop 2022-07-25 16:30:07 +00:00
10 changed files with 197 additions and 0 deletions
Showing only changes of commit e8226ca0c3 - Show all commits

View file

@ -407,6 +407,8 @@
accept: [],
reject: []
config :pleroma, :mrf_inline_quote, prefix: "Quote"
# threshold of 7 days
config :pleroma, :mrf_object_age,
threshold: 604_800,

View file

@ -292,6 +292,13 @@ def get_in_reply_to_activity(%Activity{} = activity) do
get_in_reply_to_activity_from_object(Object.normalize(activity, fetch: false))
end
def get_quoted_activity_from_object(%Object{data: %{"quoteUri" => ap_id}}) do
IO.puts(ap_id)
get_create_by_object_ap_id_with_object(ap_id)
end
def get_quoted_activity_from_object(_), do: nil
def normalize(%Activity{data: %{"id" => ap_id}}), do: get_by_ap_id_with_object(ap_id)
def normalize(%{"id" => ap_id}), do: get_by_ap_id_with_object(ap_id)
def normalize(ap_id) when is_binary(ap_id), do: get_by_ap_id_with_object(ap_id)

View file

@ -168,6 +168,7 @@ def note(%ActivityDraft{} = draft) do
"tag" => Keyword.values(draft.tags) |> Enum.uniq()
}
|> add_in_reply_to(draft.in_reply_to)
|> add_quote(draft.quote)
|> Map.merge(draft.extra)
{:ok, data, []}
@ -183,6 +184,16 @@ defp add_in_reply_to(object, in_reply_to) do
end
end
defp add_quote(object, nil), do: object
defp add_quote(object, quote) do
with %Object{} = quote_object <- Object.normalize(quote, fetch: false) do
Map.put(object, "quoteUri", quote_object.data["id"])
else
_ -> object
end
end
def answer(user, object, name) do
{:ok,
%{

View file

@ -0,0 +1,71 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do
@moduledoc "Force a quote line into the message content."
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp build_inline_quote(prefix, url) do
"<span class=\"quote-inline\"><br/><br/>#{prefix}: <a href=\"#{url}\">#{url}</a></span>"
end
defp has_inline_quote?(content, quote_url) do
cond do
# Does the quote URL exist in the content?
content =~ quote_url -> true
# Does the content already have a .quote-inline span?
content =~ "<span class=\"quote-inline\">" -> true
# No inline quote found
true -> false
end
end
defp filter_object(%{"quoteUrl" => quote_url} = object) do
content = object["content"] || ""
if has_inline_quote?(content, quote_url) do
object
else
prefix = Pleroma.Config.get([:mrf_inline_quote, :prefix])
content =
if String.ends_with?(content, "</p>"),
do:
String.trim_trailing(content, "</p>") <>
build_inline_quote(prefix, quote_url) <> "</p>",
else: content <> build_inline_quote(prefix, quote_url)
Map.put(object, "content", content)
end
end
@impl true
def filter(%{"object" => %{"quoteUrl" => _} = object} = activity) do
{:ok, Map.put(activity, "object", filter_object(object))}
end
@impl true
def filter(object), do: {:ok, object}
@impl true
def describe, do: {:ok, %{}}
@impl true
def config_description do
%{
key: :mrf_inline_quote,
related_policy: "Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy",
label: "MRF Inline Quote",
description: "Force quote post URLs inline",
children: [
%{
key: :prefix,
type: :string,
description: "Prefix before the link",
suggestions: ["RT", "QT", "RE", "RN"]
}
]
}
end
end

View file

@ -43,6 +43,7 @@ def fix_object(object, options \\ []) do
|> fix_content_map()
|> fix_addressing()
|> fix_summary()
|> fix_quote_url()
end
def fix_summary(%{"summary" => nil} = object) do
@ -879,6 +880,43 @@ defp strip_internal_tags(%{"tag" => tags} = object) do
defp strip_internal_tags(object), do: object
def fix_quote_url(object, options \\ [])
def fix_quote_url(%{"quoteUri" => quote_url} = object, options)
when not is_nil(quote_url) do
with {:ok, quoted_object} <- get_obj_helper(quote_url, options),
%Activity{} <- Activity.get_create_by_object_ap_id(quoted_object.data["id"]) do
Map.put(object, "quoteUri", quoted_object.data["id"])
else
e ->
Logger.warn("Couldn't fetch #{inspect(quote_url)}, error: #{inspect(e)}")
object
end
end
# Soapbox
def fix_quote_url(%{"quoteUrl" => quote_url} = object, options) do
object
|> Map.put("quoteUri", quote_url)
|> fix_quote_url(options)
end
# Old Fedibird (bug)
# https://github.com/fedibird/mastodon/issues/9
def fix_quote_url(%{"quoteURL" => quote_url} = object, options) do
object
|> Map.put("quoteUri", quote_url)
|> fix_quote_url(options)
end
def fix_quote_url(%{"_misskey_quote" => quote_url} = object, options) do
object
|> Map.put("quoteUri", quote_url)
|> fix_quote_url(options)
end
def fix_quote_url(object, _), do: object
def perform(:user_upgrade, user) do
# we pass a fake user so that the followers collection is stripped away
old_follower_address = User.ap_followers(%User{nickname: user.nickname})

View file

@ -496,6 +496,12 @@ defp create_request do
type: :string,
description:
"Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`."
},
quote_id: %Schema{
nullable: true,
type: :string,
description:
"Will quote a given status."
}
},
example: %{

View file

@ -132,6 +132,14 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
pinned: %Schema{
type: :boolean,
description: "Have you pinned this status? Only appears if the status is pinnable."
},
quote_id: %Schema{
type: :string,
description: "ID of the status being quoted",
nullable: true
},
quote: %Schema{
},
pleroma: %Schema{
type: :object,
@ -204,6 +212,25 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
}
}
},
akkoma: %Schema{
type: :object,
properties: %{
source: %Schema{
type: :object,
properties: %{
content: %Schema{
type: :string,
description: "The source content of the status"
},
mediaType: %{
type: :string,
description: "The source MIME type of the status",
example: "text/plain"
},
}
}
}
},
poll: %Schema{allOf: [Poll], nullable: true, description: "The poll attached to the status"},
reblog: %Schema{
allOf: [%OpenApiSpex.Reference{"$ref": "#/components/schemas/Status"}],

View file

@ -22,6 +22,8 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
attachments: [],
in_reply_to: nil,
in_reply_to_conversation: nil,
quote_id: nil,
quote: nil,
visibility: nil,
expires_at: nil,
extra: nil,
@ -37,6 +39,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
preview?: false,
changes: %{}
def new(user, params) do
%__MODULE__{user: user}
|> put_params(params)
@ -54,6 +57,7 @@ def create(user, params) do
|> with_valid(&in_reply_to/1)
|> with_valid(&in_reply_to_conversation/1)
|> with_valid(&visibility/1)
|> with_valid(&quote_id/1)
|> content()
|> with_valid(&to_and_cc/1)
|> with_valid(&context/1)
@ -108,6 +112,18 @@ defp in_reply_to_conversation(draft) do
%__MODULE__{draft | in_reply_to_conversation: in_reply_to_conversation}
end
defp quote_id(%{params: %{quote_id: ""}} = draft), do: draft
defp quote_id(%{params: %{quote_id: id}} = draft) when is_binary(id) do
%__MODULE__{draft | quote: Activity.get_by_id(id)}
end
defp quote_id(%{params: %{quote_id: %Activity{} = quote}} = draft) do
%__MODULE__{draft | quote: quote}
end
defp quote_id(draft), do: draft
defp visibility(%{params: params} = draft) do
case CommonAPI.get_visibility(params, draft.in_reply_to, draft.in_reply_to_conversation) do
{visibility, "direct"} when visibility != "direct" ->

View file

@ -112,6 +112,7 @@ def perform(:incoming_ap_doc, params) do
e ->
# Just drop those for now
Logger.debug(fn -> "Unhandled activity\n" <> Jason.encode!(params, pretty: true) end)
IO.inspect(e)
{:error, e}
end
end

View file

@ -329,6 +329,8 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
{pinned?, pinned_at} = pin_data(object, user)
quote = Activity.get_quoted_activity_from_object(object)
%{
id: to_string(activity.id),
uri: object.data["id"],
@ -363,6 +365,8 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
application: build_application(object.data["generator"]),
language: nil,
emojis: build_emojis(object.data["emoji"]),
quote_id: (if quote, do: quote.id, else: nil),
quote: maybe_render_quote(quote, opts),
pleroma: %{
local: activity.local,
conversation_id: get_context_id(activity),
@ -604,4 +608,18 @@ defp build_image_url(%URI{} = image_url_data, %URI{} = page_url_data) do
end
defp build_image_url(_, _), do: nil
defp maybe_render_quote(nil, _), do: nil
defp maybe_render_quote(quote, opts) do
if opts[:do_not_recurse] do
nil
else
opts =
opts
|> Map.put(:activity, quote)
|> Map.put(:do_not_recurse, true)
render("show.json", opts)
end
end
end