Add link headers in ChatController.index2
[akkoma] / lib / pleroma / web / pleroma_api / controllers / chat_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4 defmodule Pleroma.Web.PleromaAPI.ChatController do
5 use Pleroma.Web, :controller
6
7 import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
8
9 alias Pleroma.Activity
10 alias Pleroma.Chat
11 alias Pleroma.Chat.MessageReference
12 alias Pleroma.Object
13 alias Pleroma.Pagination
14 alias Pleroma.Repo
15 alias Pleroma.User
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
18 alias Pleroma.Web.Plugs.OAuthScopesPlug
19
20 import Ecto.Query
21
22 action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
23
24 plug(
25 OAuthScopesPlug,
26 %{scopes: ["write:chats"]}
27 when action in [
28 :post_chat_message,
29 :create,
30 :mark_as_read,
31 :mark_message_as_read,
32 :delete_message
33 ]
34 )
35
36 plug(
37 OAuthScopesPlug,
38 %{scopes: ["read:chats"]} when action in [:messages, :index, :index2, :show]
39 )
40
41 plug(Pleroma.Web.ApiSpec.CastAndValidate)
42
43 defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ChatOperation
44
45 def delete_message(%{assigns: %{user: %{id: user_id} = user}} = conn, %{
46 message_id: message_id,
47 id: chat_id
48 }) do
49 with %MessageReference{} = cm_ref <-
50 MessageReference.get_by_id(message_id),
51 ^chat_id <- to_string(cm_ref.chat_id),
52 %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
53 {: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 _e ->
59 {:error, :could_not_delete}
60 end
61 end
62
63 defp remove_or_delete(
64 %{object: %{data: %{"actor" => actor, "id" => id}}},
65 %{ap_id: actor} = user
66 ) do
67 with %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
68 CommonAPI.delete(activity.id, user)
69 end
70 end
71
72 defp remove_or_delete(cm_ref, _), do: MessageReference.delete(cm_ref)
73
74 def post_chat_message(
75 %{body_params: params, assigns: %{user: user}} = conn,
76 %{id: id}
77 ) do
78 with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
79 %User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
80 {:ok, activity} <-
81 CommonAPI.post_chat_message(user, recipient, params[:content],
82 media_id: params[:media_id],
83 idempotency_key: idempotency_key(conn)
84 ),
85 message <- Object.normalize(activity, fetch: false),
86 cm_ref <- MessageReference.for_chat_and_object(chat, message) do
87 conn
88 |> put_view(MessageReferenceView)
89 |> render("show.json", chat_message_reference: cm_ref)
90 else
91 {:reject, message} ->
92 conn
93 |> put_status(:unprocessable_entity)
94 |> json(%{error: message})
95
96 {:error, message} ->
97 conn
98 |> put_status(:bad_request)
99 |> json(%{error: message})
100 end
101 end
102
103 def mark_message_as_read(
104 %{assigns: %{user: %{id: user_id}}} = conn,
105 %{id: chat_id, message_id: message_id}
106 ) do
107 with %MessageReference{} = cm_ref <- MessageReference.get_by_id(message_id),
108 ^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 end
115 end
116
117 def mark_as_read(
118 %{body_params: %{last_read_id: last_read_id}, assigns: %{user: user}} = conn,
119 %{id: id}
120 ) do
121 with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
122 {_n, _} <- MessageReference.set_all_seen_for_chat(chat, last_read_id) do
123 render(conn, "show.json", chat: chat)
124 end
125 end
126
127 def messages(%{assigns: %{user: user}} = conn, %{id: id} = params) do
128 with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
129 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 end
139 end
140
141 def index(%{assigns: %{user: user}} = conn, params) do
142 chats =
143 index_query(user, params)
144 |> Repo.all()
145
146 render(conn, "index.json", chats: chats)
147 end
148
149 def index2(%{assigns: %{user: user}} = conn, params) do
150 chats =
151 index_query(user, params)
152 |> Pagination.fetch_paginated(params)
153
154 conn
155 |> add_link_headers(chats)
156 |> render("index.json", chats: chats)
157 end
158
159 defp index_query(%{id: user_id} = user, params) do
160 exclude_users =
161 User.cached_blocked_users_ap_ids(user) ++
162 if params[:with_muted], do: [], else: User.cached_muted_users_ap_ids(user)
163
164 user_id
165 |> Chat.for_user_query()
166 |> where([c], c.recipient not in ^exclude_users)
167 end
168
169 def create(%{assigns: %{user: user}} = conn, %{id: id}) do
170 with %User{ap_id: recipient} <- User.get_cached_by_id(id),
171 {:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
172 render(conn, "show.json", chat: chat)
173 end
174 end
175
176 def show(%{assigns: %{user: user}} = conn, %{id: id}) do
177 with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
178 render(conn, "show.json", chat: chat)
179 end
180 end
181
182 defp idempotency_key(conn) do
183 case get_req_header(conn, "idempotency-key") do
184 [key] -> key
185 _ -> nil
186 end
187 end
188 end