Merge remote-tracking branch 'remotes/origin/develop' into ostatus-controller-no...
[akkoma] / lib / pleroma / filter.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.Filter do
6 use Ecto.Schema
7
8 import Ecto.Changeset
9 import Ecto.Query
10
11 alias Pleroma.Repo
12 alias Pleroma.User
13
14 schema "filters" do
15 belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
16 field(:filter_id, :integer)
17 field(:hide, :boolean, default: false)
18 field(:whole_word, :boolean, default: true)
19 field(:phrase, :string)
20 field(:context, {:array, :string})
21 field(:expires_at, :utc_datetime)
22
23 timestamps()
24 end
25
26 def get(id, %{id: user_id} = _user) do
27 query =
28 from(
29 f in Pleroma.Filter,
30 where: f.filter_id == ^id,
31 where: f.user_id == ^user_id
32 )
33
34 Repo.one(query)
35 end
36
37 def get_active(query) do
38 from(f in query, where: is_nil(f.expires_at) or f.expires_at > ^NaiveDateTime.utc_now())
39 end
40
41 def get_irreversible(query) do
42 from(f in query, where: f.hide)
43 end
44
45 def get_filters(query \\ __MODULE__, %User{id: user_id}) do
46 query =
47 from(
48 f in query,
49 where: f.user_id == ^user_id,
50 order_by: [desc: :id]
51 )
52
53 Repo.all(query)
54 end
55
56 def create(%Pleroma.Filter{user_id: user_id, filter_id: nil} = filter) do
57 # If filter_id wasn't given, use the max filter_id for this user plus 1.
58 # XXX This could result in a race condition if a user tries to add two
59 # different filters for their account from two different clients at the
60 # same time, but that should be unlikely.
61
62 max_id_query =
63 from(
64 f in Pleroma.Filter,
65 where: f.user_id == ^user_id,
66 select: max(f.filter_id)
67 )
68
69 filter_id =
70 case Repo.one(max_id_query) do
71 # Start allocating from 1
72 nil ->
73 1
74
75 max_id ->
76 max_id + 1
77 end
78
79 filter
80 |> Map.put(:filter_id, filter_id)
81 |> Repo.insert()
82 end
83
84 def create(%Pleroma.Filter{} = filter) do
85 Repo.insert(filter)
86 end
87
88 def delete(%Pleroma.Filter{id: filter_key} = filter) when is_number(filter_key) do
89 Repo.delete(filter)
90 end
91
92 def delete(%Pleroma.Filter{id: filter_key} = filter) when is_nil(filter_key) do
93 %Pleroma.Filter{id: id} = get(filter.filter_id, %{id: filter.user_id})
94
95 filter
96 |> Map.put(:id, id)
97 |> Repo.delete()
98 end
99
100 def update(%Pleroma.Filter{} = filter, params) do
101 filter
102 |> cast(params, [:phrase, :context, :hide, :expires_at, :whole_word])
103 |> validate_required([:phrase, :context])
104 |> Repo.update()
105 end
106
107 def compose_regex(user_or_filters, format \\ :postgres)
108
109 def compose_regex(%User{} = user, format) do
110 __MODULE__
111 |> get_active()
112 |> get_irreversible()
113 |> get_filters(user)
114 |> compose_regex(format)
115 end
116
117 def compose_regex([_ | _] = filters, format) do
118 phrases =
119 filters
120 |> Enum.map(& &1.phrase)
121 |> Enum.join("|")
122
123 case format do
124 :postgres ->
125 "\\y(#{phrases})\\y"
126
127 :re ->
128 ~r/\b#{phrases}\b/i
129
130 _ ->
131 nil
132 end
133 end
134
135 def compose_regex(_, _), do: nil
136 end