Merge branch 'develop' into issue/2099
[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 alias Pleroma.Activity
8 alias Pleroma.Chat
9 alias Pleroma.Chat.MessageReference
10 alias Pleroma.Object
11 alias Pleroma.Pagination
12 alias Pleroma.Plugs.OAuthScopesPlug
13 alias Pleroma.Repo
14 alias Pleroma.User
15 alias Pleroma.Web.CommonAPI
16 alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
17 alias Pleroma.Web.PleromaAPI.ChatView
18
19 import Ecto.Query
20
21 action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
22
23 plug(
24 OAuthScopesPlug,
25 %{scopes: ["write:chats"]}
26 when action in [
27 :post_chat_message,
28 :create,
29 :mark_as_read,
30 :mark_message_as_read,
31 :delete_message
32 ]
33 )
34
35 plug(
36 OAuthScopesPlug,
37 %{scopes: ["read:chats"]} when action in [:messages, :index, :show]
38 )
39
40 plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
41
42 defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ChatOperation
43
44 def delete_message(%{assigns: %{user: %{id: user_id} = user}} = conn, %{
45 message_id: message_id,
46 id: chat_id
47 }) do
48 with %MessageReference{} = cm_ref <-
49 MessageReference.get_by_id(message_id),
50 ^chat_id <- cm_ref.chat_id |> to_string(),
51 %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
52 {:ok, _} <- remove_or_delete(cm_ref, user) do
53 conn
54 |> put_view(MessageReferenceView)
55 |> render("show.json", chat_message_reference: cm_ref)
56 else
57 _e ->
58 {:error, :could_not_delete}
59 end
60 end
61
62 defp remove_or_delete(
63 %{object: %{data: %{"actor" => actor, "id" => id}}},
64 %{ap_id: actor} = user
65 ) do
66 with %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
67 CommonAPI.delete(activity.id, user)
68 end
69 end
70
71 defp remove_or_delete(cm_ref, _) do
72 cm_ref
73 |> MessageReference.delete()
74 end
75
76 def post_chat_message(
77 %{body_params: params, assigns: %{user: %{id: user_id} = user}} = conn,
78 %{
79 id: id
80 }
81 ) do
82 with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id),
83 %User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
84 {:ok, activity} <-
85 CommonAPI.post_chat_message(user, recipient, params[:content],
86 media_id: params[:media_id]
87 ),
88 message <- Object.normalize(activity, false),
89 cm_ref <- MessageReference.for_chat_and_object(chat, message) do
90 conn
91 |> put_view(MessageReferenceView)
92 |> render("show.json", chat_message_reference: cm_ref)
93 else
94 {:reject, message} ->
95 conn
96 |> put_status(:unprocessable_entity)
97 |> json(%{error: message})
98
99 {:error, message} ->
100 conn
101 |> put_status(:bad_request)
102 |> json(%{error: message})
103 end
104 end
105
106 def mark_message_as_read(%{assigns: %{user: %{id: user_id}}} = conn, %{
107 id: chat_id,
108 message_id: message_id
109 }) do
110 with %MessageReference{} = cm_ref <-
111 MessageReference.get_by_id(message_id),
112 ^chat_id <- cm_ref.chat_id |> to_string(),
113 %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
114 {:ok, cm_ref} <- MessageReference.mark_as_read(cm_ref) do
115 conn
116 |> put_view(MessageReferenceView)
117 |> render("show.json", chat_message_reference: cm_ref)
118 end
119 end
120
121 def mark_as_read(
122 %{
123 body_params: %{last_read_id: last_read_id},
124 assigns: %{user: %{id: user_id}}
125 } = conn,
126 %{id: id}
127 ) do
128 with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id),
129 {_n, _} <-
130 MessageReference.set_all_seen_for_chat(chat, last_read_id) do
131 conn
132 |> put_view(ChatView)
133 |> render("show.json", chat: chat)
134 end
135 end
136
137 def messages(%{assigns: %{user: %{id: user_id}}} = conn, %{id: id} = params) do
138 with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id) do
139 cm_refs =
140 chat
141 |> MessageReference.for_chat_query()
142 |> Pagination.fetch_paginated(params)
143
144 conn
145 |> put_view(MessageReferenceView)
146 |> render("index.json", chat_message_references: cm_refs)
147 else
148 _ ->
149 conn
150 |> put_status(:not_found)
151 |> json(%{error: "not found"})
152 end
153 end
154
155 def index(%{assigns: %{user: %{id: user_id} = user}} = conn, _params) do
156 blocked_ap_ids = User.blocked_users_ap_ids(user)
157
158 chats =
159 Chat.for_user_query(user_id)
160 |> where([c], c.recipient not in ^blocked_ap_ids)
161 |> Repo.all()
162
163 conn
164 |> put_view(ChatView)
165 |> render("index.json", chats: chats)
166 end
167
168 def create(%{assigns: %{user: user}} = conn, params) do
169 with %User{ap_id: recipient} <- User.get_by_id(params[:id]),
170 {:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
171 conn
172 |> put_view(ChatView)
173 |> render("show.json", chat: chat)
174 end
175 end
176
177 def show(%{assigns: %{user: user}} = conn, params) do
178 with %Chat{} = chat <- Repo.get_by(Chat, user_id: user.id, id: params[:id]) do
179 conn
180 |> put_view(ChatView)
181 |> render("show.json", chat: chat)
182 end
183 end
184 end