forked from YokaiRick/akkoma
Merge branch 'feature/emojireactvalidator' into 'develop'
Move EmojiReacts to the Pipeline. See merge request pleroma/pleroma!2473
This commit is contained in:
commit
fbcc53760e
19 changed files with 276 additions and 169 deletions
|
@ -356,31 +356,6 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec react_with_emoji(User.t(), Object.t(), String.t(), keyword()) ::
|
|
||||||
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
|
||||||
def react_with_emoji(user, object, emoji, options \\ []) do
|
|
||||||
with {:ok, result} <-
|
|
||||||
Repo.transaction(fn -> do_react_with_emoji(user, object, emoji, options) end) do
|
|
||||||
result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp do_react_with_emoji(user, object, emoji, options) do
|
|
||||||
with local <- Keyword.get(options, :local, true),
|
|
||||||
activity_id <- Keyword.get(options, :activity_id, nil),
|
|
||||||
true <- Pleroma.Emoji.is_unicode_emoji?(emoji),
|
|
||||||
reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id),
|
|
||||||
{:ok, activity} <- insert(reaction_data, local),
|
|
||||||
{:ok, object} <- add_emoji_reaction_to_object(activity, object),
|
|
||||||
_ <- notify_and_stream(activity),
|
|
||||||
:ok <- maybe_federate(activity) do
|
|
||||||
{:ok, activity, object}
|
|
||||||
else
|
|
||||||
false -> {:error, false}
|
|
||||||
{:error, error} -> Repo.rollback(error)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) ::
|
@spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) ::
|
||||||
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
||||||
def announce(
|
def announce(
|
||||||
|
|
|
@ -10,6 +10,18 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
|
||||||
|
@spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
|
||||||
|
def emoji_react(actor, object, emoji) do
|
||||||
|
with {:ok, data, meta} <- object_action(actor, object) do
|
||||||
|
data =
|
||||||
|
data
|
||||||
|
|> Map.put("content", emoji)
|
||||||
|
|> Map.put("type", "EmojiReact")
|
||||||
|
|
||||||
|
{:ok, data, meta}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@spec undo(User.t(), Activity.t()) :: {:ok, map(), keyword()}
|
@spec undo(User.t(), Activity.t()) :: {:ok, map(), keyword()}
|
||||||
def undo(actor, object) do
|
def undo(actor, object) do
|
||||||
{:ok,
|
{:ok,
|
||||||
|
@ -52,6 +64,17 @@ def delete(actor, object_id) do
|
||||||
|
|
||||||
@spec like(User.t(), Object.t()) :: {:ok, map(), keyword()}
|
@spec like(User.t(), Object.t()) :: {:ok, map(), keyword()}
|
||||||
def like(actor, object) do
|
def like(actor, object) do
|
||||||
|
with {:ok, data, meta} <- object_action(actor, object) do
|
||||||
|
data =
|
||||||
|
data
|
||||||
|
|> Map.put("type", "Like")
|
||||||
|
|
||||||
|
{:ok, data, meta}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec object_action(User.t(), Object.t()) :: {:ok, map(), keyword()}
|
||||||
|
defp object_action(actor, object) do
|
||||||
object_actor = User.get_cached_by_ap_id(object.data["actor"])
|
object_actor = User.get_cached_by_ap_id(object.data["actor"])
|
||||||
|
|
||||||
# Address the actor of the object, and our actor's follower collection if the post is public.
|
# Address the actor of the object, and our actor's follower collection if the post is public.
|
||||||
|
@ -73,7 +96,6 @@ def like(actor, object) do
|
||||||
%{
|
%{
|
||||||
"id" => Utils.generate_activity_id(),
|
"id" => Utils.generate_activity_id(),
|
||||||
"actor" => actor.ap_id,
|
"actor" => actor.ap_id,
|
||||||
"type" => "Like",
|
|
||||||
"object" => object.data["id"],
|
"object" => object.data["id"],
|
||||||
"to" => to,
|
"to" => to,
|
||||||
"cc" => cc,
|
"cc" => cc,
|
||||||
|
|
|
@ -12,6 +12,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
|
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator
|
||||||
|
@ -47,6 +48,16 @@ def validate(%{"type" => "Like"} = object, meta) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate(%{"type" => "EmojiReact"} = object, meta) do
|
||||||
|
with {:ok, object} <-
|
||||||
|
object
|
||||||
|
|> EmojiReactValidator.cast_and_validate()
|
||||||
|
|> Ecto.Changeset.apply_action(:insert) do
|
||||||
|
object = stringify_keys(object |> Map.from_struct())
|
||||||
|
{:ok, object, meta}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def stringify_keys(%{__struct__: _} = object) do
|
def stringify_keys(%{__struct__: _} = object) do
|
||||||
object
|
object
|
||||||
|> Map.from_struct()
|
|> Map.from_struct()
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||||
|
|
||||||
|
@primary_key false
|
||||||
|
|
||||||
|
embedded_schema do
|
||||||
|
field(:id, Types.ObjectID, primary_key: true)
|
||||||
|
field(:type, :string)
|
||||||
|
field(:object, Types.ObjectID)
|
||||||
|
field(:actor, Types.ObjectID)
|
||||||
|
field(:context, :string)
|
||||||
|
field(:content, :string)
|
||||||
|
field(:to, {:array, :string}, default: [])
|
||||||
|
field(:cc, {:array, :string}, default: [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_and_validate(data) do
|
||||||
|
data
|
||||||
|
|> cast_data()
|
||||||
|
|> validate_data()
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_data(data) do
|
||||||
|
%__MODULE__{}
|
||||||
|
|> changeset(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def changeset(struct, data) do
|
||||||
|
struct
|
||||||
|
|> cast(data, __schema__(:fields))
|
||||||
|
|> fix_after_cast()
|
||||||
|
end
|
||||||
|
|
||||||
|
def fix_after_cast(cng) do
|
||||||
|
cng
|
||||||
|
|> fix_context()
|
||||||
|
end
|
||||||
|
|
||||||
|
def fix_context(cng) do
|
||||||
|
object = get_field(cng, :object)
|
||||||
|
|
||||||
|
with nil <- get_field(cng, :context),
|
||||||
|
%Object{data: %{"context" => context}} <- Object.get_cached_by_ap_id(object) do
|
||||||
|
cng
|
||||||
|
|> put_change(:context, context)
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
cng
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_emoji(cng) do
|
||||||
|
content = get_field(cng, :content)
|
||||||
|
|
||||||
|
if Pleroma.Emoji.is_unicode_emoji?(content) do
|
||||||
|
cng
|
||||||
|
else
|
||||||
|
cng
|
||||||
|
|> add_error(:content, "must be a single character emoji")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_data(data_cng) do
|
||||||
|
data_cng
|
||||||
|
|> validate_inclusion(:type, ["EmojiReact"])
|
||||||
|
|> validate_required([:id, :type, :object, :actor, :context, :to, :cc, :content])
|
||||||
|
|> validate_actor_presence()
|
||||||
|
|> validate_object_presence()
|
||||||
|
|> validate_emoji()
|
||||||
|
end
|
||||||
|
end
|
|
@ -34,6 +34,18 @@ def handle(%{data: %{"type" => "Undo", "object" => undone_object}} = object, met
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Tasks this handles:
|
||||||
|
# - Add reaction to object
|
||||||
|
# - Set up notification
|
||||||
|
def handle(%{data: %{"type" => "EmojiReact"}} = object, meta) do
|
||||||
|
reacted_object = Object.get_by_ap_id(object.data["object"])
|
||||||
|
Utils.add_emoji_reaction_to_object(object, reacted_object)
|
||||||
|
|
||||||
|
Notification.create_notifications(object)
|
||||||
|
|
||||||
|
{:ok, object, meta}
|
||||||
|
end
|
||||||
|
|
||||||
# Tasks this handles:
|
# Tasks this handles:
|
||||||
# - Delete and unpins the create activity
|
# - Delete and unpins the create activity
|
||||||
# - Replace object with Tombstone
|
# - Replace object with Tombstone
|
||||||
|
|
|
@ -656,7 +656,7 @@ def handle_incoming(
|
||||||
|> handle_incoming(options)
|
|> handle_incoming(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_incoming(%{"type" => "Like"} = data, _options) do
|
def handle_incoming(%{"type" => type} = data, _options) when type in ["Like", "EmojiReact"] do
|
||||||
with :ok <- ObjectValidator.fetch_actor_and_object(data),
|
with :ok <- ObjectValidator.fetch_actor_and_object(data),
|
||||||
{:ok, activity, _meta} <-
|
{:ok, activity, _meta} <-
|
||||||
Pipeline.common_pipeline(data, local: false) do
|
Pipeline.common_pipeline(data, local: false) do
|
||||||
|
@ -666,27 +666,6 @@ def handle_incoming(%{"type" => "Like"} = data, _options) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_incoming(
|
|
||||||
%{
|
|
||||||
"type" => "EmojiReact",
|
|
||||||
"object" => object_id,
|
|
||||||
"actor" => _actor,
|
|
||||||
"id" => id,
|
|
||||||
"content" => emoji
|
|
||||||
} = data,
|
|
||||||
_options
|
|
||||||
) do
|
|
||||||
with actor <- Containment.get_actor(data),
|
|
||||||
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
|
|
||||||
{:ok, object} <- get_obj_helper(object_id),
|
|
||||||
{:ok, activity, _object} <-
|
|
||||||
ActivityPub.react_with_emoji(actor, object, emoji, activity_id: id, local: false) do
|
|
||||||
{:ok, activity}
|
|
||||||
else
|
|
||||||
_e -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_incoming(
|
def handle_incoming(
|
||||||
%{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data,
|
%{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data,
|
||||||
_options
|
_options
|
||||||
|
|
|
@ -191,8 +191,10 @@ def unfavorite(id, user) do
|
||||||
|
|
||||||
def react_with_emoji(id, user, emoji) do
|
def react_with_emoji(id, user, emoji) do
|
||||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||||
object <- Object.normalize(activity) do
|
object <- Object.normalize(activity),
|
||||||
ActivityPub.react_with_emoji(user, object, emoji)
|
{:ok, emoji_react, _} <- Builder.emoji_react(user, object, emoji),
|
||||||
|
{:ok, activity, _} <- Pipeline.common_pipeline(emoji_react, local: true) do
|
||||||
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
{:error, dgettext("errors", "Could not add reaction emoji")}
|
{:error, dgettext("errors", "Could not add reaction emoji")}
|
||||||
|
|
|
@ -86,7 +86,7 @@ def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id}
|
||||||
end
|
end
|
||||||
|
|
||||||
def react_with_emoji(%{assigns: %{user: user}} = conn, %{"id" => activity_id, "emoji" => emoji}) do
|
def react_with_emoji(%{assigns: %{user: user}} = conn, %{"id" => activity_id, "emoji" => emoji}) do
|
||||||
with {:ok, _activity, _object} <- CommonAPI.react_with_emoji(activity_id, user, emoji),
|
with {:ok, _activity} <- CommonAPI.react_with_emoji(activity_id, user, emoji),
|
||||||
activity <- Activity.get_by_id(activity_id) do
|
activity <- Activity.get_by_id(activity_id) do
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|
|
|
@ -24,7 +24,7 @@ test "creates a notification for an emoji reaction" do
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "yeah"})
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "yeah"})
|
||||||
{:ok, activity, _object} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
{:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
||||||
|
|
||||||
{:ok, [notification]} = Notification.create_notifications(activity)
|
{:ok, [notification]} = Notification.create_notifications(activity)
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.AdminAPI.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.Federator
|
|
||||||
|
|
||||||
import ExUnit.CaptureLog
|
import ExUnit.CaptureLog
|
||||||
import Mock
|
import Mock
|
||||||
|
@ -874,71 +873,6 @@ test "returns reblogs for users for whom reblogs have not been muted" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "react to an object" do
|
|
||||||
test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do
|
|
||||||
Config.put([:instance, :federating], true)
|
|
||||||
user = insert(:user)
|
|
||||||
reactor = insert(:user)
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
|
|
||||||
assert object = Object.normalize(activity)
|
|
||||||
|
|
||||||
{:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
|
|
||||||
|
|
||||||
assert called(Federator.publish(reaction_activity))
|
|
||||||
end
|
|
||||||
|
|
||||||
test "adds an emoji reaction activity to the db" do
|
|
||||||
user = insert(:user)
|
|
||||||
reactor = insert(:user)
|
|
||||||
third_user = insert(:user)
|
|
||||||
fourth_user = insert(:user)
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
|
|
||||||
assert object = Object.normalize(activity)
|
|
||||||
|
|
||||||
{:ok, reaction_activity, object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
|
|
||||||
|
|
||||||
assert reaction_activity
|
|
||||||
|
|
||||||
assert reaction_activity.data["actor"] == reactor.ap_id
|
|
||||||
assert reaction_activity.data["type"] == "EmojiReact"
|
|
||||||
assert reaction_activity.data["content"] == "🔥"
|
|
||||||
assert reaction_activity.data["object"] == object.data["id"]
|
|
||||||
assert reaction_activity.data["to"] == [User.ap_followers(reactor), activity.data["actor"]]
|
|
||||||
assert reaction_activity.data["context"] == object.data["context"]
|
|
||||||
assert object.data["reaction_count"] == 1
|
|
||||||
assert object.data["reactions"] == [["🔥", [reactor.ap_id]]]
|
|
||||||
|
|
||||||
{:ok, _reaction_activity, object} = ActivityPub.react_with_emoji(third_user, object, "☕")
|
|
||||||
|
|
||||||
assert object.data["reaction_count"] == 2
|
|
||||||
assert object.data["reactions"] == [["🔥", [reactor.ap_id]], ["☕", [third_user.ap_id]]]
|
|
||||||
|
|
||||||
{:ok, _reaction_activity, object} = ActivityPub.react_with_emoji(fourth_user, object, "🔥")
|
|
||||||
|
|
||||||
assert object.data["reaction_count"] == 3
|
|
||||||
|
|
||||||
assert object.data["reactions"] == [
|
|
||||||
["🔥", [fourth_user.ap_id, reactor.ap_id]],
|
|
||||||
["☕", [third_user.ap_id]]
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "reverts emoji reaction on error" do
|
|
||||||
[user, reactor] = insert_list(2, :user)
|
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "Status"})
|
|
||||||
object = Object.normalize(activity)
|
|
||||||
|
|
||||||
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
|
|
||||||
assert {:error, :reverted} = ActivityPub.react_with_emoji(reactor, object, "😀")
|
|
||||||
end
|
|
||||||
|
|
||||||
object = Object.get_by_ap_id(object.data["id"])
|
|
||||||
refute object.data["reaction_count"]
|
|
||||||
refute object.data["reactions"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "announcing an object" do
|
describe "announcing an object" do
|
||||||
test "adds an announce activity to the db" do
|
test "adds an announce activity to the db" do
|
||||||
note_activity = insert(:note_activity)
|
note_activity = insert(:note_activity)
|
||||||
|
|
|
@ -10,6 +10,46 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "EmojiReacts" do
|
||||||
|
setup do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, post_activity} = CommonAPI.post(user, %{"status" => "uguu"})
|
||||||
|
|
||||||
|
object = Pleroma.Object.get_by_ap_id(post_activity.data["object"])
|
||||||
|
|
||||||
|
{:ok, valid_emoji_react, []} = Builder.emoji_react(user, object, "👌")
|
||||||
|
|
||||||
|
%{user: user, post_activity: post_activity, valid_emoji_react: valid_emoji_react}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it validates a valid EmojiReact", %{valid_emoji_react: valid_emoji_react} do
|
||||||
|
assert {:ok, _, _} = ObjectValidator.validate(valid_emoji_react, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it is not valid without a 'content' field", %{valid_emoji_react: valid_emoji_react} do
|
||||||
|
without_content =
|
||||||
|
valid_emoji_react
|
||||||
|
|> Map.delete("content")
|
||||||
|
|
||||||
|
{:error, cng} = ObjectValidator.validate(without_content, [])
|
||||||
|
|
||||||
|
refute cng.valid?
|
||||||
|
assert {:content, {"can't be blank", [validation: :required]}} in cng.errors
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it is not valid with a non-emoji content field", %{valid_emoji_react: valid_emoji_react} do
|
||||||
|
without_emoji_content =
|
||||||
|
valid_emoji_react
|
||||||
|
|> Map.put("content", "x")
|
||||||
|
|
||||||
|
{:error, cng} = ObjectValidator.validate(without_emoji_content, [])
|
||||||
|
|
||||||
|
refute cng.valid?
|
||||||
|
|
||||||
|
assert {:content, {"must be a single character emoji", []}} in cng.errors
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "Undos" do
|
describe "Undos" do
|
||||||
setup do
|
setup do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
|
@ -72,13 +72,40 @@ test "it handles user deletions", %{delete_user: delete, user: user} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "EmojiReact objects" do
|
||||||
|
setup do
|
||||||
|
poster = insert(:user)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, post} = CommonAPI.post(poster, %{"status" => "hey"})
|
||||||
|
|
||||||
|
{:ok, emoji_react_data, []} = Builder.emoji_react(user, post.object, "👌")
|
||||||
|
{:ok, emoji_react, _meta} = ActivityPub.persist(emoji_react_data, local: true)
|
||||||
|
|
||||||
|
%{emoji_react: emoji_react, user: user, poster: poster}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "adds the reaction to the object", %{emoji_react: emoji_react, user: user} do
|
||||||
|
{:ok, emoji_react, _} = SideEffects.handle(emoji_react)
|
||||||
|
object = Object.get_by_ap_id(emoji_react.data["object"])
|
||||||
|
|
||||||
|
assert object.data["reaction_count"] == 1
|
||||||
|
assert ["👌", [user.ap_id]] in object.data["reactions"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "creates a notification", %{emoji_react: emoji_react, poster: poster} do
|
||||||
|
{:ok, emoji_react, _} = SideEffects.handle(emoji_react)
|
||||||
|
assert Repo.get_by(Notification, user_id: poster.id, activity_id: emoji_react.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "Undo objects" do
|
describe "Undo objects" do
|
||||||
setup do
|
setup do
|
||||||
poster = insert(:user)
|
poster = insert(:user)
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, post} = CommonAPI.post(poster, %{"status" => "hey"})
|
{:ok, post} = CommonAPI.post(poster, %{"status" => "hey"})
|
||||||
{:ok, like} = CommonAPI.favorite(user, post.id)
|
{:ok, like} = CommonAPI.favorite(user, post.id)
|
||||||
{:ok, reaction, _} = CommonAPI.react_with_emoji(post.id, user, "👍")
|
{:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")
|
||||||
{:ok, announce, _} = CommonAPI.repeat(post.id, user)
|
{:ok, announce, _} = CommonAPI.repeat(post.id, user)
|
||||||
{:ok, block} = ActivityPub.block(user, poster)
|
{:ok, block} = ActivityPub.block(user, poster)
|
||||||
User.block(user, poster)
|
User.block(user, poster)
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.Transmogrifier.EmojiReactHandlingTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
test "it works for incoming emoji reactions" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user, local: false)
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/emoji-reaction.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", activity.data["object"])
|
||||||
|
|> Map.put("actor", other_user.ap_id)
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["actor"] == other_user.ap_id
|
||||||
|
assert data["type"] == "EmojiReact"
|
||||||
|
assert data["id"] == "http://mastodon.example.org/users/admin#reactions/2"
|
||||||
|
assert data["object"] == activity.data["object"]
|
||||||
|
assert data["content"] == "👌"
|
||||||
|
|
||||||
|
object = Object.get_by_ap_id(data["object"])
|
||||||
|
|
||||||
|
assert object.data["reaction_count"] == 1
|
||||||
|
assert match?([["👌", _]], object.data["reactions"])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it reject invalid emoji reactions" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user, local: false)
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/emoji-reaction-too-long.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", activity.data["object"])
|
||||||
|
|> Map.put("actor", other_user.ap_id)
|
||||||
|
|
||||||
|
assert {:error, _} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/emoji-reaction-no-emoji.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", activity.data["object"])
|
||||||
|
|> Map.put("actor", other_user.ap_id)
|
||||||
|
|
||||||
|
assert {:error, _} = Transmogrifier.handle_incoming(data)
|
||||||
|
end
|
||||||
|
end
|
|
@ -17,7 +17,7 @@ test "it works for incoming emoji reaction undos" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
|
||||||
{:ok, reaction_activity, _object} = CommonAPI.react_with_emoji(activity.id, user, "👌")
|
{:ok, reaction_activity} = CommonAPI.react_with_emoji(activity.id, user, "👌")
|
||||||
|
|
||||||
data =
|
data =
|
||||||
File.read!("test/fixtures/mastodon-undo-like.json")
|
File.read!("test/fixtures/mastodon-undo-like.json")
|
||||||
|
|
|
@ -325,43 +325,6 @@ test "it cleans up incoming notices which are not really DMs" do
|
||||||
assert object_data["cc"] == to
|
assert object_data["cc"] == to
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it works for incoming emoji reactions" do
|
|
||||||
user = insert(:user)
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
|
|
||||||
|
|
||||||
data =
|
|
||||||
File.read!("test/fixtures/emoji-reaction.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|> Map.put("object", activity.data["object"])
|
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
|
||||||
|
|
||||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
|
||||||
assert data["type"] == "EmojiReact"
|
|
||||||
assert data["id"] == "http://mastodon.example.org/users/admin#reactions/2"
|
|
||||||
assert data["object"] == activity.data["object"]
|
|
||||||
assert data["content"] == "👌"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it reject invalid emoji reactions" do
|
|
||||||
user = insert(:user)
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
|
|
||||||
|
|
||||||
data =
|
|
||||||
File.read!("test/fixtures/emoji-reaction-too-long.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|> Map.put("object", activity.data["object"])
|
|
||||||
|
|
||||||
assert :error = Transmogrifier.handle_incoming(data)
|
|
||||||
|
|
||||||
data =
|
|
||||||
File.read!("test/fixtures/emoji-reaction-no-emoji.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|> Map.put("object", activity.data["object"])
|
|
||||||
|
|
||||||
assert :error = Transmogrifier.handle_incoming(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it works for incoming announces" do
|
test "it works for incoming announces" do
|
||||||
data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
|
data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
|
||||||
|
|
||||||
|
@ -518,7 +481,7 @@ test "it strips internal likes" do
|
||||||
test "it strips internal reactions" do
|
test "it strips internal reactions" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
|
||||||
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, user, "📢")
|
{:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "📢")
|
||||||
|
|
||||||
%{object: object} = Activity.get_by_id_with_object(activity.id)
|
%{object: object} = Activity.get_by_id_with_object(activity.id)
|
||||||
assert Map.has_key?(object.data, "reactions")
|
assert Map.has_key?(object.data, "reactions")
|
||||||
|
|
|
@ -358,7 +358,7 @@ test "reacting to a status with an emoji" do
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
|
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
|
||||||
|
|
||||||
{:ok, reaction, _} = CommonAPI.react_with_emoji(activity.id, user, "👍")
|
{:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
|
||||||
|
|
||||||
assert reaction.data["actor"] == user.ap_id
|
assert reaction.data["actor"] == user.ap_id
|
||||||
assert reaction.data["content"] == "👍"
|
assert reaction.data["content"] == "👍"
|
||||||
|
@ -373,7 +373,7 @@ test "unreacting to a status with an emoji" do
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
|
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
|
||||||
{:ok, reaction, _} = CommonAPI.react_with_emoji(activity.id, user, "👍")
|
{:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
|
||||||
|
|
||||||
{:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
|
{:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ test "EmojiReact notification" do
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
|
||||||
{:ok, _activity, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
{:ok, _activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
||||||
|
|
||||||
activity = Repo.get(Activity, activity.id)
|
activity = Repo.get(Activity, activity.id)
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,9 @@ test "has an emoji reaction list" do
|
||||||
third_user = insert(:user)
|
third_user = insert(:user)
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "dae cofe??"})
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "dae cofe??"})
|
||||||
|
|
||||||
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, user, "☕")
|
{:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "☕")
|
||||||
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵")
|
{:ok, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵")
|
||||||
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
||||||
activity = Repo.get(Activity, activity.id)
|
activity = Repo.get(Activity, activity.id)
|
||||||
status = StatusView.render("show.json", activity: activity)
|
status = StatusView.render("show.json", activity: activity)
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
|
||||||
{:ok, _reaction, _object} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
{:ok, _reaction_activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
||||||
|
|
||||||
ObanHelpers.perform_all()
|
ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
@ -77,8 +77,8 @@ test "GET /api/v1/pleroma/statuses/:id/reactions", %{conn: conn} do
|
||||||
|
|
||||||
assert result == []
|
assert result == []
|
||||||
|
|
||||||
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
|
{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
|
||||||
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, doomed_user, "🎅")
|
{:ok, _} = CommonAPI.react_with_emoji(activity.id, doomed_user, "🎅")
|
||||||
|
|
||||||
User.perform(:delete, doomed_user)
|
User.perform(:delete, doomed_user)
|
||||||
|
|
||||||
|
@ -115,8 +115,8 @@ test "GET /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
|
||||||
|
|
||||||
assert result == []
|
assert result == []
|
||||||
|
|
||||||
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
|
{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
|
||||||
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
{:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
||||||
|
|
||||||
result =
|
result =
|
||||||
conn
|
conn
|
||||||
|
|
Loading…
Reference in a new issue