Merge branch 'refactor/locked_user_field' into 'develop'
[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.Repo
15 alias Pleroma.User
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
18 alias Pleroma.Web.PleromaAPI.ChatView
19 alias Pleroma.Web.Plugs.OAuthScopesPlug
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 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 conn
124 |> put_view(ChatView)
125 |> render("show.json", chat: chat)
126 end
127 end
128
129 def messages(%{assigns: %{user: user}} = conn, %{id: id} = params) do
130 with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
131 chat_message_refs =
132 chat
133 |> MessageReference.for_chat_query()
134 |> Pagination.fetch_paginated(params)
135
136 conn
137 |> add_link_headers(chat_message_refs)
138 |> put_view(MessageReferenceView)
139 |> render("index.json", chat_message_references: chat_message_refs)
140 end
141 end
142
143 def index(%{assigns: %{user: %{id: user_id} = user}} = conn, _params) do
144 blocked_ap_ids = User.blocked_users_ap_ids(user)
145
146 chats =
147 Chat.for_user_query(user_id)
148 |> where([c], c.recipient not in ^blocked_ap_ids)
149 |> Repo.all()
150
151 conn
152 |> put_view(ChatView)
153 |> render("index.json", chats: chats)
154 end
155
156 def create(%{assigns: %{user: user}} = conn, %{id: id}) do
157 with %User{ap_id: recipient} <- User.get_cached_by_id(id),
158 {:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
159 conn
160 |> put_view(ChatView)
161 |> render("show.json", chat: chat)
162 end
163 end
164
165 def show(%{assigns: %{user: user}} = conn, %{id: id}) do
166 with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
167 conn
168 |> put_view(ChatView)
169 |> render("show.json", chat: chat)
170 end
171 end
172 end