Merge remote-tracking branch 'pleroma/develop' into feature/disable-account
[akkoma] / lib / pleroma / notification.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Notification do
6 use Ecto.Schema
7
8 alias Pleroma.Activity
9 alias Pleroma.Notification
10 alias Pleroma.Object
11 alias Pleroma.Pagination
12 alias Pleroma.Repo
13 alias Pleroma.User
14 alias Pleroma.Web.CommonAPI
15 alias Pleroma.Web.CommonAPI.Utils
16
17 import Ecto.Query
18 import Ecto.Changeset
19
20 schema "notifications" do
21 field(:seen, :boolean, default: false)
22 belongs_to(:user, User, type: Pleroma.FlakeId)
23 belongs_to(:activity, Activity, type: Pleroma.FlakeId)
24
25 timestamps()
26 end
27
28 def changeset(%Notification{} = notification, attrs) do
29 notification
30 |> cast(attrs, [:seen])
31 end
32
33 def for_user_query(user) do
34 Notification
35 |> where(user_id: ^user.id)
36 |> where(
37 [n, a],
38 fragment(
39 "? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')",
40 a.actor
41 )
42 )
43 |> join(:inner, [n], activity in assoc(n, :activity))
44 |> join(:left, [n, a], object in Object,
45 on:
46 fragment(
47 "(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
48 object.data,
49 a.data
50 )
51 )
52 |> preload([n, a, o], activity: {a, object: o})
53 end
54
55 def for_user(user, opts \\ %{}) do
56 user
57 |> for_user_query()
58 |> Pagination.fetch_paginated(opts)
59 end
60
61 def set_read_up_to(%{id: user_id} = _user, id) do
62 query =
63 from(
64 n in Notification,
65 where: n.user_id == ^user_id,
66 where: n.id <= ^id,
67 update: [
68 set: [seen: true]
69 ]
70 )
71
72 Repo.update_all(query, [])
73 end
74
75 def read_one(%User{} = user, notification_id) do
76 with {:ok, %Notification{} = notification} <- get(user, notification_id) do
77 notification
78 |> changeset(%{seen: true})
79 |> Repo.update()
80 end
81 end
82
83 def get(%{id: user_id} = _user, id) do
84 query =
85 from(
86 n in Notification,
87 where: n.id == ^id,
88 join: activity in assoc(n, :activity),
89 preload: [activity: activity]
90 )
91
92 notification = Repo.one(query)
93
94 case notification do
95 %{user_id: ^user_id} ->
96 {:ok, notification}
97
98 _ ->
99 {:error, "Cannot get notification"}
100 end
101 end
102
103 def clear(user) do
104 from(n in Notification, where: n.user_id == ^user.id)
105 |> Repo.delete_all()
106 end
107
108 def destroy_multiple(%{id: user_id} = _user, ids) do
109 from(n in Notification,
110 where: n.id in ^ids,
111 where: n.user_id == ^user_id
112 )
113 |> Repo.delete_all()
114 end
115
116 def dismiss(%{id: user_id} = _user, id) do
117 notification = Repo.get(Notification, id)
118
119 case notification do
120 %{user_id: ^user_id} ->
121 Repo.delete(notification)
122
123 _ ->
124 {:error, "Cannot dismiss notification"}
125 end
126 end
127
128 def create_notifications(%Activity{data: %{"to" => _, "type" => type}} = activity)
129 when type in ["Create", "Like", "Announce", "Follow"] do
130 users = get_notified_from_activity(activity)
131
132 notifications = Enum.map(users, fn user -> create_notification(activity, user) end)
133 {:ok, notifications}
134 end
135
136 def create_notifications(_), do: {:ok, []}
137
138 # TODO move to sql, too.
139 def create_notification(%Activity{} = activity, %User{} = user) do
140 unless skip?(activity, user) do
141 notification = %Notification{user_id: user.id, activity: activity}
142 {:ok, notification} = Repo.insert(notification)
143 Pleroma.Web.Streamer.stream("user", notification)
144 Pleroma.Web.Push.send(notification)
145 notification
146 end
147 end
148
149 def get_notified_from_activity(activity, local_only \\ true)
150
151 def get_notified_from_activity(
152 %Activity{data: %{"to" => _, "type" => type} = _data} = activity,
153 local_only
154 )
155 when type in ["Create", "Like", "Announce", "Follow"] do
156 recipients =
157 []
158 |> Utils.maybe_notify_to_recipients(activity)
159 |> Utils.maybe_notify_mentioned_recipients(activity)
160 |> Utils.maybe_notify_subscribers(activity)
161 |> Enum.uniq()
162
163 User.get_users_from_set(recipients, local_only)
164 end
165
166 def get_notified_from_activity(_, _local_only), do: []
167
168 def skip?(activity, user) do
169 [:self, :blocked, :local, :muted, :followers, :follows, :recently_followed]
170 |> Enum.any?(&skip?(&1, activity, user))
171 end
172
173 def skip?(:self, activity, user) do
174 activity.data["actor"] == user.ap_id
175 end
176
177 def skip?(:blocked, activity, user) do
178 actor = activity.data["actor"]
179 User.blocks?(user, %{ap_id: actor})
180 end
181
182 def skip?(:local, %{local: true}, %{info: %{notification_settings: %{"local" => false}}}),
183 do: true
184
185 def skip?(:local, %{local: false}, %{info: %{notification_settings: %{"remote" => false}}}),
186 do: true
187
188 def skip?(:muted, activity, user) do
189 actor = activity.data["actor"]
190
191 User.mutes?(user, %{ap_id: actor}) or CommonAPI.thread_muted?(user, activity)
192 end
193
194 def skip?(
195 :followers,
196 activity,
197 %{info: %{notification_settings: %{"followers" => false}}} = user
198 ) do
199 actor = activity.data["actor"]
200 follower = User.get_cached_by_ap_id(actor)
201 User.following?(follower, user)
202 end
203
204 def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do
205 actor = activity.data["actor"]
206 followed = User.get_by_ap_id(actor)
207 User.following?(user, followed)
208 end
209
210 def skip?(:recently_followed, %{data: %{"type" => "Follow"}} = activity, user) do
211 actor = activity.data["actor"]
212
213 Notification.for_user(user)
214 |> Enum.any?(fn
215 %{activity: %{data: %{"type" => "Follow", "actor" => ^actor}}} -> true
216 _ -> false
217 end)
218 end
219
220 def skip?(_, _, _), do: false
221 end