common_api.ex 18.8 KB
Newer Older
1
# Pleroma: A lightweight social networking server
2
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3
4
# SPDX-License-Identifier: AGPL-3.0-only

lain's avatar
lain committed
5
defmodule Pleroma.Web.CommonAPI do
Haelwenn's avatar
Haelwenn committed
6
  alias Pleroma.Activity
7
  alias Pleroma.Conversation.Participation
lain's avatar
lain committed
8
  alias Pleroma.Formatter
Haelwenn's avatar
Haelwenn committed
9
  alias Pleroma.Object
10
  alias Pleroma.ThreadMute
11
  alias Pleroma.User
12
  alias Pleroma.UserRelationship
lain's avatar
lain committed
13
  alias Pleroma.Web.ActivityPub.ActivityPub
14
  alias Pleroma.Web.ActivityPub.Builder
15
  alias Pleroma.Web.ActivityPub.Pipeline
16
  alias Pleroma.Web.ActivityPub.Utils
17
  alias Pleroma.Web.ActivityPub.Visibility
18

19
  import Pleroma.Web.Gettext
20
  import Pleroma.Web.CommonAPI.Utils
lain's avatar
lain committed
21

22
  require Pleroma.Constants
23
  require Logger
24

25
26
27
28
29
30
31
  def block(blocker, blocked) do
    with {:ok, block_data, _} <- Builder.block(blocker, blocked),
         {:ok, block, _} <- Pipeline.common_pipeline(block_data, local: true) do
      {:ok, block}
    end
  end

lain's avatar
lain committed
32
  def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do
33
34
    with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]),
         :ok <- validate_chat_content_length(content, !!maybe_attachment),
35
36
37
38
39
         {_, {:ok, chat_message_data, _meta}} <-
           {:build_object,
            Builder.chat_message(
              user,
              recipient.ap_id,
40
              content |> format_chat_content,
lain's avatar
lain committed
41
              attachment: maybe_attachment
42
43
44
45
46
47
48
49
50
            )},
         {_, {:ok, create_activity_data, _meta}} <-
           {:build_create_activity, Builder.create(user, chat_message_data, [recipient.ap_id])},
         {_, {:ok, %Activity{} = activity, _meta}} <-
           {:common_pipeline,
            Pipeline.common_pipeline(create_activity_data,
              local: true
            )} do
      {:ok, activity}
Haelwenn's avatar
Haelwenn committed
51
52
53
    else
      {:common_pipeline, {:reject, _} = e} -> e
      e -> e
lain's avatar
lain committed
54
55
56
    end
  end

57
58
59
  defp format_chat_content(nil), do: nil

  defp format_chat_content(content) do
lain's avatar
lain committed
60
61
62
63
    {text, _, _} =
      content
      |> Formatter.html_escape("text/plain")
      |> Formatter.linkify()
64
65
66
      |> (fn {text, mentions, tags} ->
            {String.replace(text, ~r/\r?\n/, "<br>"), mentions, tags}
          end).()
lain's avatar
lain committed
67
68

    text
69
70
71
72
73
74
  end

  defp validate_chat_content_length(_, true), do: :ok
  defp validate_chat_content_length(nil, false), do: {:error, :no_content}

  defp validate_chat_content_length(content, _) do
lain's avatar
lain committed
75
76
    if String.length(content) <= Pleroma.Config.get([:instance, :chat_limit]) do
      :ok
77
    else
lain's avatar
lain committed
78
      {:error, :content_too_long}
lain's avatar
lain committed
79
80
81
    end
  end

82
  def unblock(blocker, blocked) do
83
    with {_, %Activity{} = block} <- {:fetch_block, Utils.fetch_latest_block(blocker, blocked)},
84
85
86
         {:ok, unblock_data, _} <- Builder.undo(blocker, block),
         {:ok, unblock, _} <- Pipeline.common_pipeline(unblock_data, local: true) do
      {:ok, unblock}
87
88
89
90
91
92
93
94
95
96
97
    else
      {:fetch_block, nil} ->
        if User.blocks?(blocker, blocked) do
          User.unblock(blocker, blocked)
          {:ok, :no_activity}
        else
          {:error, :not_blocking}
        end

      e ->
        e
98
99
100
    end
  end

101
  def follow(follower, followed) do
minibikini's avatar
minibikini committed
102
103
    timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout])

104
105
    with {:ok, follow_data, _} <- Builder.follow(follower, followed),
         {:ok, activity, _} <- Pipeline.common_pipeline(follow_data, local: true),
minibikini's avatar
minibikini committed
106
         {:ok, follower, followed} <- User.wait_and_refresh(timeout, follower, followed) do
107
108
109
110
111
      if activity.data["state"] == "reject" do
        {:error, :rejected}
      else
        {:ok, follower, followed, activity}
      end
112
113
114
    end
  end

115
116
  def unfollow(follower, unfollowed) do
    with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed),
117
         {:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed),
118
         {:ok, _subscription} <- User.unsubscribe(follower, unfollowed) do
119
120
121
122
123
      {:ok, follower}
    end
  end

  def accept_follow_request(follower, followed) do
124
    with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
lain's avatar
lain committed
125
126
         {:ok, accept_data, _} <- Builder.accept(followed, follow_activity),
         {:ok, _activity, _} <- Pipeline.common_pipeline(accept_data, local: true) do
127
128
129
130
131
132
      {:ok, follower}
    end
  end

  def reject_follow_request(follower, followed) do
    with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
133
134
         {:ok, reject_data, _} <- Builder.reject(followed, follow_activity),
         {:ok, _activity, _} <- Pipeline.common_pipeline(reject_data, local: true) do
135
136
137
138
      {:ok, follower}
    end
  end

lain's avatar
lain committed
139
  def delete(activity_id, user) do
140
141
142
143
    with {_, %Activity{data: %{"object" => _, "type" => "Create"}} = activity} <-
           {:find_activity, Activity.get_by_id(activity_id)},
         {_, %Object{} = object, _} <-
           {:find_object, Object.normalize(activity, false), activity},
144
         true <- User.superuser?(user) || user.ap_id == object.data["actor"],
145
146
         {:ok, delete_data, _} <- Builder.delete(user, object.data["id"]),
         {:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do
lain's avatar
lain committed
147
      {:ok, delete}
Sergey Suprunenko's avatar
Sergey Suprunenko committed
148
    else
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
      {:find_activity, _} ->
        {:error, :not_found}

      {:find_object, nil, %Activity{data: %{"actor" => actor, "object" => object}}} ->
        # We have the create activity, but not the object, it was probably pruned.
        # Insert a tombstone and try again
        with {:ok, tombstone_data, _} <- Builder.tombstone(actor, object),
             {:ok, _tombstone} <- Object.create(tombstone_data) do
          delete(activity_id, user)
        else
          _ ->
            Logger.error(
              "Could not insert tombstone for missing object on deletion. Object is #{object}."
            )

            {:error, dgettext("errors", "Could not delete")}
        end

      _ ->
        {:error, dgettext("errors", "Could not delete")}
lain's avatar
lain committed
169
170
    end
  end
lain's avatar
lain committed
171

172
  def repeat(id, user, params \\ %{}) do
173
174
175
176
177
178
179
    with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
         object = %Object{} <- Object.normalize(activity, false),
         {_, nil} <- {:existing_announce, Utils.get_existing_announce(user.ap_id, object)},
         public = public_announce?(object, params),
         {:ok, announce, _} <- Builder.announce(user, object, public: public),
         {:ok, activity, _} <- Pipeline.common_pipeline(announce, local: true) do
      {:ok, activity}
lain's avatar
lain committed
180
    else
181
182
183
184
185
      {:existing_announce, %Activity{} = announce} ->
        {:ok, announce}

      _ ->
        {:error, :not_found}
lain's avatar
lain committed
186
187
188
    end
  end

189
190
  def unrepeat(id, user) do
    with {_, %Activity{data: %{"type" => "Create"}} = activity} <-
191
192
193
194
195
196
           {:find_activity, Activity.get_by_id(id)},
         %Object{} = note <- Object.normalize(activity, false),
         %Activity{} = announce <- Utils.get_existing_announce(user.ap_id, note),
         {:ok, undo, _} <- Builder.undo(user, announce),
         {:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do
      {:ok, activity}
normandy's avatar
normandy committed
197
    else
198
      {:find_activity, _} -> {:error, :not_found}
minibikini's avatar
minibikini committed
199
      _ -> {:error, dgettext("errors", "Could not unrepeat")}
normandy's avatar
normandy committed
200
201
202
    end
  end

lain's avatar
lain committed
203
  @spec favorite(User.t(), binary()) :: {:ok, Activity.t() | :already_liked} | {:error, any()}
204
  def favorite(%User{} = user, id) do
lain's avatar
lain committed
205
206
207
208
209
210
211
212
213
214
215
216
217
218
    case favorite_helper(user, id) do
      {:ok, _} = res ->
        res

      {:error, :not_found} = res ->
        res

      {:error, e} ->
        Logger.error("Could not favorite #{id}. Error: #{inspect(e, pretty: true)}")
        {:error, dgettext("errors", "Could not favorite")}
    end
  end

  def favorite_helper(user, id) do
219
220
221
222
    with {_, %Activity{object: object}} <- {:find_object, Activity.get_by_id_with_object(id)},
         {_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)},
         {_, {:ok, %Activity{} = activity, _meta}} <-
           {:common_pipeline,
223
            Pipeline.common_pipeline(like_object, Keyword.put(meta, :local, true))} do
224
      {:ok, activity}
lain's avatar
lain committed
225
    else
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
      {:find_object, _} ->
        {:error, :not_found}

      {:common_pipeline,
       {
         :error,
         {
           :validate_object,
           {
             :error,
             changeset
           }
         }
       }} = e ->
        if {:object, {"already liked by this actor", []}} in changeset.errors do
          {:ok, :already_liked}
        else
lain's avatar
lain committed
243
          {:error, e}
244
245
        end

246
      e ->
lain's avatar
lain committed
247
        {:error, e}
lain's avatar
lain committed
248
249
250
    end
  end

251
252
  def unfavorite(id, user) do
    with {_, %Activity{data: %{"type" => "Create"}} = activity} <-
253
254
255
256
           {:find_activity, Activity.get_by_id(id)},
         %Object{} = note <- Object.normalize(activity, false),
         %Activity{} = like <- Utils.get_existing_like(user.ap_id, note),
         {:ok, undo, _} <- Builder.undo(user, like),
257
         {:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do
258
      {:ok, activity}
lain's avatar
lain committed
259
    else
260
      {:find_activity, _} -> {:error, :not_found}
minibikini's avatar
minibikini committed
261
      _ -> {:error, dgettext("errors", "Could not unfavorite")}
lain's avatar
lain committed
262
263
264
    end
  end

lain's avatar
lain committed
265
266
  def react_with_emoji(id, user, emoji) do
    with %Activity{} = activity <- Activity.get_by_id(id),
lain's avatar
lain committed
267
268
269
270
         object <- Object.normalize(activity),
         {:ok, emoji_react, _} <- Builder.emoji_react(user, object, emoji),
         {:ok, activity, _} <- Pipeline.common_pipeline(emoji_react, local: true) do
      {:ok, activity}
lain's avatar
lain committed
271
272
273
274
275
276
    else
      _ ->
        {:error, dgettext("errors", "Could not add reaction emoji")}
    end
  end

lain's avatar
lain committed
277
  def unreact_with_emoji(id, user, emoji) do
278
279
280
281
    with %Activity{} = reaction_activity <- Utils.get_latest_reaction(id, user, emoji),
         {:ok, undo, _} <- Builder.undo(user, reaction_activity),
         {:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do
      {:ok, activity}
lain's avatar
lain committed
282
283
284
285
286
287
    else
      _ ->
        {:error, dgettext("errors", "Could not remove reaction emoji")}
    end
  end

minibikini's avatar
minibikini committed
288
289
290
291
  def vote(user, %{data: %{"type" => "Question"}} = object, choices) do
    with :ok <- validate_not_author(object, user),
         :ok <- validate_existing_votes(user, object),
         {:ok, options, choices} <- normalize_and_validate_choices(choices, object) do
rinpatch's avatar
rinpatch committed
292
293
      answer_activities =
        Enum.map(choices, fn index ->
294
295
296
297
298
299
300
301
302
303
304
305
306
          {:ok, answer_object, _meta} =
            Builder.answer(user, object, Enum.at(options, index)["name"])

          {:ok, activity_data, _meta} = Builder.create(user, answer_object, [])

          {:ok, activity, _meta} =
            activity_data
            |> Map.put("cc", answer_object["cc"])
            |> Map.put("context", answer_object["context"])
            |> Pipeline.common_pipeline(local: true)

          # TODO: Do preload of Pleroma.Object in Pipeline
          Activity.normalize(activity.data)
rinpatch's avatar
rinpatch committed
307
308
        end)

309
      object = Object.get_cached_by_ap_id(object.data["id"])
rinpatch's avatar
rinpatch committed
310
311
312
313
      {:ok, answer_activities, object}
    end
  end

minibikini's avatar
minibikini committed
314
315
316
317
318
319
320
321
  defp validate_not_author(%{data: %{"actor" => ap_id}}, %{ap_id: ap_id}),
    do: {:error, dgettext("errors", "Poll's author can't vote")}

  defp validate_not_author(_, _), do: :ok

  defp validate_existing_votes(%{ap_id: ap_id}, object) do
    if Utils.get_existing_votes(ap_id, object) == [] do
      :ok
rinpatch's avatar
rinpatch committed
322
    else
minibikini's avatar
minibikini committed
323
      {:error, dgettext("errors", "Already voted")}
rinpatch's avatar
rinpatch committed
324
325
326
    end
  end

Haelwenn's avatar
Haelwenn committed
327
328
329
330
331
332
333
  defp get_options_and_max_count(%{data: %{"anyOf" => any_of}})
       when is_list(any_of) and any_of != [],
       do: {any_of, Enum.count(any_of)}

  defp get_options_and_max_count(%{data: %{"oneOf" => one_of}})
       when is_list(one_of) and one_of != [],
       do: {one_of, 1}
rinpatch's avatar
rinpatch committed
334

minibikini's avatar
minibikini committed
335
336
337
338
339
340
341
342
343
344
345
346
  defp normalize_and_validate_choices(choices, object) do
    choices = Enum.map(choices, fn i -> if is_binary(i), do: String.to_integer(i), else: i end)
    {options, max_count} = get_options_and_max_count(object)
    count = Enum.count(options)

    with {_, true} <- {:valid_choice, Enum.all?(choices, &(&1 < count))},
         {_, true} <- {:count_check, Enum.count(choices) <= max_count} do
      {:ok, options, choices}
    else
      {:valid_choice, _} -> {:error, dgettext("errors", "Invalid indices")}
      {:count_check, _} -> {:error, dgettext("errors", "Too many choices")}
    end
347
348
  end

349
  def public_announce?(_, %{visibility: visibility})
350
351
352
      when visibility in ~w{public unlisted private direct},
      do: visibility in ~w(public unlisted)

353
  def public_announce?(object, _) do
354
355
356
    Visibility.is_public?(object)
  end

minibikini's avatar
minibikini committed
357
  def get_visibility(_, _, %Participation{}), do: {"direct", "direct"}
358

359
  def get_visibility(%{visibility: visibility}, in_reply_to, _)
lain's avatar
lain committed
360
      when visibility in ~w{public unlisted private direct},
lain's avatar
lain committed
361
      do: {visibility, get_replied_to_visibility(in_reply_to)}
lain's avatar
lain committed
362

363
  def get_visibility(%{visibility: "list:" <> list_id}, in_reply_to, _) do
364
365
    visibility = {:list, String.to_integer(list_id)}
    {visibility, get_replied_to_visibility(in_reply_to)}
minibikini's avatar
minibikini committed
366
367
  end

368
  def get_visibility(_, in_reply_to, _) when not is_nil(in_reply_to) do
lain's avatar
lain committed
369
370
    visibility = get_replied_to_visibility(in_reply_to)
    {visibility, visibility}
371
  end
lain's avatar
lain committed
372

373
  def get_visibility(_, in_reply_to, _), do: {"public", get_replied_to_visibility(in_reply_to)}
374

375
  def get_replied_to_visibility(nil), do: nil
376

377
378
  def get_replied_to_visibility(activity) do
    with %Object{} = object <- Object.normalize(activity) do
minibikini's avatar
minibikini committed
379
      Visibility.get_visibility(object)
380
    end
381
  end
lain's avatar
lain committed
382

minibikini's avatar
minibikini committed
383
  def check_expiry_date({:ok, nil} = res), do: res
384

minibikini's avatar
minibikini committed
385
  def check_expiry_date({:ok, in_seconds}) do
386
    expiry = DateTime.add(DateTime.utc_now(), in_seconds)
387

388
    if Pleroma.Workers.PurgeExpiredActivity.expires_late_enough?(expiry) do
389
390
391
392
393
394
      {:ok, expiry}
    else
      {:error, "Expiry date is too soon"}
    end
  end

minibikini's avatar
minibikini committed
395
  def check_expiry_date(expiry_str) do
396
397
398
399
    Ecto.Type.cast(:integer, expiry_str)
    |> check_expiry_date()
  end

400
401
402
403
  def listen(user, data) do
    visibility = Map.get(data, :visibility, "public")

    with {to, cc} <- get_to_and_cc(user, [], nil, visibility, nil),
kaniini's avatar
kaniini committed
404
         listen_data <-
405
406
407
           data
           |> Map.take([:album, :artist, :title, :length])
           |> Map.new(fn {key, value} -> {to_string(key), value} end)
408
409
           |> Map.put("type", "Audio")
           |> Map.put("to", to)
410
411
           |> Map.put("cc", cc)
           |> Map.put("actor", user.ap_id),
kaniini's avatar
kaniini committed
412
413
414
415
416
417
         {:ok, activity} <-
           ActivityPub.listen(%{
             actor: user,
             to: to,
             object: listen_data,
             context: Utils.generate_context_id(),
418
             additional: %{"cc" => cc}
kaniini's avatar
kaniini committed
419
420
421
422
           }) do
      {:ok, activity}
    end
  end
423

424
  def post(user, %{status: _} = data) do
minibikini's avatar
minibikini committed
425
    with {:ok, draft} <- Pleroma.Web.CommonAPI.ActivityDraft.create(user, data) do
426
      ActivityPub.create(draft.changes, draft.preview?)
lain's avatar
lain committed
427
428
    end
  end
429

430
  def pin(id, %{ap_id: user_ap_id} = user) do
431
432
    with %Activity{
           actor: ^user_ap_id,
minibikini's avatar
minibikini committed
433
           data: %{"type" => "Create"},
rinpatch's avatar
rinpatch committed
434
           object: %Object{data: %{"type" => object_type}}
435
         } = activity <- Activity.get_by_id_with_object(id),
rinpatch's avatar
rinpatch committed
436
         true <- object_type in ["Note", "Article", "Question"],
437
         true <- Visibility.is_public?(activity),
438
         {:ok, _user} <- User.add_pinnned_activity(user, activity) do
minibikini's avatar
minibikini committed
439
440
      {:ok, activity}
    else
441
      {:error, %{errors: [pinned_activities: {err, _}]}} -> {:error, err}
minibikini's avatar
minibikini committed
442
      _ -> {:error, dgettext("errors", "Could not pin")}
minibikini's avatar
minibikini committed
443
444
445
    end
  end

446
447
  def unpin(id, user) do
    with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
448
         {:ok, _user} <- User.remove_pinnned_activity(user, activity) do
minibikini's avatar
minibikini committed
449
450
      {:ok, activity}
    else
451
      {:error, %{errors: [pinned_activities: {err, _}]}} -> {:error, err}
minibikini's avatar
minibikini committed
452
      _ -> {:error, dgettext("errors", "Could not unpin")}
minibikini's avatar
minibikini committed
453
454
    end
  end
455
456

  def add_mute(user, activity) do
457
    with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]),
lain's avatar
lain committed
458
         _ <- Pleroma.Notification.mark_context_as_read(user, activity.data["context"]) do
459
460
      {:ok, activity}
    else
461
      {:error, _} -> {:error, dgettext("errors", "conversation is already muted")}
462
463
464
465
466
467
468
469
    end
  end

  def remove_mute(user, activity) do
    ThreadMute.remove_mute(user.id, activity.data["context"])
    {:ok, activity}
  end

470
  def thread_muted?(%User{id: user_id}, %{data: %{"context" => context}})
471
      when is_binary(context) do
472
    ThreadMute.exists?(user_id, context)
473
  end
minibikini's avatar
Reports    
minibikini committed
474

475
476
  def thread_muted?(_, _), do: false

477
478
479
  def report(user, data) do
    with {:ok, account} <- get_reported_account(data.account_id),
         {:ok, {content_html, _, _}} <- make_report_content_html(data[:comment]),
minibikini's avatar
minibikini committed
480
481
482
483
484
485
486
         {:ok, statuses} <- get_report_statuses(account, data) do
      ActivityPub.flag(%{
        context: Utils.generate_context_id(),
        actor: user,
        account: account,
        statuses: statuses,
        content: content_html,
487
        forward: Map.get(data, :forward, false)
minibikini's avatar
minibikini committed
488
489
490
491
492
493
494
495
      })
    end
  end

  defp get_reported_account(account_id) do
    case User.get_cached_by_id(account_id) do
      %User{} = account -> {:ok, account}
      _ -> {:error, dgettext("errors", "Account not found")}
minibikini's avatar
Reports    
minibikini committed
496
497
    end
  end
498

499
500
501
502
503
504
505
  def update_report_state(activity_ids, state) when is_list(activity_ids) do
    case Utils.update_report_state(activity_ids, state) do
      :ok -> {:ok, activity_ids}
      _ -> {:error, dgettext("errors", "Could not update state")}
    end
  end

Sergey Suprunenko's avatar
Sergey Suprunenko committed
506
  def update_report_state(activity_id, state) do
minibikini's avatar
minibikini committed
507
508
    with %Activity{} = activity <- Activity.get_by_id(activity_id) do
      Utils.update_report_state(activity, state)
Sergey Suprunenko's avatar
Sergey Suprunenko committed
509
    else
510
511
      nil -> {:error, :not_found}
      _ -> {:error, dgettext("errors", "Could not update state")}
Sergey Suprunenko's avatar
Sergey Suprunenko committed
512
513
514
515
516
    end
  end

  def update_activity_scope(activity_id, opts \\ %{}) do
    with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
minibikini's avatar
minibikini committed
517
518
         {:ok, activity} <- toggle_sensitive(activity, opts) do
      set_visibility(activity, opts)
Sergey Suprunenko's avatar
Sergey Suprunenko committed
519
    else
520
521
      nil -> {:error, :not_found}
      {:error, reason} -> {:error, reason}
Sergey Suprunenko's avatar
Sergey Suprunenko committed
522
523
524
    end
  end

525
526
  defp toggle_sensitive(activity, %{sensitive: sensitive}) when sensitive in ~w(true false) do
    toggle_sensitive(activity, %{sensitive: String.to_existing_atom(sensitive)})
Sergey Suprunenko's avatar
Sergey Suprunenko committed
527
528
  end

529
  defp toggle_sensitive(%Activity{object: object} = activity, %{sensitive: sensitive})
Sergey Suprunenko's avatar
Sergey Suprunenko committed
530
531
532
533
534
535
536
537
538
539
540
541
542
       when is_boolean(sensitive) do
    new_data = Map.put(object.data, "sensitive", sensitive)

    {:ok, object} =
      object
      |> Object.change(%{data: new_data})
      |> Object.update_and_set_cache()

    {:ok, Map.put(activity, :object, object)}
  end

  defp toggle_sensitive(activity, _), do: {:ok, activity}

543
  defp set_visibility(activity, %{visibility: visibility}) do
Sergey Suprunenko's avatar
Sergey Suprunenko committed
544
545
546
547
548
    Utils.update_activity_visibility(activity, visibility)
  end

  defp set_visibility(activity, _), do: {:ok, activity}

549
550
  def hide_reblogs(%User{} = user, %User{} = target) do
    UserRelationship.create_reblog_mute(user, target)
551
552
  end

553
554
  def show_reblogs(%User{} = user, %User{} = target) do
    UserRelationship.delete_reblog_mute(user, target)
555
  end
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572

  def get_user(ap_id, fake_record_fallback \\ true) do
    cond do
      user = User.get_cached_by_ap_id(ap_id) ->
        user

      user = User.get_by_guessed_nickname(ap_id) ->
        user

      fake_record_fallback ->
        # TODO: refactor (fake records is never a good idea)
        User.error_user(ap_id)

      true ->
        nil
    end
  end
lain's avatar
lain committed
573
end