chat_controller.ex 5.24 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
    exclude_users =
143
144
      User.blocked_users_ap_ids(user) ++
        if params[:with_muted], do: [], else: User.muted_users_ap_ids(user)
145

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

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

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

162
163
  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
164
      render(conn, "show.json", chat: chat)
lain's avatar
lain committed
165
166
    end
  end
167
168
169
170
171
172
173

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