Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into feature/emojireactv...
[akkoma] / lib / pleroma / web / pleroma_api / controllers / pleroma_api_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
5 defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
6 use Pleroma.Web, :controller
7
8 import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, skip_relationships?: 1]
9
10 alias Pleroma.Activity
11 alias Pleroma.Conversation.Participation
12 alias Pleroma.Notification
13 alias Pleroma.Object
14 alias Pleroma.Plugs.OAuthScopesPlug
15 alias Pleroma.User
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.AccountView
19 alias Pleroma.Web.MastodonAPI.ConversationView
20 alias Pleroma.Web.MastodonAPI.NotificationView
21 alias Pleroma.Web.MastodonAPI.StatusView
22
23 plug(
24 OAuthScopesPlug,
25 %{scopes: ["read:statuses"]}
26 when action in [:conversation, :conversation_statuses]
27 )
28
29 plug(
30 OAuthScopesPlug,
31 %{scopes: ["read:statuses"], fallback: :proceed_unauthenticated}
32 when action == :emoji_reactions_by
33 )
34
35 plug(
36 OAuthScopesPlug,
37 %{scopes: ["write:statuses"]}
38 when action in [:react_with_emoji, :unreact_with_emoji]
39 )
40
41 plug(
42 OAuthScopesPlug,
43 %{scopes: ["write:conversations"]}
44 when action in [:update_conversation, :mark_conversations_as_read]
45 )
46
47 plug(
48 OAuthScopesPlug,
49 %{scopes: ["write:notifications"]} when action == :mark_notifications_as_read
50 )
51
52 def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id} = params) do
53 with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
54 %Object{data: %{"reactions" => emoji_reactions}} when is_list(emoji_reactions) <-
55 Object.normalize(activity) do
56 reactions =
57 emoji_reactions
58 |> Enum.map(fn [emoji, user_ap_ids] ->
59 if params["emoji"] && params["emoji"] != emoji do
60 nil
61 else
62 users =
63 Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1)
64 |> Enum.filter(fn
65 %{deactivated: false} -> true
66 _ -> false
67 end)
68
69 %{
70 name: emoji,
71 count: length(users),
72 accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}),
73 me: !!(user && user.ap_id in user_ap_ids)
74 }
75 end
76 end)
77 |> Enum.filter(& &1)
78
79 conn
80 |> json(reactions)
81 else
82 _e ->
83 conn
84 |> json([])
85 end
86 end
87
88 def react_with_emoji(%{assigns: %{user: user}} = conn, %{"id" => activity_id, "emoji" => emoji}) do
89 with {:ok, _activity} <- CommonAPI.react_with_emoji(activity_id, user, emoji),
90 activity <- Activity.get_by_id(activity_id) do
91 conn
92 |> put_view(StatusView)
93 |> render("show.json", %{activity: activity, for: user, as: :activity})
94 end
95 end
96
97 def unreact_with_emoji(%{assigns: %{user: user}} = conn, %{
98 "id" => activity_id,
99 "emoji" => emoji
100 }) do
101 with {:ok, _activity, _object} <- CommonAPI.unreact_with_emoji(activity_id, user, emoji),
102 activity <- Activity.get_by_id(activity_id) do
103 conn
104 |> put_view(StatusView)
105 |> render("show.json", %{activity: activity, for: user, as: :activity})
106 end
107 end
108
109 def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
110 with %Participation{} = participation <- Participation.get(participation_id),
111 true <- user.id == participation.user_id do
112 conn
113 |> put_view(ConversationView)
114 |> render("participation.json", %{participation: participation, for: user})
115 else
116 _error ->
117 conn
118 |> put_status(404)
119 |> json(%{"error" => "Unknown conversation id"})
120 end
121 end
122
123 def conversation_statuses(
124 %{assigns: %{user: %{id: user_id} = user}} = conn,
125 %{"id" => participation_id} = params
126 ) do
127 with %Participation{user_id: ^user_id} = participation <-
128 Participation.get(participation_id, preload: [:conversation]) do
129 params =
130 params
131 |> Map.put("blocking_user", user)
132 |> Map.put("muting_user", user)
133 |> Map.put("user", user)
134
135 activities =
136 participation.conversation.ap_id
137 |> ActivityPub.fetch_activities_for_context_query(params)
138 |> Pleroma.Pagination.fetch_paginated(Map.put(params, "total", false))
139 |> Enum.reverse()
140
141 conn
142 |> add_link_headers(activities)
143 |> put_view(StatusView)
144 |> render("index.json",
145 activities: activities,
146 for: user,
147 as: :activity,
148 skip_relationships: skip_relationships?(params)
149 )
150 else
151 _error ->
152 conn
153 |> put_status(404)
154 |> json(%{"error" => "Unknown conversation id"})
155 end
156 end
157
158 def update_conversation(
159 %{assigns: %{user: user}} = conn,
160 %{"id" => participation_id, "recipients" => recipients}
161 ) do
162 with %Participation{} = participation <- Participation.get(participation_id),
163 true <- user.id == participation.user_id,
164 {:ok, participation} <- Participation.set_recipients(participation, recipients) do
165 conn
166 |> put_view(ConversationView)
167 |> render("participation.json", %{participation: participation, for: user})
168 else
169 {:error, message} ->
170 conn
171 |> put_status(:bad_request)
172 |> json(%{"error" => message})
173
174 _error ->
175 conn
176 |> put_status(404)
177 |> json(%{"error" => "Unknown conversation id"})
178 end
179 end
180
181 def mark_conversations_as_read(%{assigns: %{user: user}} = conn, _params) do
182 with {:ok, _, participations} <- Participation.mark_all_as_read(user) do
183 conn
184 |> add_link_headers(participations)
185 |> put_view(ConversationView)
186 |> render("participations.json", participations: participations, for: user)
187 end
188 end
189
190 def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
191 with {:ok, notification} <- Notification.read_one(user, notification_id) do
192 conn
193 |> put_view(NotificationView)
194 |> render("show.json", %{notification: notification, for: user})
195 else
196 {:error, message} ->
197 conn
198 |> put_status(:bad_request)
199 |> json(%{"error" => message})
200 end
201 end
202
203 def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"max_id" => max_id} = params) do
204 with notifications <- Notification.set_read_up_to(user, max_id) do
205 notifications = Enum.take(notifications, 80)
206
207 conn
208 |> put_view(NotificationView)
209 |> render("index.json",
210 notifications: notifications,
211 for: user,
212 skip_relationships: skip_relationships?(params)
213 )
214 end
215 end
216 end