add tests, inline prefix mrf
ci/woodpecker/push/release Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/pr/release Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details

This commit is contained in:
FloatingGhost 2022-07-25 12:26:31 +01:00
parent 29be90332f
commit 5879862aa3
9 changed files with 317 additions and 15 deletions

View File

@ -407,7 +407,7 @@ config :pleroma, :mrf_vocabulary,
accept: [],
reject: []
config :pleroma, :mrf_inline_quote, prefix: "Quote"
config :pleroma, :mrf_inline_quote, prefix: "RE"
# threshold of 7 days
config :pleroma, :mrf_object_age,

View File

@ -21,7 +21,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do
end
end
defp filter_object(%{"quoteUrl" => quote_url} = object) do
defp filter_object(%{"quoteUri" => quote_url} = object) do
content = object["content"] || ""
if has_inline_quote?(content, quote_url) do
@ -30,18 +30,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do
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)
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)
end
Map.put(object, "content", content)
end
end
@impl true
def filter(%{"object" => %{"quoteUrl" => _} = object} = activity) do
def filter(%{"object" => %{"quoteUri" => _} = object} = activity) do
{:ok, Map.put(activity, "object", filter_object(object))}
end
@ -63,7 +63,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do
key: :prefix,
type: :string,
description: "Prefix before the link",
suggestions: ["RT", "QT", "RE", "RN"]
suggestions: ["RE", "QT", "RT", "RN"]
}
]
}

View File

@ -0,0 +1,52 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"ostatus": "http://ostatus.org#",
"atomUri": "ostatus:atomUri",
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"conversation": "ostatus:conversation",
"sensitive": "as:sensitive",
"toot": "http://joinmastodon.org/ns#",
"votersCount": "toot:votersCount",
"expiry": "toot:expiry"
}
],
"id": "https://fedibird.com/users/noellabo/statuses/107663670404015196",
"type": "Note",
"summary": null,
"inReplyTo": null,
"published": "2022-01-22T02:07:16Z",
"url": "https://fedibird.com/@noellabo/107663670404015196",
"attributedTo": "https://fedibird.com/users/noellabo",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://fedibird.com/users/noellabo/followers"
],
"sensitive": false,
"atomUri": "https://fedibird.com/users/noellabo/statuses/107663670404015196",
"inReplyToAtomUri": null,
"conversation": "tag:fedibird.com,2022-01-22:objectId=107663670404038002:objectType=Conversation",
"context": "https://fedibird.com/contexts/107663670404038002",
"quoteURL": "https://misskey.io/notes/8vsn2izjwh",
"_misskey_quote": "https://misskey.io/notes/8vsn2izjwh",
"_misskey_content": "いつの生まれだシトリン",
"content": "<p>いつの生まれだシトリン<span class=\"quote-inline\"><br/>QT: <a class=\"status-url-link\" data-status-account-acct=\"Citrine@misskey.io\" data-status-id=\"107663207194225003\" href=\"https://misskey.io/notes/8vsn2izjwh\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">misskey.io/notes/8vsn2izjwh</span><span class=\"invisible\"></span></a></span></p>",
"contentMap": {
"ja": "<p>いつの生まれだシトリン<span class=\"quote-inline\"><br/>QT: <a class=\"status-url-link\" data-status-account-acct=\"Citrine@misskey.io\" data-status-id=\"107663207194225003\" href=\"https://misskey.io/notes/8vsn2izjwh\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">misskey.io/notes/8vsn2izjwh</span><span class=\"invisible\"></span></a></span></p>"
},
"attachment": [],
"tag": [],
"replies": {
"id": "https://fedibird.com/users/noellabo/statuses/107663670404015196/replies",
"type": "Collection",
"first": {
"type": "CollectionPage",
"next": "https://fedibird.com/users/noellabo/statuses/107663670404015196/replies?only_other_accounts=true&page=true",
"partOf": "https://fedibird.com/users/noellabo/statuses/107663670404015196/replies",
"items": []
}
}
}

View File

@ -0,0 +1,54 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"ostatus": "http://ostatus.org#",
"atomUri": "ostatus:atomUri",
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"conversation": "ostatus:conversation",
"sensitive": "as:sensitive",
"toot": "http://joinmastodon.org/ns#",
"votersCount": "toot:votersCount",
"fedibird": "http://fedibird.com/ns#",
"quoteUri": "fedibird:quoteUri",
"expiry": "fedibird:expiry"
}
],
"id": "https://fedibird.com/users/noellabo/statuses/107699335988346142",
"type": "Note",
"summary": null,
"inReplyTo": null,
"published": "2022-01-28T09:17:30Z",
"url": "https://fedibird.com/@noellabo/107699335988346142",
"attributedTo": "https://fedibird.com/users/noellabo",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://fedibird.com/users/noellabo/followers"
],
"sensitive": false,
"atomUri": "https://fedibird.com/users/noellabo/statuses/107699335988346142",
"inReplyToAtomUri": null,
"conversation": "tag:fedibird.com,2022-01-28:objectId=107699335988345290:objectType=Conversation",
"context": "https://fedibird.com/contexts/107699335988345290",
"quoteUri": "https://fedibird.com/users/yamako/statuses/107699333438289729",
"_misskey_quote": "https://fedibird.com/users/yamako/statuses/107699333438289729",
"_misskey_content": "美味しそう",
"content": "<p>美味しそう<span class=\"quote-inline\"><br/>QT: <a class=\"status-url-link\" data-status-account-acct=\"yamako\" data-status-id=\"107699333438289729\" href=\"https://fedibird.com/@yamako/107699333438289729\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"ellipsis\">fedibird.com/@yamako/107699333</span><span class=\"invisible\">438289729</span></a></span></p>",
"contentMap": {
"ja": "<p>美味しそう<span class=\"quote-inline\"><br/>QT: <a class=\"status-url-link\" data-status-account-acct=\"yamako\" data-status-id=\"107699333438289729\" href=\"https://fedibird.com/@yamako/107699333438289729\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"ellipsis\">fedibird.com/@yamako/107699333</span><span class=\"invisible\">438289729</span></a></span></p>"
},
"attachment": [],
"tag": [],
"replies": {
"id": "https://fedibird.com/users/noellabo/statuses/107699335988346142/replies",
"type": "Collection",
"first": {
"type": "CollectionPage",
"next": "https://fedibird.com/users/noellabo/statuses/107699335988346142/replies?only_other_accounts=true&page=true",
"partOf": "https://fedibird.com/users/noellabo/statuses/107699335988346142/replies",
"items": []
}
}
}

View File

@ -0,0 +1,46 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"sensitive": "as:sensitive",
"Hashtag": "as:Hashtag",
"quoteUrl": "as:quoteUrl",
"toot": "http://joinmastodon.org/ns#",
"Emoji": "toot:Emoji",
"featured": "toot:featured",
"discoverable": "toot:discoverable",
"schema": "http://schema.org#",
"PropertyValue": "schema:PropertyValue",
"value": "schema:value",
"misskey": "https://misskey.io/ns#",
"_misskey_content": "misskey:_misskey_content",
"_misskey_quote": "misskey:_misskey_quote",
"_misskey_reaction": "misskey:_misskey_reaction",
"_misskey_votes": "misskey:_misskey_votes",
"_misskey_talk": "misskey:_misskey_talk",
"isCat": "misskey:isCat",
"vcard": "http://www.w3.org/2006/vcard/ns#"
}
],
"id": "https://misskey.io/notes/8vs6ylpfez",
"type": "Note",
"attributedTo": "https://misskey.io/users/7rkrarq81i",
"summary": null,
"content": "<p><span>投稿者の設定によるね<br>Fanboxについても投稿者によっては過去の投稿は高額なプランに移動してることがある<br><br>RE: </span><a href=\"https://misskey.io/notes/8vs6wxufd0\">https://misskey.io/notes/8vs6wxufd0</a></p>",
"_misskey_content": "投稿者の設定によるね\nFanboxについても投稿者によっては過去の投稿は高額なプランに移動してることがある",
"_misskey_quote": "https://misskey.io/notes/8vs6wxufd0",
"quoteUrl": "https://misskey.io/notes/8vs6wxufd0",
"published": "2022-01-21T16:38:30.243Z",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://misskey.io/users/7rkrarq81i/followers"
],
"inReplyTo": null,
"attachment": [],
"sensitive": false,
"tag": []
}

View File

@ -13,6 +13,7 @@ defmodule Pleroma.Web.ActivityPub.BuilderTest do
test "returns note data" do
user = insert(:user)
note = insert(:note)
quote = insert(:note)
user2 = insert(:user)
user3 = insert(:user)
@ -25,7 +26,8 @@ defmodule Pleroma.Web.ActivityPub.BuilderTest do
tags: [name: "jimm"],
summary: "test summary",
cc: [user3.ap_id],
extra: %{"custom_tag" => "test"}
extra: %{"custom_tag" => "test"},
quote: quote
}
expected = %{
@ -39,7 +41,8 @@ defmodule Pleroma.Web.ActivityPub.BuilderTest do
"tag" => ["jimm"],
"to" => [user2.ap_id],
"type" => "Note",
"custom_tag" => "test"
"custom_tag" => "test",
"quoteUri" => quote.data["id"]
}
assert {:ok, ^expected, []} = Builder.note(draft)

View File

@ -0,0 +1,56 @@
# 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.InlineQuotePolicyTest do
alias Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy
use Pleroma.DataCase
test "adds quote URL to post content" do
quote_url = "https://example.com/objects/1234"
activity = %{
"type" => "Create",
"actor" => "https://example.com/users/alex",
"object" => %{
"type" => "Note",
"content" => "<p>Nice post</p>",
"quoteUri" => quote_url
}
}
{:ok, %{"object" => %{"content" => filtered}}} = InlineQuotePolicy.filter(activity)
assert filtered ==
"<p>Nice post<span class=\"quote-inline\"><br/><br/>RE: <a href=\"https://example.com/objects/1234\">https://example.com/objects/1234</a></span></p>"
end
test "ignores Misskey quote posts" do
object = File.read!("test/fixtures/quote_post/misskey_quote_post.json") |> Jason.decode!()
activity = %{
"type" => "Create",
"actor" => "https://misskey.io/users/7rkrarq81i",
"object" => object
}
{:ok, filtered} = InlineQuotePolicy.filter(activity)
assert filtered == activity
end
test "ignores Fedibird quote posts" do
object = File.read!("test/fixtures/quote_post/fedibird_quote_post.json") |> Jason.decode!()
# Normally the ObjectValidator will fix this before it reaches MRF
object = Map.put(object, "quoteUrl", object["quoteURL"])
activity = %{
"type" => "Create",
"actor" => "https://fedibird.com/users/noellabo",
"object" => object
}
{:ok, filtered} = InlineQuotePolicy.filter(activity)
assert filtered == activity
end
end

View File

@ -193,10 +193,14 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
assert response["irreversible"] == true
assert response["expires_at"] ==
NaiveDateTime.utc_now()
|> NaiveDateTime.add(in_seconds)
|> Pleroma.Web.CommonAPI.Utils.to_masto_date()
expected_time =
NaiveDateTime.utc_now()
|> NaiveDateTime.add(in_seconds)
assert NaiveDateTime.diff(
NaiveDateTime.from_iso8601!(response["expires_at"]),
expected_time
) < 5
filter = Filter.get(response["id"], user)

View File

@ -1944,4 +1944,91 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
} = result
end
end
describe "posting quotes" do
setup do: oauth_access(["write:statuses"])
test "posting a quote", %{conn: conn} do
user = insert(:user)
{:ok, quoted_status} = CommonAPI.post(user, %{status: "tell me, for whom do you fight?"})
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "Hmph, how very glib",
"quote_id" => quoted_status.id
})
response = json_response_and_validate_schema(conn, 200)
assert response["quote_id"] == quoted_status.id
assert response["quote"]["id"] == quoted_status.id
assert response["quote"]["content"] == quoted_status.object.data["content"]
end
test "posting a quote, quoting a status that isn't public", %{conn: conn} do
user = insert(:user)
Enum.each(["private", "local", "direct"], fn visibility ->
{:ok, quoted_status} =
CommonAPI.post(user, %{
status: "tell me, for whom do you fight?",
visibility: visibility
})
assert %{"error" => "You can only quote public or unlisted statuses"} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "Hmph, how very glib",
"quote_id" => quoted_status.id
})
|> json_response_and_validate_schema(422)
end)
end
test "posting a quote, after quote, the status gets deleted", %{conn: conn} do
user = insert(:user)
{:ok, quoted_status} =
CommonAPI.post(user, %{status: "tell me, for whom do you fight?", visibility: "public"})
resp =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "I fight for eorzea!",
"quote_id" => quoted_status.id
})
|> json_response_and_validate_schema(200)
{:ok, _} = CommonAPI.delete(quoted_status.id, user)
resp =
conn
|> get("/api/v1/statuses/#{resp["id"]}")
|> json_response_and_validate_schema(200)
assert is_nil(resp["quote"])
end
test "posting a quote of a deleted status", %{conn: conn} do
user = insert(:user)
{:ok, quoted_status} =
CommonAPI.post(user, %{status: "tell me, for whom do you fight?", visibility: "public"})
{:ok, _} = CommonAPI.delete(quoted_status.id, user)
assert %{"error" => _} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "I fight for eorzea!",
"quote_id" => quoted_status.id
})
|> json_response_and_validate_schema(422)
end
end
end