b9949236c5a5074369da4647048b856b72768332
[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 import Pleroma.Web.ActivityPub.ObjectValidator, only: [stringify_keys: 1]
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, :show]
39 )
40
41 plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
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 <- cm_ref.chat_id |> to_string(),
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
73 cm_ref
74 |> MessageReference.delete()
75 end
76
77 def post_chat_message(
78 %{body_params: params, assigns: %{user: %{id: user_id} = user}} = conn,
79 %{
80 id: id
81 }
82 ) do
83 with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id),
84 %User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
85 {:ok, activity} <-
86 CommonAPI.post_chat_message(user, recipient, params[:content],
87 media_id: params[:media_id]
88 ),
89 message <- Object.normalize(activity, false),
90 cm_ref <- MessageReference.for_chat_and_object(chat, message) do
91 conn
92 |> put_view(MessageReferenceView)
93 |> render("show.json", for: user, chat_message_reference: cm_ref)
94 end
95 end
96
97 def mark_message_as_read(%{assigns: %{user: %{id: user_id} = user}} = conn, %{
98 id: chat_id,
99 message_id: message_id
100 }) do
101 with %MessageReference{} = cm_ref <-
102 MessageReference.get_by_id(message_id),
103 ^chat_id <- cm_ref.chat_id |> to_string(),
104 %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
105 {:ok, cm_ref} <- MessageReference.mark_as_read(cm_ref) do
106 conn
107 |> put_view(MessageReferenceView)
108 |> render("show.json", for: user, chat_message_reference: cm_ref)
109 end
110 end
111
112 def mark_as_read(
113 %{body_params: %{last_read_id: last_read_id}, assigns: %{user: %{id: user_id}}} = conn,
114 %{id: id}
115 ) do
116 with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id),
117 {_n, _} <-
118 MessageReference.set_all_seen_for_chat(chat, last_read_id) do
119 conn
120 |> put_view(ChatView)
121 |> render("show.json", chat: chat)
122 end
123 end
124
125 def messages(%{assigns: %{user: %{id: user_id} = user}} = conn, %{id: id} = params) do
126 with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id) do
127 cm_refs =
128 chat
129 |> MessageReference.for_chat_query()
130 |> Pagination.fetch_paginated(params |> stringify_keys())
131
132 conn
133 |> put_view(MessageReferenceView)
134 |> render("index.json", for: user, chat_message_references: cm_refs)
135 else
136 _ ->
137 conn
138 |> put_status(:not_found)
139 |> json(%{error: "not found"})
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 from(c in Chat,
148 where: c.user_id == ^user_id,
149 where: c.recipient not in ^blocked_ap_ids,
150 order_by: [desc: c.updated_at]
151 )
152 |> Pagination.fetch_paginated(params |> stringify_keys)
153
154 conn
155 |> put_view(ChatView)
156 |> render("index.json", chats: chats)
157 end
158
159 def create(%{assigns: %{user: user}} = conn, params) do
160 with %User{ap_id: recipient} <- User.get_by_id(params[:id]),
161 {:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
162 conn
163 |> put_view(ChatView)
164 |> render("show.json", chat: chat)
165 end
166 end
167
168 def show(%{assigns: %{user: user}} = conn, params) do
169 with %Chat{} = chat <- Repo.get_by(Chat, user_id: user.id, id: params[:id]) do
170 conn
171 |> put_view(ChatView)
172 |> render("show.json", chat: chat)
173 end
174 end
175 end