chat_controller.ex 5.22 KB
Newer Older
1
2
3
4
5
6
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ChatController do
  use Pleroma.Web, :controller

7
8
  import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]

9
  alias Pleroma.Activity
10
  alias Pleroma.Chat
11
  alias Pleroma.Chat.MessageReference
12
  alias Pleroma.Object
lain's avatar
lain committed
13
  alias Pleroma.Pagination
14
  alias Pleroma.Repo
lain's avatar
lain committed
15
16
  alias Pleroma.User
  alias Pleroma.Web.CommonAPI
17
  alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
18
  alias Pleroma.Web.Plugs.OAuthScopesPlug
19
20
21

  import Ecto.Query

22
  action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
lain's avatar
lain committed
23

lain's avatar
lain committed
24
25
  plug(
    OAuthScopesPlug,
26
    %{scopes: ["write:chats"]}
27
28
29
30
31
32
33
    when action in [
           :post_chat_message,
           :create,
           :mark_as_read,
           :mark_message_as_read,
           :delete_message
         ]
lain's avatar
lain committed
34
35
36
37
  )

  plug(
    OAuthScopesPlug,
38
    %{scopes: ["read:chats"]} when action in [:messages, :index, :show]
lain's avatar
lain committed
39
40
  )

41
  plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
lain's avatar
lain committed
42

43
44
  defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ChatOperation

45
46
47
  def delete_message(%{assigns: %{user: %{id: user_id} = user}} = conn, %{
        message_id: message_id,
        id: chat_id
48
      }) do
49
50
    with %MessageReference{} = cm_ref <-
           MessageReference.get_by_id(message_id),
51
         ^chat_id <- to_string(cm_ref.chat_id),
52
53
         %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
         {:ok, _} <- remove_or_delete(cm_ref, user) do
54
      conn
55
      |> put_view(MessageReferenceView)
56
      |> render("show.json", chat_message_reference: cm_ref)
57
    else
58
59
      _e ->
        {:error, :could_not_delete}
60
61
62
    end
  end

63
64
65
66
67
68
69
70
71
  defp remove_or_delete(
         %{object: %{data: %{"actor" => actor, "id" => id}}},
         %{ap_id: actor} = user
       ) do
    with %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
      CommonAPI.delete(activity.id, user)
    end
  end

72
  defp remove_or_delete(cm_ref, _), do: MessageReference.delete(cm_ref)
73

lain's avatar
lain committed
74
  def post_chat_message(
75
76
        %{body_params: params, assigns: %{user: user}} = conn,
        %{id: id}
lain's avatar
lain committed
77
      ) do
78
    with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
lain's avatar
lain committed
79
         %User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
lain's avatar
lain committed
80
         {:ok, activity} <-
81
           CommonAPI.post_chat_message(user, recipient, params[:content],
82
83
             media_id: params[:media_id],
             idempotency_key: idempotency_key(conn)
84
           ),
85
         message <- Object.normalize(activity, false),
86
         cm_ref <- MessageReference.for_chat_and_object(chat, message) do
lain's avatar
lain committed
87
      conn
88
      |> put_view(MessageReferenceView)
89
      |> render("show.json", chat_message_reference: cm_ref)
Haelwenn's avatar
Haelwenn committed
90
91
92
93
94
95
96
97
98
99
    else
      {:reject, message} ->
        conn
        |> put_status(:unprocessable_entity)
        |> json(%{error: message})

      {:error, message} ->
        conn
        |> put_status(:bad_request)
        |> json(%{error: message})
lain's avatar
lain committed
100
101
102
    end
  end

103
104
105
106
107
108
  def mark_message_as_read(
        %{assigns: %{user: %{id: user_id}}} = conn,
        %{id: chat_id, message_id: message_id}
      ) do
    with %MessageReference{} = cm_ref <- MessageReference.get_by_id(message_id),
         ^chat_id <- to_string(cm_ref.chat_id),
109
         %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
110
         {:ok, cm_ref} <- MessageReference.mark_as_read(cm_ref) do
111
      conn
112
      |> put_view(MessageReferenceView)
113
      |> render("show.json", chat_message_reference: cm_ref)
114
115
116
    end
  end

117
  def mark_as_read(
118
        %{body_params: %{last_read_id: last_read_id}, assigns: %{user: user}} = conn,
119
120
        %{id: id}
      ) do
121
122
    with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
         {_n, _} <- MessageReference.set_all_seen_for_chat(chat, last_read_id) do
minibikini's avatar
minibikini committed
123
      render(conn, "show.json", chat: chat)
lain's avatar
lain committed
124
125
126
    end
  end

127
128
129
  def messages(%{assigns: %{user: user}} = conn, %{id: id} = params) do
    with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
      chat_message_refs =
130
        chat
131
        |> MessageReference.for_chat_query()
132
        |> Pagination.fetch_paginated(params)
133
134

      conn
135
      |> add_link_headers(chat_message_refs)
136
      |> put_view(MessageReferenceView)
137
      |> render("index.json", chat_message_references: chat_message_refs)
138
139
140
    end
  end

141
  def index(%{assigns: %{user: %{id: user_id} = user}} = conn, _params) do
minibikini's avatar
minibikini committed
142
143
144
145
    exclude_users =
      user
      |> User.blocked_users_ap_ids()
      |> Enum.concat(User.muted_users_ap_ids(user))
146

147
    chats =
minibikini's avatar
minibikini committed
148
149
150
      user_id
      |> Chat.for_user_query()
      |> where([c], c.recipient not in ^exclude_users)
151
      |> Repo.all()
152

minibikini's avatar
minibikini committed
153
    render(conn, "index.json", chats: chats)
154
155
  end

156
157
  def create(%{assigns: %{user: user}} = conn, %{id: id}) do
    with %User{ap_id: recipient} <- User.get_cached_by_id(id),
158
         {:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
minibikini's avatar
minibikini committed
159
      render(conn, "show.json", chat: chat)
160
161
    end
  end
lain's avatar
lain committed
162

163
164
  def show(%{assigns: %{user: user}} = conn, %{id: id}) do
    with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
minibikini's avatar
minibikini committed
165
      render(conn, "show.json", chat: chat)
lain's avatar
lain committed
166
167
    end
  end
168
169
170
171
172
173
174

  defp idempotency_key(conn) do
    case get_req_header(conn, "idempotency-key") do
      [key] -> key
      _ -> nil
    end
  end
175
end