Chat: Allow posting without content if an attachment is present.

This commit is contained in:
lain 2020-05-13 15:31:28 +02:00
parent 06cad239e5
commit 0f0acc740d
9 changed files with 111 additions and 19 deletions

View file

@ -166,11 +166,10 @@ Posting a chat message for given Chat id works like this:
`POST /api/v1/pleroma/chats/{id}/messages` `POST /api/v1/pleroma/chats/{id}/messages`
Parameters: Parameters:
- content: The text content of the message - content: The text content of the message. Optional if media is attached.
- media_id: The id of an upload that will be attached to the message. - media_id: The id of an upload that will be attached to the message.
Currently, no formatting beyond basic escaping and emoji is implemented, as well as no Currently, no formatting beyond basic escaping and emoji is implemented.
attachments. This will most probably change.
Returned data: Returned data:

View file

@ -61,12 +61,24 @@ def changeset(struct, data) do
def validate_data(data_cng) do def validate_data(data_cng) do
data_cng data_cng
|> validate_inclusion(:type, ["ChatMessage"]) |> validate_inclusion(:type, ["ChatMessage"])
|> validate_required([:id, :actor, :to, :type, :content, :published]) |> validate_required([:id, :actor, :to, :type, :published])
|> validate_content_or_attachment()
|> validate_length(:to, is: 1) |> validate_length(:to, is: 1)
|> validate_length(:content, max: Pleroma.Config.get([:instance, :remote_limit])) |> validate_length(:content, max: Pleroma.Config.get([:instance, :remote_limit]))
|> validate_local_concern() |> validate_local_concern()
end end
def validate_content_or_attachment(cng) do
attachment = get_field(cng, :attachment)
if attachment do
cng
else
cng
|> validate_required([:content])
end
end
@doc """ @doc """
Validates the following Validates the following
- If both users are in our system - If both users are in our system

View file

@ -5,6 +5,7 @@
defmodule Pleroma.Web.ApiSpec.ChatOperation do defmodule Pleroma.Web.ApiSpec.ChatOperation do
alias OpenApiSpex.Operation alias OpenApiSpex.Operation
alias OpenApiSpex.Schema alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.Chat alias Pleroma.Web.ApiSpec.Schemas.Chat
alias Pleroma.Web.ApiSpec.Schemas.ChatMessage alias Pleroma.Web.ApiSpec.Schemas.ChatMessage
@ -149,14 +150,15 @@ def post_chat_message_operation do
parameters: [ parameters: [
Operation.parameter(:id, :path, :string, "The ID of the Chat") Operation.parameter(:id, :path, :string, "The ID of the Chat")
], ],
requestBody: request_body("Parameters", chat_message_create(), required: true), requestBody: request_body("Parameters", chat_message_create()),
responses: %{ responses: %{
200 => 200 =>
Operation.response( Operation.response(
"The newly created ChatMessage", "The newly created ChatMessage",
"application/json", "application/json",
ChatMessage ChatMessage
) ),
400 => Operation.response("Bad Request", "application/json", ApiError)
}, },
security: [ security: [
%{ %{
@ -292,10 +294,12 @@ def chat_message_create do
description: "POST body for creating an chat message", description: "POST body for creating an chat message",
type: :object, type: :object,
properties: %{ properties: %{
content: %Schema{type: :string, description: "The content of your message"}, content: %Schema{
type: :string,
description: "The content of your message. Optional if media_id is present"
},
media_id: %Schema{type: :string, description: "The id of an upload"} media_id: %Schema{type: :string, description: "The id of an upload"}
}, },
required: [:content],
example: %{ example: %{
"content" => "Hey wanna buy feet pics?", "content" => "Hey wanna buy feet pics?",
"media_id" => "134234" "media_id" => "134234"

View file

@ -16,7 +16,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.ChatMessage do
id: %Schema{type: :string}, id: %Schema{type: :string},
account_id: %Schema{type: :string, description: "The Mastodon API id of the actor"}, account_id: %Schema{type: :string, description: "The Mastodon API id of the actor"},
chat_id: %Schema{type: :string}, chat_id: %Schema{type: :string},
content: %Schema{type: :string}, content: %Schema{type: :string, nullable: true},
created_at: %Schema{type: :string, format: :"date-time"}, created_at: %Schema{type: :string, format: :"date-time"},
emojis: %Schema{type: :array}, emojis: %Schema{type: :array},
attachment: %Schema{type: :object, nullable: true} attachment: %Schema{type: :object, nullable: true}

View file

@ -26,14 +26,14 @@ defmodule Pleroma.Web.CommonAPI do
require Logger require Logger
def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do
with :ok <- validate_chat_content_length(content), with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]),
maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]), :ok <- validate_chat_content_length(content, !!maybe_attachment),
{_, {:ok, chat_message_data, _meta}} <- {_, {:ok, chat_message_data, _meta}} <-
{:build_object, {:build_object,
Builder.chat_message( Builder.chat_message(
user, user,
recipient.ap_id, recipient.ap_id,
content |> Formatter.html_escape("text/plain"), content |> format_chat_content,
attachment: maybe_attachment attachment: maybe_attachment
)}, )},
{_, {:ok, create_activity_data, _meta}} <- {_, {:ok, create_activity_data, _meta}} <-
@ -47,7 +47,16 @@ def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ [])
end end
end end
defp validate_chat_content_length(content) do defp format_chat_content(nil), do: nil
defp format_chat_content(content) do
content |> Formatter.html_escape("text/plain")
end
defp validate_chat_content_length(_, true), do: :ok
defp validate_chat_content_length(nil, false), do: {:error, :no_content}
defp validate_chat_content_length(content, _) do
if String.length(content) <= Pleroma.Config.get([:instance, :chat_limit]) do if String.length(content) <= Pleroma.Config.get([:instance, :chat_limit]) do
:ok :ok
else else

View file

@ -58,8 +58,7 @@ def delete_message(%{assigns: %{user: %{ap_id: actor} = user}} = conn, %{
end end
def post_chat_message( def post_chat_message(
%{body_params: %{content: content} = params, assigns: %{user: %{id: user_id} = user}} = %{body_params: params, assigns: %{user: %{id: user_id} = user}} = conn,
conn,
%{ %{
id: id id: id
} }
@ -67,7 +66,9 @@ def post_chat_message(
with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id), with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id),
%User{} = recipient <- User.get_cached_by_ap_id(chat.recipient), %User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
{:ok, activity} <- {:ok, activity} <-
CommonAPI.post_chat_message(user, recipient, content, media_id: params[:media_id]), CommonAPI.post_chat_message(user, recipient, params[:content],
media_id: params[:media_id]
),
message <- Object.normalize(activity) do message <- Object.normalize(activity) do
conn conn
|> put_view(ChatMessageView) |> put_view(ChatMessageView)

View file

@ -103,6 +103,38 @@ test "validates for a basic object with an attachment", %{
assert object["attachment"] assert object["attachment"]
end end
test "validates for a basic object with an attachment but without content", %{
valid_chat_message: valid_chat_message,
user: user
} do
file = %Plug.Upload{
content_type: "image/jpg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id)
valid_chat_message =
valid_chat_message
|> Map.put("attachment", attachment.data)
|> Map.delete("content")
assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, [])
assert object["attachment"]
end
test "does not validate if the message has no content", %{
valid_chat_message: valid_chat_message
} do
contentless =
valid_chat_message
|> Map.delete("content")
refute match?({:ok, _object, _meta}, ObjectValidator.validate(contentless, []))
end
test "does not validate if the message is longer than the remote_limit", %{ test "does not validate if the message is longer than the remote_limit", %{
valid_chat_message: valid_chat_message valid_chat_message: valid_chat_message
} do } do

View file

@ -27,6 +27,29 @@ defmodule Pleroma.Web.CommonAPITest do
describe "posting chat messages" do describe "posting chat messages" do
setup do: clear_config([:instance, :chat_limit]) setup do: clear_config([:instance, :chat_limit])
test "it posts a chat message without content but with an attachment" do
author = insert(:user)
recipient = insert(:user)
file = %Plug.Upload{
content_type: "image/jpg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, upload} = ActivityPub.upload(file, actor: author.ap_id)
{:ok, activity} =
CommonAPI.post_chat_message(
author,
recipient,
nil,
media_id: upload.id
)
assert activity
end
test "it posts a chat message" do test "it posts a chat message" do
author = insert(:user) author = insert(:user)
recipient = insert(:user) recipient = insert(:user)

View file

@ -53,6 +53,20 @@ test "it posts a message to the chat", %{conn: conn, user: user} do
assert result["chat_id"] == chat.id |> to_string() assert result["chat_id"] == chat.id |> to_string()
end end
test "it fails if there is no content", %{conn: conn, user: user} do
other_user = insert(:user)
{:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
result =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/chats/#{chat.id}/messages")
|> json_response_and_validate_schema(400)
assert result
end
test "it works with an attachment", %{conn: conn, user: user} do test "it works with an attachment", %{conn: conn, user: user} do
file = %Plug.Upload{ file = %Plug.Upload{
content_type: "image/jpg", content_type: "image/jpg",
@ -70,13 +84,11 @@ test "it works with an attachment", %{conn: conn, user: user} do
conn conn
|> put_req_header("content-type", "application/json") |> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{ |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{
"content" => "Hallo!!",
"media_id" => to_string(upload.id) "media_id" => to_string(upload.id)
}) })
|> json_response_and_validate_schema(200) |> json_response_and_validate_schema(200)
assert result["content"] == "Hallo!!" assert result["attachment"]
assert result["chat_id"] == chat.id |> to_string()
end end
end end