Merge branch 'develop' into command-available-check
[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 end
94 end
95
96 def mark_message_as_read(%{assigns: %{user: %{id: user_id}}} = conn, %{
97 id: chat_id,
98 message_id: message_id
99 }) do
100 with %MessageReference{} = cm_ref <-
101 MessageReference.get_by_id(message_id),
102 ^chat_id <- cm_ref.chat_id |> to_string(),
103 %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
104 {:ok, cm_ref} <- MessageReference.mark_as_read(cm_ref) do
105 conn
106 |> put_view(MessageReferenceView)
107 |> render("show.json", chat_message_reference: cm_ref)
108 end
109 end
110
111 def mark_as_read(
112 %{
113 body_params: %{last_read_id: last_read_id},
114 assigns: %{user: %{id: user_id}}
115 } = conn,
116 %{id: id}
117 ) do
118 with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id),
119 {_n, _} <-
120 MessageReference.set_all_seen_for_chat(chat, last_read_id) do
121 conn
122 |> put_view(ChatView)
123 |> render("show.json", chat: chat)
124 end
125 end
126
127 def messages(%{assigns: %{user: %{id: user_id}}} = conn, %{id: id} = params) do
128 with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id) do
129 cm_refs =
130 chat
131 |> MessageReference.for_chat_query()
132 |> Pagination.fetch_paginated(params)
133
134 conn
135 |> put_view(MessageReferenceView)
136 |> render("index.json", chat_message_references: cm_refs)
137 else
138 _ ->
139 conn
140 |> put_status(:not_found)
141 |> json(%{error: "not found"})
142 end
143 end
144
145 def index(%{assigns: %{user: %{id: user_id} = user}} = conn, _params) do
146 blocked_ap_ids = User.blocked_users_ap_ids(user)
147
148 chats =
149 from(c in Chat,
150 where: c.user_id == ^user_id,
151 where: c.recipient not in ^blocked_ap_ids,
152 order_by: [desc: c.updated_at]
153 )
154 |> Repo.all()
155
156 conn
157 |> put_view(ChatView)
158 |> render("index.json", chats: chats)
159 end
160
161 def create(%{assigns: %{user: user}} = conn, params) do
162 with %User{ap_id: recipient} <- User.get_by_id(params[:id]),
163 {:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
164 conn
165 |> put_view(ChatView)
166 |> render("show.json", chat: chat)
167 end
168 end
169
170 def show(%{assigns: %{user: user}} = conn, params) do
171 with %Chat{} = chat <- Repo.get_by(Chat, user_id: user.id, id: params[:id]) do
172 conn
173 |> put_view(ChatView)
174 |> render("show.json", chat: chat)
175 end
176 end
177 end