ChatMessageReference: Introduce and switch in chat controller.
[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.ChatMessageReference
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.ChatView
17 alias Pleroma.Web.PleromaAPI.ChatMessageReferenceView
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:statuses"]}
27 when action in [:post_chat_message, :create, :mark_as_read, :delete_message]
28 )
29
30 plug(
31 OAuthScopesPlug,
32 %{scopes: ["read:statuses"]} when action in [:messages, :index, :show]
33 )
34
35 plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
36
37 defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ChatOperation
38
39 def delete_message(%{assigns: %{user: %{id: user_id} = user}} = conn, %{
40 message_id: message_id,
41 id: chat_id
42 }) do
43 with %ChatMessageReference{} = cm_ref <-
44 ChatMessageReference.get_by_id(message_id),
45 ^chat_id <- cm_ref.chat_id |> to_string(),
46 %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
47 {:ok, _} <- remove_or_delete(cm_ref, user) do
48 conn
49 |> put_view(ChatMessageReferenceView)
50 |> render("show.json", chat_message_reference: cm_ref)
51 else
52 _e ->
53 {:error, :could_not_delete}
54 end
55 end
56
57 defp remove_or_delete(
58 %{object: %{data: %{"actor" => actor, "id" => id}}},
59 %{ap_id: actor} = user
60 ) do
61 with %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
62 CommonAPI.delete(activity.id, user)
63 end
64 end
65
66 defp remove_or_delete(cm_ref, _) do
67 cm_ref
68 |> ChatMessageReference.delete()
69 end
70
71 def post_chat_message(
72 %{body_params: params, assigns: %{user: %{id: user_id} = user}} = conn,
73 %{
74 id: id
75 }
76 ) do
77 with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id),
78 %User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
79 {:ok, activity} <-
80 CommonAPI.post_chat_message(user, recipient, params[:content],
81 media_id: params[:media_id]
82 ),
83 message <- Object.normalize(activity, false),
84 cm_ref <- ChatMessageReference.for_chat_and_object(chat, message) do
85 conn
86 |> put_view(ChatMessageReferenceView)
87 |> render("show.json", for: user, chat_message_reference: cm_ref)
88 end
89 end
90
91 def mark_as_read(%{assigns: %{user: %{id: user_id}}} = conn, %{id: id}) do
92 with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id),
93 {:ok, chat} <- Chat.mark_as_read(chat) do
94 conn
95 |> put_view(ChatView)
96 |> render("show.json", chat: chat)
97 end
98 end
99
100 def messages(%{assigns: %{user: %{id: user_id} = user}} = conn, %{id: id} = params) do
101 with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id) do
102 cm_refs =
103 chat
104 |> ChatMessageReference.for_chat_query()
105 |> Pagination.fetch_paginated(params |> stringify_keys())
106
107 conn
108 |> put_view(ChatMessageReferenceView)
109 |> render("index.json", for: user, chat_message_references: cm_refs)
110 else
111 _ ->
112 conn
113 |> put_status(:not_found)
114 |> json(%{error: "not found"})
115 end
116 end
117
118 def index(%{assigns: %{user: %{id: user_id} = user}} = conn, params) do
119 blocked_ap_ids = User.blocked_users_ap_ids(user)
120
121 chats =
122 from(c in Chat,
123 where: c.user_id == ^user_id,
124 where: c.recipient not in ^blocked_ap_ids,
125 order_by: [desc: c.updated_at]
126 )
127 |> Pagination.fetch_paginated(params |> stringify_keys)
128
129 conn
130 |> put_view(ChatView)
131 |> render("index.json", chats: chats)
132 end
133
134 def create(%{assigns: %{user: user}} = conn, params) do
135 with %User{ap_id: recipient} <- User.get_by_id(params[:id]),
136 {:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
137 conn
138 |> put_view(ChatView)
139 |> render("show.json", chat: chat)
140 end
141 end
142
143 def show(%{assigns: %{user: user}} = conn, params) do
144 with %Chat{} = chat <- Repo.get_by(Chat, user_id: user.id, id: params[:id]) do
145 conn
146 |> put_view(ChatView)
147 |> render("show.json", chat: chat)
148 end
149 end
150 end