forked from AkkomaGang/akkoma
ChatMessage: Basic incoming handling.
This commit is contained in:
parent
19335be24c
commit
3775683a04
12 changed files with 250 additions and 2 deletions
|
@ -10,7 +10,7 @@ defmodule Pleroma.Chat do
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Chat keeps a reference to DirectMessage conversations between a user and an recipient. The recipient can be a user (for now) or a group (not implemented yet).
|
Chat keeps a reference to ChatMessage conversations between a user and an recipient. The recipient can be a user (for now) or a group (not implemented yet).
|
||||||
|
|
||||||
It is a helper only, to make it easy to display a list of chats with other people, ordered by last bump. The actual messages are retrieved by querying the recipients of the ChatMessages.
|
It is a helper only, to make it easy to display a list of chats with other people, ordered by last bump. The actual messages are retrieved by querying the recipients of the ChatMessages.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -397,6 +397,7 @@ defp do_unreact_with_emoji(user, reaction_id, options) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO: Is this even used now?
|
||||||
# TODO: This is weird, maybe we shouldn't check here if we can make the activity.
|
# TODO: This is weird, maybe we shouldn't check here if we can make the activity.
|
||||||
@spec like(User.t(), Object.t(), String.t() | nil, boolean()) ::
|
@spec like(User.t(), Object.t(), String.t() | nil, boolean()) ::
|
||||||
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
||||||
|
|
|
@ -12,18 +12,46 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
|
||||||
|
|
||||||
@spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
|
@spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
|
||||||
def validate(object, meta)
|
def validate(object, meta)
|
||||||
|
|
||||||
def validate(%{"type" => "Like"} = object, meta) do
|
def validate(%{"type" => "Like"} = object, meta) do
|
||||||
with {:ok, object} <-
|
with {:ok, object} <-
|
||||||
object |> LikeValidator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert) do
|
object
|
||||||
|
|> LikeValidator.cast_and_validate()
|
||||||
|
|> Ecto.Changeset.apply_action(:insert) do
|
||||||
object = stringify_keys(object |> Map.from_struct())
|
object = stringify_keys(object |> Map.from_struct())
|
||||||
{:ok, object, meta}
|
{:ok, object, meta}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate(%{"type" => "ChatMessage"} = object, meta) do
|
||||||
|
with {:ok, object} <-
|
||||||
|
object
|
||||||
|
|> ChatMessageValidator.cast_and_apply() do
|
||||||
|
object = stringify_keys(object)
|
||||||
|
{:ok, object, meta}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate(%{"type" => "Create"} = object, meta) do
|
||||||
|
with {:ok, object} <-
|
||||||
|
object
|
||||||
|
|> CreateChatMessageValidator.cast_and_apply() do
|
||||||
|
object = stringify_keys(object)
|
||||||
|
{:ok, object, meta}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def stringify_keys(%{__struct__: _} = object) do
|
||||||
|
object
|
||||||
|
|> Map.from_struct()
|
||||||
|
|> stringify_keys
|
||||||
|
end
|
||||||
|
|
||||||
def stringify_keys(object) do
|
def stringify_keys(object) do
|
||||||
object
|
object
|
||||||
|> Map.new(fn {key, val} -> {to_string(key), val} end)
|
|> Map.new(fn {key, val} -> {to_string(key), val} end)
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
# 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.ChatMessageValidator do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
@primary_key false
|
||||||
|
@derive Jason.Encoder
|
||||||
|
|
||||||
|
embedded_schema do
|
||||||
|
field(:id, Types.ObjectID, primary_key: true)
|
||||||
|
field(:to, Types.Recipients, default: [])
|
||||||
|
field(:type, :string)
|
||||||
|
field(:content, :string)
|
||||||
|
field(:actor, Types.ObjectID)
|
||||||
|
field(:published, Types.DateTime)
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_and_apply(data) do
|
||||||
|
data
|
||||||
|
|> cast_data
|
||||||
|
|> apply_action(:insert)
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_and_validate(data) do
|
||||||
|
data
|
||||||
|
|> cast_data()
|
||||||
|
|> validate_data()
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_data(data) do
|
||||||
|
%__MODULE__{}
|
||||||
|
|> changeset(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def fix(data) do
|
||||||
|
data
|
||||||
|
|> Map.put_new("actor", data["attributedTo"])
|
||||||
|
end
|
||||||
|
|
||||||
|
def changeset(struct, data) do
|
||||||
|
data = fix(data)
|
||||||
|
|
||||||
|
struct
|
||||||
|
|> cast(data, __schema__(:fields))
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_data(data_cng) do
|
||||||
|
data_cng
|
||||||
|
|> validate_inclusion(:type, ["ChatMessage"])
|
||||||
|
|> validate_required([:id, :actor, :to, :type, :content])
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
# NOTES
|
||||||
|
# - Can probably be a generic create validator
|
||||||
|
# - doesn't embed, will only get the object id
|
||||||
|
# - object has to be validated first, maybe with some meta info from the surrounding create
|
||||||
|
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
@primary_key false
|
||||||
|
|
||||||
|
embedded_schema do
|
||||||
|
field(:id, Types.ObjectID, primary_key: true)
|
||||||
|
field(:actor, Types.ObjectID)
|
||||||
|
field(:type, :string)
|
||||||
|
field(:to, Types.Recipients, default: [])
|
||||||
|
field(:object, Types.ObjectID)
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_and_apply(data) do
|
||||||
|
data
|
||||||
|
|> cast_data
|
||||||
|
|> apply_action(:insert)
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_data(data) do
|
||||||
|
cast(%__MODULE__{}, data, __schema__(:fields))
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.Recipients do
|
||||||
|
use Ecto.Type
|
||||||
|
|
||||||
|
def type, do: {:array, :string}
|
||||||
|
|
||||||
|
def cast(object) when is_binary(object) do
|
||||||
|
cast([object])
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast([_ | _] = data), do: {:ok, data}
|
||||||
|
|
||||||
|
def cast(_) do
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
|
||||||
|
def dump(data) do
|
||||||
|
{:ok, data}
|
||||||
|
end
|
||||||
|
|
||||||
|
def load(data) do
|
||||||
|
{:ok, data}
|
||||||
|
end
|
||||||
|
end
|
|
@ -16,6 +16,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
|
||||||
alias Pleroma.Web.ActivityPub.Pipeline
|
alias Pleroma.Web.ActivityPub.Pipeline
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier.ChatMessageHandling
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
|
@ -612,6 +613,12 @@ def handle_incoming(
|
||||||
|> handle_incoming(options)
|
|> handle_incoming(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_incoming(
|
||||||
|
%{"type" => "Create", "object" => %{"type" => "ChatMessage"}} = data,
|
||||||
|
options
|
||||||
|
),
|
||||||
|
do: ChatMessageHandling.handle_incoming(data, options)
|
||||||
|
|
||||||
def handle_incoming(%{"type" => "Like"} = data, _options) do
|
def handle_incoming(%{"type" => "Like"} = data, _options) do
|
||||||
with {_, {:ok, cast_data_sym}} <-
|
with {_, {:ok, cast_data_sym}} <-
|
||||||
{:casting_data,
|
{:casting_data,
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# 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.ChatMessageHandling do
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidator
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
|
||||||
|
alias Pleroma.Web.ActivityPub.Pipeline
|
||||||
|
|
||||||
|
def handle_incoming(
|
||||||
|
%{"type" => "Create", "object" => %{"type" => "ChatMessage"} = object_data} = data,
|
||||||
|
_options
|
||||||
|
) do
|
||||||
|
with {_, {:ok, cast_data_sym}} <-
|
||||||
|
{:casting_data, data |> CreateChatMessageValidator.cast_and_apply()},
|
||||||
|
cast_data = ObjectValidator.stringify_keys(cast_data_sym),
|
||||||
|
{_, {:ok, object_cast_data_sym}} <-
|
||||||
|
{:casting_object_data, object_data |> ChatMessageValidator.cast_and_apply()},
|
||||||
|
object_cast_data = ObjectValidator.stringify_keys(object_cast_data_sym),
|
||||||
|
{_, {:ok, validated_object, _meta}} <-
|
||||||
|
{:validate_object, ObjectValidator.validate(object_cast_data, %{})},
|
||||||
|
{_, {:ok, _created_object}} <- {:persist_object, Object.create(validated_object)},
|
||||||
|
{_, {:ok, activity, _meta}} <-
|
||||||
|
{:common_pipeline, Pipeline.common_pipeline(cast_data, local: false)} do
|
||||||
|
{:ok, activity}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
19
test/fixtures/create-chat-message.json
vendored
Normal file
19
test/fixtures/create-chat-message.json
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"actor": "http://2hu.gensokyo/users/raymoo",
|
||||||
|
"id": "http://2hu.gensokyo/objects/1",
|
||||||
|
"object": {
|
||||||
|
"attributedTo": "http://2hu.gensokyo/users/raymoo",
|
||||||
|
"content": "You expected a cute girl? Too bad.",
|
||||||
|
"id": "http://2hu.gensokyo/objects/2",
|
||||||
|
"published": "2020-02-12T14:08:20Z",
|
||||||
|
"to": [
|
||||||
|
"http://2hu.gensokyo/users/marisa"
|
||||||
|
],
|
||||||
|
"type": "ChatMessage"
|
||||||
|
},
|
||||||
|
"published": "2018-02-12T14:08:20Z",
|
||||||
|
"to": [
|
||||||
|
"http://2hu.gensokyo/users/marisa"
|
||||||
|
],
|
||||||
|
"type": "Create"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
defmodule Pleroma.Web.ObjectValidators.Types.RecipientsTest do
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.Types.Recipients
|
||||||
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
test "it works with a list" do
|
||||||
|
list = ["https://lain.com/users/lain"]
|
||||||
|
assert {:ok, list} == Recipients.cast(list)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it turns a single string into a list" do
|
||||||
|
recipient = "https://lain.com/users/lain"
|
||||||
|
|
||||||
|
assert {:ok, [recipient]} == Recipients.cast(recipient)
|
||||||
|
end
|
||||||
|
end
|
32
test/web/activity_pub/transmogrifier/chat_message_test.exs
Normal file
32
test/web/activity_pub/transmogrifier/chat_message_test.exs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# 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.ChatMessageTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
|
||||||
|
describe "handle_incoming" do
|
||||||
|
test "it insert it" do
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/create-chat-message.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|
||||||
|
author = insert(:user, ap_id: data["actor"], local: false)
|
||||||
|
recipient = insert(:user, ap_id: List.first(data["to"]), local: false)
|
||||||
|
|
||||||
|
{:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert activity.actor == author.ap_id
|
||||||
|
assert activity.recipients == [recipient.ap_id, author.ap_id]
|
||||||
|
|
||||||
|
%Object{} = object = Object.get_by_ap_id(activity.data["object"])
|
||||||
|
assert object
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue