added paginate links to headers for /chats/:id/messages
[akkoma] / lib / pleroma / web / pleroma_api / controllers / chat_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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.Plugs.OAuthScopesPlug
15 alias Pleroma.Repo
16 alias Pleroma.User
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
19 alias Pleroma.Web.PleromaAPI.ChatView
20
21 import Ecto.Query
22
23 action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
24
25 plug(
26 OAuthScopesPlug,
27 %{scopes: ["write:chats"]}
28 when action in [
29 :post_chat_message,
30 :create,
31 :mark_as_read,
32 :mark_message_as_read,
33 :delete_message
34 ]
35 )
36
37 plug(
38 OAuthScopesPlug,
39 %{scopes: ["read:chats"]} when action in [:messages, :index, :show]
40 )
41
42 plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
43
44 defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ChatOperation
45
46 def delete_message(%{assigns: %{user: %{id: user_id} = user}} = conn, %{
47 message_id: message_id,
48 id: chat_id
49 }) do
50 with %MessageReference{} = cm_ref <-
51 MessageReference.get_by_id(message_id),
52 ^chat_id <- to_string(cm_ref.chat_id),
53 %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
54 {:ok, _} <- remove_or_delete(cm_ref, user) do
55 conn
56 |> put_view(MessageReferenceView)
57 |> render("show.json", chat_message_reference: cm_ref)
58 else
59 _e ->
60 {:error, :could_not_delete}
61 end
62 end
63
64 defp remove_or_delete(
65 %{object: %{data: %{"actor" => actor, "id" => id}}},
66 %{ap_id: actor} = user
67 ) do
68 with %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
69 CommonAPI.delete(activity.id, user)
70 end
71 end
72
73 defp remove_or_delete(cm_ref, _), do: MessageReference.delete(cm_ref)
74
75 def post_chat_message(
76 %{body_params: params, assigns: %{user: user}} = conn,
77 %{id: id}
78 ) do
79 with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
80 %User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
81 {:ok, activity} <-
82 CommonAPI.post_chat_message(user, recipient, params[:content],
83 media_id: params[:media_id]
84 ),
85 message <- Object.normalize(activity, 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 end
91 end
92
93 def mark_message_as_read(
94 %{assigns: %{user: %{id: user_id}}} = conn,
95 %{id: chat_id, message_id: message_id}
96 ) do
97 with %MessageReference{} = cm_ref <- MessageReference.get_by_id(message_id),
98 ^chat_id <- to_string(cm_ref.chat_id),
99 %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
100 {:ok, cm_ref} <- MessageReference.mark_as_read(cm_ref) do
101 conn
102 |> put_view(MessageReferenceView)
103 |> render("show.json", chat_message_reference: cm_ref)
104 end
105 end
106
107 def mark_as_read(
108 %{body_params: %{last_read_id: last_read_id}, assigns: %{user: user}} = conn,
109 %{id: id}
110 ) do
111 with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
112 {_n, _} <- MessageReference.set_all_seen_for_chat(chat, last_read_id) do
113 conn
114 |> put_view(ChatView)
115 |> render("show.json", chat: chat)
116 end
117 end
118
119 def messages(%{assigns: %{user: user}} = conn, %{id: id} = params) do
120 with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
121 chat_message_refs =
122 chat
123 |> MessageReference.for_chat_query()
124 |> Pagination.fetch_paginated(params)
125
126 conn
127 |> add_link_headers(chat_message_refs)
128 |> put_view(MessageReferenceView)
129 |> render("index.json", chat_message_references: chat_message_refs)
130 end
131 end
132
133 def index(%{assigns: %{user: %{id: user_id} = user}} = conn, _params) do
134 blocked_ap_ids = User.blocked_users_ap_ids(user)
135
136 chats =
137 from(c in Chat,
138 where: c.user_id == ^user_id,
139 where: c.recipient not in ^blocked_ap_ids,
140 order_by: [desc: c.updated_at]
141 )
142 |> Repo.all()
143
144 conn
145 |> put_view(ChatView)
146 |> render("index.json", chats: chats)
147 end
148
149 def create(%{assigns: %{user: user}} = conn, %{id: id}) do
150 with %User{ap_id: recipient} <- User.get_cached_by_id(id),
151 {:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
152 conn
153 |> put_view(ChatView)
154 |> render("show.json", chat: chat)
155 end
156 end
157
158 def show(%{assigns: %{user: user}} = conn, %{id: id}) do
159 with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
160 conn
161 |> put_view(ChatView)
162 |> render("show.json", chat: chat)
163 end
164 end
165 end