summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex20
-rw-r--r--lib/pleroma/web/activity_pub/builder.ex14
-rw-r--r--lib/pleroma/web/activity_pub/object_validator.ex14
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/listen_validator.ex82
-rw-r--r--lib/pleroma/web/activity_pub/side_effects.ex12
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex59
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex26
-rw-r--r--lib/pleroma/web/common_api.ex10
-rw-r--r--lib/pleroma/web/pleroma_api/views/scrobble_view.ex32
-rw-r--r--test/pleroma/web/activity_pub/activity_pub_test.exs24
-rw-r--r--test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs33
-rw-r--r--test/pleroma/web/activity_pub/transmogrifier/listen_handling_test.exs59
-rw-r--r--test/pleroma/web/activity_pub/transmogrifier_test.exs2
-rw-r--r--test/pleroma/web/common_api_test.exs22
14 files changed, 255 insertions, 154 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 064f93b22..8f009d6fd 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -314,26 +314,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
:ok
end
- @spec listen(map()) :: {:ok, Activity.t()} | {:error, any()}
- def listen(%{to: to, actor: actor, context: context, object: object} = params) do
- additional = params[:additional] || %{}
- # only accept false as false value
- local = !(params[:local] == false)
- published = params[:published]
-
- listen_data =
- make_listen_data(
- %{to: to, actor: actor, published: published, context: context, object: object},
- additional
- )
-
- with {:ok, activity} <- insert(listen_data, local),
- _ <- notify_and_stream(activity),
- :ok <- maybe_federate(activity) do
- {:ok, activity}
- end
- end
-
@spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) ::
{:ok, Activity.t()} | nil | {:error, any()}
def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex
index 5b25138a4..cf50cef73 100644
--- a/lib/pleroma/web/activity_pub/builder.ex
+++ b/lib/pleroma/web/activity_pub/builder.ex
@@ -337,4 +337,18 @@ defmodule Pleroma.Web.ActivityPub.Builder do
defp pinned_url(nickname) when is_binary(nickname) do
Pleroma.Web.Router.Helpers.activity_pub_url(Pleroma.Web.Endpoint, :pinned, nickname)
end
+
+ def listen(%{to: to, actor: %{ap_id: actor}, object: object} = params, additional) do
+ {:ok,
+ %{
+ "type" => "Listen",
+ "id" => Utils.generate_activity_id(),
+ "to" => to |> Enum.uniq(),
+ "actor" => actor,
+ "object" => object,
+ "published" => Map.get(params, :published, Utils.make_date()),
+ "context" => params.context
+ }
+ |> Map.merge(additional), []}
+ end
end
diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex
index c2afd6370..de5b8e522 100644
--- a/lib/pleroma/web/activity_pub/object_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validator.ex
@@ -31,6 +31,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
alias Pleroma.Web.ActivityPub.ObjectValidators.EventValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
+ alias Pleroma.Web.ActivityPub.ObjectValidators.ListenValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator
@@ -99,6 +100,19 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
end
def validate(
+ %{"type" => "Listen", "object" => %{"type" => "Audio"} = object} = activity,
+ meta
+ ) do
+ with {:ok, activity} <-
+ activity
+ |> ListenValidator.cast_and_validate(meta)
+ |> Ecto.Changeset.apply_action(:insert) do
+ activity = stringify_keys(activity)
+ {:ok, activity, meta}
+ end
+ end
+
+ def validate(
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
meta
)
diff --git a/lib/pleroma/web/activity_pub/object_validators/listen_validator.ex b/lib/pleroma/web/activity_pub/object_validators/listen_validator.ex
new file mode 100644
index 000000000..0c5e6819d
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/object_validators/listen_validator.ex
@@ -0,0 +1,82 @@
+# 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.ObjectValidators.ListenValidator do
+ use Ecto.Schema
+
+ alias Pleroma.EctoType.ActivityPub.ObjectValidators
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
+
+ import Ecto.Changeset
+
+ @primary_key false
+ @derive Jason.Encoder
+
+ embedded_schema do
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ end
+ end
+
+ field(:actor, ObjectValidators.ObjectID)
+ field(:published, ObjectValidators.DateTime)
+
+ embeds_one :object, UrlObjectValidator, primary_key: false do
+ field(:type, :string)
+
+ field(:to, ObjectValidators.Recipients, default: [])
+ field(:cc, ObjectValidators.Recipients, default: [])
+ field(:bto, ObjectValidators.Recipients, default: [])
+ field(:bcc, ObjectValidators.Recipients, default: [])
+
+ field(:title, :string)
+ field(:artist, :string)
+ field(:album, :string)
+ field(:length, :integer)
+ end
+ end
+
+ def changeset(struct, data) do
+ struct
+ |> cast(data, __schema__(:fields) -- [:object])
+ |> cast_embed(:object, with: &audio_changeset/2)
+ end
+
+ def audio_changeset(struct, data) do
+ struct
+ |> cast(data, Map.keys(struct) -- [:__struct__])
+ |> validate_inclusion(:type, ["Audio"])
+ end
+
+ def cast_data(data, meta \\ []) do
+ data = fix(data, meta)
+
+ %__MODULE__{}
+ |> changeset(data)
+ end
+
+ def cast_and_validate(data, meta \\ []) do
+ data
+ |> cast_data(meta)
+ |> validate_data(meta)
+ end
+
+ defp fix(data, _meta) do
+ data
+ |> CommonFixes.fix_actor()
+ |> CommonFixes.fix_activity_addressing()
+ end
+
+ defp validate_data(data_cng, _meta) do
+ # TODO: Restrict to Audio objects
+
+ data_cng
+ |> validate_inclusion(:type, ["Listen"])
+ |> validate_required([:id, :type, :object, :actor, :to, :cc, :published])
+ |> CommonValidations.validate_actor_presence()
+ end
+end
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
index b997c15db..ce0f61da2 100644
--- a/lib/pleroma/web/activity_pub/side_effects.ex
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -234,6 +234,18 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
end
end
+ # Tasks this handles
+ # - Actually create object
+ # - Rollback if we couldn't create it
+ @impl true
+ def handle(%{data: %{"type" => "Listen"}} = activity, meta) do
+ with {:ok, _object, meta} <- handle_object_creation(meta[:object_data], activity, meta) do
+ {:ok, activity, meta}
+ else
+ e -> Repo.rollback(e)
+ end
+ end
+
# Tasks this handles:
# - Add announce to object
# - Set up notification
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index a70330f0e..e240b6bbe 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -384,37 +384,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id) < 8,
do: :error
- def handle_incoming(
- %{"type" => "Listen", "object" => %{"type" => "Audio"} = object} = data,
- options
- ) do
- actor = Containment.get_actor(data)
-
- data =
- Map.put(data, "actor", actor)
- |> fix_addressing
-
- with {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do
- reply_depth = (options[:depth] || 0) + 1
- options = Keyword.put(options, :depth, reply_depth)
- object = fix_object(object, options)
-
- params = %{
- to: data["to"],
- object: object,
- actor: user,
- context: nil,
- local: false,
- published: data["published"],
- additional: Map.take(data, ["cc", "id"])
- }
-
- ActivityPub.listen(params)
- else
- _e -> :error
- end
- end
-
@misskey_reactions %{
"like" => "👍",
"love" => "❤️",
@@ -493,6 +462,17 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def handle_incoming(
+ %{"type" => "Listen", "object" => %{"type" => "Audio"}} = data,
+ _options
+ ) do
+ with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
+ {:ok, activity, _} <-
+ Pipeline.common_pipeline(data, local: false) do
+ {:ok, activity}
+ end
+ end
+
+ def handle_incoming(
%{"type" => "Delete"} = data,
_options
) do
@@ -694,8 +674,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
# internal -> Mastodon
# """
- def prepare_outgoing(%{"type" => activity_type, "object" => object_id} = data)
- when activity_type in ["Create", "Listen"] do
+ def prepare_outgoing(%{"type" => "Create", "object" => object_id} = data) do
object =
object_id
|> Object.normalize(fetch: false)
@@ -711,6 +690,20 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, data}
end
+ def prepare_outgoing(%{"type" => "Listen", "object" => object} = data) do
+ object =
+ object
+ |> prepare_object
+
+ data =
+ data
+ |> Map.put("object", object)
+ |> Map.merge(Utils.make_json_ld_header())
+ |> Map.delete("bcc")
+
+ {:ok, data}
+ end
+
def prepare_outgoing(%{"type" => "Announce", "actor" => ap_id, "object" => object_id} = data) do
object =
object_id
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 9cde7805c..392c2a8c9 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -23,16 +23,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
require Logger
require Pleroma.Constants
- @supported_object_types [
- "Article",
- "Note",
- "Event",
- "Video",
- "Page",
- "Question",
- "Answer",
- "Audio"
- ]
+ @supported_object_types ~w[Article Note Event Video Page Question Answer Audio]
@strip_status_report_states ~w(closed resolved)
@supported_report_states ~w(open closed resolved)
@valid_visibilities ~w(public unlisted private direct)
@@ -675,21 +666,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> Map.merge(additional)
end
- #### Listen-related helpers
- def make_listen_data(params, additional) do
- published = params.published || make_date()
-
- %{
- "type" => "Listen",
- "to" => params.to |> Enum.uniq(),
- "actor" => params.actor.ap_id,
- "object" => params.object,
- "published" => published,
- "context" => params.context
- }
- |> Map.merge(additional)
- end
-
#### Flag-related helpers
@spec make_flag_data(map(), map()) :: map()
def make_flag_data(%{actor: actor, context: context, content: content} = params, additional) do
diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex
index 1b95ee89c..d5a4e72fa 100644
--- a/lib/pleroma/web/common_api.ex
+++ b/lib/pleroma/web/common_api.ex
@@ -391,8 +391,14 @@ defmodule Pleroma.Web.CommonAPI do
end
def listen(user, data) do
- with {:ok, draft} <- ActivityDraft.listen(user, data) do
- ActivityPub.listen(draft.changes)
+ with {_, {:ok, %{changes: draft}}} <- {:draft, ActivityDraft.listen(user, data)},
+ {_, {:ok, activity_data, []}} <- {:builder, Builder.listen(draft, draft.additional)},
+ {_, {:ok, activity, _}} <-
+ {:pipeline, Pipeline.common_pipeline(activity_data, local: true)},
+ {_, %Activity{} = activity} <- {:norm, Activity.normalize(activity)} do
+ {:ok, activity}
+ else
+ e -> {:error, e}
end
end
diff --git a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex
index a5985fb2a..dd2173b0b 100644
--- a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex
+++ b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex
@@ -9,25 +9,35 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleView do
alias Pleroma.Activity
alias Pleroma.HTML
- alias Pleroma.Object
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MastodonAPI.AccountView
- def render("show.json", %{activity: %Activity{data: %{"type" => "Listen"}} = activity} = opts) do
- object = Object.normalize(activity, fetch: false)
-
- user = CommonAPI.get_user(activity.data["actor"])
- created_at = Utils.to_masto_date(activity.data["published"])
+ def render(
+ "show.json",
+ %{
+ activity: %Activity{
+ id: id,
+ data: %{
+ "type" => "Listen",
+ "actor" => actor,
+ "published" => published,
+ "object" => object
+ }
+ }
+ } = opts
+ ) do
+ user = CommonAPI.get_user(actor)
+ created_at = Utils.to_masto_date(published)
%{
- id: activity.id,
+ id: id,
account: AccountView.render("show.json", %{user: user, for: opts[:for]}),
created_at: created_at,
- title: object.data["title"] |> HTML.strip_tags(),
- artist: object.data["artist"] |> HTML.strip_tags(),
- album: object.data["album"] |> HTML.strip_tags(),
- length: object.data["length"]
+ title: object["title"] |> HTML.strip_tags(),
+ artist: object["artist"] |> HTML.strip_tags(),
+ album: object["album"] |> HTML.strip_tags(),
+ length: object["length"]
}
end
diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs
index 8aa586f40..d07cdab10 100644
--- a/test/pleroma/web/activity_pub/activity_pub_test.exs
+++ b/test/pleroma/web/activity_pub/activity_pub_test.exs
@@ -601,30 +601,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
end
describe "listen activities" do
- test "does not increase user note count" do
- user = insert(:user)
-
- {:ok, activity} =
- ActivityPub.listen(%{
- to: ["https://www.w3.org/ns/activitystreams#Public"],
- actor: user,
- context: "",
- object: %{
- "actor" => user.ap_id,
- "to" => ["https://www.w3.org/ns/activitystreams#Public"],
- "artist" => "lain",
- "title" => "lain radio episode 1",
- "length" => 180_000,
- "type" => "Audio"
- }
- })
-
- assert activity.actor == user.ap_id
-
- user = User.get_cached_by_id(user.id)
- assert user.note_count == 0
- end
-
test "can be fetched into a timeline" do
_listen_activity_1 = insert(:listen)
_listen_activity_2 = insert(:listen)
diff --git a/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs
index d1eb21a0c..832533339 100644
--- a/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs
+++ b/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs
@@ -12,39 +12,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AudioHandlingTest do
import Pleroma.Factory
- test "it works for incoming listens" do
- _user = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
-
- data = %{
- "@context" => "https://www.w3.org/ns/activitystreams",
- "to" => ["https://www.w3.org/ns/activitystreams#Public"],
- "cc" => [],
- "type" => "Listen",
- "id" => "http://mastodon.example.org/users/admin/listens/1234/activity",
- "actor" => "http://mastodon.example.org/users/admin",
- "object" => %{
- "type" => "Audio",
- "to" => ["https://www.w3.org/ns/activitystreams#Public"],
- "cc" => [],
- "id" => "http://mastodon.example.org/users/admin/listens/1234",
- "attributedTo" => "http://mastodon.example.org/users/admin",
- "title" => "lain radio episode 1",
- "artist" => "lain",
- "album" => "lain radio",
- "length" => 180_000
- }
- }
-
- {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
-
- object = Object.normalize(activity, fetch: false)
-
- assert object.data["title"] == "lain radio episode 1"
- assert object.data["artist"] == "lain"
- assert object.data["album"] == "lain radio"
- assert object.data["length"] == 180_000
- end
-
test "Funkwhale Audio object" do
Tesla.Mock.mock(fn
%{url: "https://channels.tests.funkwhale.audio/federation/actors/compositions"} ->
diff --git a/test/pleroma/web/activity_pub/transmogrifier/listen_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/listen_handling_test.exs
new file mode 100644
index 000000000..cab451116
--- /dev/null
+++ b/test/pleroma/web/activity_pub/transmogrifier/listen_handling_test.exs
@@ -0,0 +1,59 @@
+# 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.ListenHandlingTest do
+ use Pleroma.DataCase, async: true
+
+ alias Pleroma.Activity
+ alias Pleroma.Web.ActivityPub.Transmogrifier
+
+ import Pleroma.Factory
+
+ test "it works for incoming listens" do
+ _user = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
+
+ audio_data = %{
+ "type" => "Audio",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [],
+ "id" => "http://mastodon.example.org/users/admin/listens/1234",
+ "attributedTo" => "http://mastodon.example.org/users/admin",
+ "title" => "lain radio episode 1",
+ "artist" => "lain",
+ "album" => "lain radio",
+ "length" => 180_000
+ }
+
+ data = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [],
+ "type" => "Listen",
+ "id" => "http://mastodon.example.org/users/admin/listens/1234/activity",
+ "actor" => "http://mastodon.example.org/users/admin",
+ "object" => audio_data
+ }
+
+ Tesla.Mock.mock(fn
+ %{url: "http://mastodon.example.org/users/admin/listens/1234"} ->
+ %Tesla.Env{
+ status: 200,
+ body: audio_data,
+ headers: HttpRequestMock.activitypub_object_headers()
+ }
+ end)
+
+ {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
+
+ assert activity.data["type"] == "Listen"
+
+ assert object = activity.data["object"]
+
+ assert object["type"] == "Audio"
+ assert object["title"] == "lain radio episode 1"
+ assert object["artist"] == "lain"
+ assert object["album"] == "lain radio"
+ assert object["length"] == 180_000
+ end
+end
diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs
index 335fe1a30..41381a637 100644
--- a/test/pleroma/web/activity_pub/transmogrifier_test.exs
+++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs
@@ -295,7 +295,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
{:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"})
- {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data)
+ {:ok, _modified} = Transmogrifier.prepare_outgoing(activity)
end
test "custom emoji urls are URI encoded" do
diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs
index b502aaa03..b09cc91fc 100644
--- a/test/pleroma/web/common_api_test.exs
+++ b/test/pleroma/web/common_api_test.exs
@@ -1371,9 +1371,9 @@ defmodule Pleroma.Web.CommonAPITest do
length: 180_000
})
- object = Object.normalize(activity, fetch: false)
+ assert object = activity.data["object"]
- assert object.data["title"] == "lain radio episode 1"
+ assert object["title"] == "lain radio episode 1"
assert Visibility.get_visibility(activity) == "public"
end
@@ -1381,7 +1381,7 @@ defmodule Pleroma.Web.CommonAPITest do
test "respects visibility=private" do
user = insert(:user)
- {:ok, activity} =
+ {:ok, %Activity{} = activity} =
CommonAPI.listen(user, %{
title: "lain radio episode 1",
album: "lain radio",
@@ -1390,12 +1390,24 @@ defmodule Pleroma.Web.CommonAPITest do
visibility: "private"
})
- object = Object.normalize(activity, fetch: false)
+ assert object = activity.data["object"]
- assert object.data["title"] == "lain radio episode 1"
+ assert object["title"] == "lain radio episode 1"
assert Visibility.get_visibility(activity) == "private"
end
+
+ test "does not increase user note count" do
+ user = insert(:user)
+
+ {:ok, %Activity{actor: actor}} =
+ CommonAPI.listen(user, %{artist: "lain", title: "lain radio episode 1", length: 80_000})
+
+ assert actor == user.ap_id
+
+ user = User.get_cached_by_id(user.id)
+ assert user.note_count == 0
+ end
end
describe "get_user/1" do