notification.ex 4.7 KB
Newer Older
1
# Pleroma: A lightweight social networking server
kaniini's avatar
kaniini committed
2
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3
4
# SPDX-License-Identifier: AGPL-3.0-only

5
6
defmodule Pleroma.Notification do
  use Ecto.Schema
7
  alias Pleroma.{User, Activity, Notification, Repo, Object}
8
9
10
  import Ecto.Query

  schema "notifications" do
lain's avatar
lain committed
11
12
13
    field(:seen, :boolean, default: false)
    belongs_to(:user, Pleroma.User)
    belongs_to(:activity, Pleroma.Activity)
14
15
16
17

    timestamps()
  end

lain's avatar
lain committed
18
19
  # TODO: Make generic and unify (see activity_pub.ex)
  defp restrict_max(query, %{"max_id" => max_id}) do
lain's avatar
lain committed
20
    from(activity in query, where: activity.id < ^max_id)
lain's avatar
lain committed
21
  end
lain's avatar
lain committed
22

lain's avatar
lain committed
23
24
25
  defp restrict_max(query, _), do: query

  defp restrict_since(query, %{"since_id" => since_id}) do
lain's avatar
lain committed
26
    from(activity in query, where: activity.id > ^since_id)
lain's avatar
lain committed
27
  end
lain's avatar
lain committed
28

lain's avatar
lain committed
29
30
  defp restrict_since(query, _), do: query

31
  def for_user(user, opts \\ %{}) do
lain's avatar
lain committed
32
33
34
35
36
37
38
39
40
41
42
43
44
    query =
      from(
        n in Notification,
        where: n.user_id == ^user.id,
        order_by: [desc: n.id],
        preload: [:activity],
        limit: 20
      )

    query =
      query
      |> restrict_since(opts)
      |> restrict_max(opts)
lain's avatar
lain committed
45

46
47
48
    Repo.all(query)
  end

49
50
51
52
53
54
55
56
57
58
59
60
61
62
  def set_read_up_to(%{id: user_id} = _user, id) do
    query =
      from(
        n in Notification,
        where: n.user_id == ^user_id,
        where: n.id <= ^id,
        update: [
          set: [seen: true]
        ]
      )

    Repo.update_all(query, [])
  end

63
  def get(%{id: user_id} = _user, id) do
lain's avatar
lain committed
64
65
66
67
68
69
    query =
      from(
        n in Notification,
        where: n.id == ^id,
        preload: [:activity]
      )
70
71

    notification = Repo.one(query)
lain's avatar
lain committed
72

73
74
75
    case notification do
      %{user_id: ^user_id} ->
        {:ok, notification}
lain's avatar
lain committed
76

77
78
79
80
81
82
      _ ->
        {:error, "Cannot get notification"}
    end
  end

  def clear(user) do
Maxim Filippov's avatar
Maxim Filippov committed
83
84
    from(n in Notification, where: n.user_id == ^user.id)
    |> Repo.delete_all()
85
86
87
88
  end

  def dismiss(%{id: user_id} = _user, id) do
    notification = Repo.get(Notification, id)
lain's avatar
lain committed
89

90
91
92
    case notification do
      %{user_id: ^user_id} ->
        Repo.delete(notification)
lain's avatar
lain committed
93

94
95
96
97
98
      _ ->
        {:error, "Cannot dismiss notification"}
    end
  end

lain's avatar
lain committed
99
100
  def create_notifications(%Activity{id: _, data: %{"to" => _, "type" => type}} = activity)
      when type in ["Create", "Like", "Announce", "Follow"] do
101
    users = get_notified_from_activity(activity)
102

lain's avatar
lain committed
103
    notifications = Enum.map(users, fn user -> create_notification(activity, user) end)
104
105
    {:ok, notifications}
  end
lain's avatar
lain committed
106

107
108
109
110
  def create_notifications(_), do: {:ok, []}

  # TODO move to sql, too.
  def create_notification(%Activity{} = activity, %User{} = user) do
111
    unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or
112
113
114
115
116
117
             user.ap_id == activity.data["actor"] or
             (activity.data["type"] == "Follow" and
                Enum.any?(Notification.for_user(user), fn notif ->
                  notif.activity.data["type"] == "Follow" and
                    notif.activity.data["actor"] == activity.data["actor"]
                end)) do
118
      notification = %Notification{user_id: user.id, activity: activity}
119
      {:ok, notification} = Repo.insert(notification)
120
      Pleroma.Web.Streamer.stream("user", notification)
minibikini's avatar
minibikini committed
121
      Pleroma.Web.Push.send(notification)
122
123
      notification
    end
124
  end
125

126
127
  def get_notified_from_activity(activity, local_only \\ true)

128
  def get_notified_from_activity(
Maksim's avatar
Maksim committed
129
        %Activity{data: %{"to" => _, "type" => type} = _data} = activity,
130
        local_only
131
132
133
134
135
136
137
138
139
140
141
      )
      when type in ["Create", "Like", "Announce", "Follow"] do
    recipients =
      []
      |> maybe_notify_to_recipients(activity)
      |> maybe_notify_mentioned_recipients(activity)
      |> Enum.uniq()

    User.get_users_from_set(recipients, local_only)
  end

Maksim's avatar
Maksim committed
142
  def get_notified_from_activity(_, _local_only), do: []
143

144
145
  defp maybe_notify_to_recipients(
         recipients,
Maksim's avatar
Maksim committed
146
         %Activity{data: %{"to" => to, "type" => _type}} = _activity
147
148
149
150
151
152
       ) do
    recipients ++ to
  end

  defp maybe_notify_mentioned_recipients(
         recipients,
Maksim's avatar
Maksim committed
153
         %Activity{data: %{"to" => _to, "type" => type} = data} = _activity
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
       )
       when type == "Create" do
    object = Object.normalize(data["object"])

    object_data =
      cond do
        !is_nil(object) ->
          object.data

        is_map(data["object"]) ->
          data["object"]

        true ->
          %{}
      end

    tagged_mentions = maybe_extract_mentions(object_data)

    recipients ++ tagged_mentions
  end

  defp maybe_notify_mentioned_recipients(recipients, _), do: recipients

  defp maybe_extract_mentions(%{"tag" => tag}) do
    tag
    |> Enum.filter(fn x -> is_map(x) end)
    |> Enum.filter(fn x -> x["type"] == "Mention" end)
    |> Enum.map(fn x -> x["href"] end)
  end

  defp maybe_extract_mentions(_), do: []
185
end