notification: remove local/remote match rules (too complicated)
[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 [
170 :self,
171 :blocked,
172 :muted,
173 :followers,
174 :follows,
175 :non_followers,
176 :non_follows,
177 :recently_followed
178 ]
179 |> Enum.any?(&skip?(&1, activity, user))
180 end
181
182 def skip?(:self, activity, user) do
183 activity.data["actor"] == user.ap_id
184 end
185
186 def skip?(:blocked, activity, user) do
187 actor = activity.data["actor"]
188 User.blocks?(user, %{ap_id: actor})
189 end
190
191 def skip?(:muted, activity, user) do
192 actor = activity.data["actor"]
193
194 User.mutes?(user, %{ap_id: actor}) or CommonAPI.thread_muted?(user, activity)
195 end
196
197 def skip?(
198 :followers,
199 activity,
200 %{info: %{notification_settings: %{"followers" => false}}} = user
201 ) do
202 actor = activity.data["actor"]
203 follower = User.get_cached_by_ap_id(actor)
204 User.following?(follower, user)
205 end
206
207 def skip?(
208 :non_followers,
209 activity,
210 %{info: %{notification_settings: %{"non_followers" => false}}} = user
211 ) do
212 actor = activity.data["actor"]
213 follower = User.get_cached_by_ap_id(actor)
214 !User.following?(follower, user)
215 end
216
217 def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do
218 actor = activity.data["actor"]
219 followed = User.get_cached_by_ap_id(actor)
220 User.following?(user, followed)
221 end
222
223 def skip?(
224 :non_follows,
225 activity,
226 %{info: %{notification_settings: %{"non_follows" => false}}} = user
227 ) do
228 actor = activity.data["actor"]
229 followed = User.get_cached_by_ap_id(actor)
230 !User.following?(user, followed)
231 end
232
233 def skip?(:recently_followed, %{data: %{"type" => "Follow"}} = activity, user) do
234 actor = activity.data["actor"]
235
236 Notification.for_user(user)
237 |> Enum.any?(fn
238 %{activity: %{data: %{"type" => "Follow", "actor" => ^actor}}} -> true
239 _ -> false
240 end)
241 end
242
243 def skip?(_, _, _), do: false
244 end