`POST /api/v1/pleroma/chats/{id}/messages`
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.
-Currently, no formatting beyond basic escaping and emoji is implemented, as well as no
-attachments. This will most probably change.
+Currently, no formatting beyond basic escaping and emoji is implemented.
Returned data:
def validate_data(data_cng) do
data_cng
|> 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(:content, max: Pleroma.Config.get([:instance, :remote_limit]))
|> validate_local_concern()
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 """
Validates the following
- If both users are in our system
defmodule Pleroma.Web.ApiSpec.ChatOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
+ alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.Chat
alias Pleroma.Web.ApiSpec.Schemas.ChatMessage
parameters: [
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: %{
200 =>
Operation.response(
"The newly created ChatMessage",
"application/json",
ChatMessage
- )
+ ),
+ 400 => Operation.response("Bad Request", "application/json", ApiError)
},
security: [
%{
description: "POST body for creating an chat message",
type: :object,
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"}
},
- required: [:content],
example: %{
"content" => "Hey wanna buy feet pics?",
"media_id" => "134234"
id: %Schema{type: :string},
account_id: %Schema{type: :string, description: "The Mastodon API id of the actor"},
chat_id: %Schema{type: :string},
- content: %Schema{type: :string},
+ content: %Schema{type: :string, nullable: true},
created_at: %Schema{type: :string, format: :"date-time"},
emojis: %Schema{type: :array},
attachment: %Schema{type: :object, nullable: true}
require Logger
def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do
- with :ok <- validate_chat_content_length(content),
- maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]),
+ with 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}} <-
{:build_object,
Builder.chat_message(
user,
recipient.ap_id,
- content |> Formatter.html_escape("text/plain"),
+ content |> format_chat_content,
attachment: maybe_attachment
)},
{_, {:ok, create_activity_data, _meta}} <-
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
:ok
else
end
def post_chat_message(
- %{body_params: %{content: content} = params, assigns: %{user: %{id: user_id} = user}} =
- conn,
+ %{body_params: params, assigns: %{user: %{id: user_id} = user}} = conn,
%{
id: id
}
with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id),
%User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
{: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
conn
|> put_view(ChatMessageView)
assert object["attachment"]
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", %{
valid_chat_message: valid_chat_message
} do
describe "posting chat messages" do
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
author = insert(:user)
recipient = insert(:user)
assert result["chat_id"] == chat.id |> to_string()
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
file = %Plug.Upload{
content_type: "image/jpg",
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{
- "content" => "Hallo!!",
"media_id" => to_string(upload.id)
})
|> json_response_and_validate_schema(200)
- assert result["content"] == "Hallo!!"
- assert result["chat_id"] == chat.id |> to_string()
+ assert result["attachment"]
end
end